1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.custom.suggest;
20
21 import java.io.IOException;
22 import java.util.Iterator;
23 import java.util.Map;
24
25 import javax.faces.component.EditableValueHolder;
26 import javax.faces.component.UIComponent;
27 import javax.faces.component.UISelectItems;
28 import javax.faces.component.ValueHolder;
29 import javax.faces.component.html.HtmlInputText;
30 import javax.faces.context.FacesContext;
31 import javax.faces.context.ResponseWriter;
32 import javax.faces.render.Renderer;
33
34 import org.apache.myfaces.component.html.ext.HtmlInputHidden;
35 import org.apache.myfaces.renderkit.html.util.AddResource;
36 import org.apache.myfaces.renderkit.html.util.AddResourceFactory;
37 import org.apache.myfaces.shared_tomahawk.renderkit.html.HTML;
38 import org.apache.myfaces.shared_tomahawk.renderkit.JSFAttr;
39 import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils;
40
41
42
43
44
45
46
47
48
49
50
51
52
53 public class InputSuggestRenderer
54 extends Renderer
55 {
56 private String NEW_TEXT_KEY = "-1";
57
58 public boolean getRendersChildren()
59 {
60
61 return true;
62 }
63
64 public void decode(FacesContext context, UIComponent component)
65 {
66
67 if (isDisabledOrReadOnly(component))
68 {
69 return;
70 }
71
72 Map params = context.getExternalContext().getRequestParameterMap();
73 String text = (String) params.get(getTextId(component, context));
74 String choice = (String) params.get(getChoiceId(component, context));
75 if (choice != null)
76 {
77 ( (EditableValueHolder) component).setSubmittedValue(choice);
78
79 if (choice.equals(NEW_TEXT_KEY))
80 {
81 Map choices = getChoices(component);
82 choices.put(NEW_TEXT_KEY, text);
83 }
84 }
85 }
86
87 public void encodeBegin(FacesContext context, UIComponent component) throws
88 IOException
89 {
90
91 if (!component.isRendered())
92 {
93 return;
94 }
95
96
97 String value = (String) ( (EditableValueHolder) component).
98 getSubmittedValue();
99 if (value == null)
100 {
101 value = (String) ( (ValueHolder) component).getValue();
102 }
103
104 String text = null;
105 Map choices = getChoices(component);
106 if (value != null && choices != null)
107 {
108 text = (String) choices.get(value);
109 }
110
111 ResponseWriter out = context.getResponseWriter();
112 renderInputField(out, text, getTextId(component, context), component);
113
114
115 HtmlInputHidden hiddenChoice = new HtmlInputHidden();
116 hiddenChoice.setId(getChoiceId(component, context));
117 hiddenChoice.setValue(value);
118 hiddenChoice.getAttributes().put(JSFAttr.FORCE_ID_ATTR, Boolean.TRUE);
119
120 hiddenChoice.encodeBegin(context);
121 hiddenChoice.encodeEnd(context);
122
123 encodeSuggestions(context, out, choices,
124 getSuggestionsId(component, context), component);
125 encodeStyles(component, context);
126 encodeJavascript(component, context);
127 }
128
129 private void renderInputField(ResponseWriter out, String text,
130 String clientId, UIComponent component) throws
131 IOException
132 {
133
134 out.startElement("input", component);
135 out.writeAttribute("name", clientId, null);
136 out.writeAttribute("id", clientId, null);
137
138 if (text != null)
139 {
140 out.writeAttribute("value", text, "value");
141 }
142 else
143 {
144 out.writeAttribute("value", "", "value");
145 }
146
147 component.getAttributes().put("autocomplete","off");
148
149 HtmlRendererUtils.renderHTMLAttributes(out,
150 component, HTML.INPUT_PASSTHROUGH_ATTRIBUTES_WITHOUT_DISABLED);
151
152 if((component instanceof HtmlInputText) && ((HtmlInputText) component).isDisabled())
153 {
154 out.writeAttribute(HTML.DISABLED_ATTR, Boolean.TRUE, null);
155 }
156
157 out.endElement("input");
158 }
159
160 private void encodeSuggestions(FacesContext context, ResponseWriter out,
161 Map choices, String clientId,
162 UIComponent component) throws IOException
163 {
164
165
166
167 out.startElement(HTML.DIV_ELEM, component);
168 out.writeAttribute(HTML.ID_ATTR, clientId, null);
169
170 Iterator i = choices.keySet().iterator();
171 while (i.hasNext())
172 {
173 String choice = (String) i.next();
174 if(choice.compareTo("-1")==0) continue;
175 String text = (String) choices.get(choice);
176 out.startElement(HTML.DIV_ELEM, null);
177 out.writeAttribute(HTML.ID_ATTR,
178 component.getClientId(context) + "_choice" +
179 choice, null);
180 out.writeAttribute(HTML.CLASS_ATTR, "ACdiv", null);
181 out.writeText(text, null);
182 out.endElement(HTML.DIV_ELEM);
183 }
184
185 out.endElement(HTML.DIV_ELEM);
186 }
187
188
189
190
191
192 private boolean isDisabledOrReadOnly(UIComponent component)
193 {
194 boolean disabled = false;
195 boolean readOnly = false;
196
197 Object disabledAttr = component.getAttributes().get("disabled");
198 if (disabledAttr != null)
199 {
200 disabled = disabledAttr.equals(Boolean.TRUE);
201 }
202 Object readOnlyAttr = component.getAttributes().get("readonly");
203 if (readOnlyAttr != null)
204 {
205 readOnly = readOnlyAttr.equals(Boolean.TRUE);
206 }
207 return disabled || readOnly;
208 }
209
210 private Map getChoices(UIComponent component)
211 {
212
213 Object choices = null;
214 Iterator i = component.getChildren().iterator();
215 while (i.hasNext())
216 {
217 UIComponent kid = (UIComponent) i.next();
218
219 if (kid instanceof UISelectItems)
220 {
221 choices = ( (UISelectItems) kid).getValue();
222 }
223 }
224
225
226
227
228
229 return (Map) choices;
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246 }
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282 private void encodeStyles(UIComponent component, FacesContext context) throws
283 IOException
284 {
285 AddResource addResource = AddResourceFactory.getInstance(context);
286 String styleLocation = (String) component.getAttributes().get(JSFAttr.
287 STYLE_LOCATION);
288 if (styleLocation == null)
289 {
290 addResource.addStyleSheet(context, AddResource.HEADER_BEGIN, InputSuggestRenderer.class,
291 "css/suggest.css");
292 }
293 else
294 {
295 addResource.addStyleSheet(context, AddResource.HEADER_BEGIN, styleLocation + "/suggest.css");
296 }
297 }
298
299
300
301
302
303
304
305
306 private void encodeJavascript(UIComponent component, FacesContext context) throws
307 IOException
308 {
309 ResponseWriter out = context.getResponseWriter();
310
311 AddResource addResource = AddResourceFactory.getInstance(context);
312 String javascriptLocation = (String) component.getAttributes().get(
313 JSFAttr.JAVASCRIPT_LOCATION);
314 if (javascriptLocation == null)
315 {
316 addResource.addJavaScriptHere(context, InputSuggestRenderer.class,
317 "javascript/suggest.js");
318 }
319 else
320 {
321 addResource.addJavaScriptHere(context, javascriptLocation + "/suggest.js");
322 }
323
324
325 out.startElement(HTML.SCRIPT_ELEM, null);
326 out.writeAttribute(HTML.TYPE_ATTR, "text/javascript", null);
327
328 String textId = getTextId(component, context);
329 String choiceId = getChoiceId(component, context);
330 String suggestionsId = getSuggestionsId(component, context);
331
332
333 String modifiedTextId = textId.replace(':', '_');
334 modifiedTextId = modifiedTextId.replace('|', '_');
335 modifiedTextId = modifiedTextId.replace('.', '_');
336
337
338 out.writeText("\nvar " + modifiedTextId +
339 "Row = -1; // this should always be initialized to -1\n", null);
340 out.writeText("var " + modifiedTextId +
341 "RowDiv = null; // this should always be initialized to null\n", null);
342 out.writeText("var " + modifiedTextId +
343 "MinRow = 0; // this should always be initialized to 0\n", null);
344 out.writeText("var ACrowHeight = 15;\n", null);
345 out.writeText("var ACfield = document.getElementById('" + textId +
346 "');\n", null);
347 out.writeText("var " + modifiedTextId + "Scroll = true;\n", null);
348 out.writeText("var " + modifiedTextId + "CaseSensitive = false;\n", null);
349 out.writeText("var " + modifiedTextId + "DisplayRows = 5;\n", null);
350 out.writeText("var " + modifiedTextId +
351 "Div = document.getElementById('" + suggestionsId +
352 "');\n", null);
353 out.writeText("var " + modifiedTextId + "HiddenFldId = '" + choiceId +
354 "';\n", null);
355 out.writeText("var " + modifiedTextId + "NormalClass = 'ACdiv';\n", null);
356 out.writeText("var " + modifiedTextId +
357 "HighlightClass = 'AChighlighted';\n", null);
358 out.writeText("ACfield.onfocus = new Function('" + modifiedTextId +
359 "Div.style.visibility = \"visible\"');\n", null);
360 out.writeText("ACfield.onblur = new Function('blurACfld(this)');\n", null);
361 out.writeText("ACfield.onkeyup = new Function(\"event\", \"return handleACkeyUp(this, event)\");\n", null);
362 out.writeText("ACfield.onkeydown = new Function(\"event\", \"return handleACkeyDown(this, event)\");\n", null);
363 out.writeText("var " + modifiedTextId + "Options = " + modifiedTextId +
364 "Div.getElementsByTagName(\"DIV\");\n", null);
365 out.writeText(modifiedTextId +
366 "Div.onscroll = new Function(\"setACfieldFocus('" +
367 textId + "')\");\n", null);
368
369 out.writeText("var optLen = " + modifiedTextId + "Options.length;\n", null);
370 out.writeText("for (var ii=0; ii<optLen; ii++) {\n", null);
371 out.writeText(modifiedTextId +
372 "Options[ii].style.height = ACrowHeight + 'px';\n", null);
373 out.writeText(modifiedTextId +
374 "Options[ii].onmouseover = new Function(\"highlightACDiv(this, '" +
375 textId +
376 "', \" + ii + \")\");\n", null);
377 out.writeText(modifiedTextId +
378 "Options[ii].onmouseout = new Function(\"unHighlightACDiv(this, '" +
379 textId + "')\");\n", null);
380 out.writeText(modifiedTextId +
381 "Options[ii].onmousedown = new Function(\"selectACDiv('" +
382 textId + "')\");\n", null);
383 out.writeText("}\n", null);
384
385 out.writeText("if (navigator.appVersion.toLowerCase().indexOf('msie') != -1 && " +
386 "navigator.userAgent.toLowerCase().indexOf('opera') == -1)\n", null);
387 out.writeText("document.writeln('<iframe id=\"" + modifiedTextId + "Shim\" src=\"javascript:false;\" " +
388 "scrolling=\"no\" frameborder=\"0\" style=\"position:absolute; top:0px; left:0px;\">" +
389 "</iframe>');\n", null);
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413 out.endElement(HTML.SCRIPT_ELEM);
414
415 }
416
417 private String getTextId(UIComponent component, FacesContext context)
418 {
419 return (component.getId() + "_text");
420 }
421
422 private String getChoiceId(UIComponent component, FacesContext context)
423 {
424 return (component.getId() + "_choice");
425 }
426
427 private String getSuggestionsId(UIComponent component, FacesContext context)
428 {
429 return ("AC" + getTextId(component, context));
430 }
431 }