html5forms-rails 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +5 -0
- data/Gemfile +11 -0
- data/README.md +208 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/demos/html5-form-demo.html +79 -0
- data/html5forms-rails.gemspec +142 -0
- data/lib/html5forms.rb +6 -0
- data/vendor/assets/images/colorpicker/blank.gif +0 -0
- data/vendor/assets/images/colorpicker/colorpicker_background.png +0 -0
- data/vendor/assets/images/colorpicker/colorpicker_hex.png +0 -0
- data/vendor/assets/images/colorpicker/colorpicker_hsb_b.png +0 -0
- data/vendor/assets/images/colorpicker/colorpicker_hsb_h.png +0 -0
- data/vendor/assets/images/colorpicker/colorpicker_hsb_s.png +0 -0
- data/vendor/assets/images/colorpicker/colorpicker_indic.gif +0 -0
- data/vendor/assets/images/colorpicker/colorpicker_overlay.png +0 -0
- data/vendor/assets/images/colorpicker/colorpicker_rgb_b.png +0 -0
- data/vendor/assets/images/colorpicker/colorpicker_rgb_g.png +0 -0
- data/vendor/assets/images/colorpicker/colorpicker_rgb_r.png +0 -0
- data/vendor/assets/images/colorpicker/colorpicker_select.gif +0 -0
- data/vendor/assets/images/colorpicker/colorpicker_submit.png +0 -0
- data/vendor/assets/images/colorpicker/custom_background.png +0 -0
- data/vendor/assets/images/colorpicker/custom_hex.png +0 -0
- data/vendor/assets/images/colorpicker/custom_hsb_b.png +0 -0
- data/vendor/assets/images/colorpicker/custom_hsb_h.png +0 -0
- data/vendor/assets/images/colorpicker/custom_hsb_s.png +0 -0
- data/vendor/assets/images/colorpicker/custom_indic.gif +0 -0
- data/vendor/assets/images/colorpicker/custom_rgb_b.png +0 -0
- data/vendor/assets/images/colorpicker/custom_rgb_g.png +0 -0
- data/vendor/assets/images/colorpicker/custom_rgb_r.png +0 -0
- data/vendor/assets/images/colorpicker/custom_submit.png +0 -0
- data/vendor/assets/images/colorpicker/select.png +0 -0
- data/vendor/assets/images/colorpicker/select2.png +0 -0
- data/vendor/assets/images/colorpicker/slider.png +0 -0
- data/vendor/assets/images/h5f/form_validation.png +0 -0
- data/vendor/assets/images/html5form-shim/asterisk.png +0 -0
- data/vendor/assets/images/html5form-shim/down.png +0 -0
- data/vendor/assets/images/html5form-shim/fail.png +0 -0
- data/vendor/assets/images/html5form-shim/ok.png +0 -0
- data/vendor/assets/images/html5forms/jscolor/arrow.gif +0 -0
- data/vendor/assets/images/html5forms/jscolor/cross.gif +0 -0
- data/vendor/assets/images/html5forms/jscolor/hs.png +0 -0
- data/vendor/assets/images/html5forms/jscolor/hv.png +0 -0
- data/vendor/assets/images/html5forms/slider/slider-1.png +0 -0
- data/vendor/assets/images/html5forms/slider/slider-disabled-1.png +0 -0
- data/vendor/assets/images/html5forms/slider/slider-disabled.png +0 -0
- data/vendor/assets/images/html5forms/slider/slider.png +0 -0
- data/vendor/assets/javascripts/colorpicker.js +484 -0
- data/vendor/assets/javascripts/colorpicker.min.js +9 -0
- data/vendor/assets/javascripts/h5f.js +328 -0
- data/vendor/assets/javascripts/h5f.min.js +4 -0
- data/vendor/assets/javascripts/html5forms/EventHelpers.min.js +15 -0
- data/vendor/assets/javascripts/html5forms/autocomplete.min.js +1 -0
- data/vendor/assets/javascripts/html5forms/cssQuery-p.min.js +6 -0
- data/vendor/assets/javascripts/html5forms/dev/EventHelpers.js +486 -0
- data/vendor/assets/javascripts/html5forms/dev/autocomplete.js +387 -0
- data/vendor/assets/javascripts/html5forms/dev/cssQuery-p.js +6 -0
- data/vendor/assets/javascripts/html5forms/dev/html5.js +121 -0
- data/vendor/assets/javascripts/html5forms/dev/html5Forms.js +892 -0
- data/vendor/assets/javascripts/html5forms/dev/html5Widgets.js +1417 -0
- data/vendor/assets/javascripts/html5forms/dev/jscolor.js +840 -0
- data/vendor/assets/javascripts/html5forms/dev/slider.js +797 -0
- data/vendor/assets/javascripts/html5forms/dev/timer.js +137 -0
- data/vendor/assets/javascripts/html5forms/dev/visibleIf.js +1100 -0
- data/vendor/assets/javascripts/html5forms/html5.min.js +2 -0
- data/vendor/assets/javascripts/html5forms/html5Forms.min.js +1 -0
- data/vendor/assets/javascripts/html5forms/html5Widgets.min.js +20 -0
- data/vendor/assets/javascripts/html5forms/jscolor.min.js +10 -0
- data/vendor/assets/javascripts/html5forms/slider.min.js +25 -0
- data/vendor/assets/javascripts/html5forms/timer.min.js +1 -0
- data/vendor/assets/javascripts/html5forms/visibleIf.min.js +19 -0
- data/vendor/assets/javascripts/html5forms.fallback.js +115 -0
- data/vendor/assets/javascripts/html5forms.fallback.min.js +11 -0
- data/vendor/assets/javascripts/jquery.html5form-shim.js +402 -0
- data/vendor/assets/javascripts/jquery.html5form.min.js +4 -0
- data/vendor/assets/javascripts/jquery.placehold.min.js +7 -0
- data/vendor/assets/javascripts/ui.spinner.js +649 -0
- data/vendor/assets/javascripts/ui.spinner.min.js +7 -0
- data/vendor/assets/javascripts/webforms2/webforms2-msie.js +1 -0
- data/vendor/assets/javascripts/webforms2/webforms2-p.js +14 -0
- data/vendor/assets/javascripts/webforms2/webforms2.js +14 -0
- data/vendor/assets/javascripts/webforms2/webforms2_src.js +3195 -0
- data/vendor/assets/stylesheets/colorpicker.css +161 -0
- data/vendor/assets/stylesheets/h5f.css +86 -0
- data/vendor/assets/stylesheets/html5form-shim.css +109 -0
- data/vendor/assets/stylesheets/html5forms/number.css +35 -0
- data/vendor/assets/stylesheets/html5forms/slider.css +169 -0
- data/vendor/assets/stylesheets/html5forms/slider_ie.css +41 -0
- data/vendor/assets/stylesheets/html5forms/visibleIf.css +23 -0
- data/vendor/assets/stylesheets/html5forms.layout.css +116 -0
- data/vendor/assets/stylesheets/ui.spinner.css +3 -0
- data/vendor/assets/stylesheets/webforms2.css +42 -0
- 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
|
+
|