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