html5forms-rails 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. data/CHANGELOG.md +5 -0
  2. data/Gemfile +11 -0
  3. data/README.md +208 -0
  4. data/Rakefile +49 -0
  5. data/VERSION +1 -0
  6. data/demos/html5-form-demo.html +79 -0
  7. data/html5forms-rails.gemspec +142 -0
  8. data/lib/html5forms.rb +6 -0
  9. data/vendor/assets/images/colorpicker/blank.gif +0 -0
  10. data/vendor/assets/images/colorpicker/colorpicker_background.png +0 -0
  11. data/vendor/assets/images/colorpicker/colorpicker_hex.png +0 -0
  12. data/vendor/assets/images/colorpicker/colorpicker_hsb_b.png +0 -0
  13. data/vendor/assets/images/colorpicker/colorpicker_hsb_h.png +0 -0
  14. data/vendor/assets/images/colorpicker/colorpicker_hsb_s.png +0 -0
  15. data/vendor/assets/images/colorpicker/colorpicker_indic.gif +0 -0
  16. data/vendor/assets/images/colorpicker/colorpicker_overlay.png +0 -0
  17. data/vendor/assets/images/colorpicker/colorpicker_rgb_b.png +0 -0
  18. data/vendor/assets/images/colorpicker/colorpicker_rgb_g.png +0 -0
  19. data/vendor/assets/images/colorpicker/colorpicker_rgb_r.png +0 -0
  20. data/vendor/assets/images/colorpicker/colorpicker_select.gif +0 -0
  21. data/vendor/assets/images/colorpicker/colorpicker_submit.png +0 -0
  22. data/vendor/assets/images/colorpicker/custom_background.png +0 -0
  23. data/vendor/assets/images/colorpicker/custom_hex.png +0 -0
  24. data/vendor/assets/images/colorpicker/custom_hsb_b.png +0 -0
  25. data/vendor/assets/images/colorpicker/custom_hsb_h.png +0 -0
  26. data/vendor/assets/images/colorpicker/custom_hsb_s.png +0 -0
  27. data/vendor/assets/images/colorpicker/custom_indic.gif +0 -0
  28. data/vendor/assets/images/colorpicker/custom_rgb_b.png +0 -0
  29. data/vendor/assets/images/colorpicker/custom_rgb_g.png +0 -0
  30. data/vendor/assets/images/colorpicker/custom_rgb_r.png +0 -0
  31. data/vendor/assets/images/colorpicker/custom_submit.png +0 -0
  32. data/vendor/assets/images/colorpicker/select.png +0 -0
  33. data/vendor/assets/images/colorpicker/select2.png +0 -0
  34. data/vendor/assets/images/colorpicker/slider.png +0 -0
  35. data/vendor/assets/images/h5f/form_validation.png +0 -0
  36. data/vendor/assets/images/html5form-shim/asterisk.png +0 -0
  37. data/vendor/assets/images/html5form-shim/down.png +0 -0
  38. data/vendor/assets/images/html5form-shim/fail.png +0 -0
  39. data/vendor/assets/images/html5form-shim/ok.png +0 -0
  40. data/vendor/assets/images/html5forms/jscolor/arrow.gif +0 -0
  41. data/vendor/assets/images/html5forms/jscolor/cross.gif +0 -0
  42. data/vendor/assets/images/html5forms/jscolor/hs.png +0 -0
  43. data/vendor/assets/images/html5forms/jscolor/hv.png +0 -0
  44. data/vendor/assets/images/html5forms/slider/slider-1.png +0 -0
  45. data/vendor/assets/images/html5forms/slider/slider-disabled-1.png +0 -0
  46. data/vendor/assets/images/html5forms/slider/slider-disabled.png +0 -0
  47. data/vendor/assets/images/html5forms/slider/slider.png +0 -0
  48. data/vendor/assets/javascripts/colorpicker.js +484 -0
  49. data/vendor/assets/javascripts/colorpicker.min.js +9 -0
  50. data/vendor/assets/javascripts/h5f.js +328 -0
  51. data/vendor/assets/javascripts/h5f.min.js +4 -0
  52. data/vendor/assets/javascripts/html5forms/EventHelpers.min.js +15 -0
  53. data/vendor/assets/javascripts/html5forms/autocomplete.min.js +1 -0
  54. data/vendor/assets/javascripts/html5forms/cssQuery-p.min.js +6 -0
  55. data/vendor/assets/javascripts/html5forms/dev/EventHelpers.js +486 -0
  56. data/vendor/assets/javascripts/html5forms/dev/autocomplete.js +387 -0
  57. data/vendor/assets/javascripts/html5forms/dev/cssQuery-p.js +6 -0
  58. data/vendor/assets/javascripts/html5forms/dev/html5.js +121 -0
  59. data/vendor/assets/javascripts/html5forms/dev/html5Forms.js +892 -0
  60. data/vendor/assets/javascripts/html5forms/dev/html5Widgets.js +1417 -0
  61. data/vendor/assets/javascripts/html5forms/dev/jscolor.js +840 -0
  62. data/vendor/assets/javascripts/html5forms/dev/slider.js +797 -0
  63. data/vendor/assets/javascripts/html5forms/dev/timer.js +137 -0
  64. data/vendor/assets/javascripts/html5forms/dev/visibleIf.js +1100 -0
  65. data/vendor/assets/javascripts/html5forms/html5.min.js +2 -0
  66. data/vendor/assets/javascripts/html5forms/html5Forms.min.js +1 -0
  67. data/vendor/assets/javascripts/html5forms/html5Widgets.min.js +20 -0
  68. data/vendor/assets/javascripts/html5forms/jscolor.min.js +10 -0
  69. data/vendor/assets/javascripts/html5forms/slider.min.js +25 -0
  70. data/vendor/assets/javascripts/html5forms/timer.min.js +1 -0
  71. data/vendor/assets/javascripts/html5forms/visibleIf.min.js +19 -0
  72. data/vendor/assets/javascripts/html5forms.fallback.js +115 -0
  73. data/vendor/assets/javascripts/html5forms.fallback.min.js +11 -0
  74. data/vendor/assets/javascripts/jquery.html5form-shim.js +402 -0
  75. data/vendor/assets/javascripts/jquery.html5form.min.js +4 -0
  76. data/vendor/assets/javascripts/jquery.placehold.min.js +7 -0
  77. data/vendor/assets/javascripts/ui.spinner.js +649 -0
  78. data/vendor/assets/javascripts/ui.spinner.min.js +7 -0
  79. data/vendor/assets/javascripts/webforms2/webforms2-msie.js +1 -0
  80. data/vendor/assets/javascripts/webforms2/webforms2-p.js +14 -0
  81. data/vendor/assets/javascripts/webforms2/webforms2.js +14 -0
  82. data/vendor/assets/javascripts/webforms2/webforms2_src.js +3195 -0
  83. data/vendor/assets/stylesheets/colorpicker.css +161 -0
  84. data/vendor/assets/stylesheets/h5f.css +86 -0
  85. data/vendor/assets/stylesheets/html5form-shim.css +109 -0
  86. data/vendor/assets/stylesheets/html5forms/number.css +35 -0
  87. data/vendor/assets/stylesheets/html5forms/slider.css +169 -0
  88. data/vendor/assets/stylesheets/html5forms/slider_ie.css +41 -0
  89. data/vendor/assets/stylesheets/html5forms/visibleIf.css +23 -0
  90. data/vendor/assets/stylesheets/html5forms.layout.css +116 -0
  91. data/vendor/assets/stylesheets/ui.spinner.css +3 -0
  92. data/vendor/assets/stylesheets/webforms2.css +42 -0
  93. metadata +221 -0
