605 lines
18 KiB
JavaScript
605 lines
18 KiB
JavaScript
|
var xmlHttp
|
||
|
|
||
|
/*function getForm(str)
|
||
|
{
|
||
|
xmlHttp=GetXmlHttpObject()
|
||
|
if (xmlHttp==null)
|
||
|
{
|
||
|
alert ("Browser does not support HTTP Request")
|
||
|
return
|
||
|
}
|
||
|
var url="/index.php?task=ajax"
|
||
|
url=url+"&type="+str
|
||
|
if(getForm.arguments[1]!=null)
|
||
|
{
|
||
|
url=url+"&id="+getForm.arguments[1]
|
||
|
}
|
||
|
xmlHttp.open("GET",url,true)
|
||
|
xmlHttp.onreadystatechange=stateChanged
|
||
|
xmlHttp.send(null);
|
||
|
}
|
||
|
|
||
|
function stateChanged()
|
||
|
{
|
||
|
if (xmlHttp.readyState==4 || xmlHttp.readyState=="complete")
|
||
|
{
|
||
|
response = xmlHttp.responseText;
|
||
|
}
|
||
|
}
|
||
|
*/
|
||
|
function GetXmlHttpObject()
|
||
|
{
|
||
|
var xmlHttp=null;
|
||
|
try
|
||
|
{
|
||
|
// Firefox, Opera 8.0+, Safari
|
||
|
xmlHttp=new XMLHttpRequest();
|
||
|
}
|
||
|
catch (e)
|
||
|
{
|
||
|
//Internet Explorer
|
||
|
try
|
||
|
{
|
||
|
xmlHttp=new ActiveXObject("Msxml2.XMLHTTP");
|
||
|
}
|
||
|
catch (e)
|
||
|
{
|
||
|
xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
|
||
|
}
|
||
|
}
|
||
|
return xmlHttp;
|
||
|
}
|
||
|
/**
|
||
|
* Class: InlineEditor
|
||
|
*
|
||
|
* Version 0.1 (pending)
|
||
|
*
|
||
|
* License: Public Domain
|
||
|
*
|
||
|
*
|
||
|
* User-overridable functions:
|
||
|
* More documentation further down in code.
|
||
|
*
|
||
|
* InlineEditor.customEditor = function( theElement ) { ...
|
||
|
* InlineEditor.editorValue = fuction( theEditor ) { ...
|
||
|
* InlineEditor.elementValue = function( theElement ) { ...
|
||
|
* InlineEditor.elementChanged = function( theElement, oldVal, newVal ) { ...
|
||
|
*
|
||
|
*
|
||
|
* Key CSS class names:
|
||
|
* editable: Tables with this class will have their 'td' cells made editable.
|
||
|
* uneditable: At event time, cells with this class will NOT be editable.
|
||
|
* editing: When editing, the 'td' element will have this class.
|
||
|
*
|
||
|
*
|
||
|
* Useful utility functions:
|
||
|
*
|
||
|
* InlineEditor.addClass( element, classname )
|
||
|
* Adds classname to the 'class' attribute of the element.
|
||
|
*
|
||
|
* InlineEditor.removeClass( element, classname )
|
||
|
* Removes classname from the 'class' attribute of the element.
|
||
|
*
|
||
|
* InlineEditor.checkClass( element, classname )
|
||
|
* True if class attribute of the element contains classname, false otherwise.
|
||
|
*
|
||
|
* InlineEditor.swapClass( element, classname1, classname2 )
|
||
|
* Replaces classname1 with classname2 in class attribute of the element.
|
||
|
*
|
||
|
* InlineEditor.columnNumer( cell )
|
||
|
* Returns column number of cell (zero index) or -1 if there are problems.
|
||
|
*
|
||
|
* InlineEditor.rowNumer( cell )
|
||
|
* Returns row number of cell (zero index) or -1 if there are problems.
|
||
|
*
|
||
|
* InlineEditor.rowID( cell )
|
||
|
* Returns row ID, useful if you use that to tie to a database primary key.
|
||
|
*
|
||
|
*
|
||
|
* Change Log:
|
||
|
*
|
||
|
* v0.1.1 - More reliable window.onload event adding. Added customEditor
|
||
|
* extensible function.
|
||
|
* v0.1 - Initial release.
|
||
|
*
|
||
|
*
|
||
|
* Author:
|
||
|
*
|
||
|
* Robert Harder
|
||
|
* rharder # users,sf,net
|
||
|
*/
|
||
|
|
||
|
/** Global var to test for IE */
|
||
|
var inlineeditor_isIE = ( navigator.userAgent.toLowerCase().search( 'msie' ) != -1 && navigator.userAgent.toLowerCase().search( 'opera' ) == -1 ) ? true : false;
|
||
|
|
||
|
var InlineEditor = {
|
||
|
|
||
|
/* ******** F U N C T I O N S Y O U M I G H T C A L L ******** */
|
||
|
|
||
|
|
||
|
alreadyInited : false, // In case we get called twice.
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
* This should be called automatically when the page loads,
|
||
|
* but if you also are setting up a function to run on
|
||
|
* window.onload then this might get bumped out of position.
|
||
|
* If that's the case, then make sure you call InlineEditor.init()
|
||
|
* yourself.
|
||
|
*
|
||
|
* If you create some nodes programmatically, you can also rerun
|
||
|
* this code to scan for "editable" classes. Like this:
|
||
|
*
|
||
|
* var foo document.createElement('div');
|
||
|
* foo.className = 'editable';
|
||
|
* ...
|
||
|
* InlineEditor.init( foo );
|
||
|
*/
|
||
|
init: function( arg )
|
||
|
{
|
||
|
// What is the arg?
|
||
|
var isNode = false;
|
||
|
var isEvent = false;
|
||
|
if( arg.nodeType )
|
||
|
isNode = true;
|
||
|
|
||
|
// If we're already inited and we're not being asked to
|
||
|
// init a new a node, bail out.
|
||
|
if( !isNode && InlineEditor.alreadyInited )
|
||
|
return;
|
||
|
|
||
|
// Find all elements with class 'editable' and make them editable
|
||
|
var rootEl = isNode ? arg : document;
|
||
|
var allEl = rootEl.getElementsByTagName('*');
|
||
|
for (var i= 0; i < allEl.length; i++ ){
|
||
|
if( InlineEditor.checkClass( allEl[i], 'editable' ) ){
|
||
|
|
||
|
switch( allEl[i].tagName ){
|
||
|
|
||
|
// For tables, set up individual cells.
|
||
|
case 'TABLE':
|
||
|
var tds = allEl[i].getElementsByTagName( 'td' );
|
||
|
for( var j = 0; j < tds.length; j++ )
|
||
|
InlineEditor.recursiveAddOnClickHandler( tds[j] );
|
||
|
break;
|
||
|
|
||
|
// Default behavior is to set up all editable elements
|
||
|
// which requires setting up all its children elements.
|
||
|
// This shouldn't be necessary, but these 'dblclick' events
|
||
|
// don't seem to propagate through the elements to parents.
|
||
|
default:
|
||
|
InlineEditor.recursiveAddOnClickHandler( allEl[i] );
|
||
|
} // end switch: tagname
|
||
|
|
||
|
|
||
|
} // end if: editable
|
||
|
} // end for: each element
|
||
|
|
||
|
if( !isNode )
|
||
|
InlineEditor.alreadyInited = true;
|
||
|
}, // end init
|
||
|
|
||
|
|
||
|
|
||
|
addClass: function(o,c) { return InlineEditor.jscss('add',o,c); },
|
||
|
removeClass: function(o,c) { return InlineEditor.jscss('remove',o,c); },
|
||
|
checkClass: function(o,c) { return InlineEditor.jscss('check',o,c); },
|
||
|
swapClass: function(o,c1,c2) { return InlineEditor.jscss('swap',o,c1,c2); },
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Return the column number, if a table cell.
|
||
|
*/
|
||
|
columnNumber: function( cell )
|
||
|
{
|
||
|
// Ensure we have a 'td' cell
|
||
|
if( cell.nodeType != 1 ) return -1;
|
||
|
if( cell.tagName != 'TD' ) return -1;
|
||
|
|
||
|
// Find cell and return column number
|
||
|
if( !cell.parentNode || cell.parentNode.tagName != 'TR' ) return -1;
|
||
|
var tr = cell.parentNode;
|
||
|
var tds = tr.getElementsByTagName('TD');
|
||
|
for( var i = 0; i < tds.length; i++ )
|
||
|
if( tds[i] == cell )
|
||
|
return i;
|
||
|
|
||
|
return -1;
|
||
|
}, // end columnNumber
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Return the row number, based on the row's immediate
|
||
|
* parent, which may be a 'tbody' or the actual 'table'.
|
||
|
*/
|
||
|
rowNumber: function( cell )
|
||
|
{
|
||
|
// Ensure we have a 'td' cell
|
||
|
if( cell.nodeType != 1 ) return -1;
|
||
|
if( cell.tagName != 'TD' ) return -1;
|
||
|
|
||
|
// Find cell's parent row and return row number
|
||
|
if( !cell.parentNode || cell.parentNode.tagName != 'TR' ) return -1;
|
||
|
var tr = cell.parentNode;
|
||
|
var trs = tr.parentNode.childNodes;
|
||
|
for( var i = 0; i < trs.length; i++ )
|
||
|
if( trs[i] == tr )
|
||
|
return i;
|
||
|
|
||
|
return -1;
|
||
|
}, // end rowNumber
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Returns the ID of the parent row. Useful if you use
|
||
|
* that to track the row to some sort of database primary key.
|
||
|
*/
|
||
|
rowID: function( cell )
|
||
|
{
|
||
|
// Ensure we have a 'td' cell
|
||
|
if( cell.nodeType != 1 ) return -1;
|
||
|
if( cell.tagName != 'TD' ) return -1;
|
||
|
|
||
|
if( !cell.parentNode || cell.parentNode.tagName != 'TR' ) return -1;
|
||
|
var tr = cell.parentNode;
|
||
|
return tr.id;
|
||
|
}, // end rowID
|
||
|
|
||
|
|
||
|
sizeTo: function( changeMe, model )
|
||
|
{
|
||
|
changeMe.style.position = 'absolute';
|
||
|
changeMe.style.zindex = 99;
|
||
|
changeMe.style.left = model .offsetLeft + 'px';
|
||
|
changeMe.style.top = model .offsetTop + 'px';
|
||
|
changeMe.style.width = model .offsetWidth + 'px';
|
||
|
changeMe.style.height = model .offsetHeight + 'px';
|
||
|
|
||
|
return changeMe;
|
||
|
}, // end sizeTo
|
||
|
|
||
|
|
||
|
getID: function( id )
|
||
|
{
|
||
|
|
||
|
var request = GetXmlHttpObject(); // I was using Google's tools
|
||
|
var url = "/index.php?task=ajax&call=edit&id=" + id;
|
||
|
|
||
|
request.open("GET", url, true);
|
||
|
request.onreadystatechange = function() {
|
||
|
if (request.readyState == 4) {
|
||
|
if( navigator.appName == "Opera" )
|
||
|
{
|
||
|
document.getElementById("ebox").value = request.responseText;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
document.getElementById("ebox").innerHTML = request.responseText;
|
||
|
}
|
||
|
|
||
|
} // end if: readystate 4
|
||
|
}; // end onreadystatechange
|
||
|
|
||
|
request.send(null);
|
||
|
},
|
||
|
|
||
|
getHTML: function( id )
|
||
|
{
|
||
|
|
||
|
var request = GetXmlHttpObject(); // I was using Google's tools
|
||
|
var url = "/index.php?task=ajax&call=editHTML&id=" + id;
|
||
|
|
||
|
request.open("GET", url, true);
|
||
|
request.onreadystatechange = function() {
|
||
|
if (request.readyState == 4) {
|
||
|
document.getElementById(id).innerHTML = request.responseText;
|
||
|
|
||
|
} // end if: readystate 4
|
||
|
}; // end onreadystatechange
|
||
|
|
||
|
request.send(null);
|
||
|
},
|
||
|
|
||
|
/* ******** F U N C T I O N S Y O U M I G H T O V E R R I D E ******** */
|
||
|
|
||
|
|
||
|
// These are examples of how you might override certain functions
|
||
|
// if you want to add more complex behaviors.
|
||
|
|
||
|
|
||
|
// The default editor is a one-line 'input' element.
|
||
|
// If you need anything more complex like a textarea
|
||
|
// or a select box or something, return it here.
|
||
|
//
|
||
|
// Remember to code the following:
|
||
|
//
|
||
|
// - Set the editor's starting value
|
||
|
// - Set the editor's size
|
||
|
|
||
|
/* //
|
||
|
InlineEditor.customEditor: = function( theElement )
|
||
|
{
|
||
|
// Only interested in setting up something custom
|
||
|
// for paragraph tags.
|
||
|
if( theElement.tagName != 'span' )
|
||
|
return;
|
||
|
|
||
|
var editor = document.createElement( 'textarea' );
|
||
|
editor.innerHTML = theElement.innerHTML;
|
||
|
editor.style.width = "100%";
|
||
|
editor.style.height = theElement.offsetHeight + "px";
|
||
|
|
||
|
return editor;
|
||
|
} // end customEditor
|
||
|
|
||
|
|
||
|
|
||
|
// If you use a custom editor, you may need to provide a
|
||
|
// way to determine what the value is. The default behavior,
|
||
|
// which will still take over if you return nothing, is
|
||
|
// to check for the presence of the 'value' property.
|
||
|
// If your editor has no 'value' property then the 'innerHTML'
|
||
|
// property is used. If this suits your needs
|
||
|
// even with your custom editor, then there's no need to
|
||
|
// use this function.
|
||
|
//
|
||
|
|
||
|
InlineEditor.editorValue = function( editor )
|
||
|
{
|
||
|
// Hypothetical editor with some obscure way
|
||
|
// of determing what the user selection is.
|
||
|
return editor.value;
|
||
|
|
||
|
} // end editorValue
|
||
|
|
||
|
|
||
|
// If you have anything "funny" going on you're welcome
|
||
|
// to define/override this function to determine just what
|
||
|
// the starting value is. The default behavior, which will
|
||
|
// be employed if you return nothing, is to use 'innerHTML'.
|
||
|
InlineEditor.elementValue: = function( theElement )
|
||
|
{
|
||
|
alert(theElement);
|
||
|
// Ignore the extra 'span' I threw in there. Just give me text.
|
||
|
return theElement.innerText;
|
||
|
|
||
|
} // end elementValue
|
||
|
|
||
|
|
||
|
|
||
|
// Unless you just want people to dink around with the
|
||
|
// transient-by-nature current page, you'll probably want
|
||
|
// to define/override this function and do something that
|
||
|
// saves the user's changes. Here is an example using
|
||
|
// "ajax" to immediately post a change. In this case,
|
||
|
// I was using Google's Map APIs, so that's how I create
|
||
|
// the HttpRequest.
|
||
|
//
|
||
|
*/
|
||
|
elementChanged: function( id, newVal,theElement )
|
||
|
{
|
||
|
InlineEditor.addClass( theElement, 'uneditable' ); // Special InlineEditor class
|
||
|
|
||
|
|
||
|
var request = GetXmlHttpObject(); // I was using Google's tools
|
||
|
var newvale = newVal.replace(/\n/g, '.||.');
|
||
|
var url = "/index.php?task=ajax&call=upedit&id=" + id +"&text=" + newvale;
|
||
|
|
||
|
request.open("GET", url, true);
|
||
|
request.onreadystatechange = function() {
|
||
|
if (request.readyState == 4) {
|
||
|
|
||
|
InlineEditor.removeClass( theElement, 'uneditable' );
|
||
|
document.getElementById(id).innerHTML = request.responseText;
|
||
|
} // end if: readystate 4
|
||
|
}; // end onreadystatechange
|
||
|
request.send(null);
|
||
|
|
||
|
}, // end elementChanged
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// end elementChanged
|
||
|
|
||
|
/* ******** F U N C T I O N S Y O U S H O U L D N O T C A L L ******** */
|
||
|
|
||
|
|
||
|
recursiveAddOnClickHandler: function( element )
|
||
|
{
|
||
|
element.onclick = InlineEditor.handleOnClick;
|
||
|
|
||
|
if( element.childNodes ){
|
||
|
children = element.childNodes;
|
||
|
for( i = 0; i < children.length; i++ ){
|
||
|
if( children[i].onclick ){
|
||
|
|
||
|
InlineEditor.recursiveAddOnClickHandler( children[i] );
|
||
|
|
||
|
} // end if: child also needs handler
|
||
|
} // end for: each child
|
||
|
} // end if: children
|
||
|
}, // end recursiveAddOnClickHandler
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Called when element is double-clicked.
|
||
|
* At the time of this event, the class is checked to see
|
||
|
* if the cell is marked 'uneditable'.
|
||
|
*/
|
||
|
handleOnClick: function( evt )
|
||
|
{
|
||
|
var evt = InlineEditor.fixEvent( evt );
|
||
|
//var target = evt.target;
|
||
|
var target = InlineEditor.findEditableTarget( evt.target );
|
||
|
// If element is "uneditable" or "editing" don't edit.
|
||
|
if( InlineEditor.checkClass( target, 'uneditable' ) || InlineEditor.checkClass( target, 'editing' ) )
|
||
|
return;
|
||
|
|
||
|
// Save original value.
|
||
|
var oldHTML = target.innerHTML;
|
||
|
var oldVal = null;
|
||
|
if( InlineEditor.elementValue ) // USER CAN PROVIDE OVERRIDE FUNCTION
|
||
|
oldVal = InlineEditor.elementValue( target );
|
||
|
if( !oldVal )
|
||
|
oldVal = target.innerHTML;
|
||
|
|
||
|
// Set up editor element
|
||
|
// User overridable function
|
||
|
var editor = null;
|
||
|
if( InlineEditor.customEditor ){ // USER CAN PROVIDE OVERRIDE FUNCTION
|
||
|
|
||
|
editor = InlineEditor.customEditor( target );
|
||
|
|
||
|
} // end if: customEditor
|
||
|
|
||
|
if( !editor ) { // If user didn't provide custom editor
|
||
|
|
||
|
|
||
|
editor = document.createElement('textarea')
|
||
|
//editor.innerHTML = oldVal;
|
||
|
editor.setAttribute('id','ebox')
|
||
|
editor.setAttribute('name','ebox')
|
||
|
|
||
|
// editor.style.position = 'absolute';
|
||
|
// editor.style.zindex = 99;
|
||
|
// editor.style.left = target.offsetLeft + 'px';
|
||
|
// editor.style.top = target.offsetTop + 'px';
|
||
|
editor.style.width = '500px';
|
||
|
editor.style.height = '70px';
|
||
|
|
||
|
} // end else: default
|
||
|
|
||
|
// Listen for when focus is lost.
|
||
|
editor.onblur = function(){ InlineEditor.handleInputBlur( editor, target.id, oldVal, oldHTML ); }
|
||
|
|
||
|
// Prep target
|
||
|
InlineEditor.addClass( target, 'editing' );
|
||
|
|
||
|
// Add editor
|
||
|
InlineEditor.getID(target.id);
|
||
|
target.innerHTML = "";
|
||
|
target.appendChild( editor );
|
||
|
editor.focus();
|
||
|
|
||
|
return false; // Don't propagate up. No need. Right?
|
||
|
}, // end handleDoubleClick
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Called when user is done editing the cell.
|
||
|
*/
|
||
|
handleInputBlur: function( editor, id, oldVal, oldHTML )
|
||
|
{
|
||
|
// Gather values
|
||
|
var parent = editor.parentNode;
|
||
|
var newVal = null;
|
||
|
if( InlineEditor.editorValue )
|
||
|
newVal = InlineEditor.editorValue( editor );
|
||
|
if( !newVal )
|
||
|
newVal = editor.value ? editor.value : editor.innerHTML; // Fallback
|
||
|
|
||
|
// Save value in the element
|
||
|
|
||
|
// If user wants to know of the change, pass it on.
|
||
|
if( InlineEditor.elementChanged )
|
||
|
InlineEditor.elementChanged( id, newVal, parent );
|
||
|
|
||
|
//InlineEditor.getHTML(id);
|
||
|
InlineEditor.removeClass( parent, 'editing' );
|
||
|
|
||
|
|
||
|
}, // end handleInputBlur
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Thanks to http://www.onlinetools.org/articles/unobtrusivejavascript/cssjsseparation.html
|
||
|
* for this bit of code.
|
||
|
*/
|
||
|
jscss: function(a,o,c1,c2)
|
||
|
{
|
||
|
switch (a){
|
||
|
case 'swap':
|
||
|
o.className=!InlineEditor.jscss('check',o,c1) ?
|
||
|
o.className.replace(c2,c1) :
|
||
|
o.className.replace(c1,c2);
|
||
|
break;
|
||
|
case 'add':
|
||
|
if(!InlineEditor.jscss('check',o,c1)){o.className+=o.className?' '+c1:c1;}
|
||
|
break;
|
||
|
case 'remove':
|
||
|
var rep=o.className.match(' '+c1)?' '+c1:c1;
|
||
|
o.className=o.className.replace(rep,'');
|
||
|
break;
|
||
|
case 'check':
|
||
|
return new RegExp('\\b'+c1+'\\b').test(o.className)
|
||
|
break;
|
||
|
} // end switch: action
|
||
|
}, // end jscss
|
||
|
|
||
|
|
||
|
fixEvent: function( evt )
|
||
|
{
|
||
|
var E = evt ? evt : window.event; // 'event' seems to be a special word in IE, so I'm using 'E' instead.
|
||
|
if( E.target )
|
||
|
if( E.target.nodeType == 3 )
|
||
|
E.target = E.target.parentNode;
|
||
|
|
||
|
//make sure Opera doesn't set this object
|
||
|
if( inlineeditor_isIE )
|
||
|
if( E.srcElement )
|
||
|
E.target = E.srcElement;
|
||
|
|
||
|
return E;
|
||
|
}, // end fixEvent
|
||
|
|
||
|
|
||
|
findEditableTarget: function( target )
|
||
|
{
|
||
|
// If a table cell, we assume that's editable
|
||
|
if( target.nodeType == 1 && target.tagName == 'TD' )
|
||
|
return target;
|
||
|
|
||
|
if( InlineEditor.checkClass( target, 'editable' ) )
|
||
|
return target;
|
||
|
|
||
|
if( target.parentNode )
|
||
|
return InlineEditor.findEditableTarget( target.parentNode );
|
||
|
|
||
|
return null;
|
||
|
}, // end findEditableTarget
|
||
|
|
||
|
|
||
|
addEvent: function( target, eventName, func, capture )
|
||
|
{
|
||
|
if( target.addEventListener ){
|
||
|
target.addEventListener( eventName, func, capture );
|
||
|
return true;
|
||
|
} // end if: addEventListener
|
||
|
else if( target.attachEvent )
|
||
|
return target.attachEvent( 'on'+eventName, func );
|
||
|
}, // end addEvent
|
||
|
|
||
|
|
||
|
removeEvent: function( target, eventName, func, capture )
|
||
|
{
|
||
|
if( target.removeEventListener ){
|
||
|
target.removeEventListener( eventName, func, capture );
|
||
|
return true;
|
||
|
} // end if: removeEventListener
|
||
|
else if( target.detachEvent )
|
||
|
return target.detachEvent( 'on'+eventName, func );
|
||
|
} // end removeEvent
|
||
|
|
||
|
} // end class InlineEditor
|
||
|
|
||
|
/**
|
||
|
* Add InlineEditor.init() to window.onload.
|
||
|
*/
|
||
|
InlineEditor.addEvent(window,'load',InlineEditor.init,false);
|