Project

General

Profile

Download (9.17 KB) Statistics
| Branch: | Tag: | Revision:
1

    
2
/**
3
 * An autosuggest textbox control.
4
 * @class
5
 * @scope public
6
 */
7
function AutoSuggestControl(oTextbox /*:HTMLInputElement*/, 
8
                            oProvider /*:SuggestionProvider*/) {
9
    
10
    /**
11
     * The currently selected suggestions.
12
     * @scope private
13
     */   
14
    this.cur /*:int*/ = -1;
15

    
16
    /**
17
     * The dropdown list layer.
18
     * @scope private
19
     */
20
    this.layer = null;
21
    
22
    /**
23
     * Suggestion provider for the autosuggest feature.
24
     * @scope private.
25
     */
26
    this.provider /*:SuggestionProvider*/ = oProvider;
27
    
28
    /**
29
     * The textbox to capture.
30
     * @scope private
31
     */
32
    this.textbox /*:HTMLInputElement*/ = oTextbox;
33
    
34
    //initialize the control
35
    this.init();
36
    
37
}
38

    
39
/**
40
 * Autosuggests one or more suggestions for what the user has typed.
41
 * If no suggestions are passed in, then no autosuggest occurs.
42
 * @scope private
43
 * @param aSuggestions An array of suggestion strings.
44
 * @param bTypeAhead If the control should provide a type ahead suggestion.
45
 */
46
AutoSuggestControl.prototype.autosuggest = function (aSuggestions /*:Array*/,
47
                                                     bTypeAhead /*:boolean*/) {
48
    
49
    //make sure there's at least one suggestion
50
    if (aSuggestions.length > 0) {
51
        if (bTypeAhead) {
52
           this.typeAhead(aSuggestions[0]);
53
        }
54
        
55
        this.showSuggestions(aSuggestions);
56
    } else {
57
        this.hideSuggestions();
58
    }
59
};
60

    
61
/**
62
 * Creates the dropdown layer to display multiple suggestions.
63
 * @scope private
64
 */
65
AutoSuggestControl.prototype.createDropDown = function () {
66

    
67
    var oThis = this;
68

    
69
    //create the layer and assign styles
70
    this.layer = document.createElement("div");
71
    this.layer.className = "suggestions";
72
    this.layer.style.visibility = "hidden";
73
    this.layer.style.width = this.textbox.offsetWidth;
74
    
75
    //when the user clicks on the a suggestion, get the text (innerHTML)
76
    //and place it into a textbox
77
    this.layer.onmousedown = 
78
    this.layer.onmouseup = 
79
    this.layer.onmouseover = function (oEvent) {
80
        oEvent = oEvent || window.event;
81
        oTarget = oEvent.target || oEvent.srcElement;
82

    
83
        if (oEvent.type == "mousedown") {
84
            oThis.textbox.value = oTarget.firstChild.nodeValue;
85
            oThis.hideSuggestions();
86
        } else if (oEvent.type == "mouseover") {
87
            oThis.highlightSuggestion(oTarget);
88
        } else {
89
            oThis.textbox.focus();
90
        }
91
    };
92
    
93
    
94
    document.body.appendChild(this.layer);
95
};
96

    
97
/**
98
 * Gets the left coordinate of the textbox.
99
 * @scope private
100
 * @return The left coordinate of the textbox in pixels.
101
 */
102
AutoSuggestControl.prototype.getLeft = function () /*:int*/ {
103

    
104
    var oNode = this.textbox;
105
    var iLeft = 0;
106
    
107
    while(oNode.tagName != "BODY") {
108
        iLeft += oNode.offsetLeft;
109
        oNode = oNode.offsetParent;        
110
    }
111
    
112
    return iLeft;
113
};
114

    
115
/**
116
 * Gets the top coordinate of the textbox.
117
 * @scope private
118
 * @return The top coordinate of the textbox in pixels.
119
 */
120
AutoSuggestControl.prototype.getTop = function () /*:int*/ {
121

    
122
    var oNode = this.textbox;
123
    var iTop = 0;
124
    
125
    while(oNode.tagName != "BODY") {
126
        iTop += oNode.offsetTop;
127
        oNode = oNode.offsetParent;
128
    }
129
    
130
    return iTop;
131
};
132

    
133
/**
134
 * Handles three keydown events.
135
 * @scope private
136
 * @param oEvent The event object for the keydown event.
137
 */
138
AutoSuggestControl.prototype.handleKeyDown = function (oEvent /*:Event*/) {
139

    
140
    switch(oEvent.keyCode) {
141
        case 38: //up arrow
142
            this.previousSuggestion();
143
            break;
144
        case 40: //down arrow 
145
            this.nextSuggestion();
146
            break;
147
        case 13: //enter
148
            this.hideSuggestions();
149
            break;
150
    }
151

    
152
};
153

    
154
/**
155
 * Handles keyup events.
156
 * @scope private
157
 * @param oEvent The event object for the keyup event.
158
 */
159
AutoSuggestControl.prototype.handleKeyUp = function (oEvent /*:Event*/) {
160

    
161
    var iKeyCode = oEvent.keyCode;
162

    
163
    //for backspace (8) and delete (46), shows suggestions without typeahead
164
    if (iKeyCode == 8 || iKeyCode == 46) {
165
        this.provider.requestSuggestions(this, false);
166
        
167
    //make sure not to interfere with non-character keys
168
    } else if (iKeyCode < 32 || (iKeyCode >= 33 && iKeyCode < 46) || (iKeyCode >= 112 && iKeyCode <= 123)) {
169
        //ignore
170
    } else {
171
        //request suggestions from the suggestion provider with typeahead
172
        this.provider.requestSuggestions(this, true);
173
    }
174
};
175

    
176
/**
177
 * Hides the suggestion dropdown.
178
 * @scope private
179
 */
