r78129 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r78128‎ | r78129 | r78130 >
Date:18:13, 9 December 2010
Author:yaron
Status:deferred
Tags:
Comment:
Another major overhaul: changed around much of the code to make better use of jQuery and the class-based approach that it enables, instead of the element-ID approach from before; this enabled a lot of code to get simplified. Also fixed handling of comboboxes, autogrow and uploadable fields in multiple-instance templates, and some other small improvements.
Modified paths:
  • /trunk/extensions/SemanticForms/libs/SemanticForms.js (modified) (history)

Diff [purge]

Index: trunk/extensions/SemanticForms/libs/SemanticForms.js
@@ -10,18 +10,11 @@
1111 * @author Eugene Mednikov
1212 */
1313
14 -function isEmpty(obj) {
15 - for(var i in obj) {
16 - return false;
17 - }
18 - return true;
19 -}
20 -
21 -function sf_autocomplete(input_name, container_name, values, api_url, data_type, delimiter, data_source) {
 14+jQuery.fn.sfAutocomplete = function(values, api_url, data_type, delimiter, data_source) {
2215 var myServer = api_url;
2316 jQuery.noConflict();
2417
25 -/* extending jquery functions for custom highlighting */
 18+ /* extending jQuery functions for custom highlighting */
2619 jQuery.ui.autocomplete.prototype._renderItem = function( ul, item) {
2720
2821 var re = new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + this.term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi");
@@ -65,11 +58,11 @@
6659 });
6760
6861 if (values != null) {
 62+ // Local autocompletion
6963
70 - /* delimiter != '' means multiple autocomplete */
71 -
7264 if (delimiter != null) {
73 - jQuery(document).ready(function(){
 65+ // Autocomplete for multiple values
 66+
7467 function split(val) {
7568 return val.split(delimiter);
7669 }
@@ -77,7 +70,7 @@
7871 return split(term).pop();
7972 }
8073
81 - jQuery("#" + input_name).autocomplete({
 74+ jQuery(this).autocomplete({
8275 minLength: 0,
8376 source: function(request, response) {
8477
@@ -99,17 +92,15 @@
10093 return false;
10194 }
10295 });
103 - } );
10496
105 - } else{
106 - jQuery(document).ready(function(){
107 - jQuery("#" + input_name).autocomplete({
 97+ } else {
 98+ // Autocomplete for a single value
 99+ jQuery(this).autocomplete({
108100 source:values
109101 });
110 - } ) ;
111102 }
112103 } else {
113 -
 104+ // Remote autocompletion
114105 if (data_type == 'property')
115106 myServer += "?action=sfautocomplete&format=json&property=" + data_source;
116107 else if (data_type == 'relation')
@@ -125,55 +116,49 @@
126117
127118 if (delimiter != null) {
128119
129 - jQuery(document).ready(function(){
130120 function split(val) {
131121 return val.split(delimiter);
132122 }
133123 function extractLast(term) {
134124 return split(term).pop();
135125 }
136 - jQuery("#" + input_name).autocomplete({
137 - source: function(request, response) {
138 - jQuery.getJSON(myServer, {
139 - substr: extractLast(request.term)
140 - }, function( data ) {
141 - response(jQuery.map(data.sfautocomplete, function(item) {
142 - return {
143 - value: item.title
144 - }
145 - }))
146 -
147 - });
148 - },
149 - search: function() {
150 - // custom minLength
151 - var term = extractLast(this.value);
152 - if (term.length < 1) {
 126+ jQuery(this).autocomplete({
 127+ source: function(request, response) {
 128+ jQuery.getJSON(myServer, {
 129+ substr: extractLast(request.term)
 130+ }, function( data ) {
 131+ response(jQuery.map(data.sfautocomplete, function(item) {
 132+ return {
 133+ value: item.title
 134+ }
 135+ }))
 136+ });
 137+ },
 138+ search: function() {
 139+ // custom minLength
 140+ var term = extractLast(this.value);
 141+ if (term.length < 1) {
 142+ return false;
 143+ }
 144+ },
 145+ focus: function() {
 146+ // prevent value inserted on focus
153147 return false;
 148+ },
 149+ select: function(event, ui) {
 150+ var terms = split( this.value );
 151+ // remove the current input
 152+ terms.pop();
 153+ // add the selected item
 154+ terms.push( ui.item.value );
 155+ // add placeholder to get the comma-and-space at the end
 156+ terms.push("");
 157+ this.value = terms.join(delimiter);
 158+ return false;
154159 }
155 - },
156 - focus: function() {
157 - // prevent value inserted on focus
158 - return false;
159 - },
160 - select: function(event, ui) {
161 - var terms = split( this.value );
162 - // remove the current input
163 - terms.pop();
164 - // add the selected item
165 - terms.push( ui.item.value );
166 - // add placeholder to get the comma-and-space at the end
167 - terms.push("");
168 - this.value = terms.join(delimiter);
169 - return false;
170 - }
171 - });
172 -
173 -
174 - } );
 160+ } );
175161 } else {
176 - jQuery(document).ready(function(){
177 - jQuery("#" + input_name).autocomplete({
 162+ jQuery(this).autocomplete({
178163 minLength: 1,
179164 source: function(request, response) {
180165 jQuery.ajax({
@@ -181,27 +166,24 @@
182167 dataType: "json",
183168 data: {
184169 substr:request.term
185 -
186170 },
187171 success: function( data ) {
188172 response(jQuery.map(data.sfautocomplete, function(item) {
189 - return {
190 - value: item.title
191 - }
192 - }))
 173+ return {
 174+ value: item.title
 175+ }
 176+ }))
193177 }
194178 });
195 - },
 179+ },
196180 open: function() {
197181 jQuery(this).removeClass("ui-corner-all").addClass("ui-corner-top");
198182 },
199183 close: function() {
200184 jQuery(this).removeClass("ui-corner-top").addClass("ui-corner-all");
201185 }
202 - });
203 -
204 - } );
205 - }
 186+ } );
 187+ }
206188 }
207189 };
208190
@@ -211,24 +193,25 @@
212194
213195 // Display a div that would otherwise be hidden by "show on select".
214196 function showDiv(div_id) {
215 - jQuery('#' + div_id).find("input, select, textarea").removeClass('hiddenBySF');
 197+ jQuery('#' + div_id).find(".hiddenBySF").removeClass('hiddenBySF');
216198 jQuery('#' + div_id).show();
217199 }
218200
219201 // Hide a div due to "show on select". The CSS class is there so that SF can
220202 // ignore the div's contents when the form is submitted.
221203 function hideDiv(div_id) {
222 - jQuery('#' + div_id).find("input, select, textarea").addClass('hiddenBySF');
 204+ jQuery('#' + div_id).find("span, div").addClass('hiddenBySF');
223205 jQuery('#' + div_id).hide();
224206 }
225207
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
229 -function showIfSelected(input_id, options_array, div_id) {
230 - input_val = jQuery('#' + input_id).val();
231 - for (var i in options_array) {
232 - if (input_val == options_array[i]) {
 208+// Show this div if the current value is any of the relevant options -
 209+// otherwise, hide it.
 210+function showDivIfSelected(options, div_id, inputVal) {
 211+ for (var j in options) {
 212+ // If it's a listbox and the user has selected more than one
 213+ // value, it'll be an array - handle either case.
 214+ if ((jQuery.isArray(inputVal) && jQuery.inArray(options[j], inputVal) >= 0) ||
 215+ (!jQuery.isArray(inputVal) && (inputVal == options[j]))) {
233216 showDiv(div_id);
234217 return;
235218 }
@@ -236,34 +219,22 @@
237220 hideDiv(div_id);
238221 }
239222
240 -// Like showIfSelected(), but only for list boxes
241 -function showIfSelectedInListBox(input_id, options_array, div_id) {
242 - the_input = document.getElementById(input_id);
243 - // Show the div if any of the list box's selected options match
244 - // any of the options that point to this div
245 - // - we have to cycle through because the field "selectedOptions" is
246 - // apparently not supported on all browsers.
247 - for (var i = 0; i < the_input.options.length; i++) {
248 - if ( the_input.item(i).selected ) {
249 - cur_option = the_input.item(i).text;
250 - for (var j in options_array) {
251 - if (cur_option == options_array[j]) {
252 - showDiv(div_id);
253 - return;
254 - }
255 - }
256 - }
 223+// Used for handling 'show on select' for the 'dropdown' and 'listbox' inputs.
 224+jQuery.fn.showIfSelected = function() {
 225+ var inputVal = this.val();
 226+ var showOnSelectVals = sfgShowOnSelect[this.attr("id")];
 227+ for (i in showOnSelectVals) {
 228+ var options = showOnSelectVals[i][0];
 229+ var div_id = showOnSelectVals[i][1];
 230+ showDivIfSelected(options, div_id, inputVal);
257231 }
258 - hideDiv(div_id);
259232 }
260233
261 -// Used for checkboxes and radiobuttons.
262 -// Show the relevant div if any one of the relevant selections are
263 -// checked - otherwise, hide it.
264 -function showIfChecked(checkbox_inputs, div_id) {
265 - for (var i in checkbox_inputs) {
266 - checkbox = document.getElementById(checkbox_inputs[i]);
267 - if (checkbox != null && checkbox.checked) {
 234+// Show this div if any of the relevant selections are checked -
 235+// otherwise, hide it.
 236+jQuery.fn.showDivIfChecked = function(options, div_id) {
 237+ for (var i in options) {
 238+ if (jQuery(this).find('[value="' + options[i] + '"]').is(":checked")) {
268239 showDiv(div_id);
269240 return;
270241 }
@@ -271,18 +242,38 @@
272243 hideDiv(div_id);
273244 }
274245
275 -// Evaluate an array of passed-in JS calls - this is a hack, but I can't
276 -// think of a better solution
277 -for (var i = 0; i < sfgShowOnSelectCalls.length; i++ ) {
278 - eval(sfgShowOnSelectCalls[i]);
 246+// Used for handling 'show on select' for the 'checkboxes' and 'radiobutton'
 247+// inputs.
 248+jQuery.fn.showIfChecked = function() {
 249+ var showOnSelectVals = sfgShowOnSelect[this.attr("id")];
 250+ for (i in showOnSelectVals) {
 251+ var options = showOnSelectVals[i][0];
 252+ var div_id = showOnSelectVals[i][1];
 253+ this.showDivIfChecked(options, div_id);
 254+ }
279255 }
280256
281 -function validate_mandatory_field(field_id, info_id) {
282 - // if the field was hidden via "show on select", ignore it
283 - if (jQuery('#' + field_id).hasClass('hiddenBySF')) {
284 - return true;
 257+// Used for handling 'show on select' for the 'checkbox' input.
 258+jQuery.fn.showIfCheckedCheckbox = function() {
 259+ var div_id = sfgShowOnSelect[this.attr("id")];
 260+ if (jQuery(this).is(":checked")) {
 261+ showDiv(div_id);
 262+ } else {
 263+ hideDiv(div_id);
285264 }
286 - var fieldVal = jQuery('#' + field_id).val();
 265+}
 266+
 267+/*
 268+ * Validation functions
 269+ */
 270+
 271+// Display an error message on the end of an input.
 272+jQuery.fn.addErrorMessage = function(msg) {
 273+ this.append(' <span class="errorMessage">' + msg + '</span>');
 274+}
 275+
 276+jQuery.fn.validateMandatoryField = function() {
 277+ var fieldVal = this.find(".mandatoryField").val();
287278 if (fieldVal == null) {
288279 var isEmpty = true;
289280 } else if (jQuery.isArray(fieldVal)) {
@@ -291,250 +282,224 @@
292283 var isEmpty = (fieldVal.replace(/\s+/, '') == '');
293284 }
294285 if (isEmpty) {
295 - jQuery('#' + info_id).html(sfgBlankErrorStr);
 286+ this.addErrorMessage(sfgBlankErrorStr);
296287 return false;
297288 } else {
298 - jQuery('#' + info_id).html('');
299289 return true;
300290 }
301291 }
302292
303 -// Special handling for radiobuttons, because what's being checked
304 -// is the first radiobutton, which has value of "None"
305 -function validate_mandatory_radiobutton(none_button_id, info_id) {
306 - // if the field was hidden via "show on select", ignore it
307 - if (jQuery('#' + none_button_id).hasClass('hiddenBySF')) {
 293+jQuery.fn.validateMandatoryComboBox = function() {
 294+ if (this.find("input").val() == '') {
 295+ this.addErrorMessage(sfgBlankErrorStr);
 296+ return false;
 297+ } else {
308298 return true;
309299 }
310 - if (jQuery('#' + none_button_id).is(':checked')) {
311 - jQuery('#' + info_id).html(sfgBlankErrorStr);
 300+}
 301+
 302+jQuery.fn.validateMandatoryDateField = function() {
 303+ if (this.find(".dayInput").val() == '' ||
 304+ this.find(".monthInput").val() == '' ||
 305+ this.find(".yearInput").val() == '') {
 306+ this.addErrorMessage(sfgBlankErrorStr);
312307 return false;
313308 } else {
314 - jQuery('#' + info_id).html('');
315309 return true;
316310 }
317311 }
318312
319 -function validate_mandatory_checkboxes(field_id, info_id) {
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();
 313+// Special handling for radiobuttons, because what's being checked
 314+// is the first radiobutton, which has an empty value.
 315+jQuery.fn.validateMandatoryRadioButton = function() {
 316+ if (this.find("[value='']").is(':checked')) {
 317+ this.addErrorMessage(sfgBlankErrorStr);
 318+ return false;
 319+ } else {
 320+ return true;
 321+ }
 322+}
 323+
 324+jQuery.fn.validateMandatoryCheckboxes = function() {
 325+ // Get the number of checked checkboxes within this span - must
 326+ // be at least one.
 327+ var numChecked = this.find("input:checked").size();
323328 if (numChecked == 0) {
324 - jQuery('#' + info_id).html(sfgBlankErrorStr);
 329+ this.addErrorMessage(sfgBlankErrorStr);
325330 return false;
326331 } else {
327 - jQuery('#' + info_id).html('');
328332 return true;
329333 }
330334 }
331335
332 -// validate a mandatory field that exists across multiple instances of
333 -// a template - we have to find each one, matching on the pattern of its
334 -// ID, and validate it
335 -function validate_multiple_mandatory_fields(field_num) {
336 - var num_errors = 0;
337 - elems = document.getElementsByTagName("*");
338 - if ( ! elems) { elems = []; }
339 - var field_pattern = new RegExp('input_(.*)_' + field_num);
340 - for (var i = 0; i < elems.length; i++) {
341 - id = elems[i].id;
342 - if (matches = field_pattern.exec(id)) {
343 - instance_num = matches[1];
344 - var input_name = "input_" + instance_num + "_" + field_num;
345 - var info_name = "info_" + instance_num + "_" + field_num;
346 - if (! validate_mandatory_field(input_name, info_name)) {
347 - num_errors += 1;
348 - }
349 - }
 336+/*
 337+ * Type-based validation
 338+ */
 339+
 340+jQuery.fn.validateURLField = function() {
 341+ var fieldVal = this.find("input").val();
 342+ // code borrowed from http://snippets.dzone.com/posts/show/452
 343+ var url_regexp = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
 344+ if (fieldVal == "" || url_regexp.test(fieldVal)) {
 345+ return true;
 346+ } else {
 347+ this.addErrorMessage(sfgBadURLErrorStr);
 348+ return false;
350349 }
351 - return (num_errors == 0);
352350 }
353351
354 -function validate_field_type(field_id, type, info_id) {
355 - fieldVal = jQuery('#' + field_id).val();
356 - if (type != 'date' && fieldVal == '') {
 352+jQuery.fn.validateEmailField = function() {
 353+ var fieldVal = this.find("input").val();
 354+ // code borrowed from http://javascript.internet.com/forms/email-validation---basic.html
 355+ var email_regexp = /^\s*\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,6})+\s*$/;
 356+ if (fieldVal == '' || email_regexp.test(fieldVal)) {
357357 return true;
358358 } else {
359 - if (type == 'URL') {
360 - // code borrowed from http://snippets.dzone.com/posts/show/452
361 - var url_regexp = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
362 - if (url_regexp.test(fieldVal)) {
363 - jQuery('#' + info_id).html('');
364 - return true;
365 - } else {
366 - jQuery('#' + info_id).html(sfgBadURLErrorStr);
367 - return false;
368 - }
369 - } else if (type == 'email') {
370 - // code borrowed from http://javascript.internet.com/forms/email-validation---basic.html
371 - var email_regexp = /^\s*\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,6})+\s*$/;
372 - if (email_regexp.test(fieldVal)) {
373 - jQuery('#' + info_id).html('');
374 - return true;
375 - } else {
376 - jQuery('#' + info_id).html(sfgBadEmailErrorStr);
377 - return false;
378 - }
379 - } else if (type == 'number') {
380 - if (fieldVal.match(/^\s*\-?[\d\.,]+\s*$/)) {
381 - jQuery('#' + info_id).html('');
382 - return true;
383 - } else {
384 - jQuery('#' + info_id).html(sfgBadNumberErrorStr);
385 - return false;
386 - }
387 - } else if (type == 'date') {
388 - // validate only if day and year fields are both filled in
389 - dayVal = jQuery('#' + field_id + "_day").val();
390 - yearVal = jQuery('#' + field_id + "_year").val();
391 - if (dayVal == '' || yearVal == '') {
392 - jQuery('#' + info_id).html('');
393 - return true;
394 - } else if (dayVal.match(/^\d+$/) && dayVal <= 31) {
395 - // no year validation, since it can also include
396 - // 'BC' and possibly other non-number strings
397 - jQuery('#' + info_id).html('');
398 - return true;
399 - } else {
400 - jQuery('#' + info_id).html(sfgBadDateErrorStr);
401 - return false;
402 - }
403 - } else {
404 - return true;
405 - }
 359+ this.addErrorMessage(sfgBadEmailErrorStr);
 360+ return false;
406361 }
407362 }
408363
409 -// Same as validate_multiple_mandatory_fields(), but for type validation
410 -function validate_type_of_multiple_fields(field_num, type) {
411 - var num_errors = 0;
412 - elems = document.getElementsByTagName("*");
413 - if ( ! elems) { elems = []; }
414 - var field_pattern = new RegExp('input_(.*)_' + field_num);
415 - for (var i = 0; i < elems.length; i++) {
416 - id = elems[i].id;
417 - if (matches = field_pattern.exec(id)) {
418 - instance_num = matches[1];
419 - var input_name = "input_" + instance_num + "_" + field_num;
420 - var info_name = "info_" + instance_num + "_" + field_num;
421 - if (! validate_field_type(input_name, type, info_name)) {
422 - num_errors += 1;
423 - }
424 - }
 364+jQuery.fn.validateNumberField = function() {
 365+ var fieldVal = this.find("input").val();
 366+ if (fieldVal == '' || fieldVal.match(/^\s*\-?[\d\.,]+\s*$/)) {
 367+ return true;
 368+ } else {
 369+ this.addErrorMessage(sfgBadNumberErrorStr);
 370+ return false;
425371 }
426 - return (num_errors == 0);
427372 }
428373
429 -jQuery('#sfForm').submit( function() { return validate_all(); } );
 374+jQuery.fn.validateDateField = function() {
 375+ // validate only if day and year fields are both filled in
 376+ var dayVal = this.find(".dayInput").val();
 377+ var yearVal = this.find(".yearInput").val();
 378+ if (dayVal == '' || yearVal == '') {
 379+ return true;
 380+ } else if (dayVal.match(/^\d+$/) && dayVal <= 31) {
 381+ // no year validation, since it can also include
 382+ // 'BC' and possibly other non-number strings
 383+ return true;
 384+ } else {
 385+ this.addErrorMessage(sfgBadDateErrorStr);
 386+ return false;
 387+ }
 388+}
430389
431 -function validate_all() {
 390+// If the form is submitted, validate everything!
 391+jQuery('#sfForm').submit( function() { return validateAll(); } );
 392+
 393+function validateAll() {
432394 var num_errors = 0;
433395
434 - // evaluate all the passed-in JS validation calls - as with the
435 - // "show on select" calls, this is a hack.
436 - for (var i = 0; i < sfgJSValidationCalls.length; i++ ) {
437 - if (! eval(sfgJSValidationCalls[i]) ) num_errors += 1;
438 - }
 396+ // Remove all old error messages.
 397+ jQuery(".errorMessage").remove();
439398
 399+ // Make sure all inputs are ignored in the "starter" instance
 400+ // of any multiple-instance template.
 401+ jQuery(".multipleTemplateStarter").find("span, div").addClass("hiddenBySF");
 402+
 403+ jQuery("span.inputSpan.mandatoryFieldSpan").not(".hiddenBySF").each( function() {
 404+ if (! jQuery(this).validateMandatoryField() ) num_errors += 1;
 405+ });
 406+ jQuery("div.ui-widget.mandatory").not(".hiddenBySF").each( function() {
 407+ if (! jQuery(this).validateMandatoryComboBox() ) num_errors += 1;
 408+ });
 409+ jQuery("span.dateInput.mandatoryFieldSpan").not(".hiddenBySF").each( function() {
 410+ if (! jQuery(this).validateMandatoryDateField() ) num_errors += 1;
 411+ });
 412+ jQuery("span.radioButtonSpan.mandatoryFieldSpan").not(".hiddenBySF").each( function() {
 413+ if (! jQuery(this).validateMandatoryRadioButton() ) num_errors += 1;
 414+ });
 415+ jQuery("span.checkboxesSpan.mandatoryFieldSpan").not(".hiddenBySF").each( function() {
 416+ if (! jQuery(this).validateMandatoryCheckboxes() ) num_errors += 1;
 417+ });
 418+ jQuery("span.URLInput").not(".hiddenBySF").each( function() {
 419+ if (! jQuery(this).validateURLField() ) num_errors += 1;
 420+ });
 421+ jQuery("span.emailInput").not(".hiddenBySF").each( function() {
 422+ if (! jQuery(this).validateEmailField() ) num_errors += 1;
 423+ });
 424+ jQuery("span.numberInput").not(".hiddenBySF").each( function() {
 425+ if (! jQuery(this).validateNumberField() ) num_errors += 1;
 426+ });
 427+ jQuery("span.dateInput").not(".hiddenBySF").each( function() {
 428+ if (! jQuery(this).validateDateField() ) num_errors += 1;
 429+ });
 430+
440431 if (num_errors > 0) {
441432 // add error header, if it's not there already
442 - if (! document.getElementById("form_error_header")) {
 433+ if (jQuery("#form_error_header").size() == 0) {
443434 jQuery("#contentSub").append('<div id="form_error_header" class="warningMessage" style="font-size: medium">' + sfgFormErrorsHeader + '</div>');
444435 }
445436 scroll(0, 0);
446437 } 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');
 438+ // Disable inputs hidden due to either "show on select" or
 439+ // because they're part of the "starter" div for
 440+ // multiple-instance templates, so that they aren't
 441+ // submitted by the form.
 442+ jQuery('.hiddenBySF').find("input, select, textarea").attr('disabled', 'disabled');
450443 }
451444 return (num_errors == 0);
452445 }
453446
454 -var num_elements = 0;
455 -
456 -for (var i = 0; i < sfgAdderButtons.length; i++) {
457 - var components = sfgAdderButtons[i].split(',');
458 - adderID = components[0];
459 - templateName = components[1];
460 - fieldNum = components[2];
461 - jQuery('#' + adderID).click( addInstanceEventHandler(templateName, fieldNum) );
462 -}
463 -
464447 function addInstanceEventHandler(templateName, fieldNum) {
465448 return function() {
466449 addInstance('starter_' + templateName, 'main_' + templateName, fieldNum);
467450 }
468451 }
469452
470 -function addInstance(starter_div_id, main_div_id, tab_index)
471 -{
472 - var starter_div = document.getElementById(starter_div_id);
473 - var main_div = document.getElementById(main_div_id);
 453+function addInstance(starter_div_id, main_div_id, tab_index) {
474454 num_elements++;
475455
476 - //Create the new instance
477 - var new_div = starter_div.cloneNode(true);
478 - var div_id = 'div_gen_' + num_elements;
479 - new_div.className = 'multipleTemplate';
480 - new_div.id = div_id;
481 - new_div.style.display = 'block';
 456+ // Create the new instance
 457+ var new_div = jQuery('#' + starter_div_id).clone()
 458+ .removeClass('multipleTemplateStarter')
 459+ .addClass('multipleTemplate')
 460+ .removeAttr("id")
 461+ .css("display", "block");
482462
483 - // make internal ID unique for the relevant divs and spans, and replace
 463+ // Make internal ID unique for the relevant divs and spans, and replace
484464 // the [num] index in the element names with an actual unique index
485 - var children = new_div.getElementsByTagName('*');
486 - // this array is needed to counteract an IE bug
487 - var orig_children = starter_div.getElementsByTagName('*');
488 - var fancybox_ids = new Array();
489 - var x;
490 - for (x = 0; x < children.length; x++) {
491 - if (children[x].name)
492 - children[x].name = children[x].name.replace(/\[num\]/g, '[' + num_elements + ']');
493 - if (children[x].id)
494 - children[x].id = children[x].id
495 - .replace(/input_/g, 'input_' + num_elements + '_')
496 - .replace(/info_/g, 'info_' + num_elements + '_')
497 - .replace(/div_/g, 'div_' + num_elements + '_');
498 - if (children[x].href)
499 - children[x].href = children[x].href
500 - .replace(/input_/g, 'input_' + num_elements + '_');
501 - if (children[x].id.match("^fancybox")) {
502 - fancybox_ids.push(children[x].id);
 465+ new_div.find("input, select, textarea").each(
 466+ function() {
 467+ if (this.name)
 468+ this.name = this.name.replace(/\[num\]/g, '[' + num_elements + ']');
 469+ if (this.id)
 470+ this.id = this.id.replace(/input_/g, 'input_' + num_elements + '_')
503471 }
 472+ );
 473+ new_div.find('a').attr('href', function() {
 474+ return this.href.replace(/input_/g, 'input_' + num_elements + '_');
 475+ });
 476+ new_div.find('span').attr('id', function() {
 477+ return this.id.replace(/span_/g, 'span_' + num_elements + '_');
 478+ });
504479
505 - // for dropdowns, copy over selectedIndex from original div,
506 - // to get around a bug in IE
507 - if (children[x].type == 'select-one') {
508 - children[x].selectedIndex = orig_children[x].selectedIndex;
509 - }
510 - }
511 - if (children[x]) {
512 - //We clone the last object
513 - if (children[x].href) {
514 - children[x].href = children[x].href
515 - .replace(/input_/g, 'input_' + num_elements + '_')
516 - .replace(/info_/g, 'info_' + num_elements + '_')
517 - .replace(/div_/g, 'div_' + num_elements + '_');
518 - }
519 - }
520 - // Since we clone the first object and we have uploadable field
521 - // we must replace the input_ in order to let the printer return
522 - // the value into the right field
523 - //Create remove button
524 - var remove_button = document.createElement('input');
525 - remove_button.type = 'button';
526 - remove_button.value = sfgRemoveText;
527 - remove_button.tabIndex = tab_index;
528 - remove_button.onclick = removeInstanceEventHandler(div_id);
529 - new_div.appendChild(remove_button);
 480+ // Create remove button
 481+ var removeButton = jQuery("<input>").attr({
 482+ type: 'button',
 483+ class: 'remover',
 484+ value: sfgRemoveText,
 485+ tabIndex: tab_index,
 486+ });
 487+ new_div.append(removeButton);
530488
531 - //Add the new instance
532 - main_div.appendChild(new_div);
533 - new_div.attachAutocompleteToAllFields;
 489+ // Add the new instance
 490+ jQuery('#' + main_div_id).append(new_div);
534491
535 - // For each 'upload file' link in this latest instance,
536 - // add a call to fancybox()
537 - for (x = 0; x < fancybox_ids.length; x++) {
538 - jQuery("#" + fancybox_ids[x]).fancybox({
 492+ // Enable the new remover
 493+ new_div.find('.remover').click( function() {
 494+ jQuery(this).parent().remove();
 495+ });
 496+
 497+ // Enable autocompletion
 498+ //alert(new_div.find('.autocompleteInput').size());
 499+ new_div.find('.autocompleteInput').attachAutocomplete();
 500+
 501+ // Apply the relevant Javascript call for all FancyBox, combobox
 502+ // and autogrow instances in this div.
 503+ new_div.find('.sfFancyBox').fancybox({
539504 'width' : '75%',
540505 'height' : '75%',
541506 'autoScale' : false,
@@ -543,48 +508,30 @@
544509 'type' : 'iframe',
545510 'overlayColor' : '#222',
546511 'overlayOpacity' : '0.8'
547 - });
548 - }
549 -}
 512+ });
 513+ // Somewhat of a hack - remove the divs that the combobox() call
 514+ // adds on, so that we can just call combobox() again without
 515+ // duplicating anything. There's probably a nicer way to do this,
 516+ // that doesn't involve removing and then recreating divs.
 517+ new_div.find('.sfComboBoxActual').remove();
 518+ new_div.find('.sfComboBox').combobox();
550519
551 -for (var i = 0; i < sfgRemoverButtons.length; i++) {
552 - var components = sfgRemoverButtons[i].split(',');
553 - removerID = components[0];
554 - wrapperID = components[1];
555 - jQuery('#' + removerID).click( removeInstanceEventHandler(wrapperID) );
 520+ // Handle AutoGrow as well.
 521+ new_div.find('.autoGrow').autoGrow();
556522 }
557523
558 -function removeInstanceEventHandler(divID)
559 -{
560 - return function() {
561 - jQuery('#' + divID).remove();
562 - };
563 -}
564 -
565 -// Activate autocomplete functionality for every field on the document
566 -function attachAutocompleteToAllDocumentFields() {
567 - jQuery('form[name="createbox"]').attachAutocompleteToAllFields();
568 -}
569 -
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 - );
578 -}
579 -
580524 // Activate autocomplete functionality for the specified field
581 -function attachAutocompleteToField(input) {
582 - input_id = input.id;
 525+jQuery.fn.attachAutocomplete = function() {
 526+ input_id = this.attr("id");
 527+ // For some reason, the find() call that calls this function will
 528+ // still call it even if no elements are found - if that happens,
 529+ // escape here to avoid an error.
 530+ if (input_id == null) return;
583531 // Extract the field ID number from the input field
584532 var field_num = parseInt(input_id.substring(input_id.lastIndexOf('_') + 1, input_id.length),10);
585533 // Add the autocomplete string, if a mapping exists.
586534 var field_string = sfgAutocompleteMappings[field_num];
587535 if (field_string) {
588 - var div_id = input_id.replace(/input_/g, 'div_');
589536 var field_values = field_string.split(',');
590537 var delimiter = null;
591538 var data_source = field_values[0];
@@ -595,39 +542,66 @@
596543 }
597544 }
598545 if (sfgAutocompleteValues[field_string] != null) {
599 - sf_autocomplete(input_id, div_id, sfgAutocompleteValues[field_string], null, null, delimiter, data_source);
 546+ this.sfAutocomplete(sfgAutocompleteValues[field_string], null, null, delimiter, data_source);
600547 } else {
601 - sf_autocomplete(input_id, div_id, null, wgScriptPath + "/api.php", sfgAutocompleteDataTypes[field_string], delimiter, data_source);
 548+ this.sfAutocomplete(null, wgScriptPath + "/api.php", sfgAutocompleteDataTypes[field_string], delimiter, data_source);
602549 }
603550 }
604551 }
605552
 553+var num_elements = 0;
 554+
 555+// Once the document has finished loading, set up everything!
606556 jQuery(document).ready(function() {
607 - for (var i = 0; i < sfgComboBoxInputs.length; i++ ) {
608 - jQuery("#input_" + sfgComboBoxInputs[i]).combobox();
609 - }
 557+ jQuery(".sfShowIfSelected").each( function() {
 558+ jQuery(this).showIfSelected();
 559+ jQuery(this).change( function() {
 560+ jQuery(this).showIfSelected();
 561+ });
 562+ });
 563+
 564+ jQuery(".sfShowIfChecked").each( function() {
 565+ jQuery(this).showIfChecked();
 566+ jQuery(this).change( function() {
 567+ jQuery(this).showIfChecked();
 568+ });
 569+ });
 570+
 571+ jQuery(".sfShowIfCheckedCheckbox").each( function() {
 572+ jQuery(this).showIfCheckedCheckbox();
 573+ jQuery(this).change( function() {
 574+ jQuery(this).showIfCheckedCheckbox();
 575+ });
 576+ });
610577
611 - for (var i = 0; i < sfgAutogrowInputs.length; i++ ) {
612 - jQuery("#" + sfgAutogrowInputs[i]).autoGrow();
613 - }
 578+ jQuery(".remover").click( function() {
 579+ jQuery(this).parent().remove();
 580+ });
 581+ jQuery(".autocompleteInput").attachAutocomplete();
 582+ jQuery(".sfComboBox").combobox();
 583+ jQuery(".autoGrow").autoGrow();
 584+ jQuery(".sfFancyBox").fancybox({
 585+ 'width' : '75%',
 586+ 'height' : '75%',
 587+ 'autoScale' : false,
 588+ 'transitionIn' : 'none',
 589+ 'transitionOut' : 'none',
 590+ 'type' : 'iframe',
 591+ 'overlayColor' : '#222',
 592+ 'overlayOpacity' : '0.8'
 593+ });
614594
615 - for (var i = 0; i < sfgFancyBoxInputs.length; i++ ) {
616 - jQuery("#fancybox_" + sfgFancyBoxInputs[i]).fancybox({
617 - 'width' : '75%',
618 - 'height' : '75%',
619 - 'autoScale' : false,
620 - 'transitionIn' : 'none',
621 - 'transitionOut' : 'none',
622 - 'type' : 'iframe',
623 - 'overlayColor' : '#222',
624 - 'overlayOpacity' : '0.8'
625 - });
 595+ // Could this be done via classes and attributes, instead of a
 596+ // global variable?
 597+ for (var i in sfgAdderButtons) {
 598+ var components = sfgAdderButtons[i].split(',');
 599+ adderID = components[0];
 600+ templateName = components[1];
 601+ fieldNum = components[2];
 602+ jQuery('#' + adderID).click( addInstanceEventHandler(templateName, fieldNum) );
626603 }
627604 });
628605
629 -jQuery.event.add(window, "load", attachAutocompleteToAllDocumentFields);
630 -
631 -
632606 /* extending jquery functions */
633607
634608 (function(jQuery) {
@@ -675,7 +649,7 @@
676650 },
677651 minLength: 0
678652 })
679 - .addClass("ui-widget ui-widget-content ui-corner-left");
 653+ .addClass("ui-widget ui-widget-content ui-corner-left sfComboBoxActual");
680654 jQuery('<button type="button">&nbsp;</button>')
681655 .attr("tabIndex", -1)
682656 .attr("title", "Show All Items")
@@ -686,7 +660,7 @@
687661 },
688662 text: false
689663 }).removeClass("ui-corner-all")
690 - .addClass("ui-corner-right ui-button-icon")
 664+ .addClass("ui-corner-right ui-button-icon sfComboBoxActual")
691665 .click(function() {
692666 // close if already visible
693667 if (input.autocomplete("widget").is(":visible")) {

Status & tagging log