html5forms-rails 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+