1 /* |
1 /* |
2 * Auto-completing page/username fields |
2 * Auto-completing page/username fields |
3 * NOTE: A more efficient version of the username field is used for Mozilla browsers. The updated code is in autofill.js. |
3 * NOTE: A more efficient version of the username field is used for Mozilla browsers. The updated code is in autofill.js. |
4 */ |
4 */ |
5 |
5 |
6 // The ultimate Javascript app: AJAX auto-completion, which responds to up/down arrow keys, the enter key, and the escape key |
6 // |
7 // The idea was pilfered mercilessly from vBulletin, but uses about 8 |
7 // **** 1.1.4: DEPRECATED **** |
8 // bytes of vB code. All the rest was coded by me, Mr. Javascript Newbie... |
8 // Replaced with Spry-based mechanism. |
9 |
9 // |
10 // ...in about 8 hours. |
|
11 // You folks better like this stuff. |
|
12 |
|
13 function nameCompleteEventHandler(e) |
|
14 { |
|
15 if(!e) e = window.event; |
|
16 switch(e.keyCode) |
|
17 { |
|
18 case 38: // up |
|
19 unSelectMove('up'); |
|
20 break; |
|
21 case 40: // down |
|
22 unSelectMove('down'); |
|
23 break; |
|
24 case 27: // escape |
|
25 case 9: // tab |
|
26 destroyUsernameDropdowns(); |
|
27 break; |
|
28 case 13: // enter |
|
29 unSelect(); |
|
30 break; |
|
31 default: return false; break; |
|
32 } |
|
33 return true; |
|
34 } |
|
35 |
|
36 function unSelectMove(dir) |
|
37 { |
|
38 if(submitAuthorized) return false; |
|
39 var thediv = document.getElementById(unObjDivCurrentId); |
|
40 thetable = thediv.firstChild; |
|
41 cel = thetable.firstChild.firstChild; |
|
42 d = true; |
|
43 index = false; |
|
44 changed = false; |
|
45 // Object of the game: extract the username, determine its index in the userlist array, and then color the menu items and set unObjCurrentSelection |
|
46 while(d) // Set to false if an exception occurs or if we arrive at our destination |
|
47 { |
|
48 //* |
|
49 if(!cel) d=false; |
|
50 celbak = cel; |
|
51 cel = cel.nextSibling; |
|
52 if(!cel) d=false; |
|
53 try { |
|
54 if(cel.firstChild.nextSibling) html = cel.firstChild.nextSibling.innerHTML; |
|
55 else html = cel.firstChild.innerHTML; |
|
56 cel.firstChild.className = 'row1'; |
|
57 if(cel.firstChild.nextSibling) cel.firstChild.nextSibling.className = 'row1'; |
|
58 thename = html.substr(7, html.length-15); |
|
59 // FINALLY! we have extracted the username |
|
60 // Now get its position in the userlist array |
|
61 if(thename == unObjCurrentSelection) |
|
62 { |
|
63 index = parseInt(in_array(thename, userlist)); |
|
64 } |
|
65 if(typeof(index) == 'number') |
|
66 { |
|
67 if(dir=='down') |
|
68 n = index+1; |
|
69 else if(dir == 'up') |
|
70 n = index - 1; |
|
71 |
|
72 // Try to trap moving the selection up or down beyond the top of bottom |
|
73 if(n > userlist.length-1 || n < 0) |
|
74 { |
|
75 cel.firstChild.className = 'row2'; |
|
76 if(cel.firstChild.nextSibling) cel.firstChild.nextSibling.className = 'row2'; |
|
77 return; |
|
78 } |
|
79 |
|
80 if(dir=='down') no=cel.nextSibling; |
|
81 else if(dir=='up') no=cel.previousSibling; |
|
82 no.firstChild.className = 'row2'; |
|
83 if(no.firstChild.nextSibling) no.firstChild.nextSibling.className = 'row2'; |
|
84 if(no.firstChild.id) |
|
85 { |
|
86 scroll = getScrollOffset() + getHeight(); |
|
87 elemht = getElementHeight(no.firstChild.id); |
|
88 elemoff = fetch_offset(no.firstChild); |
|
89 whereto = elemoff['top'] + elemht; |
|
90 if(whereto > scroll) |
|
91 { |
|
92 window.location.hash = '#'+no.firstChild.id; |
|
93 unObj.focus(); |
|
94 } |
|
95 } |
|
96 cel=cel.nextSibling; |
|
97 unObjCurrentSelection = userlist[n]; |
|
98 index = false; |
|
99 changed = true; |
|
100 return; |
|
101 } |
|
102 } catch(e) { } |
|
103 //*/ d = false; |
|
104 } |
|
105 } |
|
106 |
|
107 function unSelect() |
|
108 { |
|
109 if(!unObj || submitAuthorized) return false; |
|
110 if ( unObjCurrentSelection ) |
|
111 unObj.value = unObjCurrentSelection; |
|
112 destroyUsernameDropdowns(); |
|
113 } |
|
114 |
|
115 function in_array(needle, haystack) |
|
116 { |
|
117 for(var i in haystack) |
|
118 { |
|
119 if(haystack[i] == needle) return i; |
|
120 } |
|
121 return false; |
|
122 } |
|
123 |
|
124 function ajaxUserNameComplete(o) |
|
125 { |
|
126 if(!o) {destroyUsernameDropdowns(); return;} |
|
127 if(!o.value) {destroyUsernameDropdowns(); return;} |
|
128 if(o.value.length < 3) {destroyUsernameDropdowns(); return;} |
|
129 //if(IE) return; // This control doesn't work in IE. Yes, it's true! document.createElement doesn't work. |
|
130 if(!o.id) |
|
131 { |
|
132 o.id = 'usernametextboxobj_' + Math.floor(Math.random() * 10000000); |
|
133 } |
|
134 unObj = o; |
|
135 o.setAttribute("autocomplete","off"); |
|
136 o.onkeyup = function(e, o) { o=unObj; if(!nameCompleteEventHandler(e)) ajaxUserNameComplete(o); } |
|
137 val = escape(o.value).replace('+', '%2B'); |
|
138 ajaxGet(stdAjaxPrefix+'&_mode=fillusername&name='+val, function() |
|
139 { |
|
140 if ( ajax.readyState == 4 && ajax.status == 200 ) |
|
141 { |
|
142 // Determine the appropriate left/top positions, then create a div to use for the drop-down list |
|
143 // The trick here is to be able to make the div dynamically destroy itself depending on how far the user's mouse is from it |
|
144 destroyUsernameDropdowns(); |
|
145 off = fetch_offset(unObj); |
|
146 dim = fetch_dimensions(unObj); |
|
147 left = off['left']; |
|
148 i1 = off['top']; |
|
149 i2 = dim['h']; |
|
150 var top = 0; |
|
151 top = i1 + i2; |
|
152 var thediv = document.createElement('div'); |
|
153 thediv.className = 'tblholder'; |
|
154 thediv.style.marginTop = '0px'; |
|
155 thediv.style.position = 'absolute'; |
|
156 thediv.style.top = top + 'px'; |
|
157 thediv.style.left = left + 'px'; |
|
158 thediv.style.zIndex = getHighestZ() + 2; |
|
159 id = 'usernamehoverobj_' + Math.floor(Math.random() * 10000000); |
|
160 unObjDivCurrentId = id; |
|
161 thediv.id = id; |
|
162 unObj.onblur = function() { destroyUsernameDropdowns(); } |
|
163 |
|
164 var response = String(ajax.responseText) + ' '; |
|
165 if ( response.substr(0,1) != '{' ) |
|
166 { |
|
167 new MessageBox(MB_OK|MB_ICONSTOP, 'Invalid response', 'Invalid or unexpected JSON response from server:<pre>' + ajax.responseText + '</pre>'); |
|
168 return false; |
|
169 } |
|
170 |
|
171 response = parseJSON(response); |
|
172 var errorstring = false; |
|
173 if ( response.mode == 'error' ) |
|
174 { |
|
175 errorstring = response.error; |
|
176 } |
|
177 else |
|
178 { |
|
179 var userlist = response.users_real; |
|
180 } |
|
181 |
|
182 if(errorstring) |
|
183 { |
|
184 html = '<span style="color: #555; padding: 4px;">'+errorstring+'</span>'; |
|
185 } |
|
186 else |
|
187 { |
|
188 html = '<table border="0" cellspacing="1" cellpadding="3" style="width: auto;"><tr><th><small>' + $lang.get('user_autofill_heading_suggestions') + '</small></th></tr>'; |
|
189 cls = 'row2'; |
|
190 unObjCurrentSelection = userlist[0]; |
|
191 for(i=0;i<userlist.length;i++) |
|
192 { |
|
193 tmpnam = 'listobjnode_'+Math.floor(Math.random() * 10000000); |
|
194 html = html + '<tr><td id="'+tmpnam+'" class="'+cls+'" style="cursor: pointer;" onclick="document.getElementById(\''+unObj.id+'\').value=\''+userlist[i]+'\';destroyUsernameDropdowns();"><small>'+userlist[i]+'</small></td></tr>'; |
|
195 if(cls=='row2') cls='row1'; |
|
196 } |
|
197 html = html + '</table>'; |
|
198 } |
|
199 |
|
200 thediv.innerHTML = html; |
|
201 var body = document.getElementsByTagName('body'); |
|
202 body = body[0]; |
|
203 unSelectMenuOn = true; |
|
204 submitAuthorized = false; |
|
205 body.appendChild(thediv); |
|
206 } |
|
207 }); |
|
208 } |
|
209 |
|
210 function ajaxPageNameComplete(o) |
|
211 { |
|
212 if(!o) {destroyUsernameDropdowns(); return;} |
|
213 if(!o.value) {destroyUsernameDropdowns(); return;} |
|
214 if(o.value.length < 3) {destroyUsernameDropdowns(); return;} |
|
215 if(IE) return; // This control doesn't work in IE. Yes, it's true! document.createElement doesn't work. |
|
216 if(!o.id) |
|
217 { |
|
218 o.id = 'usernametextboxobj_' + Math.floor(Math.random() * 10000000); |
|
219 } |
|
220 o.setAttribute("autocomplete","off"); |
|
221 unObj = o; |
|
222 o.onkeyup = function(e, o) { o=unObj; if(!nameCompleteEventHandler(e)) ajaxPageNameComplete(o); } |
|
223 val = escape(o.value).replace('+', '%2B'); |
|
224 ajaxGet(stdAjaxPrefix+'&_mode=fillpagename&name='+val, function() |
|
225 { |
|
226 if(!ajax) return; |
|
227 if ( ajax.readyState == 4 && ajax.status == 200 ) |
|
228 { |
|
229 // Determine the appropriate left/top positions, then create a div to use for the drop-down list |
|
230 // The trick here is to be able to make the div dynamically destroy itself depending on how far the user's mouse is from it |
|
231 destroyUsernameDropdowns(); |
|
232 off = fetch_offset(unObj); |
|
233 dim = fetch_dimensions(unObj); |
|
234 left = off['left']; |
|
235 top = off['top'] + dim['h']; |
|
236 var thediv = document.createElement('div'); |
|
237 thediv.className = 'tblholder'; |
|
238 thediv.style.marginTop = '0px'; |
|
239 thediv.style.position = 'absolute'; |
|
240 thediv.style.top = top + 'px'; |
|
241 thediv.style.left = left + 'px'; |
|
242 thediv.style.zIndex = getHighestZ() + 2; |
|
243 id = 'usernamehoverobj_' + Math.floor(Math.random() * 10000000); |
|
244 unObjDivCurrentId = id; |
|
245 thediv.id = id; |
|
246 |
|
247 eval(ajax.responseText); |
|
248 if(errorstring) |
|
249 { |
|
250 html = '<span style="color: #555; padding: 4px;">'+errorstring+'</span>'; |
|
251 } |
|
252 else |
|
253 { |
|
254 html = '<table border="0" cellspacing="1" cellpadding="3" style="width: auto;"><tr><th colspan="2">' + $lang.get('page_autosuggest_heading') + '</th></tr><tr><th><small>' + $lang.get('page_autosuggest_col_name') + '</small></th><th><small>' + $lang.get('page_autosuggest_col_page_id') + '</small></th></tr>'; |
|
255 cls = 'row2'; |
|
256 unObjCurrentSelection = userlist[0]; |
|
257 for(i=0;i<userlist.length;i++) |
|
258 { |
|
259 tmpnam = 'listobjnode_'+Math.floor(Math.random() * 10000000); |
|
260 html = html + '<tr><td id="'+tmpnam+'" class="'+cls+'" style="cursor: pointer;" onclick="document.getElementById(\''+unObj.id+'\').value=\''+userlist[i]+'\';destroyUsernameDropdowns();"><small>'+namelist[i]+'</small></td><td class="'+cls+'" style="cursor: pointer;" onclick="document.getElementById(\''+unObj.id+'\').value=\''+userlist[i]+'\';destroyUsernameDropdowns();"><small>'+userlist[i]+'</small></td></tr>'; |
|
261 if(cls=='row2') cls='row1'; |
|
262 } |
|
263 html = html + '</table>'; |
|
264 } |
|
265 |
|
266 thediv.innerHTML = html; |
|
267 var body = document.getElementsByTagName('body'); |
|
268 body = body[0]; |
|
269 unSelectMenuOn = true; |
|
270 submitAuthorized = false; |
|
271 body.appendChild(thediv); |
|
272 } |
|
273 }); |
|
274 } |
|
275 |
|
276 function destroyUsernameDropdowns() |
|
277 { |
|
278 var divs = document.getElementsByTagName('div'); |
|
279 var prefix = 'usernamehoverobj_'; |
|
280 for(i=0;i<divs.length;i++) |
|
281 { |
|
282 if ( divs[i].id ) |
|
283 { |
|
284 if(divs[i].id.substr(0, prefix.length)==prefix) |
|
285 { |
|
286 divs[i].innerHTML = ''; |
|
287 divs[i].style.display = 'none'; |
|
288 } |
|
289 } |
|
290 } |
|
291 unSelectMenuOn = false; |
|
292 unObjDivCurrentId = false; |
|
293 unObjCurrentSelection = false; |
|
294 submitAuthorized = true; |
|
295 } |
|
296 |
10 |
297 function get_parent_form(o) |
11 function get_parent_form(o) |
298 { |
12 { |
299 if ( !o.parentNode ) |
13 if ( !o.parentNode ) |
300 return false; |
14 return false; |