180
AutoSuggestControl.prototype.hideSuggestions = function () {
181
    this.layer.style.visibility = "hidden";
182
};
183

    
184
/**
185
 * Highlights the given node in the suggestions dropdown.
186
 * @scope private
187
 * @param oSuggestionNode The node representing a suggestion in the dropdown.
188
 */
189
AutoSuggestControl.prototype.highlightSuggestion = function (oSuggestionNode) {
190
    
191
    for (var i=0; i < this.layer.childNodes.length; i++) {
192
        var oNode = this.layer.childNodes[i];
193
        if (oNode == oSuggestionNode) {
194
            oNode.className = "current"
195
        } else if (oNode.className == "current") {
196
            oNode.className = "";
197
        }
198
    }
199
};
200

    
201
/**
202
 * Initializes the textbox with event handlers for
203
 * auto suggest functionality.
204
 * @scope private
205
 */
206
AutoSuggestControl.prototype.init = function () {
207

    
208
    //save a reference to this object
209
    var oThis = this;
210
    
211
    //assign the onkeyup event handler
212
    this.textbox.onkeyup = function (oEvent) {
213
    
214
        //check for the proper location of the event object
215
        if (!oEvent) {
216
            oEvent = window.event;
217
        }    
218
        
219
        //call the handleKeyUp() method with the event object
220
        oThis.handleKeyUp(oEvent);
221
    };
222
    
223
    //assign onkeydown event handler
224
    this.textbox.onkeydown = function (oEvent) {
225
    
226
        //check for the proper location of the event object
227
        if (!oEvent) {
228
            oEvent = window.event;
229
        }    
230
        
231
        //call the handleKeyDown() method with the event object
232
        oThis.handleKeyDown(oEvent);
233
    };
234
    
235
    //assign onblur event handler (hides suggestions)    
236
    this.textbox.onblur = function () {
237
        oThis.hideSuggestions();
238
    };
239
    
240
    //create the suggestions dropdown
241
    this.createDropDown();
242
};
243

    
244
/**
245
 * Highlights the next suggestion in the dropdown and
246
 * places the suggestion into the textbox.
247
 * @scope private
248
 */
249
AutoSuggestControl.prototype.nextSuggestion = function () {
250
    var cSuggestionNodes = this.layer.childNodes;
251

    
252
    if (cSuggestionNodes.length > 0 && this.cur < cSuggestionNodes.length-1) {
253
        var oNode = cSuggestionNodes[++this.cur];
254
        this.highlightSuggestion(oNode);
255
        this.textbox.value = oNode.firstChild.nodeValue; 
256
    }
257
};
258

    
259
/**
260
 * Highlights the previous suggestion in the dropdown and
261
 * places the suggestion into the textbox.
262
 * @scope private
263
 */
264
AutoSuggestControl.prototype.previousSuggestion = function () {
265
    var cSuggestionNodes = this.layer.childNodes;
266

    
267
    if (cSuggestionNodes.length > 0 && this.cur > 0) {
268
        var oNode = cSuggestionNodes[--this.cur];
269
        this.highlightSuggestion(oNode);
270
        this.textbox.value = oNode.firstChild.nodeValue;   
271
    }
272
};
273

    
274
/**
275
 * Selects a range of text in the textbox.
276
 * @scope public
277
 * @param iStart The start index (base 0) of the selection.
278
 * @param iLength The number of characters to select.
279
 */
280
AutoSuggestControl.prototype.selectRange = function (iStart /*:int*/, iLength /*:int*/) {
281

    
282
    //use text ranges for Internet Explorer
283
    if (this.textbox.createTextRange) {
284
        var oRange = this.textbox.createTextRange(); 
285
        oRange.moveStart("character", iStart); 
286
        oRange.moveEnd("character", iLength - this.textbox.value.length);      
287
        oRange.select();
288
        
289
    //use setSelectionRange() for Mozilla
290
    } else if (this.textbox.setSelectionRange) {
291
        this.textbox.setSelectionRange(iStart, iLength);
292
    }     
293

    
294
    //set focus back to the textbox
295
    this.textbox.focus();      
296
}; 
297

    
298
/**
299
 * Builds the suggestion layer contents, moves it into position,
300
 * and displays the layer.
301
 * @scope private
302
 * @param aSuggestions An array of suggestions for the control.
303
 */
304
AutoSuggestControl.prototype.showSuggestions = function (aSuggestions /*:Array*/) {
305
    
306
    var oDiv = null;
307
    this.layer.innerHTML = "";  //clear contents of the layer
308
    
309
    for (var i=0; i < aSuggestions.length; i++) {
310
        oDiv = document.createElement("div");
311
        oDiv.appendChild(document.createTextNode(aSuggestions[i]));
312
        this.layer.appendChild(oDiv);
313
    }
314
    
315
    this.layer.style.left = this.getLeft() + "px";
316
    this.layer.style.top = (this.getTop()+this.textbox.offsetHeight) + "px";
317
    this.layer.style.visibility = "visible";
318

    
319
};
320

    
321
/**
322
 * Inserts a suggestion into the textbox, highlighting the 
323
 * suggested part of the text.
324
 * @scope private
325
 * @param sSuggestion The suggestion for the textbox.
326
 */
327
AutoSuggestControl.prototype.typeAhead = function (sSuggestion /*:String*/) {
328

    
329
    //check for support of typeahead functionality
330
    if (this.textbox.createTextRange || this.textbox.setSelectionRange){
331
        var iLen = this.textbox.value.length; 
332
        this.textbox.value = sSuggestion; 
333
        this.selectRange(iLen, sSuggestion.length);
334
    }
335
};
336

    
(2-2/17)