r77418 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r77417‎ | r77418 | r77419 >
Date:03:33, 29 November 2010
Author:yaron
Status:deferred
Tags:
Comment:
Another semi-overhaul of the Javascript code:
- changed much of the code that accesses certain elements of the page to use jQuery's built-in selectors, allowing for shorter code.
- added "hiddenBySF" class to divs hidden by "show on select", so that SF can ignore only those divs during validation/form submit.
- improved code to extend comboboxes, via a fix from Pete C.
- removed validate_mandatory_combobox() function - comboboxes are now handled by generic validate_mandatory_field().
- finally got error messages that no longer apply to go away if a form is re-submitted and fails validation more than once.
Modified paths:
  • /trunk/extensions/SemanticForms/libs/SemanticForms.js (modified) (history)

Diff [purge]

Index: trunk/extensions/SemanticForms/libs/SemanticForms.js
@@ -209,25 +209,36 @@
210210 * Functions for handling 'show on select'
211211 */
212212
213 -// show the relevant div if any one of the relevant options are passed in
214 -// to the relevant dropdown - otherwise, hide it
 213+// Display a div that would otherwise be hidden by "show on select".
 214+function showDiv(div_id) {
 215+ jQuery('#' + div_id).find("input, select, textarea").removeClass('hiddenBySF');
 216+ jQuery('#' + div_id).show();
 217+}
 218+
 219+// Hide a div due to "show on select". The CSS class is there so that SF can
 220+// ignore the div's contents when the form is submitted.
 221+function hideDiv(div_id) {
 222+ jQuery('#' + div_id).find("input, select, textarea").addClass('hiddenBySF');
 223+ jQuery('#' + div_id).hide();
 224+}
 225+
 226+// Used for the dropdown input only.
 227+// Show the relevant div if any one of the relevant options are passed in
 228+// to this dropdown - otherwise, hide it
215229 function showIfSelected(input_id, options_array, div_id) {
216 - the_input = document.getElementById(input_id);
217 - the_div = document.getElementById(div_id);
218 - if ( the_div == null ) { return; }
 230+ input_val = jQuery('#' + input_id).val();
219231 for (var i in options_array) {
220 - if (the_input.value == options_array[i]) {
221 - the_div.style.display = ''; // return to default
 232+ if (input_val == options_array[i]) {
 233+ showDiv(div_id);
222234 return;
223235 }
224236 }
225 - the_div.style.display = 'none';
 237+ hideDiv(div_id);
226238 }
227239
228240 // Like showIfSelected(), but only for list boxes
229241 function showIfSelectedInListBox(input_id, options_array, div_id) {
230242 the_input = document.getElementById(input_id);
231 - the_div = document.getElementById(div_id);
232243 // Show the div if any of the list box's selected options match
233244 // any of the options that point to this div
234245 // - we have to cycle through because the field "selectedOptions" is
@@ -237,28 +248,27 @@
238249 cur_option = the_input.item(i).text;
239250 for (var j in options_array) {
240251 if (cur_option == options_array[j]) {
241 - the_div.style.display = ''; // return to default
 252+ showDiv(div_id);
242253 return;
243254 }
244255 }
245256 }
246257 }
247 - the_div.style.display = 'none';
 258+ hideDiv(div_id);
248259 }
249260
250 -// show the relevant div if any one of the relevant checkboxes are
251 -// checked - otherwise, hide it
 261+// Used for checkboxes and radiobuttons.
 262+// Show the relevant div if any one of the relevant selections are
 263+// checked - otherwise, hide it.
