Browse files

initial commit (migrating from old SVN)

  • Loading branch information...
0 parents commit 4bdc7ac91f8c28ad1026664366de55cd54d159b0 Alex King committed Sep 12, 2009
28 README.txt
@@ -0,0 +1,28 @@
+In this distribution:
+
+phptagengine.class.inc.php
+ The PHP class definition of PHP Tag Engine, this contains the core code.
+
+phptagengine.config.inc.php
+ Put your config settings in here.
+
+phptagengine.css
+ Make things look pretty here.
+
+phptagengine.inc.php
+ Include this file in your project, in turn it includes the rest of the PHP Tag Engine files and handles AJAX requests.
+
+phptagengine.php.js
+ AJAX communication code.
+
+languages/*
+ Put your translations here.
+
+yui/*
+ Yahoo! Auto-Complete library code.
+
+README.txt
+ this file
+
+
+See http://phptagengine.com/ for more information.
32 languages/english.inc.php
@@ -0,0 +1,32 @@
+<?php
+
+// Copyright (c) 2006-2007 Alex King. All rights reserved.
+// http://alexking.org/projects/php-tag-engine
+//
+// Released under the LGPL license
+// http://www.opensource.org/licenses/lgpl-license.php
+//
+// **********************************************************************
+// 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.
+// **********************************************************************
+
+/**
+ * English language file for PHP Tag Engine
+ *
+ * @package phptagengine
+ */
+
+$pte->charset = 'ISO-8859-1';
+
+$pte->strings["action_cancel"] = "Cancel";
+$pte->strings["action_edit"] = "Edit";
+$pte->strings["action_delete"] = "Delete";
+$pte->strings["action_delete_text_icon"] = "X";
+$pte->strings["action_save"] = "Save";
+$pte->strings["action_saving"] = "Saving...";
+$pte->strings["data_none"] = "(none)";
+$pte->strings["label_tags"] = "Tags:";
+
+?>
32 languages/english_utf8.inc.php
@@ -0,0 +1,32 @@
+<?php
+
+// Copyright (c) 2006-2007 Alex King. All rights reserved.
+// http://alexking.org/projects/php-tag-engine
+//
+// Released under the LGPL license
+// http://www.opensource.org/licenses/lgpl-license.php
+//
+// **********************************************************************
+// 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.
+// **********************************************************************
+
+/**
+ * English language file for PHP Tag Engine
+ *
+ * @package phptagengine
+ */
+
+$pte->charset = 'UTF-8';
+
+$pte->strings["action_cancel"] = "Cancel";
+$pte->strings["action_edit"] = "Edit";
+$pte->strings["action_delete"] = "Delete";
+$pte->strings["action_delete_text_icon"] = "X";
+$pte->strings["action_save"] = "Save";
+$pte->strings["action_saving"] = "Saving...";
+$pte->strings["data_none"] = "(none)";
+$pte->strings["label_tags"] = "Tags:";
+
+?>
32 languages/french.inc.php
@@ -0,0 +1,32 @@
+<?php
+
+// Copyright (c) 2006-2007 Alex King. All rights reserved.
+// http://alexking.org/projects/php-tag-engine
+//
+// Released under the LGPL license
+// http://www.opensource.org/licenses/lgpl-license.php
+//
+// **********************************************************************
+// 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.
+// **********************************************************************
+
+/**
+ * French language file for PHP Tag Engine
+ *
+ * @package phptagengine
+ */
+
+$pte->charset = 'ISO-8859-1';
+
+$pte->strings["action_cancel"] = "Annuler";
+$pte->strings["action_edit"] = "Modifier";
+$pte->strings["action_delete"] = "Supprimer";
+$pte->strings["action_delete_text_icon"] = "X";
+$pte->strings["action_save"] = "Enregistrer";
+$pte->strings["action_saving"] = "Enregistrement...";
+$pte->strings["data_none"] = "(aucun)";
+$pte->strings["label_tags"] = "Tags:";
+
+?>
32 languages/german.inc.php
@@ -0,0 +1,32 @@
+<?php
+
+// Copyright (c) 2006-2007 Alex King. All rights reserved.
+// http://alexking.org/projects/php-tag-engine
+//
+// Released under the LGPL license
+// http://www.opensource.org/licenses/lgpl-license.php
+//
+// **********************************************************************
+// 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.
+// **********************************************************************
+
+/**
+ * German language file for PHP Tag Engine
+ *
+ * @package phptagengine
+ */
+
+$pte->charset = 'ISO-8859-1';
+
+$pte->strings["action_cancel"] = "Abbrechen";
+$pte->strings["action_edit"] = "Bearbeiten";
+$pte->strings["action_delete"] = "L&ouml;schen";
+$pte->strings["action_delete_text_icon"] = "X";
+$pte->strings["action_save"] = "Sichern";
+$pte->strings["action_saving"] = "Sichern...";
+$pte->strings["data_none"] = "(keine)";
+$pte->strings["label_tags"] = "Stichworte:";
+
+?>
32 languages/german_utf8.inc.php
@@ -0,0 +1,32 @@
+<?php
+
+// Copyright (c) 2006-2007 Alex King. All rights reserved.
+// http://alexking.org/projects/php-tag-engine
+//
+// Released under the LGPL license
+// http://www.opensource.org/licenses/lgpl-license.php
+//
+// **********************************************************************
+// 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.
+// **********************************************************************
+
+/**
+ * German language file for PHP Tag Engine
+ *
+ * @package phptagengine
+ */
+
+$pte->charset = 'UTF-8';
+
+$pte->strings["action_cancel"] = "Abbrechen";
+$pte->strings["action_edit"] = "Bearbeiten";
+$pte->strings["action_delete"] = "L&ouml;schen";
+$pte->strings["action_delete_text_icon"] = "X";
+$pte->strings["action_save"] = "Sichern";
+$pte->strings["action_saving"] = "Sichern...";
+$pte->strings["data_none"] = "(keine)";
+$pte->strings["label_tags"] = "Stichworte:";
+
+?>
32 languages/nederlands.inc.php
@@ -0,0 +1,32 @@
+<?php
+
+// Copyright (c) 2006-2007 Alex King. All rights reserved.
+// http://alexking.org/projects/php-tag-engine
+//
+// Released under the LGPL license
+// http://www.opensource.org/licenses/lgpl-license.php
+//
+// **********************************************************************
+// 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.
+// **********************************************************************
+
+/**
+ * Dutch language file for PHP Tag Engine
+ *
+ * @package phptagengine
+ */
+
+$pte->charset = 'ISO-8859-1';
+
+$pte->strings["action_cancel"] = "Afbreken";
+$pte->strings["action_edit"] = "Bewerken";
+$pte->strings["action_delete"] = "Verwijderen";
+$pte->strings["action_delete_text_icon"] = "X";
+$pte->strings["action_save"] = "Bewaren";
+$pte->strings["action_saving"] = "Bewaren...";
+$pte->strings["data_none"] = "(geen)";
+$pte->strings["label_tags"] = "Tags:";
+
+?>
32 languages/nederlands_utf8.inc.php
@@ -0,0 +1,32 @@
+<?php
+
+// Copyright (c) 2006-2007 Alex King. All rights reserved.
+// http://alexking.org/projects/php-tag-engine
+//
+// Released under the LGPL license
+// http://www.opensource.org/licenses/lgpl-license.php
+//
+// **********************************************************************
+// 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.
+// **********************************************************************
+
+/**
+ * Dutch language file for PHP Tag Engine
+ *
+ * @package phptagengine
+ */
+
+$pte->charset = 'UTF-8';
+
+$pte->strings["action_cancel"] = "Afbreken";
+$pte->strings["action_edit"] = "Bewerken";
+$pte->strings["action_delete"] = "Verwijderen";
+$pte->strings["action_delete_text_icon"] = "X";
+$pte->strings["action_save"] = "Bewaren";
+$pte->strings["action_saving"] = "Bewaren...";
+$pte->strings["data_none"] = "(geen)";
+$pte->strings["label_tags"] = "Tags:";
+
+?>
32 languages/norwegian.inc.php
@@ -0,0 +1,32 @@
+<?php
+
+// Copyright (c) 2006-2007 Alex King. All rights reserved.
+// http://alexking.org/projects/php-tag-engine
+//
+// Released under the LGPL license
+// http://www.opensource.org/licenses/lgpl-license.php
+//
+// **********************************************************************
+// 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.
+// **********************************************************************
+
+/**
+ * Norwegian language file for PHP Tag Engine
+ *
+ * @package phptagengine
+ */
+
+$pte->charset = 'ISO-8859-1';
+
+$pte->strings["action_cancel"] = "Avbryt";
+$pte->strings["action_edit"] = "Rediger";
+$pte->strings["action_delete"] = "Slett";
+$pte->strings["action_delete_text_icon"] = "X";
+$pte->strings["action_save"] = "Lagre";
+$pte->strings["action_saving"] = "Lagrer...";
+$pte->strings["data_none"] = "(ingen)";
+$pte->strings["label_tags"] = "Emneord:";
+
+?>
1 languages/spanish.inc.php
@@ -0,0 +1 @@
+<?php
1,244 phptagengine.class.inc.php
@@ -0,0 +1,1244 @@
+<?php
+
+// Copyright (c) 2006-2007 Alex King. All rights reserved.
+// http://alexking.org/projects/php-tag-engine
+//
+// Released under the LGPL license
+// http://www.opensource.org/licenses/lgpl-license.php
+//
+// **********************************************************************
+// 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.
+// **********************************************************************
+
+/**
+ * This file contains the PHP Tag Engine class definition.
+ *
+ * @package phptagengine
+ */
+
+if (__FILE__ == basename($_SERVER['SCRIPT_NAME'])) { die(); }
+
+/**
+ * The phptagengine class, all the good stuff happens here.
+ *
+ * @package phptagengine
+ */
+class phptagengine {
+ /**
+ * set to global $ADOdb instance
+ * @var mixed
+ */
+ var $db;
+ /**
+ * Column name escape string for the database being used. Checks for: mysql, postgres7, mssql
+ *
+ * @var string
+ */
+ var $db_col_escape_char;
+ /**
+ * Name of the tags table in the database
+ *
+ * @var string
+ */
+ var $table_tags;
+ /**
+ * Name of the tag names table in the database
+ *
+ * @var string
+ */
+ var $table_tag_names;
+ /**
+ * Name of the users table in the database
+ *
+ * @var string
+ */
+ var $table_users;
+ /**
+ * The display name column (normally name or username) in the users table in the database
+ *
+ * @var string
+ */
+ var $table_users_name;
+ /**
+ * URL to the PHP Tag Engine folder, include the trailing slash
+ *
+ * @var string
+ */
+ var $base_url;
+ /**
+ * URL of the file handling AJAX requests
+ *
+ * @var string
+ */
+ var $ajax_handler;
+ /**
+ * Name of language file to include - languages/(language).inc.php
+ *
+ * @var string
+ */
+ var $language;
+ /**
+ * Character set
+ *
+ * @var string
+ */
+ var $charset;
+ /**
+ * Array of strings for localization
+ *
+ * @var array
+ */
+ var $strings;
+ /**
+ * Value to be used if no type is passed in, useful if you only have one
+ * type of object you are tagging and just need a default value
+ *
+ * @var string
+ */
+ var $default_type;
+ /**
+ * Value to be used if no user is passed in, useful if you are not tracking
+ * tags by user and just need a default value
+ *
+ * @var string
+ */
+ var $default_user;
+ /**
+ * URL for browsing items by tag, includes token replacement for tag, type
+ *
+ * example: http://example.com/index.php?view=tag&tag=<tag>&type=<type>
+ *
+ * @var string
+ */
+ var $tag_browse_url;
+ /**
+ * Show an X next to each tag to allow removal of that tag
+ *
+ * @var boolean
+ */
+ var $show_remove_links;
+ /**
+ * Show text or image?
+ *
+ * example: 'text' - uses @link phptagengine::$strings
+ * example: 'image'
+ *
+ * @var string
+ */
+ var $edit_button_display;
+ /**
+ * URL of the image for the edit button
+ *
+ * @var string
+ */
+ var $edit_button_image_url;
+ /**
+ * Show text or image?
+ *
+ * example: 'text' - uses @link phptagengine::$strings
+ * example: 'image'
+ *
+ * @var string
+ */
+ var $delete_button_display;
+ /**
+ * URL of the image for the delete button
+ *
+ * @var string
+ */
+ var $delete_button_image_url;
+ /**
+ * Stores arrays of tags for items, use as a cache to reduce queries
+ *
+ * @var array
+ */
+ var $item_tags_cache;
+ /**
+ * Enable Yahoo! Auto-Complete
+ *
+ * @var boolean
+ */
+ var $yac;
+ /**
+ * List of Yahoo! UI Pattern files to be included, in case Yahoo! UI Patterns are already in use elsewhere and we don't want to duplicate their inclusion. The array should contain filenames in the 'yui' directory.
+ *
+ * Note: order matters in this list.
+ *
+ * @var array
+ */
+ var $yac_files;
+ /**
+ * PHP Tag Engine Version
+ *
+ * @var string
+ */
+ var $version;
+ /**
+ * Show or hide error messages
+ *
+ * @var boolean
+ */
+ var $debug;
+
+ /**
+ * Initializes the class
+ *
+ * @return phptagengine
+ */
+ function phptagengine() {
+ $this->base_url = 'http://example.com/';
+ $this->ajax_handler = 'http://example.com/ajax.php';
+ $this->language = 'english';
+ $this->charset = 'UTF-8';
+ $this->db_type = 'mysql';
+ $this->strings = array();
+ $this->tag_browse_url = 'http://example.com/index.php?view=tags&tag=<tag>&type=<type>';
+ $this->default_type = '';
+ $this->default_user = 1;
+ $this->edit_button_display = 'text';
+ $this->delete_button_display = 'text';
+ $this->yac = true;
+ $this->yac_files = array(
+ 'yahoo-dom-event.js'
+ ,'autocomplete.js'
+ );
+ $this->version = '1.01';
+ $this->debug = false;
+ }
+
+ /**
+ * Sets the character used for escaping column names for the database type in use
+ */
+ function set_db_col_escape_char() {
+ switch ($this->db->databaseType) {
+ case 'mysql':
+ $this->db_col_escape_char = '`';
+ break;
+ case 'postgres7':
+ case 'mssql':
+ $this->db_col_escape_char = '"';
+ break;
+ }
+ }
+
+ /**
+ * Sets the type or user property to the default value if the value is
+ * null, could be extended in the future
+ * @param string $prop expected 'type' or 'user'
+ * @param mixed $value if this is null, we set the default
+ */
+ function default_value($prop, $value) {
+ if ($value == null && in_array($prop, array('type', 'user'))) {
+ eval('$value = $this->default_'.$prop.';');
+ }
+ return $value;
+ }
+
+ /**
+ * Set a tag to lowercase and remove spaces, could be extended in the future
+ * @param mixed $value the value to be normalized
+ * @param string $type the type of value, used for the switch statement
+ * @return mixed
+ */
+ function normalize($value, $type = 'tag') {
+ switch ($type) {
+ case 'tag':
+ $value = preg_replace('|[^a-z0-9_.\-@#$%*!&]|i', '', strtolower($value));
+ break;
+ }
+ return $value;
+ }
+
+ /**
+ * Does a tag already exist
+ *
+ * @uses phptagengine::get_tag_id()
+ *
+ * @param string $tag
+ * @return boolean
+ */
+ function tag_exists($tag) {
+ if ($this->get_tag_id($tag) != false) {
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ /**
+ * Creates a new tag (noramlized) in the database, returns the ID of the
+ * created tag or false
+ *
+ * @uses phptagengine::normalize() to normalize the $tag
+ * @uses phptagengine::tag_exists() to see if the tag needs creating
+ *
+ * @param string $tag
+ * @return mixed
+ */
+ function create_tag($tag = null) {
+ if ($tag == null) {
+ return false;
+ }
+ $tag = $this->normalize($tag);
+ if (strlen($tag) < 1) {
+ return false;
+ }
+ $test = $this->get_tag_id($tag);
+ if ($test != false) {
+ return $test;
+ }
+ $result = $this->db->Execute("
+ INSERT
+ INTO $this->table_tag_names
+ ( ".$this->db_col_escape_char."name".$this->db_col_escape_char."
+ )
+ VALUES
+ ( ".$this->db->qstr($tag)."
+ )
+ ") or die(throw_error($this->db->ErrorMsg(), __FILE__, __LINE__));
+
+ if ($result) {
+ $id = false;
+ if (strstr($this->db->databaseType, 'postgres')) {
+ $id = $this->db->GetOne("
+ SELECT CURRVAL('".$this->table_tag_names."_id_seq') as id
+ ") or die(throw_error($this->db->ErrorMsg(), __FILE__, __LINE__));
+ }
+ else {
+ $id = $this->db->Insert_ID();
+ }
+ return $id;
+ }
+
+ return false;
+ }
+
+ /**
+ * Adds a tag to an item, creates the tag if needed
+ *
+ * @uses phptagengine::default_value() to set a default value for $type and $user if needed
+ * @uses phptagengine::create_tag() to create a new tag or get the id of the existing tag
+ *
+ * @param string $user user creating the tag
+ * @param string $item item being tagged
+ * @param string $tag tag being used
+ * @param string $type type of item being tagged
+ * @return boolean
+ */
+ function add_tag($user = null, $item = null, $tag = null, $type = null) {
+ if ($item == null || $tag == null) {
+ return false;
+ }
+ $type = $this->default_value('type', $type);
+ $user = $this->default_value('user', $user);
+ $tag_id = $this->create_tag($tag);
+ if ($tag_id == false) {
+ return false;
+ }
+ if ($this->item_tag_exists($user, $item, $tag, $type)) {
+ return true;
+ }
+ else {
+ $this->set_db_col_escape_char();
+ $result = $this->db->Execute("
+ INSERT
+ INTO $this->table_tags
+ ( ".$this->db_col_escape_char."user".$this->db_col_escape_char."
+ , ".$this->db_col_escape_char."item".$this->db_col_escape_char."
+ , ".$this->db_col_escape_char."type".$this->db_col_escape_char."
+ , ".$this->db_col_escape_char."tag".$this->db_col_escape_char."
+ , ".$this->db_col_escape_char."date".$this->db_col_escape_char."
+ )
+ VALUES
+ ( ".$this->db->qstr($user)."
+ , ".$this->db->qstr($item)."
+ , ".$this->db->qstr($type)."
+ , ".$this->db->qstr($tag_id)."
+ , ".$this->db->DBDate(date("Y-m-d H:i:s"))."
+ )
+ ") or die(throw_error($this->db->ErrorMsg(), __FILE__, __LINE__));
+ if ($result) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Save item tags
+ *
+ * @param string $item
+ * @param string $tags
+ * @param string $type
+ * @param string $user
+ *
+ * @return array key success is 'y/n', key tags is an arra of the item's tags
+ */
+ function save_tags($item, $tags, $type = null, $user = null) {
+ $type = $this->default_value('type', $type);
+ $user = $this->default_value('user', $user);
+ $old_tags = $this->get_tags($item, $type, $user);
+ $new_tags = array();
+ $success = 'y';
+ if ($tags != null && $tags != '') {
+ $tags = explode(' ', $tags);
+ $tags = array_unique($tags);
+ natcasesort($tags);
+ reset($tags);
+ if (count($tags) > 0) {
+ foreach ($tags as $tag) {
+ if ($tag != '') {
+ if ($this->add_tag($user, $item, $tag, $type)) {
+ $new_tags[] = $this->normalize($tag);
+ }
+ else {
+ $success = 'n';
+ }
+ }
+ }
+ if (count($new_tags) > 0) {
+ $tags = implode(' ', $new_tags);
+ }
+ }
+ }
+ if (count($old_tags) > 0) {
+ foreach ($old_tags as $id => $tag) {
+ if (!in_array($tag, $new_tags)) {
+ if (!$this->remove_tag_by_id(str_replace('id_', '', $id))) {
+ $success = 'n';
+ }
+ }
+ }
+ }
+ $result = array(
+ 'success' => $success
+ , 'tags' => $new_tags
+ );
+ return $result;
+ }
+
+
+ /**
+ * Removes a tag
+ *
+ * @uses phptagengine::get_item_tag_id()
+ * @uses phptagengine::remove_tag_by_id()
+ *
+ * @param string $item
+ * @param string $tag
+ * @param string $user
+ * @param string $type
+ * @return boolean
+ */
+ function remove_tag($item = null, $tag = null, $user = null, $type = null) {
+ $id = $this->get_item_tag_id($user, $item, $tag, $type);
+ return $this->remove_tag_by_id($id);
+ }
+
+ /**
+ * Removes a tag by id
+ *
+ * @param integer $id
+ * @return boolean
+ */
+ function remove_tag_by_id($id) {
+ if ($id == null) {
+ return false;
+ }
+ $result = $this->db->Execute("
+ DELETE
+ FROM $this->table_tags
+ WHERE id = ".$this->db->qstr($id)."
+ ") or die(throw_error($this->db->ErrorMsg(), __FILE__, __LINE__));
+ if ($result) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Gets the tags for an item
+ *
+ * @param string $item
+ * @param string $type
+ * @param string $user
+ * @param boolean $use_cache
+ * @return array
+ */
+ function get_tags($item = null, $type = null, $user = null, $use_cache = false) {
+ if ($item == null) {
+ return false;
+ }
+ $tags = array();
+ if ($use_cache) {
+ if (isset($this->item_tags_cache['item_'.$item])) {
+ $tags = $this->item_tags_cache['item_'.$item];
+ }
+ return $tags;
+ }
+ $where = '';
+ if ($user != null) {
+ $where .= ' AND t.user = '.$this->db->qstr($user);
+ }
+ if ($item != null) {
+ $where .= ' AND t.item = '.$this->db->qstr($item);
+ }
+ if ($type != null) {
+ $where .= ' AND t.type = '.$this->db->qstr($type);
+ }
+ if ($where == '') {
+ return false;
+ }
+ $result = $this->db->Execute("
+ SELECT t.id AS ID
+ , tn.name AS NAME
+ FROM $this->table_tags t
+ JOIN $this->table_tag_names tn
+ ON t.tag = tn.id
+ WHERE 1 = 1
+ $where
+ ORDER BY tn.name
+ ") or die(throw_error($this->db->ErrorMsg(), __FILE__, __LINE__));
+ if ($result && $result->RowCount() > 0) {
+ while ($data = $result->FetchNextObject()) {
+ $tags['id_'.$data->ID] = $data->NAME;
+ }
+ }
+ return $tags;
+ }
+
+ /**
+ * Completely removes a tag from the system (from @link phptagengine::$table_tags and @link phptagengine::$table_tag_names)
+ *
+ * @param string $tag
+ * @return boolean
+ */
+ function delete_tag($tag = null) {
+ if (is_null($tag) || strstr($tag, ' ')) {
+ return false;
+ }
+ $tag_id = $this->get_tag_id($tag);
+ if ($tag_id == false) {
+ return false;
+ }
+ $result = $this->db->Execute("
+ DELETE
+ FROM $this->table_tags
+ WHERE tag = '$tag_id'
+ ") or die(throw_error($this->db->ErrorMsg(), __FILE__, __LINE__));
+ if (!$result) {
+ return false;
+ }
+ $result = $this->db->Execute("
+ DELETE
+ FROM $this->table_tag_names
+ WHERE id = '$tag_id'
+ ") or die(throw_error($this->db->ErrorMsg(), __FILE__, __LINE__));
+ if (!$result) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Changes the name of a tag, or consolidates two existing tags
+ *
+ * @param string $old_tag
+ * @param string $new_tag
+ * @return boolean
+ */
+ function edit_tag($old_tag = null, $new_tag = null) {
+ if (is_null($old_tag) || is_null($new_tag) || strstr($new_tag, ' ')) {
+ return false;
+ }
+ if ($old_tag == $new_tag) {
+ return true;
+ }
+ if ($this->tag_exists($new_tag)) {
+// move all tags to existing tag
+ $old_tag_id = $this->get_tag_id($old_tag);
+ $new_tag_id = $this->get_tag_id($new_tag);
+ $result = $this->db->Execute("
+ UPDATE $this->table_tags
+ SET tag = '$new_tag_id'
+ WHERE tag = '$old_tag_id'
+ ") or die(throw_error($this->db->ErrorMsg(), __FILE__, __LINE__));
+ if (!$result) {
+ return false;
+ }
+// check for dupes
+ $result = $this->db->Execute("
+ SELECT *
+ FROM $this->table_tags
+ WHERE tag = '$new_tag_id'
+ ") or die(throw_error($this->db->ErrorMsg(), __FILE__, __LINE__));
+ if (!$result) {
+ return false;
+ }
+ else if ($result->RowCount() > 0) {
+ $tags = array();
+ $ids_to_delete = array();
+ while ($data = $result->FetchNextObject()) {
+ $key = $data->TAG.$data->ITEM.$data->TYPE.$data->USER;
+ if (!in_array($key, $tags)) {
+ $tags[] = $key;
+ }
+ else {
+ $ids_to_delete[] = $data->ID;
+ }
+ }
+// remove dupes
+ if (count($ids_to_delete) > 0) {
+ $result = $this->db->Execute("
+ DELETE
+ FROM $this->table_tags
+ WHERE id IN (".implode(',', $ids_to_delete).")
+ ") or die(throw_error($this->db->ErrorMsg(), __FILE__, __LINE__));
+ if (!$result) {
+ return false;
+ }
+ }
+ }
+ }
+ else {
+ $result = $this->db->Execute("
+ UPDATE $this->table_tag_names
+ SET name = ".$this->db->qstr($new_tag)."
+ WHERE name = ".$this->db->qstr($old_tag)."
+ ") or die(throw_error($this->db->ErrorMsg(), __FILE__, __LINE__));
+ if (!$result) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Populate cache with items and tags to reduce queries
+ *
+ * @param array $items
+ * @param mixed $user
+ * @param mixed $type
+ */
+ function populate_cache($items = array(), $user = null, $type = null) {
+ if (count($items) == 0) {
+ return;
+ }
+ $where = '';
+ if ($user != null) {
+ $where .= ' AND t.user = '.$this->db->qstr($user);
+ }
+ if ($type != null) {
+ $where .= ' AND t.type = '.$this->db->qstr($type);
+ }
+ $where .= ' AND ( ';
+ $i = 0;
+ foreach ($items as $item) {
+ if ($i > 0) {
+ $where .= ' OR ';
+ }
+ $where .= ' t.item = '.$this->db->qstr($item);
+ $i++;
+ }
+ $where .= ' )';
+ $result = $this->db->Execute("
+ SELECT t.item AS ITEM
+ , t.id AS ID
+ , tn.name AS NAME
+ FROM $this->table_tags t
+ JOIN $this->table_tag_names tn
+ ON t.tag = tn.id
+ WHERE 1 = 1
+ $where
+ ORDER BY t.item, tn.name
+ ") or die(throw_error($this->db->ErrorMsg(), __FILE__, __LINE__));
+ if ($result) {
+ while ($data = $result->FetchNextObject()) {
+ if (!isset($this->item_tags_cache['item_'.$data->ITEM])) {
+ $this->item_tags_cache['item_'.$data->ITEM] = array();
+ }
+ $this->item_tags_cache['item_'.$data->ITEM]['id_'.$data->ID] = $data->NAME;
+ }
+ }
+ }
+
+ /**
+ * Gets the ID of a tag, returns false if the tag does not exist
+ *
+ * @param string $name
+ * @return mixed
+ */
+ function get_tag_id($name = null) {
+ if ($name == null) {
+ return false;
+ }
+ $name = $this->normalize($name);
+ $result = $this->db->Execute("
+ SELECT id AS ID
+ FROM $this->table_tag_names
+ WHERE name = ".$this->db->qstr($name)."
+ ") or die(throw_error($this->db->ErrorMsg(), __FILE__, __LINE__));
+ if ($result && $result->RowCount() == 1) {
+ while ($data = $result->FetchNextObject()) {
+ return $data->ID;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Checks if an item already has a certain tag
+ *
+ * @uses phptagengine::get_item_tag_id()
+ *
+ * @param mixed $user
+ * @param mixed $item
+ * @param mixed $tag
+ * @param mixed $type
+ * @return boolean
+ */
+ function item_tag_exists($user = null, $item = null, $tag = null, $type = null) {
+ if (!$this->get_item_tag_id($user, $item, $tag, $type)) {
+ return false;
+ }
+ else {
+ return true;
+ }
+ }
+
+ /**
+ * Gets the id of an item tag if it exists
+ *
+ * @param mixed $user
+ * @param mixed $item
+ * @param mixed $tag
+ * @param mixed $type
+ * @return mixed id of item or false
+ */
+ function get_item_tag_id($user = null, $item = null, $tag = null, $type = null) {
+ if ($item == null || $tag == null) {
+ return false;
+ }
+ $tag = $this->normalize($tag);
+ $type = $this->default_value('type', $type);
+ $user = $this->default_value('user', $user);
+ $tags = $this->get_tags($item, $type, $user);
+ if (count($tags) == 0) {
+ return false;
+ }
+ else {
+ foreach ($tags as $id => $existing_tag) {
+ if ($tag == $existing_tag) {
+ return str_replace('id_', '', $id);
+ }
+ }
+ }
+ }
+
+ /**
+ * Sends an XML response - used for AJAX
+ *
+ * @param string $string
+ */
+ function xml_out($string) {
+ header("Content-type: text/xml");
+ die('<?xml version="1.0"?>'."\n".'<response>'.$string.'</response>');
+ }
+
+ /**
+ * Prints the CSS and JS links needed in the HTML output
+ *
+ */
+ function html_head() {
+ print('
+<!-- PHP Tag Engine CSS/JS - begin -->
+
+<style type="text/css"> @import url('.$this->base_url.'phptagengine.css?version='.$this->version.'); </style>
+<script type="text/javascript" src="'.$this->base_url.'phptagengine.js.php?version='.$this->version.'"></script>
+ '."\n");
+ if ($this->yac) {
+ $tags = $this->get_all_tags();
+ if (count($tags) > 0) {
+ $tags = implode("','", $tags);
+ }
+ else {
+ $tags = '';
+ }
+ print("
+<script type=\"text/javascript\">
+var tags = ['".$tags."'];
+var yac_tags = new YAHOO.widget.DS_JSArray(tags);
+</script>
+ \n");
+ }
+ print('<!-- PHP Tag Engine CSS/JS - end -->'."\n");
+ }
+
+ /**
+ * Wrapper for htmlspecialchars, in case we need any special processing
+ *
+ * @param string $string
+ * @return string
+ */
+ function html($string) {
+ return htmlspecialchars($string);
+ }
+
+ /**
+ * Get a URL (to be used in a link) for browsing by tag
+ *
+ * @uses phptagengine::$tag_browse_url with string replacement
+ * @uses phptagengine::token_replace()
+ *
+ * @param string $tag the tag to browse
+ * @param mixed $type the type of item, if needed
+ * @return string
+ */
+ function tag_browse_url($tag = '', $type = '') {
+ $type = $this->default_value('type', $type);
+ return $this->token_replace($this->tag_browse_url, urlencode($tag), urlencode($type));
+ }
+
+ /**
+ * Returns a list of all tags
+ *
+ * @param mixed $type
+ * @return array
+ */
+ function get_all_tags($type = null) {
+ $type = $this->default_value('type', $type);
+ $tags = array();
+ $result = $this->db->Execute("
+ SELECT tn.id AS ID
+ , tn.name AS NAME
+ FROM $this->table_tag_names tn
+ JOIN $this->table_tags t
+ ON tn.id = t.tag
+ ORDER BY tn.name
+ ") or die(throw_error($this->db->ErrorMsg(), __FILE__, __LINE__));
+ if ($result) {
+ while ($data = $result->FetchNextObject()) {
+ if (!isset($tags['id_'.$data->ID])) {
+ $tags['id_'.$data->ID] = $data->NAME;
+ }
+ }
+ }
+ return $tags;
+ }
+
+ /**
+ * Prints an HTML list of all tags
+ *
+ * @uses phptagengine::get_all_tags()
+ *
+ * @param string $url
+ * @param mixed $type
+ * @param mixed $onclick
+ */
+ function html_tags_list($url = null, $type = null, $onclick = null, $edit_button = false, $delete_button = false) {
+ if (is_null($url)) {
+ $url = $this->tag_browse_url;
+ }
+ $tags = $this->get_all_tags($type);
+ print('
+ <ul class="pte_tags_list_all">
+ ');
+ if (count($tags) == 0) {
+ print('
+ <li>'.$this->strings['data_none'].'</li>
+ ');
+ }
+ else {
+ foreach ($tags as $id => $tag) {
+ if (is_null($onclick)) {
+ $onclick_str = '';
+ }
+ else {
+ $onclick_str = ' onclick="'.$this->token_replace($onclick, $tag, $type).'"';
+ }
+
+ /**
+ * @todo in some future release, add support for editing and deleting tags
+ * /
+/*
+ if (!$edit_button) {
+ $edit_button_str = '';
+ }
+ else {
+ $edit_button_str = '<input type="image" src="'.$this->edit_button_image_url.'" onclick="" alt="'.$this->strings['action_edit'].'" class="pte_image_button" />';
+ }
+ if (!$delete_button) {
+ $delete_button_str = '';
+ }
+ else {
+ $delete_button_str = '<input type="image" src="'.$this->delete_button_image_url.'" onclick="" alt="'.$this->strings['action_delete'].'" class="pte_image_button" />';
+ }
+ print('
+ <li id="pte_tag_'.$tag.'">'.$delete_button_str.$edit_button_str.'<a href="'.$this->token_replace($url, $tag, $type).'"'.$onclick_str.'>'.$this->html($tag).'</a></li>
+ ');
+*/
+ print('
+ <li id="pte_tag_'.$tag.'"><a href="'.$this->token_replace($url, $tag, $type).'"'.$onclick_str.'>'.$this->html($tag).'</a></li>
+ ');
+ }
+ }
+ print('
+ </ul>
+ ');
+ }
+
+ /**
+ * Returns a string, replacing tokens for tag, type
+ *
+ * @param string $url
+ * @param string $tag
+ * @param string $type
+ * @return string
+ */
+ function token_replace($string = null, $tag = null, $type = null) {
+ if (is_null($string) || is_null($tag)) {
+ return '';
+ }
+ $type = $this->default_value('type', $type);
+ return str_replace('<tag>', $tag, str_replace('<type>', $type, $string));
+ }
+
+ /**
+ * Outputs the HTML display of tags for an item
+ *
+ * @param string $item
+ * @param mixed $type
+ */
+ function html_item_tags($item, $type = null, $user = null, $use_cache = false, $read_only = false) {
+ $user = $this->default_value('user', $user);
+ $type = $this->default_value('type', $type);
+ $tags = $this->get_tags($item, $type, $user, $use_cache);
+ if (count($tags) > 0) {
+ $tags_class = ' pte_has_tags';
+ }
+ else {
+ $tags_class = '';
+ }
+ print('
+ <!-- PHP Tag Engine html_item_tags for '.$item.' - begin -->
+ <div id="pte_tag_form_'.$item.'" class="pte_tags_form'.$tags_class.'">
+ <form id="pte_tag_edit_form_'.$item.'" action="'.$this->ajax_handler.'" onsubmit="pte.save_tags(\''.$user.'\', \''.$item.'\', this.tags.value, this.type.value); return false;">
+ <label for="pte_tags_'.$item.'">'.$this->strings['label_tags'].'</label>
+ <ul id="pte_tags_list_'.$item.'" class="pte_tags_list">
+ ');
+ if (count($tags) > 0) {
+ foreach ($tags as $id => $tag) {
+ print('
+ <li id="pte_tag_'.$item.'_'.$tag.'"><a href="'.$this->tag_browse_url($tag, $type).'">'.$this->html($tag).'</a>
+ ');
+ if ($this->show_remove_links && !$read_only) {
+ print('
+ <a href="javascript:void(pte.remove_tag(\''.$item.'\', \''.$tag.'\', \''.$type.'\'));" title="'.$this->strings['action_delete'].'">'.$this->button_display('delete').'</a>
+ ');
+ }
+ print('
+ </li>
+ ');
+ }
+ $edit_value = implode(' ', $tags).' ';
+ }
+ else {
+ print('
+ <li>'.$this->strings['data_none'].'</li>
+ ');
+ $edit_value = '';
+ }
+ if ($read_only) {
+ print('
+ </ul>
+ </form>
+ ');
+ }
+ else {
+ print('
+ <li class="pte_edit"><a href="javascript:void(pte.item_tag_view(\''.$item.'\', \'edit\'));">'.$this->button_display('edit').'</a></li>
+ </ul>
+ <fieldset id="pte_tags_edit_'.$item.'" class="pte_tags_edit">
+ <div class="pte_edit_wrapper">
+ <input type="text" id="pte_tags_edit_field_'.$item.'" class="pte_tags_edit_field" name="tags" value="'.$edit_value.'" />
+ ');
+ if ($this->yac) {
+ print('
+ <div id="yac_container_'.$item.'" class="yac_list"></div>
+ ');
+ }
+ print('
+ </div>
+ <input type="submit" name="submit_button" value="'.$this->strings['action_save'].'" />
+ <input type="button" name="cancel_button" value="'.$this->strings['action_cancel'].'" onclick="pte.item_tag_view(\''.$item.'\', \'view\')" />
+ <input type="hidden" id="pte_tags_edit_type_'.$item.'" name="type" value="'.$type.'" />
+ </fieldset>
+ <span id="pte_tags_saving_'.$item.'" class="pte_tags_saving">'.$this->strings['action_saving'].'</span>
+ </form>
+ </div>
+ ');
+ if ($this->yac) {
+ print('
+ <script type="text/javascript"><!--
+ yac_'.$item.' = new YAHOO.widget.AutoComplete("pte_tags_edit_field_'.$item.'","yac_container_'.$item.'", yac_tags);
+ yac_'.$item.'.delimChar = " ";
+ yac_'.$item.'.maxResultsDisplayed = 20;
+ yac_'.$item.'.queryDelay = 0;
+ // --></script>
+ ');
+ }
+ }
+ print('<!-- PHP Tag Engine html_item_tags for '.$item.' - end -->'."\n");
+ }
+
+ /**
+ * Returns a string with the specified character escaped with a backslash, used for JS strings, etc.
+ *
+ * Note: There is a JS equivilent of this funciton that should be updated to mirror it.
+ *
+ * @param string $string the string needing escaping
+ * @param string $needle the character to be slashed
+ *
+ * @return string
+ */
+ function slash($string, $needle = '"') {
+ return str_replace($needle, "\$needle", $string);
+ }
+
+ /**
+ * Excodes a string as XML
+ *
+ * @todo use a smarter function than just htmlentities()
+ *
+ * @param string $string
+ * @return string
+ */
+ function xml_encode($string) {
+ return htmlentities($string);
+ }
+
+ /**
+ * Returns the proper string to be used for an action link
+ *
+ * Note: There is a JS equivilent of this funciton that should be updated to mirror it.
+ *
+ * @param string $type
+ * @return string
+ */
+ function button_display($type) {
+ $display = '';
+ switch ($type) {
+ case 'edit':
+ $case = $this->edit_button_display;
+ $url = $this->edit_button_image_url;
+ break;
+ case 'delete':
+ $case = $this->delete_button_display;
+ $url = $this->delete_button_image_url;
+ break;
+ default:
+ return $display;
+ }
+ switch ($case) {
+ case 'text':
+ if (isset($this->strings['action_'.$type.'_text_icon'])) {
+ $display = $this->strings['action_'.$type.'_text_icon'];
+ }
+ else {
+ $display = '['.$this->strings['action_'.$type].']';
+ }
+ break;
+ case 'image':
+ $display = '<img src="'.$url.'" alt="'.$this->slash($this->strings['action_'.$type]).'" class="pte_button_'.$type.'" />';
+ break;
+ }
+ return $display;
+ }
+
+ /**
+ * This handles the PHP Tag Engine AJAX requests
+ *
+ */
+ function ajax_engine() {
+ if (!isset($_GET['pte_action'])) {
+ return;
+ }
+ $output = null;
+ $vars = array(
+ 'user'
+ ,'item'
+ ,'tag'
+ ,'tags'
+ ,'type'
+ ,'old_tag'
+ ,'new_tag'
+ );
+ foreach ($vars as $var) {
+ if (!empty($_REQUEST[$var])) {
+ $$var = stripslashes($_REQUEST[$var]);
+ }
+ else {
+ $$var = null;
+ }
+ }
+ switch ($_GET['pte_action']) {
+ case 'save_tags':
+ $result = $this->save_tags($item, $tags, $type, $user);
+ $output = '<result action="'.$_GET['pte_action'].'" success="'.$result['success'].'" user="'
+ .$this->xml_encode($user).'" item="'.$this->xml_encode($item).'" type="'.$this->xml_encode($type).'">'
+ .'<tags><![CDATA['.implode(' ', $result['tags']).']]></tags>'
+ .'</result>';
+ break;
+ case 'add_tag':
+// TODO - specifically adding a tag goes here
+ break;
+ case 'remove_tag':
+ if ($this->remove_tag($item, $tag, $user, $type)) {
+ $success = 'y';
+ }
+ else {
+ $success = 'n';
+ }
+ $output = '<result action="'.$_GET['pte_action'].'" success="'.$success.'" user="'
+ .$this->xml_encode($user).'" item="'.$this->xml_encode($item).'">'
+ .'<tag><![CDATA['.$tag.']]></tag>'
+ .'</result>';
+ break;
+ case 'edit_tag':
+ if ($this->edit_tag($old_tag, $new_tag)) {
+ $success = 'y';
+ }
+ else {
+ $success = 'n';
+ }
+ $output = '<result action="'.$_GET['pte_action'].'" success="'.$success.'">'
+ .'<old_tag><![CDATA['.$old_tag.']]></old_tag>'
+ .'<new_tag><![CDATA['.$new_tag.']]></new_tag>'
+ .'</result>';
+ break;
+ case 'delete_tag':
+ if ($this->delete_tag($tag)) {
+ $success = 'y';
+ }
+ else {
+ $success = 'n';
+ }
+ $output = '<result action="'.$_GET['pte_action'].'" success="'.$success.'">'
+ .'<tag><![CDATA['.$tag.']]></tag>'
+ .'</result>';
+ break;
+ }
+ if ($output != null) {
+ $this->xml_out($output);
+ }
+ else {
+ $this->xml_out('<result action="'.$this->xml_encode(stripslashes($_GET['pte_action'])).'" success="n" />');
+ }
+ }
+
+ /**
+ * Create the PHP Tag Engine tables
+ *
+ * @return boolean
+ */
+ function install() {
+ require_once('phptagengine.config.inc.php');
+
+ $tables = array();
+ $indexes = array();
+
+// TAGS
+
+ $tables[$this->table_tags] = '
+ `id` I4 AUTO PRIMARY NOTNULL
+ , `item` C(255) NOTNULL
+ , `tag` I4 NOTNULL
+ , `type` C(255) NOTNULL
+ , `user` C(255) NOTNULL
+ , `date` T DEFTIMESTAMP NOTNULL
+ ';
+
+ $indexes[$this->table_tags] = array(
+ 'id'
+ ,'item'
+ ,'tag'
+ ,'type'
+ ,'user'
+ ,'date'
+ );
+
+// TAG NAMES
+
+ $tables[$this->table_tag_names] = '
+ `id` I4 AUTO PRIMARY NOTNULL
+ , `name` C(255) NOTNULL
+ ';
+
+ $indexes[$this->table_tag_names] = array(
+ 'id'
+ ,'name'
+ );
+
+ $dict = NewDataDictionary($this->db);
+
+ $error = 0;
+
+ foreach ($tables as $table => $data) {
+ $result = $dict->ExecuteSQLArray(
+ $dict->CreateTableSQL(
+ $table
+ ,$data
+ )
+ ) or die(throw_error($this->db->ErrorMsg(), __FILE__, __LINE__));
+
+ if ($result) {
+ if (isset($indexes[$table]) && count($indexes[$table]) > 0) {
+ foreach ($indexes[$table] as $index) {
+ $extra = false;
+ if (substr($index, 0, 10) == '__unique__') {
+ $index = str_replace('__unique__', '', $index);
+ $extra = array('UNIQUE');
+ }
+ $result = $dict->ExecuteSQLArray(
+ $dict->CreateIndexSQL(
+ $index
+ ,$table
+ ,'`'.$index.'`'
+ ,$extra
+ )
+ ) or die(throw_error($this->db->ErrorMsg(), __FILE__, __LINE__));
+ if (!$result) {
+ $error++;
+ }
+ }
+ }
+ }
+ else {
+ $error++;
+ }
+ }
+ if ($error == 0) {
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ /**
+ * Print error, or not, depending on debug setting.
+ *
+ * @return string
+ */
+ function throw_error($msg, $file, $line) {
+ if ($this->debug) {
+ return $msg.' in '.$file.' on line: '.$line;
+ }
+ }
+}
+
+
+?>
66 phptagengine.config.inc.php
@@ -0,0 +1,66 @@
+<?php
+
+// Copyright (c) 2006-2007 Alex King. All rights reserved.
+// http://alexking.org/projects/php-tag-engine
+//
+// Released under the LGPL license
+// http://www.opensource.org/licenses/lgpl-license.php
+//
+// **********************************************************************
+// 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.
+// **********************************************************************
+
+/**
+ * The PHP Tag Engine configuration settings
+ *
+ * @package phptagengine
+ */
+
+if (__FILE__ == basename($_SERVER['SCRIPT_NAME'])) { die(); }
+
+// -- instantiate PHP Tag Engine
+
+$pte = new phptagengine;
+
+if (!isset($pte_js) || !$pte_js) { // no database needed for JS
+
+// -- database info
+ $pte->db = $db; // where $db is your ADOdb instance
+ $pte->table_tags = $table_prefix.'tags';
+ $pte->table_tag_names = $table_prefix.'tag_names';
+ $pte->table_users = $table_prefix.'users';
+ $pte->table_users_name = 'name';
+
+}
+
+// -- misc
+
+$pte->base_url = 'phptagengine/';
+$pte->ajax_handler = 'u_rail.php';
+$pte->tag_browse_url = 'index.php?screen=tags&tag=<tag>&type=<type>';
+
+// -- default values (optional)
+
+$pte->default_type = '';
+$pte->default_user = '';
+
+// -- language file
+
+require('languages/english.inc.php');
+
+// -- buttons
+
+$pte->edit_button_display = 'text';
+$pte->edit_button_image_url = 'images/icon_edit_tag_small.gif';
+
+$pte->show_remove_links = false;
+$pte->delete_button_display = 'text';
+$pte->delete_button_image_url = 'images/icon_delete_tag.gif';
+
+// -- Yahoo! Auto-Complete
+
+$pte->yac = true;
+
+?>
103 phptagengine.css
@@ -0,0 +1,103 @@
+/*
+// Copyright (c) 2006-2007 Alex King. All rights reserved.
+// http://alexking.org/projects/php-tag-engine
+//
+// Released under the LGPL license
+// http://www.opensource.org/licenses/lgpl-license.php
+//
+// **********************************************************************
+// 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.
+// **********************************************************************
+*/
+
+/**
+ * Default CSS for PHP Tag Engine
+ */
+
+div.pte_tags_form {
+ font: 10px Verdana, sans-serif;
+ margin: 4px 0;
+ padding: 0;
+}
+div.pte_tags_form label
+, div.pte_tags_form ul.pte_tags_list
+, div.pte_tags_form ul.pte_tags_list li
+, div.pte_tags_form fieldset.pte_tags_edit
+, div.pte_tags_form fieldset.pte_tags_edit div.pte_edit_wrapper
+, div.pte_tags_form span.pte_tags_saving
+, div.pte_tags_form img.pte_button_edit
+, div.pte_tags_form img.pte_button_delete {
+ display: inline;
+ vertical-align: middle;
+}
+div.pte_tags_form ul.pte_tags_list {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+div.pte_tags_form ul.pte_tags_list li {
+ color: #666;
+ margin: 0;
+ padding: 0 3px 0 0;
+}
+div.pte_tags_form ul.pte_tags_list li.pte_edit a {
+ color: #444;
+}
+div.pte_tags_form span.pte_tags_saving {
+ color: #666;
+}
+div.pte_tags_form fieldset.pte_tags_edit, div.pte_tags_form span.pte_tags_saving {
+ display: none;
+}
+div.pte_tags_form fieldset.pte_tags_edit {
+ border: 0;
+ margin: 0;
+ padding: 0;
+}
+div.pte_tags_form img.pte_button_edit, div.pte_tags_form img.pte_button_delete {
+ border: 0;
+}
+ul.pte_tags_list_all {
+ border: 1px solid #ccc;
+ border-bottom: 0;
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+ul.pte_tags_list_all li {
+ border-bottom: 1px solid #ccc;
+ margin: 0;
+ padding: 3px 4px;
+}
+ul.pte_tags_list_all li a {
+ vertical-align: middle;
+}
+ul.pte_tags_list_all li input.pte_image_button {
+ float: right;
+ margin-left: 4px;
+ vertical-align: middle;
+}
+div.yac_list {
+ position: absolute;
+ width: 450px;
+}
+div.yac_list ul {
+ background: #fff;
+ border: 1px solid #ccc;
+ border-top: 0;
+ color: #000;
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+div.yac_list ul li {
+ cursor: pointer;
+ cursor: hand;
+ margin: 0;
+ padding: 2px;
+}
+div.yac_list ul li.yui-ac-highlight {
+ background: #D9E7FF;
+}
32 phptagengine.inc.php
@@ -0,0 +1,32 @@
+<?php
+
+// Copyright (c) 2006-2007 Alex King. All rights reserved.
+// http://alexking.org/projects/php-tag-engine
+//
+// Released under the LGPL license
+// http://www.opensource.org/licenses/lgpl-license.php
+//
+// **********************************************************************
+// 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.
+// **********************************************************************
+
+/**
+ * The main include for PHP Tag Engine - this includes the other files
+ *
+ * It should be include()ed in the file specified as the
+ * @link phptagengine::$ajax_handler
+ *
+ * @package phptagengine
+ */
+
+if (__FILE__ == basename($_SERVER['SCRIPT_NAME'])) { die(); }
+
+require_once('phptagengine.class.inc.php');
+require_once('phptagengine.config.inc.php');
+
+// handle AJAX Requests
+$pte->ajax_engine();
+
+?>
355 phptagengine.js.php
@@ -0,0 +1,355 @@
+<?php
+
+// Copyright (c) 2006-2007 Alex King. All rights reserved.
+// http://alexking.org/projects/php-tag-engine
+//
+// Released under the LGPL license
+// http://www.opensource.org/licenses/lgpl-license.php
+//
+// **********************************************************************
+// 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.
+// **********************************************************************
+
+ini_set('display_errors', '0');
+ini_set('error_reporting', E_PARSE);
+
+/**
+ * The JavaScript for PHP Tag Engine
+ *
+ * Includes the AJAX code and sets the strings for localization
+ *
+ * @package phptagengine
+ */
+
+$pte_js = true;
+
+require_once('phptagengine.class.inc.php');
+require_once('phptagengine.config.inc.php');
+
+if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
+ $if_modified_since = preg_replace('/;.*$/', '', $_SERVER['HTTP_IF_MODIFIED_SINCE']);
+
+ $config = filemtime('phptagengine.config.inc.php');
+ $language = filemtime('languages/'.$pte->language.'.inc.php');
+ if ($config > $language) {
+ $mtime = $config;
+ }
+ else {
+ $mtime = $language;
+ }
+ $gmdate_mod = gmdate('D, d M Y H:i:s', $mtime) . ' GMT';
+
+ if ($if_modified_since == $gmdate_mod) {
+ header("HTTP/1.0 304 Not Modified");
+ exit;
+ }
+ header("Last-Modified: $gmdate_mod");
+}
+
+@ob_start('ob_gzhandler');
+
+header("Content-type: text/javascript");
+header('Expires: '.gmdate('D, d M Y H:i:s', time()+24*60*60) . ' GMT');
+
+if ($pte->yac) {
+ foreach ($pte->yac_files as $file) {
+ readfile('yui/'.$file);
+ }
+}
+
+?>
+
+// Copyright (c) 2006 Alex King. All rights reserved.
+// http://www.alexking.org/software/phptagengine/
+//
+// Released under the LGPL license
+// http://www.opensource.org/licenses/lgpl-license.php
+//
+// **********************************************************************
+// 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.
+// **********************************************************************
+
+var pte = {
+ req : false
+ , ajax_handler : '<?php print($pte->ajax_handler); ?>'
+ , tag_browse_url : '<?php print($pte->tag_browse_url); ?>'
+ , strings : {}
+ , show_remove_links : <?php if ($pte->show_remove_links) { print('true'); } else { print('false'); } ?>
+ , edit_button_display : '<?php print($pte->edit_button_display); ?>'
+ , edit_button_image_url : '<?php print($pte->edit_button_image_url); ?>'
+ , delete_button_display : '<?php print($pte->delete_button_display); ?>'
+ , delete_button_image_url : '<?php print($pte->delete_button_image_url); ?>'
+}
+
+<?php
+// put language strings into the JS scope
+foreach ($pte->strings as $k => $v) {
+ print('pte.strings["'.$k.'"] = "'.$pte->slash($v).'";'."\n");
+}
+?>
+
+pte.pipe = function(url, handler, content) {
+ if (typeof content == 'undefined') {
+ content = '';
+ }
+ pte.req = false;
+ if (window.XMLHttpRequest) {
+// branch for native XMLHttpRequest object
+ try {
+ pte.req = new XMLHttpRequest();
+ }
+ catch(e) {
+ pte.req = false;
+ }
+ }
+ else if (window.ActiveXObject) {
+// branch for IE/Windows ActiveX version
+ try {
+ pte.req = new ActiveXObject("Msxml2.XMLHTTP");
+ }
+ catch(e) {
+ try {
+ pte.req = new ActiveXObject("Microsoft.XMLHTTP");
+ }
+ catch(e) {
+ pte.req = false;
+ }
+ }
+ }
+ if (pte.req) {
+// debug
+// prompt('piping:', url);
+ pte.req.onreadystatechange = handler;
+ pte.req.open("GET", url, true);
+ pte.req.send(content);
+ }
+}
+
+pte.save_tags = function(user, item, tags, type) {
+ pte.item_tag_view(item, 'saving');
+ var url = this.ajax_handler + '?pte_action=save_tags'
+ + '&user=' + encodeURIComponent(user)
+ + '&item=' + encodeURIComponent(item)
+ + '&tags=' + encodeURIComponent(tags)
+ + '&type=' + encodeURIComponent(type)
+ ;
+ pte.pipe(url, pte.save_tags_handler);
+}
+
+pte.save_tags_handler = function() {
+ if (pte.req.readyState == 4) {
+ if (pte.req.status == 200) {
+ var result = pte.req.responseXML.getElementsByTagName('result')[0];
+ if (result.getAttribute('success') == 'y' && result.getAttribute('action') == 'save_tags') {
+ var tags = result.getElementsByTagName('tags')[0].firstChild.nodeValue;
+ var item = result.getAttribute('item');
+
+ var tags_node = document.getElementById('pte_tags_list_' + item);
+ tags_node.innerHTML = '';
+
+ if (tags == '') {
+ pte.clear_tags_display(item);
+ }
+ else {
+ document.getElementById('pte_tags_edit_field_' + item).value = tags + ' ';
+ if (tags.indexOf(' ') != -1) {
+ tags = tags.split(' ');
+ }
+ else {
+ tags = new Array(tags);
+ }
+ if (tags.length > 0) {
+ var tag_nodes = pte.create_tag_nodes(tags, result);
+ for (var i = 0; i < tag_nodes.length; i++) {
+ tags_node.appendChild(tag_nodes[i]);
+ }
+ }
+ }
+ tags_node.appendChild(pte.create_edit_node(item));
+
+ pte.item_tag_view(item, 'view');
+ }
+ else {
+// TODO
+alert('Error saving tags: ' + pte.req.responseText);
+ }
+ }
+ }
+}
+
+pte.remove_tag = function(item, tag, type) {
+ var url = pte.ajax_handler + '?pte_action=remove_tag&item=' + encodeURIComponent(item)
+ + '&tag=' + encodeURIComponent(tag) + '&type=' + encodeURIComponent(type);
+ pte.pipe(url, pte.remove_tag_handler);
+}
+
+pte.remove_tag_handler = function() {
+ if (pte.req.readyState == 4) {
+ if (pte.req.status == 200) {
+ var result = pte.req.responseXML.getElementsByTagName('result')[0];
+ if (result.getAttribute('success') == 'y' && result.getAttribute('action') == 'remove_tag') {
+ var item = result.getAttribute('item');
+ var tag = result.getElementsByTagName('tag')[0].firstChild.nodeValue;
+ var parent = document.getElementById('pte_tags_list_' + item);
+ var child = document.getElementById('pte_tag_' + item + '_' + tag);
+ parent.removeChild(child);
+ }
+ }
+ }
+}
+
+pte.edit_tag = function(tag) {
+ var url = pte.ajax_handler + '?pte_action=edit_tag&tag=' + encodeURIComponent(tag);
+ pte.pipe(url, pte.edit_tag_handler);
+}
+
+pte.edit_tag_handler = function() {
+ if (pte.req.readyState == 4) {
+ if (pte.req.status == 200) {
+ var result = pte.req.responseXML.getElementsByTagName('result')[0];
+ if (result.getAttribute('success') == 'y' && result.getAttribute('action') == 'edit_tag') {
+ var old_tag = result.getElementsByTagName('old_tag')[0].firstChild.nodeValue;
+ var new_tag = result.getElementsByTagName('new_tag')[0].firstChild.nodeValue;
+ var old_tag_node = document.getElementById('pte_tag_' + old_tag);
+ var new_tag_node = document.getElementById('pte_tag_' + new_tag);
+ if (new_tag_node) {
+ old_tag_node.parentNode.removeChild(old_tag_node);
+ }
+ else if (old_tag_node) {
+ old_tag_node.id = 'pte_tag_' + new_tag;
+ old_tag_node.getElementsByTagName['a'][0].innerHTML = new_tag;
+ }
+ }
+ }
+ }
+}
+
+pte.delete_tag = function(tag) {
+ var url = pte.ajax_handler + '?pte_action=delete_tag&tag=' + encodeURIComponent(tag);
+ pte.pipe(url, pte.delete_tag_handler);
+}
+
+pte.delete_tag_handler = function() {
+ if (pte.req.readyState == 4) {
+ if (pte.req.status == 200) {
+ var result = pte.req.responseXML.getElementsByTagName('result')[0];
+ if (result.getAttribute('success') == 'y' && result.getAttribute('action') == 'delete_tag') {
+ var tag = result.getElementsByTagName('tag')[0].firstChild.nodeValue;
+ var tag_node = document.getElementById('pte_tag_' + tag);
+ if (tag_node) {
+ tag_node.parentNode.removeChild(tag_node);
+ }
+ }
+ }
+ }
+}
+
+pte.create_tag_nodes = function(tags, result) {
+ var tag_nodes = new Array();
+ var item = result.getAttribute('item');
+ var type = result.getAttribute('type');
+ for (var i = 0; i < tags.length; i++) {
+ var tag_link = document.createElement('a');
+ tag_link.innerHTML = tags[i];
+ tag_link.href = pte.get_tag_browse_url(tags[i], result.getAttribute('type'));
+
+ var tag_node = document.createElement('li');
+ tag_node.id = 'pte_tag_' + item + '_' + tags[i];
+ tag_node.appendChild(tag_link);
+ tag_node.appendChild(document.createTextNode(' '));
+
+ if (pte.show_remove_links) {
+ var remove_link = document.createElement('a');
+ remove_link.innerHTML = pte.button_display('delete');
+ remove_link.href = "javascript:void(pte.remove_tag('" + item + "', '" + tags[i] + "', '" + type + "'));";
+ tag_node.appendChild(remove_link);
+ }
+
+ tag_nodes.push(tag_node);
+ }
+ return tag_nodes;
+}
+
+pte.get_tag_browse_url = function(tag, type) {
+ var url = pte.tag_browse_url.replace('<tag>', tag);
+ return url.replace('<type>', type);
+}
+
+pte.item_tag_view = function(item, display) {
+ document.getElementById('pte_tags_list_' + item).style.display = 'none';
+ document.getElementById('pte_tags_edit_' + item).style.display = 'none';
+ document.getElementById('pte_tags_saving_' + item).style.display = 'none';
+ switch (display) {
+ case 'view':
+ display = 'list';
+ case 'edit':
+ case 'saving':
+ document.getElementById('pte_tags_' + display + '_' + item).style.display = 'inline';
+ if (display == 'edit') {
+ document.getElementById('pte_tags_edit_field_' + item).focus();
+ }
+ break;
+ }
+}
+
+pte.clear_tags_display = function(item) {
+// clear view
+ var tags = document.getElementById('pte_tags_list_' + item);
+ tags.innerHTML = '';
+ banner = document.createElement('li');
+ banner.innerHTML = pte.strings['data_none'];
+ tags.appendChild(banner);
+ pte.item_tag_view(item, 'view');
+// clear edit
+ document.getElementById('pte_tags_edit_field_' + item).value = '';
+}
+
+pte.button_display = function(type) {
+ var display = '';
+ switch (type) {
+ case 'edit':
+ var scase = pte.edit_button_display;
+ var url = pte.edit_button_image_url;
+ break;
+ case 'delete':
+ var scase = pte.delete_button_display;
+ var url = pte.delete_button_image_url;
+ break;
+ default:
+ return display;
+ }
+ switch (scase) {
+ case 'text':
+ if (pte.strings['action_' + type + '_text_icon']) {
+ display = pte.strings['action_' + type + '_text_icon'];
+ }
+ else {
+ display = pte.strings['action_' + type];
+ }
+ break;
+ case 'image':
+ display = '<img src="' + url + '" alt="' + pte.slash(pte.strings['action_' + type], '"') + '" class="pte_button_' + type + '" />';
+ break;
+ }
+ return display;
+}
+
+pte.create_edit_node = function(item) {
+ var edit_link = document.createElement('a');
+ edit_link.innerHTML = '[' + pte.button_display('edit') + ']';
+ edit_link.href = "javascript:void(pte.item_tag_view('" + item + "', 'edit'));";
+
+ var edit_node = document.createElement('li');
+ edit_node.className = 'pte_edit';
+ edit_node.appendChild(edit_link);
+
+ return edit_node;
+}
+
+pte.slash = function(str, escape) {
+ return str.replace(escape, '\\' + escape);
+}
184 yui/autocomplete.js
@@ -0,0 +1,184 @@
+/*
+Copyright (c) 2007, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.2.2
+*/
+
+YAHOO.widget.AutoComplete=function(elInput,elContainer,oDataSource,oConfigs){if(elInput&&elContainer&&oDataSource){if(oDataSource instanceof YAHOO.widget.DataSource){this.dataSource=oDataSource;}
+else{return;}
+if(YAHOO.util.Dom.inDocument(elInput)){if(YAHOO.lang.isString(elInput)){this._sName="instance"+YAHOO.widget.AutoComplete._nIndex+" "+elInput;this._oTextbox=document.getElementById(elInput);}
+else{this._sName=(elInput.id)?"instance"+YAHOO.widget.AutoComplete._nIndex+" "+elInput.id:"instance"+YAHOO.widget.AutoComplete._nIndex;this._oTextbox=elInput;}}
+else{return;}
+if(YAHOO.util.Dom.inDocument(elContainer)){if(YAHOO.lang.isString(elContainer)){this._oContainer=document.getElementById(elContainer);}
+else{this._oContainer=elContainer;}
+if(this._oContainer.style.display=="none"){}}
+else{return;}
+if(oConfigs&&(oConfigs.constructor==Object)){for(var sConfig in oConfigs){if(sConfig){this[sConfig]=oConfigs[sConfig];}}}
+this._initContainer();this._initProps();this._initList();this._initContainerHelpers();var oSelf=this;var oTextbox=this._oTextbox;var oContent=this._oContainer._oContent;YAHOO.util.Event.addListener(oTextbox,"keyup",oSelf._onTextboxKeyUp,oSelf);YAHOO.util.Event.addListener(oTextbox,"keydown",oSelf._onTextboxKeyDown,oSelf);YAHOO.util.Event.addListener(oTextbox,"focus",oSelf._onTextboxFocus,oSelf);YAHOO.util.Event.addListener(oTextbox,"blur",oSelf._onTextboxBlur,oSelf);YAHOO.util.Event.addListener(oContent,"mouseover",oSelf._onContainerMouseover,oSelf);YAHOO.util.Event.addListener(oContent,"mouseout",oSelf._onContainerMouseout,oSelf);YAHOO.util.Event.addListener(oContent,"scroll",oSelf._onContainerScroll,oSelf);YAHOO.util.Event.addListener(oContent,"resize",oSelf._onContainerResize,oSelf);if(oTextbox.form){YAHOO.util.Event.addListener(oTextbox.form,"submit",oSelf._onFormSubmit,oSelf);}
+YAHOO.util.Event.addListener(oTextbox,"keypress",oSelf._onTextboxKeyPress,oSelf);this.textboxFocusEvent=new YAHOO.util.CustomEvent("textboxFocus",this);this.textboxKeyEvent=new YAHOO.util.CustomEvent("textboxKey",this);this.dataRequestEvent=new YAHOO.util.CustomEvent("dataRequest",this);this.dataReturnEvent=new YAHOO.util.CustomEvent("dataReturn",this);this.dataErrorEvent=new YAHOO.util.CustomEvent("dataError",this);this.containerExpandEvent=new YAHOO.util.CustomEvent("containerExpand",this);this.typeAheadEvent=new YAHOO.util.CustomEvent("typeAhead",this);this.itemMouseOverEvent=new YAHOO.util.CustomEvent("itemMouseOver",this);this.itemMouseOutEvent=new YAHOO.util.CustomEvent("itemMouseOut",this);this.itemArrowToEvent=new YAHOO.util.CustomEvent("itemArrowTo",this);this.itemArrowFromEvent=new YAHOO.util.CustomEvent("itemArrowFrom",this);this.itemSelectEvent=new YAHOO.util.CustomEvent("itemSelect",this);this.unmatchedItemSelectEvent=new YAHOO.util.CustomEvent("unmatchedItemSelect",this);this.selectionEnforceEvent=new YAHOO.util.CustomEvent("selectionEnforce",this);this.containerCollapseEvent=new YAHOO.util.CustomEvent("containerCollapse",this);this.textboxBlurEvent=new YAHOO.util.CustomEvent("textboxBlur",this);oTextbox.setAttribute("autocomplete","off");YAHOO.widget.AutoComplete._nIndex++;}
+else{}};YAHOO.widget.AutoComplete.prototype.dataSource=null;YAHOO.widget.AutoComplete.prototype.minQueryLength=1;YAHOO.widget.AutoComplete.prototype.maxResultsDisplayed=10;YAHOO.widget.AutoComplete.prototype.queryDelay=0.5;YAHOO.widget.AutoComplete.prototype.highlightClassName="yui-ac-highlight";YAHOO.widget.AutoComplete.prototype.prehighlightClassName=null;YAHOO.widget.AutoComplete.prototype.delimChar=null;YAHOO.widget.AutoComplete.prototype.autoHighlight=true;YAHOO.widget.AutoComplete.prototype.typeAhead=false;YAHOO.widget.AutoComplete.prototype.animHoriz=false;YAHOO.widget.AutoComplete.prototype.animVert=true;YAHOO.widget.AutoComplete.prototype.animSpeed=0.3;YAHOO.widget.AutoComplete.prototype.forceSelection=false;YAHOO.widget.AutoComplete.prototype.allowBrowserAutocomplete=true;YAHOO.widget.AutoComplete.prototype.alwaysShowContainer=false;YAHOO.widget.AutoComplete.prototype.useIFrame=false;YAHOO.widget.AutoComplete.prototype.useShadow=false;YAHOO.widget.AutoComplete.prototype.toString=function(){return"AutoComplete "+this._sName;};YAHOO.widget.AutoComplete.prototype.isContainerOpen=function(){return this._bContainerOpen;};YAHOO.widget.AutoComplete.prototype.getListItems=function(){return this._aListItems;};YAHOO.widget.AutoComplete.prototype.getListItemData=function(oListItem){if(oListItem._oResultData){return oListItem._oResultData;}
+else{return false;}};YAHOO.widget.AutoComplete.prototype.setHeader=function(sHeader){if(sHeader){if(this._oContainer._oContent._oHeader){this._oContainer._oContent._oHeader.innerHTML=sHeader;this._oContainer._oContent._oHeader.style.display="block";}}
+else{this._oContainer._oContent._oHeader.innerHTML="";this._oContainer._oContent._oHeader.style.display="none";}};YAHOO.widget.AutoComplete.prototype.setFooter=function(sFooter){if(sFooter){if(this._oContainer._oContent._oFooter){this._oContainer._oContent._oFooter.innerHTML=sFooter;this._oContainer._oContent._oFooter.style.display="block";}}
+else{this._oContainer._oContent._oFooter.innerHTML="";this._oContainer._oContent._oFooter.style.display="none";}};YAHOO.widget.AutoComplete.prototype.setBody=function(sBody){if(sBody){if(this._oContainer._oContent._oBody){this._oContainer._oContent._oBody.innerHTML=sBody;this._oContainer._oContent._oBody.style.display="block";this._oContainer._oContent.style.display="block";}}
+else{this._oContainer._oContent._oBody.innerHTML="";this._oContainer._oContent.style.display="none";}
+this._maxResultsDisplayed=0;};YAHOO.widget.AutoComplete.prototype.formatResult=function(oResultItem,sQuery){var sResult=oResultItem[0];if(sResult){return sResult;}
+else{return"";}};YAHOO.widget.AutoComplete.prototype.doBeforeExpandContainer=function(oResultItem,sQuery){return true;};YAHOO.widget.AutoComplete.prototype.sendQuery=function(sQuery){this._sendQuery(sQuery);};YAHOO.widget.AutoComplete.prototype.textboxFocusEvent=null;YAHOO.widget.AutoComplete.prototype.textboxKeyEvent=null;YAHOO.widget.AutoComplete.prototype.dataRequestEvent=null;YAHOO.widget.AutoComplete.prototype.dataReturnEvent=null;YAHOO.widget.AutoComplete.prototype.dataErrorEvent=null;YAHOO.widget.AutoComplete.prototype.containerExpandEvent=null;YAHOO.widget.AutoComplete.prototype.typeAheadEvent=null;YAHOO.widget.AutoComplete.prototype.itemMouseOverEvent=null;YAHOO.widget.AutoComplete.prototype.itemMouseOutEvent=null;YAHOO.widget.AutoComplete.prototype.itemArrowToEvent=null;YAHOO.widget.AutoComplete.prototype.itemArrowFromEvent=null;YAHOO.widget.AutoComplete.prototype.itemSelectEvent=null;YAHOO.widget.AutoComplete.prototype.unmatchedItemSelectEvent=null;YAHOO.widget.AutoComplete.prototype.selectionEnforceEvent=null;YAHOO.widget.AutoComplete.prototype.containerCollapseEvent=null;YAHOO.widget.AutoComplete.prototype.textboxBlurEvent=null;YAHOO.widget.AutoComplete._nIndex=0;YAHOO.widget.AutoComplete.prototype._sName=null;YAHOO.widget.AutoComplete.prototype._oTextbox=null;YAHOO.widget.AutoComplete.prototype._bFocused=true;YAHOO.widget.AutoComplete.prototype._oAnim=null;YAHOO.widget.AutoComplete.prototype._oContainer=null;YAHOO.widget.AutoComplete.prototype._bContainerOpen=false;YAHOO.widget.AutoComplete.prototype._bOverContainer=false;YAHOO.widget.AutoComplete.prototype._aListItems=null;YAHOO.widget.AutoComplete.prototype._nDisplayedItems=0;YAHOO.widget.AutoComplete.prototype._maxResultsDisplayed=0;YAHOO.widget.AutoComplete.prototype._sCurQuery=null;YAHOO.widget.AutoComplete.prototype._sSavedQuery=null;YAHOO.widget.AutoComplete.prototype._oCurItem=null;YAHOO.widget.AutoComplete.prototype._bItemSelected=false;YAHOO.widget.AutoComplete.prototype._nKeyCode=null;YAHOO.widget.AutoComplete.prototype._nDelayID=-1;YAHOO.widget.AutoComplete.prototype._iFrameSrc="javascript:false;";YAHOO.widget.AutoComplete.prototype._queryInterval=null;YAHOO.widget.AutoComplete.prototype._sLastTextboxValue=null;YAHOO.widget.AutoComplete.prototype._initProps=function(){var minQueryLength=this.minQueryLength;if(!YAHOO.lang.isNumber(minQueryLength)||(minQueryLength<1)){minQueryLength=1;}
+var maxResultsDisplayed=this.maxResultsDisplayed;if(!YAHOO.lang.isNumber(maxResultsDisplayed)||(maxResultsDisplayed<1)){maxResultsDisplayed=10;}
+var queryDelay=this.queryDelay;if(!YAHOO.lang.isNumber(queryDelay)||(queryDelay<0)){queryDelay=0.5;}
+var delimChar=this.delimChar;if(YAHOO.lang.isString(delimChar)){delimChar=[delimChar];}
+else if(!YAHOO.lang.isArray(delimChar)){delimChar=null;}
+var animSpeed=this.animSpeed;if((this.animHoriz||this.animVert)&&YAHOO.util.Anim){if(!YAHOO.lang.isNumber(animSpeed)||(animSpeed<0)){animSpeed=0.3;}
+if(!this._oAnim){oAnim=new YAHOO.util.Anim(this._oContainer._oContent,{},animSpeed);this._oAnim=oAnim;}
+else{this._oAnim.duration=animSpeed;}}
+if(this.forceSelection&&delimChar){}};YAHOO.widget.AutoComplete.prototype._initContainerHelpers=function(){if(this.useShadow&&!this._oContainer._oShadow){var oShadow=document.createElement("div");oShadow.className="yui-ac-shadow";this._oContainer._oShadow=this._oContainer.appendChild(oShadow);}
+if(this.useIFrame&&!this._oContainer._oIFrame){var oIFrame=document.createElement("iframe");oIFrame.src=this._iFrameSrc;oIFrame.frameBorder=0;oIFrame.scrolling="no";oIFrame.style.position="absolute";oIFrame.style.width="100%";oIFrame.style.height="100%";oIFrame.tabIndex=-1;this._oContainer._oIFrame=this._oContainer.appendChild(oIFrame);}};YAHOO.widget.AutoComplete.prototype._initContainer=function(){if(!this._oContainer._oContent){var oContent=document.createElement("div");oContent.className="yui-ac-content";oContent.style.display="none";this._oContainer._oContent=this._oContainer.appendChild(oContent);var oHeader=document.createElement("div");oHeader.className="yui-ac-hd";oHeader.style.display="none";this._oContainer._oContent._oHeader=this._oContainer._oContent.appendChild(oHeader);var oBody=document.createElement("div");oBody.className="yui-ac-bd";this._oContainer._oContent._oBody=this._oContainer._oContent.appendChild(oBody);var oFooter=document.createElement("div");oFooter.className="yui-ac-ft";oFooter.style.display="none";this._oContainer._oContent._oFooter=this._oContainer._oContent.appendChild(oFooter);}
+else{}};YAHOO.widget.AutoComplete.prototype._initList=function(){this._aListItems=[];while(this._oContainer._oContent._oBody.hasChildNodes()){var oldListItems=this.getListItems();if(oldListItems){for(var oldi=oldListItems.length-1;oldi>=0;oldi--){oldListItems[oldi]=null;}}