// WontoMedia - a wontology web application // Copyright (C) 2011 - Glen E. Ivey // www.wontology.com // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License version // 3 as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program in the file COPYING and/or LICENSE. If not, // see <http://www.gnu.org/licenses/>. var nameAjaxStart = 400; // to avoid unnecessary server traffic, // wait after Name change before check // define fields subject to check, order they occur in form var requiredItemElements = [ "sti_type", "title", "name", "description", "submit" ]; var itemElementNames = [ "Type selector", "Title field", "Name field", "Description box", "Create button" ]; var indexTitle = 1; var indexName = 2; var indexDescription = 3; var maxLengths = [ 0, 255, 80, 65535 ]; // globals w/ defaults, real values figured in plumbEvent...() var thereIsAClassControl = false; var thereIsATypeControl = false; var originalItemName = ""; var controlNamePrefix = ""; var itemSubmit; // encoding: -1 -> haven't checked yet // false -> no error // true -> error condition present var itemFormErrors = {}; // after key presses, have to wait for browser to update the input field // before we check it (delay in ms) var dly = 200; var valueWhenLastChecked = ""; var uniquenessTimerId = -1; var ajaxRequestInProgress = null; function plumbEventHandlersToItemCreationElements(customizationSelector){ for (var c=0; c < requiredItemElements.length-2; c++) itemFormErrors["item_" + requiredItemElements[c]] = creatingNewItem ? -1 : false; for (var c=1; c < requiredItemElements.length-1; c++) itemFormErrors["length_" + requiredItemElements[c]] = false; if (!creatingNewItem){ var arrayOfForms = document.getElementsByTagName('form'); var newItemId; for (var c=0; c < arrayOfForms.length; c++){ newItemId = arrayOfForms[c].id; var mtch = newItemId.match(/item/); if (mtch != null) break; } controlNamePrefix = newItemId.replace(/^edit_/, ""). replace(/item_?[0-9]*$/, ""); originalItemName = $(controlNamePrefix + 'item_name').value; } var testing = $('item_sti_type'); if (testing != null && testing.type != "hidden") thereIsATypeControl = true; else itemFormErrors['item_sti_type'] = false testing = $(controlNamePrefix + 'item_class_item_id') thereIsAClassControl = testing != null && testing.type != "hidden"; itemSubmit = $(controlNamePrefix + 'item_submit'); if (thereIsATypeControl){ var ck = $(controlNamePrefix + 'item_sti_type').value; if (ck != null && ck != "") itemFormErrors['item_sti_type'] = false; } if (thereIsAClassControl && thereIsATypeControl){ $(controlNamePrefix + 'item_class_item_id'). observe('change', classSelectOnchange); } if (thereIsATypeControl){ var a = $('item_sti_type'); a.observe('keyup', function(ev){ if (ev.keyCode != Event.KEY_TAB){ // onfocus does all we need for this setTimeout( function(){ checkFieldRequired(a); maybeClearIcon('sti_type'); } , dly ); } } ); a.observe('change', function(){ typeSelectOnchange(); checkFieldRequired(a); maybeClearIcon('sti_type'); } ); } var b = $(controlNamePrefix + 'item_title'); var c = $(controlNamePrefix + 'item_name'); function checkTitle(){ // this check is unique to Title, do here var mtch = b.value.match(/\n|\r/m); if (mtch != null && mtch.length > 0){ $('title_multi_line').className = "helpTextFlagged"; itemFormErrors['ml_title'] = true; $('title_error_icon').src = "/images/error_error_icon.png"; } else { $('title_multi_line').className = ""; itemFormErrors['ml_title'] = false; } checkFieldRequired(b); checkFieldLength(b, indexTitle); maybeClearIcon('title'); if (creatingNewItem){ var emptyToNotEmpty = generateFromTitle(b, c, checkName); if (emptyToNotEmpty){ itemFormErrors["item_name"] = false; $('name_required').className = ""; maybeClearIcon('name'); } } nameFieldValidityCheck(); } b.observe('change', function(){checkTitle();}); b.observe('keyup', function(ev){ if (ev.keyCode != Event.KEY_TAB) setTimeout(checkTitle, dly); } ); function checkName(){ nameFieldValidityCheck(); checkFieldRequired(c); checkFieldLength(c, indexName); maybeClearIcon('name'); if (creatingNewItem) generateToName(b, c); // do last, because we're going to skip part of this if other errors... maybeCheckNameUniqueness(nameAjaxStart); } c.observe('change', function(){checkName();}); c.observe('keyup', function(ev){ if (ev.keyCode != Event.KEY_TAB) setTimeout(checkName, dly); } ); function nameFieldValidityCheck(){ var mtch, val = $(controlNamePrefix + 'item_name').value; $('name_start_char').className = ""; itemFormErrors['char_1_name'] = false; if (val.length > 0){ mtch = val.match(/^[a-zA-Z]/); if (mtch == null || mtch.length == 0){ $('name_start_char').className = "helpTextFlagged"; itemFormErrors['char_1_name'] = true; $('name_error_icon').src = "/images/error_error_icon.png"; } } $('name_nth_char').className = ""; itemFormErrors['char_N_name'] = false; if (val.length > 1){ mtch = c.value.match(/^.[a-zA-Z0-9_-]+$/); if (mtch == null || mtch.length == 0){ $('name_nth_char').className = "helpTextFlagged"; itemFormErrors['char_N_name'] = true; $('name_error_icon').src = "/images/error_error_icon.png"; } } } var d = $(controlNamePrefix + 'item_description'); function checkDescription(){ checkFieldRequired(d); checkFieldLength(d, indexDescription); maybeClearIcon('description'); } d.observe('change', function(){checkDescription();}); d.observe('keyup', function(ev){ if (ev.keyCode != Event.KEY_TAB) setTimeout(checkDescription, dly); } ); if (creatingNewItem) makeButtonSeemDisabled(itemSubmit);// items/new -- can't submit a blank form else makeButtonSeemEnabled(itemSubmit); // items/##/edit -- can submit as-is form if (customizationSelector != "submitViaModalbox"){ itemSubmit.observe('click', function(ev){ if (!okToSubmitItemForm()) ev.stop(); else if (thereIsATypeControl) $('item_sti_type').disabled = false; } ); } if (thereIsATypeControl) a.observe('focus', function(){onfocusCommonBehavior(a);}); b.observe('focus', function(){onfocusCommonBehavior(b);}); c.observe('focus', function(){onfocusCommonBehavior(c);}); d.observe('focus', function(){onfocusCommonBehavior(d);}); itemSubmit. observe('focus', function(){onfocusCommonBehavior(itemSubmit);}); } function flagItemAsRequired(elemName){ var req = $(elemName + "_required"); if ( req != null ){ req.className = "helpTextFlagged"; $(elemName + "_error_icon").src = "/images/error_error_icon.png"; } else { $(elemName + "_recommended").className = "helpTextFlagged"; $(elemName + "_error_icon").src = "/images/warn_error_icon.png"; } } function maybeClearIcon(field){ icon = $(field + "_error_icon"); var mtch = icon.src.match(/blank_/); // if icon already clear, then don't need to actually clear if (mtch == null || mtch.length == 0){ // "recommended" is a special case.... var descReco = itemFormErrors["item_description"]; itemFormErrors["item_description"] = false; var canClear = true; for (var err in itemFormErrors){ mtch = err.match(new RegExp(field)); if (mtch != null && mtch.length > 0 && itemFormErrors[err]){ canClear = false; break; } } itemFormErrors["item_description"] = descReco; // restore if (canClear){ if (field == "description" && descReco) icon.src = "/images/warn_error_icon.png"; else icon.src = "/images/blank_error_icon.png"; } } // if *all* the error flags are clear, then indicate that we can submit for (var err in itemFormErrors) if (err != "item_description" && itemFormErrors[err] != false){ makeButtonSeemDisabled(itemSubmit); return; } makeButtonSeemEnabled(itemSubmit); } function onfocusCommonBehavior(elem){ var eId = elem.id; var c; for (c=0; c < requiredItemElements.length && eId != controlNamePrefix + "item_" + requiredItemElements[c]; c++) ; if (c >= requiredItemElements.length) return; // Hmmmm..... maybe not var lastToCheck = c-1; var errFlag = false; c = thereIsATypeControl ? 0 : 1; for (; c <= lastToCheck; c++){ var ck = $(controlNamePrefix + "item_" + requiredItemElements[c]); if (ck.value == null || ck.value == ""){ itemFormErrors["item_" + requiredItemElements[c]] = true; flagItemAsRequired(requiredItemElements[c]); if (c < requiredItemElements.length-2) errFlag = true; } else itemFormErrors["item_" + requiredItemElements[c]] = false; } if (errFlag) makeButtonSeemDisabled(itemSubmit); // refresh everything if (thereIsATypeControl) maybeClearIcon('sti_type'); maybeClearIcon('title'); maybeClearIcon('name'); maybeClearIcon('description'); maybeCheckNameUniqueness(nameAjaxStart); } function checkFieldRequired(f){ var idStr = f.id; if (!creatingNewItem) idStr = idStr.replace(new RegExp(controlNamePrefix), ""); var name = idStr.match(/item_(.+)$/)[1]; if (f.value == null || f.value == ""){ flagItemAsRequired(name); itemFormErrors[idStr] = true; } else { var req = $(name + "_required"); if ( req != null ) req.className = ""; else $(name + "_recommended").className = ""; itemFormErrors[idStr] = false; } } function checkFieldLength(elem, index){ var name = elem.id.match(/item_(.+)$/)[1]; if (elem.value.length > maxLengths[index]){ $(name + '_too_long').className = "helpTextFlagged"; itemFormErrors['length_' + name] = true; $(name + "_error_icon").src = "/images/error_error_icon.png"; } else { $(name + '_too_long').className = ""; itemFormErrors['length_' + name] = false; } } function genDialog(){ var newDialogText = "<p>"; var accum = false; for (var c=0; c < requiredItemElements.length-2; c++){ if (itemFormErrors["item_" + requiredItemElements[c]] == true){ accum = true; newDialogText += "The " + itemElementNames[c] + " is required. "; } } for (var c=1; c < requiredItemElements.length-1; c++){ if (itemFormErrors["length_" + requiredItemElements[c]] == true){ accum = true; newDialogText += "The " + itemElementNames[c] + " field must be " + maxLengths[c] + " or fewer characters. "; } } if (accum){ newDialogText += "</p>"; var titleText = creatingNewItem ? "Can't create this item yet" : "Can't update this item"; Modalbox.show(newDialogText, { title: titleText, slideDownDuration: 0.25, slideUpDuration: 0.1 } ); } return accum; } function clearNameUniquenessIndicators(){ $('name_must_be_unique').className = ""; $('name_is_unique').className = "confirmationTextInvisible"; $('name_status_icon').src = '/images/blank_status_icon.png'; } function nameUCheckSuccess(transport){ ajaxRequestInProgress = null; $('name_must_be_unique').className = "helpTextFlagged"; $('name_status_icon').src = '/images/error_status_icon.png'; itemFormErrors["unique_name"] = true; makeButtonSeemDisabled(itemSubmit); } function nameUCheckFailure(transport){ ajaxRequestInProgress = null; if (transport.status == 404){ $('name_is_unique').className = "confirmationTextShow"; $('name_status_icon').src = '/images/good_status_icon.png'; } else $('name_status_icon').src = '/images/blank_status_icon.png'; } function launchNameUniquenessCheck(){ // we only have one timer ever, and this call shows it just expired, so... uniquenessTimerId = -1; var l = window.location; var lookup = l.protocol + "//" + l.hostname + ":" + l.port + '/w/items/lookup'; ajaxRequestInProgress = new Ajax.Request( lookup, { method: 'get', parameters: "name=" + $F(controlNamePrefix + 'item_name'), onSuccess: nameUCheckSuccess, onFailure: nameUCheckFailure }); $('name_status_icon').src = '/images/working_status_icon.gif'; } function maybeCheckNameUniqueness(delay){ var name = $(controlNamePrefix + 'item_name'); if (name.value != valueWhenLastChecked){ valueWhenLastChecked = name.value; if (uniquenessTimerId != -1) clearTimeout(uniquenessTimerId); uniquenessTimerId = -1; if (ajaxRequestInProgress != null) ajaxRequestInProgress.transport.abort(); ajaxRequestInProgress = null; clearNameUniquenessIndicators(); var old = itemFormErrors["unique_name"]; itemFormErrors["unique_name"] = false; if (old) maybeClearIcon('name'); var mtch = $('name_error_icon').src.match(/error_error_icon/); if (name.value != null && name.value != "" && name.value != originalItemName && (mtch == null || mtch.length == 0)){ if (delay > 0) uniquenessTimerId = setTimeout(launchNameUniquenessCheck, delay); else launchNameUniquenessCheck(); } } } function getCurrentType(){ return $('item_sti_type').value; } function okToSubmitItemForm(){ onfocusCommonBehavior(itemSubmit); var errors = genDialog(); if (errors) makeButtonSeemDisabled(itemSubmit); else makeButtonSeemEnabled(itemSubmit); return !errors; } function classSelectOnchange(){ var class_ctrl = $(controlNamePrefix + 'item_class_item_id'); var type_ctrl = $('item_sti_type'); var class_val = class_ctrl.value; type_ctrl.disabled = false; if ( class_val.search( /^[0-9]+$/ ) == -1 ) return; else { var new_type = class_to_type['id' + class_val]; if (typeof new_type == 'undefined') return; type_ctrl.value = new_type; type_ctrl.disabled = true; } } // function to highlight help text based on Type <select> element state function typeSelectOnchange(){ var sel = $('item_sti_type'); var tags = [ "category_title", "category_desc", "individual_title", "individual_desc", "property_title", "property_desc" ]; if ($(tags[0]) == null) // this version of form w/o explanatory text return; for (var c=0; c != tags.length; c++) $(tags[c]).className = ""; if (sel.value == "CategoryItem"){ $(tags[0]).className = "titleSelectedItemDescription"; $(tags[1]).className = "bodySelectedItemDescription"; } else if (sel.value == "IndividualItem"){ $(tags[2]).className = "titleSelectedItemDescription"; $(tags[3]).className = "bodySelectedItemDescription"; } else if (sel.value == "PropertyItem"){ $(tags[4]).className = "titleSelectedItemDescription"; $(tags[5]).className = "bodySelectedItemDescription"; } }