Location: PHPKode > scripts > Ajax Table Edit > ajax-table-edit/AjaxTableEditor.class.php
<?php

class TableEditor {

	/*
	
		Written by: Andrew Sullivan 15 MAY 2006
		TableEditor:
			An ajax enabled data table editor.  Essentially, you can edit
			a table similar to how you would a spread sheet.
			
		TODO:
			give the ability to do mouseovers and such to individual cells
			ability to specifiy a js function for data verification
			
		This is free to use and modify as you see fit.  If there is something
		that you think is missing, please let me know, and I'll do what I can
		to add it in.  You don't have to let me know if you decide to use
		this script, but for my own ego, please send me an email:
		hide@address.com

	*/

	/*	set to 0, unless "ShowJS()" has been called */
	var $jsshown = 0;
	
	/*	if you want to return only the data rows, with no table opener, set this to true */
	var $dataonly = false;
	
	/*	to ensure that something has been set for the submit page */
	var $SubmitSet = 0;
	
	/*
		These vars are arrays that contain key => value pairs of attribs for the table.
		For example:
			['class'] => 'evenrow'
			
		Would result in the even rows having the attrib <tr class="evenrow">
		
			['align'] => 'center'
			['valign'] => 'top'
			['style'] => 'background-color: #EEEEEE;'
			
		Would result in <tr align="center" valign="top" style="background-color: #EEEEEE;">
	*/
	//array that contains the table attribs
	var $tattrib = array();
	
	//odd row attribs
	var $oddattrib = array();
	var $odd = "<tr>";
	
	//even row class
	var $evenattrib = array();
	var $even = "<tr>";
	
	//header attribs
	var $headerattrib = array();
	
	/*
		The data contains a multidimensional array that consists of the data for the table.
		It should be in the following format:
			In the primary array, each element should have a key eqaul to the row id and the value is
			a sub array that contains column data.
			
			The column data subarray should have key values that are the column values from the db and the values
			are the data to be displayed.
			
		An example would be:
			[1] => array(
					[Name] => 'Some Guy'
					[Address] => '123 Some Street'
					[Phone] => '123-123-1234'
				)
			[2] => array(
					[Name] => 'Some Girl'
					[Address] => '321 Some Drive'
					[Phone] => '987-987-9876'
				}
			etc.
			
	*/

	var $data = array();
	
	/*	Holds the table html after processing	*/
	
	var $html = "";
	
	/*	
		A simple one dimensional array that contains the values for 
		the <th> in the first row
	*/

	var $headers = array();
	
	/*
		This contains the javascript to create the xmlhttprequest, send the data, and do the other
		ajax js stuff.  This is a copy of the of SACK 1.6 Library, as written by Gregory Wild-Smith,
		and avaialble from Twilight Universe at: http://twilightuniverse.com/resources/code/sack/.
	*/
	
	var $ajaxjs = '
		<script type="text/javascript">
			/* Simple AJAX Code-Kit (SACK) v1.6.1 */
			/* ©2005 Gregory Wild-Smith */
			/* www.twilightuniverse.com */
			/* Software licenced under a modified X11 licence,
			   see documentation or authors website for more details */
			
