git-svn-id: https://192.168.0.254/svn/Proyectos.Incam_SGD/tags/3.7.0.2_original@1 eb19766c-00d9-a042-a3a0-45cb8ec72764
378 lines
14 KiB
JavaScript
378 lines
14 KiB
JavaScript
// simple event stacking.
|
|
// i don't like Mochikit's one.
|
|
|
|
function getBindTarget(fieldset) {
|
|
var possibles = getElementsByTagAndClassName('DIV','conditional_target', fieldset);
|
|
|
|
return possibles[0];
|
|
}
|
|
|
|
function attachToElementEvent(elem, event_name, func) {
|
|
// catch IE (grumble)
|
|
if (elem.attachEvent) {
|
|
elem.attachEvent('on'+event_name, func);
|
|
} else {
|
|
elem.addEventListener(event_name, func, false);
|
|
}
|
|
}
|
|
|
|
function removeFromElementEvent(elem, event_name, func) {
|
|
// catch IE (grumble)
|
|
if (elem.detachEvent) {
|
|
elem.detachEvent('on'+event_name, func);
|
|
} else {
|
|
elem.removeEventListener(event_name, func, false);
|
|
}
|
|
}
|
|
|
|
// quick and dirty helper - find the nearest parent item matching tagName.
|
|
// FIXME steal the klass or tagName logic from MochiK.
|
|
// FIXME add to a core js-lib, and add some unit-tests.
|
|
function breadcrumbFind(elem, tagName) {
|
|
var stopTag = 'BODY';
|
|
var currentTag = elem.tagName;
|
|
var currentElem = elem;
|
|
while ((currentTag != stopTag) && (currentTag != tagName)) {
|
|
currentElem = currentElem.parentNode;
|
|
currentTag = currentElem.tagName;
|
|
}
|
|
if (currentTag == tagName) {
|
|
return currentElem;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/* Conditional Metadata Usage
|
|
*
|
|
* Allows the system to respond to conditional metadata events.
|
|
*/
|
|
|
|
/**
|
|
|
|
== Basic process around Conditional Metadata JS/HTML interaction ==
|
|
|
|
The system works based on 3 concepts:
|
|
|
|
1. on the appropriate "activation" command, the entire "field" is serialised and replaced with a hidden
|
|
input var, and a "user friendly" label.
|
|
2. a undo stack needs to be kept, which provides the user with a way to "un-fix" items.
|
|
3. When an item is activated, the system:
|
|
(i) polls the page for fixed input-vars: this _includes_ (for example) fieldset_id, as well as later items.
|
|
(ii) submits these to a targeturl (set as a "global var" - currently in _this_ file.) // FIXME: this needs to be programmatically settable.
|
|
|
|
|
|
// TODO make this operate on a particular subset of the page, and be _instantiable_. (use fieldset as the "controlling component".
|
|
// TODO lazy bind all activation handlers to ensure that the above problem is solveable.
|
|
// TODO ensure that this functions across the required browser sets.
|
|
// TODO verify that the entire set of "lookup" values works here: select and input seem to work.
|
|
|
|
*/
|
|
|
|
var conditional_usage_undostack = new Array();
|
|
var conditional_usage_keys = new Array();
|
|
|
|
|
|
// sorry mom.
|
|
function checkStackForFieldset(fieldset) {
|
|
for (var i=0; i<conditional_usage_keys.length; i++) {
|
|
if (conditional_usage_keys[i] == fieldset) {
|
|
simpleLog('DEBUG','found undostack at keyindex '+i);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
// grow and go.
|
|
function getStackForFieldset(fieldset) {
|
|
for (var i=0; i<conditional_usage_keys.length; i++) {
|
|
if (conditional_usage_keys[i] == fieldset) {
|
|
simpleLog('DEBUG','found undostack at keyindex '+i);
|
|
return conditional_usage_undostack[i];
|
|
}
|
|
}
|
|
// we would have returned by now. onward, and upward.
|
|
// i == conditional_usage_keys.length == conditional_usage_undostack.length
|
|
conditional_usage_undostack.push(Array());
|
|
conditional_usage_keys.push(fieldset);
|
|
simpleLog('DEBUG','created undostack at keyindex '+i+' for fieldset '+fieldset);
|
|
simpleLog('DEBUG','undoStack: '+conditional_usage_undostack+'\nundoKeyStack: '+conditional_usage_keys)
|
|
return conditional_usage_undostack[i]; // must be the "new" element, which is 1 past the old size.
|
|
}
|
|
|
|
// Stack implementation
|
|
function pushStack(fieldset, subtree) {
|
|
// FIXME how do I bind this to a particular fieldset object.
|
|
// FIXME at worst, we need to use the HTMLFieldSet object as a "key" of sorts into a stack it's O(n) initially, unless we can do some other magic ...
|
|
simpleLog('DEBUG','pushStack received: '+fieldset);
|
|
var undostack = getStackForFieldset(fieldset);
|
|
undostack.push(subtree); // onto the end, so it can be popped.
|
|
simpleLog('ERROR','added item to undo stack..');
|
|
}
|
|
|
|
function popStack(fieldset) {
|
|
var undostack = getStackForFieldset(fieldset);
|
|
if (undostack.length == 0) {
|
|
return ;
|
|
}
|
|
var last_item = undostack.pop();
|
|
simpleLog('DEBUG','popping item\n'+toHTML(last_item));
|
|
last_item.parentNode.removeChild(last_item);
|
|
updateFieldset(fieldset);
|
|
}
|
|
|
|
/**
|
|
- creates a replacement widget,
|
|
- adds the _old_ widget to the correct stack.
|
|
*/
|
|
|
|
function createFixedWidget(fieldset, widget, i_name, i_value, i_label) {
|
|
// bad, but there's nothing else we can do in the current design.
|
|
// we need to walk the TR for the TH (widget.tagName == TR)
|
|
if (widget.tagName != 'DIV')
|
|
{
|
|
// alert('Invalid widget in conditional.'+widget);
|
|
simpleLog('ERROR','invalid widget in conditional.');
|
|
return false;
|
|
}
|
|
simpleLog('DEBUG','creating fixed widget');
|
|
var header = widget.getElementsByTagName('LABEL')[0]; // FIXME _could_ fail if pathalogical.
|
|
simpleLog('DEBUG','got label');
|
|
// check for "requird" and edit.
|
|
var headlist = header.getElementsByTagName('SPAN');
|
|
simpleLog('DEBUG','got headlist - ' + headlist.length);
|
|
if (headlist.length != 0) {
|
|
header.removeChild(headlist[0]);
|
|
}
|
|
simpleLog('DEBUG','getting name');
|
|
var i_friendly_name = scrapeText(header);
|
|
|
|
var newWidget = DIV({'class':'field fixed'},
|
|
createDOM('LABEL',null, i_friendly_name),
|
|
DIV(null,
|
|
INPUT({'type':'hidden','name':i_name, 'value':i_value,'class':'fixed'}),
|
|
SPAN(null, i_label)
|
|
)
|
|
);
|
|
swapDOM(widget, newWidget);
|
|
pushStack(fieldset, newWidget);
|
|
simpleLog('ERROR','conditional_usage passed in fieldset '+fieldset+' and widget '+newWidget);
|
|
}
|
|
|
|
/** handles the "update" event.
|
|
|
|
needs to:
|
|
- "replace" the contents of the widget with a "fixed" input.
|
|
- trigger the "updateFieldset"
|
|
*/
|
|
|
|
function handleSelectChange(fieldset, widget, select_object) {
|
|
simpleLog('DEBUG','handleSelectChange on select with name "'+select_object.name+'"');
|
|
var i_name = select_object.name;
|
|
var i_value = select_object.value;
|
|
var i_label = scrapeText(select_object.options[select_object.selectedIndex]);
|
|
simpleLog('DEBUG','handleSelectChange creating');
|
|
createFixedWidget(fieldset, widget, i_name, i_value, i_label);
|
|
simpleLog('DEBUG','handleSelectChange updating');
|
|
updateFieldset(fieldset);
|
|
}
|
|
|
|
function handleRadioChange(fieldset, widget, radio_object) {
|
|
simpleLog('ERROR','call to stub: handleRadioChange on radio with name "'+radio_object.name+'"');
|
|
var i_name = radio_object.name;
|
|
var i_value = radio_object.value;
|
|
|
|
|
|
var oLabel = breadcrumbFind(radio_object, 'LABEL');
|
|
if (oLabel == null) {
|
|
simpleLog('ERROR','radiobutton ('+radio_object.name+':'+radio_object.value+') has no associated label. failing.');
|
|
return false;
|
|
} else {
|
|
var i_label = scrapeText(oLabel);
|
|
}
|
|
|
|
createFixedWidget(fieldset, widget, i_name, i_value, i_label);
|
|
updateFieldset(fieldset);
|
|
}
|
|
|
|
/** extract all the appropriate input-vars from a given fieldset, so that it can
|
|
be passed into a backed. Returns an array ("formKeys" => array(), "formValues" => array())
|
|
that can be passed to be backend.
|
|
|
|
// actually, this is ONLY and issue for the "fieldset_id" form-field:
|
|
// for the rest of them, the backend should handle this sanely (e.g. in what it sends _us_). Suspect the "best" option is to call this
|
|
// 'fieldset_id[]' since the backend can then extract which fieldsets have been called. other vars will get converted
|
|
// from <input type="radio" ... name="xxxx"> and <select ... name="xxxx"> to <input type="hidden" class="fixed">
|
|
//
|
|
*/
|
|
function parseFieldsetToForm(fieldset) {
|
|
simpleLog('ERROR','call to untested fn: parseFieldsetToForm. ');
|
|
var formContent = new Array();
|
|
|
|
var input_vars = getElementsByTagAndClassName('input','fixed',fieldset);
|
|
formContent["formKeys"] = new Array();
|
|
formContent["formValues"] = new Array();
|
|
|
|
for (var i=0; i<input_vars.length; i++) {
|
|
var input_object = input_vars[i];
|
|
// don't delete the undo button.
|
|
if (input_object.type != 'button') {
|
|
formContent["formKeys"].push(input_object.name);
|
|
formContent["formValues"].push(input_object.value);
|
|
}
|
|
}
|
|
|
|
return formContent;
|
|
}
|
|
|
|
/** bind a "widget" to a particular fieldset, and populate the appropriate event-handlers
|
|
- find the various types of input objects and hook in appropriately:
|
|
make sure that the function binds:
|
|
- fieldset
|
|
- pseudo-widget (the div that surrounds each group of options.)
|
|
-handler.
|
|
// FIXME: this assumes that inputs are either "select" or "<input type='radio'>"
|
|
// FIXME: is that a valid assumption?
|
|
*/
|
|
function bindToConditionalFieldset(fieldset, widget) {
|
|
|
|
// handleChange needs to be bound to each input widget.
|
|
// for <input type != "hidden"> type variables this means binding to onclick
|
|
// for <select> this means binding to onchange
|
|
|
|
var select_fields = widget.getElementsByTagName('SELECT');
|
|
var input_fields = widget.getElementsByTagName('INPUT'); // needs to be filtered - no "hidden" vars.
|
|
|
|
for (var i=0; i<select_fields.length; i++) {
|
|
var select_object = select_fields[i];
|
|
var handler = partial(handleSelectChange, fieldset, widget, select_object);
|
|
attachToElementEvent(select_object, 'change', handler);
|
|
}
|
|
|
|
for (var i=0; i<input_fields.length; i++) {
|
|
var input_object = input_fields[i];
|
|
var handler = partial(handleRadioChange, fieldset, widget, input_object);
|
|
if (input_object.type == 'radio') {
|
|
attachToElementEvent(input_object, 'click', handler);
|
|
} else if (input_object.type == 'hidden') {
|
|
; // this is OK, and expected.
|
|
} else {
|
|
simpleLog('ERROR','bindToConditionalFieldset found a non-hidden input field of type: '+input_object.type);
|
|
}
|
|
}
|
|
simpleLog('DEBUG','bindToConditionalFieldset complete');
|
|
}
|
|
|
|
function clearUnfixedWidgets(fieldset) {
|
|
var widgets = getElementsByTagAndClassName('DIV', 'field', fieldset);
|
|
for (var i=0; i<widgets.length; i++) {
|
|
var w = widgets[i];
|
|
if (hasElementClass(w, 'fixed')) {
|
|
simpleLog('DEBUG','Not deleting widget with class '+w.getAttribute('class'));
|
|
} else {
|
|
w.parentNode.removeChild(w);
|
|
simpleLog('DEBUG','Deleting widget with class '+w.getAttribute('class'));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* XMLHttpRequest functions
|
|
*
|
|
*/
|
|
|
|
function updateFieldset(fieldset) {
|
|
var baseurl = getElement('kt-core-baseurl').value;
|
|
var targeturl = baseurl + '/presentation/lookAndFeel/knowledgeTree/ajaxConditional.php'; // test_metadata_update.txt';
|
|
simpleLog('DEBUG','AJAX function called: updateFieldset');
|
|
|
|
var formdata = parseFieldsetToForm(fieldset);
|
|
formdata.formKeys.push('action');
|
|
formdata.formValues.push('updateFieldset');
|
|
var POSTval = queryString(formdata.formKeys, formdata.formValues);
|
|
|
|
var req = getXMLHttpRequest();
|
|
req.open('POST',targeturl, true); // MUST be async.
|
|
//simpleLog('DEBUG','form submission from updateFieldset: '+logFormSubmission(formdata));
|
|
simpleLog('DEBUG','form submission from updateFieldset: '+(formdata));
|
|
req.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
|
|
var deferred = sendXMLHttpRequest(req, POSTval);
|
|
|
|
deferred.addErrback(partial(do_handleError, 'updateFieldset'));
|
|
deferred.addCallback(partial(do_updateFieldset, fieldset));
|
|
}
|
|
|
|
function do_handleError(function_name, err) {
|
|
simpleLog('ERROR','AJAX request from function '+function_name+' failed on exception: '+err);
|
|
}
|
|
|
|
function do_updateFieldset(fieldset, req) {
|
|
simpleLog('DEBUG','AJAX function do_updateFieldset received: \n'+req.responseText);
|
|
// clear unfixed widgets before we start.
|
|
clearUnfixedWidgets(fieldset);
|
|
// create an unparented div for HTML insertion.
|
|
var hold = DIV(null);
|
|
|
|
hold.innerHTML = req.responseText;
|
|
|
|
var new_widgets = getElementsByTagAndClassName('DIV','field', hold);
|
|
|
|
simpleLog('DEBUG','new_widgets.length: '+new_widgets.length);
|
|
var target = getBindTarget(fieldset);
|
|
simpleLog('DEBUG','new_widgets.length: '+new_widgets.length);
|
|
for (var i=0; i<new_widgets.length; i++) {
|
|
var w = new_widgets[i];
|
|
simpleLog('DEBUG','binding: '+toHTML(w));
|
|
target.appendChild(w);
|
|
bindToConditionalFieldset(fieldset, w);
|
|
}
|
|
simpleLog('DEBUG','fieldset ends as: \n'+toHTML(fieldset));
|
|
delete t; // clean this up.
|
|
}
|
|
|
|
/* HTML callbacks - functions called on-event.
|
|
*
|
|
*/
|
|
|
|
function reviseConditional(buttonsource) {
|
|
var fieldset = breadcrumbFind(buttonsource, 'FIELDSET');
|
|
setElementClass(fieldset, 'conditional_metadata');
|
|
if (!checkStackForFieldset(fieldset)) {
|
|
var undo_button = INPUT({'type':'button','value':_('Undo')},null);
|
|
attachToElementEvent(undo_button,'click',partial(popStack, fieldset));
|
|
fieldset.appendChild(undo_button);
|
|
// initialise the stack.
|
|
getStackForFieldset(fieldset);
|
|
updateFieldset(fieldset);
|
|
buttonsource.parentNode.removeChild(buttonsource);
|
|
}
|
|
|
|
}
|
|
|
|
/* Fieldset creation and update.
|
|
*
|
|
*/
|
|
|
|
function initialiseConditionalFieldsets() {
|
|
|
|
var fieldsets = getElementsByTagAndClassName('FIELDSET','conditional_metadata');
|
|
simpleLog('DEBUG','found fieldsets: '+fieldsets.length);
|
|
// triggers initial update - since this contains no "fixed" vars, it'll remove "unfixed" widgets
|
|
// and insert the initial (master) field.
|
|
for (var i=0; i<fieldsets.length; i++) {
|
|
if (!checkStackForFieldset(fieldsets[i])) {
|
|
var undo_button = INPUT({'type':'button','value':_('Undo')},null);
|
|
attachToElementEvent(undo_button,'click',partial(popStack, fieldsets[i]));
|
|
fieldsets[i].appendChild(undo_button);
|
|
// initialise the stack.
|
|
getStackForFieldset(fieldsets[i]);
|
|
updateFieldset(fieldsets[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
addLoadEvent(initialiseConditionalFieldsets);
|