Index: trunk/extensions/SemanticForms/libs/SemanticForms.js |
— | — | @@ -10,18 +10,11 @@ |
11 | 11 | * @author Eugene Mednikov |
12 | 12 | */ |
13 | 13 | |
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) { |
22 | 15 | var myServer = api_url; |
23 | 16 | jQuery.noConflict(); |
24 | 17 | |
25 | | -/* extending jquery functions for custom highlighting */ |
| 18 | + /* extending jQuery functions for custom highlighting */ |
26 | 19 | jQuery.ui.autocomplete.prototype._renderItem = function( ul, item) { |
27 | 20 | |
28 | 21 | var re = new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + this.term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"); |
— | — | @@ -65,11 +58,11 @@ |
66 | 59 | }); |
67 | 60 | |
68 | 61 | if (values != null) { |
| 62 | + // Local autocompletion |
69 | 63 | |
70 | | - /* delimiter != '' means multiple autocomplete */ |
71 | | - |
72 | 64 | if (delimiter != null) { |
73 | | - jQuery(document).ready(function(){ |
| 65 | + // Autocomplete for multiple values |
| 66 | + |
74 | 67 | function split(val) { |
75 | 68 | return val.split(delimiter); |
76 | 69 | } |
— | — | @@ -77,7 +70,7 @@ |
78 | 71 | return split(term).pop(); |
79 | 72 | } |
80 | 73 | |
81 | | - jQuery("#" + input_name).autocomplete({ |
| 74 | + jQuery(this).autocomplete({ |
82 | 75 | minLength: 0, |
83 | 76 | source: function(request, response) { |
84 | 77 | |
— | — | @@ -99,17 +92,15 @@ |
100 | 93 | return false; |
101 | 94 | } |
102 | 95 | }); |
103 | | - } ); |
104 | 96 | |
105 | | - } else{ |
106 | | - jQuery(document).ready(function(){ |
107 | | - jQuery("#" + input_name).autocomplete({ |
| 97 | + } else { |
| 98 | + // Autocomplete for a single value |
| 99 | + jQuery(this).autocomplete({ |
108 | 100 | source:values |
109 | 101 | }); |
110 | | - } ) ; |
111 | 102 | } |
112 | 103 | } else { |
113 | | - |
| 104 | + // Remote autocompletion |
114 | 105 | if (data_type == 'property') |
115 | 106 | myServer += "?action=sfautocomplete&format=json&property=" + data_source; |
116 | 107 | else if (data_type == 'relation') |
— | — | @@ -125,55 +116,49 @@ |
126 | 117 | |
127 | 118 | if (delimiter != null) { |
128 | 119 | |
129 | | - jQuery(document).ready(function(){ |
130 | 120 | function split(val) { |
131 | 121 | return val.split(delimiter); |
132 | 122 | } |
133 | 123 | function extractLast(term) { |
134 | 124 | return split(term).pop(); |
135 | 125 | } |
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 |
153 | 147 | 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; |
154 | 159 | } |
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 | + } ); |
175 | 161 | } else { |
176 | | - jQuery(document).ready(function(){ |
177 | | - jQuery("#" + input_name).autocomplete({ |
| 162 | + jQuery(this).autocomplete({ |
178 | 163 | minLength: 1, |
179 | 164 | source: function(request, response) { |
180 | 165 | jQuery.ajax({ |
— | — | @@ -181,27 +166,24 @@ |
182 | 167 | dataType: "json", |
183 | 168 | data: { |
184 | 169 | substr:request.term |
185 | | - |
186 | 170 | }, |
187 | 171 | success: function( data ) { |
188 | 172 | response(jQuery.map(data.sfautocomplete, function(item) { |
189 | | - return { |
190 | | - value: item.title |
191 | | - } |
192 | | - })) |
| 173 | + return { |
| 174 | + value: item.title |
| 175 | + } |
| 176 | + })) |
193 | 177 | } |
194 | 178 | }); |
195 | | - }, |
| 179 | + }, |
196 | 180 | open: function() { |
197 | 181 | jQuery(this).removeClass("ui-corner-all").addClass("ui-corner-top"); |
198 | 182 | }, |
199 | 183 | close: function() { |
200 | 184 | jQuery(this).removeClass("ui-corner-top").addClass("ui-corner-all"); |
201 | 185 | } |
202 | | - }); |
203 | | - |
204 | | - } ); |
205 | | - } |
| 186 | + } ); |
| 187 | + } |
206 | 188 | } |
207 | 189 | }; |
208 | 190 | |
— | — | @@ -211,24 +193,25 @@ |
212 | 194 | |
213 | 195 | // Display a div that would otherwise be hidden by "show on select". |
214 | 196 | function showDiv(div_id) { |
215 | | - jQuery('#' + div_id).find("input, select, textarea").removeClass('hiddenBySF'); |
| 197 | + jQuery('#' + div_id).find(".hiddenBySF").removeClass('hiddenBySF'); |
216 | 198 | jQuery('#' + div_id).show(); |
217 | 199 | } |
218 | 200 | |
219 | 201 | // Hide a div due to "show on select". The CSS class is there so that SF can |
220 | 202 | // ignore the div's contents when the form is submitted. |
221 | 203 | function hideDiv(div_id) { |
222 | | - jQuery('#' + div_id).find("input, select, textarea").addClass('hiddenBySF'); |
| 204 | + jQuery('#' + div_id).find("span, div").addClass('hiddenBySF'); |
223 | 205 | jQuery('#' + div_id).hide(); |
224 | 206 | } |
225 | 207 | |
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]))) { |
233 | 216 | showDiv(div_id); |
234 | 217 | return; |
235 | 218 | } |
— | — | @@ -236,34 +219,22 @@ |
237 | 220 | hideDiv(div_id); |
238 | 221 | } |
239 | 222 | |
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); |
257 | 231 | } |
258 | | - hideDiv(div_id); |
259 | 232 | } |
260 | 233 | |
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")) { |
268 | 239 | showDiv(div_id); |
269 | 240 | return; |
270 | 241 | } |
— | — | @@ -271,18 +242,38 @@ |
272 | 243 | hideDiv(div_id); |
273 | 244 | } |
274 | 245 | |
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 | + } |
279 | 255 | } |
280 | 256 | |
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); |
285 | 264 | } |
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(); |
287 | 278 | if (fieldVal == null) { |
288 | 279 | var isEmpty = true; |
289 | 280 | } else if (jQuery.isArray(fieldVal)) { |
— | — | @@ -291,250 +282,224 @@ |
292 | 283 | var isEmpty = (fieldVal.replace(/\s+/, '') == ''); |
293 | 284 | } |
294 | 285 | if (isEmpty) { |
295 | | - jQuery('#' + info_id).html(sfgBlankErrorStr); |
| 286 | + this.addErrorMessage(sfgBlankErrorStr); |
296 | 287 | return false; |
297 | 288 | } else { |
298 | | - jQuery('#' + info_id).html(''); |
299 | 289 | return true; |
300 | 290 | } |
301 | 291 | } |
302 | 292 | |
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 { |
308 | 298 | return true; |
309 | 299 | } |
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); |
312 | 307 | return false; |
313 | 308 | } else { |
314 | | - jQuery('#' + info_id).html(''); |
315 | 309 | return true; |
316 | 310 | } |
317 | 311 | } |
318 | 312 | |
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(); |
323 | 328 | if (numChecked == 0) { |
324 | | - jQuery('#' + info_id).html(sfgBlankErrorStr); |
| 329 | + this.addErrorMessage(sfgBlankErrorStr); |
325 | 330 | return false; |
326 | 331 | } else { |
327 | | - jQuery('#' + info_id).html(''); |
328 | 332 | return true; |
329 | 333 | } |
330 | 334 | } |
331 | 335 | |
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; |
350 | 349 | } |
351 | | - return (num_errors == 0); |
352 | 350 | } |
353 | 351 | |
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)) { |
357 | 357 | return true; |
358 | 358 | } 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; |
406 | 361 | } |
407 | 362 | } |
408 | 363 | |
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; |
425 | 371 | } |
426 | | - return (num_errors == 0); |
427 | 372 | } |
428 | 373 | |
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 | +} |
430 | 389 | |
431 | | -function validate_all() { |
| 390 | +// If the form is submitted, validate everything! |
| 391 | +jQuery('#sfForm').submit( function() { return validateAll(); } ); |
| 392 | + |
| 393 | +function validateAll() { |
432 | 394 | var num_errors = 0; |
433 | 395 | |
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(); |
439 | 398 | |
| 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 | + |
440 | 431 | if (num_errors > 0) { |
441 | 432 | // add error header, if it's not there already |
442 | | - if (! document.getElementById("form_error_header")) { |
| 433 | + if (jQuery("#form_error_header").size() == 0) { |
443 | 434 | jQuery("#contentSub").append('<div id="form_error_header" class="warningMessage" style="font-size: medium">' + sfgFormErrorsHeader + '</div>'); |
444 | 435 | } |
445 | 436 | scroll(0, 0); |
446 | 437 | } 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'); |
450 | 443 | } |
451 | 444 | return (num_errors == 0); |
452 | 445 | } |
453 | 446 | |
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 | | - |
464 | 447 | function addInstanceEventHandler(templateName, fieldNum) { |
465 | 448 | return function() { |
466 | 449 | addInstance('starter_' + templateName, 'main_' + templateName, fieldNum); |
467 | 450 | } |
468 | 451 | } |
469 | 452 | |
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) { |
474 | 454 | num_elements++; |
475 | 455 | |
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"); |
482 | 462 | |
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 |
484 | 464 | // 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 + '_') |
503 | 471 | } |
| 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 | + }); |
504 | 479 | |
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); |
530 | 488 | |
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); |
534 | 491 | |
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({ |
539 | 504 | 'width' : '75%', |
540 | 505 | 'height' : '75%', |
541 | 506 | 'autoScale' : false, |
— | — | @@ -543,48 +508,30 @@ |
544 | 509 | 'type' : 'iframe', |
545 | 510 | 'overlayColor' : '#222', |
546 | 511 | '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(); |
550 | 519 | |
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(); |
556 | 522 | } |
557 | 523 | |
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 | | - |
580 | 524 | // 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; |
583 | 531 | // Extract the field ID number from the input field |
584 | 532 | var field_num = parseInt(input_id.substring(input_id.lastIndexOf('_') + 1, input_id.length),10); |
585 | 533 | // Add the autocomplete string, if a mapping exists. |
586 | 534 | var field_string = sfgAutocompleteMappings[field_num]; |
587 | 535 | if (field_string) { |
588 | | - var div_id = input_id.replace(/input_/g, 'div_'); |
589 | 536 | var field_values = field_string.split(','); |
590 | 537 | var delimiter = null; |
591 | 538 | var data_source = field_values[0]; |
— | — | @@ -595,39 +542,66 @@ |
596 | 543 | } |
597 | 544 | } |
598 | 545 | 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); |
600 | 547 | } 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); |
602 | 549 | } |
603 | 550 | } |
604 | 551 | } |
605 | 552 | |
| 553 | +var num_elements = 0; |
| 554 | + |
| 555 | +// Once the document has finished loading, set up everything! |
606 | 556 | 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 | + }); |
610 | 577 | |
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 | + }); |
614 | 594 | |
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) ); |
626 | 603 | } |
627 | 604 | }); |
628 | 605 | |
629 | | -jQuery.event.add(window, "load", attachAutocompleteToAllDocumentFields); |
630 | | - |
631 | | - |
632 | 606 | /* extending jquery functions */ |
633 | 607 | |
634 | 608 | (function(jQuery) { |
— | — | @@ -675,7 +649,7 @@ |
676 | 650 | }, |
677 | 651 | minLength: 0 |
678 | 652 | }) |
679 | | - .addClass("ui-widget ui-widget-content ui-corner-left"); |
| 653 | + .addClass("ui-widget ui-widget-content ui-corner-left sfComboBoxActual"); |
680 | 654 | jQuery('<button type="button"> </button>') |
681 | 655 | .attr("tabIndex", -1) |
682 | 656 | .attr("title", "Show All Items") |
— | — | @@ -686,7 +660,7 @@ |
687 | 661 | }, |
688 | 662 | text: false |
689 | 663 | }).removeClass("ui-corner-all") |
690 | | - .addClass("ui-corner-right ui-button-icon") |
| 664 | + .addClass("ui-corner-right ui-button-icon sfComboBoxActual") |
691 | 665 | .click(function() { |
692 | 666 | // close if already visible |
693 | 667 | if (input.autocomplete("widget").is(":visible")) { |