			function sack(file) {
				this.xmlhttp = null;
			
				this.resetData = function() {
					this.method = "POST";
					this.queryStringSeparator = "?";
					this.argumentSeparator = "&";
					this.URLString = "";
					this.encodeURIString = true;
					this.execute = false;
					this.element = null;
					this.elementObj = null;
					this.requestFile = file;
					this.vars = new Object();
					this.responseStatus = new Array(2);
				};
			
				this.resetFunctions = function() {
					this.onLoading = function() { };
					this.onLoaded = function() { };
					this.onInteractive = function() { };
					this.onCompletion = function() { };
					this.onError = function() { };
					this.onFail = function() { };
				};
			
				this.reset = function() {
					this.resetFunctions();
					this.resetData();
				};
			
				this.createAJAX = function() {
					try {
						this.xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
					} catch (e1) {
						try {
							this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
						} catch (e2) {
							this.xmlhttp = null;
						}
					}
			
					if (! this.xmlhttp) {
						if (typeof XMLHttpRequest != "undefined") {
							this.xmlhttp = new XMLHttpRequest();
						} else {
							this.failed = true;
						}
					}
				};
			
				this.setVar = function(name, value){
					this.vars[name] = Array(value, false);
				};
			
				this.encVar = function(name, value, returnvars) {
					if (true == returnvars) {
						return Array(encodeURIComponent(name), encodeURIComponent(value));
					} else {
						this.vars[encodeURIComponent(name)] = Array(encodeURIComponent(value), true);
					}
				}
			
				this.processURLString = function(string, encode) {
					encoded = encodeURIComponent(this.argumentSeparator);
					regexp = new RegExp(this.argumentSeparator + "|" + encoded);
					varArray = string.split(regexp);
					for (i = 0; i < varArray.length; i++){
						urlVars = varArray[i].split("=");
						if (true == encode){
							this.encVar(urlVars[0], urlVars[1]);
						} else {
							this.setVar(urlVars[0], urlVars[1]);
						}
					}
				}
			
				this.createURLString = function(urlstring) {
					if (this.encodeURIString && this.URLString.length) {
						this.processURLString(this.URLString, true);
					}
			
					if (urlstring) {
						if (this.URLString.length) {
							this.URLString += this.argumentSeparator + urlstring;
						} else {
							this.URLString = urlstring;
						}
					}
			
					// prevents caching of URLString
					this.setVar("rndval", new Date().getTime());
			
					urlstringtemp = new Array();
					for (key in this.vars) {
						if (false == this.vars[key][1] && true == this.encodeURIString) {
							encoded = this.encVar(key, this.vars[key][0], true);
							delete this.vars[key];
							this.vars[encoded[0]] = Array(encoded[1], true);
							key = encoded[0];
						}
			
						urlstringtemp[urlstringtemp.length] = key + "=" + this.vars[key][0];
					}
					if (urlstring){
						this.URLString += this.argumentSeparator + urlstringtemp.join(this.argumentSeparator);
					} else {
						this.URLString += urlstringtemp.join(this.argumentSeparator);
					}
				}
			
				this.runResponse = function() {
					eval(this.response);
				}
			
				this.runAJAX = function(urlstring) {
					if (this.failed) {
						this.onFail();
					} else {
						this.createURLString(urlstring);
						if (this.element) {
							this.elementObj = document.getElementById(this.element);
						}
						if (this.xmlhttp) {
							var self = this;
							if (this.method == "GET") {
								totalurlstring = this.requestFile + this.queryStringSeparator + this.URLString;
								this.xmlhttp.open(this.method, totalurlstring, true);
							} else {
								this.xmlhttp.open(this.method, this.requestFile, true);
								try {
									this.xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
								} catch (e) { }
							}
			
							this.xmlhttp.onreadystatechange = function() {
								switch (self.xmlhttp.readyState) {
									case 1:
										self.onLoading();
										break;
									case 2:
										self.onLoaded();
										break;
			
									case 3:
										self.onInteractive();
										break;
									case 4:
										self.response = self.xmlhttp.responseText;
										self.responseXML = self.xmlhttp.responseXML;
										self.responseStatus[0] = self.xmlhttp.status;
										self.responseStatus[1] = self.xmlhttp.statusText;
			
										if (self.execute) {
											self.runResponse();
										}
			
										if (self.elementObj) {
											elemNodeName = self.elementObj.nodeName;
											elemNodeName.toLowerCase();
											if (elemNodeName == "input"
											|| elemNodeName == "select"
											|| elemNodeName == "option"
											|| elemNodeName == "textarea") {
												self.elementObj.value = self.response;
											} else {
												self.elementObj.innerHTML = self.response;
											}
										}
										if (self.responseStatus[0] == "200") {
											self.onCompletion();
										} else {
											self.onError();
										}
			
										self.URLString = "";
										break;
								}
							};
			
							this.xmlhttp.send(this.URLString);
						}
					}
				};
			
				this.reset();
				this.createAJAX();
			}
			</script>
			';
	
	/*
		This is the js to do the main functions for editing the cells.  There are three
		main functions"
			editCell(rowid, colid, cellid)
				this is called when the cell to edit is clicked (onClick), the function
				is passed the rowid from the database (1, 2, 3 etc), the colid (Name, Address, etc)
				and the unique cellid to identify the cell from all the others
				
			saveCell(n)
				this is called when the user clicks off the edited cell (using onBlur).
				It takes the edited text, passes it, along with the row and col information
				to the server where it gets saved.
				
			showSaved(index)
				called at the end to change the contents of the changed cell to the new value
	*/
	
	var $editjs;
	
