Adding float-no-zero branch hosted build

This commit is contained in:
ahocevar
2014-03-07 10:55:12 +01:00
parent 84cad42f6d
commit bd9092199b
1664 changed files with 731463 additions and 0 deletions
@@ -0,0 +1,50 @@
// Copyright 2012 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Utility methods supporting the autocomplete package.
*
* @see ../../demos/autocomplete-basic.html
*/
goog.provide('goog.ui.ac');
goog.require('goog.ui.ac.ArrayMatcher');
goog.require('goog.ui.ac.AutoComplete');
goog.require('goog.ui.ac.InputHandler');
goog.require('goog.ui.ac.Renderer');
/**
* Factory function for building a basic autocomplete widget that autocompletes
* an inputbox or text area from a data array.
* @param {Array} data Data array.
* @param {Element} input Input element or text area.
* @param {boolean=} opt_multi Whether to allow multiple entries separated with
* semi-colons or commas.
* @param {boolean=} opt_useSimilar use similar matches. e.g. "gost" => "ghost".
* @return {!goog.ui.ac.AutoComplete} A new autocomplete object.
*/
goog.ui.ac.createSimpleAutoComplete =
function(data, input, opt_multi, opt_useSimilar) {
var matcher = new goog.ui.ac.ArrayMatcher(data, !opt_useSimilar);
var renderer = new goog.ui.ac.Renderer();
var inputHandler = new goog.ui.ac.InputHandler(null, null, !!opt_multi);
var autoComplete = new goog.ui.ac.AutoComplete(
matcher, renderer, inputHandler);
inputHandler.attachAutoComplete(autoComplete);
inputHandler.attachInputs(input);
return autoComplete;
};
@@ -0,0 +1,162 @@
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Basic class for matching words in an array.
*
*/
goog.provide('goog.ui.ac.ArrayMatcher');
goog.require('goog.string');
/**
* Basic class for matching words in an array
* @constructor
* @param {Array} rows Dictionary of items to match. Can be objects if they
* have a toString method that returns the value to match against.
* @param {boolean=} opt_noSimilar if true, do not do similarity matches for the
* input token against the dictionary.
*/
goog.ui.ac.ArrayMatcher = function(rows, opt_noSimilar) {
this.rows_ = rows;
this.useSimilar_ = !opt_noSimilar;
};
/**
* Replaces the rows that this object searches over.
* @param {Array} rows Dictionary of items to match.
*/
goog.ui.ac.ArrayMatcher.prototype.setRows = function(rows) {
this.rows_ = rows;
};
/**
* Function used to pass matches to the autocomplete
* @param {string} token Token to match.
* @param {number} maxMatches Max number of matches to return.
* @param {Function} matchHandler callback to execute after matching.
* @param {string=} opt_fullString The full string from the input box.
*/
goog.ui.ac.ArrayMatcher.prototype.requestMatchingRows =
function(token, maxMatches, matchHandler, opt_fullString) {
var matches = this.getPrefixMatches(token, maxMatches);
if (matches.length == 0 && this.useSimilar_) {
matches = this.getSimilarRows(token, maxMatches);
}
matchHandler(token, matches);
};
/**
* Matches the token against the start of words in the row.
* @param {string} token Token to match.
* @param {number} maxMatches Max number of matches to return.
* @return {Array} Rows that match.
*/
goog.ui.ac.ArrayMatcher.prototype.getPrefixMatches =
function(token, maxMatches) {
var matches = [];
if (token != '') {
var escapedToken = goog.string.regExpEscape(token);
var matcher = new RegExp('(^|\\W+)' + escapedToken, 'i');
for (var i = 0; i < this.rows_.length && matches.length < maxMatches; i++) {
var row = this.rows_[i];
if (String(row).match(matcher)) {
matches.push(row);
}
}
}
return matches;
};
/**
* Matches the token against similar rows, by calculating "distance" between the
* terms.
* @param {string} token Token to match.
* @param {number} maxMatches Max number of matches to return.
* @return {Array} The best maxMatches rows.
*/
goog.ui.ac.ArrayMatcher.prototype.getSimilarRows = function(token, maxMatches) {
var results = [];
for (var index = 0; index < this.rows_.length; index++) {
var row = this.rows_[index];
var str = token.toLowerCase();
var txt = String(row).toLowerCase();
var score = 0;
if (txt.indexOf(str) != -1) {
score = parseInt((txt.indexOf(str) / 4).toString(), 10);
} else {
var arr = str.split('');
var lastPos = -1;
var penalty = 10;
for (var i = 0, c; c = arr[i]; i++) {
var pos = txt.indexOf(c);
if (pos > lastPos) {
var diff = pos - lastPos - 1;
if (diff > penalty - 5) {
diff = penalty - 5;
}
score += diff;
lastPos = pos;
} else {
score += penalty;
penalty += 5;
}
}
}
if (score < str.length * 6) {
results.push({
str: row,
score: score,
index: index
});
}
}
results.sort(function(a, b) {
var diff = a.score - b.score;
if (diff != 0) {
return diff;
}
return a.index - b.index;
});
var matches = [];
for (var i = 0; i < maxMatches && i < results.length; i++) {
matches.push(results[i].str);
}
return matches;
};
@@ -0,0 +1,919 @@
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Gmail-like AutoComplete logic.
*
* @see ../../demos/autocomplete-basic.html
*/
goog.provide('goog.ui.ac.AutoComplete');
goog.provide('goog.ui.ac.AutoComplete.EventType');
goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.events');
goog.require('goog.events.EventTarget');
goog.require('goog.object');
/**
* This is the central manager class for an AutoComplete instance. The matcher
* can specify disabled rows that should not be hilited or selected by
* implementing <code>isRowDisabled(row):boolean</code> for each autocomplete
* row. No row will not be considered disabled if this method is not
* implemented.
*
* @param {Object} matcher A data source and row matcher, implements
* <code>requestMatchingRows(token, maxMatches, matchCallback)</code>.
* @param {goog.events.EventTarget} renderer An object that implements
* <code>
* isVisible():boolean<br>
* renderRows(rows:Array, token:string, target:Element);<br>
* hiliteId(row-id:number);<br>
* dismiss();<br>
* dispose():
* </code>.
* @param {Object} selectionHandler An object that implements
* <code>
* selectRow(row);<br>
* update(opt_force);
* </code>.
*
* @constructor
* @extends {goog.events.EventTarget}
*/
goog.ui.ac.AutoComplete = function(matcher, renderer, selectionHandler) {
goog.events.EventTarget.call(this);
/**
* A data-source which provides autocomplete suggestions.
*
* TODO(user): Tighten the type to !Object.
*
* @type {Object}
* @protected
* @suppress {underscore}
*/
this.matcher_ = matcher;
/**
* A handler which interacts with the input DOM element (textfield, textarea,
* or richedit).
*
* TODO(user): Tighten the type to !Object.
*
* @type {Object}
* @protected
* @suppress {underscore}
*/
this.selectionHandler_ = selectionHandler;
/**
* A renderer to render/show/highlight/hide the autocomplete menu.
* @type {goog.events.EventTarget}
* @protected
* @suppress {underscore}
*/
this.renderer_ = renderer;
goog.events.listen(
renderer,
[
goog.ui.ac.AutoComplete.EventType.HILITE,
goog.ui.ac.AutoComplete.EventType.SELECT,
goog.ui.ac.AutoComplete.EventType.CANCEL_DISMISS,
goog.ui.ac.AutoComplete.EventType.DISMISS
],
this.handleEvent, false, this);
/**
* Currently typed token which will be used for completion.
* @type {?string}
* @protected
* @suppress {underscore}
*/
this.token_ = null;
/**
* Autocomplete suggestion items.
* @type {Array}
* @protected
* @suppress {underscore}
*/
this.rows_ = [];
/**
* Id of the currently highlighted row.
* @type {number}
* @protected
* @suppress {underscore}
*/
this.hiliteId_ = -1;
/**
* Id of the first row in autocomplete menu. Note that new ids are assigned
* everytime new suggestions are fetched.
*
* TODO(user): Figure out what subclass does with this value
* and whether we should expose a more proper API.
*
* @type {number}
* @protected
* @suppress {underscore}
*/
this.firstRowId_ = 0;
/**
* The target HTML node for displaying.
* @type {Element}
* @protected
* @suppress {underscore}
*/
this.target_ = null;
/**
* The timer id for dismissing autocomplete menu with a delay.
* @type {?number}
* @private
*/
this.dismissTimer_ = null;
/**
* Mapping from text input element to the anchor element. If the
* mapping does not exist, the input element will act as the anchor
* element.
* @type {Object.<Element>}
* @private
*/
this.inputToAnchorMap_ = {};
};
goog.inherits(goog.ui.ac.AutoComplete, goog.events.EventTarget);
/**
* The maximum number of matches that should be returned
* @type {number}
* @private
*/
goog.ui.ac.AutoComplete.prototype.maxMatches_ = 10;
/**
* True iff the first row should automatically be highlighted
* @type {boolean}
* @private
*/
goog.ui.ac.AutoComplete.prototype.autoHilite_ = true;
/**
* True iff the user can unhilight all rows by pressing the up arrow.
* @type {boolean}
* @private
*/
goog.ui.ac.AutoComplete.prototype.allowFreeSelect_ = false;
/**
* True iff item selection should wrap around from last to first. If
* allowFreeSelect_ is on in conjunction, there is a step of free selection
* before wrapping.
* @type {boolean}
* @private
*/
goog.ui.ac.AutoComplete.prototype.wrap_ = false;
/**
* Whether completion from suggestion triggers fetching new suggestion.
* @type {boolean}
* @private
*/
goog.ui.ac.AutoComplete.prototype.triggerSuggestionsOnUpdate_ = false;
/**
* Events associated with the autocomplete
* @enum {string}
*/
goog.ui.ac.AutoComplete.EventType = {
/** A row has been highlighted by the renderer */
ROW_HILITE: 'rowhilite',
// Note: The events below are used for internal autocomplete events only and
// should not be used in non-autocomplete code.
/** A row has been mouseovered and should be highlighted by the renderer. */
HILITE: 'hilite',
/** A row has been selected by the renderer */
SELECT: 'select',
/** A dismiss event has occurred */
DISMISS: 'dismiss',
/** Event that cancels a dismiss event */
CANCEL_DISMISS: 'canceldismiss',
/**
* Field value was updated. A row field is included and is non-null when a
* row has been selected. The value of the row typically includes fields:
* contactData and formattedValue as well as a toString function (though none
* of these fields are guaranteed to exist). The row field may be used to
* return custom-type row data.
*/
UPDATE: 'update',
/**
* The list of suggestions has been updated, usually because either the list
* has opened, or because the user has typed another character and the
* suggestions have been updated, or the user has dismissed the autocomplete.
*/
SUGGESTIONS_UPDATE: 'suggestionsupdate'
};
/**
* @return {!Object} The data source providing the `autocomplete
* suggestions.
*/
goog.ui.ac.AutoComplete.prototype.getMatcher = function() {
return goog.asserts.assert(this.matcher_);
};
/**
* Sets the data source providing the autocomplete suggestions.
*
* See constructor documentation for the interface.
*
* @param {!Object} matcher The matcher.
* @protected
*/
goog.ui.ac.AutoComplete.prototype.setMatcher = function(matcher) {
this.matcher_ = matcher;
};
/**
* @return {!Object} The handler used to interact with the input DOM
* element (textfield, textarea, or richedit), e.g. to update the
* input DOM element with selected value.
* @protected
*/
goog.ui.ac.AutoComplete.prototype.getSelectionHandler = function() {
return goog.asserts.assert(this.selectionHandler_);
};
/**
* @return {goog.events.EventTarget} The renderer that
* renders/shows/highlights/hides the autocomplete menu.
* See constructor documentation for the expected renderer API.
*/
goog.ui.ac.AutoComplete.prototype.getRenderer = function() {
return this.renderer_;
};
/**
* Sets the renderer that renders/shows/highlights/hides the autocomplete
* menu.
*
* See constructor documentation for the expected renderer API.
*
* @param {goog.events.EventTarget} renderer The renderer.
* @protected
*/
goog.ui.ac.AutoComplete.prototype.setRenderer = function(renderer) {
this.renderer_ = renderer;
};
/**
* @return {?string} The currently typed token used for completion.
* @protected
*/
goog.ui.ac.AutoComplete.prototype.getToken = function() {
return this.token_;
};
/**
* Sets the current token (without changing the rendered autocompletion).
*
* NOTE(user): This method will likely go away when we figure
* out a better API.
*
* @param {?string} token The new token.
* @protected
*/
goog.ui.ac.AutoComplete.prototype.setTokenInternal = function(token) {
this.token_ = token;
};
/**
* @param {number} index The suggestion index, must be within the
* interval [0, this.getSuggestionCount()).
* @return {Object} The currently suggested item at the given index
* (or null if there is none).
*/
goog.ui.ac.AutoComplete.prototype.getSuggestion = function(index) {
return this.rows_[index];
};
/**
* @return {!Array} The current autocomplete suggestion items.
*/
goog.ui.ac.AutoComplete.prototype.getAllSuggestions = function() {
return goog.asserts.assert(this.rows_);
};
/**
* @return {number} The number of currently suggested items.
*/
goog.ui.ac.AutoComplete.prototype.getSuggestionCount = function() {
return this.rows_.length;
};
/**
* @return {number} The id (not index!) of the currently highlighted row.
*/
goog.ui.ac.AutoComplete.prototype.getHighlightedId = function() {
return this.hiliteId_;
};
/**
* Sets the current highlighted row to the given id (not index). Note
* that this does not change any rendering.
*
* NOTE(user): This method will likely go away when we figure
* out a better API.
*
* @param {number} id The new highlighted row id.
*/
goog.ui.ac.AutoComplete.prototype.setHighlightedIdInternal = function(id) {
this.hiliteId_ = id;
};
/**
* Generic event handler that handles any events this object is listening to.
* @param {goog.events.Event} e Event Object.
*/
goog.ui.ac.AutoComplete.prototype.handleEvent = function(e) {
if (e.target == this.renderer_) {
switch (e.type) {
case goog.ui.ac.AutoComplete.EventType.HILITE:
this.hiliteId(/** @type {number} */ (e.row));
break;
case goog.ui.ac.AutoComplete.EventType.SELECT:
// e.row can be either a valid row number or empty.
var rowId = /** @type {number} */ (e.row);
var index = this.getIndexOfId(rowId);
var row = this.rows_[index];
// Make sure the row selected is not a disabled row.
var rowDisabled = !!row && this.matcher_.isRowDisabled &&
this.matcher_.isRowDisabled(row);
if (rowId && row && !rowDisabled && this.hiliteId_ != rowId) {
// Event target row not currently highlighted - fix the mismatch.
this.hiliteId(rowId);
}
if (!rowDisabled) {
// Note that rowDisabled can be false even if e.row does not
// contain a valid row ID; at least one client depends on us
// proceeding anyway.
this.selectHilited();
}
break;
case goog.ui.ac.AutoComplete.EventType.CANCEL_DISMISS:
this.cancelDelayedDismiss();
break;
case goog.ui.ac.AutoComplete.EventType.DISMISS:
this.dismissOnDelay();
break;
}
}
};
/**
* Sets the max number of matches to fetch from the Matcher.
*
* @param {number} max Max number of matches.
*/
goog.ui.ac.AutoComplete.prototype.setMaxMatches = function(max) {
this.maxMatches_ = max;
};
/**
* Sets whether or not the first row should be highlighted by default.
*
* @param {boolean} autoHilite true iff the first row should be
* highlighted by default.
*/
goog.ui.ac.AutoComplete.prototype.setAutoHilite = function(autoHilite) {
this.autoHilite_ = autoHilite;
};
/**
* Sets whether or not the up/down arrow can unhilite all rows.
*
* @param {boolean} allowFreeSelect true iff the up arrow can unhilite all rows.
*/
goog.ui.ac.AutoComplete.prototype.setAllowFreeSelect =
function(allowFreeSelect) {
this.allowFreeSelect_ = allowFreeSelect;
};
/**
* Sets whether or not selections can wrap around the edges.
*
* @param {boolean} wrap true iff sections should wrap around the edges.
*/
goog.ui.ac.AutoComplete.prototype.setWrap = function(wrap) {
this.wrap_ = wrap;
};
/**
* Sets whether or not to request new suggestions immediately after completion
* of a suggestion.
*
* @param {boolean} triggerSuggestionsOnUpdate true iff completion should fetch
* new suggestions.
*/
goog.ui.ac.AutoComplete.prototype.setTriggerSuggestionsOnUpdate = function(
triggerSuggestionsOnUpdate) {
this.triggerSuggestionsOnUpdate_ = triggerSuggestionsOnUpdate;
};
/**
* Sets the token to match against. This triggers calls to the Matcher to
* fetch the matches (up to maxMatches), and then it triggers a call to
* <code>renderer.renderRows()</code>.
*
* @param {string} token The string for which to search in the Matcher.
* @param {string=} opt_fullString Optionally, the full string in the input
* field.
*/
goog.ui.ac.AutoComplete.prototype.setToken = function(token, opt_fullString) {
if (this.token_ == token) {
return;
}
this.token_ = token;
this.matcher_.requestMatchingRows(this.token_,
this.maxMatches_, goog.bind(this.matchListener_, this), opt_fullString);
this.cancelDelayedDismiss();
};
/**
* Gets the current target HTML node for displaying autocomplete UI.
* @return {Element} The current target HTML node for displaying autocomplete
* UI.
*/
goog.ui.ac.AutoComplete.prototype.getTarget = function() {
return this.target_;
};
/**
* Sets the current target HTML node for displaying autocomplete UI.
* Can be an implementation specific definition of how to display UI in relation
* to the target node.
* This target will be passed into <code>renderer.renderRows()</code>
*
* @param {Element} target The current target HTML node for displaying
* autocomplete UI.
*/
goog.ui.ac.AutoComplete.prototype.setTarget = function(target) {
this.target_ = target;
};
/**
* @return {boolean} Whether the autocomplete's renderer is open.
*/
goog.ui.ac.AutoComplete.prototype.isOpen = function() {
return this.renderer_.isVisible();
};
/**
* @return {number} Number of rows in the autocomplete.
* @deprecated Use this.getSuggestionCount().
*/
goog.ui.ac.AutoComplete.prototype.getRowCount = function() {
return this.getSuggestionCount();
};
/**
* Moves the hilite to the next non-disabled row.
* Calls renderer.hiliteId() when there's something to do.
* @return {boolean} Returns true on a successful hilite.
*/
goog.ui.ac.AutoComplete.prototype.hiliteNext = function() {
var lastId = this.firstRowId_ + this.rows_.length - 1;
var toHilite = this.hiliteId_;
// Hilite the next row, skipping any disabled rows.
for (var i = 0; i < this.rows_.length; i++) {
// Increment to the next row.
if (toHilite >= this.firstRowId_ && toHilite < lastId) {
toHilite++;
} else if (toHilite == -1) {
toHilite = this.firstRowId_;
} else if (this.allowFreeSelect_ && toHilite == lastId) {
this.hiliteId(-1);
return false;
} else if (this.wrap_ && toHilite == lastId) {
toHilite = this.firstRowId_;
} else {
return false;
}
if (this.hiliteId(toHilite)) {
return true;
}
}
return false;
};
/**
* Moves the hilite to the previous non-disabled row. Calls
* renderer.hiliteId() when there's something to do.
* @return {boolean} Returns true on a successful hilite.
*/
goog.ui.ac.AutoComplete.prototype.hilitePrev = function() {
var lastId = this.firstRowId_ + this.rows_.length - 1;
var toHilite = this.hiliteId_;
// Hilite the previous row, skipping any disabled rows.
for (var i = 0; i < this.rows_.length; i++) {
// Decrement to the previous row.
if (toHilite > this.firstRowId_) {
toHilite--;
} else if (this.allowFreeSelect_ && toHilite == this.firstRowId_) {
this.hiliteId(-1);
return false;
} else if (this.wrap_ && (toHilite == -1 || toHilite == this.firstRowId_)) {
toHilite = lastId;
} else {
return false;
}
if (this.hiliteId(toHilite)) {
return true;
}
}
return false;
};
/**
* Hilites the id if it's valid and the row is not disabled, otherwise does
* nothing.
* @param {number} id A row id (not index).
* @return {boolean} Whether the id was hilited. Returns false if the row is
* disabled.
*/
goog.ui.ac.AutoComplete.prototype.hiliteId = function(id) {
var index = this.getIndexOfId(id);
var row = this.rows_[index];
var rowDisabled = !!row && this.matcher_.isRowDisabled &&
this.matcher_.isRowDisabled(row);
if (!rowDisabled) {
this.hiliteId_ = id;
this.renderer_.hiliteId(id);
return index != -1;
}
return false;
};
/**
* Hilites the index, if it's valid and the row is not disabled, otherwise does
* nothing.
* @param {number} index The row's index.
* @return {boolean} Whether the index was hilited.
*/
goog.ui.ac.AutoComplete.prototype.hiliteIndex = function(index) {
return this.hiliteId(this.getIdOfIndex_(index));
};
/**
* If there are any current matches, this passes the hilited row data to
* <code>selectionHandler.selectRow()</code>
* @return {boolean} Whether there are any current matches.
*/
goog.ui.ac.AutoComplete.prototype.selectHilited = function() {
var index = this.getIndexOfId(this.hiliteId_);
if (index != -1) {
var selectedRow = this.rows_[index];
var suppressUpdate = this.selectionHandler_.selectRow(selectedRow);
if (this.triggerSuggestionsOnUpdate_) {
this.token_ = null;
this.dismissOnDelay();
} else {
this.dismiss();
}
if (!suppressUpdate) {
this.dispatchEvent({
type: goog.ui.ac.AutoComplete.EventType.UPDATE,
row: selectedRow
});
if (this.triggerSuggestionsOnUpdate_) {
this.selectionHandler_.update(true);
}
}
return true;
} else {
this.dismiss();
this.dispatchEvent(
{
type: goog.ui.ac.AutoComplete.EventType.UPDATE,
row: null
});
return false;
}
};
/**
* Returns whether or not the autocomplete is open and has a highlighted row.
* @return {boolean} Whether an autocomplete row is highlighted.
*/
goog.ui.ac.AutoComplete.prototype.hasHighlight = function() {
return this.isOpen() && this.getIndexOfId(this.hiliteId_) != -1;
};
/**
* Clears out the token, rows, and hilite, and calls
* <code>renderer.dismiss()</code>
*/
goog.ui.ac.AutoComplete.prototype.dismiss = function() {
this.hiliteId_ = -1;
this.token_ = null;
this.firstRowId_ += this.rows_.length;
this.rows_ = [];
window.clearTimeout(this.dismissTimer_);
this.dismissTimer_ = null;
this.renderer_.dismiss();
this.dispatchEvent(goog.ui.ac.AutoComplete.EventType.SUGGESTIONS_UPDATE);
this.dispatchEvent(goog.ui.ac.AutoComplete.EventType.DISMISS);
};
/**
* Call a dismiss after a delay, if there's already a dismiss active, ignore.
*/
goog.ui.ac.AutoComplete.prototype.dismissOnDelay = function() {
if (!this.dismissTimer_) {
this.dismissTimer_ = window.setTimeout(goog.bind(this.dismiss, this), 100);
}
};
/**
* Cancels any delayed dismiss events immediately.
* @return {boolean} Whether a delayed dismiss was cancelled.
* @private
*/
goog.ui.ac.AutoComplete.prototype.immediatelyCancelDelayedDismiss_ =
function() {
if (this.dismissTimer_) {
window.clearTimeout(this.dismissTimer_);
this.dismissTimer_ = null;
return true;
}
return false;
};
/**
* Cancel the active delayed dismiss if there is one.
*/
goog.ui.ac.AutoComplete.prototype.cancelDelayedDismiss = function() {
// Under certain circumstances a cancel event occurs immediately prior to a
// delayedDismiss event that it should be cancelling. To handle this situation
// properly, a timer is used to stop that event.
// Using only the timer creates undesirable behavior when the cancel occurs
// less than 10ms before the delayed dismiss timout ends. If that happens the
// clearTimeout() will occur too late and have no effect.
if (!this.immediatelyCancelDelayedDismiss_()) {
window.setTimeout(goog.bind(this.immediatelyCancelDelayedDismiss_, this),
10);
}
};
/** @override */
goog.ui.ac.AutoComplete.prototype.disposeInternal = function() {
goog.ui.ac.AutoComplete.superClass_.disposeInternal.call(this);
delete this.inputToAnchorMap_;
this.renderer_.dispose();
this.selectionHandler_.dispose();
this.matcher_ = null;
};
/**
* Callback passed to Matcher when requesting matches for a token.
* This might be called synchronously, or asynchronously, or both, for
* any implementation of a Matcher.
* If the Matcher calls this back, with the same token this AutoComplete
* has set currently, then this will package the matching rows in object
* of the form
* <pre>
* {
* id: an integer ID unique to this result set and AutoComplete instance,
* data: the raw row data from Matcher
* }
* </pre>
*
* @param {string} matchedToken Token that corresponds with the rows.
* @param {!Array} rows Set of data that match the given token.
* @param {(boolean|goog.ui.ac.RenderOptions)=} opt_options If true,
* keeps the currently hilited (by index) element hilited. If false not.
* Otherwise a RenderOptions object.
* @private
*/
goog.ui.ac.AutoComplete.prototype.matchListener_ =
function(matchedToken, rows, opt_options) {
if (this.token_ != matchedToken) {
// Matcher's response token doesn't match current token.
// This is probably an async response that came in after
// the token was changed, so don't do anything.
return;
}
this.renderRows(rows, opt_options);
};
/**
* Renders the rows and adds highlighting.
* @param {!Array} rows Set of data that match the given token.
* @param {(boolean|goog.ui.ac.RenderOptions)=} opt_options If true,
* keeps the currently hilited (by index) element hilited. If false not.
* Otherwise a RenderOptions object.
*/
goog.ui.ac.AutoComplete.prototype.renderRows = function(rows, opt_options) {
// The optional argument should be a RenderOptions object. It can be a
// boolean for backwards compatibility, defaulting to false.
var optionsObj = goog.typeOf(opt_options) == 'object' && opt_options;
var preserveHilited =
optionsObj ? optionsObj.getPreserveHilited() : opt_options;
var indexToHilite = preserveHilited ? this.getIndexOfId(this.hiliteId_) : -1;
// Current token matches the matcher's response token.
this.firstRowId_ += this.rows_.length;
this.rows_ = rows;
var rendRows = [];
for (var i = 0; i < rows.length; ++i) {
rendRows.push({
id: this.getIdOfIndex_(i),
data: rows[i]
});
}
var anchor = null;
if (this.target_) {
anchor = this.inputToAnchorMap_[goog.getUid(this.target_)] || this.target_;
}
this.renderer_.setAnchorElement(anchor);
this.renderer_.renderRows(rendRows, this.token_, this.target_);
var autoHilite = this.autoHilite_;
if (optionsObj && optionsObj.getAutoHilite() !== undefined) {
autoHilite = optionsObj.getAutoHilite();
}
this.hiliteId_ = -1;
if ((autoHilite || indexToHilite >= 0) &&
rendRows.length != 0 &&
this.token_) {
if (indexToHilite >= 0) {
this.hiliteId(this.getIdOfIndex_(indexToHilite));
} else {
// Hilite the first non-disabled row.
this.hiliteNext();
}
}
this.dispatchEvent(goog.ui.ac.AutoComplete.EventType.SUGGESTIONS_UPDATE);
};
/**
* Gets the index corresponding to a particular id.
* @param {number} id A unique id for the row.
* @return {number} A valid index into rows_, or -1 if the id is invalid.
* @protected
*/
goog.ui.ac.AutoComplete.prototype.getIndexOfId = function(id) {
var index = id - this.firstRowId_;
if (index < 0 || index >= this.rows_.length) {
return -1;
}
return index;
};
/**
* Gets the id corresponding to a particular index. (Does no checking.)
* @param {number} index The index of a row in the result set.
* @return {number} The id that currently corresponds to that index.
* @private
*/
goog.ui.ac.AutoComplete.prototype.getIdOfIndex_ = function(index) {
return this.firstRowId_ + index;
};
/**
* Attach text areas or input boxes to the autocomplete by DOM reference. After
* elements are attached to the autocomplete, when a user types they will see
* the autocomplete drop down.
* @param {...Element} var_args Variable args: Input or text area elements to
* attach the autocomplete too.
*/
goog.ui.ac.AutoComplete.prototype.attachInputs = function(var_args) {
// Delegate to the input handler
var inputHandler = /** @type {goog.ui.ac.InputHandler} */
(this.selectionHandler_);
inputHandler.attachInputs.apply(inputHandler, arguments);
};
/**
* Detach text areas or input boxes to the autocomplete by DOM reference.
* @param {...Element} var_args Variable args: Input or text area elements to
* detach from the autocomplete.
*/
goog.ui.ac.AutoComplete.prototype.detachInputs = function(var_args) {
// Delegate to the input handler
var inputHandler = /** @type {goog.ui.ac.InputHandler} */
(this.selectionHandler_);
inputHandler.detachInputs.apply(inputHandler, arguments);
// Remove mapping from input to anchor if one exists.
goog.array.forEach(arguments, function(input) {
goog.object.remove(this.inputToAnchorMap_, goog.getUid(input));
}, this);
};
/**
* Attaches the autocompleter to a text area or text input element
* with an anchor element. The anchor element is the element the
* autocomplete box will be positioned against.
* @param {Element} inputElement The input element. May be 'textarea',
* text 'input' element, or any other element that exposes similar
* interface.
* @param {Element} anchorElement The anchor element.
*/
goog.ui.ac.AutoComplete.prototype.attachInputWithAnchor = function(
inputElement, anchorElement) {
this.inputToAnchorMap_[goog.getUid(inputElement)] = anchorElement;
this.attachInputs(inputElement);
};
/**
* Forces an update of the display.
* @param {boolean=} opt_force Whether to force an update.
*/
goog.ui.ac.AutoComplete.prototype.update = function(opt_force) {
var inputHandler = /** @type {goog.ui.ac.InputHandler} */
(this.selectionHandler_);
inputHandler.update(opt_force);
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,114 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Factory class to create a simple autocomplete that will match
* from an array of data provided via ajax.
*
* @see ../../demos/autocompleteremote.html
*/
goog.provide('goog.ui.ac.Remote');
goog.require('goog.ui.ac.AutoComplete');
goog.require('goog.ui.ac.InputHandler');
goog.require('goog.ui.ac.RemoteArrayMatcher');
goog.require('goog.ui.ac.Renderer');
/**
* Factory class for building a remote autocomplete widget that autocompletes
* an inputbox or text area from a data array provided via ajax.
* @param {string} url The Uri which generates the auto complete matches.
* @param {Element} input Input element or text area.
* @param {boolean=} opt_multi Whether to allow multiple entries; defaults
* to false.
* @param {boolean=} opt_useSimilar Whether to use similar matches; e.g.
* "gost" => "ghost".
* @constructor
* @extends {goog.ui.ac.AutoComplete}
*/
goog.ui.ac.Remote = function(url, input, opt_multi, opt_useSimilar) {
var matcher = new goog.ui.ac.RemoteArrayMatcher(url, !opt_useSimilar);
this.matcher_ = matcher;
var renderer = new goog.ui.ac.Renderer();
var inputhandler = new goog.ui.ac.InputHandler(null, null, !!opt_multi, 300);
goog.ui.ac.AutoComplete.call(this, matcher, renderer, inputhandler);
inputhandler.attachAutoComplete(this);
inputhandler.attachInputs(input);
};
goog.inherits(goog.ui.ac.Remote, goog.ui.ac.AutoComplete);
/**
* Set whether or not standard highlighting should be used when rendering rows.
* @param {boolean} useStandardHighlighting true if standard highlighting used.
*/
goog.ui.ac.Remote.prototype.setUseStandardHighlighting =
function(useStandardHighlighting) {
this.renderer_.setUseStandardHighlighting(useStandardHighlighting);
};
/**
* Gets the attached InputHandler object.
* @return {goog.ui.ac.InputHandler} The input handler.
*/
goog.ui.ac.Remote.prototype.getInputHandler = function() {
return /** @type {goog.ui.ac.InputHandler} */ (
this.selectionHandler_);
};
/**
* Set the send method ("GET", "POST") for the matcher.
* @param {string} method The send method; default: GET.
*/
goog.ui.ac.Remote.prototype.setMethod = function(method) {
this.matcher_.setMethod(method);
};
/**
* Set the post data for the matcher.
* @param {string} content Post data.
*/
goog.ui.ac.Remote.prototype.setContent = function(content) {
this.matcher_.setContent(content);
};
/**
* Set the HTTP headers for the matcher.
* @param {Object|goog.structs.Map} headers Map of headers to add to the
* request.
*/
goog.ui.ac.Remote.prototype.setHeaders = function(headers) {
this.matcher_.setHeaders(headers);
};
/**
* Set the timeout interval for the matcher.
* @param {number} interval Number of milliseconds after which an
* incomplete request will be aborted; 0 means no timeout is set.
*/
goog.ui.ac.Remote.prototype.setTimeoutInterval = function(interval) {
this.matcher_.setTimeoutInterval(interval);
};
@@ -0,0 +1,271 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Class that retrieves autocomplete matches via an ajax call.
*
*/
goog.provide('goog.ui.ac.RemoteArrayMatcher');
goog.require('goog.Disposable');
goog.require('goog.Uri');
goog.require('goog.events');
goog.require('goog.json');
goog.require('goog.net.EventType');
goog.require('goog.net.XhrIo');
/**
* An array matcher that requests matches via ajax.
* @param {string} url The Uri which generates the auto complete matches. The
* search term is passed to the server as the 'token' query param.
* @param {boolean=} opt_noSimilar If true, request that the server does not do
* similarity matches for the input token against the dictionary.
* The value is sent to the server as the 'use_similar' query param which is
* either "1" (opt_noSimilar==false) or "0" (opt_noSimilar==true).
* @constructor
* @extends {goog.Disposable}
*/
goog.ui.ac.RemoteArrayMatcher = function(url, opt_noSimilar) {
goog.Disposable.call(this);
/**
* The base URL for the ajax call. The token and max_matches are added as
* query params.
* @type {string}
* @private
*/
this.url_ = url;
/**
* Whether similar matches should be found as well. This is sent as a hint
* to the server only.
* @type {boolean}
* @private
*/
this.useSimilar_ = !opt_noSimilar;
/**
* The XhrIo object used for making remote requests. When a new request
* is made, the current one is aborted and the new one sent.
* @type {goog.net.XhrIo}
* @private
*/
this.xhr_ = new goog.net.XhrIo();
};
goog.inherits(goog.ui.ac.RemoteArrayMatcher, goog.Disposable);
/**
* The HTTP send method (GET, POST) to use when making the ajax call.
* @type {string}
* @private
*/
goog.ui.ac.RemoteArrayMatcher.prototype.method_ = 'GET';
/**
* Data to submit during a POST.
* @type {string|undefined}
* @private
*/
goog.ui.ac.RemoteArrayMatcher.prototype.content_ = undefined;
/**
* Headers to send with every HTTP request.
* @type {Object|goog.structs.Map}
* @private
*/
goog.ui.ac.RemoteArrayMatcher.prototype.headers_ = null;
/**
* Key to the listener on XHR. Used to clear previous listeners.
* @type {goog.events.Key}
* @private
*/
goog.ui.ac.RemoteArrayMatcher.prototype.lastListenerKey_ = null;
/**
* Set the send method ("GET", "POST").
* @param {string} method The send method; default: GET.
*/
goog.ui.ac.RemoteArrayMatcher.prototype.setMethod = function(method) {
this.method_ = method;
};
/**
* Set the post data.
* @param {string} content Post data.
*/
goog.ui.ac.RemoteArrayMatcher.prototype.setContent = function(content) {
this.content_ = content;
};
/**
* Set the HTTP headers.
* @param {Object|goog.structs.Map} headers Map of headers to add to the
* request.
*/
goog.ui.ac.RemoteArrayMatcher.prototype.setHeaders = function(headers) {
this.headers_ = headers;
};
/**
* Set the timeout interval.
* @param {number} interval Number of milliseconds after which an
* incomplete request will be aborted; 0 means no timeout is set.
*/
goog.ui.ac.RemoteArrayMatcher.prototype.setTimeoutInterval =
function(interval) {
this.xhr_.setTimeoutInterval(interval);
};
/**
* Builds a complete GET-style URL, given the base URI and autocomplete related
* parameter values.
* <b>Override this to build any customized lookup URLs.</b>
* <b>Can be used to change request method and any post content as well.</b>
* @param {string} uri The base URI of the request target.
* @param {string} token Current token in autocomplete.
* @param {number} maxMatches Maximum number of matches required.
* @param {boolean} useSimilar A hint to the server.
* @param {string=} opt_fullString Complete text in the input element.
* @return {?string} The complete url. Return null if no request should be sent.
* @protected
*/
goog.ui.ac.RemoteArrayMatcher.prototype.buildUrl = function(uri,
token, maxMatches, useSimilar, opt_fullString) {
var url = new goog.Uri(uri);
url.setParameterValue('token', token);
url.setParameterValue('max_matches', String(maxMatches));
url.setParameterValue('use_similar', String(Number(useSimilar)));
return url.toString();
};
/**
* Returns whether the suggestions should be updated?
* <b>Override this to prevent updates eg - when token is empty.</b>
* @param {string} uri The base URI of the request target.
* @param {string} token Current token in autocomplete.
* @param {number} maxMatches Maximum number of matches required.
* @param {boolean} useSimilar A hint to the server.
* @param {string=} opt_fullString Complete text in the input element.
* @return {boolean} Whether new matches be requested.
* @protected
*/
goog.ui.ac.RemoteArrayMatcher.prototype.shouldRequestMatches =
function(uri, token, maxMatches, useSimilar, opt_fullString) {
return true;
};
/**
* Parses and retrieves the array of suggestions from XHR response.
* <b>Override this if the response is not a simple JSON array.</b>
* @param {string} responseText The XHR response text.
* @return {Array.<string>} The array of suggestions.
* @protected
*/
goog.ui.ac.RemoteArrayMatcher.prototype.parseResponseText = function(
responseText) {
var matches = [];
// If there is no response text, unsafeParse will throw a syntax error.
if (responseText) {
/** @preserveTry */
try {
matches = goog.json.unsafeParse(responseText);
} catch (exception) {
}
}
return /** @type {Array.<string>} */ (matches);
};
/**
* Handles the XHR response.
* @param {string} token The XHR autocomplete token.
* @param {Function} matchHandler The AutoComplete match handler.
* @param {goog.events.Event} event The XHR success event.
*/
goog.ui.ac.RemoteArrayMatcher.prototype.xhrCallback = function(token,
matchHandler, event) {
var text = event.target.getResponseText();
matchHandler(token, this.parseResponseText(text));
};
/**
* Retrieve a set of matching rows from the server via ajax.
* @param {string} token The text that should be matched; passed to the server
* as the 'token' query param.
* @param {number} maxMatches The maximum number of matches requested from the
* server; passed as the 'max_matches' query param. The server is
* responsible for limiting the number of matches that are returned.
* @param {Function} matchHandler Callback to execute on the result after
* matching.
* @param {string=} opt_fullString The full string from the input box.
*/
goog.ui.ac.RemoteArrayMatcher.prototype.requestMatchingRows =
function(token, maxMatches, matchHandler, opt_fullString) {
if (!this.shouldRequestMatches(this.url_, token, maxMatches, this.useSimilar_,
opt_fullString)) {
return;
}
// Set the query params on the URL.
var url = this.buildUrl(this.url_, token, maxMatches, this.useSimilar_,
opt_fullString);
if (!url) {
// Do nothing if there is no URL.
return;
}
// The callback evals the server response and calls the match handler on
// the array of matches.
var callback = goog.bind(this.xhrCallback, this, token, matchHandler);
// Abort the current request and issue the new one; prevent requests from
// being queued up by the browser with a slow server
if (this.xhr_.isActive()) {
this.xhr_.abort();
}
// This ensures if previous XHR is aborted or ends with error, the
// corresponding success-callbacks are cleared.
if (this.lastListenerKey_) {
goog.events.unlistenByKey(this.lastListenerKey_);
}
// Listen once ensures successful callback gets cleared by itself.
this.lastListenerKey_ = goog.events.listenOnce(this.xhr_,
goog.net.EventType.SUCCESS, callback);
this.xhr_.send(url, this.method_, this.content_, this.headers_);
};
/** @override */
goog.ui.ac.RemoteArrayMatcher.prototype.disposeInternal = function() {
this.xhr_.dispose();
goog.ui.ac.RemoteArrayMatcher.superClass_.disposeInternal.call(
this);
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,80 @@
// Copyright 2012 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Options for rendering matches.
*
*/
goog.provide('goog.ui.ac.RenderOptions');
/**
* A simple class that contains options for rendering a set of autocomplete
* matches. Used as an optional argument in the callback from the matcher.
* @constructor
*/
goog.ui.ac.RenderOptions = function() {
};
/**
* Whether the current highlighting is to be preserved when displaying the new
* set of matches.
* @type {boolean}
* @private
*/
goog.ui.ac.RenderOptions.prototype.preserveHilited_ = false;
/**
* Whether the first match is to be highlighted. When undefined the autoHilite
* flag of the autocomplete is used.
* @type {boolean|undefined}
* @private
*/
goog.ui.ac.RenderOptions.prototype.autoHilite_;
/**
* @param {boolean} flag The new value for the preserveHilited_ flag.
*/
goog.ui.ac.RenderOptions.prototype.setPreserveHilited = function(flag) {
this.preserveHilited_ = flag;
};
/**
* @return {boolean} The value of the preserveHilited_ flag.
*/
goog.ui.ac.RenderOptions.prototype.getPreserveHilited = function() {
return this.preserveHilited_;
};
/**
* @param {boolean} flag The new value for the autoHilite_ flag.
*/
goog.ui.ac.RenderOptions.prototype.setAutoHilite = function(flag) {
this.autoHilite_ = flag;
};
/**
* @return {boolean|undefined} The value of the autoHilite_ flag.
*/
goog.ui.ac.RenderOptions.prototype.getAutoHilite = function() {
return this.autoHilite_;
};
@@ -0,0 +1,58 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Class for managing the interactions between a rich autocomplete
* object and a text-input or textarea.
*
*/
goog.provide('goog.ui.ac.RichInputHandler');
goog.require('goog.ui.ac.InputHandler');
/**
* Class for managing the interaction between an autocomplete object and a
* text-input or textarea.
* @param {?string=} opt_separators Seperators to split multiple entries.
* @param {?string=} opt_literals Characters used to delimit text literals.
* @param {?boolean=} opt_multi Whether to allow multiple entries
* (Default: true).
* @param {?number=} opt_throttleTime Number of milliseconds to throttle
* keyevents with (Default: 150).
* @constructor
* @extends {goog.ui.ac.InputHandler}
*/
goog.ui.ac.RichInputHandler = function(opt_separators, opt_literals,
opt_multi, opt_throttleTime) {
goog.ui.ac.InputHandler.call(this, opt_separators, opt_literals,
opt_multi, opt_throttleTime);
};
goog.inherits(goog.ui.ac.RichInputHandler, goog.ui.ac.InputHandler);
/**
* Selects the given rich row. The row's select(target) method is called.
* @param {Object} row The row to select.
* @return {boolean} Whether to suppress the update event.
* @override
*/
goog.ui.ac.RichInputHandler.prototype.selectRow = function(row) {
var suppressUpdate = goog.ui.ac.RichInputHandler.superClass_
.selectRow.call(this, row);
row.select(this.ac_.getTarget());
return suppressUpdate;
};
@@ -0,0 +1,107 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Factory class to create a rich autocomplete that will match
* from an array of data provided via ajax. The server returns a complex data
* structure that is used with client-side javascript functions to render the
* results.
*
* The server sends a list of the form:
* [["type1", {...}, {...}, ...], ["type2", {...}, {...}, ...], ...]
* The first element of each sublist is a string designating the type of the
* hashes in the sublist, each of which represents one match. The type string
* must be the name of a function(item) which converts the hash into a rich
* row that contains both a render(node, token) and a select(target) method.
* The render method is called by the renderer when rendering the rich row,
* and the select method is called by the RichInputHandler when the rich row is
* selected.
*
* @see ../../demos/autocompleterichremote.html
*/
goog.provide('goog.ui.ac.RichRemote');
goog.require('goog.ui.ac.AutoComplete');
goog.require('goog.ui.ac.Remote');
goog.require('goog.ui.ac.Renderer');
goog.require('goog.ui.ac.RichInputHandler');
goog.require('goog.ui.ac.RichRemoteArrayMatcher');
/**
* Factory class to create a rich autocomplete widget that autocompletes an
* inputbox or textarea from data provided via ajax. The server returns a
* complex data structure that is used with client-side javascript functions to
* render the results.
* @param {string} url The Uri which generates the auto complete matches.
* @param {Element} input Input element or text area.
* @param {boolean=} opt_multi Whether to allow multiple entries; defaults
* to false.
* @param {boolean=} opt_useSimilar Whether to use similar matches; e.g.
* "gost" => "ghost".
* @constructor
* @extends {goog.ui.ac.Remote}
*/
goog.ui.ac.RichRemote = function(url, input, opt_multi, opt_useSimilar) {
// Create a custom renderer that renders rich rows. The renderer calls
// row.render(node, token) for each row.
var customRenderer = {};
customRenderer.renderRow = function(row, token, node) {
return row.data.render(node, token);
};
/**
* A standard renderer that uses a custom row renderer to display the
* rich rows generated by this autocomplete widget.
* @type {goog.ui.ac.Renderer}
* @private
*/
var renderer = new goog.ui.ac.Renderer(null, customRenderer);
this.renderer_ = renderer;
/**
* A remote matcher that parses rich results returned by the server.
* @type {goog.ui.ac.RichRemoteArrayMatcher}
* @private
*/
var matcher = new goog.ui.ac.RichRemoteArrayMatcher(url,
!opt_useSimilar);
this.matcher_ = matcher;
/**
* An input handler that calls select on a row when it is selected.
* @type {goog.ui.ac.RichInputHandler}
* @private
*/
var inputhandler = new goog.ui.ac.RichInputHandler(null, null,
!!opt_multi, 300);
// Create the widget and connect it to the input handler.
goog.ui.ac.AutoComplete.call(this, matcher, renderer, inputhandler);
inputhandler.attachAutoComplete(this);
inputhandler.attachInputs(input);
};
goog.inherits(goog.ui.ac.RichRemote, goog.ui.ac.Remote);
/**
* Set the filter that is called before the array matches are returned.
* @param {Function} rowFilter A function(rows) that returns an array of rows as
* a subset of the rows input array.
*/
goog.ui.ac.RichRemote.prototype.setRowFilter = function(rowFilter) {
this.matcher_.setRowFilter(rowFilter);
};
@@ -0,0 +1,125 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Class that retrieves rich autocomplete matches, represented as
* a structured list of lists, via an ajax call. The first element of each
* sublist is the name of a client-side javascript function that converts the
* remaining sublist elements into rich rows.
*
*/
goog.provide('goog.ui.ac.RichRemoteArrayMatcher');
goog.require('goog.json');
goog.require('goog.ui.ac.RemoteArrayMatcher');
/**
* An array matcher that requests rich matches via ajax and converts them into
* rich rows.
* @param {string} url The Uri which generates the auto complete matches. The
* search term is passed to the server as the 'token' query param.
* @param {boolean=} opt_noSimilar If true, request that the server does not do
* similarity matches for the input token against the dictionary.
* The value is sent to the server as the 'use_similar' query param which is
* either "1" (opt_noSimilar==false) or "0" (opt_noSimilar==true).
* @constructor
* @extends {goog.ui.ac.RemoteArrayMatcher}
*/
goog.ui.ac.RichRemoteArrayMatcher = function(url, opt_noSimilar) {
goog.ui.ac.RemoteArrayMatcher.call(this, url, opt_noSimilar);
/**
* A function(rows) that is called before the array matches are returned.
* It runs client-side and filters the results given by the server before
* being rendered by the client.
* @type {Function}
* @private
*/
this.rowFilter_ = null;
};
goog.inherits(goog.ui.ac.RichRemoteArrayMatcher, goog.ui.ac.RemoteArrayMatcher);
/**
* Set the filter that is called before the array matches are returned.
* @param {Function} rowFilter A function(rows) that returns an array of rows as
* a subset of the rows input array.
*/
goog.ui.ac.RichRemoteArrayMatcher.prototype.setRowFilter = function(rowFilter) {
this.rowFilter_ = rowFilter;
};
/**
* Retrieve a set of matching rows from the server via ajax and convert them
* into rich rows.
* @param {string} token The text that should be matched; passed to the server
* as the 'token' query param.
* @param {number} maxMatches The maximum number of matches requested from the
* server; passed as the 'max_matches' query param. The server is
* responsible for limiting the number of matches that are returned.
* @param {Function} matchHandler Callback to execute on the result after
* matching.
* @override
*/
goog.ui.ac.RichRemoteArrayMatcher.prototype.requestMatchingRows =
function(token, maxMatches, matchHandler) {
// The RichRemoteArrayMatcher must map over the results and filter them
// before calling the request matchHandler. This is done by passing
// myMatchHandler to RemoteArrayMatcher.requestMatchingRows which maps,
// filters, and then calls matchHandler.
var myMatchHandler = goog.bind(function(token, matches) {
/** @preserveTry */
try {
var rows = [];
for (var i = 0; i < matches.length; i++) {
var func = /** @type {!Function} */
(goog.json.unsafeParse(matches[i][0]));
for (var j = 1; j < matches[i].length; j++) {
var richRow = func(matches[i][j]);
rows.push(richRow);
// If no render function was provided, set the node's innerHTML.
if (typeof richRow.render == 'undefined') {
richRow.render = function(node, token) {
node.innerHTML = richRow.toString();
};
}
// If no select function was provided, set the text of the input.
if (typeof richRow.select == 'undefined') {
richRow.select = function(target) {
target.value = richRow.toString();
};
}
}
}
if (this.rowFilter_) {
rows = this.rowFilter_(rows);
}
matchHandler(token, rows);
} catch (exception) {
// TODO(user): Is this what we want?
matchHandler(token, []);
}
}, this);
// Call the super's requestMatchingRows with myMatchHandler
goog.ui.ac.RichRemoteArrayMatcher.superClass_
.requestMatchingRows.call(this, token, maxMatches, myMatchHandler);
};