Look-up Tables: Look-up table edit
21 March 2009Next up in our continuing refactoring of the client-side look-up table components is the Look-up Table edit function. The two major elements of this feature are the HTML page, lookup_table_edit_example.html and the corresponding Javascript file, lookup_table_edit_example.js. Changes to HTML page are minimal, however, and consist of simply adding the new context to the URL for the script that produces the Property Type options.
Changes to the Javascript file are more extensive, but again center on adding in code to accommodate the new context concept wherever required. The updated version now looks like this:
// lookup_table_edit_example.js
var dataURIStart = '/core/table/';
var context = 'global';
var propertyTypeURIStart = '/core/entry/global/dataType/';
var propertyTypeInfo = {};
var propertyTypeLoadIndex = 0;
var queryParameter = {};
var userId = '';
var userURI = '';
var userName = '';
var newTable = true;
var visibleRows = 0;
var availableRows = 20;
var inputErrors = '';
/**
* Initialize the page
*/
function initPage() {
loadQueryParameters();
setUpSelectOptions();
ajaxGet('/id/whoami', 'initPageCont','ajaxError');
}
/**
* Loads in any parameters found in the current URL's query string
*/
function loadQueryParameters() {
var thisURL = document.location.href;
if (thisURL.indexOf('?') != -1) {
var queryString = thisURL.substring(thisURL.indexOf('?') + 1);
var parts = queryString.split('&');
for (var i=0; i<parts.length; i++) {
var moreParts = parts[i].split('=');
if (moreParts != null && moreParts.length > 1) {
queryParameter[moreParts[0]] = moreParts[1];
}
}
}
}
/**
* Sets up all of the property type and source select options
*/
function setUpSelectOptions() {
var types = [];
types[0] = {label: '-- select type --', value: ''};
if (propertyTypeOptions != null && propertyTypeOptions.length > 0) {
for (var i=0; i<propertyTypeOptions.length; i++) {
types[i+1] = propertyTypeOptions[i];
}
}
var sources = [];
sources[0] = {label: '* Open entry', value: ''};
if (tableOptions != null) {
var tablesInContext = tableOptions[queryParameter.context];
if (tablesInContext!= null && tablesInContext.length > 0) {
for (var i=0; i<tablesInContext.length; i++) {
sources[i+1] = tablesInContext[i];
}
}
}
for (var i=0; i<availableRows; i++) {
setUpOptions('type[' + i + ']', types);
setUpOptions('source[' + i + ']', sources);
}
}
/**
* Sets up the options on a single select element
*/
function setUpOptions(elementId, options) {
var element = document.getElementById(elementId);
if (element != null) {
var i = 0;
// clear the old options
i = parseInt(element.length);
while (i > 0) {
i = i - 1;
element.options[i] = null;
}
// establish the new options
for (i=0; i<options.length; i++) {
element.options[i] = new Option(options[i].label, options[i].value);
}
element.options[0].selected = true;
}
}
/**
* Initialize the page (continued)
*/
function initPageCont(xmlDoc) {
var root = xmlDoc.documentElement;
userId = root.getElementsByTagName('remoteUser')[0].attributes[0].value;
userURI = root.getElementsByTagName('remoteUser')[0].attributes[1].value;
userName = root.getElementsByTagName('remoteUser')[0].firstChild.data;
var userLink = '<a class="action" href="javascript:popUpUserData(userURI);" title="' +
userName + '">' + userName + '</a>';
document.getElementById('userLink').innerHTML = userLink;
loadPropertyTypeInfo();
}
/**
* Loads the property type information
*/
function loadPropertyTypeInfo() {
if (propertyTypeLoadIndex < propertyTypeOptions.length) {
var thisType = propertyTypeOptions[propertyTypeLoadIndex].value;
propertyTypeLoadIndex++;
ajaxGet(propertyTypeURIStart + thisType, 'processPropertyType','ajaxError');
} else {
fetchTable();
}
}
/**
* Processes the property type information
*/
function processPropertyType(xmlDoc) {
var root = xmlDoc.documentElement;
var thisType = root.attributes[2].value;
var maximumSize = '';
if (root.getElementsByTagName('property00')[0].firstChild != null) {
maximumSize = root.getElementsByTagName('property00')[0].firstChild.data;
}
var sourceUsed = '';
if (root.getElementsByTagName('property01')[0].firstChild != null) {
sourceUsed = root.getElementsByTagName('property01')[0].firstChild.data;
}
var typeInfo = {};
typeInfo.type = thisType;
typeInfo.maximumSize = (maximumSize * 1);
typeInfo.sourceUsed = (sourceUsed == 'true');
propertyTypeInfo[thisType] = typeInfo;
loadPropertyTypeInfo();
}
/**
* Initialize the page (continued)
*/
function fetchTable() {
if (queryParameter.context != null) {
context = queryParameter.context;
}
dataURIStart += context + '/';
if (queryParameter.table != null) {
ajaxGet(dataURIStart + queryParameter.table, 'populateInputFields','ajaxError');
}
}
/**
* Populates the input fields with data from the database
*/
function populateInputFields(xmlDoc) {
var root = xmlDoc.documentElement;
document.getElementById('id').value = root.attributes[2].value;
document.getElementById('id').setAttribute('readonly', 'true');
document.getElementById('name').value =
root.getElementsByTagName('displayName')[0].firstChild.data;
document.getElementById('name').focus();
document.getElementById('description').value =
root.getElementsByTagName('description')[0].firstChild.data;
var property =
root.getElementsByTagName('properties')[0].getElementsByTagName('property');
for (var i=0; i<property.length; i++) {
addPropertyRow();
if (property[i].getElementsByTagName('name')[0].firstChild != null) {
document.getElementById('name[' + i + ']').value =
property[i].getElementsByTagName('name')[0].firstChild.data;
}
if (property[i].getElementsByTagName('type')[0].firstChild != null) {
document.getElementById('type[' + i + ']').value =
property[i].getElementsByTagName('type')[0].firstChild.data;
}
if (property[i].getElementsByTagName('size')[0].firstChild != null) {
document.getElementById('size[' + i + ']').value =
property[i].getElementsByTagName('size')[0].firstChild.data;
}
if (property[i].getElementsByTagName('label')[0].firstChild != null) {
document.getElementById('label[' + i + ']').value =
property[i].getElementsByTagName('label')[0].firstChild.data;
}
if (property[i].getElementsByTagName('colHeading')[0].firstChild != null) {
document.getElementById('colHeading[' + i + ']').value =
property[i].getElementsByTagName('colHeading')[0].firstChild.data;
}
if (property[i].getElementsByTagName('inputRequired')[0].firstChild != null) {
if (property[i].getElementsByTagName('inputRequired')[0].firstChild.data == 'true') {
document.getElementById('inputRequired[' + i + ']').checked = true;
}
}
if (property[i].getElementsByTagName('displayOnList')[0].firstChild != null) {
if (property[i].getElementsByTagName('displayOnList')[0].firstChild.data == 'true') {
document.getElementById('displayOnList[' + i + ']').checked = true;
}
}
if (property[i].getElementsByTagName('source')[0].firstChild != null) {
document.getElementById('source[' + i + ']').value =
property[i].getElementsByTagName('source')[0].firstChild.data;
}
if (property[i].getElementsByTagName('notes')[0].firstChild != null) {
document.getElementById('notes[' + i + ']').value =
property[i].getElementsByTagName('notes')[0].firstChild.data;
}
typeChange(i);
}
newTable = false;
}
/**
* Invoked whenever a property type changes
*/
function typeChange(row) {
var elementName = 'type[' + row + ']';
var element = document.getElementById(elementName);
var newType = element.value;
var typeInfo = propertyTypeInfo[newType];
toggleElement('size[' + row + ']', (typeInfo.maximumSize > 0));
toggleElement('source[' + row + ']', typeInfo.sourceUsed);
}
/**
* Adds a property row
*/
function addPropertyRow() {
if (visibleRows < availableRows) {
document.getElementById('row[' + visibleRows + ']').style.visibility = 'visible';
document.getElementById('row[' + visibleRows + ']').style.display = '';
visibleRows++;
toggleElement('addProperty', visibleRows < availableRows);
toggleElement('removeProperty', visibleRows > 0);
}
}
/**
* Removes a property row
*/
function removePropertyRow() {
if (visibleRows > 0) {
visibleRows--;
document.getElementById('row[' + visibleRows + ']').style.visibility = 'hidden';
document.getElementById('row[' + visibleRows + ']').style.display = 'none';
toggleElement('addProperty', visibleRows < availableRows);
toggleElement('removeProperty', visibleRows > 0);
}
}
/**
* Hides/shows an element on the page
*/
function toggleElement(elementId, showElement) {
var element = document.getElementById(elementId);
if (showElement) {
element.style.visibility = 'visible';
element.style.display = '';
} else {
element.style.visibility = 'hidden';
element.style.display = 'none';
}
}
/**
* Opens up a new window to display the details of the current user
*/
function popUpUserData(uri) {
var userWindow = window.open(uri, 'userWindow', 'toolbar=no,directories=no,status=no,
scrollbars=yes,resizable=yes,resize=yes,menubar=no,height=400,width=500');
if (window.focus) {
userWindow.focus();
}
}
/**
* Saves the edited data
*/
function saveForm() {
inputErrors = '';
if (newTable) {
validateTableName();
} else {
validateData();
}
}
/**
* Validates the table id on new entries
*/
function validateTableName() {
var tableName = document.getElementById('id').value;
if (validKeyFormat(tableName)) {
// table id is valid format; now make sure it is not already on file
ajaxGet(dataURIStart + tableName, 'newTableAlreadyOnFile','checkNewTableResponse');
} else {
addInputError('The format of the Table ID is invalid.');
sendInputErrors();
}
}
/**
* Reports a duplicate table id error
*/
function newTableAlreadyOnFile(xmlDoc) {
addInputError('The Table ID specified ("' + tableName + '") is already on file.');
sendInputErrors();
}
/**
* Checks the response code from the duplicate Table check
*/
function checkNewTableResponse(httpRequest) {
if (httpRequest.status == 404) {
// table id is not already on file ... validate the rest of the data
validateData();
} else {
// must be some other error ... let the standard error handler take it
ajaxError(httpRequest);
}
}
/**
* Validates the user input
*/
function validateData() {
if (!(document.getElementById('name').value > '')) {
addInputError('Name is required');
}
if (!(document.getElementById('description').value > '')) {
addInputError('Description is required');
}
for (var i=0; i<visibleRows; i++) {
validateProperty(i);
}
if (inputErrors.length > 0) {
sendInputErrors();
} else {
saveUserInput();
}
}
/**
* Validates a single property row
*/
function validateProperty(row) {
var name = document.getElementById('name[' + row + ']').value;
if (!validKeyFormat(name)) {
addInputError('Property name "' + name + '" is invalid.');
}
var type = document.getElementById('type[' + row + ']').value;
var typeInfo = propertyTypeInfo[type];
if (typeInfo != null) {
if (typeInfo.maximumSize > 0) {
validateSize(row, typeInfo.maximumSize);
} else {
document.getElementById('size[' + row + ']').value = '0';
}
} else {
addInputError('Property type "' + type + '" is invalid.');
}
var label = document.getElementById('label[' + row + ']').value;
if (!(label > '')) {
addInputError('Label for property "' + name + '" is required.');
}
}
/**
* Validates the format for all key fields
*/
function validKeyFormat(id) {
var testPattern=new RegExp('^[a-z]{1}[a-zA-Z0-9]{1,32}$');
return testPattern.test(id);
}
/**
* Validates the property size
*/
function validateSize(row, max) {
//TODO
}
/**
* Adds one input error to the list of errors
*/
function addInputError(errorMessage) {
if (inputErrors.length > 0) {
inputErrors += '\n';
}
inputErrors += errorMessage;
}
/**
* Send input errors
*/
function sendInputErrors() {
var errorMessage = 'The following error(s) have been detected:\n\n';
errorMessage += inputErrors;
errorMessage += '\n\nPlease correct the error(s) and resubmit.';
alert(errorMessage);
}
/**
* Saves the user input
*/
function saveUserInput() {
// field validation has passed; update database
if (newTable) {
createNewTable();
} else {
updateExistingTable();
}
}
/**
* Creates a new table
*/
function createNewTable() {
var tableName = document.getElementById('id').value;
var xml = '<?xml version="1.0" encoding="UTF-8"?>\n';
xml += '<lookup:table id="-1" context="';
xml += context;
xml += '" tableName="';
xml += tableName;
xml += '" xmlns:lookup="http://www.restafarian.org/lookup"
xmlns:xlink="http://www.w3.org/1999/xlink">\n';
xml += ' <name>';
xml += document.getElementById('name').value;
xml += '</name>\n';
xml += ' <description>';
xml += document.getElementById('description').value;
xml += '</description>\n';
xml += ' <properties>\n';
for (var i=0; i<visibleRows; i++) {
var thisName = document.getElementById('name[' + i + ']').value;
if (thisName > '') {
xml += ' <property id="-1" tableName="' + tableName + '" sequence="' + i + '">\n';
xml += ' <name>' + thisName + '</name>\n';
xml += ' <type>' + document.getElementById('type[' + i + ']').value + '</type>\n';
xml += ' <size>' + document.getElementById('size[' + i + ']').value + '</size>\n';
xml += ' <label>' + document.getElementById('label[' + i + ']').value + '</label>\n';
xml += ' <colHeading>' + document.getElementById('colHeading[' + i + ']').value +
'</colHeading>\n';
xml += ' <inputRequired>' + document.getElementById('inputRequired[' + i +
']').checked + '</inputRequired>\n';
xml += ' <displayOnList>' + document.getElementById('displayOnList[' + i +
']').checked + '</displayOnList>\n';
xml += ' <source>' + document.getElementById('source[' + i + ']').value + '</source>\n';
xml += ' <notes>' + document.getElementById('notes[' + i + ']').value + '</notes>\n';
xml += ' </property>\n';
}
}
xml += ' </properties>\n';
xml += '</lookup:table>';
ajaxPut(dataURIStart + tableName, xml, 'insertTableSuccessful','ajaxError');
}
/**
* Notifies the user of a successful save operation
*/
function insertTableSuccessful() {
alert('The table definition has been successfully added.');
history.back();
}
/**
* Updates an existing table
*/
function updateExistingTable() {
var tableName = document.getElementById('id').value;
var parameters = 'context=' + context;
parameters += '&tableName=' + tableName;
parameters += '&displayName=' + document.getElementById('name').value;
parameters += '&description=' + document.getElementById('description').value;
for (var i=0; i<visibleRows; i++) {
var thisName = document.getElementById('name[' + i + ']').value;
if (thisName > '') {
parameters += '&name[' + i + ']=' + thisName;
if (document.getElementById('type[' + i + ']').value > '') {
parameters += '&type[' + i + ']=' + document.getElementById('type[' + i + ']').value;
}
if (document.getElementById('size[' + i + ']').value > '') {
parameters += '&size[' + i + ']=' + document.getElementById('size[' + i + ']').value;
}
if (document.getElementById('label[' + i + ']').value > '') {
parameters += '&label[' + i + ']=' + document.getElementById('label[' + i + ']').value;
}
if (document.getElementById('colHeading[' + i + ']').value > '') {
parameters += '&colHeading[' + i + ']=' + document.getElementById('colHeading[' + i +
']').value;
}
parameters += '&inputRequired[' + i + ']=' + document.getElementById('inputRequired[' + i +
']').checked;
parameters += '&displayOnList[' + i + ']=' + document.getElementById('displayOnList[' + i +
']').checked;
if (document.getElementById('source[' + i + ']').value > '') {
parameters += '&source[' + i + ']=' + document.getElementById('source[' + i + ']').value;
}
if (document.getElementById('notes[' + i + ']').value > '') {
parameters += '¬es[' + i + ']=' + document.getElementById('notes[' + i + ']').value;
}
}
}
ajaxPost(dataURIStart + tableName, parameters, 'updateTableSuccessful','ajaxError');
}
/**
* Notifies the user of a successful save operation
*/
function updateTableSuccessful() {
alert('The table definition has been successfully updated.');
history.back();
}
There is one last client-side component to deal with, and that’s the Look-up Table entry edit, which we’ll take on next. That should complete the entire refactoring process needed to add the context to the Look-up Table service, so once that’s done, it will be time to once again look for something to do.
Leave a reply
You must be logged in to post a comment.