	function SetSubmit($submit = "") {
		if ($submit == "") {
			$submit = $_SERVER['PHP_SELF'];
		}
		
		$this->editjs = '
		<script type="text/javascript">
			var inprocess = 0;
			var oldvalue = "";
			var newvalue = "";
			var editid = "";
			var rowid = "";
			var colid = "";
			var ajax = new Array();
			
			function editCell(rid, cid, cellid) {
				if (editid == cellid || inprocess == 1) {
					return false;
				}
				
				if (editid != "") {
					document.getElementById(editid).innerHTML = oldvalue;
				}
				
				editid = cellid;
				rowid = rid;
				colid = cid;
				
				var cell = document.getElementById(cellid);
				oldvalue = cell.innerHTML;
		
				var editContent = "<input type=\'text\' id=\'editCell\' size=\'15\' value=\'" + oldvalue + "\' onblur=\'saveCell(this);\'>";
		
				cell.innerHTML = editContent;
				document.getElementById(\'editCell\').focus();
			}
			
			function saveCell(n) {
				newvalue = n.value;
				
				
				//alert(newvalue + "\n" + oldvalue);
				
				if (newvalue == oldvalue) {
					document.getElementById(editid).innerHTML = "";
					document.getElementById(editid).innerHTML = oldvalue;
					oldvalue = "";
					newvalue = "";
					editid = "";
					rowid = "";
					colid = "";
					inprocess = 0;
				} else {
					inprocess = 1;
					document.getElementById(editid).innerHTML = "Saving...";
					var index = ajax.length;
					
					ajax[index] = new sack();
					ajax[index].requestFile = "' . $submit . '";
					
					ajax[index].setVar("rowid", rowid);
					ajax[index].setVar("colid", colid);
					ajax[index].setVar("new", newvalue);
					
					ajax[index].onCompletion = function(){ showSaved(index); };
					ajax[index].runAJAX();
				}
			}
			
			function showSaved(index) {
				if (editid != "") {
					var response = ajax[index].response;
					
					//alert(response);
					
					if (response == "true" || response == "") {
						document.getElementById(editid).innerHTML = newvalue;
					} else {
						alert("An error occurred saving your data!!");
						document.getElementById(editid).innerHTML = oldvalue;
					}
				
					oldvalue = "";
					newvalue = "";
					editid = "";
					rowid = "";
					colid = "";
					ajax[index] = "";
					inprocess = 0;
				}
			}
		
			</script>
		';
		
		$this->SubmitSet = 1;
		
	}
	
	//Set to true to return only the data rows for the table
	//useful if you are creating your own table headers and such.
	function ReturnDataOnly($ret) {
		if ($ret == false || $ret == true) {
			$this->dataonly = $ret;
		} else {
			$this->dataonly = false;
		}
	}
	
	function SetEvenRowAttribs($attrib) {
		$this->evenattrib = $attrib;
		$this->evenTR();
	}
	
	function SetOddRowAttribs($attrib) {
		$this->oddattrib = $attrib;
		$this->oddTR();
	}
	
	function SetTableAttribs($attrib) {
		$this->tattrib = $attrib;
	}
	
	//the preferred method for giving the class it's data.
	function SetData($data) {
		$this->data = $data;
	}
	
	function SetHeaders($headers) {
		$this->headers = $headers;
	}
	
	function SetHeaderAttribs($attrib) {
		$this->headerattrib = $attrib;
	}
	
	function oddTR() {
		$html = "<tr";
		
		foreach ($this->oddattrib as $key => $value) {
			$html .= " $key=\"$value\"";
		}
		
		$html .= ">";
		
		$this->odd = $html;
	}
	
	function evenTR() {
		$html = "<tr";
		
		foreach ($this->evenattrib as $key => $value) {
			$html .= " $key=\"$value\"";
		}
		
		$html .= ">";
		
		$this->even = $html;
	}
	
	function ShowJS() {
		if ($this->jsshown == 0) {
			if ($this->SubmitSet == 0) {
				$this->$SubmitSet();
			}
			$this->jsshown = 1;
			return $this->ajaxjs . $this->editjs;
		} else {
			return;
		}
	}
	
	function GenerateTable($data = array()) {
		$cols = 0;
		
		//using SetData will over write this, but this will not over write
		//data that was set using SetData.
		//$instance->SetData($...) is the preferred method.
		if ((empty($this->data) || !isset($this->data)) && !empty($data)) {
			$this->SetData($data);
		}
		
		//build the table opener
		$table = "\n<table";
		foreach ($this->tattrib as $key => $value) {
			$table .= " $key=\"$value\"";
		}
		$table .= ">";
		
		//build the data portion of the table
		$i = 0;
		$datacols = "";
		foreach ($this->data as $rowid => $content) {
			if ($i % 2 == 0) {
				$datacols .= "\n" . $this->even;
			} else {
				$datacols .= "\n" . $this->odd;
			}
			
			if (count($content) > $cols) {
				$cols = count($content);
			}
			
			foreach ($content as $colid => $dat) {
				$datacols .= "\n\t" . '<td id="' . $rowid . $colid . '" onClick="editCell(\'' . $rowid . '\', \'' . $colid . '\', \'' . $rowid . $colid . '\')">';
				if ($dat != "") {
					$datacols .= $dat;
				} else {
					$datacols .= "&nbsp;";
				}
				$datacols .= "</td>";
			}
			
			$datacols .= "\n</tr>";
			$i++;
		}
		
		if ($this->dataonly === true) {
			return $datacols;
		}
		
		//build the header section...this comes after the data because we need to 
		//know the total number of columns
		if (!empty($this->headers) && $cols > 0) {
			$c = 0;
			$head = "\n<tr";
			if (!empty($this->headerattrib)) {
				foreach ($this->headerattrib as $key => $value) {
					$head .= " $key=\"$value\"";
				}
			}
			$head .= ">\n";
			foreach ($this->headers as $x) {
				$head .= "\t<th>$x</th>\n";
				$c++;
			}
			
			if ($c < $cols) {
				while ($c < $cols) {
					$head .= "\t<th>&nbsp;</th>\n";
					$c++;
				}
			}
			
			$head .= "\n</tr>";
			
			$table .= $head;
		}
		
		//assemble the table
		$final = $table . $datacols . "</table>";
		
		//determine if we need to include the js with the table
		if ($this->jsshown == 1) {
			return $final;
		} else {
			return $this->ShowJS() . $final;
		}
	}	
}
Return current item: Ajax Table Edit