252264 function showIfChecked(checkbox_inputs, div_id) {
253 - the_div = document.getElementById(div_id);
254 - if ( the_div == null ) { return; }
255265 for (var i in checkbox_inputs) {
256266 checkbox = document.getElementById(checkbox_inputs[i]);
257267 if (checkbox != null && checkbox.checked) {
258 - the_div.style.display = ''; // return to default
 268+ showDiv(div_id);
259269 return;
260270 }
261271 }
262 - the_div.style.display = 'none';
 272+ hideDiv(div_id);
263273 }
264274
265275 // Evaluate an array of passed-in JS calls - this is a hack, but I can't
@@ -267,31 +277,24 @@
268278 eval(sfgShowOnSelectCalls[i]);
269279 }
270280
271 -function existsAndVisible(field) {
272 - // there's a major bug in the current implementation, which is that
273 - // it ignores fields hidden by the Header Tabs extension and others -
274 - // for now, we'll just override the attempted smartness and say that
275 - // everything is visible.
276 - return true;
277 - //return (field && field.offsetWidth);
278 -}
279 -
280281 function validate_mandatory_field(field_id, info_id) {
281 - var field = document.getElementById(field_id);
282 - // if there's nothing at that field ID, ignore it - it's probably
283 - // a hidden field
284 - if (!existsAndVisible(field)) {
 282+ // if the field was hidden via "show on select", ignore it
 283+ if (jQuery('#' + field_id).hasClass('hiddenBySF')) {
285284 return true;
286285 }
287 - if (field.value.replace(/\s+/, '') == '') {
288 - var info_span = document.getElementById(info_id);
289 - if ( info_span == null ) {
290 - alert ("no info span found at " + info_id + "!");
291 - } else {
292 - info_span.innerHTML = sfgBlankErrorStr;
293 - }
 286+ var fieldVal = jQuery('#' + field_id).val();
 287+ if (fieldVal == null) {
 288+ var isEmpty = true;
 289+ } else if (jQuery.isArray(fieldVal)) {
 290+ var isEmpty = (fieldVal.length == 0);
 291+ } else {
 292+ var isEmpty = (fieldVal.replace(/\s+/, '') == '');
 293+ }
 294+ if (isEmpty) {
 295+ jQuery('#' + info_id).html(sfgBlankErrorStr);
294296 return false;
295297 } else {
 298+ jQuery('#' + info_id).html('');
296299 return true;
297300 }
298301 }
@@ -299,55 +302,28 @@
300303 // Special handling for radiobuttons, because what's being checked
301304 // is the first radiobutton, which has value of "None"
302305 function validate_mandatory_radiobutton(none_button_id, info_id) {
303 - none_button = document.getElementById(none_button_id);
304 - if (none_button && none_button.checked) {
305 - info_span = document.getElementById(info_id);
306 - info_span.innerHTML = sfgBlankErrorStr;
307 - return false;
308 - } else {
 306+ // if the field was hidden via "show on select", ignore it
 307+ if (jQuery('#' + none_button_id).hasClass('hiddenBySF')) {
309308 return true;
310309 }
311 -}
312 -
313 -function validate_mandatory_combobox(field_id, info_id) {
314 - var field = jQuery('input#' + field_id);
315 - // if there's nothing at that field ID, ignore it - it's probably
316 - // a hidden field
317 - if (!existsAndVisible(field)) {
318 - return true;
319 - }
320 - // FIXME
321 - // field.val() unfortunately doesn't work in IE - it just returns
322 - // "undefined". For now, if that happens, just exit
323 - var value = field.val();
324 - if (value == undefined) {
325 - //alert(field.html());
326 - return true;
327 - }
328 - if (value.replace(/\s+/, '') == '') {
329 - var info_span = document.getElementById(info_id);
330 - info_span.innerHTML = sfgBlankErrorStr;
 310+ if (jQuery('#' + none_button_id).is(':checked')) {
 311+ jQuery('#' + info_id).html(sfgBlankErrorStr);
331312 return false;
332313 } else {
 314+ jQuery('#' + info_id).html('');
333315 return true;
334316 }
335317 }
336318
337319 function validate_mandatory_checkboxes(field_id, info_id) {
338 - // get all checkboxes - the "field_id" in this case is the span
339 - // surrounding all the checkboxes
340 - var checkboxes = jQuery('span#' + field_id + " > span > input");
341 - var all_unchecked = true;
342 - for (var i = 0; i < checkboxes.length; i++) {
343 - if (checkboxes[i].checked) {
344 - all_unchecked = false;
345 - }
346 - }
347 - if (all_unchecked) {
348 - info_span = document.getElementById(info_id);
349 - info_span.innerHTML = sfgBlankErrorStr;
 320+ // Get the number of checked checkboxes within this span - the
 321+ // "field_id" in this case is the span surrounding all the checkboxes.
 322+ var numChecked = jQuery('span#' + field_id + " > span > input:checked").size();
 323+ if (numChecked == 0) {
 324+ jQuery('#' + info_id).html(sfgBlankErrorStr);
350325 return false;
351326 } else {
 327+ jQuery('#' + info_id).html('');
352328 return true;
353329 }
354330 }
@@ -375,52 +351,52 @@
376352 }
377353
378354 function validate_field_type(field_id, type, info_id) {
379 - field = document.getElementById(field_id);
380 - if (type != 'date' && field.value == '') {
 355+ fieldVal = jQuery('#' + field_id).val();
 356+ if (type != 'date' && fieldVal == '') {
381357 return true;
382358 } else {
383359 if (type == 'URL') {
384360 // code borrowed from http://snippets.dzone.com/posts/show/452
385361 var url_regexp = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
386 - if (url_regexp.test(field.value)) {
 362+ if (url_regexp.test(fieldVal)) {
 363+ jQuery('#' + info_id).html('');
387364 return true;
388365 } else {
389 - info_span = document.getElementById(info_id);
390 - info_span.innerHTML = sfgBadURLErrorStr;
 366+ jQuery('#' + info_id).html(sfgBadURLErrorStr);
391367 return false;
392368 }
393369 } else if (type == 'email') {
394370 // code borrowed from http://javascript.internet.com/forms/email-validation---basic.html
395371 var email_regexp = /^\s*\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,6})+\s*$/;
396 - if (email_regexp.test(field.value)) {
 372+ if (email_regexp.test(fieldVal)) {
 373+ jQuery('#' + info_id).html('');
397374 return true;
398375 } else {
399 - info_span = document.getElementById(info_id);
400 - info_span.innerHTML = sfgBadEmailErrorStr;
 376+ jQuery('#' + info_id).html(sfgBadEmailErrorStr);
401377 return false;
402378 }
403379 } else if (type == 'number') {
404 - if (field.value.match(/^\s*\-?[\d\.,]+\s*$/)) {
 380+ if (fieldVal.match(/^\s*\-?[\d\.,]+\s*$/)) {
 381+ jQuery('#' + info_id).html('');
405382 return true;
406383 } else {
407 - info_span = document.getElementById(info_id);
408 - info_span.innerHTML = sfgBadNumberErrorStr;
 384+ jQuery('#' + info_id).html(sfgBadNumberErrorStr);
409385 return false;
410386 }
411387 } else if (type == 'date') {
412388 // validate only if day and year fields are both filled in
413 - day_field = document.getElementById(field_id + "_day");
414 - year_field = document.getElementById(field_id + "_year");
415 - if (day_field.value == '' || year_field.value == '') {
 389+ dayVal = jQuery('#' + field_id + "_day").val();
 390+ yearVal = jQuery('#' + field_id + "_year").val();
 391+ if (dayVal == '' || yearVal == '') {
 392+ jQuery('#' + info_id).html('');
416393 return true;
417 - } else if (day_field.value.match(/^\d+$/) &&
418 - day_field.value <= 31) {
 394+ } else if (dayVal.match(/^\d+$/) && dayVal <= 31) {
419395 // no year validation, since it can also include
420396 // 'BC' and possibly other non-number strings
 397+ jQuery('#' + info_id).html('');
421398 return true;
422399 } else {
423 - info_span = document.getElementById(info_id);
424 - info_span.innerHTML = sfgBadDateErrorStr;
 400+ jQuery('#' + info_id).html(sfgBadDateErrorStr);
425401 return false;
426402 }
427403 } else {
@@ -463,24 +439,14 @@
464440 if (num_errors > 0) {
465441 // add error header, if it's not there already
466442 if (! document.getElementById("form_error_header")) {
467 - var errorMsg = document.createElement('div');
468 - errorMsg.innerHTML = '<div id="form_error_header" class="warningMessage" style="font-size: medium">' + sfgFormErrorsHeader + '</div>';
469 - document.getElementById("contentSub").appendChild(errorMsg);
 443+ jQuery("#contentSub").append('<div id="form_error_header" class="warningMessage" style="font-size: medium">' + sfgFormErrorsHeader + '</div>');
470444 }
471445 scroll(0, 0);
 446+ } else {
 447+ // Disable inputs hidden due to "show on select", so that
 448+ // they aren't submitted by the form.
 449+ jQuery("form.createbox").find('.hiddenBySF').attr('disabled', 'disabled');
472450 }
473 - else
474 - {
475 - // Ensure that invisible fields, e.g. due to "show on select",
476 - // are not submitted in the HTTP POST, by setting them to
477 - // "disabled". Don't disable hidden fields, though (used e.g.
478 - // for edit timestamp).
479 - var inputs = jQuery("form.createbox").find("input, select, textarea");
480 - for (var i = 0; i < inputs.length; i++) {
481 - input = inputs[i];
482 - input.disabled = input.type != "hidden" && !existsAndVisible(input);
483 - }
484 - }
485451 return (num_errors == 0);
486452 }
487453
@@ -563,7 +529,7 @@
564530
565531 //Add the new instance
566532 main_div.appendChild(new_div);
567 - attachAutocompleteToAllFields(new_div);
 533+ new_div.attachAutocompleteToAllFields;
568534
569535 // For each 'upload file' link in this latest instance,
570536 // add a call to fancybox()
@@ -596,66 +562,49 @@
597563 }
598564
599565 // Activate autocomplete functionality for every field on the document
600 -function attachAutocompleteToAllDocumentFields()
601 -{
602 - var forms = document.getElementsByTagName("form");
603 - var x;
604 - for (x = 0; x < forms.length; x++) {
605 - if (forms[x].name == "createbox") {
606 - attachAutocompleteToAllFields(forms[x]);
607 - }
608 - }
 566+function attachAutocompleteToAllDocumentFields() {
 567+ jQuery('form[name="createbox"]').attachAutocompleteToAllFields();
609568 }
610569
611 -// Activate autocomplete functionality for every field under the specified
612 -// element
613 -function attachAutocompleteToAllFields(base)
614 -{
615 - var inputs = base.getElementsByTagName("input");
616 - var y;
617 - for (y = 0; y < inputs.length; y++) {
618 - attachAutocompleteToField(inputs[y].id);
619 - }
620 - // don't forget the textareas
621 - inputs = base.getElementsByTagName("textarea");
622 - for (y = 0; y < inputs.length; y++) {
623 - attachAutocompleteToField(inputs[y].id);
624 - }
 570+// Activate autocomplete functionality for every SF input field within the
 571+// specified element
 572+jQuery.fn.attachAutocompleteToAllFields = function() {
 573+ this.find("input[id^=input_], textarea[id^=input_]").each(
 574+ function() {
 575+ attachAutocompleteToField(this);
 576+ }
 577+ );
625578 }
626579
627580 // Activate autocomplete functionality for the specified field
628 -function attachAutocompleteToField(input_id)
629 -{
630 - // Check input id for the proper format, to ensure this is for SF
631 - if (input_id.substr(0,6) == 'input_')
632 - {
633 - // Extract the field ID number from the input field
634 - var field_num = parseInt(input_id.substring(input_id.lastIndexOf('_') + 1, input_id.length),10);
635 - // Add the autocomplete string, if a mapping exists.
636 - var field_string = sfgAutocompleteMappings[field_num];
637 - if (field_string) {
638 - var div_id = input_id.replace(/input_/g, 'div_');
639 - var field_values = field_string.split(',');
640 - var delimiter = null;
641 - var data_source = field_values[0];
642 - if (field_values[1] == 'list') {
643 - delimiter = ",";
644 - if (field_values[2] != null) {
645 - delimiter = field_values[2];
646 - }
 581+function attachAutocompleteToField(input) {
 582+ input_id = input.id;
 583+ // Extract the field ID number from the input field
 584+ var field_num = parseInt(input_id.substring(input_id.lastIndexOf('_') + 1, input_id.length),10);
 585+ // Add the autocomplete string, if a mapping exists.
 586+ var field_string = sfgAutocompleteMappings[field_num];
 587+ if (field_string) {
 588+ var div_id = input_id.replace(/input_/g, 'div_');
 589+ var field_values = field_string.split(',');
 590+ var delimiter = null;
 591+ var data_source = field_values[0];
 592+ if (field_values[1] == 'list') {
 593+ delimiter = ",";
 594+ if (field_values[2] != null) {
 595+ delimiter = field_values[2];
647596 }
648 - if (sfgAutocompleteValues[field_string] != null) {
649 - sf_autocomplete(input_id, div_id, sfgAutocompleteValues[field_string], null, null, delimiter, data_source);
650 - } else {
651 - sf_autocomplete(input_id, div_id, null, wgScriptPath + "/api.php", sfgAutocompleteDataTypes[field_string], delimiter, data_source);
652 - }
653597 }
 598+ if (sfgAutocompleteValues[field_string] != null) {
 599+ sf_autocomplete(input_id, div_id, sfgAutocompleteValues[field_string], null, null, delimiter, data_source);
 600+ } else {
 601+ sf_autocomplete(input_id, div_id, null, wgScriptPath + "/api.php", sfgAutocompleteDataTypes[field_string], delimiter, data_source);
 602+ }
654603 }
655604 }
656605
657606 for (var i = 0; i < sfgComboBoxInputs.length; i++ ) {
658607 var input_num = sfgComboBoxInputs[i];
659 - jQuery(function() {
 608+ jQuery(document).ready(function() {
660609 jQuery("#input_" + input_num).combobox();
661610 });
662611 }
@@ -670,7 +619,6 @@
671620 }
672621
673622
674 -
675623 /* extending jquery functions */
676624
677625 (function(jQuery) {
@@ -684,6 +632,7 @@
685633 var curval = select[0].options[0].value;
686634 var input = jQuery("<input id=\"" + id + "\" type=\"text\" name=\" " + name + " \" value=\"" + curval + "\">")
687635 .insertAfter(select)
 636+ .attr("tabIndex", select.attr("tabIndex"))
688637 .autocomplete({
689638 source: function(request, response) {
690639 if ( autocompleteOnAllChars ) {

Status & tagging log