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

class TableEditor {
	/*
	
		Written by: Andrew Sullivan 18 MAY 2006
		Advanced Ajax Table Edit v0.8:
			An ajax enabled data table editor.  Essentially, you can edit
			a table similar to how you would a spread sheet.
			
		TODO:
			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

	*/
	
	var $forms = array();
	
	function SetForm($name, $type = "text", $values = array()) {
		$html = '
			<div style="padding: 3px 3px 3px 3px; text-align: center;">
			<form name="edit" id="edit">
		';
		
		switch ($type) {
			case 'select':
				$html .= '
					<select name="newvalue" id="newvalue" size="1">';
				foreach ($values as $key => $value) {
					$html .= '<option value="' . $key . '">' . $value . '</option>';
				}
				$html .= '
					</select>';
				break;
			case 'area';
				$html .= '<textarea name="newvalue" id="newvalue" cols="25" rows="8"></textarea>';
				break;
			case 'radio':
				foreach ($values as $key => $value) {
					$html .= '<input type="radio" name="newvalue" id="newvalue" value="' . $key . '">' . $value;
				}
				break;
			case 'check':
				foreach ($values as $key => $value) {
					$html .= '<input type="checkbox" name="newvalue" id="newvalue" value="' . $key . '">' . $value;
				}
				break;
			case 'text':
			default:
				$html .= '<input type="text" name="newvalue" id="newvalue" size="15">';
				break;
		}
		
		$html .= '
			<br /><br />
			<input type="button" value="Save" onclick="saveCell(this.parent)">&nbsp;&nbsp;
			<input type="button" value="Cancel" onclick="cancelEdit()">
			
			</div>
		';
		
		$this->forms[$name] = $html;
	}
	
	function HandleFormRequest($name) {
		return $this->forms[$name];
	}

	/*	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 for the table */
	
	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 cancelEdit() {
				inprocess = 0;
				oldvalue = "";
				newvalue = "";
				editid = "";
				rowid = "";
				colid = "";
				
				document.getElementById(\'input-main\').style.display = \'none\';
			}
			
			function editCell(rid, cid, cellid, type) {
				if (editid == cellid || inprocess == 1) {
					return false;
				}
				
				inprocess = 1;
				
				if (editid != "") {
					document.getElementById(editid).innerHTML = oldvalue;
				}
				
				editid = cellid;
				rowid = rid;
				colid = cid;
				
				var cell = document.getElementById(cellid);
				oldvalue = cell.innerHTML;
				
				showContent(\'\',\'Please Wait...\');
				
				var index = ajax.length;
					
				ajax[index] = new sack();
				ajax[index].requestFile = "' . $submit . '";
					
				ajax[index].setVar("old", document.getElementById(cellid).innerHTML);
				ajax[index].setVar("FormRequest", "true");
				ajax[index].setVar("type", type);
					
				ajax[index].onCompletion = function(){ showContent(index); };
				ajax[index].runAJAX();

			}
			
			function saveCell() {
				var index = ajax.length;
				var value = "";
				
				nv = document.getElementById(\'newvalue\');
				
				//alert(nv.type);
				
				ajax[index] = new sack();

				if (nv.type == "text" || nv.type == "textarea") {
					value = nv.value;
				} else if (nv.type == "select-one") {
					value = nv[nv.selectedIndex].value;
				} else if (nv.type == "radio") {
					for (i = 0; i < document.forms["edit"].newvalue.length; i++) {
						if (document.forms["edit"].newvalue[i].checked) {
							value = document.forms["edit"].newvalue[i].value;
						}
					}
				} else if (nv.type == "checkbox") {
					var c = 0;
					for (i = 0; i < document.forms["edit"].newvalue.length; i++) {
						if (document.forms["edit"].newvalue[i].checked) {
							ajax[index].setVar(\'new[\' + c + \']\', document.forms["edit"].newvalue[i].value);
							c++;
						}
					}
				}
				
				//alert(value);
				
				showContent(\'\',\'Saving...\');
				
				if (value != "") {
					ajax[index].setVar("new", value);
				}
				
				ajax[index].requestFile = "' . $submit . '";
				ajax[index].setVar("FormSave", "true");
				ajax[index].setVar("rowid", rowid);
				ajax[index].setVar("colid", colid);
				//ajax[index].setVar("new", value);
				
				ajax[index].onCompletion = function(){ showSaved(index); };
				ajax[index].runAJAX();
					
				
			}
			
			function showSaved(index) {
				if (editid != "") {
					var response = ajax[index].response;
					
					//alert(response);
					
					if (response != "") {
						document.getElementById(editid).innerHTML = response;
					} else {
						alert("An error occurred saving your data, or you need to return a value!!");
						document.getElementById(editid).innerHTML = "ERROR!!";
					}
				
					cancelEdit();
				}
			}
			
			function showContent(index, text) {
				main    = document.getElementById( \'input-main\' );
				content = document.getElementById( \'input-content\' );
				
				var my_width  = 0;
				var my_height = 0;
					
				if ( typeof( window.innerWidth ) == \'number\' ) {
					my_width  = window.innerWidth;
					my_height = window.innerHeight;
				} else if ( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
					my_width  = document.documentElement.clientWidth;
					my_height = document.documentElement.clientHeight;
				} else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
					my_width  = document.body.clientWidth;
					my_height = document.body.clientHeight;
				}
			
				divheight = 400;
				divwidth  = 400;
				
				main.style.left = my_width  / 2  - (divwidth / 2 )  + \'px\';
				main.style.top  = my_height / 2 - (divheight / 2 ) + \'px\';
				
				if (!text) {
					var html = ajax[index].response;
				} else {
					var html = text;
				}
				
				content.innerHTML = html;
				
				main.style.position = \'absolute\';
				main.style.display  = \'block\';
				main.style.zIndex   = 99;	
			}
		
			</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) {
				if ($dat != "" && !empty($dat)) {
					if ($dat['input'] == "bypass") {
						$datacols .= "\n\t" . '<td>';
					} else {
						$datacols .= 
							"\n\t" 
							. '<td id="' . $rowid . $colid . '" '
							. 'onClick="editCell(\'' . $rowid . '\','
							. ' \'' . $colid . '\', \'' . $rowid
							. $colid . '\', \'' . $dat['input'] . '\')">';
					}
					$datacols .= $dat['data'];

				} 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;
		}
		
		$div = '
			<div id="input-main" style="display:none;width:400px;text-align:left;">
				<div>
					<div id="input-content" style="overflow-x:auto; border: 1px solid black; background-color: white;"></div>
				</div>
			</div>';
					
		//assemble the table
		$final = $table . $datacols . "</table>" . $div;
		
		//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