@@ -0,0 +1,3195 @@
1
+ /*
2
+ * Web Forms 2.0 Cross-browser Implementation <http://code.google.com/p/webforms2/>
3
+ * Version: 0.7 (2011-03-01)
4
+ * Copyright: 2007, Weston Ruter <http://weston.ruter.net/>
5
+ * with additions by Zoltan Hawryluk <http://www.useragentman.com>
6
+ * Licenses (as of Feb 6, 2011)
7
+ * - MIT License (http://www.opensource.org/licenses/mit-license.php)
8
+ * - GPL (http://creativecommons.org/licenses/GPL/2.0/)
9
+ *
10
+ * The comments contained in this code are largely quotations from the
11
+ * WebForms 2.0 specification: <http://whatwg.org/specs/web-forms/current-work/>
12
+ *
13
+ * Usage: <script type="text/javascript" src="webforms2_src.js"></script>
14
+ *
15
+ * Changelog:
16
+ * version 0.5.4 - initial release by Weston Ruter
17
+ * version 0.6 - refactored for use with HTML5Widgets by Zoltan Hawryluk (July 27th, 2010)
18
+ * version 0.6.1 - updated to deal with WebKit's half-implemented WebForms 2 Implementation (Sept 10, 2010)
19
+ * version 0.7 - bug fixes with nested repetition models by Zoltan Hawryluk.
20
+ * version 0.7.1 - updated to dual MIT/GPL 2.0 license.
21
+ */
22
+
23
+ if(!window.$wf2){
24
+ var $wf2 = {};
25
+
26
+ if(document.implementation && document.implementation.hasFeature &&
27
+ !document.implementation.hasFeature('WebForms', '2.0')){
28
+
29
+ $wf2 = {
30
+ version : '0.5.4',
31
+ isInitialized : false,
32
+ libpath : '',
33
+ globalEvent: null,
34
+
35
+ hasElementExtensions : (window.HTMLElement && HTMLElement.prototype),
36
+ hasGettersAndSetters : ($wf2.__defineGetter__ && $wf2.__defineSetter__),
37
+
38
+ hasBadImplementation: navigator.userAgent.indexOf('WebKit'),
39
+
40
+ callBeforeValidation : new Array(),
41
+ callAfterValidation : new Array(),
42
+ callAfterDOMContentLoaded: new Array(),
43
+
44
+
45
+ onDOMContentLoaded : function(){
46
+ if($wf2.isInitialized)
47
+ return;
48
+ $wf2.isInitialized = true; //Safari needs this here for some reason
49
+
50
+ var i,j,k,node;
51
+
52
+ //set global event for fireEvent method
53
+ if (document.createEventObject){
54
+ // dispatch for IE
55
+ $wf2.globalEvent = document.createEventObject();
56
+ } else if (document.createEvent) {
57
+ $wf2.globalEvent = document.createEvent("HTMLEvents");
58
+ }
59
+
60
+ //Include stylesheet
61
+ var style = document.createElement('link');
62
+ style.setAttribute('type', 'text/css');
63
+ style.setAttribute('rel', 'stylesheet');
64
+ style.setAttribute('href', $wf2.libpath + 'webforms2.css');
65
+ var parent = document.getElementsByTagName('head')[0];
66
+ if(!parent)
67
+ parent = document.getElementsByTagName('*')[0];
68
+ parent.insertBefore(style, parent.firstChild);
69
+
70
+ //The zero point for datetime controls is 1970-01-01T00:00:00.0Z, for datetime-local is
71
+ // 1970-01-01T00:00:00.0, for date controls is 1970-01-01, for month controls is 1970-01, for week
72
+ // controls is 1970-W01 (the week starting 1969-12-29 and containing 1970-01-01), and for time controls
73
+ // is 00:00.
74
+ $wf2.zeroPoint = {};
75
+ $wf2.zeroPoint.datetime = $wf2.parseISO8601("1970-01-01T00:00:00.0Z");
76
+ $wf2.zeroPoint['datetime-local'] = $wf2.parseISO8601("1970-01-01T00:00:00.0");
77
+ $wf2.zeroPoint.date = $wf2.zeroPoint.datetime; //parseISO8601("1970-01-01"); //.zeroPointDatetime; //1970-01-01 (UTC)
78
+ $wf2.zeroPoint.month = $wf2.zeroPoint.datetime; //parseISO8601("1970-01"); //1970-01 (UTC)
79
+ $wf2.zeroPoint.week = $wf2.parseISO8601("1970-W01"); //(UTC)
80
+ $wf2.zeroPoint.time = $wf2.zeroPoint.datetime; //parseISO8601("00:00"); //00:00 (UTC)
81
+
82
+ //## Fetching data from external resources ##################################
83
+ $wf2.xhr = null;
84
+ if(window.XMLHttpRequest)
85
+ $wf2.xhr = new XMLHttpRequest();
86
+ else if(window.ActiveXObject){
87
+ try {
88
+ $wf2.xhr = new ActiveXObject("Msxml2.XMLHTTP");
89
+ } catch(e){
90
+ try {
91
+ $wf2.xhr = new ActiveXObject("Microsoft.XMLHTTP");
92
+ } catch(e){}
93
+ }
94
+ }
95
+ if($wf2.xhr){
96
+ $wf2.prefillSelectElements();
97
+ $wf2.prefillFormElements();
98
+ }
99
+
100
+ //Initialize Repetition Behaviors ****************************************
101
+ //Before load events are fired, but after the entire document has been parsed and after forms with data
102
+ // attributes are prefilled (if necessary), UAs must iterate through every node in the document, depth
103
+ // first, looking for templates so that their initial repetition blocks can be created. ... UAs should not
104
+ // specifically wait for images and style sheets to be loaded before creating initial repetition blocks
105
+ // as described above.
106
+ $wf2.initRepetitionBlocks();
107
+ $wf2.initRepetitionTemplates();
108
+ $wf2.initRepetitionButtons('add');
109
+ $wf2.initRepetitionButtons('remove');
110
+ $wf2.initRepetitionButtons('move-up');
111
+ $wf2.initRepetitionButtons('move-down');
112
+ $wf2.updateAddButtons();
113
+ $wf2.updateMoveButtons();
114
+
115
+ // Initialize Non-Repetition Behaviors ****************************************
116
+ if(document.addEventListener){
117
+ document.addEventListener('mousedown', $wf2.clearInvalidIndicators, false);
118
+ document.addEventListener('keydown', $wf2.clearInvalidIndicators, false);
119
+ }
120
+ else if(document.attachEvent){
121
+ document.attachEvent('onmousedown', $wf2.clearInvalidIndicators);
122
+ document.attachEvent('onkeydown', $wf2.clearInvalidIndicators);
123
+ }
124
+
125
+ $wf2.initNonRepetitionFunctionality();
126
+
127
+ for (var i=0; i<$wf2.callAfterDOMContentLoaded.length; i++) {
128
+ $wf2.callAfterDOMContentLoaded[i]();
129
+ }
130
+ },
131
+
132
+
133
+ /*##############################################################################################
134
+ # Section: Fetching data from external resources
135
+ ##############################################################################################*/
136
+
137
+ prefillSelectElements : function(){
138
+ //If a select element or a datalist element being parsed has a data attribute, then as soon
139
+ // as the element and all its children have been parsed and added to the document, the
140
+ // prefilling process described here should start.
141
+ var select, selects = $wf2.getElementsByTagNames.apply(document.documentElement, ['select', 'datalist']); //$wf2.getElementsByTagNamesAndAttribute.apply(document.documentElement, [['select', 'datalist']]); //, 'data'
142
+ for(var i = 0; select = selects[i]; i++){
143
+ //If a select element or a datalist element has a data attribute, it must be a URI or
144
+ // IRI that points to a well-formed XML file whose root element is a select element
145
+ // in the http://www.w3.org/1999/xhtml namespace. The MIME type must be an XML MIME
146
+ // type [RFC3023], preferably application/xml. It should not be application/xhtml+xml
147
+ // since the root element is not html.
148
+ //UAs must process this file if it has an XML MIME type [RFC3023], if it is a well-formed
149
+ // XML file, and if the root element is the right root element in the right namespace.
150
+ // If any of these conditions are not met, UAs must act as if the attribute was not
151
+ // specified, although they may report the error to the user. UAs are expected to
152
+ // correctly handle namespaces, so the file may use prefixes, etc.
153
+ var xmlDoc = $wf2.loadDataURI(select);
154
+ if(///\bxml\b/.test(xhr.getResponseHeader('Content-Type') &&
155
+ xmlDoc &&
156
+ xmlDoc.documentElement &&
157
+ /:?\bselect$/i.test(xmlDoc.documentElement.nodeName) &&
158
+ xmlDoc.documentElement.namespaceURI == 'http://www.w3.org/1999/xhtml'
159
+ )
160
+ {
161
+ var root = xmlDoc.documentElement;
162
+ //1. Unless the root element of the file has a type attribute with the exact literal
163
+ // string incremental, the children of the select or datalist element in the original
164
+ // document must all be removed from the document.
165
+ if(root.getAttribute('type') != 'incremental'){
166
+ while(select.lastChild)
167
+ select.removeChild(select.lastChild);
168
+ }
169
+
170
+ //2. The entire contents of the select element in the referenced document are imported
171
+ // into the original document and appended as children of the select or datalist
172
+ // element. (Even if importing into a text/html document, the newly imported nodes
173
+ // will still be namespaced.)
174
+ //3. All nodes outside the select (such as style sheet processing instructions, whitespace
175
+ // text nodes, and DOCTYPEs) are ignored, as are attributes (other than type) on the
176
+ // select element.
177
+ node = root.firstChild;
178
+ while(node){
179
+ //select.appendChild(node.cloneNode(true)); //MSIE BUG: Throws "No such interface supported" exception
180
+ select.appendChild($wf2.cloneNode(node));
181
+ node = node.nextSibling;
182
+ }
183
+ }
184
+ }
185
+ },
186
+
187
+ prefillFormElements : function(){
188
+ //-- Seeding a form with initial values -------------------------------
189
+ //Before load events are fired, but after the entire document has been parsed and after select
190
+ // elements have been filled from external data sources (if necessary), forms with data attributes
191
+ // are prefilled.
192
+ var frm, frms = document.getElementsByTagName('form'); //$wf2.getElementsByTagNamesAndAttribute.apply(document.documentElement, [['form'], 'data']);
193
+ for(var i = 0; frm = frms[i]; i++){
194
+ //If a form has a data attribute, it must be a URI or IRI that points to a well-formed XML file
195
+ // whose root element is a formdata element in the http://n.whatwg.org/formdata namespace. The
196
+ // MIME type must be an XML MIME type [RFC3023], preferably application/xml.
197
+ //UAs must process this file if these conditions are met. If any of these conditions are not met,
198
+ // UAs must act as if the attribute was not specified, although they may report the error to
199
+ // the user. UAs are expected to correctly handle namespaces, so the file may use prefixes, etc.
200
+ var xmlDoc = $wf2.loadDataURI(frm);
201
+ if(///\bxml\b/.test(xhr.getResponseHeader('Content-Type') &&
202
+ xmlDoc &&
203
+ xmlDoc.documentElement &&
204
+ /:?\bformdata$/.test(xmlDoc.documentElement.nodeName) &&
205
+ xmlDoc.documentElement.namespaceURI == 'http://n.whatwg.org/formdata'
206
+ )
207
+ {
208
+ var rt;
209
+ var root = xmlDoc.documentElement;
210
+ //1. Unless the root element has a type attribute with the exact literal string incremental,
211
+ // the form must be reset to its initial values as specified in the markup.
212
+ if(root.getAttribute('type') != 'incremental')
213
+ frm.reset();
214
+
215
+ //The algorithm must be processed in the order given above, meaning any clear elements are
216
+ // handled before any repeat elements which are handled before the field elements, regardless
217
+ // of the order in which the elements are given. (Note that this implies that this process
218
+ // cannot be performed incrementally.)
219
+
220
+ //clear elements in the http://n.whatwg.org/formdata namespace that are children of
221
+ // the root element, have a non-empty template attribute, have no other non-namespaced
222
+ // attributes (ignoring xmlns attributes), and have no content, must be processed...:
223
+ //The template attribute should contain the ID of an element in the document. If the
224
+ // template attribute specifies an element that is not a repetition template, then
225
+ // the clear element is ignored.
226
+ var clr, clrs = root.getElementsByTagName('clear'); //getElementsByTagNameNS('http://n.whatwg.org/formdata', 'clr')
227
+ for(j = 0; clr = clrs[j]; j++){
228
+ if(clr.namespaceURI == 'http://n.whatwg.org/formdata' &&
229
+ clr.parentNode == root &&
230
+ !clr.firstChild &&
231
+ (rt = document.getElementById(clr.getAttribute('template'))) &&
232
+ rt.getAttribute('repeat') == 'template'
233
+ /*Examining of non-namespaced attributes skipped*/
234
+ )
235
+ {
236
+ //The user must make a note of the list of repetition blocks associated with that
237
+ // template that are siblings of the template, and must then go through this list,
238
+ // removing each repetition block in turn.
239
+ //Note that we cannot use rt.repetitionBlocks since the repetition behavior has
240
+ // not yet been initialized.
241
+ var attr,node,next;
242
+ node = rt.parentNode.firstChild;
243
+ while(node){
244
+ if(node.nodeType == 1 && (attr = node.getAttributeNode('repeat')) && attr.value != 'template'){
245
+ next = node.nextSibling;
246
+ node.parentNode.removeChild(node);
247
+ node = next;
248
+ }
249
+ else node = node.nextSibling;
250
+ }
251
+ }
252
+ }
253
+
254
+ //repeat elements in the http://n.whatwg.org/formdata namespace that are children of
255
+ // the root element, have a non-empty template attribute and an index attribute that
256
+ // contains only one or more digits in the range 0-9 with an optional leading minus
257
+ // sign (U+002D, "-"), have no other non-namespaced attributes (ignoring xmlns
258
+ // attributes), and have no content, must be processed as follows:
259
+ //The template attribute should contain the ID of an element in the document. If the
260
+ // template attribute specifies an element that is not a repetition template, then
261
+ // the repeat element is ignored.
262
+ var index, rpt, rpts = root.getElementsByTagName('repeat');
263
+ for(j = 0; rpt = rpts[j]; j++){
264
+ if(rpt.namespaceURI == 'http://n.whatwg.org/formdata' &&
265
+ rpt.parentNode == root &&
266
+ !rpt.firstChild &&
267
+ (rt = document.getElementById(rpt.getAttribute('template'))) &&
268
+ rt.getAttribute('repeat') == 'template' &&
269
+ /^-?\d+$/.test(index = rpt.getAttribute('index'))
270
+ /*Examining of non-namespaced attributes skipped*/
271
+ )
272
+ {
273
+ //If the template attribute specifies a repetition template and that template
274
+ // already has a repetition block with the index specified by the index attribute,
275
+ // then the element is ignored.
276
+ //for(j = 0; j < rt.repetitionBlocks.length; j++){
277
+ // if(rt.repetitionBlocks[j].repetititionIndex == index){
278
+ // hasIndex = true;
279
+ // break;
280
+ // }
281
+ //}
282
+ var hasIndex,attr,node,next;
283
+ node = rt.parentNode.firstChild;
284
+ while(node){
285
+ if(node.nodeType == 1 && (attr = node.getAttributeNode('repeat')) && attr.value == index){
286
+ hasIndex = true;
287
+ break;
288
+ }
289
+ node = node.nextSibling;
290
+ }
291
+
292
+ if(!hasIndex){
293
+ //Otherwise, the specified template's addRepetitionBlockByIndex() method is
294
+ // called, with a null first argument and the index specified by the repeat
295
+ // element's index attribute as the second.
296
+ $wf2.addRepetitionBlockByIndex.apply(rt, [null, index]);
297
+ }
298
+ }
299
+ }
300
+
301
+ //field elements in the http://n.whatwg.org/formdata namespace that are children of
302
+ // the root element, have a non-empty name attribute, either an index attribute
303
+ // that contains only one or more digits in the range 0-9 or no index attribute at
304
+ // all, have no other non-namespaced attributes (ignoring xmlns attributes), and
305
+ // have either nothing or only text and CDATA nodes as children, must be used to
306
+ // initialize controls...
307
+ var fld, flds = root.getElementsByTagName('field');
308
+ var formElements = $wf2.getFormElements.apply(frm);
309
+ for(j = 0; fld = flds[j]; j++){
310
+ var indexAttr = fld.getAttributeNode('index');
311
+ var name = fld.getAttribute('name');
312
+ if(!name || (indexAttr && !/^\d+$/.test(indexAttr.value)))
313
+ /*Examining of non-namespaced attributes skipped*/
314
+ /*Verification of the presence of text and CDATA nodes below*/
315
+ continue;
316
+ //First, the form control that the field references must be identified.
317
+ var value = '';
318
+ for(k = 0; node = fld.childNodes[k]; k++){
319
+ if(node.nodeType == 3 /*text*/ || node.nodeType == 4 /*CDATA*/)
320
+ value += node.data;
321
+ else break; //only text and CDATA nodes allowed
322
+ }
323
+ var ctrl, count = 0;
324
+ for(k = 0; ctrl = formElements[k]; k++){
325
+ //console.info(ctrl.name + ' == ' + name)
326
+ if(ctrl.type == 'image'){
327
+ //For image controls, instead of using the name given by the name attribute,
328
+ // the field's name is checked against two names, the first being the value
329
+ // of the name attribute with the string .x appended to it, and the second
330
+ // being the same but with .y appended instead. If an image control's name
331
+ // is the empty string (e.g. if its name attribute is omitted) then the
332
+ // names x and y must be used instead. Thus image controls are handled as
333
+ // if they were two controls.
334
+ if(ctrl.name ?
335
+ (ctrl.name + '.x' == name || ctrl.name + '.y' == name)
336
+ : (name == 'x' || name == 'y') ){
337
+
338
+ if(!indexAttr || ++count-1 >= indexAttr.value)
339
+ break;
340
+ }
341
+ }
342
+ //This is done by walking the list of form controls associated with the form until
343
+ // one is found that has a name exactly equal to the name given in the field
344
+ // element's name attribute, skipping as many such matches as is specified in
345
+ // the index attribute, or, if the index attribute was omitted, skipping over
346
+ // any type="radio" and type="checkbox" controls that have the exact name given
347
+ // but have a value that is not exactly the same as the contents of the field element.
348
+ // SPECIFICATION DEFICIENCY: Note that this is not completely true. If the value of
349
+ // a field element is empty, then it should not be skipped if it associated with
350
+ // a radio button or checkbox. For example, the specification states four paragraphs
351
+ // later, "The only values that would have an effect in this example are "", which
352
+ // would uncheck the checkbox, and "green", which would check the checkbox."
353
+ else if(ctrl.name == name){
354
+ if(indexAttr){
355
+ if(++count-1 < indexAttr.value)
356
+ continue;
357
+ }
358
+ else if((ctrl.type == 'radio' || ctrl.type == 'checkbox') &&
359
+ (value && ctrl.value != value))
360
+ continue;
361
+ break;
362
+ }
363
+ }
364
+
365
+ //If the identified form control is a file upload control, a push button control, or
366
+ // an image control, then the field element is now skipped.
367
+ if(ctrl.type == 'file' || ctrl.type == 'button' || ctrl.type == 'image')
368
+ continue;
369
+
370
+ //Next, if the identified form control is not a multiple-valued control (a multiple-
371
+ // valued control is one that can generate more than one value on submission, such
372
+ // as a <select multiple="multiple">), or if it is a multiple-valued control but it
373
+ // is the first time the control has been identified by a field element in this
374
+ // data file that was not ignored, then it is set to the given value (the contents
375
+ // of the field element), removing any previous values (even if these values were
376
+ // the result of processing previous field elements in the same data file).
377
+ if(!ctrl.getAttributeNode('multiple') || !ctrl.wf2Prefilled){
378
+ //If the element cannot be given the value specified, the field element is
379
+ // ignored and the control's value is left unchanged. For example, if a
380
+ // checkbox has its value attribute set to green and the field element
381
+ // specifies that its value should be set to blue, it won't be changed from
382
+ // its current value. (The only values that would have an effect in this
383
+ // example are "", which would uncheck the checkbox, and "green", which would
384
+ // check the checkbox.)
385
+ if(ctrl.type == 'checkbox' || ctrl.type == 'radio'){
386
+ if(!value)
387
+ ctrl.checked = false;
388
+ else if(ctrl.value == value)
389
+ ctrl.checked = true;
390
+ else break;
391
+ }
392
+ else if(ctrl.nodeName.toLowerCase() == 'select'){
393
+ ctrl.selectedIndex = -1;
394
+ for(var opt,k = 0; opt = ctrl.options[k]; k++){
395
+ if(opt.value ? opt.value == value : opt.text == value){
396
+ opt.selected = true;
397
+ break;
398
+ }
399
+ }
400
+ }
401
+ //Another example would be a datetime control where the specified value is
402
+ // outside the range allowed by the min and max attributes. The format
403
+ // must match the allowed formats for that type for the value to be set.
404
+ else {
405
+ ctrl.value = value;
406
+ $wf2.updateValidityState(ctrl);
407
+ if(!ctrl.validity.valid){
408
+ ctrl.value = ctrl.defaultValue;
409
+ $wf2.updateValidityState(ctrl);
410
+ }
411
+ }
412
+ ctrl.wf2Prefilled = true; //TRACE
413
+ }
414
+ //Otherwise, this is a subsequent value for a multiple-valued control, and the
415
+ // given value (the contents of the field element) should be added to the list of
416
+ // values that the element has selected.
417
+ //If the element is a multiple-valued control and the control already has the given
418
+ // value selected, but it can be given the value again, then that occurs.
419
+ else if(ctrl.getAttributeNode('multiple')){
420
+ for(var opt,k = 0; opt = ctrl.options[k]; k++){
421
+ if(!opt.selected && (opt.value ? opt.value == value : opt.text == value)){
422
+ opt.selected = true;
423
+ break;
424
+ }
425
+ }
426
+ }
427
+
428
+ //if(ctrl){
429
+ //
430
+ //}
431
+ }
432
+
433
+ //A formchange event is then fired on all the form controls of the form.
434
+ var formElements = $wf2.getFormElements.apply(frm);
435
+ for(j = 0; j < formElements.length; j++){
436
+ //onformchange();
437
+ //fireEvent()
438
+ }
439
+ }
440
+ }
441
+ },
442
+
443
+ /*##############################################################################################
444
+ # Section: Repetition Model
445
+ ##############################################################################################*/
446
+
447
+ //## REPETITION TEMPLATE #############################################################
448
+ repetitionTemplates:[],
449
+ constructRepetitionTemplate : function(){
450
+ if(this.wf2Initialized)
451
+ return;
452
+ this.wf2Initialized = true; //SAFARI needs this to be here for some reason...
453
+
454
+ this.style.display = 'none'; //This is also specified via a stylesheet
455
+ this.repetitionType = RepetitionElement.REPETITION_TEMPLATE;
456
+ if(!this.repetitionIndex)
457
+ this.repetitionIndex = 0;
458
+ this.repetitionTemplate = null; //IMPLEMENT GETTER
459
+ if(!this.repetitionBlocks)
460
+ this.repetitionBlocks = []; //IMPLEMENT GETTER
461
+ var _attr;
462
+ this.repeatStart = /^\d+$/.test(_attr = this.getAttribute('repeat-start')) ? parseInt(_attr) : 1;
463
+ this.repeatMin = /^\d+$/.test(_attr = this.getAttribute('repeat-min')) ? parseInt(_attr) : 0;
464
+ this.repeatMax = /^\d+$/.test(_attr = this.getAttribute('repeat-max')) ? parseInt(_attr) : Number.MAX_VALUE; //Infinity;
465
+
466
+ if(!this.addRepetitionBlock) this.addRepetitionBlock = function(refNode, index){
467
+ return $wf2.addRepetitionBlock.apply(this, [refNode, index]); //wrapper to save memory?
468
+ };
469
+ if(!this.addRepetitionBlockByIndex)
470
+ this.addRepetitionBlockByIndex = this.addRepetitionBlock/*ByIndex*/; //one method implements both algorithms
471
+
472
+ //Any form controls inside a repetition template are associated with their forms' templateElements
473
+ // DOM attributes, and are not present in the forms' elements DOM attributes.
474
+
475
+ //On the HTMLFormElement, the templateElements attribute contains the list of form controls associated
476
+ // with this form that form part of repetition templates. It is defined in more detail in the section
477
+ // on the repetition model. (Image controls are part of this array, when appropriate.) The controls
478
+ // in the elements and templateElements lists must be in document order.
479
+ var frm = this;
480
+ while(frm = frm.parentNode){
481
+ if(frm.nodeName.toLowerCase() == 'form')
482
+ break;
483
+ }
484
+
485
+ var _templateElements;
486
+ //IMAGE???, fieldset not included
487
+ if(frm && (_templateElements = $wf2.getElementsByTagNames.apply(this, ['button','input','select','textarea','isindex'])).length){
488
+ //INCORRECT IMPLEMENTATION: this should append the new elements onto the frm.templateElements array and then sort them in document order?
489
+ //each time that a nesting repetition block is instantiated, the form's templateElemenents property becomes invalid
490
+
491
+ //frm.templateElements = _templateElements;
492
+
493
+ //Controls in the templateElements attribute cannot be successful; controls inside repetition templates can never be submitted.
494
+ // Therefore disable all elements in the template; however, due to the issue below, the original disabled state must be stored in the field's class attribute as "disabled"
495
+ // this storing of the original disabled state will enable the elements in cloned blocks to be disabled as originally coded in the template
496
+ //ISSUE: inputs retain disabled (but not defaultDisabled) attribue after returning to page from back button or reload
497
+ // see http://weblogs.mozillazine.org/gerv/archives/2006/10/firefox_reload_behaviour.html
498
+ // As a workaround... this implementation requires that authors, in addition to supplying a DISABLED attribute (for Opera), to include a class name "disabled"
499
+ for(var el, i = 0; el = _templateElements[i]; i++)
500
+ el.disabled = true;
501
+
502
+ //IMPLEMENTATION DEFICIENCY: unable to remove frm.templateElements from frm.elements
503
+ }
504
+
505
+ //Repetition blocks without a repeat-template attribute are associated with their first following sibling
506
+ // that is a repetition template, if there is one.
507
+ var attr,sibling = this.parentNode.firstChild;
508
+ while(sibling && sibling != this){
509
+ if(sibling.nodeType == 1 && (attr = sibling.getAttributeNode('repeat')) && /^-?\d+$/.test(attr.value) && !sibling.getAttribute('repeat-template')){
510
+ //if(sibling.repetitionType == RepetitionElement.REPETITION_BLOCK && !sibling.getAttribute('repeat-template')){
511
+ //console.info(sibling)
512
+ sibling.repetitionTemplate = this;
513
+ sibling.setAttribute('repeat-template', this.id);
514
+ this.repetitionBlocks.push(sibling);
515
+ }
516
+ sibling = sibling.nextSibling;
517
+ }
518
+ //while(sibling = sibling.previousSibling){
519
+ // if(sibling.repetitionType == RepetitionElement.REPETITION_BLOCK && !sibling.getAttribute('repeat-template')){
520
+ // sibling.repetitionTemplate = this;
521
+ // sibling.setAttribute('repeat-template', this.id);
522
+ // this.repetitionBlocks.unshift(sibling);
523
+ // }
524
+ //}
525
+
526
+ //the UA must invoke the template's replication behaviour as many times as the repeat-start attribute
527
+ // on the same element specifies (just once, if the attribute is missing or has an invalid value).
528
+ // Then, while the number of repetition blocks associated with the repetition template is less than
529
+ // the template's repeat-min attribute, the template's replication behaviour must be further invoked.
530
+ // (Invoking the template's replication behaviour means calling its addRepetitionBlock() method).
531
+ //for(var i = 0; i < Math.max(this.repeatStart, this.repeatMin); i++)
532
+ for(var i = 0; (i < this.repeatStart || this.repetitionBlocks.length < this.repeatMin); i++)
533
+ if (!this.addRepetitionBlock()) {
534
+ break;
535
+ }
536
+
537
+ $wf2.repetitionTemplates.push(this);
538
+ this.wf2Initialized = true;
539
+ },
540
+
541
+ initRepetitionTemplates : function(parentNode){
542
+ //UAs must iterate through every node in the document, depth first, looking for templates so that their
543
+ // initial repetition blocks can be created.
544
+ //var repetitionTemplates = cssQuery("*[repeat=template]", parentNode);
545
+ var repetitionTemplates = $wf2.getElementsByTagNamesAndAttribute.apply((parentNode || document.documentElement), [['*'], 'repeat', 'template']);
546
+ for(var i = 0, rt; i < repetitionTemplates.length; i++)
547
+ $wf2.constructRepetitionTemplate.apply(repetitionTemplates[i]);
548
+ },
549
+
550
+
551
+ //## REPETITION BLOCK #############################################################
552
+ constructRepetitionBlock : function(){
553
+ if(this.wf2Initialized)
554
+ return;
555
+
556
+ this.style.display = ''; //This should preferrably be specified via a stylesheet
557
+ this.repetitionType = RepetitionElement.REPETITION_BLOCK;
558
+ var _attr;
559
+ this.repetitionIndex = /^\d+$/.test(_attr = this.getAttribute('repeat')) ? parseInt(_attr) : 0;
560
+ this.repetitionBlocks = null;
561
+
562
+ //find this block's repetition template
563
+ this.repetitionTemplate = null; //IMPLEMENT GETTER
564
+ var node;
565
+
566
+ if((node = document.getElementById(this.getAttribute('repeat-template'))) &&
567
+ node.getAttribute('repeat') == 'template')
568
+ {
569
+ this.repetitionTemplate = node;
570
+ }
571
+ else {
572
+ node = this;
573
+ while(node = node.nextSibling){
574
+ if(node.nodeType == 1 && node.getAttribute('repeat') == 'template'){
575
+ this.repetitionTemplate = node;
576
+ break;
577
+ }
578
+ }
579
+ }
580
+
581
+ if(!this.removeRepetitionBlock) this.removeRepetitionBlock = function(){
582
+ return $wf2.removeRepetitionBlock.apply(this); //wrapper to save memory
583
+ };
584
+ if(!this.moveRepetitionBlock) this.moveRepetitionBlock = function(distance){
585
+ return $wf2.moveRepetitionBlock.apply(this, [distance]); //wrapper to save memory
586
+ };
587
+ this.wf2Initialized = true;
588
+ },
589
+
590
+ initRepetitionBlocks : function(parentNode){
591
+ //var repetitionBlocks = cssQuery('*[repeat]:not([repeat="template"])', parentNode); //:not([repeat="template"])
592
+ var repetitionBlocks = $wf2.getElementsByTagNamesAndAttribute.apply((parentNode || document.documentElement), [['*'], 'repeat', 'template', true]);
593
+ for(var i = 0; i < repetitionBlocks.length; i++)
594
+ $wf2.constructRepetitionBlock.apply(repetitionBlocks[i]);
595
+ },
596
+
597
+
598
+ //## Repetition buttons #############################################################
599
+ repetitionButtonDefaultLabels : {
600
+ 'add' : 'Add',
601
+ 'remove' : 'Remove',
602
+ 'move-up' : 'Move-up',
603
+ 'move-down' : 'Move-down'
604
+ },
605
+
606
+ constructRepetitionButton : function(btnType){
607
+ if(this.wf2Initialized)
608
+ return;
609
+ this.htmlTemplate = $wf2.getHtmlTemplate(this); //IMPLEMENT GETTER
610
+ if(!this.firstChild)
611
+ this.appendChild(document.createTextNode($wf2.repetitionButtonDefaultLabels[btnType]));
612
+
613
+ //user agents must automatically disable remove, move-up, and move-down buttons when they are not in a repetition
614
+ // block. [NOT IMPLEMENTED:] This automatic disabling does not affect the DOM disabled attribute. It is an intrinsic property of these buttons.
615
+ if(btnType != 'add')
616
+ this.disabled = !$wf2.getRepetitionBlock(this);
617
+ //user agents must automatically disable add buttons (irrespective of the value of the disabled
618
+ // DOM attribute [NOT IMPLEMENTED]) when the buttons are not in a repetition block that has an
619
+ // associated template and their template attribute is either not specified or does not have
620
+ // an ID that points to a repetition template...
621
+ else {
622
+ var rb;
623
+ this.disabled = !(((rb = $wf2.getRepetitionBlock(this)) && rb.repetitionTemplate)
624
+ ||
625
+ this.htmlTemplate
626
+ );
627
+ }
628
+
629
+ if(this.addEventListener)
630
+ this.addEventListener('click', $wf2.clickRepetitionButton, false);
631
+ else if(this.attachEvent)
632
+ this.attachEvent('onclick', $wf2.clickRepetitionButton);
633
+ else this.onclick = $wf2.clickRepetitionButton;
634
+
635
+ this.wf2Initialized = true;
636
+ },
637
+
638
+ initRepetitionButtons : function(btnType, parent){
639
+ var i;
640
+ if(!parent)
641
+ parent = document.documentElement;
642
+
643
+ //change INPUTs to BUTTONs
644
+ var inpts = $wf2.getElementsByTagNamesAndAttribute.apply(parent, [['input'], 'type', btnType]);
645
+ for(i = 0; i < inpts.length; i++){
646
+ var btn = document.createElement('button');
647
+ for(var j = 0, attr; attr = inpts[i].attributes[j]; j++)
648
+ btn.setAttribute(attr.nodeName, inpts[i].getAttribute(attr.nodeName)); //MSIE returns correct value with getAttribute but not nodeValue
649
+ inpts[i].parentNode.replaceChild(btn, inpts[i]);
650
+ btn = null;
651
+ }
652
+
653
+ //construct all buttons
654
+ var btns = $wf2.getElementsByTagNamesAndAttribute.apply(parent, [['button'], 'type', btnType]);
655
+ for(var i = 0; i < btns.length; i++)
656
+ $wf2.constructRepetitionButton.apply(btns[i], [btnType]);
657
+ },
658
+
659
+ clickRepetitionButton : function(e){
660
+ if(e && e.preventDefault)
661
+ e.preventDefault(); //Keep default submission behavior from executing
662
+
663
+ //If the event is canceled (btn.returnValue === false, set within onclick handler), then the default action will not occur.
664
+ var btn;
665
+ if(e && e.target)
666
+ btn = e.target;
667
+ else if(window.event)
668
+ btn = event.srcElement;
669
+ else if(this.nodeName.toLowerCase() == 'button')
670
+ btn = this;
671
+ var btnType = String(btn.getAttribute('type')).toLowerCase();
672
+
673
+ //Prevent the onclick handler from firing afterwards (would fire after movement action)
674
+ // ISSUE: This only works in Firefox
675
+ if(btn.onclick){
676
+ btn._onclick = btn.onclick;
677
+ btn.removeAttribute('onclick');
678
+ btn.onclick = null;
679
+ }
680
+
681
+ //Terminate if an onclick handler was called beforehand and returned a false value
682
+ // passed via the button's returnValue property. Handlers defined by HTML attributes
683
+ // are called before those assigned by onclick DOM properties.
684
+ if(btn.returnValue !== undefined && !btn.returnValue){
685
+ btn.returnValue = undefined;
686
+ return false;
687
+ }
688
+
689
+ //Ensure that a user-supplied onclick handler is fired before the repetition behavior is executed
690
+ // and terminate if this onclick handler returns a false value
691
+ // Note that handlers defined in onclick HTML attributes are executed before clickRepetitionButton
692
+ if(btn._onclick && btn.returnValue === undefined){ // && !btn.getAttributeNode("onclick") //&& btn.hasAttribute && !btn.hasAttribute("onclick") //NOTE: MSIE fires this afterwards??? btn.returnValue = btn._onclick(e);
693
+ btn.returnValue = btn._onclick(e);
694
+ if(btn.returnValue !== undefined && !btn.returnValue){
695
+ btn.returnValue = undefined;
696
+ return false;
697
+ }
698
+ }
699
+ btn.returnValue = undefined;
700
+
701
+ //ISSUE: How do we ensure that the MSIE and DOM Level 2 Event handlers are executed beforehand?
702
+
703
+ var block;
704
+ if(btnType != 'add'){
705
+ block = $wf2.getRepetitionBlock(btn);
706
+
707
+ //user agents must automatically disable remove, move-up, and move-down buttons when they are not in a repetition
708
+ // block. [NOT IMPLEMENTED:] This automatic disabling does not affect the DOM disabled attribute. It is an intrinsic property of these buttons.
709
+ this.disabled = !block;
710
+
711
+ if(block){
712
+ if(btnType.indexOf('move') === 0){
713
+ block._clickedMoveBtn = btn;
714
+ block.moveRepetitionBlock(btnType == 'move-up' ? -1 : 1);
715
+ }
716
+ else if(btnType == 'remove'){
717
+ block.removeRepetitionBlock();
718
+ }
719
+ }
720
+ }
721
+ else {
722
+ var rt;
723
+ //If an add button with a template attribute is activated, and its template attribute gives the ID
724
+ // of an element in the document that is a repetition template as defined above, then that
725
+ // template's replication behaviour is invoked. (Specifically, in scripting-aware environments,
726
+ // the template's addRepetitionBlock() method is called with a null argument.) In the case of
727
+ // duplicate IDs, the behaviour should be the same as with getElementById().
728
+ if(btn.htmlTemplate)
729
+ rt = btn.htmlTemplate;
730
+ else {
731
+ //If an add button without a template attribute is activated, and it has an ancestor that is a
732
+ // repetition block that is not an orphan repetition block, then the repetition template associated
733
+ // with that repetition block has its template replication behaviour invoked with the respective
734
+ // repetition block as its argument. (Specifically, in scripting-aware environments, the template's
735
+ // addRepetitionBlock() method is called with a reference to the DOM Element node that represents
736
+ // the repetition block.)
737
+ block = $wf2.getRepetitionBlock(btn);
738
+ if(block && block.repetitionTemplate)
739
+ rt = block.repetitionTemplate;
740
+ }
741
+ if(rt)
742
+ rt.addRepetitionBlock();
743
+ else
744
+ btn.disabled = true; //NOTE: THIS IS NOT A VALID IMPLEMENTATION
745
+ }
746
+ return false;
747
+ },
748
+
749
+ //## AddRepetitionBlock algorithm #############################################################
750
+
751
+ //Element addRepetitionBlock(in Node refNode);
752
+ addRepetitionBlock : function(refNode, index){ //addRepetitionBlockByIndex functionalty enabled if @index defined
753
+ //if(refNode && !refNode.nodeType)
754
+ // throw Error("Exception: WRONG_ARGUMENTS_ERR");
755
+
756
+ //if(this.repetitionType == RepetitionElement.REPETITION_TEMPLATE)
757
+ if(this.getAttribute('repeat') != 'template')
758
+ throw $wf2.DOMException(9); //NOT_SUPPORTED_ERR
759
+
760
+ //if addRepetitionBlock called before repetition constructors called (by pre-filling forms)
761
+ if(!this.repetitionBlocks)
762
+ this.repetitionBlocks = [];
763
+ if(!this.repetitionIndex)
764
+ this.repetitionIndex = 0;
765
+ if(!this.repeatMin)
766
+ this.repeatMin = 0;
767
+ if(!this.repeatMax)
768
+ this.repeatMax = Number.MAX_VALUE;
769
+ if(!this.repeatStart)
770
+ this.repeatStart = 1;
771
+
772
+ //1. If the template has no parent node or its parent node is not an element, then the method must abort
773
+ // the steps and do nothing.
774
+ if(this.parentNode == null)
775
+ return null;
776
+
777
+ //[furthermore, if this template is the child of another template (not the child of an instance, a block) return false]
778
+ var node = this;
779
+ while(node = node.parentNode){
780
+ //if(node.repetitionType == RepetitionElement.REPETITION_TEMPLATE)
781
+ if(node.nodeType == 1 && node.getAttribute('repeat') == 'template')
782
+ return false;
783
+ }
784
+
785
+ //2. The template examines its preceding siblings, up to the start of the parent element. For each sibling
786
+ // that is a repetition block whose associated template is this template, if the repetition block's index
787
+ // is greater than or equal to the template's index, then the template's index is increased to the repetition
788
+ // block's index plus one. The total number of repetition blocks associated with this template that were
789
+ // found is used in the next step.
790
+ //QUESTION: Why not just use this.repetitionBlocks.length????????????
791
+ var sibling = this.previousSibling;
792
+ var currentBlockCount = 0;
793
+ while(sibling != null){
794
+ if(sibling.nodeType == 1){
795
+ var repeatAttr,repeatTemplateAttr;
796
+ repeat = parseInt(sibling.getAttribute('repeat'));
797
+ repeatTemplateAttr = sibling.getAttributeNode('repeat-template');
798
+
799
+ //if(sibling.repetitionType == RepetitionElement.REPETITION_BLOCK && sibling.repetitionTemplate == this)
800
+ if(!isNaN(repeat) && (!repeatTemplateAttr || repeatTemplateAttr.value == this.id))
801
+ {
802
+ //Old Note: sibling.getAttribute('repeat') is used instead of sibling.repetitionIndex because appearantly
803
+ // the sibling is not yet bound to the document and so the getters are not available
804
+ //this.repetitionIndex = Math.max(this.repetitionIndex, parseInt(sibling.getAttribute('repeat'))+1);
805
+ this.repetitionIndex = Math.max(this.repetitionIndex, repeat+1);
806
+ currentBlockCount++;
807
+ }
808
+ }
809
+ sibling = sibling.previousSibling;
810
+ }
811
+
812
+ //3. If the repetition template has a repeat-max attribute and that attribute's value is less than or equal
813
+ // to the number of repetition blocks associated with this template that were found in the previous step,
814
+ // the UA must stop at this step, returning a null value.
815
+ if(this.repeatMax <= currentBlockCount)
816
+ return null;
817
+
818
+ //4. If this algorithm was invoked via the addRepetitionBlockByIndex() method, and the value of the method's
819
+ // index argument is greater than the template's index, then the template's index is set to the value of the
820
+ // method's index argument.
821
+ if(index !== undefined && index > this.repetitionIndex)
822
+ this.repetitionIndex = index;
823
+
824
+ //(the following steps are out of order to facilitate a custom cloneNode to cope for MSIE and Gecko issues)
825
+
826
+ //9. If the new repetition block has an ID attribute (that is, an attribute specifying an ID, regardless
827
+ // of the attribute's namespace or name), then that attribute's value is used as the template name in
828
+ // the following steps. Otherwise, the template has no name. (If there is more than one ID attribute,
829
+ // the "first" one in terms of node order is used. [DOM3CORE])
830
+ // [Since this step was moved here, it uses 'this' and not 'block', which hasn't been created yet]
831
+ //var IDAttr = block.getAttributeNode('id') ? block.getAttributeNode('id') : block.getAttributeNode('name'); //DETECT ID TYPE For others?
832
+ var IDAttrName = this.getAttribute('id') ? 'id' : this.getAttribute('name') ? 'name' : ''; //NOTE: hasAttribute not implemented in MSIE
833
+ var IDAttrValue = this.getAttribute(IDAttrName);
834
+
835
+ //5. A clone of the template is made. The resulting element is the new repetition block element.
836
+ // [Note that the DOM cloneNode method is not invoked in this implementation due to MSIE
837
+ // errors, such as not being able to modify the name attribute of an existing node and strange Gecko behavior
838
+ // regarding the inconsistant correspondence of an input node's value attribute and value property.
839
+ // Instead of invoking the native cloneNode method, each element is copied manually when it is iterated over.]
840
+ // [Note: step 11 of the the specification's algorithm has been merged into step 5. See note at step 11 below]
841
+ //(11). If the template has a name and it is not being ignored (see the previous two steps), then, for every
842
+ // attribute on the new element, and for every attribute in every descendant of the new element: if the
843
+ // attribute starts with a zero-width non-breaking space character (U+FEFF) then that character is
844
+ // removed from the attribute; otherwise, any occurrences of a string consisting of an opening square
845
+ // bracket (U+005B, "[") or a modifier letter half triangular colon (U+02D1), the template's name,
846
+ // and a closing square bracket (U+005D, "]") or a middle dot (U+00B7), are replaced by the new
847
+ // repetition block's index. This is performed regardless of the types, names, or namespaces of attributes,
848
+ // and is done to all descendants, even those inside nested forms, nested repetition templates, and so forth.
849
+ var block;
850
+
851
+ //(10). If the template has a name (see the previous step), and that name contains either an opening square
852
+ // bracket (U+005B, "[") a modifier letter half triangular colon (U+02D1), a closing square bracket
853
+ // (U+005D, "]") or a middle dot (U+00B7), then the template's name is ignored for the purposes of
854
+ // [this] step.
855
+ var replaceValue = this.repetitionIndex;
856
+ var reTemplateName, processAttr;
857
+ if(IDAttrValue && !/\u005B|\u02D1|\u005D|\u00B7/.test(IDAttrValue)){ //VALID LOGIC?
858
+ reTemplateName = new RegExp("(\\[|\u02D1)" + IDAttrValue + "(\\]|\u00B7)", 'g'); //new RegExp('(\\u005B|\\u02D1)' + IDAttrValue + '(\\u005D|\\u00B7)', 'g');
859
+ processAttr = function(attrVal){ //Function that processes an attribute value as defined in step 11
860
+ if(!attrVal)
861
+ return attrVal;
862
+ attrVal = attrVal.toString();
863
+ if(attrVal.indexOf("\uFEFF") === 0)
864
+ return attrVal.replace(/^\uFEFF/, '');
865
+ return attrVal.replace(reTemplateName, replaceValue);
866
+ };
867
+ }
868
+ block = $wf2.cloneNode(this, processAttr);
869
+ block.wf2Initialized = false;
870
+ reTemplateName = null;
871
+
872
+ //6. If this algorithm was invoked via the addRepetitionBlockByIndex() method, the new repetition block
873
+ // element's index is set to the method's index argument. Otherwise, the new repetition block element's
874
+ // index is set to the template's index. [Note: if called by addRepetitionBlockByIndex() then the
875
+ // template's repetitionIndex has already been set to the index argument. Redundant algorithm step.]
876
+ //block.repetitionIndex = this.repetitionIndex; //this is set in the constructor for the repetitionBlock
877
+ //7. If the new repetition block element is in the http://www.w3.org/1999/xhtml namespace, then the
878
+ // repeat attribute in no namespace on the cloned element has its value changed to the new block's
879
+ // index. Otherwise, the repeat attribute in the http://www.w3.org/1999/xhtml namespace has its value
880
+ // changed to the new block's index.
881
+ //if(block.namespaceURI == 'http://www.w3.org/1999/xhtml')
882
+ block.setAttribute('repeat', this.repetitionIndex); //when inserted into DOM, constructor sets block.repetitionIndex
883
+ //else
884
+ // block.setAttributeNS('http://www.w3.org/1999/xhtml', 'repeat', this.repetitionIndex);
885
+
886
+ //8. If the new repetition block element is in the http://www.w3.org/1999/xhtml namespace, then any
887
+ // repeat-min, repeat-max, or repeat-start attributes in no namespace are removed from the element.
888
+ // Otherwise, any repeat-min, repeat-max, or repeat-start attributes in the http://www.w3.org/1999/xhtml
889
+ // namespace are removed instead.
890
+
891
+ //if(block.namespaceURI == 'http://www.w3.org/1999/xhtml'){
892
+ block.removeAttribute('repeat-min');
893
+ block.removeAttribute('repeat-max');
894
+ block.removeAttribute('repeat-start');
895
+ //}
896
+ //else {
897
+ // block.removeAttributeNS('http://www.w3.org/1999/xhtml', 'repeat-min');
898
+ // block.removeAttributeNS('http://www.w3.org/1999/xhtml', 'repeat-max');
899
+ // block.removeAttributeNS('http://www.w3.org/1999/xhtml', 'repeat-start');
900
+ //}
901
+
902
+ //(steps 9 and 10 moved to before step 5 (operates on this repetition template, and not on cloned block))
903
+
904
+
905
+ //11. (Note: the algorithm below which most closely follows the algorithm as described in the specification,
906
+ // this has been merged into the cloning of the template in step 5. This has been done because of MSIE
907
+ // errors, such as not being able to modify the name attribute of an existing node and strange Gecko behavior
908
+ // regarding the inconsistant correspondence of an input node's value attribute and value property.)
909
+ //if(IDAttrValue && !ignoreName){
910
+ // var reTemplateName = new RegExp('(?:\\u005B|\\u02D1)' + IDAttrValue + '(?:\\u005D|\\u00B7)', 'g');
911
+ // function processAttrs(node){
912
+ // var i,attr;
913
+ // for(i = 0; node.attributes && i < node.attributes.length; i++){
914
+ // if(!(attr = node.attributes[i]).nodeValue)
915
+ // continue;
916
+ //
917
+ // if(String(attr.nodeValue).indexOf("\uFEFF") === 0)
918
+ // attr.nodeValue = attr.nodeValue.replace(/^\uFEFF/, '');
919
+ //
920
+ // else if(reTemplateName.test(attr.nodeValue))
921
+ // attr.nodeValue = attr.nodeValue.replace(reTemplateName, block.getAttribute('repeat'));
922
+ // }
923
+ // for(i = 0; i < node.childNodes.length; i++)
924
+ // processAttrs(node.childNodes[i]);
925
+ // }
926
+ // processAttrs(block);
927
+ //}
928
+
929
+
930
+ //12. If the template has a name (see the earlier steps): If the new repetition block element is in the
931
+ // http://www.w3.org/1999/xhtml namespace, then the repeat-template attribute in no namespace on the
932
+ // cloned element has its value set to the template's name. Otherwise, the repeat-template attribute
933
+ // in the http://www.w3.org/1999/xhtml namespace has its value set to the template's name. (This
934
+ // happens even if the name was ignored for the purposes of the previous step.)
935
+ if(IDAttrName){
936
+ //if(block.namespaceURI == "http://www.w3.org/1999/xhtml")
937
+ block.setAttribute('repeat-template', IDAttrValue); //block.setAttributeNS(null, 'repeat-template', IDAttr.nodeValue);
938
+ //else
939
+ // block.setAttributeNS('http://www.w3.org/1999/xhtml', 'repeat-template', IDAttr.nodeValue);
940
+
941
+
942
+ //13. The attribute from which the template's name was derived, if any, and even if it was ignored, is
943
+ // removed from the new repetition block element. (See the previous four steps.)
944
+ block.removeAttribute(IDAttrName);
945
+ }
946
+
947
+ //14. If the first argument to the method was null, then the template once again crawls through its
948
+ // previous siblings, this time stopping at the first node (possibly the template itself) whose
949
+ // previous sibling is a repetition block (regardless of what that block's template is) or the first
950
+ // node that has no previous sibling, whichever comes first. The new element is the inserted into the
951
+ // parent of the template, immediately before that node. Mutation events are fired if appropriate.
952
+ if(!refNode){
953
+ refNode = this;
954
+ while(refNode.previousSibling && refNode.previousSibling.repetitionType != RepetitionElement.REPETITION_BLOCK)
955
+ refNode = refNode.previousSibling;
956
+ this.parentNode.insertBefore(block, refNode);
957
+ this.repetitionBlocks.push(block);
958
+ }
959
+ //15. Otherwise, the new element is inserted into the parent of the node that was passed to the method
960
+ // as the first argument, immediately after that node (before the node's following sibling, if any).
961
+ // Mutation events are fired if appropriate.
962
+ else {
963
+ refNode.parentNode.insertBefore(block, refNode.nextSibling);
964
+ this.repetitionBlocks.push(block);
965
+ if($wf2.sortNodes)
966
+ this.repetitionBlocks.sort($wf2.sortNodes);
967
+ }
968
+
969
+ //16. The template's index is increased by one.
970
+ this.repetitionIndex++;
971
+
972
+ //[apply constructors to the new repetition block, and to the new remove buttons, add buttons, etc]
973
+ $wf2.constructRepetitionBlock.apply(block);
974
+ $wf2.initRepetitionTemplates(block);
975
+
976
+ $wf2.initRepetitionButtons('add', block);
977
+ $wf2.initRepetitionButtons('remove', block);
978
+ $wf2.initRepetitionButtons('move-up', block);
979
+ $wf2.initRepetitionButtons('move-down', block);
980
+
981
+ //In addition, user agents must automatically disable add buttons (irrespective of the value of the
982
+ // disabled DOM attribute) when the buttons are not in a repetition block that has an associated
983
+ // template and their template attribute is either not specified or does not have an ID that points
984
+ // to a repetition template, and, when the repetition template's repeat-max attribute is less than
985
+ // or equal to the number of repetition blocks that are associated with that template and that have
986
+ // the same parent. This automatic disabling does not affect the DOM disabled attribute. It is an
987
+ // intrinsic property of these buttons.
988
+ if($wf2.isInitialized){ //if buttons not yet initialized, will initially be called by _init_document
989
+ $wf2.updateAddButtons(this);
990
+ $wf2.updateMoveButtons(this.parentNode);
991
+ }
992
+
993
+ //Setup block with the other WF2 behavior
994
+ $wf2.initNonRepetitionFunctionality(block);
995
+ //var els = $wf2.getElementsByTagNamesAndAttribute.apply(block, [['*'], "autofocus"]); //ISSUE: Any form control (except hidden and output controls) can have an autofocus attribute specified. //var elName = els[i].nodeName.toLowerCase(); if(elName == 'output' || (elName == 'input' && els[i].type == 'hidden'))
996
+ //for(var i = 0; i < els.length; i++)
997
+ // $wf2.initAutofocusElement(els[i]);
998
+
999
+ //17. An added event with no namespace, which bubbles but is not cancelable and has no default action,
1000
+ // must be fired on the repetition template using the RepetitionEvent interface, with the repetition
1001
+ // block's DOM node as the context information in the element attribute.
1002
+ var addEvt;
1003
+ try {
1004
+ if(document.createEvent)
1005
+ addEvt = document.createEvent('UIEvents'); //document.createEvent("RepetitionEvent")
1006
+ else if(document.createEventObject)
1007
+ addEvt = document.createEventObject();
1008
+ RepetitionEvent._upgradeEvent.apply(addEvt);
1009
+ addEvt.initRepetitionEvent('added', true /*canBubble*/, false /*cancelable*/, block);
1010
+ if(this.dispatchEvent)
1011
+ this.dispatchEvent(addEvt);
1012
+ else if(this.fireEvent){
1013
+ //console.warn("fireEvent('onadd') for MSIE is not yet working");
1014
+ //this.fireEvent('onadded', addEvt);
1015
+ }
1016
+ }
1017
+ catch(err){
1018
+ addEvt = new Object();
1019
+ RepetitionEvent._upgradeEvent.apply(addEvt);
1020
+ addEvt.initRepetitionEvent('added', true /*canBubble*/, false /*cancelable*/, block);
1021
+ }
1022
+
1023
+ //Add support for event handler set with HTML attribute
1024
+ var handlerAttr;
1025
+ if((handlerAttr = this.getAttribute('onadded')) && (!this.onadded || typeof this.onadded != 'function')){ //in MSIE, attribute == property
1026
+ this.onadded = new Function('event', handlerAttr);
1027
+ }
1028
+ //Deprecated
1029
+ else if((handlerAttr = this.getAttribute('onadd')) && (!this.onadd || typeof this.onadd != 'function')){
1030
+ this.onadd = new Function('event', handlerAttr);
1031
+ }
1032
+
1033
+ try {
1034
+ //Dispatch events for the old event model (extension to spec)
1035
+ if(this.onadded){
1036
+ //this.onadded(addEvt);
1037
+ this.onadded.apply(this, [addEvt]); //for some reason, exceptions cannot be caught if using the method above in MSIE
1038
+ }
1039
+ else if(this.onadd){ //deprecated
1040
+ //this.onadd(addEvt);
1041
+ this.onadd.apply(this, [addEvt]);
1042
+ }
1043
+ }
1044
+ catch(err){
1045
+ //throw exception within setTimeout so that the current execution will not be aborted
1046
+ setTimeout(function(){
1047
+ throw err;
1048
+ }, 0); //using 0 milliseconds done at <http://novemberborn.net/javascript/threading-quick-tip>
1049
+ }
1050
+
1051
+ //18. The return value is the newly cloned element.
1052
+ return block;
1053
+ },
1054
+ //Element addRepetitionBlockByIndex(in Node refNode, in long index);
1055
+ addRepetitionBlockByIndex : function(refNode, index){
1056
+ $wf2.addRepetitionBlock.apply(this, [refNode, index])
1057
+ },
1058
+
1059
+ //## RemoveRepetitionBlock algorithm #############################################################
1060
+
1061
+ //void removeRepetitionBlock();
1062
+ removeRepetitionBlock : function(){
1063
+ if(this.repetitionType != RepetitionElement.REPETITION_BLOCK)
1064
+ throw $wf2.DOMException(9); //NOT_SUPPORTED_ERR
1065
+
1066
+ //1. The node is removed from its parent, if it has one. Mutation events are fired if appropriate.
1067
+ // (This occurs even if the repetition block is an orphan repetition block.)
1068
+ var parentNode = this.parentNode; //save for updateMoveButtons
1069
+ var block = parentNode.removeChild(this);
1070
+ $wf2.updateMoveButtons(parentNode);
1071
+
1072
+ //The following loop used to appear within step #3 below;
1073
+ // this caused problems because the program state was incorrect when onremoved was called (repetitionBlocks was not modified)
1074
+ if(this.repetitionTemplate != null){
1075
+ for(var i = 0; i < this.repetitionTemplate.repetitionBlocks.length; i++){
1076
+ if(this.repetitionTemplate.repetitionBlocks[i] == this){
1077
+ this.repetitionTemplate.repetitionBlocks.splice(i,1);
1078
+ break;
1079
+ }
1080
+ }
1081
+ }
1082
+
1083
+ //2. If the repetition block is not an orphan, a removed event with no namespace, which bubbles but
1084
+ // is not cancelable and has no default action, must be fired on the element's repetition template,
1085
+ // using the RepetitionEvent interface, with the repetition block's DOM node as the context information
1086
+ // in the element attribute.
1087
+ if(this.repetitionTemplate != null){
1088
+ var removeEvt;
1089
+ try {
1090
+ if(document.createEvent)
1091
+ removeEvt = document.createEvent('UIEvents'); //document.createEvent("RepetitionEvent")
1092
+ else if(document.createEventObject)
1093
+ removeEvt = document.createEventObject();
1094
+ RepetitionEvent._upgradeEvent.apply(removeEvt);
1095
+ removeEvt.initRepetitionEvent('removed', true /*canBubble*/, false /*cancelable*/, this);
1096
+ if(this.repetitionTemplate.dispatchEvent)
1097
+ this.repetitionTemplate.dispatchEvent(removeEvt);
1098
+ else if(this.repetitionTemplate.fireEvent){
1099
+ //console.warn("fireEvent('onremoved') for MSIE is not yet working");
1100
+ //this.repetitionTemplate.fireEvent('onremoved', removeEvt);
1101
+ }
1102
+ }
1103
+ catch(err){
1104
+ removeEvt = new Object();
1105
+ RepetitionEvent._upgradeEvent.apply(removeEvt);
1106
+ removeEvt.initRepetitionEvent('removed', true /*canBubble*/, false /*cancelable*/, this);
1107
+ }
1108
+
1109
+ //Add support for event handler set with HTML attribute
1110
+ var handlerAttr;
1111
+ if((handlerAttr = this.repetitionTemplate.getAttribute('onremoved')) &&
1112
+ (!this.repetitionTemplate.onremoved || typeof this.repetitionTemplate.onremoved != 'function')) //in MSIE, attribute == property
1113
+ {
1114
+ this.repetitionTemplate.onremoved = new Function('event', handlerAttr);
1115
+ }
1116
+ //Deprecated
1117
+ else if((handlerAttr = this.repetitionTemplate.getAttribute('onremove')) &&
1118
+ (!this.repetitionTemplate.onremove || typeof this.repetitionTemplate.onremove != 'function'))
1119
+ {
1120
+ this.repetitionTemplate.onremove = new Function('event', handlerAttr);
1121
+ }
1122
+
1123
+ try {
1124
+ //Dispatch events for the old event model (extension to spec)
1125
+ if(this.repetitionTemplate.onremoved){
1126
+ //this.repetitionTemplate.onremoved(removeEvt);
1127
+ this.repetitionTemplate.onremoved.apply(this, [removeEvt]); //for some reason, exceptions cannot be caught if using the method above in MSIE
1128
+ }
1129
+ else if(this.repetitionTemplate.onremove){ //deprecated
1130
+ //this.repetitionTemplate.onremove(removeEvt);
1131
+ this.repetitionTemplate.onremove.apply(this, [removeEvt]);
1132
+ }
1133
+ }
1134
+ catch(err){
1135
+ //throw exception within setTimeout so that the current execution will not be aborted
1136
+ setTimeout(function(){
1137
+ throw err;
1138
+ }, 0);
1139
+ }
1140
+ }
1141
+
1142
+ //3. If the repetition block is not an orphan, then while the remaining number of repetition blocks
1143
+ // associated with the original element's repetition template and with the same parent as the template
1144
+ // is less than the template's repeat-min attribute and less than its repeat-max attribute, the
1145
+ // template's replication behaviour is invoked (specifically, its addRepetitionBlock() method is called).
1146
+ if(this.repetitionTemplate != null){
1147
+ // //BUG: The following needs to be moved before the call to onremoved
1148
+ // var t = this.repetitionTemplate;
1149
+ // for(var i = 0; i < t.repetitionBlocks.length; i++){
1150
+ // if(t.repetitionBlocks[i] == this){
1151
+ // t.repetitionBlocks.splice(i,1);
1152
+ // break;
1153
+ // }
1154
+ // }
1155
+ if(this.repetitionTemplate.repetitionBlocks.length < this.repetitionTemplate.repeatMin
1156
+ && this.repetitionTemplate.repetitionBlocks.length < this.repetitionTemplate.repeatMax)
1157
+ {
1158
+ this.repetitionTemplate.addRepetitionBlock();
1159
+ }
1160
+
1161
+ //enable add buttons
1162
+ if(this.repetitionTemplate.repetitionBlocks.length < this.repetitionTemplate.repeatMax){
1163
+ //var addBtns = cssQuery("button[type=add]");
1164
+ var addBtns = $wf2.getElementsByTagNamesAndAttribute.apply(document.documentElement, [['button'], 'type', 'add']);
1165
+ for(i = 0; i < addBtns.length; i++){
1166
+ if(addBtns[i].htmlTemplate == this.repetitionTemplate)
1167
+ addBtns[i].disabled = false;
1168
+ }
1169
+ }
1170
+ }
1171
+ },
1172
+
1173
+ //## MoveRepetitionBlock algorithm #############################################################
1174
+
1175
+ //void moveRepetitionBlock(in long distance);
1176
+ moveRepetitionBlock : function(distance){
1177
+ if(this.repetitionType != RepetitionElement.REPETITION_BLOCK)
1178
+ throw $wf2.DOMException(9); //NOT_SUPPORTED_ERR
1179
+
1180
+ //1. If distance is 0, or if the repetition block has no parent, nothing happens and the algorithm ends here.
1181
+ if(distance == 0 || this.parentNode == null)
1182
+ return;
1183
+
1184
+ //2. Set target, a reference to a DOM Node, to the repetition block being moved.
1185
+ // [Furthermore, move the reference to this block in the template's repetitionBlocks HTMLCollection to
1186
+ // reflect the new position that it is being moved to.]
1187
+ var target = this;
1188
+ if(this.repetitionTemplate){
1189
+ var pos = 0;
1190
+ var rp = this.repetitionTemplate.repetitionBlocks;
1191
+ while(pos < rp.length && rp[pos] != this)
1192
+ pos++;
1193
+ rp.splice(pos, 1);
1194
+ rp.splice(distance < 0 ? Math.max(pos+distance, 0) : Math.min(pos+distance, rp.length), 0, this);
1195
+ }
1196
+
1197
+ //3. If distance is negative: while distance is not zero and target's previousSibling is defined and is
1198
+ // not a repetition template, set target to this previousSibling and, if it is a repetition block,
1199
+ // increase distance by one (make it less negative by one).
1200
+ if(distance < 0){
1201
+ while(distance != 0 && target.previousSibling &&
1202
+ target.previousSibling.repetitionType != RepetitionElement.REPETITION_TEMPLATE)
1203
+ {
1204
+ target = target.previousSibling;
1205
+ if(target.repetitionType == RepetitionElement.REPETITION_BLOCK)
1206
+ distance++;
1207
+ }
1208
+ }
1209
+ //4. Otherwise, distance is positive: while distance is not zero and target's nextSibling is defined
1210
+ // and is not a repetition template, set target to this nextSibling and, if it is a repetition block,
1211
+ // decrease distance by one. After the loop, set target to target's nextSibling (which may be null).
1212
+ else {
1213
+ while(distance != 0 && target.nextSibling && target.nextSibling.repetitionType != RepetitionElement.REPETITION_TEMPLATE){
1214
+ target = target.nextSibling;
1215
+ if(target.repetitionType == RepetitionElement.REPETITION_BLOCK)
1216
+ distance--;
1217
+ }
1218
+ target = target.nextSibling;
1219
+ }
1220
+
1221
+ //5. Call the repetition block's parent node's insertBefore() method with the newChild argument
1222
+ // being the repetition block and the refChild argument being target (which may be null by this
1223
+ // point). Mutation events are fired if appropriate.
1224
+ this.parentNode.insertBefore(this, target);
1225
+
1226
+ //Keep focus on the move button which was clicked
1227
+ if(this._clickedMoveBtn){
1228
+ this._clickedMoveBtn.focus();
1229
+ this._clickedMoveBtn = null;
1230
+ }
1231
+
1232
+ //In addition, user agents must automatically disable move-up buttons (irrespective of the
1233
+ // value of the disabled DOM attribute) when their repetition block could not be moved any
1234
+ // higher according to the algorithm above, and when the buttons are not in a repetition
1235
+ // block. Similarly, user agents must automatically disable move-down buttons when their
1236
+ // repetition block could not be moved any lower according to the algorithm above, and
1237
+ // when the buttons are not in a repetition block. This automatic disabling does not affect
1238
+ // the DOM disabled attribute. It is an intrinsic property of these buttons.
1239
+ $wf2.updateMoveButtons(this.parentNode);
1240
+
1241
+ //6. A moved event with no namespace, which bubbles but is not cancelable and has no default action,
1242
+ // must be fired on the element's repetition template (if it has one), using the RepetitionEvent
1243
+ // interface, with the repetition block's DOM node as the context information in the element attribute.
1244
+ if(this.repetitionTemplate != null){
1245
+ var moveEvt;
1246
+ try {
1247
+ if(document.createEvent)
1248
+ moveEvt = document.createEvent('UIEvents'); //document.createEvent("RepetitionEvent")
1249
+ else if(document.createEventObject)
1250
+ moveEvt = document.createEventObject();
1251
+ RepetitionEvent._upgradeEvent.apply(moveEvt);
1252
+ moveEvt.initRepetitionEvent('moved', true /*canBubble*/, false /*cancelable*/, this);
1253
+ if(this.repetitionTemplate.dispatchEvent)
1254
+ this.repetitionTemplate.dispatchEvent(moveEvt);
1255
+ else if(this.repetitionTemplate.fireEvent){
1256
+ //console.warn("fireEvent('onmoved') for MSIE is not yet working");
1257
+ //this.fireEvent('onmoved', moveEvt);
1258
+ }
1259
+ }
1260
+ catch(err){
1261
+ moveEvt = new Object();
1262
+ RepetitionEvent._upgradeEvent.apply(moveEvt);
1263
+ moveEvt.initRepetitionEvent('moved', true /*canBubble*/, false /*cancelable*/, this);
1264
+ }
1265
+
1266
+ //Add support for event handler set with HTML attribute---------------------
1267
+ var handlerAttr;
1268
+ if((handlerAttr = this.repetitionTemplate.getAttribute('onmoved')) &&
1269
+ (!this.repetitionTemplate.onmoved || typeof this.repetitionTemplate.onmoved != 'function')) //in MSIE, attribute == property
1270
+ {
1271
+ this.repetitionTemplate.onmoved = new Function('event', handlerAttr);
1272
+ }
1273
+ //Deprecated
1274
+ else if(handlerAttr = this.repetitionTemplate.getAttribute('onmove'))
1275
+ {
1276
+ if(!this.repetitionTemplate.onmove || typeof this.repetitionTemplate.onmove != 'function'){
1277
+ this.repetitionTemplate.onmove = new Function('event', handlerAttr);
1278
+ }
1279
+
1280
+ //For MSIE, onmove is already an event, and attributes are equal to properties, so attribute value can be function.
1281
+ // The 'event' argument must be added to the function argument list.
1282
+ var funcMatches;
1283
+ if(typeof handlerAttr == 'function' && (funcMatches = handlerAttr.toString().match(/^\s*function\s+anonymous\(\s*\)\s*\{((?:.|\n)+)\}\s*$/))){
1284
+ this.repetitionTemplate.onmove = new Function('event', funcMatches[1]);
1285
+ }
1286
+ }
1287
+
1288
+
1289
+ // var onmovedAttr = this.repetitionTemplate.getAttribute('onmoved')
1290
+ // || /* deprecated */ this.repetitionTemplate.getAttribute('onmove');
1291
+
1292
+ //For MSIE, onmove is already an event, and attributes are equal to properties, so attribute value can be function.
1293
+ // The 'event' argument must be added to the function argument list.
1294
+ // var funcMatches;
1295
+ // if(typeof onmovedAttr == 'function' && (funcMatches = onmovedAttr.toString().match(/^\s*function\s+anonymous\(\s*\)\s*\{((?:.|\n)+)\}\s*$/))){
1296
+ // this.repetitionTemplate.onmoved = new Function('event', funcMatches[1]);
1297
+ // }
1298
+
1299
+ //If the onmove attribute has been set but the property (method) has not
1300
+ // if(onmovedAttr && !this.repetitionTemplate.onmoved)
1301
+ // this.repetitionTemplate.onmoved = new Function('event', onmovedAttr);
1302
+
1303
+ //This need not be done in MSIE since onmove is already an event, and attributes == properties
1304
+ //if(onmoveAttr && typeof onmoveAttr != 'function' /* for MSIE */ &&
1305
+ // (!this.repetitionTemplate.onmove || typeof this.repetitionTemplate.onmove != 'function')
1306
+ // ){
1307
+ // this.repetitionTemplate.onmove = new Function('event', onmoveAttr);
1308
+ //}
1309
+
1310
+ try {
1311
+ //Dispatch events for the old event model (extension to spec)
1312
+ if(this.repetitionTemplate.onmoved){
1313
+ //this.repetitionTemplate.onmoved(moveEvt);
1314
+ this.repetitionTemplate.onmoved.apply(this, [moveEvt]);
1315
+ }
1316
+ else if(this.repetitionTemplate.onmove){ //deprecated
1317
+ //this.repetitionTemplate.onmove(moveEvt);
1318
+ this.repetitionTemplate.onmove.apply(this, [moveEvt]);
1319
+ }
1320
+ }
1321
+ catch(err){
1322
+ //throw exception within setTimeout so that the current execution will not be aborted
1323
+ setTimeout(function(){
1324
+ throw err;
1325
+ }, 0); //using 0 milliseconds done at <http://novemberborn.net/javascript/threading-quick-tip>
1326
+ }
1327
+ }
1328
+ },
1329
+
1330
+ //## Other helper functions for the repetition model behaviors ##############################
1331
+ getRepetitionBlock : function(node){
1332
+ while(node = node.parentNode){
1333
+ if(node.repetitionType == RepetitionElement.REPETITION_BLOCK){
1334
+ return node;
1335
+ }
1336
+ }
1337
+ return null;
1338
+ },
1339
+
1340
+ getHtmlTemplate : function(button){
1341
+ var attr = button.getAttribute('template');
1342
+ var node;
1343
+ if(attr && (node = document.getElementById(attr)) && node.getAttribute('repeat') == 'template' /*node.repetitionType == RepetitionElement.REPETITION_TEMPLATE*/)
1344
+ return node;
1345
+ return null;
1346
+ },
1347
+
1348
+ updateAddButtons : function(rt){
1349
+ //In addition, user agents must automatically disable add buttons (irrespective of the value of the
1350
+ // disabled DOM attribute) when the buttons are not in a repetition block that has an associated
1351
+ // template and their template attribute is either not specified or does not have an ID that points
1352
+ // to a repetition template, and, when the repetition template's repeat-max attribute is less than
1353
+ // or equal to the number of repetition blocks that are associated with that template and that have
1354
+ // the same parent. This automatic disabling does not affect the DOM disabled attribute. It is an
1355
+ // intrinsic property of these buttons.
1356
+
1357
+ var repetitionTemplates = rt ? [rt] : $wf2.repetitionTemplates;
1358
+
1359
+ //var btns = cssQuery("button[type=add]");
1360
+ var btns = $wf2.getElementsByTagNamesAndAttribute.apply(document.documentElement, [['button'], 'type', 'add']);
1361
+ for(var i = 0; i < btns.length; i++){
1362
+ for(var t, j = 0; t = repetitionTemplates[j]; j++){
1363
+ if(btns[i].htmlTemplate == t && t.repetitionBlocks.length >= t.repeatMax){
1364
+ btns[i].disabled = true;
1365
+ }
1366
+ }
1367
+ }
1368
+ },
1369
+
1370
+ updateMoveButtons : function(parent){
1371
+ //In addition, user agents must automatically disable move-up buttons (irrespective of the value of
1372
+ // the disabled DOM attribute) when their repetition block could not be moved any higher according
1373
+ // to the algorithm above, and when the buttons are not in a repetition block. Similarly, user agents
1374
+ // must automatically disable move-down buttons when their repetition block could not be moved any
1375
+ // lower according to the algorithm above, and when the buttons are not in a repetition block. This
1376
+ // automatic disabling does not affect the DOM disabled attribute. It is an intrinsic property of
1377
+ // these buttons.
1378
+
1379
+ var i;
1380
+ var rbs = [];
1381
+
1382
+ //update all move buttons if a repetition block's parent was not given
1383
+ if(!parent){
1384
+ var visitedParents = [];
1385
+ //var rbs = cssQuery('*[repeat]:not([repeat="template"])');
1386
+ //var rbs = $wf2.getElementsByProperty('repetitionType', RepetitionElement.REPETITION_BLOCK);
1387
+ rbs = $wf2.getElementsByTagNamesAndAttribute.apply(document.documentElement, [['*'], 'repeat', 'template', true]);
1388
+ for(i = 0; block = rbs[i]; i++){
1389
+ //if(!visitedParents.some(function(i){return i == block.parentNode})){
1390
+ if(!$wf2.arrayHasItem(visitedParents, block.parentNode)){
1391
+ $wf2.updateMoveButtons(block.parentNode);
1392
+ visitedParents.push(block.parentNode);
1393
+ }
1394
+ }
1395
+ return;
1396
+ }
1397
+
1398
+ //get all of the repetition block siblings
1399
+ var j,btn,block;
1400
+ var child = parent.firstChild;
1401
+ while(child){
1402
+ if(child.repetitionType == RepetitionElement.REPETITION_BLOCK)
1403
+ rbs.push(child);
1404
+ child = child.nextSibling;
1405
+ }
1406
+
1407
+ //disable or enable movement buttons within each block
1408
+ for(i = 0; block = rbs[i]; i++){
1409
+ //var moveUpBtns = cssQuery("button[type=move-up]", block);
1410
+ var moveUpBtns = $wf2.getElementsByTagNamesAndAttribute.apply(block, [['button'], 'type', 'move-up']);
1411
+ for(j = 0; btn = moveUpBtns[j]; j++){
1412
+ btn.disabled =
1413
+ //if the button is not in a repetition block
1414
+ !(rb = $wf2.getRepetitionBlock(btn))
1415
+ ||
1416
+ //when their repetition block could not be moved any lower
1417
+ (i == 0);
1418
+ }
1419
+ //var moveDownBtns = cssQuery("button[type=move-down]", block);
1420
+ var moveDownBtns = $wf2.getElementsByTagNamesAndAttribute.apply(block, [['button'], 'type', 'move-down']);
1421
+ for(j = 0; btn = moveDownBtns[j]; j++){
1422
+ btn.disabled =
1423
+ //if the button is not in a repetition block
1424
+ !(rb = $wf2.getRepetitionBlock(btn))
1425
+ ||
1426
+ //when their repetition block could not be moved any higher
1427
+ (i == rbs.length-1);
1428
+ }
1429
+ }
1430
+ },
1431
+
1432
+ /*#############################################################################################
1433
+ # Section: Extensions to the input element
1434
+ ##############################################################################################*/
1435
+
1436
+ initNonRepetitionFunctionality : function(parent){
1437
+ parent = (parent || document.documentElement);
1438
+ var i,j, frm, frms = parent.getElementsByTagName('form');
1439
+ for(i = 0; frm = frms[i]; i++){
1440
+ if(frm.checkValidity && !$wf2.hasBadImplementation)
1441
+ continue;
1442
+ frm.checkValidity = $wf2.formCheckValidity;
1443
+
1444
+ if(frm.addEventListener)
1445
+ frm.addEventListener('submit', $wf2.onsubmitValidityHandler, false);
1446
+ else
1447
+ frm.attachEvent('onsubmit', $wf2.onsubmitValidityHandler);
1448
+ }
1449
+
1450
+ var ctrl, ctrls = $wf2.getElementsByTagNames.apply(parent, ['input','select','textarea', 'button']);//parent.getElementsByTagName([i]);
1451
+ for(i = 0; ctrl = ctrls[i]; i++){
1452
+ $wf2.applyValidityInterface(ctrl);
1453
+ $wf2.updateValidityState(ctrl); //ctrl._updateValidityState();
1454
+ }
1455
+
1456
+ //Autofocus **********************************************************
1457
+ //Authors must not set the autofocus attribute on multiple enabled elements in a document.
1458
+ // If multiple elements with the autofocus attribute set are inserted into a document, each one
1459
+ // will be processed as described above, as they are inserted. This means that during document
1460
+ // load, for example, the last focusable form control in document order with the attribute set
1461
+ // will end up with the focus.
1462
+ var els = $wf2.getElementsByTagNamesAndAttribute.apply(document.documentElement, [['*'], 'autofocus']); //ISSUE: Any form control (except hidden and output controls) can have an autofocus attribute specified. //var elName = els[i].nodeName.toLowerCase(); if(elName == 'output' || (elName == 'input' && els[i].type == 'hidden'))
1463
+ if(parent.getAttribute('autofocus'))
1464
+ els.unshift(parent);
1465
+ for(i = 0; i < els.length; i++)
1466
+ $wf2.initAutofocusElement(els[i]);
1467
+
1468
+ // Maxlength for textareas ******************************************************
1469
+ var textareas = $wf2.getElementsByTagNamesAndAttribute.apply(parent, [['textarea'], 'maxlength']);
1470
+ if(parent.nodeName.toLowerCase() == 'textarea')
1471
+ textareas.unshift(parent);
1472
+ for(i = 0; i < textareas.length; i++)
1473
+ textareas[i].maxLength = parseInt(textareas[i].getAttribute('maxlength'));
1474
+ //TODO: we must dynamically apply this behavior for new textareas (via repetition model or eventlistener)
1475
+ },
1476
+
1477
+ initAutofocusElement : function(el){
1478
+ //skip if already initialized
1479
+ if(el.autofocus === false || el.autofocus === true) //(el.autofocus !== undefined) does not work due to MSIE's handling of attributes
1480
+ return;
1481
+ el.autofocus = true;
1482
+
1483
+ //[autofocus if] the control is not disabled
1484
+ if(el.disabled)
1485
+ return;
1486
+
1487
+ //[control] is of a type normally focusable in the user's operating environment
1488
+ //Don't focus on the control if it is not visible or nor displayed
1489
+ var node = el;
1490
+ while(node && node.nodeType == 1){
1491
+ if($wf2.getElementStyle(node, 'visibility') == 'hidden' || $wf2.getElementStyle(node, 'display') == 'none')
1492
+ return;
1493
+ node = node.parentNode;
1494
+ }
1495
+
1496
+ //Then the UA should focus the control, as if the control's focus() method was invoked.
1497
+ // UAs with a viewport should also scroll the document enough to make the control visible,
1498
+ // [[even if it is not of a type normally focusable.]] //WHAT DOES THIS MEAN?
1499
+ el.focus(); //BUG: in Gecko this does not work within DOMNodeInserted event handler, but the following does; setTimeout(function(){el.focus();}, 0);
1500
+
1501
+
1502
+ },
1503
+
1504
+ /*#############################################################################################
1505
+ # Section: Form Validation model
1506
+ ##############################################################################################*/
1507
+
1508
+ formCheckValidity : function(){
1509
+ var i, el, valid = true;
1510
+
1511
+ //When a form is submitted, user agents must act as if they used the following algorithm.
1512
+ // First, each element in that form's elements list is added to a temporary list (note that
1513
+ // the elements list is defined to be in document order).
1514
+
1515
+ //An invalid event must be fired on each element that, when checked, is found to fail to
1516
+ // comply with its constraints (i.e. each element whose validity.valid DOM attribute is
1517
+ // false) and is still a member of the form after the event has been handled.
1518
+ //var _elements = [];
1519
+ var formElements = $wf2.getFormElements.apply(this);
1520
+ //for(i = 0; i < formElements.length; i++)
1521
+ // _elements.push(formElements[i]);
1522
+ for(i = 0; el = formElements[i]; i++){
1523
+ var type = (el.getAttribute('type') ? el.getAttribute('type').toLowerCase() : el.type);
1524
+ el.willValidate = !(/(hidden|button|reset|add|remove|move-up|move-down)/.test(type) || !el.name || el.disabled)
1525
+ //Then, each element in this list whose willValidate DOM attribute is true is checked for validity
1526
+ if(el.checkValidity && el.willValidate){
1527
+ if(!el.checkValidity() && el.checkValidity() != undefined)
1528
+ valid = false;
1529
+ }
1530
+ }
1531
+
1532
+ if (!valid) {
1533
+ $wf2.hiliteFirstError();
1534
+ }
1535
+ return valid;
1536
+ },
1537
+
1538
+ hiliteFirstError: function () {
1539
+
1540
+ if($wf2.invalidIndicators.length){ //second condition needed because modal in oninvalid handler may cause indicators to disappear before this is reached
1541
+ $wf2.invalidIndicators[0].errorMsg.className += " wf2_firstErrorMsg";
1542
+
1543
+ //scroll to near the location where invalid control is
1544
+ el = $wf2.invalidIndicators[0].target;
1545
+ if(el.style.display == 'none' || !el.offsetParent){
1546
+ while(el && (el.nodeType != 1 || (el.style.display == 'none' || !el.offsetParent)))
1547
+ el = el.previousSibling;
1548
+ var cur = el;
1549
+ var top = 0;
1550
+ if(cur && cur.offsetParent) {
1551
+ top = cur.offsetTop;
1552
+ while (cur = cur.offsetParent)
1553
+ top += cur.offsetTop;
1554
+ }
1555
+ scrollTo(0, top);
1556
+ }
1557
+ //focus on the first invalid control and make sure error message is visible
1558
+ else {
1559
+
1560
+ setTimeout(
1561
+ function() {
1562
+ el.focus();
1563
+ $wf2.fireEvent(el, 'focus');
1564
+ }
1565
+ , 10)
1566
+
1567
+ //NOTE: We should only do this if the control's style.bottom == 0
1568
+ scrollBy(0, $wf2.invalidIndicators[0].errorMsg.offsetHeight);
1569
+ }
1570
+ }
1571
+
1572
+ },
1573
+
1574
+ controlCheckValidity : function(){
1575
+ $wf2.controlCheckValidityOfElement(this);
1576
+
1577
+ },
1578
+
1579
+ controlCheckValidityOfElement: function (el) {
1580
+
1581
+
1582
+
1583
+ $wf2.updateValidityState(el);
1584
+
1585
+ if (el.validity.valid) {
1586
+
1587
+ return true;
1588
+ }
1589
+
1590
+ var canceled = false;
1591
+
1592
+ var evt;
1593
+ try {
1594
+ if(document.createEvent)
1595
+ evt = document.createEvent('Events'); //document.createEvent("RepetitionEvent")
1596
+ else if(document.createEventObject)
1597
+ evt = document.createEventObject();
1598
+ evt.initEvent('invalid', true /*canBubble*/, true /*cancelable*/);
1599
+ evt.srcElement = el;
1600
+ if(el.dispatchEvent)
1601
+ canceled = !el.dispatchEvent(evt);
1602
+ else if(el.fireEvent){
1603
+ //console.warn("fireEvent('oninvalid') for MSIE is not yet working");
1604
+ //el.fireEvent('oninvalid', invalidEvt);
1605
+ }
1606
+ }
1607
+ catch(err){
1608
+ evt = new Object();
1609
+ if(evt.initEvent)
1610
+ evt.initEvent('invalid', true /*canBubble*/, true /*cancelable*/);
1611
+ else {
1612
+ evt.type = 'invalid';
1613
+ evt.cancelBubble = false;
1614
+ }
1615
+ evt.target = evt.srcElement = el;
1616
+ }
1617
+
1618
+ var oninvalidAttr = el.getAttribute('oninvalid');
1619
+ if(oninvalidAttr && (!el.oninvalid || typeof el.oninvalid != 'function')) //in MSIE, attribute == property
1620
+ el.oninvalid = new Function('event', oninvalidAttr);
1621
+
1622
+ try {
1623
+ //Dispatch events for the old event model
1624
+ if(el.oninvalid){
1625
+ //canceled = el.oninvalid(evt) === false || canceled;
1626
+ canceled = el.oninvalid.apply(el, [evt]) === false || canceled; //for some reason, exceptions cannot be caught if using the method above in MSIE
1627
+ }
1628
+ }
1629
+ catch(err){
1630
+ //throw exception within setTimeout so that the current execution will not be aborted
1631
+ setTimeout(function(){
1632
+ throw err;
1633
+ }, 0);
1634
+ }
1635
+
1636
+ //Determine if this radio/checkbox already has an invalid indicator
1637
+ var hasInvalidIndicator = false;
1638
+ if(el.type == 'radio' || el.type == 'checkbox'){
1639
+ for(var i = 0; i < $wf2.invalidIndicators.length; i++){
1640
+ if(el.form[el.name][0] == $wf2.invalidIndicators[i].target){
1641
+ hasInvalidIndicator = true;
1642
+ break;
1643
+ }
1644
+ }
1645
+ }
1646
+
1647
+ //Do default action
1648
+ if(!canceled && !hasInvalidIndicator) //(!(el.form && el.form[el.name]) || !el.form[el.name].wf2HasInvalidIndicator)
1649
+ $wf2.addInvalidIndicator(el);
1650
+ return false;
1651
+ },
1652
+
1653
+ //Frequently used regular expressions //W(?:0[1-9]|[1-4]\d|5[0-2])|
1654
+ //monthRegExp : /^\d\d\d\d-(0\d|1[0-2])$/,
1655
+ //weekRegExp : /^(\d\d\d\d)-W(0[1-9]|[1-4]\d|5[0-2])$/,
1656
+ //timeRegExp : /^(0\d|1\d|2[0-4]):([0-5]\d)(:[0-5]\d(.\d+)?)?$/,
1657
+ numberRegExp : /^-?\d+(.\d+)?(e-?\d+)?$/,
1658
+ //numberOrAnyRegExp : /^(any|-?\d+(.\d+)?(e-?\d+)?)$/i,
1659
+ urlRegExp : /^(\w+):(\/\/)?.+$/i,
1660
+ emailRegExp : /^\S+@\S+$/i,
1661
+
1662
+ //Zero points for datetime-related types (set in onDOMContentLoaded function)
1663
+ // zeroPointDatetime : null,
1664
+ // zeroPointDatetimeLocal : null,
1665
+ // zeroPointDate : null,
1666
+ // zeroPointMonth : null,
1667
+ // zeroPointWeek : null,
1668
+ // zeroPointTime : null,
1669
+
1670
+ copyOf: function(obj) {
1671
+ if (obj !== null && obj !== undefined) {
1672
+ var r = new Object();
1673
+ for (i in obj) {
1674
+
1675
+ try {
1676
+ r[i] = obj[i];
1677
+ }
1678
+ catch (ex) {
1679
+ // do nothing;
1680
+ }
1681
+ }
1682
+
1683
+ } else {
1684
+ r = null;
1685
+ }
1686
+
1687
+ return r;
1688
+ },
1689
+
1690
+ getOriginalAttrNode: function (node, attrName) {
1691
+ var r;
1692
+
1693
+ var dataSetItemName = attrName + 'AttrNode';
1694
+
1695
+ if ($wf2.getDatasetItem(node, dataSetItemName) == null) {
1696
+ r = $wf2.copyOf(node.getAttributeNode(attrName));
1697
+ $wf2.setDatasetItem(node, dataSetItemName, r);
1698
+ } else {
1699
+ r = $wf2.getDatasetItem(node, dataSetItemName);
1700
+ if (r == 'null') {
1701
+ r = null;
1702
+ }
1703
+ }
1704
+
1705
+ return r;
1706
+ },
1707
+
1708
+ //This function is called "live"
1709
+ updateValidityState : function(node){
1710
+ //if(node.form && node.form[node.name] && node.form[node.name].wf2HasInvalidIndicator)
1711
+ // return;
1712
+
1713
+ var minAttrNode, maxAttrNode, valueAttrNode;
1714
+
1715
+ minAttrNode = $wf2.getOriginalAttrNode(node, 'min');
1716
+ maxAttrNode = $wf2.getOriginalAttrNode(node, 'max');
1717
+ valueAttrNode = $wf2.getOriginalAttrNode(node, 'value');
1718
+
1719
+ node.min = undefined; //wf2Min
1720
+ node.max = undefined; //wf2Max
1721
+ node.step = undefined; //wf2Step
1722
+
1723
+
1724
+
1725
+ node.validity = $wf2.createValidityState();
1726
+ node.validity.customError = !!node.validationMessage;
1727
+
1728
+ //var type = node.type ? node.type.toLowerCase() : 'text';
1729
+ //var type = (node.type ? node.getAttribute('type').toLowerCase() :
1730
+ // (node.nodeName.toLowerCase() == 'input' ? 'text' : ''));
1731
+ var type = (node.getAttribute('type') ? node.getAttribute('type').toLowerCase() : node.type);
1732
+ var isTimeRelated = (type == 'datetime' || type == 'datetime-local' || type == 'time'); //datetime, datetime-local, time
1733
+ var isDateRelated = (type == 'date' || type == 'month' || type == 'week'); //date, month, week
1734
+ var isNumberRelated = (type == 'number' || type == 'range'); //number, range
1735
+ var isFileInput = (type == 'file');
1736
+ var doCheckPrecision = (isTimeRelated || isDateRelated || isNumberRelated); //datetime, datetime-local, time, date, month, week, number, range
1737
+ var doMaxLengthCheck = doCheckPrecision || node.nodeName.toLowerCase() == 'textarea'; //datetime, datetime-local, time, date, month, week, number, range, textarea
1738
+ var doCheckRange = (doCheckPrecision || isFileInput); //datetime, datetime-local, time, date, month, week, number, range, file
1739
+ var isRadioOrCheckbox = (type == 'radio' || type == 'checkbox');
1740
+ var doRequiredCheck = (doMaxLengthCheck || //datetime, datetime-local, time, date, month, week, number, range, textarea
1741
+ isFileInput ||
1742
+ type == 'email' ||
1743
+ type == 'url' ||
1744
+ type == 'text' ||
1745
+ type == 'password'||
1746
+ type == 'tel' ||
1747
+ isRadioOrCheckbox);
1748
+
1749
+ //If a control has its type attribute changed to another type, then the user agent must reinterpret the min and
1750
+ // max attributes. If an attribute has an invalid value according to the new type, then the appropriate
1751
+ // default must be used (and not, e.g., the default appropriate for the previous type). Control values that
1752
+ // no longer match the range allowed for the control must be handled as described in the error handling section.
1753
+ //if(!node.wf2PreviousType)
1754
+ // node.wf2PreviousType == type;
1755
+ //else if(type != node.wf2PreviousType){
1756
+ // throw Error("Currently unable to change the type of a control."); //TODO
1757
+ //}
1758
+
1759
+ if(type == 'range'){
1760
+ //For this type...min defaults to 0...and value defaults to the min value.
1761
+ node.min = (minAttrNode && $wf2.numberRegExp.test(minAttrNode.value)) ? Number(minAttrNode.value) : 0;
1762
+ if((!valueAttrNode || !valueAttrNode.specified) && node.value === '' && !node.wf2ValueProvided){ //(!valueAttrNode || !valueAttrNode.specified) &&
1763
+ node.setAttribute('value', node.min);
1764
+ node.value = node.min;
1765
+ node.wf2ValueProvided = true;
1766
+ }
1767
+ }
1768
+
1769
+ node.wf2Value = node.value;
1770
+
1771
+ //valueMissing -- The control has the required attribute set but it has not been satisfied.
1772
+ //The required attribute applies to all form controls except controls with the type hidden,
1773
+ // image inputs, buttons (submit, move-up, etc), and select and output elements. For
1774
+ // disabled or readonly controls, the attribute has no effect.
1775
+ var type = (node.getAttribute('type') ? node.getAttribute('type').toLowerCase() : node.type);
1776
+ node.willValidate = !(/(hidden|button|reset|add|remove|move-up|move-down)/.test(type) || !node.name || node.disabled);
1777
+ if(doRequiredCheck && node.willValidate){
1778
+ //For checkboxes, the required attribute shall only be satisfied when one or more of
1779
+ // the checkboxes with that name in that form are checked.
1780
+ //For radio buttons, the required attribute shall only be satisfied when exactly one of
1781
+ // the radio buttons in that radio group is checked.
1782
+ if(isRadioOrCheckbox){
1783
+ if(node.form && node.form[node.name]){
1784
+ var isRequired = false;
1785
+ var hasChecked = false;
1786
+
1787
+ var inputs = node.form[node.name];
1788
+
1789
+ /*
1790
+ * remember: the above expression may return an array
1791
+ * or a single value. Must check for this.
1792
+ */
1793
+ if (inputs.length == undefined) {
1794
+ if (inputs.getAttributeNode('required'))
1795
+ isRequired = true;
1796
+ if (inputs.checked)
1797
+ hasChecked = true;
1798
+ } else {
1799
+ for (var i = 0; i < inputs.length; i++) {
1800
+ if (inputs[i].getAttributeNode('required'))
1801
+ isRequired = true;
1802
+ if (inputs[i].checked)
1803
+ hasChecked = true;
1804
+ }
1805
+ }
1806
+ node.validity.valueMissing = (isRequired && !hasChecked);
1807
+ }
1808
+ }
1809
+ //The required attribute applies to all form controls except controls with the type hidden,
1810
+ // image inputs, buttons (submit, move-up, etc), and select and output elements. For
1811
+ // disabled or readonly controls, the attribute has no effect.
1812
+ else if(node.getAttributeNode('required')){
1813
+ //if(node.options)
1814
+ // node.validity.valueMissing = (node.selectedIndex == -1);
1815
+ //For other controls, any non-empty value shall satisfy the required condition,
1816
+ // including a simple whitespace character.
1817
+ //else
1818
+ node.validity.valueMissing = (node.value == '');
1819
+ }
1820
+ //if(node.options ? node.selectedIndex == -1 : node.value === '')
1821
+ // node.validity.valueMissing = true;
1822
+ //
1823
+ }
1824
+ if(!node.validity.valueMissing && node.value){
1825
+ //patternMismatch -- The value of the control with a pattern attribute doesn't match the pattern.
1826
+ // If the control is empty, this flag must not be set.
1827
+ //If the pattern attribute is present but empty, it doesn't match any value, and thus the
1828
+ // patternMismatch flag shall be set whenever the control's value isn't empty.
1829
+ var patternAttr = node.getAttributeNode('pattern');
1830
+ if(patternAttr){
1831
+ //the pattern attribute must match the entire value, not just any subset (somewhat as if
1832
+ // it implied a ^(?: at the start of the pattern and a )$ at the end).
1833
+ var rePattern = new RegExp("^(?:" + patternAttr.value + ")$");
1834
+ //The pattern must be compiled with the global, ignoreCase, and multiline flags disabled
1835
+ rePattern.global = false;
1836
+ rePattern.ignoreCase = false;
1837
+ rePattern.multiline = false;
1838
+ //When the pattern is not a valid regular expression, it is ignored for the purposes of
1839
+ // validation, as if it wasn't specified.
1840
+ if(rePattern)
1841
+ node.validity.patternMismatch = !rePattern.test(node.value);
1842
+ }
1843
+
1844
+ //typeMismatch -- The data entered does not match the type of the control. For example, if the UA
1845
+ // allows uninterpreted arbitrary text entry for month controls, and the user has entered SEP02,
1846
+ // then this flag would be set. This code is also used when the selected file in a file upload
1847
+ // control does not have an appropriate MIME type. If the control is empty, this flag must not be set.
1848
+ if(isDateRelated || isTimeRelated)
1849
+ node.validity.typeMismatch = ((node.wf2Value = $wf2.parseISO8601(node.value, type)) == null);
1850
+ else {
1851
+ switch(type){
1852
+ case 'number':
1853
+ case 'range':
1854
+ node.validity.typeMismatch = !$wf2.numberRegExp.test(node.value);
1855
+ // if(!node.validity.typeMismatch && node.getAttribute("step") != 'any'){
1856
+ // if(node.step == undefined)
1857
+ // node.step = 1;
1858
+ // var val = Number(node.value);
1859
+ // node.validity.stepMismatch = (val == parseInt(val) && node.step != parseInt(node.step));
1860
+ // }
1861
+ break;
1862
+ case 'email':
1863
+ //An e-mail address, following the format of the addr-spec token defined in RFC 2822 section
1864
+ // 3.4.1 [RFC2822], but excluding the CFWS subtoken everywhere, and excluding the FWS
1865
+ // subtoken everywhere except in the quoted-string subtoken. UAs could, for example, offer
1866
+ // e-mail addresses from the user's address book. (See below for notes on IDN.)
1867
+ //http://www.ietf.org/rfc/rfc2822
1868
+ node.validity.typeMismatch = !$wf2.emailRegExp.test(node.value);
1869
+ break;
1870
+ case 'url':
1871
+ //An IRI, as defined by [RFC3987] (the IRI token, defined in RFC 3987 section 2.2). UAs could,
1872
+ // for example, offer the user URIs from his bookmarks. (See below for notes on IDN.) The value
1873
+ // is called url (as opposed to iri or uri) for consistency with CSS syntax and because it is
1874
+ // generally felt authors are more familiar with the term "URL" than the other, more technically
1875
+ // correct terms.
1876
+ //http://www.ietf.org/rfc/rfc3987
1877
+ node.validity.typeMismatch = !$wf2.urlRegExp.test(node.value);
1878
+ break;
1879
+ }
1880
+ }
1881
+
1882
+ if(!node.validity.patternMismatch && !node.validity.typeMismatch){
1883
+ //To limit the range of values allowed by some of the above types, two new attributes are introduced, which
1884
+ // apply to the date-related, time-related, numeric, and file upload types: min and max
1885
+
1886
+ //rangeUnderflow -- The numeric, date, or time value of a control with a min attribute is lower than
1887
+ // the minimum, or a file upload control has fewer files selected than the minimum. If the control
1888
+ // is empty or if the typeMismatch flag is set, this flag must not be set.
1889
+ //rangeOverflow -- The numeric, date, or time value of a control with a max attribute is higher than
1890
+ // the maximum, or a file upload control has more files selected than the maximum. If the control
1891
+ // is empty or if the typeMismatch flag is set, this flag must not be set.
1892
+ if(doCheckRange){
1893
+ if(isNumberRelated){
1894
+ //For numeric types (number and range) the value must exactly match the number type (numberRegExp)
1895
+ if(type == 'range'){
1896
+ //For this type...max defaults to 100
1897
+ node.max = (maxAttrNode && $wf2.numberRegExp.test(maxAttrNode.value)) ? Number(maxAttrNode.value) : 100;
1898
+ //node.min is set at the beginning of this function so that the min value can be set as the default value
1899
+ }
1900
+ else {
1901
+ if(minAttrNode && $wf2.numberRegExp.test(minAttrNode.value))
1902
+ node.min = Number(minAttrNode.value);
1903
+ if(maxAttrNode && $wf2.numberRegExp.test(maxAttrNode.value))
1904
+ node.max = Number(maxAttrNode.value);
1905
+ }
1906
+ node.validity.rangeUnderflow = (node.min != undefined && Number(node.value) < node.min);
1907
+ node.validity.rangeOverflow = (node.max != undefined && Number(node.value) > node.max);
1908
+ }
1909
+ //For file types it must be a sequence of digits 0-9, treated as a base ten integer.
1910
+ else if(type == 'file'){
1911
+ if(minAttrNode && /^\d+$/.test(minAttrNode.value))
1912
+ node.min = Number(minAttrNode.value);
1913
+ //If absent, or if the minimum value is not in exactly the expected format, there
1914
+ // is no minimum restriction, except for the ... file types, where the default is zero.
1915
+ else node.min = 0;
1916
+ if(maxAttrNode && /^\d+$/.test(maxAttrNode.value))
1917
+ node.max = Number(maxAttrNode.value);
1918
+ //If absent, or if the maximum value is not in exactly the expected format, there is no
1919
+ // maximum restriction (beyond those intrinsic to the type), except for ... the file
1920
+ // type, where the default is 1.
1921
+ else node.max = 1;
1922
+
1923
+ //node.validity.rangeUnderflow = (node.min != undefined && Number(node.value) < node.min);
1924
+ //node.validity.rangeOverflow = (node.max != undefined && Number(node.value) > node.max);
1925
+ }
1926
+ //Date related
1927
+ else {
1928
+ //For date and time types it must match the relevant format mentioned for that type, all fields
1929
+ // having the right number of digits, with the right separating punctuation.
1930
+ if(minAttrNode){
1931
+ node.min = $wf2.parseISO8601(minAttrNode.value, type);
1932
+ node.validity.rangeUnderflow = (node.min && node.wf2Value < node.min);
1933
+ }
1934
+ if(maxAttrNode){
1935
+ node.max = $wf2.parseISO8601(maxAttrNode.value, type);
1936
+ node.validity.rangeOverflow = (node.max && node.wf2Value > node.max);
1937
+ }
1938
+ }
1939
+ }
1940
+ //The step attribute controls the precision allowed for the date-related, time-related, and numeric types.
1941
+ if(doCheckPrecision && !node.validity.rangeUnderflow && !node.validity.rangeOverflow){
1942
+ //stepMismatch -- The value is not one of the values allowed by the step attribute, and the UA will
1943
+ // not be rounding the value for submission. Empty values and values that caused the typeMismatch
1944
+ // flag to be set must not cause this flag to be set.
1945
+
1946
+ var stepAttrNode = $wf2.getOriginalAttrNode(node, 'step'); //node.getAttributeNode('step');
1947
+ if(!stepAttrNode){
1948
+ //The step attribute [for types datetime, datetime-local, and time] ... defaulting to 60 (one minute).
1949
+ //For time controls, the value of the step attribute is in seconds, although it may be a fractional
1950
+ // number as well to allow fractional times. The default value of the step
1951
+ // attribute for datetime, datetime-local and time controls is 60 (one minute).
1952
+ //The step [for type date] attribute specifies the precision in days, defaulting to 1.
1953
+ //The step [for type month] attribute specifies the precision in months, defaulting to 1.
1954
+ //The step [for type week] attribute specifies the precision in weeks, defaulting to 1.
1955
+ //For date controls, the value of the step attribute is in days, weeks, or months, for the date,
1956
+ // week, and month types respectively. The format is a non-negative integer; one or more digits
1957
+ // 0-9 interpreted as base ten. If the step is zero, it is interpreted as the default. The default
1958
+ // for the step attribute for these control types is 1.
1959
+ //The step [for types number and range] attribute specifies the precision, defaulting to 1.
1960
+ node.step = isTimeRelated ? 60 : 1;
1961
+ }
1962
+ //The literal value 'any' may be used as the value of the step attribute. This keyword indicates that
1963
+ // any value may be used (within the bounds of other restrictions placed on the control).
1964
+ else if(stepAttrNode.value == 'any')
1965
+ node.step = 'any'; //isStepAny = true;
1966
+ //The format of the step attribute is the number format described above, except that
1967
+ // the value must be greater than zero.
1968
+ else if($wf2.numberRegExp.test(stepAttrNode.value) && stepAttrNode.value > 0)
1969
+ node.step = Number(stepAttrNode.value);
1970
+ else
1971
+ node.step = isTimeRelated ? 60 : 1;
1972
+
1973
+ if(node.step != 'any'){
1974
+ node.wf2StepDatum = null;
1975
+ if(minAttrNode)
1976
+ node.wf2StepDatum = node.min;
1977
+ else if(maxAttrNode)
1978
+ node.wf2StepDatum = node.max;
1979
+ else
1980
+ node.wf2StepDatum = $wf2.zeroPoint[type] ? $wf2.zeroPoint[type] : 0;
1981
+
1982
+ //The zero point for datetime controls is 1970-01-01T00:00:00.0Z, for datetime-local is
1983
+ // 1970-01-01T00:00:00.0, for date controls is 1970-01-01, for month controls is 1970-01,
1984
+ // for week controls is 1970-W01 (the week starting 1969-12-29 and containing 1970-01-01),
1985
+ // and for time controls is 00:00.
1986
+ var _step = node.step;
1987
+ if(type == 'month' && node.wf2StepDatum && node.wf2StepDatum.getUTCFullYear){
1988
+ var month1 = node.wf2StepDatum.getUTCFullYear()*12 + node.wf2StepDatum.getUTCMonth();
1989
+ var month2 = node.wf2Value.getUTCFullYear()*12 + node.wf2Value.getUTCMonth();
1990
+ node.validity.stepMismatch = (month2 - month1)%_step != 0;
1991
+ }
1992
+ else {
1993
+ switch(type){
1994
+ case 'datetime':
1995
+ case 'datetime-local':
1996
+ case 'time':
1997
+ _step = parseInt(_step * 1000); //for millisecond comparisons
1998
+ break;
1999
+ case 'date':
2000
+ _step = parseInt(_step * 24*60*60*1000);
2001
+ break;
2002
+ case 'week':
2003
+ _step = parseInt(_step * 7*24*60*60*1000);
2004
+ break;
2005
+ }
2006
+
2007
+ //For the control to be valid, the control's value must be an integral number of steps from the min value,
2008
+ // or, if there is no min attribute, the max value, or if there is neither attribute, from the zero point.
2009
+ //allow decimal places to the 1,000th place
2010
+ node.validity.stepMismatch = (Math.round((node.wf2Value - node.wf2StepDatum)*1000) % Math.round(_step*1000)) != 0;
2011
+ }
2012
+ }
2013
+ }
2014
+ }
2015
+
2016
+ //[TEXTAREA] tooLong -- The value of a control with a maxlength attribute is longer than the attribute allows,
2017
+ // and the value of the control doesn't exactly match the control's default value.
2018
+ //[The maxlength] attribute must not affect the initial value (the DOM defaultValue attribute). It must only
2019
+ // affect what the user may enter and whether a validity error is flagged during validation.
2020
+ if(doMaxLengthCheck && node.maxLength >= 0 && node.value != node.defaultValue){
2021
+ //A newline in a textarea's value must count as two code points for maxlength processing (because
2022
+ // newlines in textareas are submitted as U+000D U+000A). [[NOT IMPLEMENTED: This includes the
2023
+ // implied newlines that are added for submission when the wrap attribute has the value hard.]]
2024
+ //var matches = node.value.match(/((?<!\x0D|^)\x0A|\x0D(?!^\x0A|$))/g); //no negative lookbehind
2025
+ var shortNewlines = 0;
2026
+ var v = node.value;
2027
+ node.wf2ValueLength = v.length;
2028
+ for(var i = 1; i < v.length; i++){
2029
+ if(v[i] === "\x0A" && v[i-1] !== "\x0D" || v[i] == "\x0D" && (v[i+1] && v[i+1] !== "\x0A"))
2030
+ node.wf2ValueLength++;
2031
+ }
2032
+
2033
+ //The tooLong flag is used when this attribute is specified on a ... textarea control and the control
2034
+ // has more than the specified number of code points and the value doesn't match the control's default value.
2035
+ node.validity.tooLong = node.wf2ValueLength > node.maxLength;
2036
+ }
2037
+ }
2038
+
2039
+ //customError -- The control was marked invalid from script. See the definition of the setCustomValiditiy() method.
2040
+
2041
+ node.validity.valid = !$wf2.hasInvalidState(node.validity);
2042
+
2043
+ //This is now done onmousedown or onkeydown, just as Opera does
2044
+ //if(node.validity.valid){
2045
+ // node.className = node.className.replace(/\s*\binvalid\b\s*/g, " "); //substitute for :invalid pseudo class
2046
+ // //if(node.wf2_errorMsg){
2047
+ // // node.wf2_errorMsg.parentNode.removeChild(node.wf2_errorMsg);
2048
+ // // node.wf2_errorMsg = null;
2049
+ // //}
2050
+ // var errMsg = document.getElementById((node.id || node.name) + '_wf2_errorMsg');
2051
+ // if(errMsg)
2052
+ // errMsg.parentNode.removeChild(errMsg);
2053
+ //}
2054
+ },
2055
+
2056
+ applyValidityInterface : function(node){
2057
+
2058
+ /* Webkit browsers need this */
2059
+ if ($wf2.hasBadImplementation) {
2060
+
2061
+ if (node.type == 'submit' || node.type == 'button') {
2062
+
2063
+ node.formNoValidate=true;
2064
+
2065
+ }
2066
+ }
2067
+
2068
+ /* ZOLTAN made a change here to ensure Google's unfinished native implementation is not used. */
2069
+ else if((node.validity && node.validity.typeMismatch !== undefined)) {//MSIE needs the second test for some reason
2070
+ // console.log('bad implementation!! ' + node.id);
2071
+
2072
+ return node;
2073
+ }
2074
+ node.validationMessage = "";
2075
+
2076
+ //ValidityState interface
2077
+ node.validity = $wf2.createValidityState();
2078
+ node.willValidate = true;
2079
+
2080
+ var nodeName = node.nodeName.toLowerCase();
2081
+ if(nodeName == 'button' || nodeName == 'fieldset'){
2082
+ node.setCustomValidity = function(error){
2083
+ throw $wf2.DOMException(9); //NOT_SUPPORTED_ERR
2084
+ };
2085
+ node.checkValidity = function(){
2086
+ return true;
2087
+ };
2088
+ return node;
2089
+ }
2090
+ //node._updateValidityState = $wf2._updateValidityState;
2091
+
2092
+ if (!node.setCustomValidity) {
2093
+ node.setCustomValidity = $wf2.controlSetCustomValidity;
2094
+ }
2095
+
2096
+
2097
+ node.checkValidity = $wf2.controlCheckValidity;
2098
+
2099
+
2100
+ //var type = (node.type ? node.type.toLowerCase() : (nodeName == 'input' ? 'text' : ''));
2101
+ var type = (node.getAttribute('type') ? node.getAttribute('type').toLowerCase() : node.type);
2102
+
2103
+ if(/(hidden|button|reset|add|remove|move-up|move-down)/.test(type) || !node.name || node.disabled)
2104
+ node.willValidate = false;
2105
+ else if(window.RepetitionElement) {
2106
+ var parent = node;
2107
+ while(parent = parent.parentNode){
2108
+ if(parent.repetitionType == RepetitionElement.REPETITION_TEMPLATE){
2109
+ node.willValidate = false;
2110
+ break;
2111
+ }
2112
+ }
2113
+ }
2114
+
2115
+ //var handler = function(event){
2116
+ // return (event.currentTarget || event.srcElement)._updateValidityState();
2117
+ //};
2118
+
2119
+ ////attempt to check validity live
2120
+ //if(document.addEventListener){
2121
+ // node.addEventListener('change', handler, false);
2122
+ // node.addEventListener('blur', handler, false);
2123
+ // node.addEventListener('keyup', handler, false);
2124
+ //}
2125
+ //else if(window.attachEvent){
2126
+ // node.attachEvent('onchange', handler);
2127
+ // node.attachEvent('onblur', handler);
2128
+ // node.attachEvent('onkeyup', handler);
2129
+ //}
2130
+ //else {
2131
+ //
2132
+ //}
2133
+
2134
+ return node;
2135
+ },
2136
+
2137
+ onsubmitValidityHandler : function(event){
2138
+ var frm = event.currentTarget || event.srcElement;
2139
+ var r;
2140
+
2141
+ // call routines other libraries have set to be run before
2142
+ // validation.
2143
+ for (var i=0; i<$wf2.callBeforeValidation.length; i++) {
2144
+ $wf2.callBeforeValidation[i](event);
2145
+ }
2146
+
2147
+ /* ZOLTAN */
2148
+ if(!frm.checkValidity()){
2149
+ if(event.preventDefault)
2150
+ event.preventDefault();
2151
+ event.returnValue = false;
2152
+ r = false;
2153
+ } else {
2154
+ event.returnValue = true;
2155
+ r = true;
2156
+ }
2157
+
2158
+ // call routines other libraries have set to be run before
2159
+ // validation.
2160
+ for (var i=0; i<$wf2.callAfterValidation.length; i++) {
2161
+ $wf2.callAfterValidation[i](event, r);
2162
+ }
2163
+
2164
+
2165
+
2166
+ return r;
2167
+ },
2168
+
2169
+ controlSetCustomValidity : function(error){
2170
+ if(error){
2171
+ this.validationMessage = String(error);
2172
+ this.validity.customError = true;
2173
+ }
2174
+ else {
2175
+ this.validationMessage = "";
2176
+ this.validity.customError = false;
2177
+ }
2178
+ this.validity.valid = !$wf2.hasInvalidState(this.validity);
2179
+ },
2180
+ hasInvalidState : function(validity){
2181
+ return validity.typeMismatch
2182
+ || validity.rangeUnderflow
2183
+ || validity.rangeOverflow
2184
+ || validity.stepMismatch
2185
+ || validity.tooLong
2186
+ || validity.patternMismatch
2187
+ || validity.valueMissing
2188
+ || validity.customError;
2189
+ },
2190
+ createValidityState : function(){
2191
+ return {
2192
+ typeMismatch : false,
2193
+ rangeUnderflow : false,
2194
+ rangeOverflow : false,
2195
+ stepMismatch : false,
2196
+ tooLong : false,
2197
+ patternMismatch : false,
2198
+ valueMissing : false,
2199
+ customError : false,
2200
+ valid : true
2201
+ };
2202
+ },
2203
+
2204
+ //## Default action functions for invalid events ##################################################
2205
+
2206
+ invalidIndicators : [],
2207
+ indicatorTimeoutId : null,
2208
+ indicatorIntervalId : null,
2209
+
2210
+ stepUnits : {
2211
+ 'datetime' : 'second',
2212
+ 'datetime-local': 'second',
2213
+ 'time': 'second',
2214
+ 'date': 'day',
2215
+ 'week': 'week',
2216
+ 'month': 'month'
2217
+ },
2218
+
2219
+ invalidMessages : {
2220
+ valueMissing : 'A value must be supplied or selected.',
2221
+ typeMismatch : 'The value is invalid for %s type.',
2222
+ rangeUnderflow : 'The value must be equal to or greater than %s.',
2223
+ rangeOverflow : 'The value must be equal to or less than %s.',
2224
+ stepMismatch : 'The value has a step mismatch; it must be a certain number multiples of %s from %s.',
2225
+ tooLong : 'The value is too long. The field may have a maximum of %s characters but you supplied %s. Note that each line-break counts as two characters.',
2226
+ patternMismatch: 'The value is not in the format required.'
2227
+ },
2228
+
2229
+ valueToWF2Type : function(value, type){
2230
+ switch(String(type).toLowerCase()){
2231
+ case 'datetime':
2232
+ case 'datetime-local':
2233
+ case 'date':
2234
+ case 'month':
2235
+ case 'week':
2236
+ case 'time':
2237
+ return $wf2.dateToISO8601(value, type);
2238
+ default:
2239
+ return value;
2240
+ }
2241
+ },
2242
+
2243
+ addInvalidIndicator : function(target){
2244
+ //show contextual help message
2245
+ var msg = document.createElement('div');
2246
+ msg.className = 'wf2_errorMsg';
2247
+ //msg.title = "Close";
2248
+ msg.id = (target.id || target.name) + '_wf2_errorMsg'; //QUESTION: does this work for MSIE?
2249
+ msg.onmousedown = function(){
2250
+ this.parentNode.removeChild(this);
2251
+ };
2252
+ //var type = String(target.getAttribute('type')).toLowerCase();
2253
+ //var type = (target.type ? target.type.toLowerCase() : (target.nodeName.toLowerCase() == 'input' ? 'text' : ''));
2254
+ var type = (target.getAttribute('type') ? target.getAttribute('type').toLowerCase() : target.type);
2255
+ var isDateTimeRelated = (type == 'datetime' || type == 'datetime-local' || type == 'time' || type == 'date' || type == 'month' || type == 'week');
2256
+
2257
+ var ol = document.createElement('ol');
2258
+ if(target.validity.valueMissing)
2259
+ ol.appendChild($wf2.createLI($wf2.invalidMessages.valueMissing));
2260
+ if(target.validity.typeMismatch)
2261
+ ol.appendChild($wf2.createLI($wf2.invalidMessages.typeMismatch.replace(/%s/, type)));
2262
+ if(target.validity.rangeUnderflow)
2263
+ ol.appendChild($wf2.createLI($wf2.invalidMessages.rangeUnderflow.replace(/%s/, $wf2.valueToWF2Type(target.min, type))));
2264
+ if(target.validity.rangeOverflow)
2265
+ ol.appendChild($wf2.createLI($wf2.invalidMessages.rangeOverflow.replace(/%s/, $wf2.valueToWF2Type(target.max, type))));
2266
+ if(target.validity.stepMismatch)
2267
+ ol.appendChild($wf2.createLI($wf2.invalidMessages.stepMismatch.replace(/%s/, target.step + ($wf2.stepUnits[type] ? ' ' + $wf2.stepUnits[type] + '(s)' : '')).replace(/%s/, $wf2.valueToWF2Type(target.wf2StepDatum, type))));
2268
+ if(target.validity.tooLong)
2269
+ ol.appendChild($wf2.createLI($wf2.invalidMessages.tooLong.replace(/%s/, target.maxLength).replace(/%s/, target.wf2ValueLength ? target.wf2ValueLength : target.value.length)));
2270
+ if(target.validity.patternMismatch)
2271
+ ol.appendChild($wf2.createLI($wf2.invalidMessages.patternMismatch.replace(/%s/, target.title ? target.title : ' "' + target.getAttribute('pattern') + '"')));
2272
+ if(target.validity.customError)
2273
+ ol.appendChild($wf2.createLI(target.validationMessage));
2274
+
2275
+ if(ol.childNodes.length == 1)
2276
+ ol.className = 'single';
2277
+
2278
+ msg.appendChild(ol);
2279
+ ////remove existing error message
2280
+ //if(document.getElementById(msg.id))
2281
+ // document.documentElement.removeChild(document.getElementById(msg.id));
2282
+ //target.parentNode.insertBefore(msg, target); //Inserting error message next to element in question causes problems when the element has a positioned containing block
2283
+ var parent = document.body ? document.body : document.documentElement;
2284
+ if($wf2.invalidIndicators.length) //insert before other error messages so that it appears on top
2285
+ parent.insertBefore(msg, $wf2.invalidIndicators[$wf2.invalidIndicators.length-1].errorMsg);
2286
+ else //insert at the end of the document
2287
+ parent.insertBefore(msg, null);
2288
+ //target.wf2_errorMsg = msg;
2289
+ //if(target.style.display == 'none' || !target.offsetParent){
2290
+ // var prevEl = target.previousSibling;
2291
+ // var nextEl = target.nextSibling;
2292
+ // var prevCount = 0, nextCount = 0;
2293
+ // while(prevEl && (prevEl.nodeType != 1 || (prevEl.style.display == 'none' || !prevEl.offsetParent)) && ++prevCount)
2294
+ // prevEl = prevEl.previousSibling;
2295
+ // while(nextEl && (nextEl.nodeType != 1 || (nextEl.style.display == 'none' || !nextEl.offsetParent)) && ++nextCount)
2296
+ // nextEl = nextEl.nextSibling;
2297
+ //
2298
+ // if(prevEl && prevCount > nextCount)
2299
+ //
2300
+ //}
2301
+ var el = target;
2302
+ while(el && (el.nodeType != 1 || (el.style.display == 'none' || el.style.visibility == 'hidden' || !el.offsetParent)))
2303
+ el = el.parentNode;
2304
+
2305
+ var top = left = 0;
2306
+ var cur = el;
2307
+ if(cur && cur.offsetParent){
2308
+ left = cur.offsetLeft;
2309
+ top = cur.offsetTop;
2310
+ while(cur = cur.offsetParent){
2311
+ left += cur.offsetLeft;
2312
+ top += cur.offsetTop;
2313
+ }
2314
+ top += el.offsetHeight;
2315
+ }
2316
+ msg.style.top = top + 'px';
2317
+ msg.style.left = left + 'px';
2318
+
2319
+ $wf2.invalidIndicators.push({
2320
+ target : target,
2321
+ errorMsg : msg
2322
+ });
2323
+ //if(target.form && target.form[target.name]){
2324
+ // target.form[target.name].wf2HasInvalidIndicator = true;
2325
+ // console.info('set')
2326
+ //}
2327
+ if(!target.className.match(/\bwf2_invalid\b/))
2328
+ target.className += " wf2_invalid";
2329
+
2330
+ if($wf2.indicatorIntervalId == null){
2331
+ //var i = $wf2.invalidIndicators.length - 1;
2332
+ $wf2.indicatorIntervalId = setInterval(function(){
2333
+ var invalidIndicator;
2334
+ for(var i = 0; invalidIndicator = $wf2.invalidIndicators[i]; i++){
2335
+ if(!invalidIndicator.target.className.match(/\bwf2_invalid\b/)){
2336
+ invalidIndicator.target.className += " wf2_invalid";
2337
+ }
2338
+ else {
2339
+ invalidIndicator.target.className = invalidIndicator.target.className.replace(/\s?wf2_invalid/, "");
2340
+ }
2341
+ }
2342
+
2343
+ }, 500);
2344
+ // call routines other libraries have set to be run before
2345
+ // validation.
2346
+ setTimeout(function() {
2347
+ for (var i=0; i<$wf2.callAfterValidation.length; i++) {
2348
+ $wf2.callAfterValidation[i](null, false);
2349
+ }
2350
+ }, 1);
2351
+ $wf2.indicatorTimeoutId = setTimeout($wf2.clearInvalidIndicators, 4000);
2352
+ }
2353
+
2354
+ },
2355
+
2356
+ clearInvalidIndicators : function(){
2357
+ clearTimeout($wf2.indicatorTimeoutId);
2358
+ $wf2.indicatorTimeoutId = null;
2359
+ clearInterval($wf2.indicatorIntervalId);
2360
+ $wf2.indicatorIntervalId = null;
2361
+
2362
+ var invalidIndicator;
2363
+ while(invalidIndicator = $wf2.invalidIndicators[0]){
2364
+ if(invalidIndicator.errorMsg && invalidIndicator.errorMsg.parentNode)
2365
+ invalidIndicator.errorMsg.parentNode.removeChild(invalidIndicator.errorMsg);
2366
+ //clearInterval(insts[0].intervalId);
2367
+ var target = invalidIndicator.target;
2368
+ //if(target.form && target.form[target.name])
2369
+ // target.form[target.name].wf2HasInvalidIndicator = false;
2370
+ target.className = target.className.replace(/\s?wf2_invalid/, ""); //([^\b]\s)?
2371
+ $wf2.invalidIndicators.shift();
2372
+ }
2373
+
2374
+ },
2375
+
2376
+ /*##############################################################################################
2377
+ # Other helper functions (not made into methods)
2378
+ ##############################################################################################*/
2379
+
2380
+ cloneNode_customAttrs : { //FOR MSIE BUG: it cannot perceive the attributes that were actually specified
2381
+ 'type':1,'template':1,'repeat':1,'repeat-template':1,'repeat-min':1,
2382
+ 'repeat-max':1,'repeat-start':1,'value':1,'class':1,'required':1,
2383
+ 'pattern':1,'form':1,'autocomplete':1,'autofocus':1,'inputmode':1,
2384
+ 'max':1,'min':1,'step':1,
2385
+ onmoved:1,onadded:1,onremoved:1,
2386
+ onadd:1,onremove:1,onmove:1 //deprecated
2387
+ },
2388
+ cloneNode_skippedAttrs : {
2389
+ 'name':1, //due to MSIE bug, set via $wf2.createElement
2390
+ 'class':1, //due to MSIE bug, set below (see http://www.alistapart.com/articles/jslogging)
2391
+ 'for':1, //due to preceived MSIE bug, set below
2392
+ 'style':1, //inline styles require special handling
2393
+ 'checked':1, //set by $wf2.createElement due to MSIE bug creating INPUT@type=radio
2394
+
2395
+ //for MSIE, properties (or methods) == attributes
2396
+ addRepetitionBlock:1,addRepetitionBlockByIndex:1,moveRepetitionBlock:1,
2397
+ removeRepetitionBlock:1, repetitionBlocks:1,
2398
+ setCustomValidity:1,checkValidity:1,validity:1,validationMessage:1,willValidate:1,
2399
+ wf2StepDatum:1,wf2Value:1,wf2Initialized:1,wf2ValueLength:1
2400
+ },
2401
+ cloneNode_rtEventHandlerAttrs : {
2402
+ onmoved:1,onadded:1,onremoved:1, //don't copy Repetition old model event attributes not methods
2403
+ onadd:1,onremove:1,onmove:1 //deprecated
2404
+ //QUESTION: is this right???
2405
+ },
2406
+
2407
+ //The following cloneNode algorithm was designed to handle the attribute processing that the repetition
2408
+ // model specifies. Gecko starts to have irratic behavior with a cloned input's value attribute and value
2409
+ // property when using DOM cloneNode; furthermore, various MSIE bugs prevent its use of DOM cloneNode
2410
+ cloneNode : function (node, processAttr, rtNestedDepth){
2411
+ if(!rtNestedDepth)
2412
+ rtNestedDepth = 0;
2413
+ var clone, i, attr;
2414
+ switch(node.nodeType){
2415
+ case 1: /*Node.ELEMENT_NODE*/
2416
+ //if(node.nodeType == 1 /*Node.ELEMENT_NODE*/){
2417
+ var isTemplate = node.getAttribute('repeat') == 'template';
2418
+ if(isTemplate)
2419
+ rtNestedDepth++;
2420
+ //BROWSER BUGS: MSIE does not allow the setting of the node.name, except when creating the new node
2421
+ // MSIE neither permits the standard DOM creation of radio buttons
2422
+ var attrs = [];
2423
+ if(node.name)
2424
+ attrs.name = processAttr ? processAttr(node.name) : node.name;
2425
+ if(node.type == 'radio')
2426
+ attrs.type = node.type;
2427
+ if(node.checked)
2428
+ attrs.checked = 'checked';
2429
+ clone = $wf2.createElement(node.nodeName, attrs);
2430
+ //clone = node.name ?
2431
+ // $wf2.createElement(node.nodeName, attrs)
2432
+ // : document.createElement(node.nodeName);
2433
+
2434
+ for(i = 0; attr = node.attributes[i]; i++){
2435
+
2436
+ //MSIE ISSUE: Custom attributes specified do not have .specified property set to true?
2437
+ //ISSUE: VALUE IS REPEATED IN MSIE WHEN VALUE ATTRIBUTE SET?
2438
+ //if(attr.specified || node.getAttribute(attr.nodeName)) //$wf2.cloneNode_customAttrs[attr.nodeName] ||
2439
+ // if(window.console && console.info) console.info(node.nodeName + "@" + attr.nodeName + " -- " + attr.specified + " <font color=red>" + node.getAttribute(attr.nodeName) + "</font>(" + typeof node.getAttribute(attr.nodeName) + ")<br>");
2440
+
2441
+ //MSIE needs $wf2.cloneNode_customAttrs[attr.name] test since attr.specified does not work with custom attributes
2442
+ //If the node is a template, the repetition event handlers should only be copied
2443
+ // if the template is nested and is being cloned by a parent repetition template.
2444
+ if((attr.specified || $wf2.cloneNode_customAttrs[attr.name])
2445
+ && !$wf2.cloneNode_skippedAttrs[attr.name] && (
2446
+ (!isTemplate || (rtNestedDepth > 1 || !$wf2.cloneNode_rtEventHandlerAttrs[attr.name])) // &&
2447
+ ))
2448
+ {
2449
+
2450
+ //MSIE BUG: when button[type=add|remove|move-up|move-down], then (attr.nodeValue and attr.value == 'button') but node.getAttribute(attr.nodeName) == 'add|remove|move-up|move-down' (as desired)
2451
+
2452
+ //clone and process an event handler property (attribute);
2453
+ // keep event handler attributes as plain text if nested repetition template
2454
+ if(rtNestedDepth < 2 && (attr.name.indexOf('on') === 0) && (typeof node[attr.name] == 'function')){
2455
+ var funcBody = processAttr(node[attr.name].toString().match(/{((?:.|\n)+)}/)[1]);
2456
+ funcBody = processAttr(funcBody);
2457
+ clone[attr.name] = new Function('event', funcBody);
2458
+ }
2459
+ //clone and process other attributes
2460
+ else {
2461
+ var attrValue = node.getAttribute(attr.name);
2462
+ attrValue = (processAttr ? processAttr(attrValue) : attrValue);
2463
+ clone.setAttribute(attr.name, attrValue);
2464
+ }
2465
+ //console.log(StringHelpers.sprintf("AFTER attr: %s val: %s (%s)", attr.name, attr.value, processAttr(attrValue)))
2466
+ }
2467
+ }
2468
+ //MSIE BUG: setAttribute('class') creates duplicate value attribute in MSIE;
2469
+ //QUESTION: will setting className on this clonedNode still cause this error later on for users? will addClassName croak? Should it be improved?
2470
+ //see: http://www.alistapart.com/articles/jslogging
2471
+ if(node.className){
2472
+ var _className = (processAttr ? processAttr(node.className) : node.className);
2473
+ if(clone.getAttributeNode('class')){
2474
+ for(i = 0; i < clone.attributes.length; i++) {
2475
+ if(clone.attributes[i].name == 'class')
2476
+ clone.attributes[i].value = _className;
2477
+ }
2478
+ }
2479
+ else clone.setAttribute('class', _className);
2480
+ }
2481
+
2482
+ //Restore the template's elements to the originally coded disabled state (indicated by 'disabled' class name)
2483
+ // All elements within the repetition template are disabled to prevent them from being successful.
2484
+ if(!/\bdisabled\b/.test(node.className))
2485
+ clone.disabled = false;
2486
+
2487
+ //Process the inline style
2488
+ if(node.style && node.style.cssText){
2489
+ //clone.setAttribute('style', processAttr(node.style.cssText));
2490
+ clone.style.cssText = (processAttr ? processAttr(node.style.cssText) : node.style.cssText);
2491
+ }
2492
+
2493
+ //label's 'for' attribute, set here due to MSIE bug
2494
+ if(node.nodeName && node.nodeName.toLowerCase() == 'label' && node.htmlFor)
2495
+ clone.htmlFor = (processAttr ? processAttr(node.htmlFor) : node.htmlFor);
2496
+
2497
+
2498
+ if(clone.nodeName.toLowerCase() == 'option'){ //MSIE clone element bug requires this
2499
+ clone.selected = node.selected;
2500
+ clone.defaultSelected = node.defaultSelected;
2501
+ }
2502
+
2503
+ for(i = 0; i < node.childNodes.length; i++){
2504
+ clone.appendChild($wf2.cloneNode(node.childNodes[i], processAttr, rtNestedDepth));
2505
+ }
2506
+ break;
2507
+ //MSIE BUG: The following three cases are for MSIE because when cloning nodes from XML
2508
+ // files loaded via SELECT@data attribute, MSIE fails when performing appendChild.
2509
+ case 3: /*Node.TEXT_NODE*/
2510
+ case 4: /*Node.CDATA_SECTION_NODE*/
2511
+ clone = document.createTextNode(node.data);
2512
+ break;
2513
+ case 8: /*Node.COMMENT_NODE*/
2514
+ clone = document.createComment(node.data);
2515
+ break;
2516
+ default:
2517
+ clone = node.cloneNode(true)
2518
+ }
2519
+ //else clone = node.cloneNode(true);
2520
+ return clone;
2521
+ },
2522
+
2523
+ getFormElements : function(){
2524
+ var elements = [];
2525
+ var allElements = $wf2.getElementsByTagNames.apply(this, ['input','output','select','textarea','button']); //fieldset
2526
+ for(var i = 0; i < allElements.length; i++){
2527
+ var node = allElements[i].parentNode;
2528
+ while(node && node.nodeType == 1 && node.getAttribute('repeat') != 'template')
2529
+ node = node.parentNode;
2530
+ if(!node || node.nodeType != 1)
2531
+ elements.push(allElements[i]);
2532
+ }
2533
+ return elements;
2534
+ },
2535
+
2536
+ loadDataURI : function(el){
2537
+ var uri = el.data || el.getAttribute('data');
2538
+ if(!uri)
2539
+ return null;
2540
+ var doc = null, matches;
2541
+ try {
2542
+ if(matches = uri.match(/^data:[^,]*xml[^,]*,((?:.|\n)+)/)){
2543
+ var xml = decodeURI(matches[1].replace(/%3D/ig, '=').replace(/%3A/ig, ':').replace(/%2F/ig, '/'));
2544
+ if(window.DOMParser){
2545
+ var parser = new DOMParser();
2546
+ doc = parser.parseFromString(xml, 'text/xml');
2547
+ }
2548
+ else if(window.ActiveXObject){
2549
+ doc = new ActiveXObject("Microsoft.XMLDOM");
2550
+ doc.async = 'false';
2551
+ doc.loadXML(xml);
2552
+ }
2553
+ }
2554
+ else {
2555
+ $wf2.xhr.open('GET', uri, false);
2556
+ $wf2.xhr.send(null); //Note: if in Firefox and null not provided, and if Firebug is disabled, an error occurs
2557
+ doc = $wf2.xhr.responseXML;
2558
+ }
2559
+ }
2560
+ catch(e){
2561
+ return null;
2562
+ }
2563
+ return doc;
2564
+ },
2565
+
2566
+ getAttributeByName: function (obj, attrName) {
2567
+ var i;
2568
+
2569
+ var attributes = obj.attributes;
2570
+ for (i=0; i<attributes.length; i++) {
2571
+ var attr = attributes[i]
2572
+ if (attr.nodeName == attrName && attr.specified) {
2573
+ return attr;
2574
+ }
2575
+ }
2576
+ return null;
2577
+ },
2578
+
2579
+ getAttributeValue: function (obj, attrName) {
2580
+ var attr = $wf2.getAttributeByName(obj, attrName);
2581
+
2582
+ if (attr != null) {
2583
+ return attr.nodeValue;
2584
+ } else {
2585
+ return null;
2586
+ }
2587
+ },
2588
+
2589
+ setAttributeValue: function (obj, attrName, attrValue) {
2590
+ var attr = $wf2.getAttributeByName(obj, attrName);
2591
+
2592
+ if (attr != null) {
2593
+ attr.nodeValue = attrValue;
2594
+ } else {
2595
+ return;
2596
+ }
2597
+ },
2598
+
2599
+
2600
+ getDatasetItem: function (obj, name) {
2601
+ var r = $wf2.getAttributeValue(obj, 'data-' + name);
2602
+
2603
+ if (!r) {
2604
+ r = $wf2.getAttributeValue(obj, 'data-' + name.toLowerCase())
2605
+ }
2606
+
2607
+ if (!r) {
2608
+ r = obj['data-' + name.toLowerCase()]
2609
+ }
2610
+ return r;
2611
+ },
2612
+
2613
+ setDatasetItem: function (obj, name, value) {
2614
+ var attrName = 'data-' + name.toLowerCase();
2615
+
2616
+ var val = $wf2.setAttributeValue(obj, attrName, value);
2617
+
2618
+ if ($wf2.getAttributeValue(obj, attrName) == null) {
2619
+ obj[attrName] = value;
2620
+
2621
+ }
2622
+ },
2623
+
2624
+ getElementsByTagNames : function(/* ... */){
2625
+ var els,i,results = [];
2626
+ if(document.evaluate){
2627
+ var _tagNames = [];
2628
+ for(i = 0; i < arguments.length; i++)
2629
+ _tagNames.push(".//" + arguments[i]);
2630
+ els = document.evaluate(_tagNames.join('|'), this, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
2631
+ for(i = 0; i < els.snapshotLength; i++)
2632
+ results.push(els.snapshotItem(i));
2633
+ }
2634
+ else {
2635
+ for(i = 0; i < arguments.length; i++){
2636
+ els = this.getElementsByTagName(arguments[i]);
2637
+ for(var j = 0; j < els.length; j++){
2638
+ results.push(els[j]);
2639
+ }
2640
+ }
2641
+ if($wf2.sortNodes)
2642
+ results.sort($wf2.sortNodes);
2643
+ }
2644
+ return results;
2645
+ },
2646
+
2647
+ getElementsByTagNamesAndAttribute : function(elNames, attrName, attrValue, isNotEqual){
2648
+ var els,el,i,j,results = [];
2649
+
2650
+ //QUESTION!!! Can we exclude all nodes that are not decendents of the repetition template?
2651
+ if(document.evaluate){
2652
+ var attrExpr = '';
2653
+ if(attrName)
2654
+ attrExpr = '[@' + attrName + (attrValue ? (isNotEqual ? '!=' : '=') + '"' + attrValue + '"' : "") + "]";
2655
+ var xPaths = [];
2656
+ for(i = 0; i < elNames.length; i++)
2657
+ xPaths.push('.//' + elNames[i] + attrExpr);
2658
+ els = document.evaluate(xPaths.join('|'), this, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
2659
+ for(i = 0; i < els.snapshotLength; i++)
2660
+ results.push(els.snapshotItem(i));
2661
+ }
2662
+ else {
2663
+ for(i = 0; i < elNames.length; i++){
2664
+ els = this.getElementsByTagName(elNames[i]);
2665
+ for(j = 0; el = els[j]; j++){
2666
+ var thisAttrNode = el.getAttributeNode(attrName);
2667
+ var thisAttrValue = el.getAttribute(attrName); //MSIE needs getAttribute here for custom button types to be read
2668
+ if(!attrName || (thisAttrNode && (attrValue === undefined || (isNotEqual ? thisAttrValue != attrValue : thisAttrValue == attrValue) ))){
2669
+ results.push(el);
2670
+ }
2671
+ }
2672
+ }
2673
+ if($wf2.sortNodes)
2674
+ results.sort($wf2.sortNodes);
2675
+ }
2676
+ return results;
2677
+ },
2678
+
2679
+ arrayHasItem : function(arr, item){
2680
+ for(var i = 0; i < arr.length; i++){
2681
+ if(arr[i] == item)
2682
+ return true;
2683
+ }
2684
+ return false;
2685
+ },
2686
+
2687
+ //Note: this function has been deemed harmful
2688
+ getElementStyle : function(el, property) { //adapted from Danny Goodman <http://www.oreillynet.com/pub/a/javascript/excerpt/JSDHTMLCkbk_chap5/index5.html>
2689
+ if(el.currentStyle)
2690
+ return el.currentStyle[property];
2691
+ else if(window.getComputedStyle)
2692
+ return getComputedStyle(el, '').getPropertyValue(property);
2693
+ else if(el.style)
2694
+ return el.style[property];
2695
+ else return '';
2696
+ },
2697
+
2698
+ //createElement code based on Anthony Lieuallen's work <http://www.easy-reader.net/archives/2005/09/02/death-to-bad-dom-implementations/#comment-444>
2699
+ // The following function enables MSIE to create elements with the name attribute set, per MSDN:
2700
+ // The NAME attribute cannot be set at run time on elements dynamically created with the
2701
+ // createElement method. To create an element with a name attribute, include the attribute
2702
+ // and value when using the createElement method.
2703
+ // The same goes for creating radio buttons and creating defaultly checked checkboxes,
2704
+ // per <http://channel9.msdn.com/wiki/default.aspx/Channel9.InternetExplorerProgrammingBugs>
2705
+ createElement : (function(){
2706
+ try {
2707
+ var el = document.createElement('<div name="foo">'); //MSIE memory leak according to Drip
2708
+ if(el.tagName.toLowerCase() != 'div' || el.name != 'foo')
2709
+ throw 'create element error';
2710
+
2711
+ return function(tag, attrs){
2712
+ var html = '<' + tag;
2713
+ for(var name in attrs)
2714
+ html += ' ' + name + '="' + attrs[name] + '"';
2715
+ html += '>';
2716
+ if(tag.toLowerCase() != 'input')
2717
+ html += '</'+tag+'>';
2718
+ return document.createElement(html); //'<'+tag+' name="'+name+'"></'+tag+'>'
2719
+ };
2720
+ }
2721
+ catch(err){
2722
+ return function(tag, attrs){
2723
+ var el = document.createElement(tag);
2724
+ for(var name in attrs)
2725
+ el.setAttribute(name, attrs[name]);
2726
+ return el;
2727
+ };
2728
+ }
2729
+ })(),
2730
+
2731
+ //Sort elements in document order (from ppk)
2732
+ sortNodes : (function(){
2733
+ var n = document.documentElement.firstChild;
2734
+ if(n.sourceIndex){
2735
+ return function (a,b){
2736
+ return a.sourceIndex - b.sourceIndex;
2737
+ };
2738
+ }
2739
+ else if(n.compareDocumentPosition){
2740
+ return function (a,b){
2741
+ return 3 - (a.compareDocumentPosition(b) & 6);
2742
+ };
2743
+ }
2744
+ })(),
2745
+
2746
+ //Shortcut to create new list items; used by default invalid event handler in listing the errors
2747
+ createLI : function(text){
2748
+ var li = document.createElement('li');
2749
+ li.appendChild(document.createTextNode(text));
2750
+ return li;
2751
+ },
2752
+
2753
+ //Initially inspired by Paul Sowden <http://delete.me.uk/2005/03/iso8601.html>
2754
+ ISO8601RegExp : /^(?:(\d\d\d\d)-(W(0[1-9]|[1-4]\d|5[0-2])|(0\d|1[0-2])(-(0\d|[1-2]\d|3[0-1])(T(0\d|1\d|2[0-4]):([0-5]\d)(:([0-5]\d)(\.(\d+))?)?(Z)?)?)?)|(0\d|1\d|2[0-4]):([0-5]\d)(:([0-5]\d)(\.(\d+))?)?)$/,
2755
+ parseISO8601 : function (str, type) {
2756
+ var d = $wf2.validateDateTimeType(str, type);
2757
+ if(!d)
2758
+ return null;
2759
+
2760
+ var date = new Date(0);
2761
+ var _timePos = 8;
2762
+
2763
+ if(d[15]){ //Time
2764
+ if(type && type != 'time') // a time date
2765
+ return null;
2766
+ _timePos = 15;
2767
+ }
2768
+ else {
2769
+ date.setUTCFullYear(d[1]);
2770
+
2771
+ //ISO8601 Week
2772
+ if(d[3]){
2773
+ if(type && type != 'week')
2774
+ return null;
2775
+ date.setUTCDate(date.getUTCDate() + ((8 - date.getUTCDay()) % 7) + (d[3]-1)*7); //set week day and week
2776
+ return date;
2777
+ }
2778
+ //Other date-related types
2779
+ else {
2780
+ date.setUTCMonth(d[4] - 1); //Month must be supplied for WF2
2781
+ if(d[6])
2782
+ date.setUTCDate(d[6]);
2783
+ }
2784
+ }
2785
+
2786
+ //Set time-related fields
2787
+ if(d[_timePos+0]) date.setUTCHours(d[_timePos+0]);
2788
+ if(d[_timePos+1]) date.setUTCMinutes(d[_timePos+1]);
2789
+ if(d[_timePos+2]) date.setUTCSeconds(d[_timePos+3]);
2790
+ if(d[_timePos+4]) date.setUTCMilliseconds(Math.round(Number(d[_timePos+4]) * 1000));
2791
+
2792
+ //Set to local time if date given, hours present and no 'Z' provided
2793
+ if(d[4] && d[_timePos+0] && !d[_timePos+6])
2794
+ date.setUTCMinutes(date.getUTCMinutes()+date.getTimezoneOffset());
2795
+
2796
+ return date;
2797
+ },
2798
+
2799
+ validateDateTimeType : function(value, type){ //returns RegExp matches
2800
+ var isValid = false;
2801
+ var d = $wf2.ISO8601RegExp.exec(value); //var d = string.match(new RegExp(regexp));
2802
+ if(!d || !type)
2803
+ return d;
2804
+ type = type.toLowerCase();
2805
+
2806
+ if(type == 'week') // a week date
2807
+ isValid = (d[2].toString().indexOf('W') === 0); //valid if W present
2808
+ else if(type == 'time') // a time date
2809
+ isValid = !!d[15];
2810
+ else if(type == 'month')
2811
+ isValid = !d[5];
2812
+ else { //a date related value
2813
+ //Verify that the number of days in the month are valid
2814
+ if(d[6]){
2815
+ var date = new Date(d[1], d[4]-1, d[6]);
2816
+ if(date.getMonth() != d[4]-1)
2817
+ isValid = false;
2818
+ else switch(type){
2819
+ case 'date':
2820
+ isValid = (d[4] && !d[7]); //valid if day of month supplied and time field not present
2821
+ break;
2822
+ case 'datetime':
2823
+ isValid = !!d[14]; //valid if Z present
2824
+ break;
2825
+ case 'datetime-local':
2826
+ isValid = (d[7] && !d[14]); //valid if time present and Z not provided
2827
+ break;
2828
+ }
2829
+ }
2830
+ }
2831
+ return isValid ? d : null;
2832
+ },
2833
+
2834
+ zeroPad : function(num, pad){
2835
+ if(!pad)
2836
+ pad = 2;
2837
+ var str = num.toString();
2838
+ while(str.length < pad)
2839
+ str = '0' + str;
2840
+ return str;
2841
+ },
2842
+
2843
+ dateToISO8601 : function(date, type){
2844
+ type = String(type).toLowerCase();
2845
+ var ms = '';
2846
+ if(date.getUTCMilliseconds())
2847
+ ms = '.' + $wf2.zeroPad(date.getUTCMilliseconds(), 3).replace(/0+$/,'');
2848
+ switch(type){
2849
+ case 'date':
2850
+ return date.getUTCFullYear() + '-' + $wf2.zeroPad(date.getUTCMonth()+1) + '-' + $wf2.zeroPad(date.getUTCDate());
2851
+ case 'datetime-local':
2852
+ return date.getFullYear() + '-' + $wf2.zeroPad(date.getMonth()+1) + '-' + $wf2.zeroPad(date.getDate()) +
2853
+ 'T' + $wf2.zeroPad(date.getHours()) + ':' + $wf2.zeroPad(date.getMinutes()) + ':' + $wf2.zeroPad(date.getMinutes()) + ms + 'Z';
2854
+ case 'month':
2855
+ return date.getUTCFullYear() + '-' + $wf2.zeroPad(date.getUTCMonth()+1);
2856
+ case 'week':
2857
+ var week1 = $wf2.parseISO8601(date.getUTCFullYear() + '-W01');
2858
+ return date.getUTCFullYear() + '-W' + $wf2.zeroPad(((date.valueOf() - week1.valueOf()) / (7*24*60*60*1000)) + 1);
2859
+ case 'time':
2860
+ return $wf2.zeroPad(date.getUTCHours()) + ':' + $wf2.zeroPad(date.getUTCMinutes()) + ':' + $wf2.zeroPad(date.getUTCMinutes()) + ms;
2861
+ case 'datetime':
2862
+ default:
2863
+ return date.getUTCFullYear() + '-' + $wf2.zeroPad(date.getUTCMonth()+1) + '-' + $wf2.zeroPad(date.getUTCDate()) +
2864
+ 'T' + $wf2.zeroPad(date.getUTCHours()) + ':' + $wf2.zeroPad(date.getUTCMinutes()) + ':' + $wf2.zeroPad(date.getUTCMinutes()) + ms + 'Z';
2865
+ }
2866
+ },
2867
+
2868
+ /*
2869
+ * Fires an event manually.
2870
+ * @author Scott Andrew - http://www.scottandrew.com/weblog/articles/cbs-events
2871
+ * @author John Resig - http://ejohn.org/projects/flexible-javascript-events/
2872
+ * @param {Object} obj - a javascript object.
2873
+ * @param {String} evType - an event attached to the object.
2874
+ * @param {Function} fn - the function that is called when the event fires.
2875
+ *
2876
+ */
2877
+ fireEvent: function (element, event, options){
2878
+
2879
+ if(!element) {
2880
+ return;
2881
+ }
2882
+
2883
+ if (document.createEventObject){
2884
+ return element.fireEvent('on' + event, $wf2.globalEvent);
2885
+ } else{
2886
+ // dispatch for firefox + others
2887
+ $wf2.globalEvent.initEvent(event, true, true); // event type,bubbling,cancelable
2888
+ return !element.dispatchEvent($wf2.globalEvent);
2889
+ }
2890
+ },
2891
+
2892
+ //Emulation of DOMException
2893
+ DOMException : function(code){
2894
+ var message = 'DOMException: ';
2895
+ switch(code){
2896
+ case 1: message += 'INDEX_SIZE_ERR'; break;
2897
+ case 9: message += 'NOT_SUPPORTED_ERR'; break;
2898
+ case 11: message += 'INVALID_STATE_ERR'; break;
2899
+ case 12: message += 'SYNTAX_ERR'; break;
2900
+ case 13: message += 'INVALID_MODIFICATION_ERR'; break;
2901
+ }
2902
+
2903
+ var err = new Error(message);
2904
+ err.code = code;
2905
+ err.name = 'DOMException';
2906
+
2907
+ //Provide error codes and messages for the exception types that are raised by WF2
2908
+ err.INDEX_SIZE_ERR = 1;
2909
+ err.NOT_SUPPORTED_ERR = 9;
2910
+ err.INVALID_STATE_ERR = 11;
2911
+ err.SYNTAX_ERR = 12;
2912
+ err.INVALID_MODIFICATION_ERR = 13;
2913
+
2914
+ //with($wf2.DOMException.prototype){
2915
+ // INDEX_SIZE_ERR = 1;
2916
+ // DOMSTRING_SIZE_ERR = 2;
2917
+ // HIERARCHY_REQUEST_ERR = 3;
2918
+ // WRONG_DOCUMENT_ERR = 4;
2919
+ // INVALID_CHARACTER_ERR = 5;
2920
+ // NO_DATA_ALLOWED_ERR = 6;
2921
+ // NO_MODIFICATION_ALLOWED_ERR = 7;
2922
+ // NOT_FOUND_ERR = 8;
2923
+ // NOT_SUPPORTED_ERR = 9;
2924
+ // INUSE_ATTRIBUTE_ERR = 10;
2925
+ // INVALID_STATE_ERR = 11;
2926
+ // SYNTAX_ERR = 12;
2927
+ // INVALID_MODIFICATION_ERR = 13;
2928
+ // NAMESPACE_ERR = 14;
2929
+ // INVALID_ACCESS_ERR = 15;
2930
+ //};
2931
+
2932
+ return err;
2933
+ }
2934
+ }; //End $wf2 = {
2935
+
2936
+
2937
+ /*##############################################################################################
2938
+ # Section: Repetition Model Definitions
2939
+ ##############################################################################################*/
2940
+
2941
+ var RepetitionElement = {
2942
+ REPETITION_NONE:0,
2943
+ REPETITION_TEMPLATE:1,
2944
+ REPETITION_BLOCK:2
2945
+ };
2946
+
2947
+ var RepetitionEvent = {
2948
+ //the following takes a UIEvent and adds the required properties for a RepetitionEvent
2949
+ _upgradeEvent : function(){
2950
+ this.initRepetitionEvent = RepetitionEvent.initRepetitionEvent;
2951
+ this.initRepetitionEventNS = RepetitionEvent.initRepetitionEventNS;
2952
+ },
2953
+ initRepetitionEvent : function(typeArg, canBubbleArg, cancelableArg, elementArg){
2954
+ if(this.initEvent)
2955
+ this.initEvent(typeArg, canBubbleArg, cancelableArg);
2956
+ else { //manually initialize event (i.e., for MSIE)
2957
+ this.type = typeArg;
2958
+ // switch(typeArg.toLowerCase()){
2959
+ // case 'added':
2960
+ // this.type = 'add';
2961
+ // break;
2962
+ // case 'removed':
2963
+ // this.type = 'remove';
2964
+ // break;
2965
+ // case 'moved':
2966
+ // this.type = 'move';
2967
+ // break;
2968
+ // }
2969
+ //this.srcElement = elementArg.repetitionTemplate;
2970
+ //this.cancelBubble = false;
2971
+ //this.cancelable = cancelableArg;
2972
+ //this.returnValue = false;
2973
+
2974
+ if(!this.preventDefault)
2975
+ this.preventDefault = function(){
2976
+ this.returnValue = false;
2977
+ };
2978
+ if(!this.stopPropagation)
2979
+ this.stopPropagation = function(){
2980
+ this.cancelBubble = true;
2981
+ };
2982
+ }
2983
+ this.element = elementArg;
2984
+ this.relatedNode = elementArg; //for Opera (deprecated?)
2985
+ },
2986
+ initRepetitionEventNS : function(namespaceURIArg, typeArg, canBubbleArg, cancelableArg, elementArg){
2987
+ throw Error("NOT IMPLEMENTED: RepetitionEvent.initRepetitionEventNS");
2988
+ //this.initEvent(namespaceURIArg, typeArg, canBubbleArg, cancelableArg);
2989
+ //this.element = elementArg;
2990
+ //this.relatedNode = elementArg; //for Opera (deprecated?)
2991
+ }
2992
+ };
2993
+
2994
+ /*##############################################################################################
2995
+ # Change the prototypes of HTML elements
2996
+ ##############################################################################################*/
2997
+
2998
+ //RepetitionElement interface must be implemented by all elements.
2999
+ if(window.Element && Element.prototype){
3000
+ Element.prototype.REPETITION_NONE = RepetitionElement.REPETITION_NONE;
3001
+ Element.prototype.REPETITION_TEMPLATE = RepetitionElement.REPETITION_TEMPLATE;
3002
+ Element.prototype.REPETITION_BLOCK = RepetitionElement.REPETITION_BLOCK;
3003
+
3004
+ Element.prototype.repetitionType = RepetitionElement.REPETITION_NONE;
3005
+ Element.prototype.repetitionIndex = 0;
3006
+ Element.prototype.repetitionTemplate = null; /*readonly*/
3007
+ Element.prototype.repetitionBlocks = null; /*readonly*/
3008
+
3009
+ Element.prototype.repeatStart = 1;
3010
+ Element.prototype.repeatMin = 0;
3011
+ Element.prototype.repeatMax = Number.MAX_VALUE; //Infinity;
3012
+
3013
+ Element.prototype.addRepetitionBlock = $wf2.addRepetitionBlock;
3014
+ Element.prototype.addRepetitionBlockByIndex = $wf2.addRepetitionBlockByIndex;
3015
+ Element.prototype.moveRepetitionBlock = $wf2.moveRepetitionBlock;
3016
+ Element.prototype.removeRepetitionBlock = $wf2.removeRepetitionBlock;
3017
+ }
3018
+
3019
+ /*##############################################################################################
3020
+ # Set mutation event handlers to automatically add WF2 behaviors
3021
+ ##############################################################################################*/
3022
+
3023
+ //When a form control is inserted into a document, the UA must check to see if it has [the autofocus]
3024
+ // attribute set. If it does, and the control is not disabled, and it is of a type normally
3025
+ // focusable in the user's operating environment, then the UA should focus the control, as if
3026
+ // the control's focus() method was invoked. UAs with a viewport should also scroll the document
3027
+ // enough to make the control visible, even if it is not of a type normally focusable.
3028
+ //REVISE: there should be one handler for all attr events on the page.
3029
+ if(document.addEventListener){
3030
+ document.addEventListener('DOMNodeInsertedIntoDocument', function(evt){ //DOMNodeInserted? DOMNodeInsertedIntoDocument
3031
+ if(evt.target.nodeType == 1 && evt.target.hasAttribute('autofocus')){
3032
+ $wf2.initAutofocusElement(evt.target);
3033
+ }
3034
+ //[[UAs may ignore this attribute if the user has indicated (for example, by starting to type in a
3035
+ // form control) that he does not wish focus to be changed.]]
3036
+ }, false);
3037
+
3038
+ //NOT CURRENTLY IMPLEMENTABLE:
3039
+ // Setting the DOM attribute to true must set the content attribute to the value autofocus.
3040
+ // Setting the DOM attribute to false must remove the content attribute.
3041
+
3042
+ document.addEventListener('DOMAttrModified', function(evt){
3043
+ //The autofocus DOM attribute must return true when the content attribute is present (regardless
3044
+ // of its value, even if it is the empty string), and false when it is absent.
3045
+ if(evt.attrName == 'autofocus'){
3046
+ if(evt.attrChange == evt.ADDITION)
3047
+ //evt.relatedNode.autofocus = true;
3048
+ $wf2.initAutofocusElement(evt.target);
3049
+ else if(evt.attrChange == evt.REMOVAL)
3050
+ evt.target.autofocus = false;
3051
+ }
3052
+ }, false);
3053
+ }
3054
+
3055
+ /*##################################################################################
3056
+ # Execute WF2 code onDOMContentLoaded
3057
+ # Some of the following code was borrowed from Dean Edwards, John Resig, et al <http://dean.edwards.name/weblog/2006/06/again/>
3058
+ ##################################################################################*/
3059
+
3060
+ (function(){
3061
+ //Get the path to the library base directory
3062
+ var match;
3063
+ //For some reason, if not using documentElement, scriptaculous fails to load if reference to
3064
+ // webforms2 script placed beforehand in Firefox
3065
+ var scripts = document.documentElement.getElementsByTagName('script');
3066
+ for(var i = 0; i < scripts.length; i++){
3067
+ if(match = scripts[i].src.match(/^(.*)webforms2[^\/]+$/))
3068
+ $wf2.libpath = match[1];
3069
+ }
3070
+
3071
+ //The script has been included after the DOM has loaded (perhaps via Greasemonkey), so fire immediately
3072
+ //NOTE: This does not work with XHTML documents in Gecko
3073
+ if(document.body){
3074
+ $wf2.onDOMContentLoaded();
3075
+ return;
3076
+ }
3077
+
3078
+ var eventSet = 0;
3079
+ if(document.addEventListener){
3080
+ //for Gecko and Opera
3081
+ document.addEventListener('DOMContentLoaded', function(){
3082
+ $wf2.onDOMContentLoaded();
3083
+ }, false);
3084
+
3085
+ //for other browsers which do not support DOMContentLoaded use the following as a fallback to be called hopefully before all other onload handlers
3086
+ window.addEventListener('load', function(){
3087
+ $wf2.onDOMContentLoaded();
3088
+ }, false);
3089
+
3090
+ eventSet = 1;
3091
+ }
3092
+
3093
+ //for Safari
3094
+ if (/WebKit/i.test(navigator.userAgent)) { //sniff
3095
+ var _timer = setInterval(function() {
3096
+ if (/loaded|complete/.test(document.readyState)) {
3097
+ clearInterval(_timer);
3098
+ delete _timer;
3099
+ $wf2.onDOMContentLoaded();
3100
+ }
3101
+ }, 10);
3102
+ eventSet = 1;
3103
+ }
3104
+ //for Internet Explorer (formerly using conditional comments) //sniff
3105
+ else if(/MSIE/i.test(navigator.userAgent) && !document.addEventListener && window.attachEvent){
3106
+ //This following attached onload handler will attempt to be the first onload handler to be called and thus
3107
+ // initiate the repetition model as early as possible if the DOMContentLoaded substitute fails.
3108
+ window.attachEvent('onload', function(){
3109
+ $wf2.onDOMContentLoaded();
3110
+ });
3111
+
3112
+ //Dean Edward's first solution: http://dean.edwards.name/weblog/2005/09/busted/
3113
+ //document.getElementsByTagName('*')[0].addBehavior(dirname + 'repetition-model.htc'); //use this if Behaviors are employed in 0.9
3114
+ document.write("<script defer src='" + $wf2.libpath + "webforms2-msie.js'><"+"/script>");
3115
+
3116
+ //Dean Edward's revisited solution <http://dean.edwards.name/weblog/2005/09/busted/> (via Matthias Miller with insights from jQuery)
3117
+ // Note that this solution will not result in its code firing before onload if there are no external images in the page; in this case, first solution above is used.
3118
+ document.write("<scr" + "ipt id='__wf2_ie_onload' defer src='//:'><\/script>"); //MSIE memory leak according to Drip
3119
+ var script = document.getElementById('__wf2_ie_onload');
3120
+ script.onreadystatechange = function(){
3121
+ if(this.readyState == 'complete'){
3122
+ this.parentNode.removeChild(this);
3123
+ $wf2.onDOMContentLoaded();
3124
+
3125
+ //See issue #3 <http://code.google.com/p/repetitionmodel/issues/detail?id=3>
3126
+ //Sometimes cssQuery doesn't find all repetition templates from here within this DOMContentLoaded substitute
3127
+ if($wf2.repetitionTemplates.length == 0)
3128
+ $wf2.isInitialized = false;
3129
+ }
3130
+ };
3131
+ script = null;
3132
+ eventSet = 1;
3133
+ }
3134
+
3135
+ //old event model used as a last-resort fallback
3136
+ if(!eventSet){
3137
+ if(window.onload){ //if(window.onload != RepetitionElement._init_document)
3138
+ var oldonload = window.onload;
3139
+ window.onload = function(){
3140
+ $wf2.onDOMContentLoaded();
3141
+ oldonload();
3142
+ };
3143
+ }
3144
+ else window.onload = function(){
3145
+ $wf2.onDOMContentLoaded();
3146
+ };
3147
+ }
3148
+ })();
3149
+ } //End If(!document.implementation...
3150
+
3151
+ /*##############################################################################################
3152
+ # Extensions for existing Web Forms 2.0 Implementations
3153
+ ##############################################################################################*/
3154
+ else if(document.addEventListener && ($wf2.oldRepetitionEventModelEnabled === undefined || $wf2.oldRepetitionEventModelEnabled)){
3155
+ $wf2.oldRepetitionEventModelEnabled = true;
3156
+ (function(){
3157
+
3158
+ var deprecatedAttrs = {
3159
+ added : 'onadd',
3160
+ removed : 'onremove',
3161
+ moved : 'onmove'
3162
+ };
3163
+
3164
+ function handleRepetitionEvent(evt){
3165
+ if(!$wf2.oldRepetitionEventModelEnabled)
3166
+ return;
3167
+ if(!evt.element && evt.relatedNode) //Opera uses evt.relatedNode instead of evt.element as the specification dictates
3168
+ evt.element = evt.relatedNode;
3169
+ if(!evt.element || !evt.element.repetitionTemplate)
3170
+ return;
3171
+
3172
+ var rt = evt.element.repetitionTemplate;
3173
+ var attrName = 'on' + evt.type;
3174
+
3175
+ //Add support for event handler set with HTML attribute
3176
+ var handlerAttr = rt.getAttribute(attrName) || /* deprecated */ rt.getAttribute(deprecatedAttrs[evt.type]);
3177
+ if(handlerAttr && (!rt[attrName] || typeof rt[attrName] != 'function')) //in MSIE, attribute == property
3178
+ rt[attrName] = new Function('event', handlerAttr);
3179
+
3180
+ if(evt.element.repetitionTemplate[attrName])
3181
+ evt.element.repetitionTemplate[attrName](evt);
3182
+ else if(evt.element.repetitionTemplate[deprecatedAttrs[evt.type]]) //deprecated
3183
+ evt.element.repetitionTemplate[deprecatedAttrs[evt.type]](evt);
3184
+ }
3185
+
3186
+ document.addEventListener('added', handleRepetitionEvent, false);
3187
+ document.addEventListener('removed', handleRepetitionEvent, false);
3188
+ document.addEventListener('moved', handleRepetitionEvent, false);
3189
+
3190
+ })();
3191
+ }
3192
+
3193
+ } //end if(!window.$wf2)
3194
+
3195
+