001: /*
002: * Copyright (c) 2002-2008 Gargoyle Software Inc. All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * 1. Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: * 2. Redistributions in binary form must reproduce the above copyright notice,
010: * this list of conditions and the following disclaimer in the documentation
011: * and/or other materials provided with the distribution.
012: * 3. The end-user documentation included with the redistribution, if any, must
013: * include the following acknowledgment:
014: *
015: * "This product includes software developed by Gargoyle Software Inc.
016: * (http://www.GargoyleSoftware.com/)."
017: *
018: * Alternately, this acknowledgment may appear in the software itself, if
019: * and wherever such third-party acknowledgments normally appear.
020: * 4. The name "Gargoyle Software" must not be used to endorse or promote
021: * products derived from this software without prior written permission.
022: * For written permission, please contact info@GargoyleSoftware.com.
023: * 5. Products derived from this software may not be called "HtmlUnit", nor may
024: * "HtmlUnit" appear in their name, without prior written permission of
025: * Gargoyle Software Inc.
026: *
027: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
028: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
029: * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARGOYLE
030: * SOFTWARE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
031: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
032: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
033: * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
034: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
035: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
036: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
037: */
038: package com.gargoylesoftware.htmlunit.javascript.host;
039:
040: import java.util.Iterator;
041: import java.util.List;
042:
043: import org.apache.commons.lang.ArrayUtils;
044: import org.mozilla.javascript.Context;
045: import org.mozilla.javascript.Scriptable;
046:
047: import com.gargoylesoftware.htmlunit.html.HTMLParser;
048: import com.gargoylesoftware.htmlunit.html.HtmlOption;
049: import com.gargoylesoftware.htmlunit.html.HtmlSelect;
050: import com.gargoylesoftware.htmlunit.javascript.HTMLOptionsCollection;
051:
052: /**
053: * The javascript object for {@link HtmlSelect}.
054: *
055: * @version $Revision: 2132 $
056: * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
057: * @author David K. Taylor
058: * @author Marc Guillemot
059: * @author Chris Erskine
060: * @author Ahmed Ashour
061: */
062: public class HTMLSelectElement extends FormField {
063:
064: private static final long serialVersionUID = 4332789476842114628L;
065: private HTMLOptionsCollection optionsArray_;
066:
067: /**
068: * Create an instance.
069: */
070: public HTMLSelectElement() {
071: }
072:
073: /**
074: * Javascript constructor. This must be declared in every javascript file because
075: * the rhino engine won't walk up the hierarchy looking for constructors.
076: */
077: public void jsConstructor() {
078: }
079:
080: /**
081: * Initialize the object.
082: *
083: */
084: public void initialize() {
085: final HtmlSelect htmlSelect = getHtmlSelect();
086: htmlSelect.setScriptObject(this );
087: if (optionsArray_ == null) {
088: optionsArray_ = new HTMLOptionsCollection(this );
089: optionsArray_.initialize(htmlSelect);
090: }
091: }
092:
093: /**
094: * Remove option at the specified index
095: * @param index The index of the item to remove
096: */
097: public void jsxFunction_remove(final int index) {
098: put(index, null, null);
099: }
100:
101: /**
102: * Add a new item to the list (optionally) before the specified item
103: * @param newOptionObject The DomNode to insert
104: * @param arg2 for Firefox: the DomNode to insert the previous element before (null if at end),
105: * for Internet Explorer: the index where the element should be placed (optional)
106: */
107: public void jsxFunction_add(final Option newOptionObject,
108: final Object arg2) {
109: if (getWindow().getWebWindow().getWebClient()
110: .getBrowserVersion().isIE()) {
111: add_IE(newOptionObject, arg2);
112: } else {
113: add(newOptionObject, arg2);
114: }
115: }
116:
117: /**
118: * Add a new item to the list (optionally) at the specified index in IE way
119: * @param newOptionObject The DomNode to insert
120: * @param index (optional) the index where the node should be inserted
121: */
122: protected void add_IE(final Option newOptionObject,
123: final Object index) {
124: final HtmlSelect select = getHtmlSelect();
125: final HtmlOption beforeOption;
126: if (Context.getUndefinedValue().equals(index)) {
127: beforeOption = null;
128: } else {
129: final int intIndex = ((Integer) Context.jsToJava(index,
130: Integer.class)).intValue();
131: if (intIndex >= select.getOptionSize()) {
132: beforeOption = null;
133: } else {
134: beforeOption = select.getOption(intIndex);
135: }
136: }
137:
138: addBefore(newOptionObject, beforeOption);
139: }
140:
141: /**
142: * Add a new item to the list (optionally) before the specified item in Mozilla way
143: * @param newOptionObject The DomNode to insert
144: * @param beforeOptionObject The DomNode to insert the previous element before (null if at end)
145: */
146: protected void add(final Option newOptionObject,
147: final Object beforeOptionObject) {
148: final HtmlOption beforeOption;
149: if (beforeOptionObject == null) {
150: beforeOption = null;
151: } else if (Context.getUndefinedValue().equals(
152: beforeOptionObject)) {
153: throw Context
154: .reportRuntimeError("Not enough arguments [SelectElement.add]");
155: } else {
156: beforeOption = (HtmlOption) ((Option) beforeOptionObject)
157: .getDomNodeOrDie();
158: }
159: addBefore(newOptionObject, beforeOption);
160: }
161:
162: /**
163: * Adds the option (and create the associated dom node if needed) before the specified one
164: * or at the end if the specified one in null
165: * @param newOptionObject the new option to add
166: * @param beforeOption the option that should be after the option to add
167: */
168: protected void addBefore(final Option newOptionObject,
169: final HtmlOption beforeOption) {
170: final HtmlSelect select = getHtmlSelect();
171:
172: HtmlOption htmlOption = (HtmlOption) newOptionObject
173: .getHtmlElementOrNull();
174: if (htmlOption == null) {
175: htmlOption = (HtmlOption) HTMLParser.getFactory(
176: HtmlOption.TAG_NAME).createElement(
177: select.getPage(), HtmlOption.TAG_NAME, null);
178: }
179:
180: if (beforeOption == null) {
181: select.appendDomChild(htmlOption);
182: } else {
183: beforeOption.insertBefore(htmlOption);
184: }
185: }
186:
187: /**
188: * Return the type of this input.
189: * @return The type
190: */
191: public String jsxGet_type() {
192: final String type;
193: if (getHtmlSelect().isMultipleSelectEnabled()) {
194: type = "select-multiple";
195: } else {
196: type = "select-one";
197: }
198: return type;
199: }
200:
201: /**
202: * Return the value of the "options" property
203: * @return The options property
204: */
205: public HTMLOptionsCollection jsxGet_options() {
206: if (optionsArray_ == null) {
207: initialize();
208: }
209: return optionsArray_;
210: }
211:
212: /**
213: * Return the value of the "selectedIndex" property
214: * @return The selectedIndex property
215: */
216: public int jsxGet_selectedIndex() {
217: final HtmlSelect htmlSelect = getHtmlSelect();
218: final List selectedOptions = htmlSelect.getSelectedOptions();
219: if (selectedOptions.isEmpty()) {
220: return -1;
221: } else {
222: final List allOptions = htmlSelect.getOptions();
223: return allOptions.indexOf(selectedOptions.get(0));
224: }
225: }
226:
227: /**
228: * Set the value of the "selectedIndex" property
229: * @param index The new value
230: */
231: public void jsxSet_selectedIndex(final int index) {
232: final HtmlSelect htmlSelect = getHtmlSelect();
233:
234: final Iterator iter = htmlSelect.getSelectedOptions()
235: .iterator();
236: while (iter.hasNext()) {
237: final HtmlOption itemToUnSelect = (HtmlOption) iter.next();
238: htmlSelect.setSelectedAttribute(itemToUnSelect, false);
239: }
240: if (index < 0) {
241: htmlSelect.fakeSelectedAttribute("");
242: return;
243: } else {
244: htmlSelect
245: .fakeSelectedAttribute(ArrayUtils.EMPTY_STRING_ARRAY);
246: }
247:
248: final List allOptions = htmlSelect.getOptions();
249:
250: if (index < allOptions.size()) {
251: final HtmlOption itemToSelect = (HtmlOption) allOptions
252: .get(index);
253: htmlSelect.setSelectedAttribute(itemToSelect, true);
254: }
255: }
256:
257: /**
258: * Return the actual value of the selected Option
259: * @return The value
260: */
261: public String jsxGet_value() {
262: final HtmlSelect htmlSelect = getHtmlSelect();
263: final List selectedOptions = htmlSelect.getSelectedOptions();
264: if (selectedOptions.isEmpty()) {
265: return "";
266: } else {
267: return ((HtmlOption) selectedOptions.get(0))
268: .getValueAttribute();
269: }
270: }
271:
272: /**
273: * Return the value of the "length" property
274: * @return The length property
275: */
276: public int jsxGet_length() {
277: if (optionsArray_ == null) {
278: initialize();
279: }
280: return optionsArray_.jsxGet_length();
281: }
282:
283: /**
284: * Remove options by reducing the "length" property
285: * @param newLength The new length property value
286: */
287: public void jsxSet_length(final int newLength) {
288: if (optionsArray_ == null) {
289: initialize();
290: }
291: optionsArray_.jsxSet_length(newLength);
292: }
293:
294: /**
295: * Return the specified indexed property
296: * @param index The index of the property
297: * @param start The scriptable object that was originally queried for this property
298: * @return The property.
299: */
300: public Object get(final int index, final Scriptable start) {
301: if (optionsArray_ == null) {
302: initialize();
303: }
304: return optionsArray_.get(index, start);
305: }
306:
307: /**
308: * Set the index property
309: * @param index The index
310: * @param start The scriptable object that was originally invoked for this property
311: * @param newValue The new value
312: */
313: public void put(final int index, final Scriptable start,
314: final Object newValue) {
315: if (optionsArray_ == null) {
316: initialize();
317: }
318: optionsArray_.put(index, start, newValue);
319: }
320:
321: /**
322: * Return the HTML select object.
323: * @return The HTML select object.
324: */
325: private HtmlSelect getHtmlSelect() {
326: return (HtmlSelect) getHtmlElementOrDie();
327: }
328:
329: /**
330: * Selects the option with the specified value
331: * @param newValue The value of the option to select
332: */
333: public void jsxSet_value(final String newValue) {
334: getHtmlSelect().setSelectedAttribute(newValue, true);
335: }
336:
337: /**
338: * Returns the <tt>size</tt> attribute.
339: * @return the <tt>size</tt> attribute.
340: */
341: public int jsxGet_size() {
342: int size = 0;
343: final String sizeAttribute = getHtmlElementOrDie()
344: .getAttributeValue("size");
345: if (sizeAttribute != HtmlSelect.ATTRIBUTE_NOT_DEFINED
346: && sizeAttribute != HtmlSelect.ATTRIBUTE_VALUE_EMPTY) {
347: try {
348: size = Integer.parseInt(sizeAttribute);
349: } catch (final Exception e) {
350: //silently ignore
351: }
352: }
353: return size;
354: }
355:
356: /**
357: * Sets the <tt>size</tt> attribute.
358: * @param size The <tt>size</tt> attribute.
359: */
360: public void jsxSet_size(final String size) {
361: getHtmlElementOrDie().setAttributeValue("size", size);
362: }
363: }
|