Update wmts-hidpi, add nicer-api-docs
This commit is contained in:
File diff suppressed because it is too large
Load Diff
50
nicer-api-docs/closure-library/closure/goog/ui/ac/ac.js
Normal file
50
nicer-api-docs/closure-library/closure/goog/ui/ac/ac.js
Normal file
@@ -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);
|
||||
};
|
||||
1308
nicer-api-docs/closure-library/closure/goog/ui/ac/inputhandler.js
Normal file
1308
nicer-api-docs/closure-library/closure/goog/ui/ac/inputhandler.js
Normal file
File diff suppressed because it is too large
Load Diff
114
nicer-api-docs/closure-library/closure/goog/ui/ac/remote.js
Normal file
114
nicer-api-docs/closure-library/closure/goog/ui/ac/remote.js
Normal file
@@ -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);
|
||||
};
|
||||
1023
nicer-api-docs/closure-library/closure/goog/ui/ac/renderer.js
Normal file
1023
nicer-api-docs/closure-library/closure/goog/ui/ac/renderer.js
Normal file
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;
|
||||
};
|
||||
107
nicer-api-docs/closure-library/closure/goog/ui/ac/richremote.js
Normal file
107
nicer-api-docs/closure-library/closure/goog/ui/ac/richremote.js
Normal file
@@ -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);
|
||||
};
|
||||
@@ -0,0 +1,347 @@
|
||||
// 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 Activity Monitor.
|
||||
*
|
||||
* Fires throttled events when a user interacts with the specified document.
|
||||
* This class also exposes the amount of time since the last user event.
|
||||
*
|
||||
* If you would prefer to get BECOME_ACTIVE and BECOME_IDLE events when the
|
||||
* user changes states, then you should use the IdleTimer class instead.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.ActivityMonitor');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.events.EventHandler');
|
||||
goog.require('goog.events.EventTarget');
|
||||
goog.require('goog.events.EventType');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Once initialized with a document, the activity monitor can be queried for
|
||||
* the current idle time.
|
||||
*
|
||||
* @param {goog.dom.DomHelper|Array.<goog.dom.DomHelper>=} opt_domHelper
|
||||
* DomHelper which contains the document(s) to listen to. If null, the
|
||||
* default document is usedinstead.
|
||||
* @param {boolean=} opt_useBubble Whether to use the bubble phase to listen for
|
||||
* events. By default listens on the capture phase so that it won't miss
|
||||
* events that get stopPropagation/cancelBubble'd. However, this can cause
|
||||
* problems in IE8 if the page loads multiple scripts that include the
|
||||
* closure event handling code.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {goog.events.EventTarget}
|
||||
*/
|
||||
goog.ui.ActivityMonitor = function(opt_domHelper, opt_useBubble) {
|
||||
goog.events.EventTarget.call(this);
|
||||
|
||||
/**
|
||||
* Array of documents that are being listened to.
|
||||
* @type {Array.<Document>}
|
||||
* @private
|
||||
*/
|
||||
this.documents_ = [];
|
||||
|
||||
/**
|
||||
* Whether to use the bubble phase to listen for events.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.useBubble_ = !!opt_useBubble;
|
||||
|
||||
/**
|
||||
* The event handler.
|
||||
* @type {goog.events.EventHandler}
|
||||
* @private
|
||||
*/
|
||||
this.eventHandler_ = new goog.events.EventHandler(this);
|
||||
|
||||
/**
|
||||
* Whether the current window is an iframe.
|
||||
* TODO(user): Move to goog.dom.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.isIframe_ = window.parent != window;
|
||||
|
||||
if (!opt_domHelper) {
|
||||
this.addDocument(goog.dom.getDomHelper().getDocument());
|
||||
} else if (goog.isArray(opt_domHelper)) {
|
||||
for (var i = 0; i < opt_domHelper.length; i++) {
|
||||
this.addDocument(opt_domHelper[i].getDocument());
|
||||
}
|
||||
} else {
|
||||
this.addDocument(opt_domHelper.getDocument());
|
||||
}
|
||||
|
||||
/**
|
||||
* The time (in milliseconds) of the last user event.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.lastEventTime_ = goog.now();
|
||||
|
||||
};
|
||||
goog.inherits(goog.ui.ActivityMonitor, goog.events.EventTarget);
|
||||
|
||||
|
||||
/**
|
||||
* The last event type that was detected.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ActivityMonitor.prototype.lastEventType_ = '';
|
||||
|
||||
|
||||
/**
|
||||
* The mouse x-position after the last user event.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ActivityMonitor.prototype.lastMouseX_;
|
||||
|
||||
|
||||
/**
|
||||
* The mouse y-position after the last user event.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ActivityMonitor.prototype.lastMouseY_;
|
||||
|
||||
|
||||
/**
|
||||
* The earliest time that another throttled ACTIVITY event will be dispatched
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ActivityMonitor.prototype.minEventTime_ = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Minimum amount of time in ms between throttled ACTIVITY events
|
||||
* @type {number}
|
||||
*/
|
||||
goog.ui.ActivityMonitor.MIN_EVENT_SPACING = 3 * 1000;
|
||||
|
||||
|
||||
/**
|
||||
* If a user executes one of these events, s/he is considered not idle.
|
||||
* @type {Array.<goog.events.EventType>}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ActivityMonitor.userEventTypesBody_ = [
|
||||
goog.events.EventType.CLICK,
|
||||
goog.events.EventType.DBLCLICK,
|
||||
goog.events.EventType.MOUSEDOWN,
|
||||
goog.events.EventType.MOUSEMOVE,
|
||||
goog.events.EventType.MOUSEUP
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* If a user executes one of these events, s/he is considered not idle.
|
||||
* Note: monitoring touch events within iframe cause problems in iOS.
|
||||
* @type {Array.<goog.events.EventType>}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ActivityMonitor.userTouchEventTypesBody_ = [
|
||||
goog.events.EventType.TOUCHEND,
|
||||
goog.events.EventType.TOUCHMOVE,
|
||||
goog.events.EventType.TOUCHSTART
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* If a user executes one of these events, s/he is considered not idle.
|
||||
* @type {Array.<goog.events.EventType>}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ActivityMonitor.userEventTypesDocuments_ =
|
||||
[goog.events.EventType.KEYDOWN, goog.events.EventType.KEYUP];
|
||||
|
||||
|
||||
/**
|
||||
* Event constants for the activity monitor.
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.ui.ActivityMonitor.Event = {
|
||||
/** Event fired when the user does something interactive */
|
||||
ACTIVITY: 'activity'
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.ActivityMonitor.prototype.disposeInternal = function() {
|
||||
goog.ui.ActivityMonitor.superClass_.disposeInternal.call(this);
|
||||
this.eventHandler_.dispose();
|
||||
this.eventHandler_ = null;
|
||||
delete this.documents_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds a document to those being monitored by this class.
|
||||
*
|
||||
* @param {Document} doc Document to monitor.
|
||||
*/
|
||||
goog.ui.ActivityMonitor.prototype.addDocument = function(doc) {
|
||||
if (goog.array.contains(this.documents_, doc)) {
|
||||
return;
|
||||
}
|
||||
this.documents_.push(doc);
|
||||
var useCapture = !this.useBubble_;
|
||||
|
||||
var eventsToListenTo = goog.array.concat(
|
||||
goog.ui.ActivityMonitor.userEventTypesDocuments_,
|
||||
goog.ui.ActivityMonitor.userEventTypesBody_);
|
||||
|
||||
if (!this.isIframe_) {
|
||||
// Monitoring touch events in iframe causes problems interacting with text
|
||||
// fields in iOS (input text, textarea, contenteditable, select/copy/paste),
|
||||
// so just ignore these events. This shouldn't matter much given that a
|
||||
// touchstart event followed by touchend event produces a click event,
|
||||
// which is being monitored correctly.
|
||||
goog.array.extend(eventsToListenTo,
|
||||
goog.ui.ActivityMonitor.userTouchEventTypesBody_);
|
||||
}
|
||||
|
||||
this.eventHandler_.listen(doc, eventsToListenTo, this.handleEvent_,
|
||||
useCapture);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes a document from those being monitored by this class.
|
||||
*
|
||||
* @param {Document} doc Document to monitor.
|
||||
*/
|
||||
goog.ui.ActivityMonitor.prototype.removeDocument = function(doc) {
|
||||
if (this.isDisposed()) {
|
||||
return;
|
||||
}
|
||||
goog.array.remove(this.documents_, doc);
|
||||
var useCapture = !this.useBubble_;
|
||||
|
||||
var eventsToUnlistenTo = goog.array.concat(
|
||||
goog.ui.ActivityMonitor.userEventTypesDocuments_,
|
||||
goog.ui.ActivityMonitor.userEventTypesBody_);
|
||||
|
||||
if (!this.isIframe_) {
|
||||
// See note above about monitoring touch events in iframe.
|
||||
goog.array.extend(eventsToUnlistenTo,
|
||||
goog.ui.ActivityMonitor.userTouchEventTypesBody_);
|
||||
}
|
||||
|
||||
this.eventHandler_.unlisten(doc, eventsToUnlistenTo, this.handleEvent_,
|
||||
useCapture);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Updates the last event time when a user action occurs.
|
||||
* @param {goog.events.BrowserEvent} e Event object.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ActivityMonitor.prototype.handleEvent_ = function(e) {
|
||||
var update = false;
|
||||
switch (e.type) {
|
||||
case goog.events.EventType.MOUSEMOVE:
|
||||
// In FF 1.5, we get spurious mouseover and mouseout events when the UI
|
||||
// redraws. We only want to update the idle time if the mouse has moved.
|
||||
if (typeof this.lastMouseX_ == 'number' &&
|
||||
this.lastMouseX_ != e.clientX ||
|
||||
typeof this.lastMouseY_ == 'number' &&
|
||||
this.lastMouseY_ != e.clientY) {
|
||||
update = true;
|
||||
}
|
||||
this.lastMouseX_ = e.clientX;
|
||||
this.lastMouseY_ = e.clientY;
|
||||
break;
|
||||
default:
|
||||
update = true;
|
||||
}
|
||||
|
||||
if (update) {
|
||||
var type = goog.asserts.assertString(e.type);
|
||||
this.updateIdleTime(goog.now(), type);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Updates the last event time to be the present time, useful for non-DOM
|
||||
* events that should update idle time.
|
||||
*/
|
||||
goog.ui.ActivityMonitor.prototype.resetTimer = function() {
|
||||
this.updateIdleTime(goog.now(), 'manual');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Updates the idle time and fires an event if time has elapsed since
|
||||
* the last update.
|
||||
* @param {number} eventTime Time (in MS) of the event that cleared the idle
|
||||
* timer.
|
||||
* @param {string} eventType Type of the event, used only for debugging.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.ActivityMonitor.prototype.updateIdleTime = function(
|
||||
eventTime, eventType) {
|
||||
// update internal state noting whether the user was idle
|
||||
this.lastEventTime_ = eventTime;
|
||||
this.lastEventType_ = eventType;
|
||||
|
||||
// dispatch event
|
||||
if (eventTime > this.minEventTime_) {
|
||||
this.dispatchEvent(goog.ui.ActivityMonitor.Event.ACTIVITY);
|
||||
this.minEventTime_ = eventTime + goog.ui.ActivityMonitor.MIN_EVENT_SPACING;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the amount of time the user has been idle.
|
||||
* @param {number=} opt_now The current time can optionally be passed in for the
|
||||
* computation to avoid an extra Date allocation.
|
||||
* @return {number} The amount of time in ms that the user has been idle.
|
||||
*/
|
||||
goog.ui.ActivityMonitor.prototype.getIdleTime = function(opt_now) {
|
||||
var now = opt_now || goog.now();
|
||||
return now - this.lastEventTime_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the type of the last user event.
|
||||
* @return {string} event type.
|
||||
*/
|
||||
goog.ui.ActivityMonitor.prototype.getLastEventType = function() {
|
||||
return this.lastEventType_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the time of the last event
|
||||
* @return {number} last event time.
|
||||
*/
|
||||
goog.ui.ActivityMonitor.prototype.getLastEventTime = function() {
|
||||
return this.lastEventTime_;
|
||||
};
|
||||
@@ -0,0 +1,366 @@
|
||||
// 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 Advanced tooltip widget implementation.
|
||||
*
|
||||
* @author eae@google.com (Emil A Eklund)
|
||||
* @see ../demos/advancedtooltip.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.AdvancedTooltip');
|
||||
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.math.Box');
|
||||
goog.require('goog.math.Coordinate');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.Tooltip');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Advanced tooltip widget with cursor tracking abilities. Works like a regular
|
||||
* tooltip but can track the cursor position and direction to determine if the
|
||||
* tooltip should be dismissed or remain open.
|
||||
*
|
||||
* @param {Element|string=} opt_el Element to display tooltip for, either
|
||||
* element reference or string id.
|
||||
* @param {?string=} opt_str Text message to display in tooltip.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
|
||||
* @constructor
|
||||
* @extends {goog.ui.Tooltip}
|
||||
*/
|
||||
goog.ui.AdvancedTooltip = function(opt_el, opt_str, opt_domHelper) {
|
||||
goog.ui.Tooltip.call(this, opt_el, opt_str, opt_domHelper);
|
||||
};
|
||||
goog.inherits(goog.ui.AdvancedTooltip, goog.ui.Tooltip);
|
||||
|
||||
|
||||
/**
|
||||
* Whether to track the cursor and thereby close the tooltip if it moves away
|
||||
* from the tooltip and keep it open if it moves towards it.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.AdvancedTooltip.prototype.cursorTracking_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* Delay in milliseconds before tooltips are hidden if cursor tracking is
|
||||
* enabled and the cursor is moving away from the tooltip.
|
||||
*
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.AdvancedTooltip.prototype.cursorTrackingHideDelayMs_ = 100;
|
||||
|
||||
|
||||
/**
|
||||
* Box object representing a margin around the tooltip where the cursor is
|
||||
* allowed without dismissing the tooltip.
|
||||
*
|
||||
* @type {goog.math.Box}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.AdvancedTooltip.prototype.hotSpotPadding_;
|
||||
|
||||
|
||||
/**
|
||||
* Bounding box.
|
||||
*
|
||||
* @type {goog.math.Box}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.AdvancedTooltip.prototype.boundingBox_;
|
||||
|
||||
|
||||
/**
|
||||
* Anchor bounding box.
|
||||
*
|
||||
* @type {goog.math.Box}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.AdvancedTooltip.prototype.anchorBox_;
|
||||
|
||||
|
||||
/**
|
||||
* Whether the cursor tracking is active.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.AdvancedTooltip.prototype.tracking_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* Sets margin around the tooltip where the cursor is allowed without dismissing
|
||||
* the tooltip.
|
||||
*
|
||||
* @param {goog.math.Box=} opt_box The margin around the tooltip.
|
||||
*/
|
||||
goog.ui.AdvancedTooltip.prototype.setHotSpotPadding = function(opt_box) {
|
||||
this.hotSpotPadding_ = opt_box || null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {goog.math.Box} box The margin around the tooltip where the cursor is
|
||||
* allowed without dismissing the tooltip.
|
||||
*/
|
||||
goog.ui.AdvancedTooltip.prototype.getHotSpotPadding = function() {
|
||||
return this.hotSpotPadding_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether to track the cursor and thereby close the tooltip if it moves
|
||||
* away from the tooltip and keep it open if it moves towards it.
|
||||
*
|
||||
* @param {boolean} b Whether to track the cursor.
|
||||
*/
|
||||
goog.ui.AdvancedTooltip.prototype.setCursorTracking = function(b) {
|
||||
this.cursorTracking_ = b;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether to track the cursor and thereby close the tooltip
|
||||
* if it moves away from the tooltip and keep it open if it moves towards
|
||||
* it.
|
||||
*/
|
||||
goog.ui.AdvancedTooltip.prototype.getCursorTracking = function() {
|
||||
return this.cursorTracking_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets delay in milliseconds before tooltips are hidden if cursor tracking is
|
||||
* enabled and the cursor is moving away from the tooltip.
|
||||
*
|
||||
* @param {number} delay The delay in milliseconds.
|
||||
*/
|
||||
goog.ui.AdvancedTooltip.prototype.setCursorTrackingHideDelayMs =
|
||||
function(delay) {
|
||||
this.cursorTrackingHideDelayMs_ = delay;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {number} The delay in milliseconds before tooltips are hidden if
|
||||
* cursor tracking is enabled and the cursor is moving away from the
|
||||
* tooltip.
|
||||
*/
|
||||
goog.ui.AdvancedTooltip.prototype.getCursorTrackingHideDelayMs = function() {
|
||||
return this.cursorTrackingHideDelayMs_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Called after the popup is shown.
|
||||
* @protected
|
||||
* @suppress {underscore}
|
||||
* @override
|
||||
*/
|
||||
goog.ui.AdvancedTooltip.prototype.onShow_ = function() {
|
||||
goog.ui.AdvancedTooltip.superClass_.onShow_.call(this);
|
||||
|
||||
this.boundingBox_ = goog.style.getBounds(this.getElement()).toBox();
|
||||
if (this.anchor) {
|
||||
this.anchorBox_ = goog.style.getBounds(this.anchor).toBox();
|
||||
}
|
||||
|
||||
this.tracking_ = this.cursorTracking_;
|
||||
goog.events.listen(this.getDomHelper().getDocument(),
|
||||
goog.events.EventType.MOUSEMOVE,
|
||||
this.handleMouseMove, false, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Called after the popup is hidden.
|
||||
* @protected
|
||||
* @suppress {underscore}
|
||||
* @override
|
||||
*/
|
||||
goog.ui.AdvancedTooltip.prototype.onHide_ = function() {
|
||||
goog.events.unlisten(this.getDomHelper().getDocument(),
|
||||
goog.events.EventType.MOUSEMOVE,
|
||||
this.handleMouseMove, false, this);
|
||||
|
||||
this.boundingBox_ = null;
|
||||
this.anchorBox_ = null;
|
||||
this.tracking_ = false;
|
||||
|
||||
goog.ui.AdvancedTooltip.superClass_.onHide_.call(this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the mouse is in the tooltip.
|
||||
* @return {boolean} True if the mouse is in the tooltip.
|
||||
*/
|
||||
goog.ui.AdvancedTooltip.prototype.isMouseInTooltip = function() {
|
||||
return this.isCoordinateInTooltip(this.cursorPosition);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether the supplied coordinate is inside the tooltip, including
|
||||
* padding if any.
|
||||
* @param {goog.math.Coordinate} coord Coordinate being tested.
|
||||
* @return {boolean} Whether the coord is in the tooltip.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.AdvancedTooltip.prototype.isCoordinateInTooltip = function(coord) {
|
||||
// Check if coord is inside the bounding box of the tooltip
|
||||
if (this.hotSpotPadding_) {
|
||||
var offset = goog.style.getPageOffset(this.getElement());
|
||||
var size = goog.style.getSize(this.getElement());
|
||||
return offset.x - this.hotSpotPadding_.left <= coord.x &&
|
||||
coord.x <= offset.x + size.width + this.hotSpotPadding_.right &&
|
||||
offset.y - this.hotSpotPadding_.top <= coord.y &&
|
||||
coord.y <= offset.y + size.height + this.hotSpotPadding_.bottom;
|
||||
}
|
||||
|
||||
return goog.ui.AdvancedTooltip.superClass_.isCoordinateInTooltip.call(this,
|
||||
coord);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Checks if supplied coordinate is in the tooltip, its triggering anchor, or
|
||||
* a tooltip that has been triggered by a child of this tooltip.
|
||||
* Called from handleMouseMove to determine if hide timer should be started,
|
||||
* and from maybeHide to determine if tooltip should be hidden.
|
||||
* @param {goog.math.Coordinate} coord Coordinate being tested.
|
||||
* @return {boolean} Whether coordinate is in the anchor, the tooltip, or any
|
||||
* tooltip whose anchor is a child of this tooltip.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.AdvancedTooltip.prototype.isCoordinateActive_ = function(coord) {
|
||||
if ((this.anchorBox_ && this.anchorBox_.contains(coord)) ||
|
||||
this.isCoordinateInTooltip(coord)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if mouse might be in active child element.
|
||||
var childTooltip = this.getChildTooltip();
|
||||
return !!childTooltip && childTooltip.isCoordinateInTooltip(coord);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Called by timer from mouse out handler. Hides tooltip if cursor is still
|
||||
* outside element and tooltip.
|
||||
* @param {Element} el Anchor when hide timer was started.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.AdvancedTooltip.prototype.maybeHide = function(el) {
|
||||
this.hideTimer = undefined;
|
||||
if (el == this.anchor) {
|
||||
// Check if cursor is inside the bounding box of the tooltip or the element
|
||||
// that triggered it, or if tooltip is active (possibly due to receiving
|
||||
// the focus), or if there is a nested tooltip being shown.
|
||||
if (!this.isCoordinateActive_(this.cursorPosition) &&
|
||||
!this.getActiveElement() &&
|
||||
!this.hasActiveChild()) {
|
||||
// Under certain circumstances gecko fires ghost mouse events with the
|
||||
// coordinates 0, 0 regardless of the cursors position.
|
||||
if (goog.userAgent.GECKO && this.cursorPosition.x == 0 &&
|
||||
this.cursorPosition.y == 0) {
|
||||
return;
|
||||
}
|
||||
this.setVisible(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler for mouse move events.
|
||||
*
|
||||
* @param {goog.events.BrowserEvent} event Event object.
|
||||
* @protected
|
||||
* @override
|
||||
*/
|
||||
goog.ui.AdvancedTooltip.prototype.handleMouseMove = function(event) {
|
||||
var startTimer = this.isVisible();
|
||||
if (this.boundingBox_) {
|
||||
var scroll = this.getDomHelper().getDocumentScroll();
|
||||
var c = new goog.math.Coordinate(event.clientX + scroll.x,
|
||||
event.clientY + scroll.y);
|
||||
if (this.isCoordinateActive_(c)) {
|
||||
startTimer = false;
|
||||
} else if (this.tracking_) {
|
||||
var prevDist = goog.math.Box.distance(this.boundingBox_,
|
||||
this.cursorPosition);
|
||||
var currDist = goog.math.Box.distance(this.boundingBox_, c);
|
||||
startTimer = currDist >= prevDist;
|
||||
}
|
||||
}
|
||||
|
||||
if (startTimer) {
|
||||
this.startHideTimer();
|
||||
|
||||
// Even though the mouse coordinate is not on the tooltip (or nested child),
|
||||
// they may have an active element because of a focus event. Don't let
|
||||
// that prevent us from taking down the tooltip(s) on this mouse move.
|
||||
this.setActiveElement(null);
|
||||
var childTooltip = this.getChildTooltip();
|
||||
if (childTooltip) {
|
||||
childTooltip.setActiveElement(null);
|
||||
}
|
||||
} else if (this.getState() == goog.ui.Tooltip.State.WAITING_TO_HIDE) {
|
||||
this.clearHideTimer();
|
||||
}
|
||||
|
||||
goog.ui.AdvancedTooltip.superClass_.handleMouseMove.call(this, event);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler for mouse over events for the tooltip element.
|
||||
*
|
||||
* @param {goog.events.BrowserEvent} event Event object.
|
||||
* @protected
|
||||
* @override
|
||||
*/
|
||||
goog.ui.AdvancedTooltip.prototype.handleTooltipMouseOver = function(event) {
|
||||
if (this.getActiveElement() != this.getElement()) {
|
||||
this.tracking_ = false;
|
||||
this.setActiveElement(this.getElement());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Override hide delay with cursor tracking hide delay while tracking.
|
||||
* @return {number} Hide delay to use.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.AdvancedTooltip.prototype.getHideDelayMs = function() {
|
||||
return this.tracking_ ? this.cursorTrackingHideDelayMs_ :
|
||||
goog.base(this, 'getHideDelayMs');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Forces the recalculation of the hotspot on the next mouse over event.
|
||||
* @deprecated Not ever necessary to call this function. Hot spot is calculated
|
||||
* as neccessary.
|
||||
*/
|
||||
goog.ui.AdvancedTooltip.prototype.resetHotSpot = goog.nullFunction;
|
||||
199
nicer-api-docs/closure-library/closure/goog/ui/animatedzippy.js
Normal file
199
nicer-api-docs/closure-library/closure/goog/ui/animatedzippy.js
Normal file
@@ -0,0 +1,199 @@
|
||||
// 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 Animated zippy widget implementation.
|
||||
*
|
||||
* @author eae@google.com (Emil A Eklund)
|
||||
* @see ../demos/zippy.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.AnimatedZippy');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.fx.Animation');
|
||||
goog.require('goog.fx.Transition');
|
||||
goog.require('goog.fx.easing');
|
||||
goog.require('goog.ui.Zippy');
|
||||
goog.require('goog.ui.ZippyEvent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Zippy widget. Expandable/collapsible container, clicking the header toggles
|
||||
* the visibility of the content.
|
||||
*
|
||||
* @param {Element|string|null} header Header element, either element
|
||||
* reference, string id or null if no header exists.
|
||||
* @param {Element|string} content Content element, either element reference or
|
||||
* string id.
|
||||
* @param {boolean=} opt_expanded Initial expanded/visibility state. Defaults to
|
||||
* false.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper An optional DOM helper.
|
||||
* @constructor
|
||||
* @extends {goog.ui.Zippy}
|
||||
*/
|
||||
goog.ui.AnimatedZippy = function(header, content, opt_expanded, opt_domHelper) {
|
||||
var domHelper = opt_domHelper || goog.dom.getDomHelper();
|
||||
|
||||
// Create wrapper element and move content into it.
|
||||
var elWrapper = domHelper.createDom('div', {'style': 'overflow:hidden'});
|
||||
var elContent = domHelper.getElement(content);
|
||||
elContent.parentNode.replaceChild(elWrapper, elContent);
|
||||
elWrapper.appendChild(elContent);
|
||||
|
||||
/**
|
||||
* Content wrapper, used for animation.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
this.elWrapper_ = elWrapper;
|
||||
|
||||
/**
|
||||
* Reference to animation or null if animation is not active.
|
||||
* @type {goog.fx.Animation}
|
||||
* @private
|
||||
*/
|
||||
this.anim_ = null;
|
||||
|
||||
// Call constructor of super class.
|
||||
goog.ui.Zippy.call(this, header, elContent, opt_expanded,
|
||||
undefined, domHelper);
|
||||
|
||||
// Set initial state.
|
||||
// NOTE: Set the class names as well otherwise animated zippys
|
||||
// start with empty class names.
|
||||
var expanded = this.isExpanded();
|
||||
this.elWrapper_.style.display = expanded ? '' : 'none';
|
||||
this.updateHeaderClassName(expanded);
|
||||
};
|
||||
goog.inherits(goog.ui.AnimatedZippy, goog.ui.Zippy);
|
||||
|
||||
|
||||
/**
|
||||
* Duration of expand/collapse animation, in milliseconds.
|
||||
* @type {number}
|
||||
*/
|
||||
goog.ui.AnimatedZippy.prototype.animationDuration = 500;
|
||||
|
||||
|
||||
/**
|
||||
* Acceleration function for expand/collapse animation.
|
||||
* @type {!Function}
|
||||
*/
|
||||
goog.ui.AnimatedZippy.prototype.animationAcceleration = goog.fx.easing.easeOut;
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether the zippy is in the process of being expanded or
|
||||
* collapsed.
|
||||
*/
|
||||
goog.ui.AnimatedZippy.prototype.isBusy = function() {
|
||||
return this.anim_ != null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets expanded state.
|
||||
*
|
||||
* @param {boolean} expanded Expanded/visibility state.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.AnimatedZippy.prototype.setExpanded = function(expanded) {
|
||||
if (this.isExpanded() == expanded && !this.anim_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset display property of wrapper to allow content element to be
|
||||
// measured.
|
||||
if (this.elWrapper_.style.display == 'none') {
|
||||
this.elWrapper_.style.display = '';
|
||||
}
|
||||
|
||||
// Measure content element.
|
||||
var h = this.getContentElement().offsetHeight;
|
||||
|
||||
// Stop active animation (if any) and determine starting height.
|
||||
var startH = 0;
|
||||
if (this.anim_) {
|
||||
expanded = this.isExpanded();
|
||||
goog.events.removeAll(this.anim_);
|
||||
this.anim_.stop(false);
|
||||
|
||||
var marginTop = parseInt(this.getContentElement().style.marginTop, 10);
|
||||
startH = h - Math.abs(marginTop);
|
||||
} else {
|
||||
startH = expanded ? 0 : h;
|
||||
}
|
||||
|
||||
// Updates header class name after the animation has been stopped.
|
||||
this.updateHeaderClassName(expanded);
|
||||
|
||||
// Set up expand/collapse animation.
|
||||
this.anim_ = new goog.fx.Animation([0, startH],
|
||||
[0, expanded ? h : 0],
|
||||
this.animationDuration,
|
||||
this.animationAcceleration);
|
||||
|
||||
var events = [goog.fx.Transition.EventType.BEGIN,
|
||||
goog.fx.Animation.EventType.ANIMATE,
|
||||
goog.fx.Transition.EventType.END];
|
||||
goog.events.listen(this.anim_, events, this.onAnimate_, false, this);
|
||||
goog.events.listen(this.anim_,
|
||||
goog.fx.Transition.EventType.END,
|
||||
goog.bind(this.onAnimationCompleted_, this, expanded));
|
||||
|
||||
// Start animation.
|
||||
this.anim_.play(false);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Called during animation
|
||||
*
|
||||
* @param {goog.events.Event} e The event.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.AnimatedZippy.prototype.onAnimate_ = function(e) {
|
||||
var contentElement = this.getContentElement();
|
||||
var h = contentElement.offsetHeight;
|
||||
contentElement.style.marginTop = (e.y - h) + 'px';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Called once the expand/collapse animation has completed.
|
||||
*
|
||||
* @param {boolean} expanded Expanded/visibility state.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.AnimatedZippy.prototype.onAnimationCompleted_ = function(expanded) {
|
||||
// Fix wrong end position if the content has changed during the animation.
|
||||
if (expanded) {
|
||||
this.getContentElement().style.marginTop = '0';
|
||||
}
|
||||
|
||||
goog.events.removeAll(this.anim_);
|
||||
this.setExpandedInternal(expanded);
|
||||
this.anim_ = null;
|
||||
|
||||
if (!expanded) {
|
||||
this.elWrapper_.style.display = 'none';
|
||||
}
|
||||
|
||||
// Fire toggle event.
|
||||
this.dispatchEvent(new goog.ui.ZippyEvent(goog.ui.Zippy.Events.TOGGLE,
|
||||
this, expanded));
|
||||
};
|
||||
473
nicer-api-docs/closure-library/closure/goog/ui/attachablemenu.js
Normal file
473
nicer-api-docs/closure-library/closure/goog/ui/attachablemenu.js
Normal file
@@ -0,0 +1,473 @@
|
||||
// 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 Definition of the AttachableMenu class.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.AttachableMenu');
|
||||
|
||||
goog.require('goog.a11y.aria');
|
||||
goog.require('goog.a11y.aria.State');
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.classes');
|
||||
goog.require('goog.events.Event');
|
||||
goog.require('goog.events.KeyCodes');
|
||||
goog.require('goog.string');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.ItemEvent');
|
||||
goog.require('goog.ui.MenuBase');
|
||||
goog.require('goog.ui.PopupBase');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* An implementation of a menu that can attach itself to DOM element that
|
||||
* are annotated appropriately.
|
||||
*
|
||||
* The following attributes are used by the AttachableMenu
|
||||
*
|
||||
* menu-item - Should be set on DOM elements that function as items in the
|
||||
* menu that can be selected.
|
||||
* classNameSelected - A class that will be added to the element's class names
|
||||
* when the item is selected via keyboard or mouse.
|
||||
*
|
||||
* @param {Element=} opt_element A DOM element for the popup.
|
||||
* @constructor
|
||||
* @extends {goog.ui.MenuBase}
|
||||
* @deprecated Use goog.ui.PopupMenu.
|
||||
*/
|
||||
goog.ui.AttachableMenu = function(opt_element) {
|
||||
goog.ui.MenuBase.call(this, opt_element);
|
||||
};
|
||||
goog.inherits(goog.ui.AttachableMenu, goog.ui.MenuBase);
|
||||
|
||||
|
||||
/**
|
||||
* The currently selected element (mouse was moved over it or keyboard arrows)
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.AttachableMenu.prototype.selectedElement_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Class name to append to a menu item's class when it's selected
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.AttachableMenu.prototype.itemClassName_ = 'menu-item';
|
||||
|
||||
|
||||
/**
|
||||
* Class name to append to a menu item's class when it's selected
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.AttachableMenu.prototype.selectedItemClassName_ = 'menu-item-selected';
|
||||
|
||||
|
||||
/**
|
||||
* Keep track of when the last key was pressed so that a keydown-scroll doesn't
|
||||
* trigger a mouseover event
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.AttachableMenu.prototype.lastKeyDown_ = goog.now();
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.AttachableMenu.prototype.disposeInternal = function() {
|
||||
goog.ui.AttachableMenu.superClass_.disposeInternal.call(this);
|
||||
this.selectedElement_ = null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the class name to use for menu items
|
||||
*
|
||||
* @return {string} The class name to use for items.
|
||||
*/
|
||||
goog.ui.AttachableMenu.prototype.getItemClassName = function() {
|
||||
return this.itemClassName_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the class name to use for menu items
|
||||
*
|
||||
* @param {string} name The class name to use for items.
|
||||
*/
|
||||
goog.ui.AttachableMenu.prototype.setItemClassName = function(name) {
|
||||
this.itemClassName_ = name;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the class name to use for selected menu items
|
||||
* todo(user) - reevaluate if we can simulate pseudo classes in IE
|
||||
*
|
||||
* @return {string} The class name to use for selected items.
|
||||
*/
|
||||
goog.ui.AttachableMenu.prototype.getSelectedItemClassName = function() {
|
||||
return this.selectedItemClassName_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the class name to use for selected menu items
|
||||
* todo(user) - reevaluate if we can simulate pseudo classes in IE
|
||||
*
|
||||
* @param {string} name The class name to use for selected items.
|
||||
*/
|
||||
goog.ui.AttachableMenu.prototype.setSelectedItemClassName = function(name) {
|
||||
this.selectedItemClassName_ = name;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the selected item
|
||||
*
|
||||
* @return {Element} The item selected or null if no item is selected.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.AttachableMenu.prototype.getSelectedItem = function() {
|
||||
return this.selectedElement_;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.AttachableMenu.prototype.setSelectedItem = function(obj) {
|
||||
var elt = /** @type {Element} */ (obj);
|
||||
if (this.selectedElement_) {
|
||||
goog.dom.classes.remove(this.selectedElement_, this.selectedItemClassName_);
|
||||
}
|
||||
|
||||
this.selectedElement_ = elt;
|
||||
|
||||
var el = this.getElement();
|
||||
goog.asserts.assert(el, 'The attachable menu DOM element cannot be null.');
|
||||
if (this.selectedElement_) {
|
||||
goog.dom.classes.add(this.selectedElement_, this.selectedItemClassName_);
|
||||
|
||||
if (elt.id) {
|
||||
// Update activedescendant to reflect the new selection. ARIA roles for
|
||||
// menu and menuitem can be set statically (thru Soy templates, for
|
||||
// example) whereas this needs to be updated as the selection changes.
|
||||
goog.a11y.aria.setState(el, goog.a11y.aria.State.ACTIVEDESCENDANT,
|
||||
elt.id);
|
||||
}
|
||||
|
||||
var top = this.selectedElement_.offsetTop;
|
||||
var height = this.selectedElement_.offsetHeight;
|
||||
var scrollTop = el.scrollTop;
|
||||
var scrollHeight = el.offsetHeight;
|
||||
|
||||
// If the menu is scrollable this scrolls the selected item into view
|
||||
// (this has no effect when the menu doesn't scroll)
|
||||
if (top < scrollTop) {
|
||||
el.scrollTop = top;
|
||||
} else if (top + height > scrollTop + scrollHeight) {
|
||||
el.scrollTop = top + height - scrollHeight;
|
||||
}
|
||||
} else {
|
||||
// Clear off activedescendant to reflect no selection.
|
||||
goog.a11y.aria.setState(el, goog.a11y.aria.State.ACTIVEDESCENDANT, '');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.AttachableMenu.prototype.showPopupElement = function() {
|
||||
// The scroll position cannot be set for hidden (display: none) elements in
|
||||
// gecko browsers.
|
||||
var el = /** @type {Element} */ (this.getElement());
|
||||
goog.style.setElementShown(el, true);
|
||||
el.scrollTop = 0;
|
||||
el.style.visibility = 'visible';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Called after the menu is shown.
|
||||
* @protected
|
||||
* @suppress {underscore}
|
||||
* @override
|
||||
*/
|
||||
goog.ui.AttachableMenu.prototype.onShow_ = function() {
|
||||
goog.ui.AttachableMenu.superClass_.onShow_.call(this);
|
||||
|
||||
// In IE, focusing the menu causes weird scrolling to happen. Focusing the
|
||||
// first child makes the scroll behavior better, and the key handling still
|
||||
// works. In FF, focusing the first child causes us to lose key events, so we
|
||||
// still focus the menu.
|
||||
var el = this.getElement();
|
||||
goog.userAgent.IE ? el.firstChild.focus() :
|
||||
el.focus();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the next or previous item. Used for up/down arrows.
|
||||
*
|
||||
* @param {boolean} prev True to go to the previous element instead of next.
|
||||
* @return {Element} The next or previous element.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.AttachableMenu.prototype.getNextPrevItem = function(prev) {
|
||||
// first find the index of the next element
|
||||
var elements = this.getElement().getElementsByTagName('*');
|
||||
var elementCount = elements.length;
|
||||
var index;
|
||||
// if there is a selected element, find its index and then inc/dec by one
|
||||
if (this.selectedElement_) {
|
||||
for (var i = 0; i < elementCount; i++) {
|
||||
if (elements[i] == this.selectedElement_) {
|
||||
index = prev ? i - 1 : i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if no selected element, start from beginning or end
|
||||
if (!goog.isDef(index)) {
|
||||
index = prev ? elementCount - 1 : 0;
|
||||
}
|
||||
|
||||
// iterate forward or backwards through the elements finding the next
|
||||
// menu item
|
||||
for (var i = 0; i < elementCount; i++) {
|
||||
var multiplier = prev ? -1 : 1;
|
||||
var nextIndex = index + (multiplier * i) % elementCount;
|
||||
|
||||
// if overflowed/underflowed, wrap around
|
||||
if (nextIndex < 0) {
|
||||
nextIndex += elementCount;
|
||||
} else if (nextIndex >= elementCount) {
|
||||
nextIndex -= elementCount;
|
||||
}
|
||||
|
||||
if (this.isMenuItem_(elements[nextIndex])) {
|
||||
return elements[nextIndex];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Mouse over handler for the menu.
|
||||
* @param {goog.events.Event} e The event object.
|
||||
* @protected
|
||||
* @override
|
||||
*/
|
||||
goog.ui.AttachableMenu.prototype.onMouseOver = function(e) {
|
||||
var eltItem = this.getAncestorMenuItem_(/** @type {Element} */ (e.target));
|
||||
if (eltItem == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop the keydown triggering a mouseover in FF.
|
||||
if (goog.now() - this.lastKeyDown_ > goog.ui.PopupBase.DEBOUNCE_DELAY_MS) {
|
||||
this.setSelectedItem(eltItem);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Mouse out handler for the menu.
|
||||
* @param {goog.events.Event} e The event object.
|
||||
* @protected
|
||||
* @override
|
||||
*/
|
||||
goog.ui.AttachableMenu.prototype.onMouseOut = function(e) {
|
||||
var eltItem = this.getAncestorMenuItem_(/** @type {Element} */ (e.target));
|
||||
if (eltItem == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop the keydown triggering a mouseout in FF.
|
||||
if (goog.now() - this.lastKeyDown_ > goog.ui.PopupBase.DEBOUNCE_DELAY_MS) {
|
||||
this.setSelectedItem(null);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Mouse down handler for the menu. Prevents default to avoid text selection.
|
||||
* @param {!goog.events.Event} e The event object.
|
||||
* @protected
|
||||
* @override
|
||||
*/
|
||||
goog.ui.AttachableMenu.prototype.onMouseDown = goog.events.Event.preventDefault;
|
||||
|
||||
|
||||
/**
|
||||
* Mouse up handler for the menu.
|
||||
* @param {goog.events.Event} e The event object.
|
||||
* @protected
|
||||
* @override
|
||||
*/
|
||||
goog.ui.AttachableMenu.prototype.onMouseUp = function(e) {
|
||||
var eltItem = this.getAncestorMenuItem_(/** @type {Element} */ (e.target));
|
||||
if (eltItem == null) {
|
||||
return;
|
||||
}
|
||||
this.setVisible(false);
|
||||
this.onItemSelected_(eltItem);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Key down handler for the menu.
|
||||
* @param {goog.events.KeyEvent} e The event object.
|
||||
* @protected
|
||||
* @override
|
||||
*/
|
||||
goog.ui.AttachableMenu.prototype.onKeyDown = function(e) {
|
||||
switch (e.keyCode) {
|
||||
case goog.events.KeyCodes.DOWN:
|
||||
this.setSelectedItem(this.getNextPrevItem(false));
|
||||
this.lastKeyDown_ = goog.now();
|
||||
break;
|
||||
case goog.events.KeyCodes.UP:
|
||||
this.setSelectedItem(this.getNextPrevItem(true));
|
||||
this.lastKeyDown_ = goog.now();
|
||||
break;
|
||||
case goog.events.KeyCodes.ENTER:
|
||||
if (this.selectedElement_) {
|
||||
this.onItemSelected_();
|
||||
this.setVisible(false);
|
||||
}
|
||||
break;
|
||||
case goog.events.KeyCodes.ESC:
|
||||
this.setVisible(false);
|
||||
break;
|
||||
default:
|
||||
if (e.charCode) {
|
||||
var charStr = String.fromCharCode(e.charCode);
|
||||
this.selectByName_(charStr, 1, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Prevent the browser's default keydown behaviour when the menu is open,
|
||||
// e.g. keyboard scrolling.
|
||||
e.preventDefault();
|
||||
|
||||
// Stop propagation to prevent application level keyboard shortcuts from
|
||||
// firing.
|
||||
e.stopPropagation();
|
||||
|
||||
this.dispatchEvent(e);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Find an item that has the given prefix and select it.
|
||||
*
|
||||
* @param {string} prefix The entered prefix, so far.
|
||||
* @param {number=} opt_direction 1 to search forward from the selection
|
||||
* (default), -1 to search backward (e.g. to go to the previous match).
|
||||
* @param {boolean=} opt_skip True if should skip the current selection,
|
||||
* unless no other item has the given prefix.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.AttachableMenu.prototype.selectByName_ =
|
||||
function(prefix, opt_direction, opt_skip) {
|
||||
var elements = this.getElement().getElementsByTagName('*');
|
||||
var elementCount = elements.length;
|
||||
var index;
|
||||
|
||||
if (elementCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.selectedElement_ ||
|
||||
(index = goog.array.indexOf(elements, this.selectedElement_)) == -1) {
|
||||
// no selection or selection isn't known => start at the beginning
|
||||
index = 0;
|
||||
}
|
||||
|
||||
var start = index;
|
||||
var re = new RegExp('^' + goog.string.regExpEscape(prefix), 'i');
|
||||
var skip = opt_skip && this.selectedElement_;
|
||||
var dir = opt_direction || 1;
|
||||
|
||||
do {
|
||||
if (elements[index] != skip && this.isMenuItem_(elements[index])) {
|
||||
var name = goog.dom.getTextContent(elements[index]);
|
||||
if (name.match(re)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
index += dir;
|
||||
if (index == elementCount) {
|
||||
index = 0;
|
||||
} else if (index < 0) {
|
||||
index = elementCount - 1;
|
||||
}
|
||||
} while (index != start);
|
||||
|
||||
if (this.selectedElement_ != elements[index]) {
|
||||
this.setSelectedItem(elements[index]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Dispatch an ITEM_ACTION event when an item is selected
|
||||
* @param {Object=} opt_item Item selected.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.AttachableMenu.prototype.onItemSelected_ = function(opt_item) {
|
||||
this.dispatchEvent(new goog.ui.ItemEvent(goog.ui.MenuBase.Events.ITEM_ACTION,
|
||||
this, opt_item || this.selectedElement_));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the specified element is a menu item.
|
||||
* @param {Element|undefined} elt The element to find a menu item ancestor of.
|
||||
* @return {boolean} Whether the specified element is a menu item.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.AttachableMenu.prototype.isMenuItem_ = function(elt) {
|
||||
return !!elt && goog.dom.classes.has(elt, this.itemClassName_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the menu-item scoping the specified element, or null if there is
|
||||
* none.
|
||||
* @param {Element|undefined} elt The element to find a menu item ancestor of.
|
||||
* @return {Element} The menu-item scoping the specified element, or null if
|
||||
* there is none.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.AttachableMenu.prototype.getAncestorMenuItem_ = function(elt) {
|
||||
if (elt) {
|
||||
var ownerDocumentBody = goog.dom.getOwnerDocument(elt).body;
|
||||
while (elt != null && elt != ownerDocumentBody) {
|
||||
if (this.isMenuItem_(elt)) {
|
||||
return elt;
|
||||
}
|
||||
elt = /** @type {Element} */ (elt.parentNode);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
178
nicer-api-docs/closure-library/closure/goog/ui/bidiinput.js
Normal file
178
nicer-api-docs/closure-library/closure/goog/ui/bidiinput.js
Normal file
@@ -0,0 +1,178 @@
|
||||
// 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 Component for an input field with bidi direction automatic
|
||||
* detection. The input element directionality is automatically set according
|
||||
* to the contents (value) of the element.
|
||||
*
|
||||
* @see ../demos/bidiinput.html
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.ui.BidiInput');
|
||||
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.InputHandler');
|
||||
goog.require('goog.i18n.bidi');
|
||||
goog.require('goog.ui.Component');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Default implementation of BidiInput.
|
||||
*
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
|
||||
* @constructor
|
||||
* @extends {goog.ui.Component}
|
||||
*/
|
||||
goog.ui.BidiInput = function(opt_domHelper) {
|
||||
goog.ui.Component.call(this, opt_domHelper);
|
||||
};
|
||||
goog.inherits(goog.ui.BidiInput, goog.ui.Component);
|
||||
|
||||
|
||||
/**
|
||||
* The input handler that provides the input event.
|
||||
* @type {goog.events.InputHandler?}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.BidiInput.prototype.inputHandler_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Decorates the given HTML element as a BidiInput. The HTML element can be an
|
||||
* input element with type='text', a textarea element, or any contenteditable.
|
||||
* Overrides {@link goog.ui.Component#decorateInternal}. Considered protected.
|
||||
* @param {Element} element Element to decorate.
|
||||
* @protected
|
||||
* @override
|
||||
*/
|
||||
goog.ui.BidiInput.prototype.decorateInternal = function(element) {
|
||||
goog.ui.BidiInput.superClass_.decorateInternal.call(this, element);
|
||||
this.init_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates the element for the text input.
|
||||
* @protected
|
||||
* @override
|
||||
*/
|
||||
goog.ui.BidiInput.prototype.createDom = function() {
|
||||
this.setElementInternal(
|
||||
this.getDomHelper().createDom('input', {'type': 'text'}));
|
||||
this.init_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the events and initial text direction.
|
||||
* Called from either decorate or createDom, after the input field has
|
||||
* been created.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.BidiInput.prototype.init_ = function() {
|
||||
// Set initial direction by current text
|
||||
this.setDirection_();
|
||||
|
||||
// Listen to value change events
|
||||
this.inputHandler_ = new goog.events.InputHandler(this.getElement());
|
||||
goog.events.listen(this.inputHandler_,
|
||||
goog.events.InputHandler.EventType.INPUT,
|
||||
this.setDirection_, false, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Set the direction of the input element based on the current value. If the
|
||||
* value does not have any strongly directional characters, remove the dir
|
||||
* attribute so that the direction is inherited instead.
|
||||
* This method is called when the user changes the input element value, or
|
||||
* when a program changes the value using
|
||||
* {@link goog.ui.BidiInput#setValue}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.BidiInput.prototype.setDirection_ = function() {
|
||||
var element = this.getElement();
|
||||
var text = this.getValue();
|
||||
switch (goog.i18n.bidi.estimateDirection(text)) {
|
||||
case (goog.i18n.bidi.Dir.LTR):
|
||||
element.dir = 'ltr';
|
||||
break;
|
||||
case (goog.i18n.bidi.Dir.RTL):
|
||||
element.dir = 'rtl';
|
||||
break;
|
||||
default:
|
||||
// Default for no direction, inherit from document.
|
||||
element.removeAttribute('dir');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the direction of the input element.
|
||||
* @return {?string} Return 'rtl' for right-to-left text,
|
||||
* 'ltr' for left-to-right text, or null if the value itself is not
|
||||
* enough to determine directionality (e.g. an empty value), and the
|
||||
* direction is inherited from a parent element (typically the body
|
||||
* element).
|
||||
*/
|
||||
goog.ui.BidiInput.prototype.getDirection = function() {
|
||||
var dir = this.getElement().dir;
|
||||
if (dir == '') {
|
||||
dir = null;
|
||||
}
|
||||
return dir;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the value of the underlying input field, and sets the direction
|
||||
* according to the given value.
|
||||
* @param {string} value The Value to set in the underlying input field.
|
||||
*/
|
||||
goog.ui.BidiInput.prototype.setValue = function(value) {
|
||||
var element = this.getElement();
|
||||
if (goog.isDefAndNotNull(element.value)) {
|
||||
element.value = value;
|
||||
} else {
|
||||
goog.dom.setTextContent(element, value);
|
||||
}
|
||||
this.setDirection_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the value of the underlying input field.
|
||||
* @return {string} Value of the underlying input field.
|
||||
*/
|
||||
goog.ui.BidiInput.prototype.getValue = function() {
|
||||
var element = this.getElement();
|
||||
return goog.isDefAndNotNull(element.value) ? element.value :
|
||||
goog.dom.getRawTextContent(element);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.BidiInput.prototype.disposeInternal = function() {
|
||||
if (this.inputHandler_) {
|
||||
goog.events.removeAll(this.inputHandler_);
|
||||
this.inputHandler_.dispose();
|
||||
this.inputHandler_ = null;
|
||||
goog.ui.BidiInput.superClass_.disposeInternal.call(this);
|
||||
}
|
||||
};
|
||||
472
nicer-api-docs/closure-library/closure/goog/ui/bubble.js
Normal file
472
nicer-api-docs/closure-library/closure/goog/ui/bubble.js
Normal file
@@ -0,0 +1,472 @@
|
||||
// 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 Definition of the Bubble class.
|
||||
*
|
||||
*
|
||||
* @see ../demos/bubble.html
|
||||
*
|
||||
* TODO: support decoration and addChild
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.Bubble');
|
||||
|
||||
goog.require('goog.Timer');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.math.Box');
|
||||
goog.require('goog.positioning');
|
||||
goog.require('goog.positioning.AbsolutePosition');
|
||||
goog.require('goog.positioning.AnchoredPosition');
|
||||
goog.require('goog.positioning.Corner');
|
||||
goog.require('goog.positioning.CornerBit');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('goog.ui.Popup');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The Bubble provides a general purpose bubble implementation that can be
|
||||
* anchored to a particular element and displayed for a period of time.
|
||||
*
|
||||
* @param {string|Element} message HTML string or an element to display inside
|
||||
* the bubble.
|
||||
* @param {Object=} opt_config The configuration
|
||||
* for the bubble. If not specified, the default configuration will be
|
||||
* used. {@see goog.ui.Bubble.defaultConfig}.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
|
||||
* @constructor
|
||||
* @extends {goog.ui.Component}
|
||||
*/
|
||||
goog.ui.Bubble = function(message, opt_config, opt_domHelper) {
|
||||
goog.ui.Component.call(this, opt_domHelper);
|
||||
|
||||
/**
|
||||
* The HTML string or element to display inside the bubble.
|
||||
*
|
||||
* @type {string|Element}
|
||||
* @private
|
||||
*/
|
||||
this.message_ = message;
|
||||
|
||||
/**
|
||||
* The Popup element used to position and display the bubble.
|
||||
*
|
||||
* @type {goog.ui.Popup}
|
||||
* @private
|
||||
*/
|
||||
this.popup_ = new goog.ui.Popup();
|
||||
|
||||
/**
|
||||
* Configuration map that contains bubble's UI elements.
|
||||
*
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
this.config_ = opt_config || goog.ui.Bubble.defaultConfig;
|
||||
|
||||
/**
|
||||
* Id of the close button for this bubble.
|
||||
*
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.closeButtonId_ = this.makeId('cb');
|
||||
|
||||
/**
|
||||
* Id of the div for the embedded element.
|
||||
*
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.messageId_ = this.makeId('mi');
|
||||
|
||||
};
|
||||
goog.inherits(goog.ui.Bubble, goog.ui.Component);
|
||||
|
||||
|
||||
/**
|
||||
* In milliseconds, timeout after which the button auto-hides. Null means
|
||||
* infinite.
|
||||
* @type {?number}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.Bubble.prototype.timeout_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Key returned by the bubble timer.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.Bubble.prototype.timerId_ = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Key returned by the listen function for the close button.
|
||||
* @type {goog.events.Key}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.Bubble.prototype.listener_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Key returned by the listen function for the close button.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.Bubble.prototype.anchor_ = null;
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.Bubble.prototype.createDom = function() {
|
||||
goog.ui.Bubble.superClass_.createDom.call(this);
|
||||
|
||||
var element = this.getElement();
|
||||
element.style.position = 'absolute';
|
||||
element.style.visibility = 'hidden';
|
||||
|
||||
this.popup_.setElement(element);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Attaches the bubble to an anchor element. Computes the positioning and
|
||||
* orientation of the bubble.
|
||||
*
|
||||
* @param {Element} anchorElement The element to which we are attaching.
|
||||
*/
|
||||
goog.ui.Bubble.prototype.attach = function(anchorElement) {
|
||||
this.setAnchoredPosition_(
|
||||
anchorElement, this.computePinnedCorner_(anchorElement));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the corner of the bubble to used in the positioning algorithm.
|
||||
*
|
||||
* @param {goog.positioning.Corner} corner The bubble corner used for
|
||||
* positioning constants.
|
||||
*/
|
||||
goog.ui.Bubble.prototype.setPinnedCorner = function(corner) {
|
||||
this.popup_.setPinnedCorner(corner);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the position of the bubble. Pass null for corner in AnchoredPosition
|
||||
* for corner to be computed automatically.
|
||||
*
|
||||
* @param {goog.positioning.AbstractPosition} position The position of the
|
||||
* bubble.
|
||||
*/
|
||||
goog.ui.Bubble.prototype.setPosition = function(position) {
|
||||
if (position instanceof goog.positioning.AbsolutePosition) {
|
||||
this.popup_.setPosition(position);
|
||||
} else if (position instanceof goog.positioning.AnchoredPosition) {
|
||||
this.setAnchoredPosition_(position.element, position.corner);
|
||||
} else {
|
||||
throw Error('Bubble only supports absolute and anchored positions!');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the timeout after which bubble hides itself.
|
||||
*
|
||||
* @param {number} timeout Timeout of the bubble.
|
||||
*/
|
||||
goog.ui.Bubble.prototype.setTimeout = function(timeout) {
|
||||
this.timeout_ = timeout;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether the bubble should be automatically hidden whenever user clicks
|
||||
* outside the bubble element.
|
||||
*
|
||||
* @param {boolean} autoHide Whether to hide if user clicks outside the bubble.
|
||||
*/
|
||||
goog.ui.Bubble.prototype.setAutoHide = function(autoHide) {
|
||||
this.popup_.setAutoHide(autoHide);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether the bubble should be visible.
|
||||
*
|
||||
* @param {boolean} visible Desired visibility state.
|
||||
*/
|
||||
goog.ui.Bubble.prototype.setVisible = function(visible) {
|
||||
if (visible && !this.popup_.isVisible()) {
|
||||
this.configureElement_();
|
||||
}
|
||||
this.popup_.setVisible(visible);
|
||||
if (!this.popup_.isVisible()) {
|
||||
this.unconfigureElement_();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether the bubble is visible.
|
||||
*/
|
||||
goog.ui.Bubble.prototype.isVisible = function() {
|
||||
return this.popup_.isVisible();
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.Bubble.prototype.disposeInternal = function() {
|
||||
this.unconfigureElement_();
|
||||
this.popup_.dispose();
|
||||
this.popup_ = null;
|
||||
goog.ui.Bubble.superClass_.disposeInternal.call(this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates element's contents and configures all timers. This is called on
|
||||
* setVisible(true).
|
||||
* @private
|
||||
*/
|
||||
goog.ui.Bubble.prototype.configureElement_ = function() {
|
||||
if (!this.isInDocument()) {
|
||||
throw Error('You must render the bubble before showing it!');
|
||||
}
|
||||
|
||||
var element = this.getElement();
|
||||
var corner = this.popup_.getPinnedCorner();
|
||||
element.innerHTML = this.computeHtmlForCorner_(corner);
|
||||
|
||||
if (typeof this.message_ == 'object') {
|
||||
var messageDiv = this.getDomHelper().getElement(this.messageId_);
|
||||
this.getDomHelper().appendChild(messageDiv, this.message_);
|
||||
}
|
||||
var closeButton = this.getDomHelper().getElement(this.closeButtonId_);
|
||||
this.listener_ = goog.events.listen(closeButton,
|
||||
goog.events.EventType.CLICK, this.hideBubble_, false, this);
|
||||
|
||||
if (this.timeout_) {
|
||||
this.timerId_ = goog.Timer.callOnce(this.hideBubble_, this.timeout_, this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets rid of the element's contents and all assoicated timers and listeners.
|
||||
* This is called on dispose as well as on setVisible(false).
|
||||
* @private
|
||||
*/
|
||||
goog.ui.Bubble.prototype.unconfigureElement_ = function() {
|
||||
if (this.listener_) {
|
||||
goog.events.unlistenByKey(this.listener_);
|
||||
this.listener_ = null;
|
||||
}
|
||||
if (this.timerId_) {
|
||||
goog.Timer.clear(this.timerId_);
|
||||
this.timerId = null;
|
||||
}
|
||||
|
||||
var element = this.getElement();
|
||||
if (element) {
|
||||
this.getDomHelper().removeChildren(element);
|
||||
element.innerHTML = '';
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Computes bubble position based on anchored element.
|
||||
*
|
||||
* @param {Element} anchorElement The element to which we are attaching.
|
||||
* @param {goog.positioning.Corner} corner The bubble corner used for
|
||||
* positioning.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.Bubble.prototype.setAnchoredPosition_ = function(anchorElement,
|
||||
corner) {
|
||||
this.popup_.setPinnedCorner(corner);
|
||||
var margin = this.createMarginForCorner_(corner);
|
||||
this.popup_.setMargin(margin);
|
||||
var anchorCorner = goog.positioning.flipCorner(corner);
|
||||
this.popup_.setPosition(new goog.positioning.AnchoredPosition(
|
||||
anchorElement, anchorCorner));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Hides the bubble. This is called asynchronously by timer of event processor
|
||||
* for the mouse click on the close button.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.Bubble.prototype.hideBubble_ = function() {
|
||||
this.setVisible(false);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns an AnchoredPosition that will position the bubble optimally
|
||||
* given the position of the anchor element and the size of the viewport.
|
||||
*
|
||||
* @param {Element} anchorElement The element to which the bubble is attached.
|
||||
* @return {goog.ui.Popup.AnchoredPosition} The AnchoredPosition to give to
|
||||
* {@link #setPosition}.
|
||||
*/
|
||||
goog.ui.Bubble.prototype.getComputedAnchoredPosition = function(anchorElement) {
|
||||
return new goog.ui.Popup.AnchoredPosition(
|
||||
anchorElement, this.computePinnedCorner_(anchorElement));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Computes the pinned corner for the bubble.
|
||||
*
|
||||
* @param {Element} anchorElement The element to which the button is attached.
|
||||
* @return {goog.positioning.Corner} The pinned corner.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.Bubble.prototype.computePinnedCorner_ = function(anchorElement) {
|
||||
var doc = this.getDomHelper().getOwnerDocument(anchorElement);
|
||||
var viewportElement = goog.style.getClientViewportElement(doc);
|
||||
var viewportWidth = viewportElement.offsetWidth;
|
||||
var viewportHeight = viewportElement.offsetHeight;
|
||||
var anchorElementOffset = goog.style.getPageOffset(anchorElement);
|
||||
var anchorElementSize = goog.style.getSize(anchorElement);
|
||||
var anchorType = 0;
|
||||
// right margin or left?
|
||||
if (viewportWidth - anchorElementOffset.x - anchorElementSize.width >
|
||||
anchorElementOffset.x) {
|
||||
anchorType += 1;
|
||||
}
|
||||
// attaches to the top or to the bottom?
|
||||
if (viewportHeight - anchorElementOffset.y - anchorElementSize.height >
|
||||
anchorElementOffset.y) {
|
||||
anchorType += 2;
|
||||
}
|
||||
return goog.ui.Bubble.corners_[anchorType];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Computes the right offset for a given bubble corner
|
||||
* and creates a margin element for it. This is done to have the
|
||||
* button anchor element on its frame rather than on the corner.
|
||||
*
|
||||
* @param {goog.positioning.Corner} corner The corner.
|
||||
* @return {goog.math.Box} the computed margin. Only left or right fields are
|
||||
* non-zero, but they may be negative.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.Bubble.prototype.createMarginForCorner_ = function(corner) {
|
||||
var margin = new goog.math.Box(0, 0, 0, 0);
|
||||
if (corner & goog.positioning.CornerBit.RIGHT) {
|
||||
margin.right -= this.config_.marginShift;
|
||||
} else {
|
||||
margin.left -= this.config_.marginShift;
|
||||
}
|
||||
return margin;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Computes the HTML string for a given bubble orientation.
|
||||
*
|
||||
* @param {goog.positioning.Corner} corner The corner.
|
||||
* @return {string} The HTML string to place inside the bubble's popup.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.Bubble.prototype.computeHtmlForCorner_ = function(corner) {
|
||||
var bubbleTopClass;
|
||||
var bubbleBottomClass;
|
||||
switch (corner) {
|
||||
case goog.positioning.Corner.TOP_LEFT:
|
||||
bubbleTopClass = this.config_.cssBubbleTopLeftAnchor;
|
||||
bubbleBottomClass = this.config_.cssBubbleBottomNoAnchor;
|
||||
break;
|
||||
case goog.positioning.Corner.TOP_RIGHT:
|
||||
bubbleTopClass = this.config_.cssBubbleTopRightAnchor;
|
||||
bubbleBottomClass = this.config_.cssBubbleBottomNoAnchor;
|
||||
break;
|
||||
case goog.positioning.Corner.BOTTOM_LEFT:
|
||||
bubbleTopClass = this.config_.cssBubbleTopNoAnchor;
|
||||
bubbleBottomClass = this.config_.cssBubbleBottomLeftAnchor;
|
||||
break;
|
||||
case goog.positioning.Corner.BOTTOM_RIGHT:
|
||||
bubbleTopClass = this.config_.cssBubbleTopNoAnchor;
|
||||
bubbleBottomClass = this.config_.cssBubbleBottomRightAnchor;
|
||||
break;
|
||||
default:
|
||||
throw Error('This corner type is not supported by bubble!');
|
||||
}
|
||||
var message = null;
|
||||
if (typeof this.message_ == 'object') {
|
||||
message = '<div id="' + this.messageId_ + '">';
|
||||
} else {
|
||||
message = this.message_;
|
||||
}
|
||||
var html =
|
||||
'<table border=0 cellspacing=0 cellpadding=0 style="z-index:1"' +
|
||||
' width=' + this.config_.bubbleWidth + '>' +
|
||||
'<tr><td colspan=4 class="' + bubbleTopClass + '">' +
|
||||
'<tr>' +
|
||||
'<td class="' + this.config_.cssBubbleLeft + '">' +
|
||||
'<td class="' + this.config_.cssBubbleFont + '"' +
|
||||
' style="padding:0 4px;background:white">' + message +
|
||||
'<td id="' + this.closeButtonId_ + '"' +
|
||||
' class="' + this.config_.cssCloseButton + '"/>' +
|
||||
'<td class="' + this.config_.cssBubbleRight + '">' +
|
||||
'<tr>' +
|
||||
'<td colspan=4 class="' + bubbleBottomClass + '">' +
|
||||
'</table>';
|
||||
return html;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A default configuration for the bubble.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
goog.ui.Bubble.defaultConfig = {
|
||||
bubbleWidth: 147,
|
||||
marginShift: 60,
|
||||
cssBubbleFont: goog.getCssName('goog-bubble-font'),
|
||||
cssCloseButton: goog.getCssName('goog-bubble-close-button'),
|
||||
cssBubbleTopRightAnchor: goog.getCssName('goog-bubble-top-right-anchor'),
|
||||
cssBubbleTopLeftAnchor: goog.getCssName('goog-bubble-top-left-anchor'),
|
||||
cssBubbleTopNoAnchor: goog.getCssName('goog-bubble-top-no-anchor'),
|
||||
cssBubbleBottomRightAnchor:
|
||||
goog.getCssName('goog-bubble-bottom-right-anchor'),
|
||||
cssBubbleBottomLeftAnchor: goog.getCssName('goog-bubble-bottom-left-anchor'),
|
||||
cssBubbleBottomNoAnchor: goog.getCssName('goog-bubble-bottom-no-anchor'),
|
||||
cssBubbleLeft: goog.getCssName('goog-bubble-left'),
|
||||
cssBubbleRight: goog.getCssName('goog-bubble-right')
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* An auxiliary array optimizing the corner computation.
|
||||
*
|
||||
* @type {Array.<goog.positioning.Corner>}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.Bubble.corners_ = [
|
||||
goog.positioning.Corner.BOTTOM_RIGHT,
|
||||
goog.positioning.Corner.BOTTOM_LEFT,
|
||||
goog.positioning.Corner.TOP_RIGHT,
|
||||
goog.positioning.Corner.TOP_LEFT
|
||||
];
|
||||
213
nicer-api-docs/closure-library/closure/goog/ui/button.js
Normal file
213
nicer-api-docs/closure-library/closure/goog/ui/button.js
Normal file
@@ -0,0 +1,213 @@
|
||||
// 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 A button control. This implementation extends {@link
|
||||
* goog.ui.Control}.
|
||||
*
|
||||
* @author attila@google.com (Attila Bodis)
|
||||
* @see ../demos/button.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.Button');
|
||||
goog.provide('goog.ui.Button.Side');
|
||||
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.events.KeyCodes');
|
||||
goog.require('goog.events.KeyHandler');
|
||||
goog.require('goog.ui.ButtonRenderer');
|
||||
goog.require('goog.ui.ButtonSide');
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('goog.ui.Control');
|
||||
goog.require('goog.ui.NativeButtonRenderer');
|
||||
goog.require('goog.ui.registry');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A button control, rendered as a native browser button by default.
|
||||
*
|
||||
* @param {goog.ui.ControlContent} content Text caption or existing DOM
|
||||
* structure to display as the button's caption.
|
||||
* @param {goog.ui.ButtonRenderer=} opt_renderer Renderer used to render or
|
||||
* decorate the button; defaults to {@link goog.ui.NativeButtonRenderer}.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM hepler, used for
|
||||
* document interaction.
|
||||
* @constructor
|
||||
* @extends {goog.ui.Control}
|
||||
*/
|
||||
goog.ui.Button = function(content, opt_renderer, opt_domHelper) {
|
||||
goog.ui.Control.call(this, content, opt_renderer ||
|
||||
goog.ui.NativeButtonRenderer.getInstance(), opt_domHelper);
|
||||
};
|
||||
goog.inherits(goog.ui.Button, goog.ui.Control);
|
||||
|
||||
|
||||
/**
|
||||
* Constants for button sides, see {@link goog.ui.Button.prototype.setCollapsed}
|
||||
* for details. Aliased from goog.ui.ButtonSide to support legacy users without
|
||||
* creating a circular dependency in {@link goog.ui.ButtonRenderer}.
|
||||
* @enum {number}
|
||||
* @deprecated use {@link goog.ui.ButtonSide} instead.
|
||||
*/
|
||||
goog.ui.Button.Side = goog.ui.ButtonSide;
|
||||
|
||||
|
||||
/**
|
||||
* Value associated with the button.
|
||||
* @type {*}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.Button.prototype.value_;
|
||||
|
||||
|
||||
/**
|
||||
* Tooltip text for the button, displayed on hover.
|
||||
* @type {string|undefined}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.Button.prototype.tooltip_;
|
||||
|
||||
|
||||
// goog.ui.Button API implementation.
|
||||
|
||||
|
||||
/**
|
||||
* Returns the value associated with the button.
|
||||
* @return {*} Button value (undefined if none).
|
||||
*/
|
||||
goog.ui.Button.prototype.getValue = function() {
|
||||
return this.value_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the value associated with the button, and updates its DOM.
|
||||
* @param {*} value New button value.
|
||||
*/
|
||||
goog.ui.Button.prototype.setValue = function(value) {
|
||||
this.value_ = value;
|
||||
var renderer = /** @type {!goog.ui.ButtonRenderer} */ (this.getRenderer());
|
||||
renderer.setValue(this.getElement(), /** @type {string} */ (value));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the value associated with the button. Unlike {@link #setValue},
|
||||
* doesn't update the button's DOM. Considered protected; to be called only
|
||||
* by renderer code during element decoration.
|
||||
* @param {*} value New button value.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.Button.prototype.setValueInternal = function(value) {
|
||||
this.value_ = value;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the tooltip for the button.
|
||||
* @return {string|undefined} Tooltip text (undefined if none).
|
||||
*/
|
||||
goog.ui.Button.prototype.getTooltip = function() {
|
||||
return this.tooltip_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the tooltip for the button, and updates its DOM.
|
||||
* @param {string} tooltip New tooltip text.
|
||||
*/
|
||||
goog.ui.Button.prototype.setTooltip = function(tooltip) {
|
||||
this.tooltip_ = tooltip;
|
||||
this.getRenderer().setTooltip(this.getElement(), tooltip);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the tooltip for the button. Unlike {@link #setTooltip}, doesn't update
|
||||
* the button's DOM. Considered protected; to be called only by renderer code
|
||||
* during element decoration.
|
||||
* @param {string} tooltip New tooltip text.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.Button.prototype.setTooltipInternal = function(tooltip) {
|
||||
this.tooltip_ = tooltip;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Collapses the border on one or both sides of the button, allowing it to be
|
||||
* combined with the adjancent button(s), forming a single UI componenet with
|
||||
* multiple targets.
|
||||
* @param {number} sides Bitmap of one or more {@link goog.ui.ButtonSide}s for
|
||||
* which borders should be collapsed.
|
||||
*/
|
||||
goog.ui.Button.prototype.setCollapsed = function(sides) {
|
||||
this.getRenderer().setCollapsed(this, sides);
|
||||
};
|
||||
|
||||
|
||||
// goog.ui.Control & goog.ui.Component API implementation.
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.Button.prototype.disposeInternal = function() {
|
||||
goog.ui.Button.superClass_.disposeInternal.call(this);
|
||||
delete this.value_;
|
||||
delete this.tooltip_;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.Button.prototype.enterDocument = function() {
|
||||
goog.ui.Button.superClass_.enterDocument.call(this);
|
||||
if (this.isSupportedState(goog.ui.Component.State.FOCUSED)) {
|
||||
var keyTarget = this.getKeyEventTarget();
|
||||
if (keyTarget) {
|
||||
this.getHandler().listen(keyTarget, goog.events.EventType.KEYUP,
|
||||
this.handleKeyEventInternal);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Attempts to handle a keyboard event; returns true if the event was handled,
|
||||
* false otherwise. If the button is enabled and the Enter/Space key was
|
||||
* pressed, handles the event by dispatching an {@code ACTION} event,
|
||||
* and returns true. Overrides {@link goog.ui.Control#handleKeyEventInternal}.
|
||||
* @param {goog.events.KeyEvent} e Key event to handle.
|
||||
* @return {boolean} Whether the key event was handled.
|
||||
* @protected
|
||||
* @override
|
||||
*/
|
||||
goog.ui.Button.prototype.handleKeyEventInternal = function(e) {
|
||||
if (e.keyCode == goog.events.KeyCodes.ENTER &&
|
||||
e.type == goog.events.KeyHandler.EventType.KEY ||
|
||||
e.keyCode == goog.events.KeyCodes.SPACE &&
|
||||
e.type == goog.events.EventType.KEYUP) {
|
||||
return this.performActionInternal(e);
|
||||
}
|
||||
// Return true for space keypress (even though the event is handled on keyup)
|
||||
// as preventDefault needs to be called up keypress to take effect in IE and
|
||||
// WebKit.
|
||||
return e.keyCode == goog.events.KeyCodes.SPACE;
|
||||
};
|
||||
|
||||
|
||||
// Register a decorator factory function for goog.ui.Buttons.
|
||||
goog.ui.registry.setDecoratorByClassName(goog.ui.ButtonRenderer.CSS_CLASS,
|
||||
function() {
|
||||
return new goog.ui.Button(null);
|
||||
});
|
||||
214
nicer-api-docs/closure-library/closure/goog/ui/buttonrenderer.js
Normal file
214
nicer-api-docs/closure-library/closure/goog/ui/buttonrenderer.js
Normal file
@@ -0,0 +1,214 @@
|
||||
// Copyright 2008 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 Default renderer for {@link goog.ui.Button}s.
|
||||
*
|
||||
* @author attila@google.com (Attila Bodis)
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.ButtonRenderer');
|
||||
|
||||
goog.require('goog.a11y.aria');
|
||||
goog.require('goog.a11y.aria.Role');
|
||||
goog.require('goog.a11y.aria.State');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.ui.ButtonSide');
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('goog.ui.ControlRenderer');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Default renderer for {@link goog.ui.Button}s. Extends the superclass with
|
||||
* the following button-specific API methods:
|
||||
* <ul>
|
||||
* <li>{@code getValue} - returns the button element's value
|
||||
* <li>{@code setValue} - updates the button element to reflect its new value
|
||||
* <li>{@code getTooltip} - returns the button element's tooltip text
|
||||
* <li>{@code setTooltip} - updates the button element's tooltip text
|
||||
* <li>{@code setCollapsed} - removes one or both of the button element's
|
||||
* borders
|
||||
* </ul>
|
||||
* For alternate renderers, see {@link goog.ui.NativeButtonRenderer},
|
||||
* {@link goog.ui.CustomButtonRenderer}, and {@link goog.ui.FlatButtonRenderer}.
|
||||
* @constructor
|
||||
* @extends {goog.ui.ControlRenderer}
|
||||
*/
|
||||
goog.ui.ButtonRenderer = function() {
|
||||
goog.ui.ControlRenderer.call(this);
|
||||
};
|
||||
goog.inherits(goog.ui.ButtonRenderer, goog.ui.ControlRenderer);
|
||||
goog.addSingletonGetter(goog.ui.ButtonRenderer);
|
||||
|
||||
|
||||
/**
|
||||
* Default CSS class to be applied to the root element of components rendered
|
||||
* by this renderer.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.ButtonRenderer.CSS_CLASS = goog.getCssName('goog-button');
|
||||
|
||||
|
||||
/**
|
||||
* Returns the ARIA role to be applied to buttons.
|
||||
* @return {goog.a11y.aria.Role|undefined} ARIA role.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.ButtonRenderer.prototype.getAriaRole = function() {
|
||||
return goog.a11y.aria.Role.BUTTON;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Updates the button's ARIA (accessibility) state if the button is being
|
||||
* treated as a checkbox. Also makes sure that attributes which aren't
|
||||
* supported by buttons aren't being added.
|
||||
* @param {Element} element Element whose ARIA state is to be updated.
|
||||
* @param {goog.ui.Component.State} state Component state being enabled or
|
||||
* disabled.
|
||||
* @param {boolean} enable Whether the state is being enabled or disabled.
|
||||
* @protected
|
||||
* @override
|
||||
*/
|
||||
goog.ui.ButtonRenderer.prototype.updateAriaState = function(element, state,
|
||||
enable) {
|
||||
switch (state) {
|
||||
// If button has CHECKED or SELECTED state, assign aria-pressed
|
||||
case goog.ui.Component.State.SELECTED:
|
||||
case goog.ui.Component.State.CHECKED:
|
||||
goog.asserts.assert(element,
|
||||
'The button DOM element cannot be null.');
|
||||
goog.a11y.aria.setState(element, goog.a11y.aria.State.PRESSED, enable);
|
||||
break;
|
||||
default:
|
||||
case goog.ui.Component.State.OPENED:
|
||||
case goog.ui.Component.State.DISABLED:
|
||||
goog.base(this, 'updateAriaState', element, state, enable);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.ButtonRenderer.prototype.createDom = function(button) {
|
||||
var element = goog.base(this, 'createDom', button);
|
||||
this.setTooltip(element, button.getTooltip());
|
||||
|
||||
var value = button.getValue();
|
||||
if (value) {
|
||||
this.setValue(element, value);
|
||||
}
|
||||
|
||||
// If this is a toggle button, set ARIA state
|
||||
if (button.isSupportedState(goog.ui.Component.State.CHECKED)) {
|
||||
this.updateAriaState(element, goog.ui.Component.State.CHECKED,
|
||||
button.isChecked());
|
||||
}
|
||||
|
||||
return element;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.ButtonRenderer.prototype.decorate = function(button, element) {
|
||||
// The superclass implementation takes care of common attributes; we only
|
||||
// need to set the value and the tooltip.
|
||||
element = goog.ui.ButtonRenderer.superClass_.decorate.call(this, button,
|
||||
element);
|
||||
|
||||
button.setValueInternal(this.getValue(element));
|
||||
button.setTooltipInternal(this.getTooltip(element));
|
||||
|
||||
// If this is a toggle button, set ARIA state
|
||||
if (button.isSupportedState(goog.ui.Component.State.CHECKED)) {
|
||||
this.updateAriaState(element, goog.ui.Component.State.CHECKED,
|
||||
button.isChecked());
|
||||
}
|
||||
|
||||
return element;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Takes a button's root element, and returns the value associated with it.
|
||||
* No-op in the base class.
|
||||
* @param {Element} element The button's root element.
|
||||
* @return {string|undefined} The button's value (undefined if none).
|
||||
*/
|
||||
goog.ui.ButtonRenderer.prototype.getValue = goog.nullFunction;
|
||||
|
||||
|
||||
/**
|
||||
* Takes a button's root element and a value, and updates the element to reflect
|
||||
* the new value. No-op in the base class.
|
||||
* @param {Element} element The button's root element.
|
||||
* @param {string} value New value.
|
||||
*/
|
||||
goog.ui.ButtonRenderer.prototype.setValue = goog.nullFunction;
|
||||
|
||||
|
||||
/**
|
||||
* Takes a button's root element, and returns its tooltip text.
|
||||
* @param {Element} element The button's root element.
|
||||
* @return {string|undefined} The tooltip text.
|
||||
*/
|
||||
goog.ui.ButtonRenderer.prototype.getTooltip = function(element) {
|
||||
return element.title;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Takes a button's root element and a tooltip string, and updates the element
|
||||
* with the new tooltip.
|
||||
* @param {Element} element The button's root element.
|
||||
* @param {string} tooltip New tooltip text.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.ButtonRenderer.prototype.setTooltip = function(element, tooltip) {
|
||||
// Don't set a title attribute if there isn't a tooltip. Blank title
|
||||
// attributes can be interpreted incorrectly by screen readers.
|
||||
if (element && tooltip) {
|
||||
element.title = tooltip;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Collapses the border on one or both sides of the button, allowing it to be
|
||||
* combined with the adjacent button(s), forming a single UI componenet with
|
||||
* multiple targets.
|
||||
* @param {goog.ui.Button} button Button to update.
|
||||
* @param {number} sides Bitmap of one or more {@link goog.ui.ButtonSide}s for
|
||||
* which borders should be collapsed.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.ButtonRenderer.prototype.setCollapsed = function(button, sides) {
|
||||
var isRtl = button.isRightToLeft();
|
||||
var collapseLeftClassName =
|
||||
goog.getCssName(this.getStructuralCssClass(), 'collapse-left');
|
||||
var collapseRightClassName =
|
||||
goog.getCssName(this.getStructuralCssClass(), 'collapse-right');
|
||||
|
||||
button.enableClassName(isRtl ? collapseRightClassName : collapseLeftClassName,
|
||||
!!(sides & goog.ui.ButtonSide.START));
|
||||
button.enableClassName(isRtl ? collapseLeftClassName : collapseRightClassName,
|
||||
!!(sides & goog.ui.ButtonSide.END));
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.ButtonRenderer.prototype.getCssClass = function() {
|
||||
return goog.ui.ButtonRenderer.CSS_CLASS;
|
||||
};
|
||||
41
nicer-api-docs/closure-library/closure/goog/ui/buttonside.js
Normal file
41
nicer-api-docs/closure-library/closure/goog/ui/buttonside.js
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright 2010 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 Enum for button side constants. In its own file so as to not
|
||||
* cause a circular dependency with {@link goog.ui.ButtonRenderer}.
|
||||
*
|
||||
* @author doughtie@google.com (Gavin Doughtie)
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.ButtonSide');
|
||||
|
||||
|
||||
/**
|
||||
* Constants for button sides, see {@link goog.ui.Button.prototype.setCollapsed}
|
||||
* for details.
|
||||
* @enum {number}
|
||||
*/
|
||||
goog.ui.ButtonSide = {
|
||||
/** Neither side. */
|
||||
NONE: 0,
|
||||
/** Left for LTR, right for RTL. */
|
||||
START: 1,
|
||||
/** Right for LTR, left for RTL. */
|
||||
END: 2,
|
||||
/** Both sides. */
|
||||
BOTH: 3
|
||||
};
|
||||
|
||||
|
||||
199
nicer-api-docs/closure-library/closure/goog/ui/charcounter.js
Normal file
199
nicer-api-docs/closure-library/closure/goog/ui/charcounter.js
Normal file
@@ -0,0 +1,199 @@
|
||||
// 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 Character counter widget implementation.
|
||||
*
|
||||
* @author eae@google.com (Emil A Eklund)
|
||||
* @see ../demos/charcounter.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.CharCounter');
|
||||
goog.provide('goog.ui.CharCounter.Display');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.EventTarget');
|
||||
goog.require('goog.events.InputHandler');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* CharCounter widget. Counts the number of characters in a input field or a
|
||||
* text box and displays the number of additional characters that may be
|
||||
* entered before the maximum length is reached.
|
||||
*
|
||||
* @extends {goog.events.EventTarget}
|
||||
* @param {HTMLInputElement|HTMLTextAreaElement} elInput Input or text area
|
||||
* element to count the number of characters in. You can pass in null
|
||||
* for this if you don't want to expose the number of chars remaining.
|
||||
* @param {Element} elCount HTML element to display the remaining number of
|
||||
* characters in.
|
||||
* @param {number} maxLength The maximum length.
|
||||
* @param {goog.ui.CharCounter.Display=} opt_displayMode Display mode for this
|
||||
* char counter. Defaults to {@link goog.ui.CharCounter.Display.REMAINING}.
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.CharCounter = function(elInput, elCount, maxLength, opt_displayMode) {
|
||||
goog.events.EventTarget.call(this);
|
||||
|
||||
/**
|
||||
* Input or text area element to count the number of characters in.
|
||||
* @type {HTMLInputElement|HTMLTextAreaElement}
|
||||
* @private
|
||||
*/
|
||||
this.elInput_ = elInput;
|
||||
|
||||
/**
|
||||
* HTML element to display the remaining number of characters in.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
this.elCount_ = elCount;
|
||||
|
||||
/**
|
||||
* The maximum length.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.maxLength_ = maxLength;
|
||||
|
||||
/**
|
||||
* The display mode for this char counter.
|
||||
* @type {!goog.ui.CharCounter.Display}
|
||||
* @private
|
||||
*/
|
||||
this.display_ = opt_displayMode || goog.ui.CharCounter.Display.REMAINING;
|
||||
|
||||
elInput.maxLength = maxLength;
|
||||
|
||||
/**
|
||||
* The input handler that provides the input event.
|
||||
* @type {goog.events.InputHandler}
|
||||
* @private
|
||||
*/
|
||||
this.inputHandler_ = new goog.events.InputHandler(elInput);
|
||||
|
||||
goog.events.listen(this.inputHandler_,
|
||||
goog.events.InputHandler.EventType.INPUT, this.onChange_, false, this);
|
||||
|
||||
this.checkLength();
|
||||
};
|
||||
goog.inherits(goog.ui.CharCounter, goog.events.EventTarget);
|
||||
|
||||
|
||||
/**
|
||||
* Display mode for the char counter.
|
||||
* @enum {number}
|
||||
*/
|
||||
goog.ui.CharCounter.Display = {
|
||||
/** Widget displays the number of characters remaining (the default). */
|
||||
REMAINING: 0,
|
||||
/** Widget displays the number of characters entered. */
|
||||
INCREMENTAL: 1
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the maximum length.
|
||||
*
|
||||
* @param {number} maxLength The maximum length.
|
||||
*/
|
||||
goog.ui.CharCounter.prototype.setMaxLength = function(maxLength) {
|
||||
this.maxLength_ = maxLength;
|
||||
this.elInput_.maxLength = maxLength;
|
||||
this.checkLength();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the maximum length.
|
||||
*
|
||||
* @return {number} The maximum length.
|
||||
*/
|
||||
goog.ui.CharCounter.prototype.getMaxLength = function() {
|
||||
return this.maxLength_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the display mode.
|
||||
*
|
||||
* @param {!goog.ui.CharCounter.Display} displayMode The display mode.
|
||||
*/
|
||||
goog.ui.CharCounter.prototype.setDisplayMode = function(displayMode) {
|
||||
this.display_ = displayMode;
|
||||
this.checkLength();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the display mode.
|
||||
*
|
||||
* @return {!goog.ui.CharCounter.Display} The display mode.
|
||||
*/
|
||||
goog.ui.CharCounter.prototype.getDisplayMode = function() {
|
||||
return this.display_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Change event handler for input field.
|
||||
*
|
||||
* @param {goog.events.BrowserEvent} event Change event.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharCounter.prototype.onChange_ = function(event) {
|
||||
this.checkLength();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Checks length of text in input field and updates the counter. Truncates text
|
||||
* if the maximum lengths is exceeded.
|
||||
*/
|
||||
goog.ui.CharCounter.prototype.checkLength = function() {
|
||||
var count = this.elInput_.value.length;
|
||||
|
||||
// There's no maxlength property for textareas so instead we truncate the
|
||||
// text if it gets too long. It's also used to truncate the text in a input
|
||||
// field if the maximum length is changed.
|
||||
if (count > this.maxLength_) {
|
||||
|
||||
var scrollTop = this.elInput_.scrollTop;
|
||||
var scrollLeft = this.elInput_.scrollLeft;
|
||||
|
||||
this.elInput_.value = this.elInput_.value.substring(0, this.maxLength_);
|
||||
count = this.maxLength_;
|
||||
|
||||
this.elInput_.scrollTop = scrollTop;
|
||||
this.elInput_.scrollLeft = scrollLeft;
|
||||
}
|
||||
|
||||
if (this.elCount_) {
|
||||
var incremental = this.display_ == goog.ui.CharCounter.Display.INCREMENTAL;
|
||||
goog.dom.setTextContent(
|
||||
this.elCount_,
|
||||
String(incremental ? count : this.maxLength_ - count));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.CharCounter.prototype.disposeInternal = function() {
|
||||
goog.ui.CharCounter.superClass_.disposeInternal.call(this);
|
||||
delete this.elInput_;
|
||||
this.inputHandler_.dispose();
|
||||
this.inputHandler_ = null;
|
||||
};
|
||||
880
nicer-api-docs/closure-library/closure/goog/ui/charpicker.js
Normal file
880
nicer-api-docs/closure-library/closure/goog/ui/charpicker.js
Normal file
@@ -0,0 +1,880 @@
|
||||
// Copyright 2009 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 Character Picker widget for picking any Unicode character.
|
||||
*
|
||||
* @see ../demos/charpicker.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.CharPicker');
|
||||
|
||||
goog.require('goog.a11y.aria');
|
||||
goog.require('goog.a11y.aria.State');
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.dom.classes');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.Event');
|
||||
goog.require('goog.events.EventHandler');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.events.InputHandler');
|
||||
goog.require('goog.events.KeyCodes');
|
||||
goog.require('goog.events.KeyHandler');
|
||||
goog.require('goog.i18n.CharListDecompressor');
|
||||
goog.require('goog.i18n.uChar');
|
||||
goog.require('goog.structs.Set');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.Button');
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('goog.ui.ContainerScroller');
|
||||
goog.require('goog.ui.FlatButtonRenderer');
|
||||
goog.require('goog.ui.HoverCard');
|
||||
goog.require('goog.ui.LabelInput');
|
||||
goog.require('goog.ui.Menu');
|
||||
goog.require('goog.ui.MenuButton');
|
||||
goog.require('goog.ui.MenuItem');
|
||||
goog.require('goog.ui.Tooltip');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Character Picker Class. This widget can be used to pick any Unicode
|
||||
* character by traversing a category-subcategory structure or by inputing its
|
||||
* hex value.
|
||||
*
|
||||
* See charpicker.html demo for example usage.
|
||||
* @param {goog.i18n.CharPickerData} charPickerData Category names and charlist.
|
||||
* @param {!goog.i18n.uChar.NameFetcher} charNameFetcher Object which fetches
|
||||
* the names of the characters that are shown in the widget. These names
|
||||
* may be stored locally or come from an external source.
|
||||
* @param {Array.<string>=} opt_recents List of characters to be displayed in
|
||||
* resently selected characters area.
|
||||
* @param {number=} opt_initCategory Sequence number of initial category.
|
||||
* @param {number=} opt_initSubcategory Sequence number of initial subcategory.
|
||||
* @param {number=} opt_rowCount Number of rows in the grid.
|
||||
* @param {number=} opt_columnCount Number of columns in the grid.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
|
||||
* @constructor
|
||||
* @extends {goog.ui.Component}
|
||||
*/
|
||||
goog.ui.CharPicker = function(charPickerData, charNameFetcher, opt_recents,
|
||||
opt_initCategory, opt_initSubcategory,
|
||||
opt_rowCount, opt_columnCount, opt_domHelper) {
|
||||
goog.ui.Component.call(this, opt_domHelper);
|
||||
|
||||
/**
|
||||
* Object used to retrieve character names.
|
||||
* @type {!goog.i18n.uChar.NameFetcher}
|
||||
* @private
|
||||
*/
|
||||
this.charNameFetcher_ = charNameFetcher;
|
||||
|
||||
/**
|
||||
* Object containing character lists and category names.
|
||||
* @type {goog.i18n.CharPickerData}
|
||||
* @private
|
||||
*/
|
||||
this.data_ = charPickerData;
|
||||
|
||||
/**
|
||||
* The category number to be used on widget init.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.initCategory_ = opt_initCategory || 0;
|
||||
|
||||
/**
|
||||
* The subcategory number to be used on widget init.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.initSubcategory_ = opt_initSubcategory || 0;
|
||||
|
||||
/**
|
||||
* Number of columns in the grid.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.columnCount_ = opt_columnCount || 10;
|
||||
|
||||
/**
|
||||
* Number of entries to be added to the grid.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.gridsize_ = (opt_rowCount || 10) * this.columnCount_;
|
||||
|
||||
/**
|
||||
* Number of the recently selected characters displayed.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.recentwidth_ = this.columnCount_ + 1;
|
||||
|
||||
/**
|
||||
* List of recently used characters.
|
||||
* @type {Array.<string>}
|
||||
* @private
|
||||
*/
|
||||
this.recents_ = opt_recents || [];
|
||||
|
||||
/**
|
||||
* Handler for events.
|
||||
* @type {goog.events.EventHandler}
|
||||
* @private
|
||||
*/
|
||||
this.eventHandler_ = new goog.events.EventHandler(this);
|
||||
|
||||
/**
|
||||
* Decompressor used to get the list of characters from a base88 encoded
|
||||
* character list.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
this.decompressor_ = new goog.i18n.CharListDecompressor();
|
||||
};
|
||||
goog.inherits(goog.ui.CharPicker, goog.ui.Component);
|
||||
|
||||
|
||||
/**
|
||||
* The last selected character.
|
||||
* @type {?string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.selectedChar_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Set of formatting characters whose display need to be swapped with nbsp
|
||||
* to prevent layout issues.
|
||||
* @type {goog.structs.Set}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.layoutAlteringChars_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* The top category menu.
|
||||
* @type {goog.ui.Menu}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.menu_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* The top category menu button.
|
||||
* @type {goog.ui.MenuButton}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.menubutton_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* The subcategory menu.
|
||||
* @type {goog.ui.Menu}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.submenu_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* The subcategory menu button.
|
||||
* @type {goog.ui.MenuButton}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.submenubutton_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* The element representing the number of rows visible in the grid.
|
||||
* This along with goog.ui.CharPicker.stick_ would help to create a scrollbar
|
||||
* of right size.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.stickwrap_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* The component containing all the buttons for each character in display.
|
||||
* @type {goog.ui.Component}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.grid_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* The component used for extra information about the character set displayed.
|
||||
* @type {goog.ui.Component}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.notice_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Grid displaying recently selected characters.
|
||||
* @type {goog.ui.Component}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.recentgrid_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Input field for entering the hex value of the character.
|
||||
* @type {goog.ui.Component}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.input_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* OK button for entering hex value of the character.
|
||||
* @type {goog.ui.Component}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.okbutton_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Element displaying character name in preview.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.charNameEl_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Element displaying character in preview.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.zoomEl_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Element displaying character number (codepoint) in preview.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.unicodeEl_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Hover card for displaying the preview of a character.
|
||||
* Preview would contain character in large size and its U+ notation. It would
|
||||
* also display the name, if available.
|
||||
* @type {goog.ui.HoverCard}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.hc_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Gets the last selected character.
|
||||
* @return {?string} The last selected character.
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.getSelectedChar = function() {
|
||||
return this.selectedChar_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the list of characters user selected recently.
|
||||
* @return {Array.<string>} The recent character list.
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.getRecentChars = function() {
|
||||
return this.recents_;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.CharPicker.prototype.createDom = function() {
|
||||
goog.ui.CharPicker.superClass_.createDom.call(this);
|
||||
|
||||
this.decorateInternal(this.getDomHelper().createElement('div'));
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.CharPicker.prototype.disposeInternal = function() {
|
||||
goog.dispose(this.hc_);
|
||||
this.hc_ = null;
|
||||
goog.dispose(this.eventHandler_);
|
||||
this.eventHandler_ = null;
|
||||
goog.ui.CharPicker.superClass_.disposeInternal.call(this);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.CharPicker.prototype.decorateInternal = function(element) {
|
||||
goog.ui.CharPicker.superClass_.decorateInternal.call(this, element);
|
||||
|
||||
// The chars below cause layout disruption or too narrow to hover:
|
||||
// \u0020, \u00AD, \u2000 - \u200f, \u2028 - \u202f, \u3000, \ufeff
|
||||
var chrs = this.decompressor_.toCharList(':2%C^O80V1H2s2G40Q%s0');
|
||||
this.layoutAlteringChars_ = new goog.structs.Set(chrs);
|
||||
|
||||
this.menu_ = new goog.ui.Menu(this.getDomHelper());
|
||||
|
||||
var categories = this.data_.categories;
|
||||
for (var i = 0; i < this.data_.categories.length; i++) {
|
||||
this.menu_.addChild(this.createMenuItem_(i, categories[i]), true);
|
||||
}
|
||||
|
||||
this.menubutton_ = new goog.ui.MenuButton('Category Menu', this.menu_,
|
||||
/* opt_renderer */ undefined, this.getDomHelper());
|
||||
this.addChild(this.menubutton_, true);
|
||||
|
||||
this.submenu_ = new goog.ui.Menu(this.getDomHelper());
|
||||
|
||||
this.submenubutton_ = new goog.ui.MenuButton('Subcategory Menu',
|
||||
this.submenu_, /* opt_renderer */ undefined, this.getDomHelper());
|
||||
this.addChild(this.submenubutton_, true);
|
||||
|
||||
// The containing component for grid component and the scroller.
|
||||
var gridcontainer = new goog.ui.Component(this.getDomHelper());
|
||||
this.addChild(gridcontainer, true);
|
||||
|
||||
var stickwrap = new goog.ui.Component(this.getDomHelper());
|
||||
gridcontainer.addChild(stickwrap, true);
|
||||
this.stickwrap_ = stickwrap.getElement();
|
||||
|
||||
var stick = new goog.ui.Component(this.getDomHelper());
|
||||
stickwrap.addChild(stick, true);
|
||||
this.stick_ = stick.getElement();
|
||||
|
||||
this.grid_ = new goog.ui.Component(this.getDomHelper());
|
||||
gridcontainer.addChild(this.grid_, true);
|
||||
|
||||
this.notice_ = new goog.ui.Component(this.getDomHelper());
|
||||
this.notice_.setElementInternal(this.getDomHelper().createDom('div'));
|
||||
this.addChild(this.notice_, true);
|
||||
|
||||
// The component used for displaying 'Recent Selections' label.
|
||||
/**
|
||||
* @desc The text label above the list of recently selected characters.
|
||||
*/
|
||||
var MSG_CHAR_PICKER_RECENT_SELECTIONS = goog.getMsg('Recent Selections:');
|
||||
var recenttext = new goog.ui.Component(this.getDomHelper());
|
||||
recenttext.setElementInternal(this.getDomHelper().createDom('span', null,
|
||||
MSG_CHAR_PICKER_RECENT_SELECTIONS));
|
||||
this.addChild(recenttext, true);
|
||||
|
||||
this.recentgrid_ = new goog.ui.Component(this.getDomHelper());
|
||||
this.addChild(this.recentgrid_, true);
|
||||
|
||||
// The component used for displaying 'U+'.
|
||||
var uplus = new goog.ui.Component(this.getDomHelper());
|
||||
uplus.setElementInternal(this.getDomHelper().createDom('span', null, 'U+'));
|
||||
this.addChild(uplus, true);
|
||||
|
||||
/**
|
||||
* @desc The text inside the input box to specify the hex code of a character.
|
||||
*/
|
||||
var MSG_CHAR_PICKER_HEX_INPUT = goog.getMsg('Hex Input');
|
||||
this.input_ = new goog.ui.LabelInput(
|
||||
MSG_CHAR_PICKER_HEX_INPUT, this.getDomHelper());
|
||||
this.addChild(this.input_, true);
|
||||
|
||||
this.okbutton_ = new goog.ui.Button(
|
||||
'OK', /* opt_renderer */ undefined, this.getDomHelper());
|
||||
this.addChild(this.okbutton_, true);
|
||||
this.okbutton_.setEnabled(false);
|
||||
|
||||
this.zoomEl_ = this.getDomHelper().createDom('div',
|
||||
{id: 'zoom', className: goog.getCssName('goog-char-picker-char-zoom')});
|
||||
|
||||
this.charNameEl_ = this.getDomHelper().createDom('div',
|
||||
{id: 'charName', className: goog.getCssName('goog-char-picker-name')});
|
||||
|
||||
this.unicodeEl_ = this.getDomHelper().createDom('div',
|
||||
{id: 'unicode', className: goog.getCssName('goog-char-picker-unicode')});
|
||||
|
||||
var card = this.getDomHelper().createDom('div',
|
||||
{'id': 'preview'},
|
||||
this.zoomEl_, this.charNameEl_, this.unicodeEl_);
|
||||
goog.style.setElementShown(card, false);
|
||||
this.hc_ = new goog.ui.HoverCard({'DIV': 'char'},
|
||||
/* opt_checkDescendants */ undefined, this.getDomHelper());
|
||||
this.hc_.setElement(card);
|
||||
var self = this;
|
||||
|
||||
/**
|
||||
* Function called by hover card just before it is visible to collect data.
|
||||
*/
|
||||
function onBeforeShow() {
|
||||
var trigger = self.hc_.getAnchorElement();
|
||||
var ch = self.getChar_(trigger);
|
||||
if (ch) {
|
||||
self.zoomEl_.innerHTML = self.displayChar_(ch);
|
||||
self.unicodeEl_.innerHTML = goog.i18n.uChar.toHexString(ch);
|
||||
// Clear the character name since we don't want to show old data because
|
||||
// it is retrieved asynchronously and the DOM object is re-used
|
||||
self.charNameEl_.innerHTML = '';
|
||||
self.charNameFetcher_.getName(ch, function(charName) {
|
||||
if (charName) {
|
||||
self.charNameEl_.innerHTML = charName;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
goog.events.listen(this.hc_, goog.ui.HoverCard.EventType.BEFORE_SHOW,
|
||||
onBeforeShow);
|
||||
|
||||
goog.dom.classes.add(element, goog.getCssName('goog-char-picker'));
|
||||
goog.dom.classes.add(this.stick_, goog.getCssName('goog-stick'));
|
||||
goog.dom.classes.add(this.stickwrap_, goog.getCssName('goog-stickwrap'));
|
||||
goog.dom.classes.add(gridcontainer.getElement(),
|
||||
goog.getCssName('goog-char-picker-grid-container'));
|
||||
goog.dom.classes.add(this.grid_.getElement(),
|
||||
goog.getCssName('goog-char-picker-grid'));
|
||||
goog.dom.classes.add(this.recentgrid_.getElement(),
|
||||
goog.getCssName('goog-char-picker-grid'));
|
||||
goog.dom.classes.add(this.recentgrid_.getElement(),
|
||||
goog.getCssName('goog-char-picker-recents'));
|
||||
|
||||
goog.dom.classes.add(this.notice_.getElement(),
|
||||
goog.getCssName('goog-char-picker-notice'));
|
||||
goog.dom.classes.add(uplus.getElement(),
|
||||
goog.getCssName('goog-char-picker-uplus'));
|
||||
goog.dom.classes.add(this.input_.getElement(),
|
||||
goog.getCssName('goog-char-picker-input-box'));
|
||||
goog.dom.classes.add(this.okbutton_.getElement(),
|
||||
goog.getCssName('goog-char-picker-okbutton'));
|
||||
goog.dom.classes.add(card, goog.getCssName('goog-char-picker-hovercard'));
|
||||
this.hc_.className = goog.getCssName('goog-char-picker-hovercard');
|
||||
|
||||
this.grid_.buttoncount = this.gridsize_;
|
||||
this.recentgrid_.buttoncount = this.recentwidth_;
|
||||
this.populateGridWithButtons_(this.grid_);
|
||||
this.populateGridWithButtons_(this.recentgrid_);
|
||||
|
||||
this.updateGrid_(this.recentgrid_, this.recents_);
|
||||
this.setSelectedCategory_(this.initCategory_, this.initSubcategory_);
|
||||
new goog.ui.ContainerScroller(this.menu_);
|
||||
new goog.ui.ContainerScroller(this.submenu_);
|
||||
|
||||
goog.dom.classes.add(this.menu_.getElement(),
|
||||
goog.getCssName('goog-char-picker-menu'));
|
||||
goog.dom.classes.add(this.submenu_.getElement(),
|
||||
goog.getCssName('goog-char-picker-menu'));
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.CharPicker.prototype.enterDocument = function() {
|
||||
goog.ui.CharPicker.superClass_.enterDocument.call(this);
|
||||
var inputkh = new goog.events.InputHandler(this.input_.getElement());
|
||||
this.keyHandler_ = new goog.events.KeyHandler(this.input_.getElement());
|
||||
|
||||
// Stop the propagation of ACTION events at menu and submenu buttons.
|
||||
// If stopped at capture phase, the button will not be set to normal state.
|
||||
// If not stopped, the user widget will receive the event, which is
|
||||
// undesired. User widget should receive an event only on the character
|
||||
// click.
|
||||
this.eventHandler_.
|
||||
listen(
|
||||
this.menubutton_,
|
||||
goog.ui.Component.EventType.ACTION,
|
||||
goog.events.Event.stopPropagation).
|
||||
listen(
|
||||
this.submenubutton_,
|
||||
goog.ui.Component.EventType.ACTION,
|
||||
goog.events.Event.stopPropagation).
|
||||
listen(
|
||||
this,
|
||||
goog.ui.Component.EventType.ACTION,
|
||||
this.handleSelectedItem_,
|
||||
true).
|
||||
listen(
|
||||
inputkh,
|
||||
goog.events.InputHandler.EventType.INPUT,
|
||||
this.handleInput_).
|
||||
listen(
|
||||
this.keyHandler_,
|
||||
goog.events.KeyHandler.EventType.KEY,
|
||||
this.handleEnter_).
|
||||
listen(
|
||||
this.recentgrid_,
|
||||
goog.ui.Component.EventType.FOCUS,
|
||||
this.handleFocus_).
|
||||
listen(
|
||||
this.grid_,
|
||||
goog.ui.Component.EventType.FOCUS,
|
||||
this.handleFocus_);
|
||||
|
||||
goog.events.listen(this.okbutton_.getElement(),
|
||||
goog.events.EventType.MOUSEDOWN, this.handleOkClick_, true, this);
|
||||
|
||||
goog.events.listen(this.stickwrap_, goog.events.EventType.SCROLL,
|
||||
this.handleScroll_, true, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles the button focus by updating the aria label with the character name
|
||||
* so it becomes possible to get spoken feedback while tabbing through the
|
||||
* visible symbols.
|
||||
* @param {goog.events.Event} e The focus event.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.handleFocus_ = function(e) {
|
||||
var button = e.target;
|
||||
var element = button.getElement();
|
||||
var ch = this.getChar_(element);
|
||||
|
||||
// Clear the aria label to avoid speaking the old value in case the button
|
||||
// element has no char attribute or the character name cannot be retrieved.
|
||||
goog.a11y.aria.setState(element, goog.a11y.aria.State.LABEL, '');
|
||||
|
||||
if (ch) {
|
||||
// This is working with screen readers because the call to getName is
|
||||
// synchronous once the values have been prefetched by the RemoteNameFetcher
|
||||
// and because it is always synchronous when using the LocalNameFetcher.
|
||||
// Also, the special character itself is not used as the label because some
|
||||
// screen readers, notably ChromeVox, are not able to speak them.
|
||||
// TODO(user): Consider changing the NameFetcher API to provide a
|
||||
// method that lets the caller retrieve multiple character names at once
|
||||
// so that this asynchronous gymnastic can be avoided.
|
||||
this.charNameFetcher_.getName(ch, function(charName) {
|
||||
if (charName) {
|
||||
goog.a11y.aria.setState(
|
||||
element, goog.a11y.aria.State.LABEL, charName);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* On scroll, updates the grid with characters correct to the scroll position.
|
||||
* @param {goog.events.Event} e Scroll event to handle.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.handleScroll_ = function(e) {
|
||||
var height = e.target.scrollHeight;
|
||||
var top = e.target.scrollTop;
|
||||
var itempos = Math.ceil(top * this.items.length / (this.columnCount_ *
|
||||
height)) * this.columnCount_;
|
||||
if (this.itempos != itempos) {
|
||||
this.itempos = itempos;
|
||||
this.modifyGridWithItems_(this.grid_, this.items, itempos);
|
||||
}
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* On a menu click, sets correct character set in the grid; on a grid click
|
||||
* accept the character as the selected one and adds to recent selection, if not
|
||||
* already present.
|
||||
* @param {goog.events.Event} e Event for the click on menus or grid.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.handleSelectedItem_ = function(e) {
|
||||
if (e.target.getParent() == this.menu_) {
|
||||
this.menu_.setVisible(false);
|
||||
this.setSelectedCategory_(e.target.getValue());
|
||||
} else if (e.target.getParent() == this.submenu_) {
|
||||
this.submenu_.setVisible(false);
|
||||
this.setSelectedSubcategory_(e.target.getValue());
|
||||
} else if (e.target.getParent() == this.grid_) {
|
||||
var button = e.target.getElement();
|
||||
this.selectedChar_ = this.getChar_(button);
|
||||
this.updateRecents_(this.selectedChar_);
|
||||
} else if (e.target.getParent() == this.recentgrid_) {
|
||||
this.selectedChar_ = this.getChar_(e.target.getElement());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* When user types the characters displays the preview. Enables the OK button,
|
||||
* if the character is valid.
|
||||
* @param {goog.events.Event} e Event for typing in input field.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.handleInput_ = function(e) {
|
||||
var ch = this.getInputChar();
|
||||
if (ch) {
|
||||
var unicode = goog.i18n.uChar.toHexString(ch);
|
||||
this.zoomEl_.innerHTML = ch;
|
||||
this.unicodeEl_.innerHTML = unicode;
|
||||
this.charNameEl_.innerHTML = '';
|
||||
var coord =
|
||||
new goog.ui.Tooltip.ElementTooltipPosition(this.input_.getElement());
|
||||
this.hc_.setPosition(coord);
|
||||
this.hc_.triggerForElement(this.input_.getElement());
|
||||
this.okbutton_.setEnabled(true);
|
||||
} else {
|
||||
this.hc_.cancelTrigger();
|
||||
this.hc_.setVisible(false);
|
||||
this.okbutton_.setEnabled(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* On OK click accepts the character and updates the recent char list.
|
||||
* @param {goog.events.Event=} opt_event Event for click on OK button.
|
||||
* @return {boolean} Indicates whether to propagate event.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.handleOkClick_ = function(opt_event) {
|
||||
var ch = this.getInputChar();
|
||||
if (ch && ch.charCodeAt(0)) {
|
||||
this.selectedChar_ = ch;
|
||||
this.updateRecents_(ch);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Behaves exactly like the OK button on Enter key.
|
||||
* @param {goog.events.KeyEvent} e Event for enter on the input field.
|
||||
* @return {boolean} Indicates whether to propagate event.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.handleEnter_ = function(e) {
|
||||
if (e.keyCode == goog.events.KeyCodes.ENTER) {
|
||||
return this.handleOkClick_() ?
|
||||
this.dispatchEvent(goog.ui.Component.EventType.ACTION) : false;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the character from the event target.
|
||||
* @param {Element} e Event target containing the 'char' attribute.
|
||||
* @return {string} The character specified in the event.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.getChar_ = function(e) {
|
||||
return e.getAttribute('char');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a menu entry for either the category listing or subcategory listing.
|
||||
* @param {number} id Id to be used for the entry.
|
||||
* @param {string} caption Text displayed for the menu item.
|
||||
* @return {goog.ui.MenuItem} Menu item to be added to the menu listing.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.createMenuItem_ = function(id, caption) {
|
||||
var item = new goog.ui.MenuItem(caption, /* model */ id, this.getDomHelper());
|
||||
item.setVisible(true);
|
||||
return item;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the category and updates the submenu items and grid accordingly.
|
||||
* @param {number} category Category index used to index the data tables.
|
||||
* @param {number=} opt_subcategory Subcategory index used with category index.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.setSelectedCategory_ = function(category,
|
||||
opt_subcategory) {
|
||||
this.category = category;
|
||||
this.menubutton_.setCaption(this.data_.categories[category]);
|
||||
while (this.submenu_.hasChildren()) {
|
||||
this.submenu_.removeChildAt(0, true).dispose();
|
||||
}
|
||||
|
||||
var subcategories = this.data_.subcategories[category];
|
||||
var charList = this.data_.charList[category];
|
||||
for (var i = 0; i < subcategories.length; i++) {
|
||||
var subtitle = charList[i].length == 0;
|
||||
var item = this.createMenuItem_(i, subcategories[i]);
|
||||
this.submenu_.addChild(item, true);
|
||||
}
|
||||
this.setSelectedSubcategory_(opt_subcategory || 0);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the subcategory and updates the grid accordingly.
|
||||
* @param {number} subcategory Sub-category index used to index the data tables.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.setSelectedSubcategory_ = function(subcategory) {
|
||||
var subcategories = this.data_.subcategories;
|
||||
var name = subcategories[this.category][subcategory];
|
||||
this.submenubutton_.setCaption(name);
|
||||
this.setSelectedGrid_(this.category, subcategory);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Updates the grid according to a given category and subcategory.
|
||||
* @param {number} category Index to the category table.
|
||||
* @param {number} subcategory Index to the subcategory table.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.setSelectedGrid_ = function(category,
|
||||
subcategory) {
|
||||
var charLists = this.data_.charList;
|
||||
var charListStr = charLists[category][subcategory];
|
||||
var content = this.decompressor_.toCharList(charListStr);
|
||||
this.charNameFetcher_.prefetch(charListStr);
|
||||
this.updateGrid_(this.grid_, content);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Updates the grid with new character list.
|
||||
* @param {goog.ui.Component} grid The grid which is updated with a new set of
|
||||
* characters.
|
||||
* @param {Array.<string>} items Characters to be added to the grid.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.updateGrid_ = function(grid, items) {
|
||||
if (grid == this.grid_) {
|
||||
/**
|
||||
* @desc The message used when there are invisible characters like space
|
||||
* or format control characters.
|
||||
*/
|
||||
var MSG_PLEASE_HOVER =
|
||||
goog.getMsg('Please hover over each cell for the character name.');
|
||||
|
||||
this.notice_.getElement().innerHTML =
|
||||
this.charNameFetcher_.isNameAvailable(items[0]) ? MSG_PLEASE_HOVER : '';
|
||||
this.items = items;
|
||||
if (this.stickwrap_.offsetHeight > 0) {
|
||||
this.stick_.style.height =
|
||||
this.stickwrap_.offsetHeight * items.length / this.gridsize_ + 'px';
|
||||
} else {
|
||||
// This is the last ditch effort if height is not avaialble.
|
||||
// Maximum of 3em is assumed to the the cell height. Extra space after
|
||||
// last character in the grid is OK.
|
||||
this.stick_.style.height = 3 * this.columnCount_ * items.length /
|
||||
this.gridsize_ + 'em';
|
||||
}
|
||||
this.stickwrap_.scrollTop = 0;
|
||||
}
|
||||
|
||||
this.modifyGridWithItems_(grid, items, 0);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Updates the grid with new character list for a given starting point.
|
||||
* @param {goog.ui.Component} grid The grid which is updated with a new set of
|
||||
* characters.
|
||||
* @param {Array.<string>} items Characters to be added to the grid.
|
||||
* @param {number} start The index from which the characters should be
|
||||
* displayed.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.modifyGridWithItems_ = function(grid, items,
|
||||
start) {
|
||||
for (var buttonpos = 0, itempos = start;
|
||||
buttonpos < grid.buttoncount && itempos < items.length;
|
||||
buttonpos++, itempos++) {
|
||||
this.modifyCharNode_(grid.getChildAt(buttonpos), items[itempos]);
|
||||
}
|
||||
|
||||
for (; buttonpos < grid.buttoncount; buttonpos++) {
|
||||
grid.getChildAt(buttonpos).setVisible(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates the grid for characters to displayed for selection.
|
||||
* @param {goog.ui.Component} grid The grid which is updated with a new set of
|
||||
* characters.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.populateGridWithButtons_ = function(grid) {
|
||||
for (var i = 0; i < grid.buttoncount; i++) {
|
||||
var button = new goog.ui.Button(' ',
|
||||
goog.ui.FlatButtonRenderer.getInstance(),
|
||||
this.getDomHelper());
|
||||
|
||||
// Dispatch the focus event so we can update the aria description while
|
||||
// the user tabs through the cells.
|
||||
button.setDispatchTransitionEvents(goog.ui.Component.State.FOCUSED, true);
|
||||
|
||||
grid.addChild(button, true);
|
||||
button.setVisible(false);
|
||||
|
||||
var buttonEl = button.getElement();
|
||||
goog.asserts.assert(buttonEl, 'The button DOM element cannot be null.');
|
||||
|
||||
// Override the button role so the user doesn't hear "button" each time he
|
||||
// tabs through the cells.
|
||||
goog.a11y.aria.removeRole(buttonEl);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Updates the grid cell with new character.
|
||||
* @param {goog.ui.Component} button This button is proped up for new character.
|
||||
* @param {string} ch Character to be displayed by the button.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.modifyCharNode_ = function(button, ch) {
|
||||
var text = this.displayChar_(ch);
|
||||
var buttonEl = button.getElement();
|
||||
buttonEl.innerHTML = text;
|
||||
buttonEl.setAttribute('char', ch);
|
||||
button.setVisible(true);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds a given character to the recent character list.
|
||||
* @param {string} character Character to be added to the recent list.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.updateRecents_ = function(character) {
|
||||
if (character && character.charCodeAt(0) &&
|
||||
!goog.array.contains(this.recents_, character)) {
|
||||
this.recents_.unshift(character);
|
||||
if (this.recents_.length > this.recentwidth_) {
|
||||
this.recents_.pop();
|
||||
}
|
||||
this.updateGrid_(this.recentgrid_, this.recents_);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the user inputed unicode character.
|
||||
* @return {string} Unicode character inputed by user.
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.getInputChar = function() {
|
||||
var text = this.input_.getValue();
|
||||
var code = parseInt(text, 16);
|
||||
return /** @type {string} */ (goog.i18n.uChar.fromCharCode(code));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the display character for the given character.
|
||||
* @param {string} ch Character whose display is fetched.
|
||||
* @return {string} The display of the given character.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CharPicker.prototype.displayChar_ = function(ch) {
|
||||
return this.layoutAlteringChars_.contains(ch) ? '\u00A0' : ch;
|
||||
};
|
||||
269
nicer-api-docs/closure-library/closure/goog/ui/checkbox.js
Normal file
269
nicer-api-docs/closure-library/closure/goog/ui/checkbox.js
Normal file
@@ -0,0 +1,269 @@
|
||||
// Copyright 2009 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 Tristate checkbox widget.
|
||||
*
|
||||
* @see ../demos/checkbox.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.Checkbox');
|
||||
goog.provide('goog.ui.Checkbox.State');
|
||||
|
||||
goog.require('goog.a11y.aria');
|
||||
goog.require('goog.a11y.aria.State');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.events.KeyCodes');
|
||||
goog.require('goog.ui.CheckboxRenderer');
|
||||
goog.require('goog.ui.Component.EventType');
|
||||
goog.require('goog.ui.Component.State');
|
||||
goog.require('goog.ui.Control');
|
||||
goog.require('goog.ui.registry');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 3-state checkbox widget. Fires CHECK or UNCHECK events before toggled and
|
||||
* CHANGE event after toggled by user.
|
||||
* The checkbox can also be enabled/disabled and get focused and highlighted.
|
||||
*
|
||||
* @param {goog.ui.Checkbox.State=} opt_checked Checked state to set.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
|
||||
* document interaction.
|
||||
* @param {goog.ui.CheckboxRenderer=} opt_renderer Renderer used to render or
|
||||
* decorate the checkbox; defaults to {@link goog.ui.CheckboxRenderer}.
|
||||
* @constructor
|
||||
* @extends {goog.ui.Control}
|
||||
*/
|
||||
goog.ui.Checkbox = function(opt_checked, opt_domHelper, opt_renderer) {
|
||||
var renderer = opt_renderer || goog.ui.CheckboxRenderer.getInstance();
|
||||
goog.ui.Control.call(this, null, renderer, opt_domHelper);
|
||||
// The checkbox maintains its own tri-state CHECKED state.
|
||||
// The control class maintains DISABLED, ACTIVE, and FOCUSED (which enable tab
|
||||
// navigation, and keyHandling with SPACE).
|
||||
|
||||
/**
|
||||
* Checked state of the checkbox.
|
||||
* @type {goog.ui.Checkbox.State}
|
||||
* @private
|
||||
*/
|
||||
this.checked_ = goog.isDef(opt_checked) ?
|
||||
opt_checked : goog.ui.Checkbox.State.UNCHECKED;
|
||||
};
|
||||
goog.inherits(goog.ui.Checkbox, goog.ui.Control);
|
||||
|
||||
|
||||
/**
|
||||
* Possible checkbox states.
|
||||
* @enum {?boolean}
|
||||
*/
|
||||
goog.ui.Checkbox.State = {
|
||||
CHECKED: true,
|
||||
UNCHECKED: false,
|
||||
UNDETERMINED: null
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Label element bound to the checkbox.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.Checkbox.prototype.label_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* @return {goog.ui.Checkbox.State} Checked state of the checkbox.
|
||||
*/
|
||||
goog.ui.Checkbox.prototype.getChecked = function() {
|
||||
return this.checked_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether the checkbox is checked.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.Checkbox.prototype.isChecked = function() {
|
||||
return this.checked_ == goog.ui.Checkbox.State.CHECKED;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether the checkbox is not checked.
|
||||
*/
|
||||
goog.ui.Checkbox.prototype.isUnchecked = function() {
|
||||
return this.checked_ == goog.ui.Checkbox.State.UNCHECKED;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether the checkbox is in partially checked state.
|
||||
*/
|
||||
goog.ui.Checkbox.prototype.isUndetermined = function() {
|
||||
return this.checked_ == goog.ui.Checkbox.State.UNDETERMINED;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the checked state of the checkbox.
|
||||
* @param {?boolean} checked The checked state to set.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.Checkbox.prototype.setChecked = function(checked) {
|
||||
if (checked != this.checked_) {
|
||||
this.checked_ = /** @type {goog.ui.Checkbox.State} */ (checked);
|
||||
this.getRenderer().setCheckboxState(this.getElement(), this.checked_);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the checked state for the checkbox. Unlike {@link #setChecked},
|
||||
* doesn't update the checkbox's DOM. Considered protected; to be called
|
||||
* only by renderer code during element decoration.
|
||||
* @param {goog.ui.Checkbox.State} checked New checkbox state.
|
||||
*/
|
||||
goog.ui.Checkbox.prototype.setCheckedInternal = function(checked) {
|
||||
this.checked_ = checked;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Binds an HTML element to the checkbox which if clicked toggles the checkbox.
|
||||
* Behaves the same way as the 'label' HTML tag. The label element has to be the
|
||||
* direct or non-direct ancestor of the checkbox element because it will get the
|
||||
* focus when keyboard support is implemented.
|
||||
*
|
||||
* @param {Element} label The label control to set. If null, only the checkbox
|
||||
* reacts to clicks.
|
||||
*/
|
||||
goog.ui.Checkbox.prototype.setLabel = function(label) {
|
||||
if (this.isInDocument()) {
|
||||
this.exitDocument();
|
||||
this.label_ = label;
|
||||
this.enterDocument();
|
||||
} else {
|
||||
this.label_ = label;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Toggles the checkbox. State transitions:
|
||||
* <ul>
|
||||
* <li>unchecked -> checked
|
||||
* <li>undetermined -> checked
|
||||
* <li>checked -> unchecked
|
||||
* </ul>
|
||||
*/
|
||||
goog.ui.Checkbox.prototype.toggle = function() {
|
||||
this.setChecked(this.checked_ ? goog.ui.Checkbox.State.UNCHECKED :
|
||||
goog.ui.Checkbox.State.CHECKED);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.Checkbox.prototype.enterDocument = function() {
|
||||
goog.base(this, 'enterDocument');
|
||||
if (this.isHandleMouseEvents()) {
|
||||
var handler = this.getHandler();
|
||||
// Listen to the label, if it was set.
|
||||
if (this.label_) {
|
||||
// Any mouse events that happen to the associated label should have the
|
||||
// same effect on the checkbox as if they were happening to the checkbox
|
||||
// itself.
|
||||
handler.
|
||||
listen(this.label_, goog.events.EventType.CLICK,
|
||||
this.handleClickOrSpace_).
|
||||
listen(this.label_, goog.events.EventType.MOUSEOVER,
|
||||
this.handleMouseOver).
|
||||
listen(this.label_, goog.events.EventType.MOUSEOUT,
|
||||
this.handleMouseOut).
|
||||
listen(this.label_, goog.events.EventType.MOUSEDOWN,
|
||||
this.handleMouseDown).
|
||||
listen(this.label_, goog.events.EventType.MOUSEUP,
|
||||
this.handleMouseUp);
|
||||
}
|
||||
// Checkbox needs to explicitly listen for click event.
|
||||
handler.listen(this.getElement(),
|
||||
goog.events.EventType.CLICK, this.handleClickOrSpace_);
|
||||
}
|
||||
|
||||
// Set aria label.
|
||||
if (this.label_) {
|
||||
if (!this.label_.id) {
|
||||
this.label_.id = this.makeId('lbl');
|
||||
}
|
||||
var checkboxElement = this.getElement();
|
||||
goog.asserts.assert(checkboxElement,
|
||||
'The checkbox DOM element cannot be null.');
|
||||
goog.a11y.aria.setState(checkboxElement,
|
||||
goog.a11y.aria.State.LABELLEDBY,
|
||||
this.label_.id);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Fix for tabindex not being updated so that disabled checkbox is not
|
||||
* focusable. In particular this fails in Chrome.
|
||||
* Note: in general tabIndex=-1 will prevent from keyboard focus but enables
|
||||
* mouse focus, however in this case the control class prevents mouse focus.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.Checkbox.prototype.setEnabled = function(enabled) {
|
||||
goog.base(this, 'setEnabled', enabled);
|
||||
var el = this.getElement();
|
||||
if (el) {
|
||||
el.tabIndex = this.isEnabled() ? 0 : -1;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles the click event.
|
||||
* @param {!goog.events.BrowserEvent} e The event.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.Checkbox.prototype.handleClickOrSpace_ = function(e) {
|
||||
e.stopPropagation();
|
||||
var eventType = this.checked_ ? goog.ui.Component.EventType.UNCHECK :
|
||||
goog.ui.Component.EventType.CHECK;
|
||||
if (this.isEnabled() && this.dispatchEvent(eventType)) {
|
||||
e.preventDefault(); // Prevent scrolling in Chrome if SPACE is pressed.
|
||||
this.toggle();
|
||||
this.dispatchEvent(goog.ui.Component.EventType.CHANGE);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.Checkbox.prototype.handleKeyEventInternal = function(e) {
|
||||
if (e.keyCode == goog.events.KeyCodes.SPACE) {
|
||||
this.handleClickOrSpace_(e);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Register this control so it can be created from markup.
|
||||
*/
|
||||
goog.ui.registry.setDecoratorByClassName(
|
||||
goog.ui.CheckboxRenderer.CSS_CLASS,
|
||||
function() {
|
||||
return new goog.ui.Checkbox();
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
// 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 A menu item class that supports checkbox semantics.
|
||||
*
|
||||
* @author attila@google.com (Attila Bodis)
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.CheckBoxMenuItem');
|
||||
|
||||
goog.require('goog.ui.MenuItem');
|
||||
goog.require('goog.ui.registry');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Class representing a checkbox menu item. This is just a convenience class
|
||||
* that extends {@link goog.ui.MenuItem} by making it checkable.
|
||||
*
|
||||
* @param {goog.ui.ControlContent} content Text caption or DOM structure to
|
||||
* display as the content of the item (use to add icons or styling to
|
||||
* menus).
|
||||
* @param {*=} opt_model Data/model associated with the menu item.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper used for
|
||||
* document interactions.
|
||||
* @constructor
|
||||
* @extends {goog.ui.MenuItem}
|
||||
*/
|
||||
goog.ui.CheckBoxMenuItem = function(content, opt_model, opt_domHelper) {
|
||||
goog.ui.MenuItem.call(this, content, opt_model, opt_domHelper);
|
||||
this.setCheckable(true);
|
||||
};
|
||||
goog.inherits(goog.ui.CheckBoxMenuItem, goog.ui.MenuItem);
|
||||
|
||||
|
||||
// Register a decorator factory function for goog.ui.CheckBoxMenuItems.
|
||||
goog.ui.registry.setDecoratorByClassName(
|
||||
goog.getCssName('goog-checkbox-menuitem'), function() {
|
||||
// CheckBoxMenuItem defaults to using MenuItemRenderer.
|
||||
return new goog.ui.CheckBoxMenuItem(null);
|
||||
});
|
||||
@@ -0,0 +1,171 @@
|
||||
// Copyright 2011 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 Default renderer for {@link goog.ui.Checkbox}s.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.CheckboxRenderer');
|
||||
|
||||
goog.require('goog.a11y.aria');
|
||||
goog.require('goog.a11y.aria.Role');
|
||||
goog.require('goog.a11y.aria.State');
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.dom.classes');
|
||||
goog.require('goog.object');
|
||||
goog.require('goog.ui.ControlRenderer');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Default renderer for {@link goog.ui.Checkbox}s. Extends the superclass
|
||||
* to support checkbox states:
|
||||
* @constructor
|
||||
* @extends {goog.ui.ControlRenderer}
|
||||
*/
|
||||
goog.ui.CheckboxRenderer = function() {
|
||||
goog.base(this);
|
||||
};
|
||||
goog.inherits(goog.ui.CheckboxRenderer, goog.ui.ControlRenderer);
|
||||
goog.addSingletonGetter(goog.ui.CheckboxRenderer);
|
||||
|
||||
|
||||
/**
|
||||
* Default CSS class to be applied to the root element of components rendered
|
||||
* by this renderer.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.CheckboxRenderer.CSS_CLASS = goog.getCssName('goog-checkbox');
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.CheckboxRenderer.prototype.createDom = function(checkbox) {
|
||||
var element = checkbox.getDomHelper().createDom(
|
||||
'span', this.getClassNames(checkbox).join(' '));
|
||||
|
||||
var state = checkbox.getChecked();
|
||||
this.setCheckboxState(element, state);
|
||||
|
||||
return element;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.CheckboxRenderer.prototype.decorate = function(checkbox, element) {
|
||||
// The superclass implementation takes care of common attributes; we only
|
||||
// need to set the checkbox state.
|
||||
element = goog.base(this, 'decorate', checkbox, element);
|
||||
|
||||
var classes = goog.dom.classes.get(element);
|
||||
// Update the checked state of the element based on its css classNames
|
||||
// with the following order: undetermined -> checked -> unchecked.
|
||||
var checked = goog.ui.Checkbox.State.UNCHECKED;
|
||||
if (goog.array.contains(classes,
|
||||
this.getClassForCheckboxState(goog.ui.Checkbox.State.UNDETERMINED))) {
|
||||
checked = goog.ui.Checkbox.State.UNDETERMINED;
|
||||
} else if (goog.array.contains(classes,
|
||||
this.getClassForCheckboxState(goog.ui.Checkbox.State.CHECKED))) {
|
||||
checked = goog.ui.Checkbox.State.CHECKED;
|
||||
} else if (goog.array.contains(classes,
|
||||
this.getClassForCheckboxState(goog.ui.Checkbox.State.UNCHECKED))) {
|
||||
checked = goog.ui.Checkbox.State.UNCHECKED;
|
||||
}
|
||||
checkbox.setCheckedInternal(checked);
|
||||
goog.asserts.assert(element, 'The element cannot be null.');
|
||||
goog.a11y.aria.setState(element, goog.a11y.aria.State.CHECKED,
|
||||
this.ariaStateFromCheckState_(checked));
|
||||
|
||||
return element;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the ARIA role to be applied to checkboxes.
|
||||
* @return {goog.a11y.aria.Role} ARIA role.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.CheckboxRenderer.prototype.getAriaRole = function() {
|
||||
return goog.a11y.aria.Role.CHECKBOX;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Updates the appearance of the control in response to a checkbox state
|
||||
* change.
|
||||
* @param {Element} element Checkbox element.
|
||||
* @param {goog.ui.Checkbox.State} state Updated checkbox state.
|
||||
*/
|
||||
goog.ui.CheckboxRenderer.prototype.setCheckboxState = function(
|
||||
element, state) {
|
||||
if (element) {
|
||||
var classToAdd = this.getClassForCheckboxState(state);
|
||||
goog.asserts.assert(classToAdd);
|
||||
if (goog.dom.classes.has(element, classToAdd)) {
|
||||
return;
|
||||
}
|
||||
goog.object.forEach(goog.ui.Checkbox.State, function(state) {
|
||||
var className = this.getClassForCheckboxState(state);
|
||||
goog.dom.classes.enable(element, className,
|
||||
className == classToAdd);
|
||||
}, this);
|
||||
goog.a11y.aria.setState(element, goog.a11y.aria.State.CHECKED,
|
||||
this.ariaStateFromCheckState_(state));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the checkbox's ARIA (accessibility) state from its checked state.
|
||||
* @param {goog.ui.Checkbox.State} state Checkbox state.
|
||||
* @return {string} The value of goog.a11y.aria.state.CHECKED. Either 'true',
|
||||
* 'false', or 'mixed'.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CheckboxRenderer.prototype.ariaStateFromCheckState_ = function(state) {
|
||||
if (state == goog.ui.Checkbox.State.UNDETERMINED) {
|
||||
return 'mixed';
|
||||
} else if (state == goog.ui.Checkbox.State.CHECKED) {
|
||||
return 'true';
|
||||
} else {
|
||||
return 'false';
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.CheckboxRenderer.prototype.getCssClass = function() {
|
||||
return goog.ui.CheckboxRenderer.CSS_CLASS;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Takes a single {@link goog.ui.Checkbox.State}, and returns the
|
||||
* corresponding CSS class name.
|
||||
* @param {goog.ui.Checkbox.State} state Checkbox state.
|
||||
* @return {string} CSS class representing the given state.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.CheckboxRenderer.prototype.getClassForCheckboxState = function(state) {
|
||||
var baseClass = this.getStructuralCssClass();
|
||||
if (state == goog.ui.Checkbox.State.CHECKED) {
|
||||
return goog.getCssName(baseClass, 'checked');
|
||||
} else if (state == goog.ui.Checkbox.State.UNCHECKED) {
|
||||
return goog.getCssName(baseClass, 'unchecked');
|
||||
} else if (state == goog.ui.Checkbox.State.UNDETERMINED) {
|
||||
return goog.getCssName(baseClass, 'undetermined');
|
||||
}
|
||||
throw Error('Invalid checkbox state: ' + state);
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright 2010 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 A color button rendered via
|
||||
* {@link goog.ui.ColorButtonRenderer}.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.ColorButton');
|
||||
|
||||
goog.require('goog.ui.Button');
|
||||
goog.require('goog.ui.ColorButtonRenderer');
|
||||
goog.require('goog.ui.registry');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A color button control. Identical to {@link goog.ui.Button}, except it
|
||||
* defaults its renderer to {@link goog.ui.ColorButtonRenderer}.
|
||||
* This button displays a particular color and is clickable.
|
||||
* It is primarily useful with {@link goog.ui.ColorSplitBehavior} to cache the
|
||||
* last selected color.
|
||||
*
|
||||
* @param {goog.ui.ControlContent} content Text caption or existing DOM
|
||||
* structure to display as the button's caption.
|
||||
* @param {goog.ui.ButtonRenderer=} opt_renderer Optional renderer used to
|
||||
* render or decorate the button; defaults to
|
||||
* {@link goog.ui.ColorButtonRenderer}.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
|
||||
* document interaction.
|
||||
* @constructor
|
||||
* @extends {goog.ui.Button}
|
||||
*/
|
||||
goog.ui.ColorButton = function(content, opt_renderer, opt_domHelper) {
|
||||
goog.ui.Button.call(this, content, opt_renderer ||
|
||||
goog.ui.ColorButtonRenderer.getInstance(), opt_domHelper);
|
||||
};
|
||||
goog.inherits(goog.ui.ColorButton, goog.ui.Button);
|
||||
|
||||
// Register a decorator factory function for goog.ui.ColorButtons.
|
||||
goog.ui.registry.setDecoratorByClassName(goog.ui.ColorButtonRenderer.CSS_CLASS,
|
||||
function() {
|
||||
// ColorButton defaults to using ColorButtonRenderer.
|
||||
return new goog.ui.ColorButton(null);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
// Copyright 2010 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 Renderer for {@link goog.ui.ColorButton}s.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.ColorButtonRenderer');
|
||||
|
||||
goog.require('goog.dom.classes');
|
||||
goog.require('goog.functions');
|
||||
goog.require('goog.ui.ColorMenuButtonRenderer');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Renderer for {@link goog.ui.ColorButton}s.
|
||||
* Uses {@link goog.ui.ColorMenuButton}s but disables the dropdown.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {goog.ui.ColorMenuButtonRenderer}
|
||||
*/
|
||||
goog.ui.ColorButtonRenderer = function() {
|
||||
goog.base(this);
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
// TODO(user): enable disabling the dropdown in goog.ui.ColorMenuButton
|
||||
this.createDropdown = goog.functions.NULL;
|
||||
|
||||
};
|
||||
goog.inherits(goog.ui.ColorButtonRenderer, goog.ui.ColorMenuButtonRenderer);
|
||||
goog.addSingletonGetter(goog.ui.ColorButtonRenderer);
|
||||
|
||||
|
||||
/**
|
||||
* Default CSS class to be applied to the root element of components rendered
|
||||
* by this renderer. Additionally, applies class to the button's caption.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.ColorButtonRenderer.CSS_CLASS = goog.getCssName('goog-color-button');
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.ColorButtonRenderer.prototype.createCaption = function(content, dom) {
|
||||
var caption = goog.base(this, 'createCaption', content, dom);
|
||||
goog.dom.classes.add(caption, goog.ui.ColorButtonRenderer.CSS_CLASS);
|
||||
return caption;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.ColorButtonRenderer.prototype.initializeDom = function(button) {
|
||||
goog.base(this, 'initializeDom', button);
|
||||
goog.dom.classes.add(button.getElement(),
|
||||
goog.ui.ColorButtonRenderer.CSS_CLASS);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
// Copyright 2008 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 A color menu button. Extends {@link goog.ui.MenuButton} by
|
||||
* showing the currently selected color in the button caption.
|
||||
*
|
||||
* @author robbyw@google.com (Robby Walker)
|
||||
* @author attila@google.com (Attila Bodis)
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.ColorMenuButton');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.object');
|
||||
goog.require('goog.ui.ColorMenuButtonRenderer');
|
||||
goog.require('goog.ui.ColorPalette');
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('goog.ui.Menu');
|
||||
goog.require('goog.ui.MenuButton');
|
||||
goog.require('goog.ui.registry');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A color menu button control. Extends {@link goog.ui.MenuButton} by adding
|
||||
* an API for getting and setting the currently selected color from a menu of
|
||||
* color palettes.
|
||||
*
|
||||
* @param {goog.ui.ControlContent} content Text caption or existing DOM
|
||||
* structure to display as the button's caption.
|
||||
* @param {goog.ui.Menu=} opt_menu Menu to render under the button when clicked;
|
||||
* should contain at least one {@link goog.ui.ColorPalette} if present.
|
||||
* @param {goog.ui.MenuButtonRenderer=} opt_renderer Button renderer;
|
||||
* defaults to {@link goog.ui.ColorMenuButtonRenderer}.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM hepler, used for
|
||||
* document interaction.
|
||||
* @constructor
|
||||
* @extends {goog.ui.MenuButton}
|
||||
*/
|
||||
goog.ui.ColorMenuButton = function(content, opt_menu, opt_renderer,
|
||||
opt_domHelper) {
|
||||
goog.ui.MenuButton.call(this, content, opt_menu, opt_renderer ||
|
||||
goog.ui.ColorMenuButtonRenderer.getInstance(), opt_domHelper);
|
||||
};
|
||||
goog.inherits(goog.ui.ColorMenuButton, goog.ui.MenuButton);
|
||||
|
||||
|
||||
/**
|
||||
* Default color palettes.
|
||||
* @type {!Object}
|
||||
*/
|
||||
goog.ui.ColorMenuButton.PALETTES = {
|
||||
/** Default grayscale colors. */
|
||||
GRAYSCALE: [
|
||||
'#000', '#444', '#666', '#999', '#ccc', '#eee', '#f3f3f3', '#fff'
|
||||
],
|
||||
|
||||
/** Default solid colors. */
|
||||
SOLID: [
|
||||
'#f00', '#f90', '#ff0', '#0f0', '#0ff', '#00f', '#90f', '#f0f'
|
||||
],
|
||||
|
||||
/** Default pastel colors. */
|
||||
PASTEL: [
|
||||
'#f4cccc', '#fce5cd', '#fff2cc', '#d9ead3', '#d0e0e3', '#cfe2f3', '#d9d2e9',
|
||||
'#ead1dc',
|
||||
'#ea9999', '#f9cb9c', '#ffe599', '#b6d7a8', '#a2c4c9', '#9fc5e8', '#b4a7d6',
|
||||
'#d5a6bd',
|
||||
'#e06666', '#f6b26b', '#ffd966', '#93c47d', '#76a5af', '#6fa8dc', '#8e7cc3',
|
||||
'#c27ba0',
|
||||
'#cc0000', '#e69138', '#f1c232', '#6aa84f', '#45818e', '#3d85c6', '#674ea7',
|
||||
'#a64d79',
|
||||
'#990000', '#b45f06', '#bf9000', '#38761d', '#134f5c', '#0b5394', '#351c75',
|
||||
'#741b47',
|
||||
'#660000', '#783f04', '#7f6000', '#274e13', '#0c343d', '#073763', '#20124d',
|
||||
'#4c1130'
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Value for the "no color" menu item object in the color menu (if present).
|
||||
* The {@link goog.ui.ColorMenuButton#handleMenuAction} method interprets
|
||||
* ACTION events dispatched by an item with this value as meaning "clear the
|
||||
* selected color."
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.ColorMenuButton.NO_COLOR = 'none';
|
||||
|
||||
|
||||
/**
|
||||
* Factory method that creates and returns a new {@link goog.ui.Menu} instance
|
||||
* containing default color palettes.
|
||||
* @param {Array.<goog.ui.Control>=} opt_extraItems Optional extra menu items to
|
||||
* add before the color palettes.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM hepler, used for
|
||||
* document interaction.
|
||||
* @return {goog.ui.Menu} Color menu.
|
||||
*/
|
||||
goog.ui.ColorMenuButton.newColorMenu = function(opt_extraItems, opt_domHelper) {
|
||||
var menu = new goog.ui.Menu(opt_domHelper);
|
||||
|
||||
if (opt_extraItems) {
|
||||
goog.array.forEach(opt_extraItems, function(item) {
|
||||
menu.addChild(item, true);
|
||||
});
|
||||
}
|
||||
|
||||
goog.object.forEach(goog.ui.ColorMenuButton.PALETTES, function(colors) {
|
||||
var palette = new goog.ui.ColorPalette(colors, null, opt_domHelper);
|
||||
palette.setSize(8);
|
||||
menu.addChild(palette, true);
|
||||
});
|
||||
|
||||
return menu;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the currently selected color (null if none).
|
||||
* @return {?string} The selected color.
|
||||
*/
|
||||
goog.ui.ColorMenuButton.prototype.getSelectedColor = function() {
|
||||
return /** @type {string} */ (this.getValue());
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the selected color, or clears the selected color if the argument is
|
||||
* null or not any of the available color choices.
|
||||
* @param {?string} color New color.
|
||||
*/
|
||||
goog.ui.ColorMenuButton.prototype.setSelectedColor = function(color) {
|
||||
this.setValue(color);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the value associated with the color menu button. Overrides
|
||||
* {@link goog.ui.Button#setValue} by interpreting the value as a color
|
||||
* spec string.
|
||||
* @param {*} value New button value; should be a color spec string.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.ColorMenuButton.prototype.setValue = function(value) {
|
||||
var color = /** @type {?string} */ (value);
|
||||
for (var i = 0, item; item = this.getItemAt(i); i++) {
|
||||
if (typeof item.setSelectedColor == 'function') {
|
||||
// This menu item looks like a color palette.
|
||||
item.setSelectedColor(color);
|
||||
}
|
||||
}
|
||||
goog.ui.ColorMenuButton.superClass_.setValue.call(this, color);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles {@link goog.ui.Component.EventType.ACTION} events dispatched by
|
||||
* the menu item clicked by the user. Updates the button, calls the superclass
|
||||
* implementation to hide the menu, stops the propagation of the event, and
|
||||
* dispatches an ACTION event on behalf of the button itself. Overrides
|
||||
* {@link goog.ui.MenuButton#handleMenuAction}.
|
||||
* @param {goog.events.Event} e Action event to handle.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.ColorMenuButton.prototype.handleMenuAction = function(e) {
|
||||
if (typeof e.target.getSelectedColor == 'function') {
|
||||
// User clicked something that looks like a color palette.
|
||||
this.setValue(e.target.getSelectedColor());
|
||||
} else if (e.target.getValue() == goog.ui.ColorMenuButton.NO_COLOR) {
|
||||
// User clicked the special "no color" menu item.
|
||||
this.setValue(null);
|
||||
}
|
||||
goog.ui.ColorMenuButton.superClass_.handleMenuAction.call(this, e);
|
||||
e.stopPropagation();
|
||||
this.dispatchEvent(goog.ui.Component.EventType.ACTION);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Opens or closes the menu. Overrides {@link goog.ui.MenuButton#setOpen} by
|
||||
* generating a default color menu on the fly if needed.
|
||||
* @param {boolean} open Whether to open or close the menu.
|
||||
* @param {goog.events.Event=} opt_e Mousedown event that caused the menu to
|
||||
* be opened.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.ColorMenuButton.prototype.setOpen = function(open, opt_e) {
|
||||
if (open && this.getItemCount() == 0) {
|
||||
this.setMenu(
|
||||
goog.ui.ColorMenuButton.newColorMenu(null, this.getDomHelper()));
|
||||
this.setValue(/** @type {?string} */ (this.getValue()));
|
||||
}
|
||||
goog.ui.ColorMenuButton.superClass_.setOpen.call(this, open, opt_e);
|
||||
};
|
||||
|
||||
|
||||
// Register a decorator factory function for goog.ui.ColorMenuButtons.
|
||||
goog.ui.registry.setDecoratorByClassName(
|
||||
goog.ui.ColorMenuButtonRenderer.CSS_CLASS,
|
||||
function() {
|
||||
return new goog.ui.ColorMenuButton(null);
|
||||
});
|
||||
@@ -0,0 +1,144 @@
|
||||
// Copyright 2008 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 Renderer for {@link goog.ui.ColorMenuButton}s.
|
||||
*
|
||||
* @author robbyw@google.com (Robby Walker)
|
||||
* @author attila@google.com (Attila Bodis)
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.ColorMenuButtonRenderer');
|
||||
|
||||
goog.require('goog.color');
|
||||
goog.require('goog.dom.classes');
|
||||
goog.require('goog.ui.MenuButtonRenderer');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Renderer for {@link goog.ui.ColorMenuButton}s.
|
||||
* @constructor
|
||||
* @extends {goog.ui.MenuButtonRenderer}
|
||||
*/
|
||||
goog.ui.ColorMenuButtonRenderer = function() {
|
||||
goog.ui.MenuButtonRenderer.call(this);
|
||||
};
|
||||
goog.inherits(goog.ui.ColorMenuButtonRenderer, goog.ui.MenuButtonRenderer);
|
||||
goog.addSingletonGetter(goog.ui.ColorMenuButtonRenderer);
|
||||
|
||||
|
||||
/**
|
||||
* Default CSS class to be applied to the root element of components rendered
|
||||
* by this renderer.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.ColorMenuButtonRenderer.CSS_CLASS =
|
||||
goog.getCssName('goog-color-menu-button');
|
||||
|
||||
|
||||
/**
|
||||
* Overrides the superclass implementation by wrapping the caption text or DOM
|
||||
* structure in a color indicator element. Creates the following DOM structure:
|
||||
* <div class="goog-inline-block goog-menu-button-caption">
|
||||
* <div class="goog-color-menu-button-indicator">
|
||||
* Contents...
|
||||
* </div>
|
||||
* </div>
|
||||
* The 'goog-color-menu-button-indicator' style should be defined to have a
|
||||
* bottom border of nonzero width and a default color that blends into its
|
||||
* background.
|
||||
* @param {goog.ui.ControlContent} content Text caption or DOM structure.
|
||||
* @param {goog.dom.DomHelper} dom DOM helper, used for document interaction.
|
||||
* @return {Element} Caption element.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.ColorMenuButtonRenderer.prototype.createCaption = function(content,
|
||||
dom) {
|
||||
return goog.ui.ColorMenuButtonRenderer.superClass_.createCaption.call(this,
|
||||
goog.ui.ColorMenuButtonRenderer.wrapCaption(content, dom), dom);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Wrap a caption in a div with the color-menu-button-indicator CSS class.
|
||||
* @param {goog.ui.ControlContent} content Text caption or DOM structure.
|
||||
* @param {goog.dom.DomHelper} dom DOM helper, used for document interaction.
|
||||
* @return {Element} Caption element.
|
||||
*/
|
||||
goog.ui.ColorMenuButtonRenderer.wrapCaption = function(content, dom) {
|
||||
return dom.createDom('div',
|
||||
goog.getCssName(goog.ui.ColorMenuButtonRenderer.CSS_CLASS, 'indicator'),
|
||||
content);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Takes a color menu button control's root element and a value object
|
||||
* (which is assumed to be a color), and updates the button's DOM to reflect
|
||||
* the new color. Overrides {@link goog.ui.ButtonRenderer#setValue}.
|
||||
* @param {Element} element The button control's root element (if rendered).
|
||||
* @param {*} value New value; assumed to be a color spec string.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.ColorMenuButtonRenderer.prototype.setValue = function(element, value) {
|
||||
if (element) {
|
||||
goog.ui.ColorMenuButtonRenderer.setCaptionValue(
|
||||
this.getContentElement(element), value);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Takes a control's content element and a value object (which is assumed
|
||||
* to be a color), and updates its DOM to reflect the new color.
|
||||
* @param {Element} caption A content element of a control.
|
||||
* @param {*} value New value; assumed to be a color spec string.
|
||||
*/
|
||||
goog.ui.ColorMenuButtonRenderer.setCaptionValue = function(caption, value) {
|
||||
// Assume that the caption's first child is the indicator.
|
||||
if (caption && caption.firstChild) {
|
||||
// Normalize the value to a hex color spec or null (otherwise setting
|
||||
// borderBottomColor will cause a JS error on IE).
|
||||
var hexColor;
|
||||
|
||||
var strValue = /** @type {string} */ (value);
|
||||
hexColor = strValue && goog.color.isValidColor(strValue) ?
|
||||
goog.color.parse(strValue).hex :
|
||||
null;
|
||||
|
||||
// Stupid IE6/7 doesn't do transparent borders.
|
||||
// TODO(attila): Add user-agent version check when IE8 comes out...
|
||||
caption.firstChild.style.borderBottomColor = hexColor ||
|
||||
(goog.userAgent.IE ? '' : 'transparent');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the button's DOM when it enters the document. Overrides the
|
||||
* superclass implementation by making sure the button's color indicator is
|
||||
* initialized.
|
||||
* @param {goog.ui.Control} button goog.ui.ColorMenuButton whose DOM is to be
|
||||
* initialized as it enters the document.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.ColorMenuButtonRenderer.prototype.initializeDom = function(button) {
|
||||
this.setValue(button.getElement(), button.getValue());
|
||||
goog.dom.classes.add(button.getElement(),
|
||||
goog.ui.ColorMenuButtonRenderer.CSS_CLASS);
|
||||
goog.ui.ColorMenuButtonRenderer.superClass_.initializeDom.call(this,
|
||||
button);
|
||||
};
|
||||
177
nicer-api-docs/closure-library/closure/goog/ui/colorpalette.js
Normal file
177
nicer-api-docs/closure-library/closure/goog/ui/colorpalette.js
Normal file
@@ -0,0 +1,177 @@
|
||||
// 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 A control for representing a palette of colors, that the user
|
||||
* can highlight or select via the keyboard or the mouse.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.ColorPalette');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.color');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.Palette');
|
||||
goog.require('goog.ui.PaletteRenderer');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A color palette is a grid of color swatches that the user can highlight or
|
||||
* select via the keyboard or the mouse. The selection state of the palette is
|
||||
* controlled by a selection model. When the user makes a selection, the
|
||||
* component fires an ACTION event. Event listeners may retrieve the selected
|
||||
* color using the {@link #getSelectedColor} method.
|
||||
*
|
||||
* @param {Array.<string>=} opt_colors Array of colors in any valid CSS color
|
||||
* format.
|
||||
* @param {goog.ui.PaletteRenderer=} opt_renderer Renderer used to render or
|
||||
* decorate the palette; defaults to {@link goog.ui.PaletteRenderer}.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
|
||||
* document interaction.
|
||||
* @constructor
|
||||
* @extends {goog.ui.Palette}
|
||||
*/
|
||||
goog.ui.ColorPalette = function(opt_colors, opt_renderer, opt_domHelper) {
|
||||
/**
|
||||
* Array of colors to show in the palette.
|
||||
* @type {Array.<string>}
|
||||
* @private
|
||||
*/
|
||||
this.colors_ = opt_colors || [];
|
||||
|
||||
goog.ui.Palette.call(this, null,
|
||||
opt_renderer || goog.ui.PaletteRenderer.getInstance(), opt_domHelper);
|
||||
|
||||
// Set the colors separately from the super call since we need the correct
|
||||
// DomHelper to be initialized for this class.
|
||||
this.setColors(this.colors_);
|
||||
};
|
||||
goog.inherits(goog.ui.ColorPalette, goog.ui.Palette);
|
||||
|
||||
|
||||
/**
|
||||
* Array of normalized colors. Initialized lazily as often never needed.
|
||||
* @type {?Array.<string>}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ColorPalette.prototype.normalizedColors_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Array of labels for the colors. Will be used for the tooltips and
|
||||
* accessibility.
|
||||
* @type {?Array.<string>}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ColorPalette.prototype.labels_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Returns the array of colors represented in the color palette.
|
||||
* @return {Array.<string>} Array of colors.
|
||||
*/
|
||||
goog.ui.ColorPalette.prototype.getColors = function() {
|
||||
return this.colors_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the colors that are contained in the palette.
|
||||
* @param {Array.<string>} colors Array of colors in any valid CSS color format.
|
||||
* @param {Array.<string>=} opt_labels The array of labels to be used as
|
||||
* tooltips. When not provided, the color value will be used.
|
||||
*/
|
||||
goog.ui.ColorPalette.prototype.setColors = function(colors, opt_labels) {
|
||||
this.colors_ = colors;
|
||||
this.labels_ = opt_labels || null;
|
||||
this.normalizedColors_ = null;
|
||||
this.setContent(this.createColorNodes());
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {?string} The current selected color in hex, or null.
|
||||
*/
|
||||
goog.ui.ColorPalette.prototype.getSelectedColor = function() {
|
||||
var selectedItem = /** @type {Element} */ (this.getSelectedItem());
|
||||
if (selectedItem) {
|
||||
var color = goog.style.getStyle(selectedItem, 'background-color');
|
||||
return goog.ui.ColorPalette.parseColor_(color);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the selected color. Clears the selection if the argument is null or
|
||||
* can't be parsed as a color.
|
||||
* @param {?string} color The color to set as selected; null clears the
|
||||
* selection.
|
||||
*/
|
||||
goog.ui.ColorPalette.prototype.setSelectedColor = function(color) {
|
||||
var hexColor = goog.ui.ColorPalette.parseColor_(color);
|
||||
if (!this.normalizedColors_) {
|
||||
this.normalizedColors_ = goog.array.map(this.colors_, function(color) {
|
||||
return goog.ui.ColorPalette.parseColor_(color);
|
||||
});
|
||||
}
|
||||
this.setSelectedIndex(hexColor ?
|
||||
goog.array.indexOf(this.normalizedColors_, hexColor) : -1);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {Array.<Node>} An array of DOM nodes for each color.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.ColorPalette.prototype.createColorNodes = function() {
|
||||
return goog.array.map(this.colors_, function(color, index) {
|
||||
var swatch = this.getDomHelper().createDom('div', {
|
||||
'class': goog.getCssName(this.getRenderer().getCssClass(),
|
||||
'colorswatch'),
|
||||
'style': 'background-color:' + color
|
||||
});
|
||||
if (this.labels_ && this.labels_[index]) {
|
||||
swatch.title = this.labels_[index];
|
||||
} else {
|
||||
swatch.title = color.charAt(0) == '#' ?
|
||||
'RGB (' + goog.color.hexToRgb(color).join(', ') + ')' : color;
|
||||
}
|
||||
return swatch;
|
||||
}, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Takes a string, attempts to parse it as a color spec, and returns a
|
||||
* normalized hex color spec if successful (null otherwise).
|
||||
* @param {?string} color String possibly containing a color spec; may be null.
|
||||
* @return {?string} Normalized hex color spec, or null if the argument can't
|
||||
* be parsed as a color.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ColorPalette.parseColor_ = function(color) {
|
||||
if (color) {
|
||||
/** @preserveTry */
|
||||
try {
|
||||
return goog.color.parse(color).hex;
|
||||
} catch (ex) {
|
||||
// Fall through.
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
344
nicer-api-docs/closure-library/closure/goog/ui/colorpicker.js
Normal file
344
nicer-api-docs/closure-library/closure/goog/ui/colorpicker.js
Normal file
@@ -0,0 +1,344 @@
|
||||
// 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 A color picker component. A color picker can compose several
|
||||
* instances of goog.ui.ColorPalette.
|
||||
*
|
||||
* NOTE: The ColorPicker is in a state of transition towards the common
|
||||
* component/control/container interface we are developing. If the API changes
|
||||
* we will do our best to update your code. The end result will be that a
|
||||
* color picker will compose multiple color palettes. In the simple case this
|
||||
* will be one grid, but may consistute 3 distinct grids, a custom color picker
|
||||
* or even a color wheel.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.ColorPicker');
|
||||
goog.provide('goog.ui.ColorPicker.EventType');
|
||||
|
||||
goog.require('goog.ui.ColorPalette');
|
||||
goog.require('goog.ui.Component');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create a new, empty color picker.
|
||||
*
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
|
||||
* @param {goog.ui.ColorPalette=} opt_colorPalette Optional color palette to
|
||||
* use for this color picker.
|
||||
* @extends {goog.ui.Component}
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.ColorPicker = function(opt_domHelper, opt_colorPalette) {
|
||||
goog.ui.Component.call(this, opt_domHelper);
|
||||
|
||||
/**
|
||||
* The color palette used inside the color picker.
|
||||
* @type {goog.ui.ColorPalette?}
|
||||
* @private
|
||||
*/
|
||||
this.colorPalette_ = opt_colorPalette || null;
|
||||
|
||||
this.getHandler().listen(
|
||||
this, goog.ui.Component.EventType.ACTION, this.onColorPaletteAction_);
|
||||
};
|
||||
goog.inherits(goog.ui.ColorPicker, goog.ui.Component);
|
||||
|
||||
|
||||
/**
|
||||
* Default number of columns in the color palette. May be overridden by calling
|
||||
* setSize.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
goog.ui.ColorPicker.DEFAULT_NUM_COLS = 5;
|
||||
|
||||
|
||||
/**
|
||||
* Constants for event names.
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.ui.ColorPicker.EventType = {
|
||||
CHANGE: 'change'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Whether the component is focusable.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ColorPicker.prototype.focusable_ = true;
|
||||
|
||||
|
||||
/**
|
||||
* Gets the array of colors displayed by the color picker.
|
||||
* Modifying this array will lead to unexpected behavior.
|
||||
* @return {Array.<string>?} The colors displayed by this widget.
|
||||
*/
|
||||
goog.ui.ColorPicker.prototype.getColors = function() {
|
||||
return this.colorPalette_ ? this.colorPalette_.getColors() : null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the array of colors to be displayed by the color picker.
|
||||
* @param {Array.<string>} colors The array of colors to be added.
|
||||
*/
|
||||
goog.ui.ColorPicker.prototype.setColors = function(colors) {
|
||||
// TODO(user): Don't add colors directly, we should add palettes and the
|
||||
// picker should support multiple palettes.
|
||||
if (!this.colorPalette_) {
|
||||
this.createColorPalette_(colors);
|
||||
} else {
|
||||
this.colorPalette_.setColors(colors);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the array of colors to be displayed by the color picker.
|
||||
* @param {Array.<string>} colors The array of colors to be added.
|
||||
* @deprecated Use setColors.
|
||||
*/
|
||||
goog.ui.ColorPicker.prototype.addColors = function(colors) {
|
||||
this.setColors(colors);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the size of the palette. Will throw an error after the picker has been
|
||||
* rendered.
|
||||
* @param {goog.math.Size|number} size The size of the grid.
|
||||
*/
|
||||
goog.ui.ColorPicker.prototype.setSize = function(size) {
|
||||
// TODO(user): The color picker should contain multiple palettes which will
|
||||
// all be resized at this point.
|
||||
if (!this.colorPalette_) {
|
||||
this.createColorPalette_([]);
|
||||
}
|
||||
this.colorPalette_.setSize(size);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the number of columns displayed.
|
||||
* @return {goog.math.Size?} The size of the grid.
|
||||
*/
|
||||
goog.ui.ColorPicker.prototype.getSize = function() {
|
||||
return this.colorPalette_ ? this.colorPalette_.getSize() : null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the number of columns. Will throw an error after the picker has been
|
||||
* rendered.
|
||||
* @param {number} n The number of columns.
|
||||
* @deprecated Use setSize.
|
||||
*/
|
||||
goog.ui.ColorPicker.prototype.setColumnCount = function(n) {
|
||||
this.setSize(n);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {number} The index of the color selected.
|
||||
*/
|
||||
goog.ui.ColorPicker.prototype.getSelectedIndex = function() {
|
||||
return this.colorPalette_ ? this.colorPalette_.getSelectedIndex() : -1;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets which color is selected. A value that is out-of-range means that no
|
||||
* color is selected.
|
||||
* @param {number} ind The index in this.colors_ of the selected color.
|
||||
*/
|
||||
goog.ui.ColorPicker.prototype.setSelectedIndex = function(ind) {
|
||||
if (this.colorPalette_) {
|
||||
this.colorPalette_.setSelectedIndex(ind);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the color that is currently selected in this color picker.
|
||||
* @return {?string} The hex string of the color selected, or null if no
|
||||
* color is selected.
|
||||
*/
|
||||
goog.ui.ColorPicker.prototype.getSelectedColor = function() {
|
||||
return this.colorPalette_ ? this.colorPalette_.getSelectedColor() : null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets which color is selected. Noop if the color palette hasn't been created
|
||||
* yet.
|
||||
* @param {string} color The selected color.
|
||||
*/
|
||||
goog.ui.ColorPicker.prototype.setSelectedColor = function(color) {
|
||||
// TODO(user): This will set the color in the first available palette that
|
||||
// contains it
|
||||
if (this.colorPalette_) {
|
||||
this.colorPalette_.setSelectedColor(color);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the component is focusable, false otherwise. The default
|
||||
* is true. Focusable components always have a tab index and allocate a key
|
||||
* handler to handle keyboard events while focused.
|
||||
* @return {boolean} True iff the component is focusable.
|
||||
*/
|
||||
goog.ui.ColorPicker.prototype.isFocusable = function() {
|
||||
return this.focusable_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether the component is focusable. The default is true.
|
||||
* Focusable components always have a tab index and allocate a key handler to
|
||||
* handle keyboard events while focused.
|
||||
* @param {boolean} focusable True iff the component is focusable.
|
||||
*/
|
||||
goog.ui.ColorPicker.prototype.setFocusable = function(focusable) {
|
||||
this.focusable_ = focusable;
|
||||
if (this.colorPalette_) {
|
||||
this.colorPalette_.setSupportedState(goog.ui.Component.State.FOCUSED,
|
||||
focusable);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* ColorPickers cannot be used to decorate pre-existing html, since the
|
||||
* structure they build is fairly complicated.
|
||||
* @param {Element} element Element to decorate.
|
||||
* @return {boolean} Returns always false.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.ColorPicker.prototype.canDecorate = function(element) {
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Renders the color picker inside the provided element. This will override the
|
||||
* current content of the element.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.ColorPicker.prototype.enterDocument = function() {
|
||||
goog.ui.ColorPicker.superClass_.enterDocument.call(this);
|
||||
if (this.colorPalette_) {
|
||||
this.colorPalette_.render(this.getElement());
|
||||
}
|
||||
this.getElement().unselectable = 'on';
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.ColorPicker.prototype.disposeInternal = function() {
|
||||
goog.ui.ColorPicker.superClass_.disposeInternal.call(this);
|
||||
if (this.colorPalette_) {
|
||||
this.colorPalette_.dispose();
|
||||
this.colorPalette_ = null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the focus to the color picker's palette.
|
||||
*/
|
||||
goog.ui.ColorPicker.prototype.focus = function() {
|
||||
if (this.colorPalette_) {
|
||||
this.colorPalette_.getElement().focus();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles actions from the color palette.
|
||||
*
|
||||
* @param {goog.events.Event} e The event.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ColorPicker.prototype.onColorPaletteAction_ = function(e) {
|
||||
e.stopPropagation();
|
||||
this.dispatchEvent(goog.ui.ColorPicker.EventType.CHANGE);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Create a color palette for the color picker.
|
||||
* @param {Array.<string>} colors Array of colors.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ColorPicker.prototype.createColorPalette_ = function(colors) {
|
||||
// TODO(user): The color picker should eventually just contain a number of
|
||||
// palettes and manage the interactions between them. This will go away then.
|
||||
var cp = new goog.ui.ColorPalette(colors, null, this.getDomHelper());
|
||||
cp.setSize(goog.ui.ColorPicker.DEFAULT_NUM_COLS);
|
||||
cp.setSupportedState(goog.ui.Component.State.FOCUSED, this.focusable_);
|
||||
// TODO(user): Use addChild(cp, true) and remove calls to render.
|
||||
this.addChild(cp);
|
||||
this.colorPalette_ = cp;
|
||||
if (this.isInDocument()) {
|
||||
this.colorPalette_.render(this.getElement());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns an unrendered instance of the color picker. The colors and layout
|
||||
* are a simple color grid, the same as the old Gmail color picker.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
|
||||
* @return {goog.ui.ColorPicker} The unrendered instance.
|
||||
*/
|
||||
goog.ui.ColorPicker.createSimpleColorGrid = function(opt_domHelper) {
|
||||
var cp = new goog.ui.ColorPicker(opt_domHelper);
|
||||
cp.setSize(7);
|
||||
cp.setColors(goog.ui.ColorPicker.SIMPLE_GRID_COLORS);
|
||||
return cp;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Array of colors for a 7-cell wide simple-grid color picker.
|
||||
* @type {Array.<string>}
|
||||
*/
|
||||
goog.ui.ColorPicker.SIMPLE_GRID_COLORS = [
|
||||
// grays
|
||||
'#ffffff', '#cccccc', '#c0c0c0', '#999999', '#666666', '#333333', '#000000',
|
||||
// reds
|
||||
'#ffcccc', '#ff6666', '#ff0000', '#cc0000', '#990000', '#660000', '#330000',
|
||||
// oranges
|
||||
'#ffcc99', '#ff9966', '#ff9900', '#ff6600', '#cc6600', '#993300', '#663300',
|
||||
// yellows
|
||||
'#ffff99', '#ffff66', '#ffcc66', '#ffcc33', '#cc9933', '#996633', '#663333',
|
||||
// olives
|
||||
'#ffffcc', '#ffff33', '#ffff00', '#ffcc00', '#999900', '#666600', '#333300',
|
||||
// greens
|
||||
'#99ff99', '#66ff99', '#33ff33', '#33cc00', '#009900', '#006600', '#003300',
|
||||
// turquoises
|
||||
'#99ffff', '#33ffff', '#66cccc', '#00cccc', '#339999', '#336666', '#003333',
|
||||
// blues
|
||||
'#ccffff', '#66ffff', '#33ccff', '#3366ff', '#3333ff', '#000099', '#000066',
|
||||
// purples
|
||||
'#ccccff', '#9999ff', '#6666cc', '#6633ff', '#6600cc', '#333399', '#330099',
|
||||
// violets
|
||||
'#ffccff', '#ff99ff', '#cc66cc', '#cc33cc', '#993399', '#663366', '#330033'
|
||||
];
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright 2010 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 Behavior for combining a color button and a menu.
|
||||
*
|
||||
* @see ../demos/split.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.ColorSplitBehavior');
|
||||
|
||||
goog.require('goog.ui.ColorMenuButton');
|
||||
goog.require('goog.ui.SplitBehavior');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a ColorSplitBehavior for combining a color button and a menu.
|
||||
* To use this, provide a goog.ui.ColorButton which will be attached with
|
||||
* a goog.ui.ColorMenuButton (with no caption).
|
||||
* Whenever a color is selected from the ColorMenuButton, it will be placed in
|
||||
* the ColorButton and the user can apply it over and over (by clicking the
|
||||
* ColorButton).
|
||||
* Primary use case - setting the color of text/background in a text editor.
|
||||
*
|
||||
* @param {!goog.ui.Button} colorButton A button to interact with a color menu
|
||||
* button (preferably a goog.ui.ColorButton).
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
|
||||
* document interaction.
|
||||
* @extends {goog.ui.SplitBehavior}
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.ColorSplitBehavior = function(colorButton, opt_domHelper) {
|
||||
goog.base(this, colorButton,
|
||||
new goog.ui.ColorMenuButton(goog.ui.ColorSplitBehavior.ZERO_WIDTH_SPACE_),
|
||||
goog.ui.SplitBehavior.DefaultHandlers.VALUE,
|
||||
undefined,
|
||||
opt_domHelper);
|
||||
};
|
||||
goog.inherits(goog.ui.ColorSplitBehavior, goog.ui.SplitBehavior);
|
||||
|
||||
|
||||
/**
|
||||
* A zero width space character.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ColorSplitBehavior.ZERO_WIDTH_SPACE_ = '\uFEFF';
|
||||
|
||||
961
nicer-api-docs/closure-library/closure/goog/ui/combobox.js
Normal file
961
nicer-api-docs/closure-library/closure/goog/ui/combobox.js
Normal file
@@ -0,0 +1,961 @@
|
||||
// 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 A combo box control that allows user input with
|
||||
* auto-suggestion from a limited set of options.
|
||||
*
|
||||
* @see ../demos/combobox.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.ComboBox');
|
||||
goog.provide('goog.ui.ComboBoxItem');
|
||||
|
||||
goog.require('goog.Timer');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.classlist');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.events.InputHandler');
|
||||
goog.require('goog.events.KeyCodes');
|
||||
goog.require('goog.events.KeyHandler');
|
||||
goog.require('goog.log');
|
||||
goog.require('goog.positioning.Corner');
|
||||
goog.require('goog.positioning.MenuAnchoredPosition');
|
||||
goog.require('goog.string');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('goog.ui.ItemEvent');
|
||||
goog.require('goog.ui.LabelInput');
|
||||
goog.require('goog.ui.Menu');
|
||||
goog.require('goog.ui.MenuItem');
|
||||
goog.require('goog.ui.MenuSeparator');
|
||||
goog.require('goog.ui.registry');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A ComboBox control.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
|
||||
* @param {goog.ui.Menu=} opt_menu Optional menu.
|
||||
* @extends {goog.ui.Component}
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.ComboBox = function(opt_domHelper, opt_menu) {
|
||||
goog.ui.Component.call(this, opt_domHelper);
|
||||
|
||||
this.labelInput_ = new goog.ui.LabelInput();
|
||||
this.enabled_ = true;
|
||||
|
||||
// TODO(user): Allow lazy creation of menus/menu items
|
||||
this.menu_ = opt_menu || new goog.ui.Menu(this.getDomHelper());
|
||||
this.setupMenu_();
|
||||
};
|
||||
goog.inherits(goog.ui.ComboBox, goog.ui.Component);
|
||||
|
||||
|
||||
/**
|
||||
* Number of milliseconds to wait before dismissing combobox after blur.
|
||||
* @type {number}
|
||||
*/
|
||||
goog.ui.ComboBox.BLUR_DISMISS_TIMER_MS = 250;
|
||||
|
||||
|
||||
/**
|
||||
* A logger to help debugging of combo box behavior.
|
||||
* @type {goog.log.Logger}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.logger_ =
|
||||
goog.log.getLogger('goog.ui.ComboBox');
|
||||
|
||||
|
||||
/**
|
||||
* Whether the combo box is enabled.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.enabled_;
|
||||
|
||||
|
||||
/**
|
||||
* Keyboard event handler to manage key events dispatched by the input element.
|
||||
* @type {goog.events.KeyHandler}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.keyHandler_;
|
||||
|
||||
|
||||
/**
|
||||
* Input handler to take care of firing events when the user inputs text in
|
||||
* the input.
|
||||
* @type {goog.events.InputHandler?}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.inputHandler_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* The last input token.
|
||||
* @type {?string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.lastToken_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* A LabelInput control that manages the focus/blur state of the input box.
|
||||
* @type {goog.ui.LabelInput?}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.labelInput_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Drop down menu for the combo box. Will be created at construction time.
|
||||
* @type {goog.ui.Menu?}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.menu_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* The cached visible count.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.visibleCount_ = -1;
|
||||
|
||||
|
||||
/**
|
||||
* The input element.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.input_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* The match function. The first argument for the match function will be
|
||||
* a MenuItem's caption and the second will be the token to evaluate.
|
||||
* @type {Function}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.matchFunction_ = goog.string.startsWith;
|
||||
|
||||
|
||||
/**
|
||||
* Element used as the combo boxes button.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.button_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Default text content for the input box when it is unchanged and unfocussed.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.defaultText_ = '';
|
||||
|
||||
|
||||
/**
|
||||
* Name for the input box created
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.fieldName_ = '';
|
||||
|
||||
|
||||
/**
|
||||
* Timer identifier for delaying the dismissal of the combo menu.
|
||||
* @type {?number}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.dismissTimer_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* True if the unicode inverted triangle should be displayed in the dropdown
|
||||
* button. Defaults to false.
|
||||
* @type {boolean} useDropdownArrow
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.useDropdownArrow_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* Create the DOM objects needed for the combo box. A span and text input.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.createDom = function() {
|
||||
this.input_ = this.getDomHelper().createDom(
|
||||
'input', {name: this.fieldName_, type: 'text', autocomplete: 'off'});
|
||||
this.button_ = this.getDomHelper().createDom('span',
|
||||
goog.getCssName('goog-combobox-button'));
|
||||
this.setElementInternal(this.getDomHelper().createDom('span',
|
||||
goog.getCssName('goog-combobox'), this.input_, this.button_));
|
||||
if (this.useDropdownArrow_) {
|
||||
this.button_.innerHTML = '▼';
|
||||
goog.style.setUnselectable(this.button_, true /* unselectable */);
|
||||
}
|
||||
this.input_.setAttribute('label', this.defaultText_);
|
||||
this.labelInput_.decorate(this.input_);
|
||||
this.menu_.setFocusable(false);
|
||||
if (!this.menu_.isInDocument()) {
|
||||
this.addChild(this.menu_, true);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Enables/Disables the combo box.
|
||||
* @param {boolean} enabled Whether to enable (true) or disable (false) the
|
||||
* combo box.
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.setEnabled = function(enabled) {
|
||||
this.enabled_ = enabled;
|
||||
this.labelInput_.setEnabled(enabled);
|
||||
goog.dom.classlist.enable(this.getElement(),
|
||||
goog.getCssName('goog-combobox-disabled'), !enabled);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.ComboBox.prototype.enterDocument = function() {
|
||||
goog.ui.ComboBox.superClass_.enterDocument.call(this);
|
||||
|
||||
var handler = this.getHandler();
|
||||
handler.listen(this.getElement(),
|
||||
goog.events.EventType.MOUSEDOWN, this.onComboMouseDown_);
|
||||
handler.listen(this.getDomHelper().getDocument(),
|
||||
goog.events.EventType.MOUSEDOWN, this.onDocClicked_);
|
||||
|
||||
handler.listen(this.input_,
|
||||
goog.events.EventType.BLUR, this.onInputBlur_);
|
||||
|
||||
this.keyHandler_ = new goog.events.KeyHandler(this.input_);
|
||||
handler.listen(this.keyHandler_,
|
||||
goog.events.KeyHandler.EventType.KEY, this.handleKeyEvent);
|
||||
|
||||
this.inputHandler_ = new goog.events.InputHandler(this.input_);
|
||||
handler.listen(this.inputHandler_,
|
||||
goog.events.InputHandler.EventType.INPUT, this.onInputEvent_);
|
||||
|
||||
handler.listen(this.menu_,
|
||||
goog.ui.Component.EventType.ACTION, this.onMenuSelected_);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.ComboBox.prototype.exitDocument = function() {
|
||||
this.keyHandler_.dispose();
|
||||
delete this.keyHandler_;
|
||||
this.inputHandler_.dispose();
|
||||
this.inputHandler_ = null;
|
||||
goog.ui.ComboBox.superClass_.exitDocument.call(this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Combo box currently can't decorate elements.
|
||||
* @return {boolean} The value false.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.canDecorate = function() {
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.ComboBox.prototype.disposeInternal = function() {
|
||||
goog.ui.ComboBox.superClass_.disposeInternal.call(this);
|
||||
|
||||
this.clearDismissTimer_();
|
||||
|
||||
this.labelInput_.dispose();
|
||||
this.menu_.dispose();
|
||||
|
||||
this.labelInput_ = null;
|
||||
this.menu_ = null;
|
||||
this.input_ = null;
|
||||
this.button_ = null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Dismisses the menu and resets the value of the edit field.
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.dismiss = function() {
|
||||
this.clearDismissTimer_();
|
||||
this.hideMenu_();
|
||||
this.menu_.setHighlightedIndex(-1);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds a new menu item at the end of the menu.
|
||||
* @param {goog.ui.MenuItem} item Menu item to add to the menu.
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.addItem = function(item) {
|
||||
this.menu_.addChild(item, true);
|
||||
this.visibleCount_ = -1;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds a new menu item at a specific index in the menu.
|
||||
* @param {goog.ui.MenuItem} item Menu item to add to the menu.
|
||||
* @param {number} n Index at which to insert the menu item.
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.addItemAt = function(item, n) {
|
||||
this.menu_.addChildAt(item, n, true);
|
||||
this.visibleCount_ = -1;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes an item from the menu and disposes it.
|
||||
* @param {goog.ui.MenuItem} item The menu item to remove.
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.removeItem = function(item) {
|
||||
var child = this.menu_.removeChild(item, true);
|
||||
if (child) {
|
||||
child.dispose();
|
||||
this.visibleCount_ = -1;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Remove all of the items from the ComboBox menu
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.removeAllItems = function() {
|
||||
for (var i = this.getItemCount() - 1; i >= 0; --i) {
|
||||
this.removeItem(this.getItemAt(i));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes a menu item at a given index in the menu.
|
||||
* @param {number} n Index of item.
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.removeItemAt = function(n) {
|
||||
var child = this.menu_.removeChildAt(n, true);
|
||||
if (child) {
|
||||
child.dispose();
|
||||
this.visibleCount_ = -1;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns a reference to the menu item at a given index.
|
||||
* @param {number} n Index of menu item.
|
||||
* @return {goog.ui.MenuItem?} Reference to the menu item.
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.getItemAt = function(n) {
|
||||
return /** @type {goog.ui.MenuItem?} */(this.menu_.getChildAt(n));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of items in the list, including non-visible items,
|
||||
* such as separators.
|
||||
* @return {number} Number of items in the menu for this combobox.
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.getItemCount = function() {
|
||||
return this.menu_.getChildCount();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {goog.ui.Menu} The menu that pops up.
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.getMenu = function() {
|
||||
return this.menu_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {Element} The input element.
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.getInputElement = function() {
|
||||
return this.input_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {number} The number of visible items in the menu.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.getNumberOfVisibleItems_ = function() {
|
||||
if (this.visibleCount_ == -1) {
|
||||
var count = 0;
|
||||
for (var i = 0, n = this.menu_.getChildCount(); i < n; i++) {
|
||||
var item = this.menu_.getChildAt(i);
|
||||
if (!(item instanceof goog.ui.MenuSeparator) && item.isVisible()) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
this.visibleCount_ = count;
|
||||
}
|
||||
|
||||
goog.log.info(this.logger_,
|
||||
'getNumberOfVisibleItems() - ' + this.visibleCount_);
|
||||
return this.visibleCount_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the match function to be used when filtering the combo box menu.
|
||||
* @param {Function} matchFunction The match function to be used when filtering
|
||||
* the combo box menu.
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.setMatchFunction = function(matchFunction) {
|
||||
this.matchFunction_ = matchFunction;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {Function} The match function for the combox box.
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.getMatchFunction = function() {
|
||||
return this.matchFunction_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the default text for the combo box.
|
||||
* @param {string} text The default text for the combo box.
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.setDefaultText = function(text) {
|
||||
this.defaultText_ = text;
|
||||
if (this.labelInput_) {
|
||||
this.labelInput_.setLabel(this.defaultText_);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} text The default text for the combox box.
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.getDefaultText = function() {
|
||||
return this.defaultText_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the field name for the combo box.
|
||||
* @param {string} fieldName The field name for the combo box.
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.setFieldName = function(fieldName) {
|
||||
this.fieldName_ = fieldName;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} The field name for the combo box.
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.getFieldName = function() {
|
||||
return this.fieldName_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Set to true if a unicode inverted triangle should be displayed in the
|
||||
* dropdown button.
|
||||
* This option defaults to false for backwards compatibility.
|
||||
* @param {boolean} useDropdownArrow True to use the dropdown arrow.
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.setUseDropdownArrow = function(useDropdownArrow) {
|
||||
this.useDropdownArrow_ = !!useDropdownArrow;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the current value of the combo box.
|
||||
* @param {string} value The new value.
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.setValue = function(value) {
|
||||
goog.log.info(this.logger_, 'setValue() - ' + value);
|
||||
if (this.labelInput_.getValue() != value) {
|
||||
this.labelInput_.setValue(value);
|
||||
this.handleInputChange_();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} The current value of the combo box.
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.getValue = function() {
|
||||
return this.labelInput_.getValue();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} HTML escaped token.
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.getToken = function() {
|
||||
// TODO(user): Remove HTML escaping and fix the existing calls.
|
||||
return goog.string.htmlEscape(this.getTokenText_());
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} The token for the current cursor position in the
|
||||
* input box, when multi-input is disabled it will be the full input value.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.getTokenText_ = function() {
|
||||
// TODO(user): Implement multi-input such that getToken returns a substring
|
||||
// of the whole input delimited by commas.
|
||||
return goog.string.trim(this.labelInput_.getValue().toLowerCase());
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.setupMenu_ = function() {
|
||||
var sm = this.menu_;
|
||||
sm.setVisible(false);
|
||||
sm.setAllowAutoFocus(false);
|
||||
sm.setAllowHighlightDisabled(true);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Shows the menu if it isn't already showing. Also positions the menu
|
||||
* correctly, resets the menu item visibilities and highlights the relevent
|
||||
* item.
|
||||
* @param {boolean} showAll Whether to show all items, with the first matching
|
||||
* item highlighted.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.maybeShowMenu_ = function(showAll) {
|
||||
var isVisible = this.menu_.isVisible();
|
||||
var numVisibleItems = this.getNumberOfVisibleItems_();
|
||||
|
||||
if (isVisible && numVisibleItems == 0) {
|
||||
goog.log.fine(this.logger_, 'no matching items, hiding');
|
||||
this.hideMenu_();
|
||||
|
||||
} else if (!isVisible && numVisibleItems > 0) {
|
||||
if (showAll) {
|
||||
goog.log.fine(this.logger_, 'showing menu');
|
||||
this.setItemVisibilityFromToken_('');
|
||||
this.setItemHighlightFromToken_(this.getTokenText_());
|
||||
}
|
||||
// In Safari 2.0, when clicking on the combox box, the blur event is
|
||||
// received after the click event that invokes this function. Since we want
|
||||
// to cancel the dismissal after the blur event is processed, we have to
|
||||
// wait for all event processing to happen.
|
||||
goog.Timer.callOnce(this.clearDismissTimer_, 1, this);
|
||||
|
||||
this.showMenu_();
|
||||
}
|
||||
|
||||
this.positionMenu();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Positions the menu.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.positionMenu = function() {
|
||||
if (this.menu_ && this.menu_.isVisible()) {
|
||||
var position = new goog.positioning.MenuAnchoredPosition(this.getElement(),
|
||||
goog.positioning.Corner.BOTTOM_START, true);
|
||||
position.reposition(this.menu_.getElement(),
|
||||
goog.positioning.Corner.TOP_START);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Show the menu and add an active class to the combo box's element.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.showMenu_ = function() {
|
||||
this.menu_.setVisible(true);
|
||||
goog.dom.classlist.add(this.getElement(),
|
||||
goog.getCssName('goog-combobox-active'));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Hide the menu and remove the active class from the combo box's element.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.hideMenu_ = function() {
|
||||
this.menu_.setVisible(false);
|
||||
goog.dom.classlist.remove(this.getElement(),
|
||||
goog.getCssName('goog-combobox-active'));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clears the dismiss timer if it's active.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.clearDismissTimer_ = function() {
|
||||
if (this.dismissTimer_) {
|
||||
goog.Timer.clear(this.dismissTimer_);
|
||||
this.dismissTimer_ = null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Event handler for when the combo box area has been clicked.
|
||||
* @param {goog.events.BrowserEvent} e The browser event.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.onComboMouseDown_ = function(e) {
|
||||
// We only want this event on the element itself or the input or the button.
|
||||
if (this.enabled_ &&
|
||||
(e.target == this.getElement() || e.target == this.input_ ||
|
||||
goog.dom.contains(this.button_, /** @type {Node} */ (e.target)))) {
|
||||
if (this.menu_.isVisible()) {
|
||||
goog.log.fine(this.logger_, 'Menu is visible, dismissing');
|
||||
this.dismiss();
|
||||
} else {
|
||||
goog.log.fine(this.logger_, 'Opening dropdown');
|
||||
this.maybeShowMenu_(true);
|
||||
if (goog.userAgent.OPERA) {
|
||||
// select() doesn't focus <input> elements in Opera.
|
||||
this.input_.focus();
|
||||
}
|
||||
this.input_.select();
|
||||
this.menu_.setMouseButtonPressed(true);
|
||||
// Stop the click event from stealing focus
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
// Stop the event from propagating outside of the combo box
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Event handler for when the document is clicked.
|
||||
* @param {goog.events.BrowserEvent} e The browser event.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.onDocClicked_ = function(e) {
|
||||
if (!goog.dom.contains(
|
||||
this.menu_.getElement(), /** @type {Node} */ (e.target))) {
|
||||
goog.log.info(this.logger_, 'onDocClicked_() - dismissing immediately');
|
||||
this.dismiss();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handle the menu's select event.
|
||||
* @param {goog.events.Event} e The event.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.onMenuSelected_ = function(e) {
|
||||
goog.log.info(this.logger_, 'onMenuSelected_()');
|
||||
var item = /** @type {!goog.ui.MenuItem} */ (e.target);
|
||||
// Stop propagation of the original event and redispatch to allow the menu
|
||||
// select to be cancelled at this level. i.e. if a menu item should cause
|
||||
// some behavior such as a user prompt instead of assigning the caption as
|
||||
// the value.
|
||||
if (this.dispatchEvent(new goog.ui.ItemEvent(
|
||||
goog.ui.Component.EventType.ACTION, this, item))) {
|
||||
var caption = item.getCaption();
|
||||
goog.log.fine(this.logger_,
|
||||
'Menu selection: ' + caption + '. Dismissing menu');
|
||||
if (this.labelInput_.getValue() != caption) {
|
||||
this.labelInput_.setValue(caption);
|
||||
this.dispatchEvent(goog.ui.Component.EventType.CHANGE);
|
||||
}
|
||||
this.dismiss();
|
||||
}
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Event handler for when the input box looses focus -- hide the menu
|
||||
* @param {goog.events.BrowserEvent} e The browser event.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.onInputBlur_ = function(e) {
|
||||
goog.log.info(this.logger_, 'onInputBlur_() - delayed dismiss');
|
||||
this.clearDismissTimer_();
|
||||
this.dismissTimer_ = goog.Timer.callOnce(
|
||||
this.dismiss, goog.ui.ComboBox.BLUR_DISMISS_TIMER_MS, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles keyboard events from the input box. Returns true if the combo box
|
||||
* was able to handle the event, false otherwise.
|
||||
* @param {goog.events.KeyEvent} e Key event to handle.
|
||||
* @return {boolean} Whether the event was handled by the combo box.
|
||||
* @protected
|
||||
* @suppress {visibility} performActionInternal
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.handleKeyEvent = function(e) {
|
||||
var isMenuVisible = this.menu_.isVisible();
|
||||
|
||||
// Give the menu a chance to handle the event.
|
||||
if (isMenuVisible && this.menu_.handleKeyEvent(e)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// The menu is either hidden or didn't handle the event.
|
||||
var handled = false;
|
||||
switch (e.keyCode) {
|
||||
case goog.events.KeyCodes.ESC:
|
||||
// If the menu is visible and the user hit Esc, dismiss the menu.
|
||||
if (isMenuVisible) {
|
||||
goog.log.fine(this.logger_,
|
||||
'Dismiss on Esc: ' + this.labelInput_.getValue());
|
||||
this.dismiss();
|
||||
handled = true;
|
||||
}
|
||||
break;
|
||||
case goog.events.KeyCodes.TAB:
|
||||
// If the menu is open and an option is highlighted, activate it.
|
||||
if (isMenuVisible) {
|
||||
var highlighted = this.menu_.getHighlighted();
|
||||
if (highlighted) {
|
||||
goog.log.fine(this.logger_,
|
||||
'Select on Tab: ' + this.labelInput_.getValue());
|
||||
highlighted.performActionInternal(e);
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case goog.events.KeyCodes.UP:
|
||||
case goog.events.KeyCodes.DOWN:
|
||||
// If the menu is hidden and the user hit the up/down arrow, show it.
|
||||
if (!isMenuVisible) {
|
||||
goog.log.fine(this.logger_, 'Up/Down - maybe show menu');
|
||||
this.maybeShowMenu_(true);
|
||||
handled = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (handled) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
return handled;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles the content of the input box changing.
|
||||
* @param {goog.events.Event} e The INPUT event to handle.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.onInputEvent_ = function(e) {
|
||||
// If the key event is text-modifying, update the menu.
|
||||
goog.log.fine(this.logger_,
|
||||
'Key is modifying: ' + this.labelInput_.getValue());
|
||||
this.handleInputChange_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles the content of the input box changing, either because of user
|
||||
* interaction or programmatic changes.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.handleInputChange_ = function() {
|
||||
var token = this.getTokenText_();
|
||||
this.setItemVisibilityFromToken_(token);
|
||||
if (goog.dom.getActiveElement(this.getDomHelper().getDocument()) ==
|
||||
this.input_) {
|
||||
// Do not alter menu visibility unless the user focus is currently on the
|
||||
// combobox (otherwise programmatic changes may cause the menu to become
|
||||
// visible).
|
||||
this.maybeShowMenu_(false);
|
||||
}
|
||||
var highlighted = this.menu_.getHighlighted();
|
||||
if (token == '' || !highlighted || !highlighted.isVisible()) {
|
||||
this.setItemHighlightFromToken_(token);
|
||||
}
|
||||
this.lastToken_ = token;
|
||||
this.dispatchEvent(goog.ui.Component.EventType.CHANGE);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Loops through all menu items setting their visibility according to a token.
|
||||
* @param {string} token The token.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.setItemVisibilityFromToken_ = function(token) {
|
||||
goog.log.info(this.logger_, 'setItemVisibilityFromToken_() - ' + token);
|
||||
var isVisibleItem = false;
|
||||
var count = 0;
|
||||
var recheckHidden = !this.matchFunction_(token, this.lastToken_);
|
||||
|
||||
for (var i = 0, n = this.menu_.getChildCount(); i < n; i++) {
|
||||
var item = this.menu_.getChildAt(i);
|
||||
if (item instanceof goog.ui.MenuSeparator) {
|
||||
// Ensure that separators are only shown if there is at least one visible
|
||||
// item before them.
|
||||
item.setVisible(isVisibleItem);
|
||||
isVisibleItem = false;
|
||||
} else if (item instanceof goog.ui.MenuItem) {
|
||||
if (!item.isVisible() && !recheckHidden) continue;
|
||||
|
||||
var caption = item.getCaption();
|
||||
var visible = this.isItemSticky_(item) ||
|
||||
caption && this.matchFunction_(caption.toLowerCase(), token);
|
||||
if (typeof item.setFormatFromToken == 'function') {
|
||||
item.setFormatFromToken(token);
|
||||
}
|
||||
item.setVisible(!!visible);
|
||||
isVisibleItem = visible || isVisibleItem;
|
||||
|
||||
} else {
|
||||
// Assume all other items are correctly using their visibility.
|
||||
isVisibleItem = item.isVisible() || isVisibleItem;
|
||||
}
|
||||
|
||||
if (!(item instanceof goog.ui.MenuSeparator) && item.isVisible()) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
this.visibleCount_ = count;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Highlights the first token that matches the given token.
|
||||
* @param {string} token The token.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.setItemHighlightFromToken_ = function(token) {
|
||||
goog.log.info(this.logger_, 'setItemHighlightFromToken_() - ' + token);
|
||||
|
||||
if (token == '') {
|
||||
this.menu_.setHighlightedIndex(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0, n = this.menu_.getChildCount(); i < n; i++) {
|
||||
var item = this.menu_.getChildAt(i);
|
||||
var caption = item.getCaption();
|
||||
if (caption && this.matchFunction_(caption.toLowerCase(), token)) {
|
||||
this.menu_.setHighlightedIndex(i);
|
||||
if (item.setFormatFromToken) {
|
||||
item.setFormatFromToken(token);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.menu_.setHighlightedIndex(-1);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the item has an isSticky method and the method returns true.
|
||||
* @param {goog.ui.MenuItem} item The item.
|
||||
* @return {boolean} Whether the item has an isSticky method and the method
|
||||
* returns true.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBox.prototype.isItemSticky_ = function(item) {
|
||||
return typeof item.isSticky == 'function' && item.isSticky();
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Class for combo box items.
|
||||
* @param {goog.ui.ControlContent} content Text caption or DOM structure to
|
||||
* display as the content of the item (use to add icons or styling to
|
||||
* menus).
|
||||
* @param {Object=} opt_data Identifying data for the menu item.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional dom helper used for dom
|
||||
* interactions.
|
||||
* @param {goog.ui.MenuItemRenderer=} opt_renderer Optional renderer.
|
||||
* @constructor
|
||||
* @extends {goog.ui.MenuItem}
|
||||
*/
|
||||
goog.ui.ComboBoxItem = function(content, opt_data, opt_domHelper,
|
||||
opt_renderer) {
|
||||
goog.ui.MenuItem.call(this, content, opt_data, opt_domHelper, opt_renderer);
|
||||
};
|
||||
goog.inherits(goog.ui.ComboBoxItem, goog.ui.MenuItem);
|
||||
|
||||
|
||||
// Register a decorator factory function for goog.ui.ComboBoxItems.
|
||||
goog.ui.registry.setDecoratorByClassName(goog.getCssName('goog-combobox-item'),
|
||||
function() {
|
||||
// ComboBoxItem defaults to using MenuItemRenderer.
|
||||
return new goog.ui.ComboBoxItem(null);
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Whether the menu item is sticky, non-sticky items will be hidden as the
|
||||
* user types.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ComboBoxItem.prototype.isSticky_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* Sets the menu item to be sticky or not sticky.
|
||||
* @param {boolean} sticky Whether the menu item should be sticky.
|
||||
*/
|
||||
goog.ui.ComboBoxItem.prototype.setSticky = function(sticky) {
|
||||
this.isSticky_ = sticky;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether the menu item is sticky.
|
||||
*/
|
||||
goog.ui.ComboBoxItem.prototype.isSticky = function() {
|
||||
return this.isSticky_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the format for a menu item based on a token, bolding the token.
|
||||
* @param {string} token The token.
|
||||
*/
|
||||
goog.ui.ComboBoxItem.prototype.setFormatFromToken = function(token) {
|
||||
if (this.isEnabled()) {
|
||||
var caption = this.getCaption();
|
||||
var index = caption.toLowerCase().indexOf(token);
|
||||
if (index >= 0) {
|
||||
var domHelper = this.getDomHelper();
|
||||
this.setContent([
|
||||
domHelper.createTextNode(caption.substr(0, index)),
|
||||
domHelper.createDom('b', null, caption.substr(index, token.length)),
|
||||
domHelper.createTextNode(caption.substr(index + token.length))
|
||||
]);
|
||||
}
|
||||
}
|
||||
};
|
||||
1300
nicer-api-docs/closure-library/closure/goog/ui/component.js
Normal file
1300
nicer-api-docs/closure-library/closure/goog/ui/component.js
Normal file
File diff suppressed because it is too large
Load Diff
1322
nicer-api-docs/closure-library/closure/goog/ui/container.js
Normal file
1322
nicer-api-docs/closure-library/closure/goog/ui/container.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,371 @@
|
||||
// Copyright 2008 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 Base class for container renderers.
|
||||
*
|
||||
* @author attila@google.com (Attila Bodis)
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.ContainerRenderer');
|
||||
|
||||
goog.require('goog.a11y.aria');
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.dom.NodeType');
|
||||
goog.require('goog.dom.classes');
|
||||
goog.require('goog.string');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.registry');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Default renderer for {@link goog.ui.Container}. Can be used as-is, but
|
||||
* subclasses of Container will probably want to use renderers specifically
|
||||
* tailored for them by extending this class.
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.ContainerRenderer = function() {
|
||||
};
|
||||
goog.addSingletonGetter(goog.ui.ContainerRenderer);
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new renderer and sets the CSS class that the renderer will use
|
||||
* as the base CSS class to apply to all elements rendered by that renderer.
|
||||
* An example to use this function using a menu is:
|
||||
*
|
||||
* <pre>
|
||||
* var myCustomRenderer = goog.ui.ContainerRenderer.getCustomRenderer(
|
||||
* goog.ui.MenuRenderer, 'my-special-menu');
|
||||
* var newMenu = new goog.ui.Menu(opt_domHelper, myCustomRenderer);
|
||||
* </pre>
|
||||
*
|
||||
* Your styles for the menu can now be:
|
||||
* <pre>
|
||||
* .my-special-menu { }
|
||||
* </pre>
|
||||
*
|
||||
* <em>instead</em> of
|
||||
* <pre>
|
||||
* .CSS_MY_SPECIAL_MENU .goog-menu { }
|
||||
* </pre>
|
||||
*
|
||||
* You would want to use this functionality when you want an instance of a
|
||||
* component to have specific styles different than the other components of the
|
||||
* same type in your application. This avoids using descendant selectors to
|
||||
* apply the specific styles to this component.
|
||||
*
|
||||
* @param {Function} ctor The constructor of the renderer you want to create.
|
||||
* @param {string} cssClassName The name of the CSS class for this renderer.
|
||||
* @return {goog.ui.ContainerRenderer} An instance of the desired renderer with
|
||||
* its getCssClass() method overridden to return the supplied custom CSS
|
||||
* class name.
|
||||
*/
|
||||
goog.ui.ContainerRenderer.getCustomRenderer = function(ctor, cssClassName) {
|
||||
var renderer = new ctor();
|
||||
|
||||
/**
|
||||
* Returns the CSS class to be applied to the root element of components
|
||||
* rendered using this renderer.
|
||||
* @return {string} Renderer-specific CSS class.
|
||||
*/
|
||||
renderer.getCssClass = function() {
|
||||
return cssClassName;
|
||||
};
|
||||
|
||||
return renderer;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Default CSS class to be applied to the root element of containers rendered
|
||||
* by this renderer.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.ContainerRenderer.CSS_CLASS = goog.getCssName('goog-container');
|
||||
|
||||
|
||||
/**
|
||||
* Returns the ARIA role to be applied to the container.
|
||||
* See http://wiki/Main/ARIA for more info.
|
||||
* @return {undefined|string} ARIA role.
|
||||
*/
|
||||
goog.ui.ContainerRenderer.prototype.getAriaRole = function() {
|
||||
// By default, the ARIA role is unspecified.
|
||||
return undefined;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Enables or disables the tab index of the element. Only elements with a
|
||||
* valid tab index can receive focus.
|
||||
* @param {Element} element Element whose tab index is to be changed.
|
||||
* @param {boolean} enable Whether to add or remove the element's tab index.
|
||||
*/
|
||||
goog.ui.ContainerRenderer.prototype.enableTabIndex = function(element, enable) {
|
||||
if (element) {
|
||||
element.tabIndex = enable ? 0 : -1;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates and returns the container's root element. The default
|
||||
* simply creates a DIV and applies the renderer's own CSS class name to it.
|
||||
* To be overridden in subclasses.
|
||||
* @param {goog.ui.Container} container Container to render.
|
||||
* @return {Element} Root element for the container.
|
||||
*/
|
||||
goog.ui.ContainerRenderer.prototype.createDom = function(container) {
|
||||
return container.getDomHelper().createDom('div',
|
||||
this.getClassNames(container).join(' '));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the DOM element into which child components are to be rendered,
|
||||
* or null if the container hasn't been rendered yet.
|
||||
* @param {Element} element Root element of the container whose content element
|
||||
* is to be returned.
|
||||
* @return {Element} Element to contain child elements (null if none).
|
||||
*/
|
||||
goog.ui.ContainerRenderer.prototype.getContentElement = function(element) {
|
||||
return element;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Default implementation of {@code canDecorate}; returns true if the element
|
||||
* is a DIV, false otherwise.
|
||||
* @param {Element} element Element to decorate.
|
||||
* @return {boolean} Whether the renderer can decorate the element.
|
||||
*/
|
||||
goog.ui.ContainerRenderer.prototype.canDecorate = function(element) {
|
||||
return element.tagName == 'DIV';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Default implementation of {@code decorate} for {@link goog.ui.Container}s.
|
||||
* Decorates the element with the container, and attempts to decorate its child
|
||||
* elements. Returns the decorated element.
|
||||
* @param {goog.ui.Container} container Container to decorate the element.
|
||||
* @param {Element} element Element to decorate.
|
||||
* @return {Element} Decorated element.
|
||||
*/
|
||||
goog.ui.ContainerRenderer.prototype.decorate = function(container, element) {
|
||||
// Set the container's ID to the decorated element's DOM ID, if any.
|
||||
if (element.id) {
|
||||
container.setId(element.id);
|
||||
}
|
||||
|
||||
// Configure the container's state based on the CSS class names it has.
|
||||
var baseClass = this.getCssClass();
|
||||
var hasBaseClass = false;
|
||||
var classNames = goog.dom.classes.get(element);
|
||||
if (classNames) {
|
||||
goog.array.forEach(classNames, function(className) {
|
||||
if (className == baseClass) {
|
||||
hasBaseClass = true;
|
||||
} else if (className) {
|
||||
this.setStateFromClassName(container, className, baseClass);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
|
||||
if (!hasBaseClass) {
|
||||
// Make sure the container's root element has the renderer's own CSS class.
|
||||
goog.dom.classes.add(element, baseClass);
|
||||
}
|
||||
|
||||
// Decorate the element's children, if applicable. This should happen after
|
||||
// the container's own state has been initialized, since how children are
|
||||
// decorated may depend on the state of the container.
|
||||
this.decorateChildren(container, this.getContentElement(element));
|
||||
|
||||
return element;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the container's state based on the given CSS class name, encountered
|
||||
* during decoration. CSS class names that don't represent container states
|
||||
* are ignored. Considered protected; subclasses should override this method
|
||||
* to support more states and CSS class names.
|
||||
* @param {goog.ui.Container} container Container to update.
|
||||
* @param {string} className CSS class name.
|
||||
* @param {string} baseClass Base class name used as the root of state-specific
|
||||
* class names (typically the renderer's own class name).
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.ContainerRenderer.prototype.setStateFromClassName = function(container,
|
||||
className, baseClass) {
|
||||
if (className == goog.getCssName(baseClass, 'disabled')) {
|
||||
container.setEnabled(false);
|
||||
} else if (className == goog.getCssName(baseClass, 'horizontal')) {
|
||||
container.setOrientation(goog.ui.Container.Orientation.HORIZONTAL);
|
||||
} else if (className == goog.getCssName(baseClass, 'vertical')) {
|
||||
container.setOrientation(goog.ui.Container.Orientation.VERTICAL);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Takes a container and an element that may contain child elements, decorates
|
||||
* the child elements, and adds the corresponding components to the container
|
||||
* as child components. Any non-element child nodes (e.g. empty text nodes
|
||||
* introduced by line breaks in the HTML source) are removed from the element.
|
||||
* @param {goog.ui.Container} container Container whose children are to be
|
||||
* discovered.
|
||||
* @param {Element} element Element whose children are to be decorated.
|
||||
* @param {Element=} opt_firstChild the first child to be decorated.
|
||||
*/
|
||||
goog.ui.ContainerRenderer.prototype.decorateChildren = function(container,
|
||||
element, opt_firstChild) {
|
||||
if (element) {
|
||||
var node = opt_firstChild || element.firstChild, next;
|
||||
// Tag soup HTML may result in a DOM where siblings have different parents.
|
||||
while (node && node.parentNode == element) {
|
||||
// Get the next sibling here, since the node may be replaced or removed.
|
||||
next = node.nextSibling;
|
||||
if (node.nodeType == goog.dom.NodeType.ELEMENT) {
|
||||
// Decorate element node.
|
||||
var child = this.getDecoratorForChild(/** @type {Element} */(node));
|
||||
if (child) {
|
||||
// addChild() may need to look at the element.
|
||||
child.setElementInternal(/** @type {Element} */(node));
|
||||
// If the container is disabled, mark the child disabled too. See
|
||||
// bug 1263729. Note that this must precede the call to addChild().
|
||||
if (!container.isEnabled()) {
|
||||
child.setEnabled(false);
|
||||
}
|
||||
container.addChild(child);
|
||||
child.decorate(/** @type {Element} */(node));
|
||||
}
|
||||
} else if (!node.nodeValue || goog.string.trim(node.nodeValue) == '') {
|
||||
// Remove empty text node, otherwise madness ensues (e.g. controls that
|
||||
// use goog-inline-block will flicker and shift on hover on Gecko).
|
||||
element.removeChild(node);
|
||||
}
|
||||
node = next;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Inspects the element, and creates an instance of {@link goog.ui.Control} or
|
||||
* an appropriate subclass best suited to decorate it. Returns the control (or
|
||||
* null if no suitable class was found). This default implementation uses the
|
||||
* element's CSS class to find the appropriate control class to instantiate.
|
||||
* May be overridden in subclasses.
|
||||
* @param {Element} element Element to decorate.
|
||||
* @return {goog.ui.Control?} A new control suitable to decorate the element
|
||||
* (null if none).
|
||||
*/
|
||||
goog.ui.ContainerRenderer.prototype.getDecoratorForChild = function(element) {
|
||||
return /** @type {goog.ui.Control} */ (
|
||||
goog.ui.registry.getDecorator(element));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the container's DOM when the container enters the document.
|
||||
* Called from {@link goog.ui.Container#enterDocument}.
|
||||
* @param {goog.ui.Container} container Container whose DOM is to be initialized
|
||||
* as it enters the document.
|
||||
*/
|
||||
goog.ui.ContainerRenderer.prototype.initializeDom = function(container) {
|
||||
var elem = container.getElement();
|
||||
goog.asserts.assert(elem, 'The container DOM element cannot be null.');
|
||||
// Make sure the container's element isn't selectable. On Gecko, recursively
|
||||
// marking each child element unselectable is expensive and unnecessary, so
|
||||
// only mark the root element unselectable.
|
||||
goog.style.setUnselectable(elem, true, goog.userAgent.GECKO);
|
||||
|
||||
// IE doesn't support outline:none, so we have to use the hideFocus property.
|
||||
if (goog.userAgent.IE) {
|
||||
elem.hideFocus = true;
|
||||
}
|
||||
|
||||
// Set the ARIA role.
|
||||
var ariaRole = this.getAriaRole();
|
||||
if (ariaRole) {
|
||||
goog.a11y.aria.setRole(elem, ariaRole);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the element within the container's DOM that should receive keyboard
|
||||
* focus (null if none). The default implementation returns the container's
|
||||
* root element.
|
||||
* @param {goog.ui.Container} container Container whose key event target is
|
||||
* to be returned.
|
||||
* @return {Element} Key event target (null if none).
|
||||
*/
|
||||
goog.ui.ContainerRenderer.prototype.getKeyEventTarget = function(container) {
|
||||
return container.getElement();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the CSS class to be applied to the root element of containers
|
||||
* rendered using this renderer.
|
||||
* @return {string} Renderer-specific CSS class.
|
||||
*/
|
||||
goog.ui.ContainerRenderer.prototype.getCssClass = function() {
|
||||
return goog.ui.ContainerRenderer.CSS_CLASS;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns all CSS class names applicable to the given container, based on its
|
||||
* state. The array of class names returned includes the renderer's own CSS
|
||||
* class, followed by a CSS class indicating the container's orientation,
|
||||
* followed by any state-specific CSS classes.
|
||||
* @param {goog.ui.Container} container Container whose CSS classes are to be
|
||||
* returned.
|
||||
* @return {Array.<string>} Array of CSS class names applicable to the
|
||||
* container.
|
||||
*/
|
||||
goog.ui.ContainerRenderer.prototype.getClassNames = function(container) {
|
||||
var baseClass = this.getCssClass();
|
||||
var isHorizontal =
|
||||
container.getOrientation() == goog.ui.Container.Orientation.HORIZONTAL;
|
||||
var classNames = [
|
||||
baseClass,
|
||||
(isHorizontal ?
|
||||
goog.getCssName(baseClass, 'horizontal') :
|
||||
goog.getCssName(baseClass, 'vertical'))
|
||||
];
|
||||
if (!container.isEnabled()) {
|
||||
classNames.push(goog.getCssName(baseClass, 'disabled'));
|
||||
}
|
||||
return classNames;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the default orientation of containers rendered or decorated by this
|
||||
* renderer. The base class implementation returns {@code VERTICAL}.
|
||||
* @return {goog.ui.Container.Orientation} Default orientation for containers
|
||||
* created or decorated by this renderer.
|
||||
*/
|
||||
goog.ui.ContainerRenderer.prototype.getDefaultOrientation = function() {
|
||||
return goog.ui.Container.Orientation.VERTICAL;
|
||||
};
|
||||
@@ -0,0 +1,222 @@
|
||||
// Copyright 2008 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 Scroll behavior that can be added onto a container.
|
||||
* @author gboyer@google.com (Garry Boyer)
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.ContainerScroller');
|
||||
|
||||
goog.require('goog.Disposable');
|
||||
goog.require('goog.Timer');
|
||||
goog.require('goog.events.EventHandler');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('goog.ui.Container');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Plug-on scrolling behavior for a container.
|
||||
*
|
||||
* Use this to style containers, such as pop-up menus, to be scrolling, and
|
||||
* automatically keep the highlighted element visible.
|
||||
*
|
||||
* To use this, first style your container with the desired overflow
|
||||
* properties and height to achieve vertical scrolling. Also, the scrolling
|
||||
* div should have no vertical padding, for two reasons: it is difficult to
|
||||
* compensate for, and is generally not what you want due to the strange way
|
||||
* CSS handles padding on the scrolling dimension.
|
||||
*
|
||||
* The container must already be rendered before this may be constructed.
|
||||
*
|
||||
* @param {!goog.ui.Container} container The container to attach behavior to.
|
||||
* @constructor
|
||||
* @extends {goog.Disposable}
|
||||
*/
|
||||
goog.ui.ContainerScroller = function(container) {
|
||||
goog.Disposable.call(this);
|
||||
|
||||
/**
|
||||
* The container that we are bestowing scroll behavior on.
|
||||
* @type {!goog.ui.Container}
|
||||
* @private
|
||||
*/
|
||||
this.container_ = container;
|
||||
|
||||
/**
|
||||
* Event handler for this object.
|
||||
* @type {!goog.events.EventHandler}
|
||||
* @private
|
||||
*/
|
||||
this.eventHandler_ = new goog.events.EventHandler(this);
|
||||
|
||||
this.eventHandler_.listen(container, goog.ui.Component.EventType.HIGHLIGHT,
|
||||
this.onHighlight_);
|
||||
this.eventHandler_.listen(container, goog.ui.Component.EventType.ENTER,
|
||||
this.onEnter_);
|
||||
this.eventHandler_.listen(container, goog.ui.Container.EventType.AFTER_SHOW,
|
||||
this.onAfterShow_);
|
||||
this.eventHandler_.listen(container, goog.ui.Component.EventType.HIDE,
|
||||
this.onHide_);
|
||||
|
||||
// TODO(gboyer): Allow a ContainerScroller to be attached with a Container
|
||||
// before the container is rendered.
|
||||
|
||||
this.doScrolling_(true);
|
||||
};
|
||||
goog.inherits(goog.ui.ContainerScroller, goog.Disposable);
|
||||
|
||||
|
||||
/**
|
||||
* The last target the user hovered over.
|
||||
*
|
||||
* @see #onEnter_
|
||||
* @type {goog.ui.Component}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ContainerScroller.prototype.lastEnterTarget_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* The scrollTop of the container before it was hidden.
|
||||
* Used to restore the scroll position when the container is shown again.
|
||||
* @type {?number}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ContainerScroller.prototype.scrollTopBeforeHide_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Whether we are disabling the default handler for hovering.
|
||||
*
|
||||
* @see #onEnter_
|
||||
* @see #temporarilyDisableHover_
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ContainerScroller.prototype.disableHover_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* Handles hover events on the container's children.
|
||||
*
|
||||
* Helps enforce two constraints: scrolling should not cause mouse highlights,
|
||||
* and mouse highlights should not cause scrolling.
|
||||
*
|
||||
* @param {goog.events.Event} e The container's ENTER event.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ContainerScroller.prototype.onEnter_ = function(e) {
|
||||
if (this.disableHover_) {
|
||||
// The container was scrolled recently. Since the mouse may be over the
|
||||
// container, stop the default action of the ENTER event from causing
|
||||
// highlights.
|
||||
e.preventDefault();
|
||||
} else {
|
||||
// The mouse is moving and causing hover events. Stop the resulting
|
||||
// highlight (if it happens) from causing a scroll.
|
||||
this.lastEnterTarget_ = /** @type {goog.ui.Component} */ (e.target);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles highlight events on the container's children.
|
||||
* @param {goog.events.Event} e The container's highlight event.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ContainerScroller.prototype.onHighlight_ = function(e) {
|
||||
this.doScrolling_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles AFTER_SHOW events on the container. Makes the container
|
||||
* scroll to the previously scrolled position (if there was one),
|
||||
* then adjust it to make the highlighted element be in view (if there is one).
|
||||
* If there was no previous scroll position, then center the highlighted
|
||||
* element (if there is one).
|
||||
* @param {goog.events.Event} e The container's AFTER_SHOW event.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ContainerScroller.prototype.onAfterShow_ = function(e) {
|
||||
if (this.scrollTopBeforeHide_ != null) {
|
||||
this.container_.getElement().scrollTop = this.scrollTopBeforeHide_;
|
||||
// Make sure the highlighted item is still visible, in case the list
|
||||
// or its hilighted item has changed.
|
||||
this.doScrolling_(false);
|
||||
} else {
|
||||
this.doScrolling_(true);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles hide events on the container. Clears out the last enter target,
|
||||
* since it is no longer applicable, and remembers the scroll position of
|
||||
* the menu so that it can be restored when the menu is reopened.
|
||||
* @param {goog.events.Event} e The container's hide event.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ContainerScroller.prototype.onHide_ = function(e) {
|
||||
if (e.target == this.container_) {
|
||||
this.lastEnterTarget_ = null;
|
||||
this.scrollTopBeforeHide_ = this.container_.getElement().scrollTop;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Centers the currently highlighted item, if this is scrollable.
|
||||
* @param {boolean=} opt_center Whether to center the highlighted element
|
||||
* rather than simply ensure it is in view. Useful for the first
|
||||
* render.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ContainerScroller.prototype.doScrolling_ = function(opt_center) {
|
||||
var highlighted = this.container_.getHighlighted();
|
||||
|
||||
// Only scroll if we're visible and there is a highlighted item.
|
||||
if (this.container_.isVisible() && highlighted &&
|
||||
highlighted != this.lastEnterTarget_) {
|
||||
var element = this.container_.getElement();
|
||||
goog.style.scrollIntoContainerView(highlighted.getElement(), element,
|
||||
opt_center);
|
||||
this.temporarilyDisableHover_();
|
||||
this.lastEnterTarget_ = null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Temporarily disables hover events from changing highlight.
|
||||
* @see #onEnter_
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ContainerScroller.prototype.temporarilyDisableHover_ = function() {
|
||||
this.disableHover_ = true;
|
||||
goog.Timer.callOnce(function() {
|
||||
this.disableHover_ = false;
|
||||
}, 0, this);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.ContainerScroller.prototype.disposeInternal = function() {
|
||||
goog.ui.ContainerScroller.superClass_.disposeInternal.call(this);
|
||||
this.eventHandler_.dispose();
|
||||
this.lastEnterTarget_ = null;
|
||||
};
|
||||
1387
nicer-api-docs/closure-library/closure/goog/ui/control.js
Normal file
1387
nicer-api-docs/closure-library/closure/goog/ui/control.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,28 @@
|
||||
// Copyright 2009 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 Type declaration for control content.
|
||||
*
|
||||
* @author nicksantos@google.com (Nick Santos)
|
||||
*/
|
||||
goog.provide('goog.ui.ControlContent');
|
||||
|
||||
|
||||
/**
|
||||
* Type declaration for text caption or DOM structure to be used as the content
|
||||
* of {@link goog.ui.Control}s.
|
||||
* @typedef {string|Node|Array.<Node>|NodeList}
|
||||
*/
|
||||
goog.ui.ControlContent;
|
||||
@@ -0,0 +1,855 @@
|
||||
// Copyright 2008 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 Base class for control renderers.
|
||||
* TODO(attila): If the renderer framework works well, pull it into Component.
|
||||
*
|
||||
* @author attila@google.com (Attila Bodis)
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.ControlRenderer');
|
||||
|
||||
goog.require('goog.a11y.aria');
|
||||
goog.require('goog.a11y.aria.State');
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.classes');
|
||||
goog.require('goog.object');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Default renderer for {@link goog.ui.Control}s. Can be used as-is, but
|
||||
* subclasses of Control will probably want to use renderers specifically
|
||||
* tailored for them by extending this class. Controls that use renderers
|
||||
* delegate one or more of the following API methods to the renderer:
|
||||
* <ul>
|
||||
* <li>{@code createDom} - renders the DOM for the component
|
||||
* <li>{@code canDecorate} - determines whether an element can be decorated
|
||||
* by the component
|
||||
* <li>{@code decorate} - decorates an existing element with the component
|
||||
* <li>{@code setState} - updates the appearance of the component based on
|
||||
* its state
|
||||
* <li>{@code getContent} - returns the component's content
|
||||
* <li>{@code setContent} - sets the component's content
|
||||
* </ul>
|
||||
* Controls are stateful; renderers, on the other hand, should be stateless and
|
||||
* reusable.
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.ControlRenderer = function() {
|
||||
};
|
||||
goog.addSingletonGetter(goog.ui.ControlRenderer);
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new renderer and sets the CSS class that the renderer will use
|
||||
* as the base CSS class to apply to all elements rendered by that renderer.
|
||||
* An example to use this function using a color palette:
|
||||
*
|
||||
* <pre>
|
||||
* var myCustomRenderer = goog.ui.ControlRenderer.getCustomRenderer(
|
||||
* goog.ui.PaletteRenderer, 'my-special-palette');
|
||||
* var newColorPalette = new goog.ui.ColorPalette(
|
||||
* colors, myCustomRenderer, opt_domHelper);
|
||||
* </pre>
|
||||
*
|
||||
* Your CSS can look like this now:
|
||||
* <pre>
|
||||
* .my-special-palette { }
|
||||
* .my-special-palette-table { }
|
||||
* .my-special-palette-cell { }
|
||||
* etc.
|
||||
* </pre>
|
||||
*
|
||||
* <em>instead</em> of
|
||||
* <pre>
|
||||
* .CSS_MY_SPECIAL_PALETTE .goog-palette { }
|
||||
* .CSS_MY_SPECIAL_PALETTE .goog-palette-table { }
|
||||
* .CSS_MY_SPECIAL_PALETTE .goog-palette-cell { }
|
||||
* etc.
|
||||
* </pre>
|
||||
*
|
||||
* You would want to use this functionality when you want an instance of a
|
||||
* component to have specific styles different than the other components of the
|
||||
* same type in your application. This avoids using descendant selectors to
|
||||
* apply the specific styles to this component.
|
||||
*
|
||||
* @param {Function} ctor The constructor of the renderer you are trying to
|
||||
* create.
|
||||
* @param {string} cssClassName The name of the CSS class for this renderer.
|
||||
* @return {goog.ui.ControlRenderer} An instance of the desired renderer with
|
||||
* its getCssClass() method overridden to return the supplied custom CSS
|
||||
* class name.
|
||||
*/
|
||||
goog.ui.ControlRenderer.getCustomRenderer = function(ctor, cssClassName) {
|
||||
var renderer = new ctor();
|
||||
|
||||
/**
|
||||
* Returns the CSS class to be applied to the root element of components
|
||||
* rendered using this renderer.
|
||||
* @return {string} Renderer-specific CSS class.
|
||||
*/
|
||||
renderer.getCssClass = function() {
|
||||
return cssClassName;
|
||||
};
|
||||
|
||||
return renderer;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Default CSS class to be applied to the root element of components rendered
|
||||
* by this renderer.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.ControlRenderer.CSS_CLASS = goog.getCssName('goog-control');
|
||||
|
||||
|
||||
/**
|
||||
* Array of arrays of CSS classes that we want composite classes added and
|
||||
* removed for in IE6 and lower as a workaround for lack of multi-class CSS
|
||||
* selector support.
|
||||
*
|
||||
* Subclasses that have accompanying CSS requiring this workaround should define
|
||||
* their own static IE6_CLASS_COMBINATIONS constant and override
|
||||
* getIe6ClassCombinations to return it.
|
||||
*
|
||||
* For example, if your stylesheet uses the selector .button.collapse-left
|
||||
* (and is compiled to .button_collapse-left for the IE6 version of the
|
||||
* stylesheet,) you should include ['button', 'collapse-left'] in this array
|
||||
* and the class button_collapse-left will be applied to the root element
|
||||
* whenever both button and collapse-left are applied individually.
|
||||
*
|
||||
* Members of each class name combination will be joined with underscores in the
|
||||
* order that they're defined in the array. You should alphabetize them (for
|
||||
* compatibility with the CSS compiler) unless you are doing something special.
|
||||
* @type {Array.<Array.<string>>}
|
||||
*/
|
||||
goog.ui.ControlRenderer.IE6_CLASS_COMBINATIONS = [];
|
||||
|
||||
|
||||
/**
|
||||
* Map of component states to corresponding ARIA states. Since the mapping of
|
||||
* component states to ARIA states is neither component- nor renderer-specific,
|
||||
* this is a static property of the renderer class, and is initialized on first
|
||||
* use.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ControlRenderer.ARIA_STATE_MAP_;
|
||||
|
||||
|
||||
/**
|
||||
* Returns the ARIA role to be applied to the control.
|
||||
* See http://wiki/Main/ARIA for more info.
|
||||
* @return {goog.a11y.aria.Role|undefined} ARIA role.
|
||||
*/
|
||||
goog.ui.ControlRenderer.prototype.getAriaRole = function() {
|
||||
// By default, the ARIA role is unspecified.
|
||||
return undefined;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the control's contents wrapped in a DIV, with the renderer's own
|
||||
* CSS class and additional state-specific classes applied to it.
|
||||
* @param {goog.ui.Control} control Control to render.
|
||||
* @return {Element} Root element for the control.
|
||||
*/
|
||||
goog.ui.ControlRenderer.prototype.createDom = function(control) {
|
||||
// Create and return DIV wrapping contents.
|
||||
var element = control.getDomHelper().createDom(
|
||||
'div', this.getClassNames(control).join(' '), control.getContent());
|
||||
|
||||
this.setAriaStates(control, element);
|
||||
return element;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Takes the control's root element and returns the parent element of the
|
||||
* control's contents. Since by default controls are rendered as a single
|
||||
* DIV, the default implementation returns the element itself. Subclasses
|
||||
* with more complex DOM structures must override this method as needed.
|
||||
* @param {Element} element Root element of the control whose content element
|
||||
* is to be returned.
|
||||
* @return {Element} The control's content element.
|
||||
*/
|
||||
goog.ui.ControlRenderer.prototype.getContentElement = function(element) {
|
||||
return element;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Updates the control's DOM by adding or removing the specified class name
|
||||
* to/from its root element. May add additional combined classes as needed in
|
||||
* IE6 and lower. Because of this, subclasses should use this method when
|
||||
* modifying class names on the control's root element.
|
||||
* @param {goog.ui.Control|Element} control Control instance (or root element)
|
||||
* to be updated.
|
||||
* @param {string} className CSS class name to add or remove.
|
||||
* @param {boolean} enable Whether to add or remove the class name.
|
||||
*/
|
||||
goog.ui.ControlRenderer.prototype.enableClassName = function(control,
|
||||
className, enable) {
|
||||
var element = /** @type {Element} */ (
|
||||
control.getElement ? control.getElement() : control);
|
||||
if (element) {
|
||||
// For IE6, we need to enable any combined classes involving this class
|
||||
// as well.
|
||||
if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('7')) {
|
||||
var combinedClasses = this.getAppliedCombinedClassNames_(
|
||||
goog.dom.classes.get(element), className);
|
||||
combinedClasses.push(className);
|
||||
var f = enable ? goog.dom.classes.add : goog.dom.classes.remove;
|
||||
goog.partial(f, element).apply(null, combinedClasses);
|
||||
} else {
|
||||
goog.dom.classes.enable(element, className, enable);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Updates the control's DOM by adding or removing the specified extra class
|
||||
* name to/from its element.
|
||||
* @param {goog.ui.Control} control Control to be updated.
|
||||
* @param {string} className CSS class name to add or remove.
|
||||
* @param {boolean} enable Whether to add or remove the class name.
|
||||
*/
|
||||
goog.ui.ControlRenderer.prototype.enableExtraClassName = function(control,
|
||||
className, enable) {
|
||||
// The base class implementation is trivial; subclasses should override as
|
||||
// needed.
|
||||
this.enableClassName(control, className, enable);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if this renderer can decorate the element, false otherwise.
|
||||
* The default implementation always returns true.
|
||||
* @param {Element} element Element to decorate.
|
||||
* @return {boolean} Whether the renderer can decorate the element.
|
||||
*/
|
||||
goog.ui.ControlRenderer.prototype.canDecorate = function(element) {
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Default implementation of {@code decorate} for {@link goog.ui.Control}s.
|
||||
* Initializes the control's ID, content, and state based on the ID of the
|
||||
* element, its child nodes, and its CSS classes, respectively. Returns the
|
||||
* element.
|
||||
* @param {goog.ui.Control} control Control instance to decorate the element.
|
||||
* @param {Element} element Element to decorate.
|
||||
* @return {Element} Decorated element.
|
||||
*/
|
||||
goog.ui.ControlRenderer.prototype.decorate = function(control, element) {
|
||||
// Set the control's ID to the decorated element's DOM ID, if any.
|
||||
if (element.id) {
|
||||
control.setId(element.id);
|
||||
}
|
||||
|
||||
// Set the control's content to the decorated element's content.
|
||||
var contentElem = this.getContentElement(element);
|
||||
if (contentElem && contentElem.firstChild) {
|
||||
control.setContentInternal(contentElem.firstChild.nextSibling ?
|
||||
goog.array.clone(contentElem.childNodes) : contentElem.firstChild);
|
||||
} else {
|
||||
control.setContentInternal(null);
|
||||
}
|
||||
|
||||
// Initialize the control's state based on the decorated element's CSS class.
|
||||
// This implementation is optimized to minimize object allocations, string
|
||||
// comparisons, and DOM access.
|
||||
var state = 0x00;
|
||||
var rendererClassName = this.getCssClass();
|
||||
var structuralClassName = this.getStructuralCssClass();
|
||||
var hasRendererClassName = false;
|
||||
var hasStructuralClassName = false;
|
||||
var hasCombinedClassName = false;
|
||||
var classNames = goog.dom.classes.get(element);
|
||||
goog.array.forEach(classNames, function(className) {
|
||||
if (!hasRendererClassName && className == rendererClassName) {
|
||||
hasRendererClassName = true;
|
||||
if (structuralClassName == rendererClassName) {
|
||||
hasStructuralClassName = true;
|
||||
}
|
||||
} else if (!hasStructuralClassName && className == structuralClassName) {
|
||||
hasStructuralClassName = true;
|
||||
} else {
|
||||
state |= this.getStateFromClass(className);
|
||||
}
|
||||
}, this);
|
||||
control.setStateInternal(state);
|
||||
|
||||
// Make sure the element has the renderer's CSS classes applied, as well as
|
||||
// any extra class names set on the control.
|
||||
if (!hasRendererClassName) {
|
||||
classNames.push(rendererClassName);
|
||||
if (structuralClassName == rendererClassName) {
|
||||
hasStructuralClassName = true;
|
||||
}
|
||||
}
|
||||
if (!hasStructuralClassName) {
|
||||
classNames.push(structuralClassName);
|
||||
}
|
||||
var extraClassNames = control.getExtraClassNames();
|
||||
if (extraClassNames) {
|
||||
classNames.push.apply(classNames, extraClassNames);
|
||||
}
|
||||
|
||||
// For IE6, rewrite all classes on the decorated element if any combined
|
||||
// classes apply.
|
||||
if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('7')) {
|
||||
var combinedClasses = this.getAppliedCombinedClassNames_(
|
||||
classNames);
|
||||
if (combinedClasses.length > 0) {
|
||||
classNames.push.apply(classNames, combinedClasses);
|
||||
hasCombinedClassName = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Only write to the DOM if new class names had to be added to the element.
|
||||
if (!hasRendererClassName || !hasStructuralClassName ||
|
||||
extraClassNames || hasCombinedClassName) {
|
||||
goog.dom.classes.set(element, classNames.join(' '));
|
||||
}
|
||||
|
||||
this.setAriaStates(control, element);
|
||||
return element;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the control's DOM by configuring properties that can only be set
|
||||
* after the DOM has entered the document. This implementation sets up BiDi
|
||||
* and keyboard focus. Called from {@link goog.ui.Control#enterDocument}.
|
||||
* @param {goog.ui.Control} control Control whose DOM is to be initialized
|
||||
* as it enters the document.
|
||||
*/
|
||||
goog.ui.ControlRenderer.prototype.initializeDom = function(control) {
|
||||
// Initialize render direction (BiDi). We optimize the left-to-right render
|
||||
// direction by assuming that elements are left-to-right by default, and only
|
||||
// updating their styling if they are explicitly set to right-to-left.
|
||||
if (control.isRightToLeft()) {
|
||||
this.setRightToLeft(control.getElement(), true);
|
||||
}
|
||||
|
||||
// Initialize keyboard focusability (tab index). We assume that components
|
||||
// aren't focusable by default (i.e have no tab index), and only touch the
|
||||
// DOM if the component is focusable, enabled, and visible, and therefore
|
||||
// needs a tab index.
|
||||
if (control.isEnabled()) {
|
||||
this.setFocusable(control, control.isVisible());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the element's ARIA role.
|
||||
* @param {Element} element Element to update.
|
||||
* @param {?goog.a11y.aria.Role=} opt_preferredRole The preferred ARIA role.
|
||||
*/
|
||||
goog.ui.ControlRenderer.prototype.setAriaRole = function(element,
|
||||
opt_preferredRole) {
|
||||
var ariaRole = opt_preferredRole || this.getAriaRole();
|
||||
if (ariaRole) {
|
||||
goog.asserts.assert(element,
|
||||
'The element passed as a first parameter cannot be null.');
|
||||
goog.a11y.aria.setRole(element, ariaRole);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the element's ARIA states. An element does not need an ARIA role in
|
||||
* order to have an ARIA state. Only states which are initialized to be true
|
||||
* will be set.
|
||||
* @param {!goog.ui.Control} control Control whose ARIA state will be updated.
|
||||
* @param {!Element} element Element whose ARIA state is to be updated.
|
||||
*/
|
||||
goog.ui.ControlRenderer.prototype.setAriaStates = function(control, element) {
|
||||
goog.asserts.assert(control);
|
||||
goog.asserts.assert(element);
|
||||
|
||||
if (!control.isVisible()) {
|
||||
goog.a11y.aria.setState(
|
||||
element, goog.a11y.aria.State.HIDDEN, !control.isVisible());
|
||||
}
|
||||
if (!control.isEnabled()) {
|
||||
this.updateAriaState(
|
||||
element, goog.ui.Component.State.DISABLED, !control.isEnabled());
|
||||
}
|
||||
if (control.isSupportedState(goog.ui.Component.State.SELECTED)) {
|
||||
this.updateAriaState(
|
||||
element, goog.ui.Component.State.SELECTED, control.isSelected());
|
||||
}
|
||||
if (control.isSupportedState(goog.ui.Component.State.CHECKED)) {
|
||||
this.updateAriaState(
|
||||
element, goog.ui.Component.State.CHECKED, control.isChecked());
|
||||
}
|
||||
if (control.isSupportedState(goog.ui.Component.State.OPENED)) {
|
||||
this.updateAriaState(
|
||||
element, goog.ui.Component.State.OPENED, control.isOpen());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Allows or disallows text selection within the control's DOM.
|
||||
* @param {Element} element The control's root element.
|
||||
* @param {boolean} allow Whether the element should allow text selection.
|
||||
*/
|
||||
goog.ui.ControlRenderer.prototype.setAllowTextSelection = function(element,
|
||||
allow) {
|
||||
// On all browsers other than IE and Opera, it isn't necessary to recursively
|
||||
// apply unselectable styling to the element's children.
|
||||
goog.style.setUnselectable(element, !allow,
|
||||
!goog.userAgent.IE && !goog.userAgent.OPERA);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Applies special styling to/from the control's element if it is rendered
|
||||
* right-to-left, and removes it if it is rendered left-to-right.
|
||||
* @param {Element} element The control's root element.
|
||||
* @param {boolean} rightToLeft Whether the component is rendered
|
||||
* right-to-left.
|
||||
*/
|
||||
goog.ui.ControlRenderer.prototype.setRightToLeft = function(element,
|
||||
rightToLeft) {
|
||||
this.enableClassName(element,
|
||||
goog.getCssName(this.getStructuralCssClass(), 'rtl'), rightToLeft);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the control's key event target supports keyboard focus
|
||||
* (based on its {@code tabIndex} attribute), false otherwise.
|
||||
* @param {goog.ui.Control} control Control whose key event target is to be
|
||||
* checked.
|
||||
* @return {boolean} Whether the control's key event target is focusable.
|
||||
*/
|
||||
goog.ui.ControlRenderer.prototype.isFocusable = function(control) {
|
||||
var keyTarget;
|
||||
if (control.isSupportedState(goog.ui.Component.State.FOCUSED) &&
|
||||
(keyTarget = control.getKeyEventTarget())) {
|
||||
return goog.dom.isFocusableTabIndex(keyTarget);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Updates the control's key event target to make it focusable or non-focusable
|
||||
* via its {@code tabIndex} attribute. Does nothing if the control doesn't
|
||||
* support the {@code FOCUSED} state, or if it has no key event target.
|
||||
* @param {goog.ui.Control} control Control whose key event target is to be
|
||||
* updated.
|
||||
* @param {boolean} focusable Whether to enable keyboard focus support on the
|
||||
* control's key event target.
|
||||
*/
|
||||
goog.ui.ControlRenderer.prototype.setFocusable = function(control, focusable) {
|
||||
var keyTarget;
|
||||
if (control.isSupportedState(goog.ui.Component.State.FOCUSED) &&
|
||||
(keyTarget = control.getKeyEventTarget())) {
|
||||
if (!focusable && control.isFocused()) {
|
||||
// Blur before hiding. Note that IE calls onblur handlers asynchronously.
|
||||
try {
|
||||
keyTarget.blur();
|
||||
} catch (e) {
|
||||
// TODO(user|user): Find out why this fails on IE.
|
||||
}
|
||||
// The blur event dispatched by the key event target element when blur()
|
||||
// was called on it should have been handled by the control's handleBlur()
|
||||
// method, so at this point the control should no longer be focused.
|
||||
// However, blur events are unreliable on IE and FF3, so if at this point
|
||||
// the control is still focused, we trigger its handleBlur() method
|
||||
// programmatically.
|
||||
if (control.isFocused()) {
|
||||
control.handleBlur(null);
|
||||
}
|
||||
}
|
||||
// Don't overwrite existing tab index values unless needed.
|
||||
if (goog.dom.isFocusableTabIndex(keyTarget) != focusable) {
|
||||
goog.dom.setFocusableTabIndex(keyTarget, focusable);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Shows or hides the element.
|
||||
* @param {Element} element Element to update.
|
||||
* @param {boolean} visible Whether to show the element.
|
||||
*/
|
||||
goog.ui.ControlRenderer.prototype.setVisible = function(element, visible) {
|
||||
// The base class implementation is trivial; subclasses should override as
|
||||
// needed. It should be possible to do animated reveals, for example.
|
||||
goog.style.setElementShown(element, visible);
|
||||
if (element) {
|
||||
goog.a11y.aria.setState(element, goog.a11y.aria.State.HIDDEN, !visible);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Updates the appearance of the control in response to a state change.
|
||||
* @param {goog.ui.Control} control Control instance to update.
|
||||
* @param {goog.ui.Component.State} state State to enable or disable.
|
||||
* @param {boolean} enable Whether the control is entering or exiting the state.
|
||||
*/
|
||||
goog.ui.ControlRenderer.prototype.setState = function(control, state, enable) {
|
||||
var element = control.getElement();
|
||||
if (element) {
|
||||
var className = this.getClassForState(state);
|
||||
if (className) {
|
||||
this.enableClassName(control, className, enable);
|
||||
}
|
||||
this.updateAriaState(element, state, enable);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Updates the element's ARIA (accessibility) state.
|
||||
* @param {Element} element Element whose ARIA state is to be updated.
|
||||
* @param {goog.ui.Component.State} state Component state being enabled or
|
||||
* disabled.
|
||||
* @param {boolean} enable Whether the state is being enabled or disabled.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.ControlRenderer.prototype.updateAriaState = function(element, state,
|
||||
enable) {
|
||||
// Ensure the ARIA state map exists.
|
||||
if (!goog.ui.ControlRenderer.ARIA_STATE_MAP_) {
|
||||
goog.ui.ControlRenderer.ARIA_STATE_MAP_ = goog.object.create(
|
||||
goog.ui.Component.State.DISABLED, goog.a11y.aria.State.DISABLED,
|
||||
goog.ui.Component.State.SELECTED, goog.a11y.aria.State.SELECTED,
|
||||
goog.ui.Component.State.CHECKED, goog.a11y.aria.State.CHECKED,
|
||||
goog.ui.Component.State.OPENED, goog.a11y.aria.State.EXPANDED);
|
||||
}
|
||||
var ariaState = goog.ui.ControlRenderer.ARIA_STATE_MAP_[state];
|
||||
if (ariaState) {
|
||||
goog.asserts.assert(element,
|
||||
'The element passed as a first parameter cannot be null.');
|
||||
goog.a11y.aria.setState(element, ariaState, enable);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Takes a control's root element, and sets its content to the given text
|
||||
* caption or DOM structure. The default implementation replaces the children
|
||||
* of the given element. Renderers that create more complex DOM structures
|
||||
* must override this method accordingly.
|
||||
* @param {Element} element The control's root element.
|
||||
* @param {goog.ui.ControlContent} content Text caption or DOM structure to be
|
||||
* set as the control's content. The DOM nodes will not be cloned, they
|
||||
* will only moved under the content element of the control.
|
||||
*/
|
||||
goog.ui.ControlRenderer.prototype.setContent = function(element, content) {
|
||||
var contentElem = this.getContentElement(element);
|
||||
if (contentElem) {
|
||||
goog.dom.removeChildren(contentElem);
|
||||
if (content) {
|
||||
if (goog.isString(content)) {
|
||||
goog.dom.setTextContent(contentElem, content);
|
||||
} else {
|
||||
var childHandler = function(child) {
|
||||
if (child) {
|
||||
var doc = goog.dom.getOwnerDocument(contentElem);
|
||||
contentElem.appendChild(goog.isString(child) ?
|
||||
doc.createTextNode(child) : child);
|
||||
}
|
||||
};
|
||||
if (goog.isArray(content)) {
|
||||
// Array of nodes.
|
||||
goog.array.forEach(content, childHandler);
|
||||
} else if (goog.isArrayLike(content) && !('nodeType' in content)) {
|
||||
// NodeList. The second condition filters out TextNode which also has
|
||||
// length attribute but is not array like. The nodes have to be cloned
|
||||
// because childHandler removes them from the list during iteration.
|
||||
goog.array.forEach(goog.array.clone(/** @type {NodeList} */(content)),
|
||||
childHandler);
|
||||
} else {
|
||||
// Node or string.
|
||||
childHandler(content);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the element within the component's DOM that should receive keyboard
|
||||
* focus (null if none). The default implementation returns the control's root
|
||||
* element.
|
||||
* @param {goog.ui.Control} control Control whose key event target is to be
|
||||
* returned.
|
||||
* @return {Element} The key event target.
|
||||
*/
|
||||
goog.ui.ControlRenderer.prototype.getKeyEventTarget = function(control) {
|
||||
return control.getElement();
|
||||
};
|
||||
|
||||
|
||||
// CSS class name management.
|
||||
|
||||
|
||||
/**
|
||||
* Returns the CSS class name to be applied to the root element of all
|
||||
* components rendered or decorated using this renderer. The class name
|
||||
* is expected to uniquely identify the renderer class, i.e. no two
|
||||
* renderer classes are expected to share the same CSS class name.
|
||||
* @return {string} Renderer-specific CSS class name.
|
||||
*/
|
||||
goog.ui.ControlRenderer.prototype.getCssClass = function() {
|
||||
return goog.ui.ControlRenderer.CSS_CLASS;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of combinations of classes to apply combined class names for
|
||||
* in IE6 and below. See {@link IE6_CLASS_COMBINATIONS} for more detail. This
|
||||
* method doesn't reference {@link IE6_CLASS_COMBINATIONS} so that it can be
|
||||
* compiled out, but subclasses should return their IE6_CLASS_COMBINATIONS
|
||||
* static constant instead.
|
||||
* @return {Array.<Array.<string>>} Array of class name combinations.
|
||||
*/
|
||||
goog.ui.ControlRenderer.prototype.getIe6ClassCombinations = function() {
|
||||
return [];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the name of a DOM structure-specific CSS class to be applied to the
|
||||
* root element of all components rendered or decorated using this renderer.
|
||||
* Unlike the class name returned by {@link #getCssClass}, the structural class
|
||||
* name may be shared among different renderers that generate similar DOM
|
||||
* structures. The structural class name also serves as the basis of derived
|
||||
* class names used to identify and style structural elements of the control's
|
||||
* DOM, as well as the basis for state-specific class names. The default
|
||||
* implementation returns the same class name as {@link #getCssClass}, but
|
||||
* subclasses are expected to override this method as needed.
|
||||
* @return {string} DOM structure-specific CSS class name (same as the renderer-
|
||||
* specific CSS class name by default).
|
||||
*/
|
||||
goog.ui.ControlRenderer.prototype.getStructuralCssClass = function() {
|
||||
return this.getCssClass();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns all CSS class names applicable to the given control, based on its
|
||||
* state. The return value is an array of strings containing
|
||||
* <ol>
|
||||
* <li>the renderer-specific CSS class returned by {@link #getCssClass},
|
||||
* followed by
|
||||
* <li>the structural CSS class returned by {@link getStructuralCssClass} (if
|
||||
* different from the renderer-specific CSS class), followed by
|
||||
* <li>any state-specific classes returned by {@link #getClassNamesForState},
|
||||
* followed by
|
||||
* <li>any extra classes returned by the control's {@code getExtraClassNames}
|
||||
* method and
|
||||
* <li>for IE6 and lower, additional combined classes from
|
||||
* {@link getAppliedCombinedClassNames_}.
|
||||
* </ol>
|
||||
* Since all controls have at least one renderer-specific CSS class name, this
|
||||
* method is guaranteed to return an array of at least one element.
|
||||
* @param {goog.ui.Control} control Control whose CSS classes are to be
|
||||
* returned.
|
||||
* @return {Array.<string>} Array of CSS class names applicable to the control.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.ControlRenderer.prototype.getClassNames = function(control) {
|
||||
var cssClass = this.getCssClass();
|
||||
|
||||
// Start with the renderer-specific class name.
|
||||
var classNames = [cssClass];
|
||||
|
||||
// Add structural class name, if different.
|
||||
var structuralCssClass = this.getStructuralCssClass();
|
||||
if (structuralCssClass != cssClass) {
|
||||
classNames.push(structuralCssClass);
|
||||
}
|
||||
|
||||
// Add state-specific class names, if any.
|
||||
var classNamesForState = this.getClassNamesForState(control.getState());
|
||||
classNames.push.apply(classNames, classNamesForState);
|
||||
|
||||
// Add extra class names, if any.
|
||||
var extraClassNames = control.getExtraClassNames();
|
||||
if (extraClassNames) {
|
||||
classNames.push.apply(classNames, extraClassNames);
|
||||
}
|
||||
|
||||
// Add composite classes for IE6 support
|
||||
if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('7')) {
|
||||
classNames.push.apply(classNames,
|
||||
this.getAppliedCombinedClassNames_(classNames));
|
||||
}
|
||||
|
||||
return classNames;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of all the combined class names that should be applied based
|
||||
* on the given list of classes. Checks the result of
|
||||
* {@link getIe6ClassCombinations} for any combinations that have all
|
||||
* members contained in classes. If a combination matches, the members are
|
||||
* joined with an underscore (in order), and added to the return array.
|
||||
*
|
||||
* If opt_includedClass is provided, return only the combined classes that have
|
||||
* all members contained in classes AND include opt_includedClass as well.
|
||||
* opt_includedClass is added to classes as well.
|
||||
* @param {Array.<string>} classes Array of classes to return matching combined
|
||||
* classes for.
|
||||
* @param {?string=} opt_includedClass If provided, get only the combined
|
||||
* classes that include this one.
|
||||
* @return {Array.<string>} Array of combined class names that should be
|
||||
* applied.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ControlRenderer.prototype.getAppliedCombinedClassNames_ = function(
|
||||
classes, opt_includedClass) {
|
||||
var toAdd = [];
|
||||
if (opt_includedClass) {
|
||||
classes = classes.concat([opt_includedClass]);
|
||||
}
|
||||
goog.array.forEach(this.getIe6ClassCombinations(), function(combo) {
|
||||
if (goog.array.every(combo, goog.partial(goog.array.contains, classes)) &&
|
||||
(!opt_includedClass || goog.array.contains(combo, opt_includedClass))) {
|
||||
toAdd.push(combo.join('_'));
|
||||
}
|
||||
});
|
||||
return toAdd;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Takes a bit mask of {@link goog.ui.Component.State}s, and returns an array
|
||||
* of the appropriate class names representing the given state, suitable to be
|
||||
* applied to the root element of a component rendered using this renderer, or
|
||||
* null if no state-specific classes need to be applied. This default
|
||||
* implementation uses the renderer's {@link getClassForState} method to
|
||||
* generate each state-specific class.
|
||||
* @param {number} state Bit mask of component states.
|
||||
* @return {!Array.<string>} Array of CSS class names representing the given
|
||||
* state.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.ControlRenderer.prototype.getClassNamesForState = function(state) {
|
||||
var classNames = [];
|
||||
while (state) {
|
||||
// For each enabled state, push the corresponding CSS class name onto
|
||||
// the classNames array.
|
||||
var mask = state & -state; // Least significant bit
|
||||
classNames.push(this.getClassForState(
|
||||
/** @type {goog.ui.Component.State} */ (mask)));
|
||||
state &= ~mask;
|
||||
}
|
||||
return classNames;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Takes a single {@link goog.ui.Component.State}, and returns the
|
||||
* corresponding CSS class name (null if none).
|
||||
* @param {goog.ui.Component.State} state Component state.
|
||||
* @return {string|undefined} CSS class representing the given state (undefined
|
||||
* if none).
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.ControlRenderer.prototype.getClassForState = function(state) {
|
||||
if (!this.classByState_) {
|
||||
this.createClassByStateMap_();
|
||||
}
|
||||
return this.classByState_[state];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Takes a single CSS class name which may represent a component state, and
|
||||
* returns the corresponding component state (0x00 if none).
|
||||
* @param {string} className CSS class name, possibly representing a component
|
||||
* state.
|
||||
* @return {goog.ui.Component.State} state Component state corresponding
|
||||
* to the given CSS class (0x00 if none).
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.ControlRenderer.prototype.getStateFromClass = function(className) {
|
||||
if (!this.stateByClass_) {
|
||||
this.createStateByClassMap_();
|
||||
}
|
||||
var state = parseInt(this.stateByClass_[className], 10);
|
||||
return /** @type {goog.ui.Component.State} */ (isNaN(state) ? 0x00 : state);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates the lookup table of states to classes, used during state changes.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ControlRenderer.prototype.createClassByStateMap_ = function() {
|
||||
var baseClass = this.getStructuralCssClass();
|
||||
|
||||
/**
|
||||
* Map of component states to state-specific structural class names,
|
||||
* used when changing the DOM in response to a state change. Precomputed
|
||||
* and cached on first use to minimize object allocations and string
|
||||
* concatenation.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
this.classByState_ = goog.object.create(
|
||||
goog.ui.Component.State.DISABLED, goog.getCssName(baseClass, 'disabled'),
|
||||
goog.ui.Component.State.HOVER, goog.getCssName(baseClass, 'hover'),
|
||||
goog.ui.Component.State.ACTIVE, goog.getCssName(baseClass, 'active'),
|
||||
goog.ui.Component.State.SELECTED, goog.getCssName(baseClass, 'selected'),
|
||||
goog.ui.Component.State.CHECKED, goog.getCssName(baseClass, 'checked'),
|
||||
goog.ui.Component.State.FOCUSED, goog.getCssName(baseClass, 'focused'),
|
||||
goog.ui.Component.State.OPENED, goog.getCssName(baseClass, 'open'));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates the lookup table of classes to states, used during decoration.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.ControlRenderer.prototype.createStateByClassMap_ = function() {
|
||||
// We need the classByState_ map so we can transpose it.
|
||||
if (!this.classByState_) {
|
||||
this.createClassByStateMap_();
|
||||
}
|
||||
|
||||
/**
|
||||
* Map of state-specific structural class names to component states,
|
||||
* used during element decoration. Precomputed and cached on first use
|
||||
* to minimize object allocations and string concatenation.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
this.stateByClass_ = goog.object.transpose(this.classByState_);
|
||||
};
|
||||
183
nicer-api-docs/closure-library/closure/goog/ui/cookieeditor.js
Normal file
183
nicer-api-docs/closure-library/closure/goog/ui/cookieeditor.js
Normal file
@@ -0,0 +1,183 @@
|
||||
// Copyright 2010 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 Displays and edits the value of a cookie.
|
||||
* Intended only for debugging.
|
||||
*/
|
||||
goog.provide('goog.ui.CookieEditor');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.net.cookies');
|
||||
goog.require('goog.string');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.Component');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Displays and edits the value of a cookie.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
|
||||
* @constructor
|
||||
* @extends {goog.ui.Component}
|
||||
*/
|
||||
goog.ui.CookieEditor = function(opt_domHelper) {
|
||||
goog.base(this, opt_domHelper);
|
||||
};
|
||||
goog.inherits(goog.ui.CookieEditor, goog.ui.Component);
|
||||
|
||||
|
||||
/**
|
||||
* Cookie key.
|
||||
* @type {?string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CookieEditor.prototype.cookieKey_;
|
||||
|
||||
|
||||
/**
|
||||
* Text area.
|
||||
* @type {HTMLTextAreaElement}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CookieEditor.prototype.textAreaElem_;
|
||||
|
||||
|
||||
/**
|
||||
* Clear button.
|
||||
* @type {HTMLButtonElement}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CookieEditor.prototype.clearButtonElem_;
|
||||
|
||||
|
||||
/**
|
||||
* Invalid value warning text.
|
||||
* @type {HTMLSpanElement}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CookieEditor.prototype.valueWarningElem_;
|
||||
|
||||
|
||||
/**
|
||||
* Update button.
|
||||
* @type {HTMLButtonElement}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CookieEditor.prototype.updateButtonElem_;
|
||||
|
||||
|
||||
// TODO(user): add combobox for user to select different cookies
|
||||
/**
|
||||
* Sets the cookie which this component will edit.
|
||||
* @param {string} cookieKey Cookie key.
|
||||
*/
|
||||
goog.ui.CookieEditor.prototype.selectCookie = function(cookieKey) {
|
||||
goog.asserts.assert(goog.net.cookies.isValidName(cookieKey));
|
||||
this.cookieKey_ = cookieKey;
|
||||
if (this.textAreaElem_) {
|
||||
this.textAreaElem_.value = goog.net.cookies.get(cookieKey) || '';
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.CookieEditor.prototype.canDecorate = function() {
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.CookieEditor.prototype.createDom = function() {
|
||||
// Debug-only, so we don't need i18n.
|
||||
this.clearButtonElem_ = /** @type {HTMLButtonElement} */ (goog.dom.createDom(
|
||||
goog.dom.TagName.BUTTON, /* attributes */ null, 'Clear'));
|
||||
this.updateButtonElem_ = /** @type {HTMLButtonElement} */ (goog.dom.createDom(
|
||||
goog.dom.TagName.BUTTON, /* attributes */ null, 'Update'));
|
||||
var value = this.cookieKey_ && goog.net.cookies.get(this.cookieKey_);
|
||||
this.textAreaElem_ = /** @type {HTMLTextAreaElement} */ (goog.dom.createDom(
|
||||
goog.dom.TagName.TEXTAREA, /* attibutes */ null, value || ''));
|
||||
this.valueWarningElem_ = /** @type {HTMLSpanElement} */ (goog.dom.createDom(
|
||||
goog.dom.TagName.SPAN, /* attibutes */ {
|
||||
'style': 'display:none;color:red'
|
||||
}, 'Invalid cookie value.'));
|
||||
this.setElementInternal(goog.dom.createDom(goog.dom.TagName.DIV,
|
||||
/* attibutes */ null,
|
||||
this.valueWarningElem_,
|
||||
goog.dom.createDom(goog.dom.TagName.BR),
|
||||
this.textAreaElem_,
|
||||
goog.dom.createDom(goog.dom.TagName.BR),
|
||||
this.clearButtonElem_,
|
||||
this.updateButtonElem_));
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.CookieEditor.prototype.enterDocument = function() {
|
||||
goog.base(this, 'enterDocument');
|
||||
this.getHandler().listen(this.clearButtonElem_,
|
||||
goog.events.EventType.CLICK,
|
||||
this.handleClear_);
|
||||
this.getHandler().listen(this.updateButtonElem_,
|
||||
goog.events.EventType.CLICK,
|
||||
this.handleUpdate_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles user clicking clear button.
|
||||
* @param {!goog.events.Event} e The click event.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CookieEditor.prototype.handleClear_ = function(e) {
|
||||
if (this.cookieKey_) {
|
||||
goog.net.cookies.remove(this.cookieKey_);
|
||||
}
|
||||
this.textAreaElem_.value = '';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles user clicking update button.
|
||||
* @param {!goog.events.Event} e The click event.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CookieEditor.prototype.handleUpdate_ = function(e) {
|
||||
if (this.cookieKey_) {
|
||||
var value = this.textAreaElem_.value;
|
||||
if (value) {
|
||||
// Strip line breaks.
|
||||
value = goog.string.stripNewlines(value);
|
||||
}
|
||||
if (goog.net.cookies.isValidValue(value)) {
|
||||
goog.net.cookies.set(this.cookieKey_, value);
|
||||
goog.style.setElementShown(this.valueWarningElem_, false);
|
||||
} else {
|
||||
goog.style.setElementShown(this.valueWarningElem_, true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.CookieEditor.prototype.disposeInternal = function() {
|
||||
this.clearButtonElem_ = null;
|
||||
this.cookieKey_ = null;
|
||||
this.textAreaElem_ = null;
|
||||
this.updateButtonElem_ = null;
|
||||
this.valueWarningElem_ = null;
|
||||
};
|
||||
@@ -0,0 +1,152 @@
|
||||
// Copyright 2010 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 An alternative imageless button renderer that uses CSS3 rather
|
||||
* than voodoo to render custom buttons with rounded corners and dimensionality
|
||||
* (via a subtle flat shadow on the bottom half of the button) without the use
|
||||
* of images.
|
||||
*
|
||||
* Based on the Custom Buttons 3.1 visual specification, see
|
||||
* http://go/custombuttons
|
||||
*
|
||||
* Tested and verified to work in Gecko 1.9.2+ and WebKit 528+.
|
||||
*
|
||||
* @author eae@google.com (Emil A Eklund)
|
||||
* @author slightlyoff@google.com (Alex Russell)
|
||||
* @see ../demos/css3button.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.Css3ButtonRenderer');
|
||||
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.dom.classes');
|
||||
goog.require('goog.ui.Button');
|
||||
goog.require('goog.ui.ButtonRenderer');
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('goog.ui.INLINE_BLOCK_CLASSNAME');
|
||||
goog.require('goog.ui.registry');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Custom renderer for {@link goog.ui.Button}s. Css3 buttons can contain
|
||||
* almost arbitrary HTML content, will flow like inline elements, but can be
|
||||
* styled like block-level elements.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {goog.ui.ButtonRenderer}
|
||||
*/
|
||||
goog.ui.Css3ButtonRenderer = function() {
|
||||
goog.ui.ButtonRenderer.call(this);
|
||||
};
|
||||
goog.inherits(goog.ui.Css3ButtonRenderer, goog.ui.ButtonRenderer);
|
||||
|
||||
|
||||
/**
|
||||
* The singleton instance of this renderer class.
|
||||
* @type {goog.ui.Css3ButtonRenderer?}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.Css3ButtonRenderer.instance_ = null;
|
||||
goog.addSingletonGetter(goog.ui.Css3ButtonRenderer);
|
||||
|
||||
|
||||
/**
|
||||
* Default CSS class to be applied to the root element of components rendered
|
||||
* by this renderer.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.Css3ButtonRenderer.CSS_CLASS = goog.getCssName('goog-css3-button');
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.Css3ButtonRenderer.prototype.getContentElement = function(element) {
|
||||
return /** @type {Element} */ (element);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the button's contents wrapped in the following DOM structure:
|
||||
* <div class="goog-inline-block goog-css3-button">
|
||||
* Contents...
|
||||
* </div>
|
||||
* Overrides {@link goog.ui.ButtonRenderer#createDom}.
|
||||
* @param {goog.ui.Control} control goog.ui.Button to render.
|
||||
* @return {Element} Root element for the button.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.Css3ButtonRenderer.prototype.createDom = function(control) {
|
||||
var button = /** @type {goog.ui.Button} */ (control);
|
||||
var classNames = this.getClassNames(button);
|
||||
var attr = {
|
||||
'class': goog.ui.INLINE_BLOCK_CLASSNAME + ' ' + classNames.join(' '),
|
||||
'title': button.getTooltip() || ''
|
||||
};
|
||||
return button.getDomHelper().createDom('div', attr, button.getContent());
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if this renderer can decorate the element. Overrides
|
||||
* {@link goog.ui.ButtonRenderer#canDecorate} by returning true if the
|
||||
* element is a DIV, false otherwise.
|
||||
* @param {Element} element Element to decorate.
|
||||
* @return {boolean} Whether the renderer can decorate the element.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.Css3ButtonRenderer.prototype.canDecorate = function(element) {
|
||||
return element.tagName == goog.dom.TagName.DIV;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.Css3ButtonRenderer.prototype.decorate = function(button, element) {
|
||||
goog.dom.classes.add(element, goog.ui.INLINE_BLOCK_CLASSNAME,
|
||||
this.getCssClass());
|
||||
return goog.ui.Css3ButtonRenderer.superClass_.decorate.call(this, button,
|
||||
element);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the CSS class to be applied to the root element of components
|
||||
* rendered using this renderer.
|
||||
* @return {string} Renderer-specific CSS class.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.Css3ButtonRenderer.prototype.getCssClass = function() {
|
||||
return goog.ui.Css3ButtonRenderer.CSS_CLASS;
|
||||
};
|
||||
|
||||
|
||||
// Register a decorator factory function for goog.ui.Css3ButtonRenderer.
|
||||
goog.ui.registry.setDecoratorByClassName(
|
||||
goog.ui.Css3ButtonRenderer.CSS_CLASS,
|
||||
function() {
|
||||
return new goog.ui.Button(null,
|
||||
goog.ui.Css3ButtonRenderer.getInstance());
|
||||
});
|
||||
|
||||
|
||||
// Register a decorator factory function for toggle buttons using the
|
||||
// goog.ui.Css3ButtonRenderer.
|
||||
goog.ui.registry.setDecoratorByClassName(
|
||||
goog.getCssName('goog-css3-toggle-button'),
|
||||
function() {
|
||||
var button = new goog.ui.Button(null,
|
||||
goog.ui.Css3ButtonRenderer.getInstance());
|
||||
button.setSupportedState(goog.ui.Component.State.CHECKED, true);
|
||||
return button;
|
||||
});
|
||||
@@ -0,0 +1,147 @@
|
||||
// Copyright 2010 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 An alternative imageless button renderer that uses CSS3 rather
|
||||
* than voodoo to render custom buttons with rounded corners and dimensionality
|
||||
* (via a subtle flat shadow on the bottom half of the button) without the use
|
||||
* of images.
|
||||
*
|
||||
* Based on the Custom Buttons 3.1 visual specification, see
|
||||
* http://go/custombuttons
|
||||
*
|
||||
* Tested and verified to work in Gecko 1.9.2+ and WebKit 528+.
|
||||
*
|
||||
* @author eae@google.com (Emil A Eklund)
|
||||
* @author slightlyoff@google.com (Alex Russell)
|
||||
* @author dalewis@google.com (Darren Lewis)
|
||||
* @see ../demos/css3menubutton.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.Css3MenuButtonRenderer');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.ui.INLINE_BLOCK_CLASSNAME');
|
||||
goog.require('goog.ui.MenuButton');
|
||||
goog.require('goog.ui.MenuButtonRenderer');
|
||||
goog.require('goog.ui.registry');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Custom renderer for {@link goog.ui.MenuButton}s. Css3 buttons can contain
|
||||
* almost arbitrary HTML content, will flow like inline elements, but can be
|
||||
* styled like block-level elements.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {goog.ui.MenuButtonRenderer}
|
||||
*/
|
||||
goog.ui.Css3MenuButtonRenderer = function() {
|
||||
goog.ui.MenuButtonRenderer.call(this);
|
||||
};
|
||||
goog.inherits(goog.ui.Css3MenuButtonRenderer, goog.ui.MenuButtonRenderer);
|
||||
|
||||
|
||||
/**
|
||||
* The singleton instance of this renderer class.
|
||||
* @type {goog.ui.Css3MenuButtonRenderer?}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.Css3MenuButtonRenderer.instance_ = null;
|
||||
goog.addSingletonGetter(goog.ui.Css3MenuButtonRenderer);
|
||||
|
||||
|
||||
/**
|
||||
* Default CSS class to be applied to the root element of components rendered
|
||||
* by this renderer.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.Css3MenuButtonRenderer.CSS_CLASS = goog.getCssName('goog-css3-button');
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.Css3MenuButtonRenderer.prototype.getContentElement = function(element) {
|
||||
if (element) {
|
||||
var captionElem = goog.dom.getElementsByTagNameAndClass(
|
||||
'*', goog.getCssName(this.getCssClass(), 'caption'), element)[0];
|
||||
return captionElem;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if this renderer can decorate the element. Overrides
|
||||
* {@link goog.ui.MenuButtonRenderer#canDecorate} by returning true if the
|
||||
* element is a DIV, false otherwise.
|
||||
* @param {Element} element Element to decorate.
|
||||
* @return {boolean} Whether the renderer can decorate the element.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.Css3MenuButtonRenderer.prototype.canDecorate = function(element) {
|
||||
return element.tagName == goog.dom.TagName.DIV;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Takes a text caption or existing DOM structure, and returns the content
|
||||
* wrapped in a pseudo-rounded-corner box. Creates the following DOM structure:
|
||||
* <div class="goog-inline-block goog-css3-button goog-css3-menu-button">
|
||||
* <div class="goog-css3-button-caption">Contents...</div>
|
||||
* <div class="goog-css3-button-dropdown"></div>
|
||||
* </div>
|
||||
*
|
||||
* Used by both {@link #createDom} and {@link #decorate}. To be overridden
|
||||
* by subclasses.
|
||||
* @param {goog.ui.ControlContent} content Text caption or DOM structure to wrap
|
||||
* in a box.
|
||||
* @param {goog.dom.DomHelper} dom DOM helper, used for document interaction.
|
||||
* @return {Element} Pseudo-rounded-corner box containing the content.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.Css3MenuButtonRenderer.prototype.createButton = function(content, dom) {
|
||||
var baseClass = this.getCssClass();
|
||||
var inlineBlock = goog.ui.INLINE_BLOCK_CLASSNAME + ' ';
|
||||
return dom.createDom('div', inlineBlock,
|
||||
dom.createDom('div', [goog.getCssName(baseClass, 'caption'),
|
||||
goog.getCssName('goog-inline-block')],
|
||||
content),
|
||||
dom.createDom('div', [goog.getCssName(baseClass, 'dropdown'),
|
||||
goog.getCssName('goog-inline-block')]));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the CSS class to be applied to the root element of components
|
||||
* rendered using this renderer.
|
||||
* @return {string} Renderer-specific CSS class.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.Css3MenuButtonRenderer.prototype.getCssClass = function() {
|
||||
return goog.ui.Css3MenuButtonRenderer.CSS_CLASS;
|
||||
};
|
||||
|
||||
|
||||
// Register a decorator factory function for goog.ui.Css3MenuButtonRenderer.
|
||||
// Since we're using goog-css3-button as the base class in order to get the
|
||||
// same styling as goog.ui.Css3ButtonRenderer, we need to be explicit about
|
||||
// giving goog-css3-menu-button here.
|
||||
goog.ui.registry.setDecoratorByClassName(
|
||||
goog.getCssName('goog-css3-menu-button'),
|
||||
function() {
|
||||
return new goog.ui.MenuButton(null, null,
|
||||
goog.ui.Css3MenuButtonRenderer.getInstance());
|
||||
});
|
||||
|
||||
29
nicer-api-docs/closure-library/closure/goog/ui/cssnames.js
Normal file
29
nicer-api-docs/closure-library/closure/goog/ui/cssnames.js
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2009 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 Common CSS class name constants.
|
||||
*
|
||||
* @author mkretzschmar@google.com (Martin Kretzschmar)
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.INLINE_BLOCK_CLASSNAME');
|
||||
|
||||
|
||||
/**
|
||||
* CSS class name for applying the "display: inline-block" property in a
|
||||
* cross-browser way.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.INLINE_BLOCK_CLASSNAME = goog.getCssName('goog-inline-block');
|
||||
@@ -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 A button rendered via {@link goog.ui.CustomButtonRenderer}.
|
||||
*
|
||||
* @author attila@google.com (Attila Bodis)
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.CustomButton');
|
||||
|
||||
goog.require('goog.ui.Button');
|
||||
goog.require('goog.ui.CustomButtonRenderer');
|
||||
goog.require('goog.ui.registry');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A custom button control. Identical to {@link goog.ui.Button}, except it
|
||||
* defaults its renderer to {@link goog.ui.CustomButtonRenderer}. One could
|
||||
* just as easily pass {@code goog.ui.CustomButtonRenderer.getInstance()} to
|
||||
* the {@link goog.ui.Button} constructor and get the same result. Provided
|
||||
* for convenience.
|
||||
*
|
||||
* @param {goog.ui.ControlContent} content Text caption or existing DOM
|
||||
* structure to display as the button's caption.
|
||||
* @param {goog.ui.ButtonRenderer=} opt_renderer Optional renderer used to
|
||||
* render or decorate the button; defaults to
|
||||
* {@link goog.ui.CustomButtonRenderer}.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM hepler, used for
|
||||
* document interaction.
|
||||
* @constructor
|
||||
* @extends {goog.ui.Button}
|
||||
*/
|
||||
goog.ui.CustomButton = function(content, opt_renderer, opt_domHelper) {
|
||||
goog.ui.Button.call(this, content, opt_renderer ||
|
||||
goog.ui.CustomButtonRenderer.getInstance(), opt_domHelper);
|
||||
};
|
||||
goog.inherits(goog.ui.CustomButton, goog.ui.Button);
|
||||
|
||||
|
||||
// Register a decorator factory function for goog.ui.CustomButtons.
|
||||
goog.ui.registry.setDecoratorByClassName(goog.ui.CustomButtonRenderer.CSS_CLASS,
|
||||
function() {
|
||||
// CustomButton defaults to using CustomButtonRenderer.
|
||||
return new goog.ui.CustomButton(null);
|
||||
});
|
||||
@@ -0,0 +1,267 @@
|
||||
// Copyright 2008 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 A custom button renderer that uses CSS voodoo to render a
|
||||
* button-like object with fake rounded corners.
|
||||
*
|
||||
* @author attila@google.com (Attila Bodis)
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.CustomButtonRenderer');
|
||||
|
||||
goog.require('goog.a11y.aria.Role');
|
||||
goog.require('goog.dom.NodeType');
|
||||
goog.require('goog.dom.classes');
|
||||
goog.require('goog.string');
|
||||
goog.require('goog.ui.ButtonRenderer');
|
||||
goog.require('goog.ui.INLINE_BLOCK_CLASSNAME');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Custom renderer for {@link goog.ui.Button}s. Custom buttons can contain
|
||||
* almost arbitrary HTML content, will flow like inline elements, but can be
|
||||
* styled like block-level elements.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {goog.ui.ButtonRenderer}
|
||||
*/
|
||||
goog.ui.CustomButtonRenderer = function() {
|
||||
goog.ui.ButtonRenderer.call(this);
|
||||
};
|
||||
goog.inherits(goog.ui.CustomButtonRenderer, goog.ui.ButtonRenderer);
|
||||
goog.addSingletonGetter(goog.ui.CustomButtonRenderer);
|
||||
|
||||
|
||||
/**
|
||||
* Default CSS class to be applied to the root element of components rendered
|
||||
* by this renderer.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.CustomButtonRenderer.CSS_CLASS = goog.getCssName('goog-custom-button');
|
||||
|
||||
|
||||
/**
|
||||
* Returns the button's contents wrapped in the following DOM structure:
|
||||
* <div class="goog-inline-block goog-custom-button">
|
||||
* <div class="goog-inline-block goog-custom-button-outer-box">
|
||||
* <div class="goog-inline-block goog-custom-button-inner-box">
|
||||
* Contents...
|
||||
* </div>
|
||||
* </div>
|
||||
* </div>
|
||||
* Overrides {@link goog.ui.ButtonRenderer#createDom}.
|
||||
* @param {goog.ui.Control} control goog.ui.Button to render.
|
||||
* @return {Element} Root element for the button.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.CustomButtonRenderer.prototype.createDom = function(control) {
|
||||
var button = /** @type {goog.ui.Button} */ (control);
|
||||
var classNames = this.getClassNames(button);
|
||||
var attributes = {
|
||||
'class': goog.ui.INLINE_BLOCK_CLASSNAME + ' ' + classNames.join(' ')
|
||||
};
|
||||
var buttonElement = button.getDomHelper().createDom('div', attributes,
|
||||
this.createButton(button.getContent(), button.getDomHelper()));
|
||||
this.setTooltip(
|
||||
buttonElement, /** @type {!string}*/ (button.getTooltip()));
|
||||
this.setAriaStates(button, buttonElement);
|
||||
|
||||
return buttonElement;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the ARIA role to be applied to custom buttons.
|
||||
* @return {goog.a11y.aria.Role|undefined} ARIA role.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.CustomButtonRenderer.prototype.getAriaRole = function() {
|
||||
return goog.a11y.aria.Role.BUTTON;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Takes the button's root element and returns the parent element of the
|
||||
* button's contents. Overrides the superclass implementation by taking
|
||||
* the nested DIV structure of custom buttons into account.
|
||||
* @param {Element} element Root element of the button whose content
|
||||
* element is to be returned.
|
||||
* @return {Element} The button's content element (if any).
|
||||
* @override
|
||||
*/
|
||||
goog.ui.CustomButtonRenderer.prototype.getContentElement = function(element) {
|
||||
return element && /** @type {Element} */ (element.firstChild.firstChild);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Takes a text caption or existing DOM structure, and returns the content
|
||||
* wrapped in a pseudo-rounded-corner box. Creates the following DOM structure:
|
||||
* <div class="goog-inline-block goog-custom-button-outer-box">
|
||||
* <div class="goog-inline-block goog-custom-button-inner-box">
|
||||
* Contents...
|
||||
* </div>
|
||||
* </div>
|
||||
* Used by both {@link #createDom} and {@link #decorate}. To be overridden
|
||||
* by subclasses.
|
||||
* @param {goog.ui.ControlContent} content Text caption or DOM structure to wrap
|
||||
* in a box.
|
||||
* @param {goog.dom.DomHelper} dom DOM helper, used for document interaction.
|
||||
* @return {Element} Pseudo-rounded-corner box containing the content.
|
||||
*/
|
||||
goog.ui.CustomButtonRenderer.prototype.createButton = function(content, dom) {
|
||||
return dom.createDom('div',
|
||||
goog.ui.INLINE_BLOCK_CLASSNAME + ' ' +
|
||||
goog.getCssName(this.getCssClass(), 'outer-box'),
|
||||
dom.createDom('div',
|
||||
goog.ui.INLINE_BLOCK_CLASSNAME + ' ' +
|
||||
goog.getCssName(this.getCssClass(), 'inner-box'), content));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if this renderer can decorate the element. Overrides
|
||||
* {@link goog.ui.ButtonRenderer#canDecorate} by returning true if the
|
||||
* element is a DIV, false otherwise.
|
||||
* @param {Element} element Element to decorate.
|
||||
* @return {boolean} Whether the renderer can decorate the element.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.CustomButtonRenderer.prototype.canDecorate = function(element) {
|
||||
return element.tagName == 'DIV';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Check if the button's element has a box structure.
|
||||
* @param {goog.ui.Button} button Button instance whose structure is being
|
||||
* checked.
|
||||
* @param {Element} element Element of the button.
|
||||
* @return {boolean} Whether the element has a box structure.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.CustomButtonRenderer.prototype.hasBoxStructure = function(
|
||||
button, element) {
|
||||
var outer = button.getDomHelper().getFirstElementChild(element);
|
||||
var outerClassName = goog.getCssName(this.getCssClass(), 'outer-box');
|
||||
if (outer && goog.dom.classes.has(outer, outerClassName)) {
|
||||
|
||||
var inner = button.getDomHelper().getFirstElementChild(outer);
|
||||
var innerClassName = goog.getCssName(this.getCssClass(), 'inner-box');
|
||||
if (inner && goog.dom.classes.has(inner, innerClassName)) {
|
||||
// We have a proper box structure.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Takes an existing element and decorates it with the custom button control.
|
||||
* Initializes the control's ID, content, tooltip, value, and state based
|
||||
* on the ID of the element, its child nodes, and its CSS classes, respectively.
|
||||
* Returns the element. Overrides {@link goog.ui.ButtonRenderer#decorate}.
|
||||
* @param {goog.ui.Control} control Button instance to decorate the element.
|
||||
* @param {Element} element Element to decorate.
|
||||
* @return {Element} Decorated element.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.CustomButtonRenderer.prototype.decorate = function(control, element) {
|
||||
var button = /** @type {goog.ui.Button} */ (control);
|
||||
// Trim text nodes in the element's child node list; otherwise madness
|
||||
// ensues (i.e. on Gecko, buttons will flicker and shift when moused over).
|
||||
goog.ui.CustomButtonRenderer.trimTextNodes_(element, true);
|
||||
goog.ui.CustomButtonRenderer.trimTextNodes_(element, false);
|
||||
|
||||
// Create the buttom dom if it has not been created.
|
||||
if (!this.hasBoxStructure(button, element)) {
|
||||
element.appendChild(
|
||||
this.createButton(element.childNodes, button.getDomHelper()));
|
||||
}
|
||||
|
||||
goog.dom.classes.add(element,
|
||||
goog.ui.INLINE_BLOCK_CLASSNAME, this.getCssClass());
|
||||
return goog.ui.CustomButtonRenderer.superClass_.decorate.call(this, button,
|
||||
element);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the CSS class to be applied to the root element of components
|
||||
* rendered using this renderer.
|
||||
* @return {string} Renderer-specific CSS class.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.CustomButtonRenderer.prototype.getCssClass = function() {
|
||||
return goog.ui.CustomButtonRenderer.CSS_CLASS;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Takes an element and removes leading or trailing whitespace from the start
|
||||
* or the end of its list of child nodes. The Boolean argument determines
|
||||
* whether to trim from the start or the end of the node list. Empty text
|
||||
* nodes are removed, and the first non-empty text node is trimmed from the
|
||||
* left or the right as appropriate. For example,
|
||||
* <div class="goog-inline-block">
|
||||
* #text ""
|
||||
* #text "\n Hello "
|
||||
* <span>...</span>
|
||||
* #text " World! \n"
|
||||
* #text ""
|
||||
* </div>
|
||||
* becomes
|
||||
* <div class="goog-inline-block">
|
||||
* #text "Hello "
|
||||
* <span>...</span>
|
||||
* #text " World!"
|
||||
* </div>
|
||||
* This is essential for Gecko, where leading/trailing whitespace messes with
|
||||
* the layout of elements with -moz-inline-box (used in goog-inline-block), and
|
||||
* optional but harmless for non-Gecko.
|
||||
*
|
||||
* @param {Element} element Element whose child node list is to be trimmed.
|
||||
* @param {boolean} fromStart Whether to trim from the start or from the end.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.CustomButtonRenderer.trimTextNodes_ = function(element, fromStart) {
|
||||
if (element) {
|
||||
var node = fromStart ? element.firstChild : element.lastChild, next;
|
||||
// Tag soup HTML may result in a DOM where siblings have different parents.
|
||||
while (node && node.parentNode == element) {
|
||||
// Get the next/previous sibling here, since the node may be removed.
|
||||
next = fromStart ? node.nextSibling : node.previousSibling;
|
||||
if (node.nodeType == goog.dom.NodeType.TEXT) {
|
||||
// Found a text node.
|
||||
var text = node.nodeValue;
|
||||
if (goog.string.trim(text) == '') {
|
||||
// Found an empty text node; remove it.
|
||||
element.removeChild(node);
|
||||
} else {
|
||||
// Found a non-empty text node; trim from the start/end, then exit.
|
||||
node.nodeValue = fromStart ?
|
||||
goog.string.trimLeft(text) : goog.string.trimRight(text);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Found a non-text node; done.
|
||||
break;
|
||||
}
|
||||
node = next;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,139 @@
|
||||
// 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 A color palette with a button for adding additional colors
|
||||
* manually.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.CustomColorPalette');
|
||||
|
||||
goog.require('goog.color');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.classes');
|
||||
goog.require('goog.ui.ColorPalette');
|
||||
goog.require('goog.ui.Component');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A custom color palette is a grid of color swatches and a button that allows
|
||||
* the user to add additional colors to the palette
|
||||
*
|
||||
* @param {Array.<string>} initColors Array of initial colors to populate the
|
||||
* palette with.
|
||||
* @param {goog.ui.PaletteRenderer=} opt_renderer Renderer used to render or
|
||||
* decorate the palette; defaults to {@link goog.ui.PaletteRenderer}.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
|
||||
* document interaction.
|
||||
* @constructor
|
||||
* @extends {goog.ui.ColorPalette}
|
||||
*/
|
||||
goog.ui.CustomColorPalette = function(initColors, opt_renderer, opt_domHelper) {
|
||||
goog.ui.ColorPalette.call(this, initColors, opt_renderer, opt_domHelper);
|
||||
this.setSupportedState(goog.ui.Component.State.OPENED, true);
|
||||
};
|
||||
goog.inherits(goog.ui.CustomColorPalette, goog.ui.ColorPalette);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of DOM nodes for each color, and an additional cell with a
|
||||
* '+'.
|
||||
* @return {Array.<Node>} Array of div elements.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.CustomColorPalette.prototype.createColorNodes = function() {
|
||||
/** @desc Hover caption for the button that allows the user to add a color. */
|
||||
var MSG_CLOSURE_CUSTOM_COLOR_BUTTON = goog.getMsg('Add a color');
|
||||
|
||||
var nl = goog.base(this, 'createColorNodes');
|
||||
nl.push(goog.dom.createDom('div', {
|
||||
'class': goog.getCssName('goog-palette-customcolor'),
|
||||
'title': MSG_CLOSURE_CUSTOM_COLOR_BUTTON
|
||||
}, '+'));
|
||||
return nl;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @param {goog.events.Event} e Mouse or key event that triggered the action.
|
||||
* @return {boolean} True if the action was allowed to proceed, false otherwise.
|
||||
*/
|
||||
goog.ui.CustomColorPalette.prototype.performActionInternal = function(e) {
|
||||
var item = /** @type {Element} */ (this.getHighlightedItem());
|
||||
if (item) {
|
||||
if (goog.dom.classes.has(
|
||||
item, goog.getCssName('goog-palette-customcolor'))) {
|
||||
// User activated the special "add custom color" swatch.
|
||||
this.promptForCustomColor();
|
||||
} else {
|
||||
// User activated a normal color swatch.
|
||||
this.setSelectedItem(item);
|
||||
return this.dispatchEvent(goog.ui.Component.EventType.ACTION);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Prompts the user to enter a custom color. Currently uses a window.prompt
|
||||
* but could be updated to use a dialog box with a WheelColorPalette.
|
||||
*/
|
||||
goog.ui.CustomColorPalette.prototype.promptForCustomColor = function() {
|
||||
/** @desc Default custom color dialog. */
|
||||
var MSG_CLOSURE_CUSTOM_COLOR_PROMPT = goog.getMsg(
|
||||
'Input custom color, i.e. pink, #F00, #D015FF or rgb(100, 50, 25)');
|
||||
|
||||
// A CustomColorPalette is considered "open" while the color selection prompt
|
||||
// is open. Enabling state transition events for the OPENED state and
|
||||
// listening for OPEN events allows clients to save the selection before
|
||||
// it is destroyed (see e.g. bug 1064701).
|
||||
var response = null;
|
||||
this.setOpen(true);
|
||||
if (this.isOpen()) {
|
||||
// The OPEN event wasn't canceled; prompt for custom color.
|
||||
response = window.prompt(MSG_CLOSURE_CUSTOM_COLOR_PROMPT, '#FFFFFF');
|
||||
this.setOpen(false);
|
||||
}
|
||||
|
||||
if (!response) {
|
||||
// The user hit cancel
|
||||
return;
|
||||
}
|
||||
|
||||
var color;
|
||||
/** @preserveTry */
|
||||
try {
|
||||
color = goog.color.parse(response).hex;
|
||||
} catch (er) {
|
||||
/** @desc Alert message sent when the input string is not a valid color. */
|
||||
var MSG_CLOSURE_CUSTOM_COLOR_INVALID_INPUT = goog.getMsg(
|
||||
'ERROR: "{$color}" is not a valid color.', {'color': response});
|
||||
alert(MSG_CLOSURE_CUSTOM_COLOR_INVALID_INPUT);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(user): This is relatively inefficient. Consider adding
|
||||
// functionality to palette to add individual items after render time.
|
||||
var colors = this.getColors();
|
||||
colors.push(color);
|
||||
this.setColors(colors);
|
||||
|
||||
// Set the selected color to the new color and notify listeners of the action.
|
||||
this.setSelectedColor(color);
|
||||
this.dispatchEvent(goog.ui.Component.EventType.ACTION);
|
||||
};
|
||||
1369
nicer-api-docs/closure-library/closure/goog/ui/datepicker.js
Normal file
1369
nicer-api-docs/closure-library/closure/goog/ui/datepicker.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,55 @@
|
||||
// Copyright 2013 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 The renderer interface for {@link goog.ui.DatePicker}.
|
||||
*
|
||||
* @see ../demos/datepicker.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.DatePickerRenderer');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The renderer for {@link goog.ui.DatePicker}. Renders the date picker's
|
||||
* navigation header and footer.
|
||||
* @interface
|
||||
*/
|
||||
goog.ui.DatePickerRenderer = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* Render the navigation row.
|
||||
*
|
||||
* @param {!Element} row The parent element to render the component into.
|
||||
* @param {boolean} simpleNavigation Whether the picker should render a simple
|
||||
* navigation menu that only contains controls for navigating to the next
|
||||
* and previous month. The default navigation menu contains controls for
|
||||
* navigating to the next/previous month, next/previous year, and menus for
|
||||
* jumping to specific months and years.
|
||||
* @param {boolean} showWeekNum Whether week numbers should be shown.
|
||||
* @param {string} fullDateFormat The full date format.
|
||||
* {@see goog.i18n.DateTimeSymbols}.
|
||||
*/
|
||||
goog.ui.DatePickerRenderer.prototype.renderNavigationRow = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Render the footer row.
|
||||
*
|
||||
* @param {!Element} row The parent element to render the component into.
|
||||
* @param {boolean} showWeekNum Whether week numbers should be shown.
|
||||
*/
|
||||
goog.ui.DatePickerRenderer.prototype.renderFooterRow = goog.abstractMethod;
|
||||
38
nicer-api-docs/closure-library/closure/goog/ui/decorate.js
Normal file
38
nicer-api-docs/closure-library/closure/goog/ui/decorate.js
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2008 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 Provides a function that decorates an element based on its CSS
|
||||
* class name.
|
||||
* @author attila@google.com (Attila Bodis)
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.decorate');
|
||||
|
||||
goog.require('goog.ui.registry');
|
||||
|
||||
|
||||
/**
|
||||
* Decorates the element with a suitable {@link goog.ui.Component} instance, if
|
||||
* a matching decorator is found.
|
||||
* @param {Element} element Element to decorate.
|
||||
* @return {goog.ui.Component?} New component instance, decorating the element.
|
||||
*/
|
||||
goog.ui.decorate = function(element) {
|
||||
var decorator = goog.ui.registry.getDecorator(element);
|
||||
if (decorator) {
|
||||
decorator.decorate(element);
|
||||
}
|
||||
return decorator;
|
||||
};
|
||||
@@ -0,0 +1,202 @@
|
||||
// Copyright 2013 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 The default renderer for {@link goog.ui.DatePicker}.
|
||||
*
|
||||
* @see ../demos/datepicker.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.DefaultDatePickerRenderer');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.TagName');
|
||||
/** @suppress {extraRequire} Interface. */
|
||||
goog.require('goog.ui.DatePickerRenderer');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Default renderer for {@link goog.ui.DatePicker}. Renders the date picker's
|
||||
* navigation header and footer.
|
||||
*
|
||||
* @param {string} baseCssClass Name of base CSS class of the date picker.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper DOM helper.
|
||||
* @constructor
|
||||
* @implements {goog.ui.DatePickerRenderer}
|
||||
*/
|
||||
goog.ui.DefaultDatePickerRenderer = function(baseCssClass, opt_domHelper) {
|
||||
/**
|
||||
* Name of base CSS class of datepicker
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.baseCssClass_ = baseCssClass;
|
||||
|
||||
/**
|
||||
* @type {!goog.dom.DomHelper}
|
||||
* @private
|
||||
*/
|
||||
this.dom_ = opt_domHelper || goog.dom.getDomHelper();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the dom helper that is being used on this component.
|
||||
* @return {!goog.dom.DomHelper} The dom helper used on this component.
|
||||
*/
|
||||
goog.ui.DefaultDatePickerRenderer.prototype.getDomHelper = function() {
|
||||
return this.dom_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns base CSS class. This getter is used to get base CSS class part.
|
||||
* All CSS class names in component are created as:
|
||||
* goog.getCssName(this.getBaseCssClass(), 'CLASS_NAME')
|
||||
* @return {string} Base CSS class.
|
||||
*/
|
||||
goog.ui.DefaultDatePickerRenderer.prototype.getBaseCssClass = function() {
|
||||
return this.baseCssClass_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Render the navigation row (navigating months and maybe years).
|
||||
*
|
||||
* @param {!Element} row The parent element to render the component into.
|
||||
* @param {boolean} simpleNavigation Whether the picker should render a simple
|
||||
* navigation menu that only contains controls for navigating to the next
|
||||
* and previous month. The default navigation menu contains controls for
|
||||
* navigating to the next/previous month, next/previous year, and menus for
|
||||
* jumping to specific months and years.
|
||||
* @param {boolean} showWeekNum Whether week numbers should be shown.
|
||||
* @param {string} fullDateFormat The full date format.
|
||||
* {@see goog.i18n.DateTimeSymbols}.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.DefaultDatePickerRenderer.prototype.renderNavigationRow =
|
||||
function(row, simpleNavigation, showWeekNum, fullDateFormat) {
|
||||
// Populate the navigation row according to the configured navigation mode.
|
||||
var cell, monthCell, yearCell;
|
||||
|
||||
if (simpleNavigation) {
|
||||
cell = this.getDomHelper().createElement(goog.dom.TagName.TD);
|
||||
cell.colSpan = showWeekNum ? 1 : 2;
|
||||
this.createButton_(cell, '\u00AB',
|
||||
goog.getCssName(this.getBaseCssClass(), 'previousMonth')); // <<
|
||||
row.appendChild(cell);
|
||||
|
||||
cell = this.getDomHelper().createElement(goog.dom.TagName.TD);
|
||||
cell.colSpan = showWeekNum ? 6 : 5;
|
||||
cell.className = goog.getCssName(this.getBaseCssClass(), 'monthyear');
|
||||
row.appendChild(cell);
|
||||
|
||||
cell = this.getDomHelper().createElement(goog.dom.TagName.TD);
|
||||
this.createButton_(cell, '\u00BB',
|
||||
goog.getCssName(this.getBaseCssClass(), 'nextMonth')); // >>
|
||||
row.appendChild(cell);
|
||||
|
||||
} else {
|
||||
monthCell = this.getDomHelper().createElement(goog.dom.TagName.TD);
|
||||
monthCell.colSpan = 5;
|
||||
this.createButton_(monthCell, '\u00AB',
|
||||
goog.getCssName(this.getBaseCssClass(), 'previousMonth')); // <<
|
||||
this.createButton_(monthCell, '',
|
||||
goog.getCssName(this.getBaseCssClass(), 'month'));
|
||||
this.createButton_(monthCell, '\u00BB',
|
||||
goog.getCssName(this.getBaseCssClass(), 'nextMonth')); // >>
|
||||
|
||||
yearCell = this.getDomHelper().createElement(goog.dom.TagName.TD);
|
||||
yearCell.colSpan = 3;
|
||||
this.createButton_(yearCell, '\u00AB',
|
||||
goog.getCssName(this.getBaseCssClass(), 'previousYear')); // <<
|
||||
this.createButton_(yearCell, '',
|
||||
goog.getCssName(this.getBaseCssClass(), 'year'));
|
||||
this.createButton_(yearCell, '\u00BB',
|
||||
goog.getCssName(this.getBaseCssClass(), 'nextYear')); // <<
|
||||
|
||||
// If the date format has year ('y') appearing first before month ('m'),
|
||||
// show the year on the left hand side of the datepicker popup. Otherwise,
|
||||
// show the month on the left side. This check assumes the data to be
|
||||
// valid, and that all date formats contain month and year.
|
||||
if (fullDateFormat.indexOf('y') < fullDateFormat.indexOf('m')) {
|
||||
row.appendChild(yearCell);
|
||||
row.appendChild(monthCell);
|
||||
} else {
|
||||
row.appendChild(monthCell);
|
||||
row.appendChild(yearCell);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Render the footer row (with select buttons).
|
||||
*
|
||||
* @param {!Element} row The parent element to render the component into.
|
||||
* @param {boolean} showWeekNum Whether week numbers should be shown.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.DefaultDatePickerRenderer.prototype.renderFooterRow =
|
||||
function(row, showWeekNum) {
|
||||
// Populate the footer row with buttons for Today and None.
|
||||
var cell = this.getDomHelper().createElement(goog.dom.TagName.TD);
|
||||
cell.colSpan = showWeekNum ? 2 : 3;
|
||||
cell.className = goog.getCssName(this.getBaseCssClass(), 'today-cont');
|
||||
|
||||
/** @desc Label for button that selects the current date. */
|
||||
var MSG_DATEPICKER_TODAY_BUTTON_LABEL = goog.getMsg('Today');
|
||||
this.createButton_(cell, MSG_DATEPICKER_TODAY_BUTTON_LABEL,
|
||||
goog.getCssName(this.getBaseCssClass(), 'today-btn'));
|
||||
row.appendChild(cell);
|
||||
|
||||
cell = this.getDomHelper().createElement(goog.dom.TagName.TD);
|
||||
cell.colSpan = showWeekNum ? 4 : 3;
|
||||
row.appendChild(cell);
|
||||
|
||||
cell = this.getDomHelper().createElement(goog.dom.TagName.TD);
|
||||
cell.colSpan = 2;
|
||||
cell.className = goog.getCssName(this.getBaseCssClass(), 'none-cont');
|
||||
|
||||
/** @desc Label for button that clears the selection. */
|
||||
var MSG_DATEPICKER_NONE = goog.getMsg('None');
|
||||
this.createButton_(cell, MSG_DATEPICKER_NONE,
|
||||
goog.getCssName(this.getBaseCssClass(), 'none-btn'));
|
||||
row.appendChild(cell);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Support function for button creation.
|
||||
*
|
||||
* @param {Element} parentNode Container the button should be added to.
|
||||
* @param {string} label Button label.
|
||||
* @param {string=} opt_className Class name for button, which will be used
|
||||
* in addition to "goog-date-picker-btn".
|
||||
* @private
|
||||
* @return {Element} The created button element.
|
||||
*/
|
||||
goog.ui.DefaultDatePickerRenderer.prototype.createButton_ =
|
||||
function(parentNode, label, opt_className) {
|
||||
var classes = [goog.getCssName(this.getBaseCssClass(), 'btn')];
|
||||
if (opt_className) {
|
||||
classes.push(opt_className);
|
||||
}
|
||||
var el = this.getDomHelper().createElement(goog.dom.TagName.BUTTON);
|
||||
el.className = classes.join(' ');
|
||||
el.appendChild(this.getDomHelper().createTextNode(label));
|
||||
parentNode.appendChild(el);
|
||||
return el;
|
||||
};
|
||||
1556
nicer-api-docs/closure-library/closure/goog/ui/dialog.js
Normal file
1556
nicer-api-docs/closure-library/closure/goog/ui/dialog.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,305 @@
|
||||
// Copyright 2008 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 A dimension picker control. A dimension picker allows the
|
||||
* user to visually select a row and column count.
|
||||
*
|
||||
* @author robbyw@google.com (Robby Walker)
|
||||
* @author abefettig@google.com (Abe Fettig)
|
||||
* @see ../demos/dimensionpicker.html
|
||||
* @see ../demos/dimensionpicker_rtl.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.DimensionPicker');
|
||||
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.events.KeyCodes');
|
||||
goog.require('goog.math.Size');
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('goog.ui.Control');
|
||||
goog.require('goog.ui.DimensionPickerRenderer');
|
||||
goog.require('goog.ui.registry');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A dimension picker allows the user to visually select a row and column
|
||||
* count using their mouse and keyboard.
|
||||
*
|
||||
* The currently selected dimension is controlled by an ACTION event. Event
|
||||
* listeners may retrieve the selected item using the
|
||||
* {@link #getValue} method.
|
||||
*
|
||||
* @param {goog.ui.DimensionPickerRenderer=} opt_renderer Renderer used to
|
||||
* render or decorate the palette; defaults to
|
||||
* {@link goog.ui.DimensionPickerRenderer}.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
|
||||
* document interaction.
|
||||
* @constructor
|
||||
* @extends {goog.ui.Control}
|
||||
*/
|
||||
goog.ui.DimensionPicker = function(opt_renderer, opt_domHelper) {
|
||||
goog.ui.Control.call(this, null,
|
||||
opt_renderer || goog.ui.DimensionPickerRenderer.getInstance(),
|
||||
opt_domHelper);
|
||||
|
||||
this.size_ = new goog.math.Size(this.minColumns, this.minRows);
|
||||
};
|
||||
goog.inherits(goog.ui.DimensionPicker, goog.ui.Control);
|
||||
|
||||
|
||||
/**
|
||||
* Minimum number of columns to show in the grid.
|
||||
* @type {number}
|
||||
*/
|
||||
goog.ui.DimensionPicker.prototype.minColumns = 5;
|
||||
|
||||
|
||||
/**
|
||||
* Minimum number of rows to show in the grid.
|
||||
* @type {number}
|
||||
*/
|
||||
goog.ui.DimensionPicker.prototype.minRows = 5;
|
||||
|
||||
|
||||
/**
|
||||
* Maximum number of columns to show in the grid.
|
||||
* @type {number}
|
||||
*/
|
||||
goog.ui.DimensionPicker.prototype.maxColumns = 20;
|
||||
|
||||
|
||||
/**
|
||||
* Maximum number of rows to show in the grid.
|
||||
* @type {number}
|
||||
*/
|
||||
goog.ui.DimensionPicker.prototype.maxRows = 20;
|
||||
|
||||
|
||||
/**
|
||||
* Palette dimensions (columns x rows).
|
||||
* @type {goog.math.Size}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DimensionPicker.prototype.size_;
|
||||
|
||||
|
||||
/**
|
||||
* Currently highlighted row count.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DimensionPicker.prototype.highlightedRows_ = 1;
|
||||
|
||||
|
||||
/**
|
||||
* Currently highlighted column count.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DimensionPicker.prototype.highlightedColumns_ = 1;
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.DimensionPicker.prototype.enterDocument = function() {
|
||||
goog.ui.DimensionPicker.superClass_.enterDocument.call(this);
|
||||
|
||||
var handler = this.getHandler();
|
||||
handler.
|
||||
listen(this.getRenderer().getMouseMoveElement(this),
|
||||
goog.events.EventType.MOUSEMOVE, this.handleMouseMove).
|
||||
listen(this.getDomHelper().getWindow(), goog.events.EventType.RESIZE,
|
||||
this.handleWindowResize);
|
||||
|
||||
var parent = this.getParent();
|
||||
if (parent) {
|
||||
handler.listen(parent, goog.ui.Component.EventType.SHOW, this.handleShow_);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.DimensionPicker.prototype.exitDocument = function() {
|
||||
goog.ui.DimensionPicker.superClass_.exitDocument.call(this);
|
||||
|
||||
var handler = this.getHandler();
|
||||
handler.
|
||||
unlisten(this.getRenderer().getMouseMoveElement(this),
|
||||
goog.events.EventType.MOUSEMOVE, this.handleMouseMove).
|
||||
unlisten(this.getDomHelper().getWindow(), goog.events.EventType.RESIZE,
|
||||
this.handleWindowResize);
|
||||
|
||||
var parent = this.getParent();
|
||||
if (parent) {
|
||||
handler.unlisten(parent, goog.ui.Component.EventType.SHOW,
|
||||
this.handleShow_);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Resets the highlighted size when the picker is shown.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DimensionPicker.prototype.handleShow_ = function() {
|
||||
if (this.isVisible()) {
|
||||
this.setValue(1, 1);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.DimensionPicker.prototype.disposeInternal = function() {
|
||||
goog.ui.DimensionPicker.superClass_.disposeInternal.call(this);
|
||||
delete this.size_;
|
||||
};
|
||||
|
||||
|
||||
// Palette event handling.
|
||||
|
||||
|
||||
/**
|
||||
* Handles mousemove events. Determines which palette size was moused over and
|
||||
* highlights it.
|
||||
* @param {goog.events.BrowserEvent} e Mouse event to handle.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.DimensionPicker.prototype.handleMouseMove = function(e) {
|
||||
var highlightedSizeX = this.getRenderer().getGridOffsetX(this,
|
||||
this.isRightToLeft() ? e.target.offsetWidth - e.offsetX : e.offsetX);
|
||||
var highlightedSizeY = this.getRenderer().getGridOffsetY(this, e.offsetY);
|
||||
|
||||
this.setValue(highlightedSizeX, highlightedSizeY);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles window resize events. Ensures no scrollbars are introduced by the
|
||||
* renderer's mouse catcher.
|
||||
* @param {goog.events.Event} e Resize event to handle.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.DimensionPicker.prototype.handleWindowResize = function(e) {
|
||||
this.getRenderer().positionMouseCatcher(this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handle key events if supported, so the user can use the keyboard to
|
||||
* manipulate the highlighted rows and columns.
|
||||
* @param {goog.events.KeyEvent} e The key event object.
|
||||
* @return {boolean} Whether the key event was handled.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.DimensionPicker.prototype.handleKeyEvent = function(e) {
|
||||
var rows = this.highlightedRows_;
|
||||
var columns = this.highlightedColumns_;
|
||||
switch (e.keyCode) {
|
||||
case goog.events.KeyCodes.DOWN:
|
||||
rows++;
|
||||
break;
|
||||
case goog.events.KeyCodes.UP:
|
||||
rows--;
|
||||
break;
|
||||
case goog.events.KeyCodes.LEFT:
|
||||
if (columns == 1) {
|
||||
// Delegate to parent.
|
||||
return false;
|
||||
} else {
|
||||
columns--;
|
||||
}
|
||||
break;
|
||||
case goog.events.KeyCodes.RIGHT:
|
||||
columns++;
|
||||
break;
|
||||
default:
|
||||
return goog.ui.DimensionPicker.superClass_.handleKeyEvent.call(this, e);
|
||||
}
|
||||
this.setValue(columns, rows);
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
// Palette management.
|
||||
|
||||
|
||||
/**
|
||||
* @return {goog.math.Size} Current table size shown (columns x rows).
|
||||
*/
|
||||
goog.ui.DimensionPicker.prototype.getSize = function() {
|
||||
return this.size_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {!goog.math.Size} size The currently highlighted dimensions.
|
||||
*/
|
||||
goog.ui.DimensionPicker.prototype.getValue = function() {
|
||||
return new goog.math.Size(this.highlightedColumns_, this.highlightedRows_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the currently highlighted dimensions. If the dimensions are not valid
|
||||
* (not between 1 and the maximum number of columns/rows to show), they will
|
||||
* be changed to the closest valid value.
|
||||
* @param {(number|!goog.math.Size)} columns The number of columns to highlight,
|
||||
* or a goog.math.Size object containing both.
|
||||
* @param {number=} opt_rows The number of rows to highlight. Can be
|
||||
* omitted when columns is a good.math.Size object.
|
||||
*/
|
||||
goog.ui.DimensionPicker.prototype.setValue = function(columns,
|
||||
opt_rows) {
|
||||
if (!goog.isDef(opt_rows)) {
|
||||
columns = /** @type {!goog.math.Size} */ (columns);
|
||||
opt_rows = columns.height;
|
||||
columns = columns.width;
|
||||
} else {
|
||||
columns = /** @type {number} */ (columns);
|
||||
}
|
||||
|
||||
// Ensure that the row and column values are within the minimum value (1) and
|
||||
// maxmimum values.
|
||||
columns = Math.max(1, columns);
|
||||
opt_rows = Math.max(1, opt_rows);
|
||||
columns = Math.min(this.maxColumns, columns);
|
||||
opt_rows = Math.min(this.maxRows, opt_rows);
|
||||
|
||||
if (this.highlightedColumns_ != columns ||
|
||||
this.highlightedRows_ != opt_rows) {
|
||||
var renderer = this.getRenderer();
|
||||
// Show one more row/column than highlighted so the user understands the
|
||||
// palette can grow.
|
||||
this.size_.width = Math.max(
|
||||
Math.min(columns + 1, this.maxColumns), this.minColumns);
|
||||
this.size_.height = Math.max(
|
||||
Math.min(opt_rows + 1, this.maxRows), this.minRows);
|
||||
renderer.updateSize(this, this.getElement());
|
||||
|
||||
this.highlightedColumns_ = columns;
|
||||
this.highlightedRows_ = opt_rows;
|
||||
renderer.setHighlightedSize(this, columns, opt_rows);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Register this control so it can be created from markup
|
||||
*/
|
||||
goog.ui.registry.setDecoratorByClassName(
|
||||
goog.ui.DimensionPickerRenderer.CSS_CLASS,
|
||||
function() {
|
||||
return new goog.ui.DimensionPicker();
|
||||
});
|
||||
@@ -0,0 +1,416 @@
|
||||
// Copyright 2008 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 The default renderer for a goog.dom.DimensionPicker. A
|
||||
* dimension picker allows the user to visually select a row and column count.
|
||||
* It looks like a palette but in order to minimize DOM load it is rendered.
|
||||
* using CSS background tiling instead of as a grid of nodes.
|
||||
*
|
||||
* @author robbyw@google.com (Robby Walker)
|
||||
* @author abefettig@google.com (Abe Fettig)
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.DimensionPickerRenderer');
|
||||
|
||||
goog.require('goog.a11y.aria');
|
||||
goog.require('goog.a11y.aria.State');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.i18n.bidi');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.ControlRenderer');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Default renderer for {@link goog.ui.DimensionPicker}s. Renders the
|
||||
* palette as two divs, one with the un-highlighted background, and one with the
|
||||
* highlighted background.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {goog.ui.ControlRenderer}
|
||||
*/
|
||||
goog.ui.DimensionPickerRenderer = function() {
|
||||
goog.ui.ControlRenderer.call(this);
|
||||
};
|
||||
goog.inherits(goog.ui.DimensionPickerRenderer, goog.ui.ControlRenderer);
|
||||
goog.addSingletonGetter(goog.ui.DimensionPickerRenderer);
|
||||
|
||||
|
||||
/**
|
||||
* Default CSS class to be applied to the root element of components rendered
|
||||
* by this renderer.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.DimensionPickerRenderer.CSS_CLASS =
|
||||
goog.getCssName('goog-dimension-picker');
|
||||
|
||||
|
||||
/**
|
||||
* Return the underlying div for the given outer element.
|
||||
* @param {Element} element The root element.
|
||||
* @return {Element} The underlying div.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DimensionPickerRenderer.prototype.getUnderlyingDiv_ = function(
|
||||
element) {
|
||||
return element.firstChild.childNodes[1];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Return the highlight div for the given outer element.
|
||||
* @param {Element} element The root element.
|
||||
* @return {Element} The highlight div.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DimensionPickerRenderer.prototype.getHighlightDiv_ = function(
|
||||
element) {
|
||||
return /** @type {Element} */ (element.firstChild.lastChild);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Return the status message div for the given outer element.
|
||||
* @param {Element} element The root element.
|
||||
* @return {Element} The status message div.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DimensionPickerRenderer.prototype.getStatusDiv_ = function(
|
||||
element) {
|
||||
return /** @type {Element} */ (element.lastChild);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Return the invisible mouse catching div for the given outer element.
|
||||
* @param {Element} element The root element.
|
||||
* @return {Element} The invisible mouse catching div.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DimensionPickerRenderer.prototype.getMouseCatcher_ = function(
|
||||
element) {
|
||||
return /** @type {Element} */ (element.firstChild.firstChild);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Overrides {@link goog.ui.ControlRenderer#canDecorate} to allow decorating
|
||||
* empty DIVs only.
|
||||
* @param {Element} element The element to check.
|
||||
* @return {boolean} Whether if the element is an empty div.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.DimensionPickerRenderer.prototype.canDecorate = function(
|
||||
element) {
|
||||
return element.tagName == goog.dom.TagName.DIV && !element.firstChild;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Overrides {@link goog.ui.ControlRenderer#decorate} to decorate empty DIVs.
|
||||
* @param {goog.ui.Control} control goog.ui.DimensionPicker to decorate.
|
||||
* @param {Element} element The element to decorate.
|
||||
* @return {Element} The decorated element.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.DimensionPickerRenderer.prototype.decorate = function(control,
|
||||
element) {
|
||||
var palette = /** @type {goog.ui.DimensionPicker} */ (control);
|
||||
goog.ui.DimensionPickerRenderer.superClass_.decorate.call(this,
|
||||
palette, element);
|
||||
|
||||
this.addElementContents_(palette, element);
|
||||
this.updateSize(palette, element);
|
||||
|
||||
return element;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Scales various elements in order to update the palette's size.
|
||||
* @param {goog.ui.DimensionPicker} palette The palette object.
|
||||
* @param {Element} element The element to set the style of.
|
||||
*/
|
||||
goog.ui.DimensionPickerRenderer.prototype.updateSize =
|
||||
function(palette, element) {
|
||||
var size = palette.getSize();
|
||||
|
||||
element.style.width = size.width + 'em';
|
||||
|
||||
var underlyingDiv = this.getUnderlyingDiv_(element);
|
||||
underlyingDiv.style.width = size.width + 'em';
|
||||
underlyingDiv.style.height = size.height + 'em';
|
||||
|
||||
if (palette.isRightToLeft()) {
|
||||
this.adjustParentDirection_(palette, element);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds the appropriate content elements to the given outer DIV.
|
||||
* @param {goog.ui.DimensionPicker} palette The palette object.
|
||||
* @param {Element} element The element to decorate.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DimensionPickerRenderer.prototype.addElementContents_ = function(
|
||||
palette, element) {
|
||||
// First we create a single div containing three stacked divs. The bottom div
|
||||
// catches mouse events. We can't use document level mouse move detection as
|
||||
// we could lose events to iframes. This is especially important in Firefox 2
|
||||
// in which TrogEdit creates iframes. The middle div uses a css tiled
|
||||
// background image to represent deselected tiles. The top div uses a
|
||||
// different css tiled background image to represent selected tiles.
|
||||
var mouseCatcherDiv = palette.getDomHelper().createDom(goog.dom.TagName.DIV,
|
||||
goog.getCssName(this.getCssClass(), 'mousecatcher'));
|
||||
var unhighlightedDiv = palette.getDomHelper().createDom(goog.dom.TagName.DIV,
|
||||
{
|
||||
'class': goog.getCssName(this.getCssClass(), 'unhighlighted'),
|
||||
'style': 'width:100%;height:100%'
|
||||
});
|
||||
var highlightedDiv = palette.getDomHelper().createDom(goog.dom.TagName.DIV,
|
||||
goog.getCssName(this.getCssClass(), 'highlighted'));
|
||||
element.appendChild(
|
||||
palette.getDomHelper().createDom(goog.dom.TagName.DIV,
|
||||
{'style': 'width:100%;height:100%'},
|
||||
mouseCatcherDiv, unhighlightedDiv, highlightedDiv));
|
||||
|
||||
// Lastly we add a div to store the text version of the current state.
|
||||
element.appendChild(palette.getDomHelper().createDom(goog.dom.TagName.DIV,
|
||||
goog.getCssName(this.getCssClass(), 'status')));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a div and adds the appropriate contents to it.
|
||||
* @param {goog.ui.Control} control Picker to render.
|
||||
* @return {Element} Root element for the palette.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.DimensionPickerRenderer.prototype.createDom = function(control) {
|
||||
var palette = /** @type {goog.ui.DimensionPicker} */ (control);
|
||||
var classNames = this.getClassNames(palette);
|
||||
var element = palette.getDomHelper().createDom(goog.dom.TagName.DIV, {
|
||||
'class' : classNames ? classNames.join(' ') : ''
|
||||
});
|
||||
this.addElementContents_(palette, element);
|
||||
this.updateSize(palette, element);
|
||||
return element;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the control's DOM when the control enters the document. Called
|
||||
* from {@link goog.ui.Control#enterDocument}.
|
||||
* @param {goog.ui.Control} control Palette whose DOM is to be
|
||||
* initialized as it enters the document.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.DimensionPickerRenderer.prototype.initializeDom = function(
|
||||
control) {
|
||||
var palette = /** @type {goog.ui.DimensionPicker} */ (control);
|
||||
goog.ui.DimensionPickerRenderer.superClass_.initializeDom.call(this, palette);
|
||||
|
||||
// Make the displayed highlighted size match the dimension picker's value.
|
||||
var highlightedSize = palette.getValue();
|
||||
this.setHighlightedSize(palette,
|
||||
highlightedSize.width, highlightedSize.height);
|
||||
|
||||
this.positionMouseCatcher(palette);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get the element to listen for mouse move events on.
|
||||
* @param {goog.ui.DimensionPicker} palette The palette to listen on.
|
||||
* @return {Element} The element to listen for mouse move events on.
|
||||
*/
|
||||
goog.ui.DimensionPickerRenderer.prototype.getMouseMoveElement = function(
|
||||
palette) {
|
||||
return /** @type {Element} */ (palette.getElement().firstChild);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the x offset in to the grid for the given mouse x position.
|
||||
* @param {goog.ui.DimensionPicker} palette The table size palette.
|
||||
* @param {number} x The mouse event x position.
|
||||
* @return {number} The x offset in to the grid.
|
||||
*/
|
||||
goog.ui.DimensionPickerRenderer.prototype.getGridOffsetX = function(
|
||||
palette, x) {
|
||||
// TODO(robbyw): Don't rely on magic 18 - measure each palette's em size.
|
||||
return Math.min(palette.maxColumns, Math.ceil(x / 18));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the y offset in to the grid for the given mouse y position.
|
||||
* @param {goog.ui.DimensionPicker} palette The table size palette.
|
||||
* @param {number} y The mouse event y position.
|
||||
* @return {number} The y offset in to the grid.
|
||||
*/
|
||||
goog.ui.DimensionPickerRenderer.prototype.getGridOffsetY = function(
|
||||
palette, y) {
|
||||
return Math.min(palette.maxRows, Math.ceil(y / 18));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the highlighted size. Does nothing if the palette hasn't been rendered.
|
||||
* @param {goog.ui.DimensionPicker} palette The table size palette.
|
||||
* @param {number} columns The number of columns to highlight.
|
||||
* @param {number} rows The number of rows to highlight.
|
||||
*/
|
||||
goog.ui.DimensionPickerRenderer.prototype.setHighlightedSize = function(
|
||||
palette, columns, rows) {
|
||||
var element = palette.getElement();
|
||||
// Can't update anything if DimensionPicker hasn't been rendered.
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Style the highlight div.
|
||||
var style = this.getHighlightDiv_(element).style;
|
||||
style.width = columns + 'em';
|
||||
style.height = rows + 'em';
|
||||
|
||||
// Explicitly set style.right so the element grows to the left when increase
|
||||
// in width.
|
||||
if (palette.isRightToLeft()) {
|
||||
style.right = '0';
|
||||
}
|
||||
|
||||
// Update the aria label.
|
||||
/**
|
||||
* @desc The dimension of the columns and rows currently selected in the
|
||||
* dimension picker, as text that can be spoken by a screen reader.
|
||||
*/
|
||||
var MSG_DIMENSION_PICKER_HIGHLIGHTED_DIMENSIONS = goog.getMsg(
|
||||
'{$numCols} by {$numRows}',
|
||||
{'numCols': columns, 'numRows': rows});
|
||||
goog.a11y.aria.setState(element, goog.a11y.aria.State.LABEL,
|
||||
MSG_DIMENSION_PICKER_HIGHLIGHTED_DIMENSIONS);
|
||||
|
||||
// Update the size text.
|
||||
goog.dom.setTextContent(this.getStatusDiv_(element),
|
||||
goog.i18n.bidi.enforceLtrInText(columns + ' x ' + rows));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Position the mouse catcher such that it receives mouse events past the
|
||||
* selectedsize up to the maximum size. Takes care to not introduce scrollbars.
|
||||
* Should be called on enter document and when the window changes size.
|
||||
* @param {goog.ui.DimensionPicker} palette The table size palette.
|
||||
*/
|
||||
goog.ui.DimensionPickerRenderer.prototype.positionMouseCatcher = function(
|
||||
palette) {
|
||||
var mouseCatcher = this.getMouseCatcher_(palette.getElement());
|
||||
var doc = goog.dom.getOwnerDocument(mouseCatcher);
|
||||
var body = doc.body;
|
||||
|
||||
var position = goog.style.getRelativePosition(mouseCatcher, body);
|
||||
|
||||
// Hide the mouse catcher so it doesn't affect the body's scroll size.
|
||||
mouseCatcher.style.display = 'none';
|
||||
|
||||
// Compute the maximum size the catcher can be without introducing scrolling.
|
||||
var xAvailableEm = (palette.isRightToLeft() && position.x > 0) ?
|
||||
Math.floor(position.x / 18) :
|
||||
Math.floor((body.scrollWidth - position.x) / 18);
|
||||
|
||||
// Computing available height is more complicated - we need to check the
|
||||
// window's inner height.
|
||||
var height;
|
||||
if (goog.userAgent.IE) {
|
||||
// Offset 20px to make up for scrollbar size.
|
||||
height = goog.style.getClientViewportElement(body).scrollHeight - 20;
|
||||
} else {
|
||||
var win = goog.dom.getWindow(doc);
|
||||
// Offset 20px to make up for scrollbar size.
|
||||
height = Math.max(win.innerHeight, body.scrollHeight) - 20;
|
||||
}
|
||||
var yAvailableEm = Math.floor((height - position.y) / 18);
|
||||
|
||||
// Resize and display the mouse catcher.
|
||||
mouseCatcher.style.width = Math.min(palette.maxColumns, xAvailableEm) + 'em';
|
||||
mouseCatcher.style.height = Math.min(palette.maxRows, yAvailableEm) + 'em';
|
||||
mouseCatcher.style.display = '';
|
||||
|
||||
// Explicitly set style.right so the mouse catcher is positioned on the left
|
||||
// side instead of right.
|
||||
if (palette.isRightToLeft()) {
|
||||
mouseCatcher.style.right = '0';
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the CSS class to be applied to the root element of components
|
||||
* rendered using this renderer.
|
||||
* @return {string} Renderer-specific CSS class.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.DimensionPickerRenderer.prototype.getCssClass = function() {
|
||||
return goog.ui.DimensionPickerRenderer.CSS_CLASS;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This function adjusts the positioning from 'left' and 'top' to 'right' and
|
||||
* 'top' as appropriate for RTL control. This is so when the dimensionpicker
|
||||
* grow in width, the containing element grow to the left instead of right.
|
||||
* This won't be necessary if goog.ui.SubMenu rendering code would position RTL
|
||||
* control with 'right' and 'top'.
|
||||
* @private
|
||||
*
|
||||
* @param {goog.ui.DimensionPicker} palette The palette object.
|
||||
* @param {Element} element The palette's element.
|
||||
*/
|
||||
goog.ui.DimensionPickerRenderer.prototype.adjustParentDirection_ =
|
||||
function(palette, element) {
|
||||
var parent = palette.getParent();
|
||||
if (parent) {
|
||||
var parentElement = parent.getElement();
|
||||
|
||||
// Anchors the containing element to the right so it grows to the left
|
||||
// when it increase in width.
|
||||
var right = goog.style.getStyle(parentElement, 'right');
|
||||
if (right == '') {
|
||||
var parentPos = goog.style.getPosition(parentElement);
|
||||
var parentSize = goog.style.getSize(parentElement);
|
||||
if (parentSize.width != 0 && parentPos.x != 0) {
|
||||
var visibleRect = goog.style.getBounds(
|
||||
goog.style.getClientViewportElement());
|
||||
var visibleWidth = visibleRect.width;
|
||||
right = visibleWidth - parentPos.x - parentSize.width;
|
||||
goog.style.setStyle(parentElement, 'right', right + 'px');
|
||||
}
|
||||
}
|
||||
|
||||
// When a table is inserted, the containing elemet's position is
|
||||
// recalculated the next time it shows, set left back to '' to prevent
|
||||
// extra white space on the left.
|
||||
var left = goog.style.getStyle(parentElement, 'left');
|
||||
if (left != '') {
|
||||
goog.style.setStyle(parentElement, 'left', '');
|
||||
}
|
||||
} else {
|
||||
goog.style.setStyle(element, 'right', '0px');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,642 @@
|
||||
// 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 Detects images dragged and dropped on to the window.
|
||||
*
|
||||
* @author robbyw@google.com (Robby Walker)
|
||||
* @author wcrosby@google.com (Wayne Crosby)
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.DragDropDetector');
|
||||
goog.provide('goog.ui.DragDropDetector.EventType');
|
||||
goog.provide('goog.ui.DragDropDetector.ImageDropEvent');
|
||||
goog.provide('goog.ui.DragDropDetector.LinkDropEvent');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.events.Event');
|
||||
goog.require('goog.events.EventHandler');
|
||||
goog.require('goog.events.EventTarget');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.math.Coordinate');
|
||||
goog.require('goog.string');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new drag and drop detector.
|
||||
* @param {string=} opt_filePath The URL of the page to use for the detector.
|
||||
* It should contain the same contents as dragdropdetector_target.html in
|
||||
* the demos directory.
|
||||
* @constructor
|
||||
* @extends {goog.events.EventTarget}
|
||||
*/
|
||||
goog.ui.DragDropDetector = function(opt_filePath) {
|
||||
goog.base(this);
|
||||
|
||||
var iframe = goog.dom.createDom(goog.dom.TagName.IFRAME, {
|
||||
'frameborder': 0
|
||||
});
|
||||
// In Firefox, we do all drop detection with an IFRAME. In IE, we only use
|
||||
// the IFRAME to capture copied, non-linked images. (When we don't need it,
|
||||
// we put a text INPUT before it and push it off screen.)
|
||||
iframe.className = goog.userAgent.IE ?
|
||||
goog.getCssName(
|
||||
goog.ui.DragDropDetector.BASE_CSS_NAME_, 'ie-editable-iframe') :
|
||||
goog.getCssName(
|
||||
goog.ui.DragDropDetector.BASE_CSS_NAME_, 'w3c-editable-iframe');
|
||||
iframe.src = opt_filePath || goog.ui.DragDropDetector.DEFAULT_FILE_PATH_;
|
||||
|
||||
this.element_ = /** @type {HTMLIFrameElement} */ (iframe);
|
||||
|
||||
this.handler_ = new goog.events.EventHandler(this);
|
||||
this.handler_.listen(iframe, goog.events.EventType.LOAD, this.initIframe_);
|
||||
|
||||
if (goog.userAgent.IE) {
|
||||
// In IE, we have to bounce between an INPUT for catching links and an
|
||||
// IFRAME for catching images.
|
||||
this.textInput_ = goog.dom.createDom(goog.dom.TagName.INPUT, {
|
||||
'type': 'text',
|
||||
'className': goog.getCssName(
|
||||
goog.ui.DragDropDetector.BASE_CSS_NAME_, 'ie-input')
|
||||
});
|
||||
|
||||
this.root_ = goog.dom.createDom(goog.dom.TagName.DIV,
|
||||
goog.getCssName(goog.ui.DragDropDetector.BASE_CSS_NAME_, 'ie-div'),
|
||||
this.textInput_, iframe);
|
||||
} else {
|
||||
this.root_ = iframe;
|
||||
}
|
||||
|
||||
document.body.appendChild(this.root_);
|
||||
};
|
||||
goog.inherits(goog.ui.DragDropDetector, goog.events.EventTarget);
|
||||
|
||||
|
||||
/**
|
||||
* Drag and drop event types.
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.ui.DragDropDetector.EventType = {
|
||||
IMAGE_DROPPED: 'onimagedrop',
|
||||
LINK_DROPPED: 'onlinkdrop'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Browser specific drop event type.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DragDropDetector.DROP_EVENT_TYPE_ = goog.userAgent.IE ?
|
||||
goog.events.EventType.DROP : 'dragdrop';
|
||||
|
||||
|
||||
/**
|
||||
* Initial value for clientX and clientY indicating that the location has
|
||||
* never been updated.
|
||||
*/
|
||||
goog.ui.DragDropDetector.INIT_POSITION = -10000;
|
||||
|
||||
|
||||
/**
|
||||
* Prefix for all CSS names.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DragDropDetector.BASE_CSS_NAME_ = goog.getCssName('goog-dragdrop');
|
||||
|
||||
|
||||
/**
|
||||
* @desc Message shown to users to inform them that they can't drag and drop
|
||||
* local files.
|
||||
*/
|
||||
var MSG_DRAG_DROP_LOCAL_FILE_ERROR = goog.getMsg('It is not possible to drag ' +
|
||||
'and drop image files at this time.\nPlease drag an image from your web ' +
|
||||
'browser.');
|
||||
|
||||
|
||||
/**
|
||||
* @desc Message shown to users trying to drag and drop protected images from
|
||||
* Flickr, etc.
|
||||
*/
|
||||
var MSG_DRAG_DROP_PROTECTED_FILE_ERROR = goog.getMsg('The image you are ' +
|
||||
'trying to drag has been blocked by the hosting site.');
|
||||
|
||||
|
||||
/**
|
||||
* A map of special case information for URLs that cannot be dropped. Each
|
||||
* entry is of the form:
|
||||
* regex: url regex
|
||||
* message: user visible message about this special case
|
||||
* @type {Array.<{regex: RegExp, message: string}>}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DragDropDetector.SPECIAL_CASE_URLS_ = [
|
||||
{
|
||||
regex: /^file:\/\/\//,
|
||||
message: MSG_DRAG_DROP_LOCAL_FILE_ERROR
|
||||
},
|
||||
{
|
||||
regex: /flickr(.*)spaceball.gif$/,
|
||||
message: MSG_DRAG_DROP_PROTECTED_FILE_ERROR
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Regex that matches anything that looks kind of like a URL. It matches
|
||||
* nonspacechars://nonspacechars
|
||||
* @type {RegExp}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DragDropDetector.URL_LIKE_REGEX_ = /^\S+:\/\/\S*$/;
|
||||
|
||||
|
||||
/**
|
||||
* Path to the dragdrop.html file.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DragDropDetector.DEFAULT_FILE_PATH_ = 'dragdropdetector_target.html';
|
||||
|
||||
|
||||
/**
|
||||
* Our event handler object.
|
||||
* @type {goog.events.EventHandler}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DragDropDetector.prototype.handler_;
|
||||
|
||||
|
||||
/**
|
||||
* The root element (the IFRAME on most browsers, the DIV on IE).
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DragDropDetector.prototype.root_;
|
||||
|
||||
|
||||
/**
|
||||
* The text INPUT element used to detect link drops on IE. null on Firefox.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DragDropDetector.prototype.textInput_;
|
||||
|
||||
|
||||
/**
|
||||
* The iframe element.
|
||||
* @type {HTMLIFrameElement}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DragDropDetector.prototype.element_;
|
||||
|
||||
|
||||
/**
|
||||
* The iframe's window, null if the iframe hasn't loaded yet.
|
||||
* @type {Window}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DragDropDetector.prototype.window_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* The iframe's document, null if the iframe hasn't loaded yet.
|
||||
* @type {Document}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DragDropDetector.prototype.document_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* The iframe's body, null if the iframe hasn't loaded yet.
|
||||
* @type {HTMLBodyElement}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DragDropDetector.prototype.body_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Whether we are in "screen cover" mode in which the iframe or div is
|
||||
* covering the entire screen.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DragDropDetector.prototype.isCoveringScreen_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* The last position of the mouse while dragging.
|
||||
* @type {goog.math.Coordinate}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DragDropDetector.prototype.mousePosition_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the iframe after it has loaded.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DragDropDetector.prototype.initIframe_ = function() {
|
||||
// Set up a holder for position data.
|
||||
this.mousePosition_ = new goog.math.Coordinate(
|
||||
goog.ui.DragDropDetector.INIT_POSITION,
|
||||
goog.ui.DragDropDetector.INIT_POSITION);
|
||||
|
||||
// Set up pointers to the important parts of the IFrame.
|
||||
this.window_ = this.element_.contentWindow;
|
||||
this.document_ = this.window_.document;
|
||||
this.body_ = this.document_.body;
|
||||
|
||||
if (goog.userAgent.GECKO) {
|
||||
this.document_.designMode = 'on';
|
||||
} else if (!goog.userAgent.IE) {
|
||||
// Bug 1667110
|
||||
// In IE, we only set the IFrame body as content-editable when we bring it
|
||||
// into view at the top of the page. Otherwise it may take focus when the
|
||||
// page is loaded, scrolling the user far offscreen.
|
||||
// Note that this isn't easily unit-testable, since it depends on a
|
||||
// browser-specific behavior with content-editable areas.
|
||||
this.body_.contentEditable = true;
|
||||
}
|
||||
|
||||
this.handler_.listen(document.body, goog.events.EventType.DRAGENTER,
|
||||
this.coverScreen_);
|
||||
|
||||
if (goog.userAgent.IE) {
|
||||
// IE only events.
|
||||
// Set up events on the IFrame.
|
||||
this.handler_.
|
||||
listen(this.body_,
|
||||
[goog.events.EventType.DRAGENTER, goog.events.EventType.DRAGOVER],
|
||||
goog.ui.DragDropDetector.enforceCopyEffect_).
|
||||
listen(this.body_, goog.events.EventType.MOUSEOUT,
|
||||
this.switchToInput_).
|
||||
listen(this.body_, goog.events.EventType.DRAGLEAVE,
|
||||
this.uncoverScreen_).
|
||||
listen(this.body_, goog.ui.DragDropDetector.DROP_EVENT_TYPE_,
|
||||
function(e) {
|
||||
this.trackMouse_(e);
|
||||
|
||||
// The drop event occurs before the content is added to the
|
||||
// iframe. We setTimeout so that handleNodeInserted_ is called
|
||||
// after the content is in the document.
|
||||
goog.global.setTimeout(
|
||||
goog.bind(this.handleNodeInserted_, this, e), 0);
|
||||
return true;
|
||||
}).
|
||||
|
||||
// Set up events on the DIV.
|
||||
listen(this.root_,
|
||||
[goog.events.EventType.DRAGENTER, goog.events.EventType.DRAGOVER],
|
||||
this.handleNewDrag_).
|
||||
listen(this.root_,
|
||||
[
|
||||
goog.events.EventType.MOUSEMOVE,
|
||||
goog.events.EventType.KEYPRESS
|
||||
], this.uncoverScreen_).
|
||||
|
||||
// Set up events on the text INPUT.
|
||||
listen(this.textInput_, goog.events.EventType.DRAGOVER,
|
||||
goog.events.Event.preventDefault).
|
||||
listen(this.textInput_, goog.ui.DragDropDetector.DROP_EVENT_TYPE_,
|
||||
this.handleInputDrop_);
|
||||
} else {
|
||||
// W3C events.
|
||||
this.handler_.
|
||||
listen(this.body_, goog.ui.DragDropDetector.DROP_EVENT_TYPE_,
|
||||
function(e) {
|
||||
this.trackMouse_(e);
|
||||
this.uncoverScreen_();
|
||||
}).
|
||||
listen(this.body_,
|
||||
[goog.events.EventType.MOUSEMOVE, goog.events.EventType.KEYPRESS],
|
||||
this.uncoverScreen_).
|
||||
|
||||
// Detect content insertion.
|
||||
listen(this.document_, 'DOMNodeInserted',
|
||||
this.handleNodeInserted_);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Enforce that anything dragged over the IFRAME is copied in to it, rather
|
||||
* than making it navigate to a different URL.
|
||||
* @param {goog.events.BrowserEvent} e The event to enforce copying on.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DragDropDetector.enforceCopyEffect_ = function(e) {
|
||||
var event = e.getBrowserEvent();
|
||||
// This function is only called on IE.
|
||||
if (event.dataTransfer.dropEffect.toLowerCase() != 'copy') {
|
||||
event.dataTransfer.dropEffect = 'copy';
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Cover the screen with the iframe.
|
||||
* @param {goog.events.BrowserEvent} e The event that caused this function call.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DragDropDetector.prototype.coverScreen_ = function(e) {
|
||||
// Don't do anything if the drop effect is 'none' and we are in IE.
|
||||
// It is set to 'none' in cases like dragging text inside a text area.
|
||||
if (goog.userAgent.IE &&
|
||||
e.getBrowserEvent().dataTransfer.dropEffect == 'none') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isCoveringScreen_) {
|
||||
this.isCoveringScreen_ = true;
|
||||
if (goog.userAgent.IE) {
|
||||
goog.style.setStyle(this.root_, 'top', '0');
|
||||
this.body_.contentEditable = true;
|
||||
this.switchToInput_(e);
|
||||
} else {
|
||||
goog.style.setStyle(this.root_, 'height', '5000px');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Uncover the screen.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DragDropDetector.prototype.uncoverScreen_ = function() {
|
||||
if (this.isCoveringScreen_) {
|
||||
this.isCoveringScreen_ = false;
|
||||
if (goog.userAgent.IE) {
|
||||
this.body_.contentEditable = false;
|
||||
goog.style.setStyle(this.root_, 'top', '-5000px');
|
||||
} else {
|
||||
goog.style.setStyle(this.root_, 'height', '10px');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Re-insert the INPUT into the DIV. Does nothing when the DIV is off screen.
|
||||
* @param {goog.events.BrowserEvent} e The event that caused this function call.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DragDropDetector.prototype.switchToInput_ = function(e) {
|
||||
// This is only called on IE.
|
||||
if (this.isCoveringScreen_) {
|
||||
goog.style.setElementShown(this.textInput_, true);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Remove the text INPUT so the IFRAME is showing. Does nothing when the DIV is
|
||||
* off screen.
|
||||
* @param {goog.events.BrowserEvent} e The event that caused this function call.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DragDropDetector.prototype.switchToIframe_ = function(e) {
|
||||
// This is only called on IE.
|
||||
if (this.isCoveringScreen_) {
|
||||
goog.style.setElementShown(this.textInput_, false);
|
||||
this.isShowingInput_ = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handle a new drag event.
|
||||
* @param {goog.events.BrowserEvent} e The event object.
|
||||
* @return {boolean|undefined} Returns false in IE to cancel the event.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DragDropDetector.prototype.handleNewDrag_ = function(e) {
|
||||
var event = e.getBrowserEvent();
|
||||
|
||||
// This is only called on IE.
|
||||
if (event.dataTransfer.dropEffect == 'link') {
|
||||
this.switchToInput_(e);
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Things that aren't links can be placed in the contentEditable iframe.
|
||||
this.switchToIframe_(e);
|
||||
|
||||
// No need to return true since for events return true is the same as no
|
||||
// return.
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handle mouse tracking.
|
||||
* @param {goog.events.BrowserEvent} e The event object.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DragDropDetector.prototype.trackMouse_ = function(e) {
|
||||
this.mousePosition_.x = e.clientX;
|
||||
this.mousePosition_.y = e.clientY;
|
||||
|
||||
// Check if the event is coming from within the iframe.
|
||||
if (goog.dom.getOwnerDocument(/** @type {Node} */ (e.target)) != document) {
|
||||
var iframePosition = goog.style.getClientPosition(this.element_);
|
||||
this.mousePosition_.x += iframePosition.x;
|
||||
this.mousePosition_.y += iframePosition.y;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handle a drop on the IE text INPUT.
|
||||
* @param {goog.events.BrowserEvent} e The event object.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DragDropDetector.prototype.handleInputDrop_ = function(e) {
|
||||
this.dispatchEvent(
|
||||
new goog.ui.DragDropDetector.LinkDropEvent(
|
||||
e.getBrowserEvent().dataTransfer.getData('Text')));
|
||||
this.uncoverScreen_();
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clear the contents of the iframe.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DragDropDetector.prototype.clearContents_ = function() {
|
||||
if (goog.userAgent.WEBKIT) {
|
||||
// Since this is called on a mutation event for the nodes we are going to
|
||||
// clear, calling this right away crashes some versions of WebKit. Wait
|
||||
// until the events are finished.
|
||||
goog.global.setTimeout(goog.bind(function() {
|
||||
this.innerHTML = '';
|
||||
}, this.body_), 0);
|
||||
} else {
|
||||
this.document_.execCommand('selectAll', false, null);
|
||||
this.document_.execCommand('delete', false, null);
|
||||
this.document_.execCommand('selectAll', false, null);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Event handler called when the content of the iframe changes.
|
||||
* @param {goog.events.BrowserEvent} e The event that caused this function call.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DragDropDetector.prototype.handleNodeInserted_ = function(e) {
|
||||
var uri;
|
||||
|
||||
if (this.body_.innerHTML.indexOf('<') == -1) {
|
||||
// If the document contains no tags (i.e. is just text), try it out.
|
||||
uri = goog.string.trim(goog.dom.getTextContent(this.body_));
|
||||
|
||||
// See if it looks kind of like a url.
|
||||
if (!uri.match(goog.ui.DragDropDetector.URL_LIKE_REGEX_)) {
|
||||
uri = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!uri) {
|
||||
var imgs = this.body_.getElementsByTagName(goog.dom.TagName.IMG);
|
||||
if (imgs && imgs.length) {
|
||||
// TODO(robbyw): Grab all the images, instead of just the first.
|
||||
var img = imgs[0];
|
||||
uri = img.src;
|
||||
}
|
||||
}
|
||||
|
||||
if (uri) {
|
||||
var specialCases = goog.ui.DragDropDetector.SPECIAL_CASE_URLS_;
|
||||
var len = specialCases.length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
var specialCase = specialCases[i];
|
||||
if (uri.match(specialCase.regex)) {
|
||||
alert(specialCase.message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If no special cases matched, add the image.
|
||||
if (i == len) {
|
||||
this.dispatchEvent(
|
||||
new goog.ui.DragDropDetector.ImageDropEvent(
|
||||
uri, this.mousePosition_));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var links = this.body_.getElementsByTagName(goog.dom.TagName.A);
|
||||
if (links) {
|
||||
for (i = 0, len = links.length; i < len; i++) {
|
||||
this.dispatchEvent(
|
||||
new goog.ui.DragDropDetector.LinkDropEvent(links[i].href));
|
||||
}
|
||||
}
|
||||
|
||||
this.clearContents_();
|
||||
this.uncoverScreen_();
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.DragDropDetector.prototype.disposeInternal = function() {
|
||||
goog.base(this, 'disposeInternal');
|
||||
this.handler_.dispose();
|
||||
this.handler_ = null;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new image drop event object.
|
||||
* @param {string} url The url of the dropped image.
|
||||
* @param {goog.math.Coordinate} position The screen position where the drop
|
||||
* occurred.
|
||||
* @constructor
|
||||
* @extends {goog.events.Event}
|
||||
*/
|
||||
goog.ui.DragDropDetector.ImageDropEvent = function(url, position) {
|
||||
goog.base(this, goog.ui.DragDropDetector.EventType.IMAGE_DROPPED);
|
||||
|
||||
/**
|
||||
* The url of the image that was dropped.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.url_ = url;
|
||||
|
||||
/**
|
||||
* The screen position where the drop occurred.
|
||||
* @type {goog.math.Coordinate}
|
||||
* @private
|
||||
*/
|
||||
this.position_ = position;
|
||||
};
|
||||
goog.inherits(goog.ui.DragDropDetector.ImageDropEvent,
|
||||
goog.events.Event);
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} The url of the image that was dropped.
|
||||
*/
|
||||
goog.ui.DragDropDetector.ImageDropEvent.prototype.getUrl = function() {
|
||||
return this.url_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {goog.math.Coordinate} The screen position where the drop occurred.
|
||||
* This may be have x and y of goog.ui.DragDropDetector.INIT_POSITION,
|
||||
* indicating the drop position is unknown.
|
||||
*/
|
||||
goog.ui.DragDropDetector.ImageDropEvent.prototype.getPosition = function() {
|
||||
return this.position_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new link drop event object.
|
||||
* @param {string} url The url of the dropped link.
|
||||
* @constructor
|
||||
* @extends {goog.events.Event}
|
||||
*/
|
||||
goog.ui.DragDropDetector.LinkDropEvent = function(url) {
|
||||
goog.base(this, goog.ui.DragDropDetector.EventType.LINK_DROPPED);
|
||||
|
||||
/**
|
||||
* The url of the link that was dropped.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.url_ = url;
|
||||
};
|
||||
goog.inherits(goog.ui.DragDropDetector.LinkDropEvent,
|
||||
goog.events.Event);
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} The url of the link that was dropped.
|
||||
*/
|
||||
goog.ui.DragDropDetector.LinkDropEvent.prototype.getUrl = function() {
|
||||
return this.url_;
|
||||
};
|
||||
486
nicer-api-docs/closure-library/closure/goog/ui/drilldownrow.js
Normal file
486
nicer-api-docs/closure-library/closure/goog/ui/drilldownrow.js
Normal file
@@ -0,0 +1,486 @@
|
||||
// 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 Tree-like drilldown components for HTML tables.
|
||||
*
|
||||
* This component supports expanding and collapsing groups of rows in
|
||||
* HTML tables. The behavior is like typical Tree widgets, but tables
|
||||
* need special support to enable the tree behaviors.
|
||||
*
|
||||
* Any row or rows in an HTML table can be DrilldownRows. The root
|
||||
* DrilldownRow nodes are always visible in the table, but the rest show
|
||||
* or hide as input events expand and collapse their ancestors.
|
||||
*
|
||||
* Programming them: Top-level DrilldownRows are made by decorating
|
||||
* a TR element. Children are made with addChild or addChildAt, and
|
||||
* are entered into the document by the render() method.
|
||||
*
|
||||
* A DrilldownRow can have any number of children. If it has no children
|
||||
* it can be loaded, not loaded, or with a load in progress.
|
||||
* Top-level DrilldownRows are always displayed (though setting
|
||||
* style.display on a containing DOM node could make one be not
|
||||
* visible to the user). A DrilldownRow can be expanded, or not. A
|
||||
* DrilldownRow displays if all of its ancestors are expanded.
|
||||
*
|
||||
* Set up event handlers and style each row for the application in an
|
||||
* enterDocument method.
|
||||
*
|
||||
* Children normally render into the document lazily, at the first
|
||||
* moment when all ancestors are expanded.
|
||||
*
|
||||
* @see ../demos/drilldownrow.html
|
||||
*/
|
||||
|
||||
// TODO(user): Build support for dynamically loading DrilldownRows,
|
||||
// probably using automplete as an example to follow.
|
||||
|
||||
// TODO(user): Make DrilldownRows accessible through the keyboard.
|
||||
|
||||
// The render method is redefined in this class because when addChildAt renders
|
||||
// the new child it assumes that the child's DOM node will be a child
|
||||
// of the parent component's DOM node, but all DOM nodes of DrilldownRows
|
||||
// in the same tree of DrilldownRows are siblings to each other.
|
||||
//
|
||||
// Arguments (or lack of arguments) to the render methods in Component
|
||||
// all determine the place of the new DOM node in the DOM tree, but
|
||||
// the place of a new DrilldownRow in the DOM needs to be determined by
|
||||
// its position in the tree of DrilldownRows.
|
||||
|
||||
goog.provide('goog.ui.DrilldownRow');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.classes');
|
||||
goog.require('goog.ui.Component');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Builds a DrilldownRow component, which can overlay a tree
|
||||
* structure onto sections of an HTML table.
|
||||
*
|
||||
* @param {Object=} opt_properties This parameter can contain:
|
||||
* contents: if present, user data identifying
|
||||
* the information loaded into the row and its children.
|
||||
* loaded: initializes the isLoaded property, defaults to true.
|
||||
* expanded: DrilldownRow expanded or not, default is true.
|
||||
* html: String of HTML, relevant and required for DrilldownRows to be
|
||||
* added as children. Ignored when decorating an existing table row.
|
||||
* decorator: Function that accepts one DrilldownRow argument, and
|
||||
* should customize and style the row. The default is to call
|
||||
* goog.ui.DrilldownRow.decorator.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
|
||||
* @constructor
|
||||
* @extends {goog.ui.Component}
|
||||
*/
|
||||
goog.ui.DrilldownRow = function(opt_properties, opt_domHelper) {
|
||||
goog.ui.Component.call(this, opt_domHelper);
|
||||
var properties = opt_properties || {};
|
||||
|
||||
// Initialize instance variables.
|
||||
|
||||
/**
|
||||
* String of HTML to initialize the DOM structure for the table row.
|
||||
* Should have the form '<tr attr="etc">Row contents here</tr>'.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.html_ = properties.html;
|
||||
|
||||
/**
|
||||
* Controls whether this component's children will show when it shows.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.expanded_ = typeof properties.expanded != 'undefined' ?
|
||||
properties.expanded : true;
|
||||
|
||||
/**
|
||||
* Is this component loaded? States are true, false, and null for
|
||||
* 'loading in progress'. For in-memory
|
||||
* trees of components, this is always true.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.loaded_ = typeof properties.loaded != 'undefined' ?
|
||||
properties.loaded : true;
|
||||
|
||||
/**
|
||||
* If this component's DOM element is created from a string of
|
||||
* HTML, this is the function to call when it is entered into the DOM tree.
|
||||
* @type {Function} args are DrilldownRow and goog.events.EventHandler
|
||||
* of the DrilldownRow.
|
||||
* @private
|
||||
*/
|
||||
this.decoratorFn_ = properties.decorator || goog.ui.DrilldownRow.decorate;
|
||||
|
||||
/**
|
||||
* Is the DrilldownRow to be displayed? If it is rendered, this mirrors
|
||||
* the style.display of the DrilldownRow's row.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.displayed_ = true;
|
||||
};
|
||||
goog.inherits(goog.ui.DrilldownRow, goog.ui.Component);
|
||||
|
||||
|
||||
/**
|
||||
* Example object with properties of the form accepted by the class
|
||||
* constructor. These are educational and show the compiler that
|
||||
* these properties can be set so it doesn't emit warnings.
|
||||
*/
|
||||
goog.ui.DrilldownRow.sampleProperties = {
|
||||
'html': '<tr><td>Sample</td><td>Sample</tr>',
|
||||
'loaded': true,
|
||||
'decorator': function(selfObj, handler) {
|
||||
// When the mouse is hovering, add CSS class goog-drilldown-hover.
|
||||
goog.ui.DrilldownRow.decorate(selfObj);
|
||||
var row = selfObj.getElement();
|
||||
handler.listen(row, 'mouseover', function() {
|
||||
goog.dom.classes.add(row, goog.getCssName('goog-drilldown-hover'));
|
||||
});
|
||||
handler.listen(row, 'mouseout', function() {
|
||||
goog.dom.classes.remove(row, goog.getCssName('goog-drilldown-hover'));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Implementations of Component methods.
|
||||
//
|
||||
|
||||
|
||||
/**
|
||||
* The base class method calls its superclass method and this
|
||||
* drilldown's 'decorator' method as defined in the constructor.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.DrilldownRow.prototype.enterDocument = function() {
|
||||
goog.ui.DrilldownRow.superClass_.enterDocument.call(this);
|
||||
this.decoratorFn_(this, this.getHandler());
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.DrilldownRow.prototype.createDom = function() {
|
||||
this.setElementInternal(goog.ui.DrilldownRow.createRowNode_(
|
||||
this.html_, this.getDomHelper().getDocument()));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A top-level DrilldownRow decorates a TR element.
|
||||
*
|
||||
* @param {Element} node The element to test for decorability.
|
||||
* @return {boolean} true iff the node is a TR.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.DrilldownRow.prototype.canDecorate = function(node) {
|
||||
return node.tagName == 'TR';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Child drilldowns are rendered when needed.
|
||||
*
|
||||
* @param {goog.ui.Component} child New DrilldownRow child to be added.
|
||||
* @param {number} index position to be occupied by the child.
|
||||
* @param {boolean=} opt_render true to force immediate rendering.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.DrilldownRow.prototype.addChildAt = function(child, index, opt_render) {
|
||||
goog.ui.DrilldownRow.superClass_.addChildAt.call(this, child, index, false);
|
||||
child.setDisplayable_(this.isVisible_() && this.isExpanded());
|
||||
if (opt_render && !child.isInDocument()) {
|
||||
child.render();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.DrilldownRow.prototype.removeChild = function(child) {
|
||||
goog.dom.removeNode(child.getElement());
|
||||
return goog.ui.DrilldownRow.superClass_.removeChild.call(this, child);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Rendering of DrilldownRow's is on need, do not call this directly
|
||||
* from application code.
|
||||
*
|
||||
* Rendering a DrilldownRow places it according to its position in its
|
||||
* tree of DrilldownRows. DrilldownRows cannot be placed any other
|
||||
* way so this method does not use any arguments. This does not call
|
||||
* the base class method and does not modify any of this
|
||||
* DrilldownRow's children.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.DrilldownRow.prototype.render = function() {
|
||||
if (arguments.length) {
|
||||
throw Error('A DrilldownRow cannot be placed under a specific parent.');
|
||||
} else {
|
||||
var parent = this.getParent();
|
||||
if (!parent.isInDocument()) {
|
||||
throw Error('Cannot render child of un-rendered parent');
|
||||
}
|
||||
// The new child's TR node needs to go just after the last TR
|
||||
// of the part of the parent's subtree that is to the left
|
||||
// of this. The subtree includes the parent.
|
||||
var previous = parent.previousRenderedChild_(this);
|
||||
var row;
|
||||
if (previous) {
|
||||
row = previous.lastRenderedLeaf_().getElement();
|
||||
} else {
|
||||
row = parent.getElement();
|
||||
}
|
||||
row = /** @type {Element} */ (row.nextSibling);
|
||||
// Render the child row component into the document.
|
||||
if (row) {
|
||||
this.renderBefore(row);
|
||||
} else {
|
||||
// Render at the end of the parent of this DrilldownRow's
|
||||
// DOM element.
|
||||
var tbody = /** @type {Element} */ (parent.getElement().parentNode);
|
||||
goog.ui.DrilldownRow.superClass_.render.call(this, tbody);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Finds the numeric index of this child within its parent Component.
|
||||
* Throws an exception if it has no parent.
|
||||
*
|
||||
* @return {number} index of this within the children of the parent Component.
|
||||
*/
|
||||
goog.ui.DrilldownRow.prototype.findIndex = function() {
|
||||
var parent = this.getParent();
|
||||
if (!parent) {
|
||||
throw Error('Component has no parent');
|
||||
}
|
||||
return parent.indexOfChild(this);
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Type-specific operations
|
||||
//
|
||||
|
||||
|
||||
/**
|
||||
* Returns the expanded state of the DrilldownRow.
|
||||
*
|
||||
* @return {boolean} true iff this is expanded.
|
||||
*/
|
||||
goog.ui.DrilldownRow.prototype.isExpanded = function() {
|
||||
return this.expanded_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the expanded state of this DrilldownRow: makes all children
|
||||
* displayable or not displayable corresponding to the expanded state.
|
||||
*
|
||||
* @param {boolean} expanded whether this should be expanded or not.
|
||||
*/
|
||||
goog.ui.DrilldownRow.prototype.setExpanded = function(expanded) {
|
||||
if (expanded != this.expanded_) {
|
||||
this.expanded_ = expanded;
|
||||
goog.dom.classes.toggle(this.getElement(),
|
||||
goog.getCssName('goog-drilldown-expanded'));
|
||||
goog.dom.classes.toggle(this.getElement(),
|
||||
goog.getCssName('goog-drilldown-collapsed'));
|
||||
if (this.isVisible_()) {
|
||||
this.forEachChild(function(child) {
|
||||
child.setDisplayable_(expanded);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns this DrilldownRow's level in the tree. Top level is 1.
|
||||
*
|
||||
* @return {number} depth of this DrilldownRow in its tree of drilldowns.
|
||||
*/
|
||||
goog.ui.DrilldownRow.prototype.getDepth = function() {
|
||||
for (var component = this, depth = 0;
|
||||
component instanceof goog.ui.DrilldownRow;
|
||||
component = component.getParent(), depth++) {}
|
||||
return depth;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This static function is a default decorator that adds HTML at the
|
||||
* beginning of the first cell to display indentation and an expander
|
||||
* image; sets up a click handler on the toggler; initializes a class
|
||||
* for the row: either goog-drilldown-expanded or
|
||||
* goog-drilldown-collapsed, depending on the initial state of the
|
||||
* DrilldownRow; and sets up a click event handler on the toggler
|
||||
* element.
|
||||
*
|
||||
* This creates a DIV with class=toggle. Your application can set up
|
||||
* CSS style rules something like this:
|
||||
*
|
||||
* tr.goog-drilldown-expanded .toggle {
|
||||
* background-image: url('minus.png');
|
||||
* }
|
||||
*
|
||||
* tr.goog-drilldown-collapsed .toggle {
|
||||
* background-image: url('plus.png');
|
||||
* }
|
||||
*
|
||||
* These background images show whether the DrilldownRow is expanded.
|
||||
*
|
||||
* @param {goog.ui.DrilldownRow} selfObj DrilldownRow to be decorated.
|
||||
*/
|
||||
goog.ui.DrilldownRow.decorate = function(selfObj) {
|
||||
var depth = selfObj.getDepth();
|
||||
var row = selfObj.getElement();
|
||||
if (!row.cells) {
|
||||
throw Error('No cells');
|
||||
}
|
||||
var cell = row.cells[0];
|
||||
var html = '<div style="float: left; width: ' + depth +
|
||||
'em;"><div class=toggle style="width: 1em; float: right;">' +
|
||||
' </div></div>';
|
||||
var fragment = selfObj.getDomHelper().htmlToDocumentFragment(html);
|
||||
cell.insertBefore(fragment, cell.firstChild);
|
||||
goog.dom.classes.add(row, selfObj.isExpanded() ?
|
||||
goog.getCssName('goog-drilldown-expanded') :
|
||||
goog.getCssName('goog-drilldown-collapsed'));
|
||||
// Default mouse event handling:
|
||||
var toggler = fragment.getElementsByTagName('div')[0];
|
||||
var key = selfObj.getHandler().listen(toggler, 'click', function(event) {
|
||||
selfObj.setExpanded(!selfObj.isExpanded());
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Private methods
|
||||
//
|
||||
|
||||
|
||||
/**
|
||||
* Turn display of a DrilldownRow on or off. If the DrilldownRow has not
|
||||
* yet been rendered, this renders it. This propagates the effect
|
||||
* of the change recursively as needed -- children displaying iff the
|
||||
* parent is displayed and expanded.
|
||||
*
|
||||
* @param {boolean} display state, true iff display is desired.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DrilldownRow.prototype.setDisplayable_ = function(display) {
|
||||
if (display && !this.isInDocument()) {
|
||||
this.render();
|
||||
}
|
||||
if (this.displayed_ == display) {
|
||||
return;
|
||||
}
|
||||
this.displayed_ = display;
|
||||
if (this.isInDocument()) {
|
||||
this.getElement().style.display = display ? '' : 'none';
|
||||
}
|
||||
var selfObj = this;
|
||||
this.forEachChild(function(child) {
|
||||
child.setDisplayable_(display && selfObj.expanded_);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* True iff this and all its DrilldownRow parents are displayable. The
|
||||
* value is an approximation to actual visibility, since it does not
|
||||
* look at whether DOM nodes containing the top-level component have
|
||||
* display: none, visibility: hidden or are otherwise not displayable.
|
||||
* So this visibility is relative to the top-level component.
|
||||
*
|
||||
* @return {boolean} visibility of this relative to its top-level drilldown.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DrilldownRow.prototype.isVisible_ = function() {
|
||||
for (var component = this;
|
||||
component instanceof goog.ui.DrilldownRow;
|
||||
component = component.getParent()) {
|
||||
if (!component.displayed_)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Create and return a TR element from HTML that looks like
|
||||
* "<tr> ... </tr>".
|
||||
*
|
||||
* @param {string} html for one row.
|
||||
* @param {Document} doc object to hold the Element.
|
||||
* @return {Element} table row node created from the HTML.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DrilldownRow.createRowNode_ = function(html, doc) {
|
||||
// Note: this may be slow.
|
||||
var tableHtml = '<table>' + html + '</table>';
|
||||
var div = doc.createElement('div');
|
||||
div.innerHTML = tableHtml;
|
||||
return div.firstChild.rows[0];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get the recursively rightmost child that is in the document.
|
||||
*
|
||||
* @return {goog.ui.DrilldownRow} rightmost child currently entered in
|
||||
* the document, potentially this DrilldownRow. If this is in the
|
||||
* document, result is non-null.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DrilldownRow.prototype.lastRenderedLeaf_ = function() {
|
||||
var leaf = null;
|
||||
for (var node = this;
|
||||
node && node.isInDocument();
|
||||
// Node will become undefined if parent has no children.
|
||||
node = node.getChildAt(node.getChildCount() - 1)) {
|
||||
leaf = node;
|
||||
}
|
||||
return /** @type {goog.ui.DrilldownRow} */ (leaf);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Search this node's direct children for the last one that is in the
|
||||
* document and is before the given child.
|
||||
* @param {goog.ui.DrilldownRow} child The child to stop the search at.
|
||||
* @return {goog.ui.Component?} The last child component before the given child
|
||||
* that is in the document.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.DrilldownRow.prototype.previousRenderedChild_ = function(child) {
|
||||
for (var i = this.getChildCount() - 1; i >= 0; i--) {
|
||||
if (this.getChildAt(i) == child) {
|
||||
for (var j = i - 1; j >= 0; j--) {
|
||||
var prev = this.getChildAt(j);
|
||||
if (prev.isInDocument()) {
|
||||
return prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
@@ -0,0 +1,440 @@
|
||||
// Copyright 2008 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 Wrapper around {@link goog.ui.Dialog}, to provide
|
||||
* dialogs that are smarter about interacting with a rich text editor.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.editor.AbstractDialog');
|
||||
goog.provide('goog.ui.editor.AbstractDialog.Builder');
|
||||
goog.provide('goog.ui.editor.AbstractDialog.EventType');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.classes');
|
||||
goog.require('goog.events.EventTarget');
|
||||
goog.require('goog.string');
|
||||
goog.require('goog.ui.Dialog');
|
||||
|
||||
|
||||
// *** Public interface ***************************************************** //
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates an object that represents a dialog box.
|
||||
* @param {goog.dom.DomHelper} domHelper DomHelper to be used to create the
|
||||
* dialog's dom structure.
|
||||
* @constructor
|
||||
* @extends {goog.events.EventTarget}
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog = function(domHelper) {
|
||||
goog.events.EventTarget.call(this);
|
||||
this.dom = domHelper;
|
||||
};
|
||||
goog.inherits(goog.ui.editor.AbstractDialog, goog.events.EventTarget);
|
||||
|
||||
|
||||
/**
|
||||
* Causes the dialog box to appear, centered on the screen. Lazily creates the
|
||||
* dialog if needed.
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.show = function() {
|
||||
// Lazily create the wrapped dialog to be shown.
|
||||
if (!this.dialogInternal_) {
|
||||
this.dialogInternal_ = this.createDialogControl();
|
||||
this.dialogInternal_.addEventListener(goog.ui.Dialog.EventType.AFTER_HIDE,
|
||||
this.handleAfterHide_, false, this);
|
||||
}
|
||||
|
||||
this.dialogInternal_.setVisible(true);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Hides the dialog, causing AFTER_HIDE to fire.
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.hide = function() {
|
||||
if (this.dialogInternal_) {
|
||||
// This eventually fires the wrapped dialog's AFTER_HIDE event, calling our
|
||||
// handleAfterHide_().
|
||||
this.dialogInternal_.setVisible(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether the dialog is open.
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.isOpen = function() {
|
||||
return !!this.dialogInternal_ && this.dialogInternal_.isVisible();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Runs the handler registered on the OK button event and closes the dialog if
|
||||
* that handler succeeds.
|
||||
* This is useful in cases such as double-clicking an item in the dialog is
|
||||
* equivalent to selecting it and clicking the default button.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.processOkAndClose = function() {
|
||||
// Fake an OK event from the wrapped dialog control.
|
||||
var evt = new goog.ui.Dialog.Event(goog.ui.Dialog.DefaultButtonKeys.OK, null);
|
||||
if (this.handleOk(evt)) {
|
||||
// handleOk calls dispatchEvent, so if any listener calls preventDefault it
|
||||
// will return false and we won't hide the dialog.
|
||||
this.hide();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// *** Dialog events ******************************************************** //
|
||||
|
||||
|
||||
/**
|
||||
* Event type constants for events the dialog fires.
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.EventType = {
|
||||
// This event is fired after the dialog is hidden, no matter if it was closed
|
||||
// via OK or Cancel or is being disposed without being hidden first.
|
||||
AFTER_HIDE: 'afterhide',
|
||||
// Either the cancel or OK events can be canceled via preventDefault or by
|
||||
// returning false from their handlers to stop the dialog from closing.
|
||||
CANCEL: 'cancel',
|
||||
OK: 'ok'
|
||||
};
|
||||
|
||||
|
||||
// *** Inner helper class *************************************************** //
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A builder class for the dialog control. All methods except build return this.
|
||||
* @param {goog.ui.editor.AbstractDialog} editorDialog Editor dialog object
|
||||
* that will wrap the wrapped dialog object this builder will create.
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.Builder = function(editorDialog) {
|
||||
// We require the editor dialog to be passed in so that the builder can set up
|
||||
// ok/cancel listeners by default, making it easier for most dialogs.
|
||||
this.editorDialog_ = editorDialog;
|
||||
this.wrappedDialog_ = new goog.ui.Dialog('', true, this.editorDialog_.dom);
|
||||
this.buttonSet_ = new goog.ui.Dialog.ButtonSet(this.editorDialog_.dom);
|
||||
this.buttonHandlers_ = {};
|
||||
this.addClassName(goog.getCssName('tr-dialog'));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the title of the dialog.
|
||||
* @param {string} title Title HTML (escaped).
|
||||
* @return {goog.ui.editor.AbstractDialog.Builder} This.
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.Builder.prototype.setTitle = function(title) {
|
||||
this.wrappedDialog_.setTitle(title);
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds an OK button to the dialog. Clicking this button will cause {@link
|
||||
* handleOk} to run, subsequently dispatching an OK event.
|
||||
* @param {string=} opt_label The caption for the button, if not "OK".
|
||||
* @return {goog.ui.editor.AbstractDialog.Builder} This.
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.Builder.prototype.addOkButton =
|
||||
function(opt_label) {
|
||||
var key = goog.ui.Dialog.DefaultButtonKeys.OK;
|
||||
/** @desc Label for an OK button in an editor dialog. */
|
||||
var MSG_TR_DIALOG_OK = goog.getMsg('OK');
|
||||
// True means this is the default/OK button.
|
||||
this.buttonSet_.set(key, opt_label || MSG_TR_DIALOG_OK, true);
|
||||
this.buttonHandlers_[key] = goog.bind(this.editorDialog_.handleOk,
|
||||
this.editorDialog_);
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds a Cancel button to the dialog. Clicking this button will cause {@link
|
||||
* handleCancel} to run, subsequently dispatching a CANCEL event.
|
||||
* @param {string=} opt_label The caption for the button, if not "Cancel".
|
||||
* @return {goog.ui.editor.AbstractDialog.Builder} This.
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.Builder.prototype.addCancelButton =
|
||||
function(opt_label) {
|
||||
var key = goog.ui.Dialog.DefaultButtonKeys.CANCEL;
|
||||
/** @desc Label for a cancel button in an editor dialog. */
|
||||
var MSG_TR_DIALOG_CANCEL = goog.getMsg('Cancel');
|
||||
// False means it's not the OK button, true means it's the Cancel button.
|
||||
this.buttonSet_.set(key, opt_label || MSG_TR_DIALOG_CANCEL, false, true);
|
||||
this.buttonHandlers_[key] = goog.bind(this.editorDialog_.handleCancel,
|
||||
this.editorDialog_);
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds a custom button to the dialog.
|
||||
* @param {string} label The caption for the button.
|
||||
* @param {function(goog.ui.Dialog.EventType):*} handler Function called when
|
||||
* the button is clicked. It is recommended that this function be a method
|
||||
* in the concrete subclass of AbstractDialog using this Builder, and that
|
||||
* it dispatch an event (see {@link handleOk}).
|
||||
* @param {string=} opt_buttonId Identifier to be used to access the button when
|
||||
* calling AbstractDialog.getButtonElement().
|
||||
* @return {goog.ui.editor.AbstractDialog.Builder} This.
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.Builder.prototype.addButton =
|
||||
function(label, handler, opt_buttonId) {
|
||||
// We don't care what the key is, just that we can match the button with the
|
||||
// handler function later.
|
||||
var key = opt_buttonId || goog.string.createUniqueString();
|
||||
this.buttonSet_.set(key, label);
|
||||
this.buttonHandlers_[key] = handler;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Puts a CSS class on the dialog's main element.
|
||||
* @param {string} className The class to add.
|
||||
* @return {goog.ui.editor.AbstractDialog.Builder} This.
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.Builder.prototype.addClassName =
|
||||
function(className) {
|
||||
goog.dom.classes.add(this.wrappedDialog_.getDialogElement(), className);
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the content element of the dialog.
|
||||
* @param {Element} contentElem An element for the main body.
|
||||
* @return {goog.ui.editor.AbstractDialog.Builder} This.
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.Builder.prototype.setContent =
|
||||
function(contentElem) {
|
||||
goog.dom.appendChild(this.wrappedDialog_.getContentElement(), contentElem);
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Builds the wrapped dialog control. May only be called once, after which
|
||||
* no more methods may be called on this builder.
|
||||
* @return {goog.ui.Dialog} The wrapped dialog control.
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.Builder.prototype.build = function() {
|
||||
if (this.buttonSet_.isEmpty()) {
|
||||
// If caller didn't set any buttons, add an OK and Cancel button by default.
|
||||
this.addOkButton();
|
||||
this.addCancelButton();
|
||||
}
|
||||
this.wrappedDialog_.setButtonSet(this.buttonSet_);
|
||||
|
||||
var handlers = this.buttonHandlers_;
|
||||
this.buttonHandlers_ = null;
|
||||
this.wrappedDialog_.addEventListener(goog.ui.Dialog.EventType.SELECT,
|
||||
// Listen for the SELECT event, which means a button was clicked, and
|
||||
// call the handler associated with that button via the key property.
|
||||
function(e) {
|
||||
if (handlers[e.key]) {
|
||||
return handlers[e.key](e);
|
||||
}
|
||||
});
|
||||
|
||||
// All editor dialogs are modal.
|
||||
this.wrappedDialog_.setModal(true);
|
||||
|
||||
var dialog = this.wrappedDialog_;
|
||||
this.wrappedDialog_ = null;
|
||||
return dialog;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Editor dialog that will wrap the wrapped dialog this builder will create.
|
||||
* @type {goog.ui.editor.AbstractDialog}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.Builder.prototype.editorDialog_;
|
||||
|
||||
|
||||
/**
|
||||
* wrapped dialog control being built by this builder.
|
||||
* @type {goog.ui.Dialog}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.Builder.prototype.wrappedDialog_;
|
||||
|
||||
|
||||
/**
|
||||
* Set of buttons to be added to the wrapped dialog control.
|
||||
* @type {goog.ui.Dialog.ButtonSet}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.Builder.prototype.buttonSet_;
|
||||
|
||||
|
||||
/**
|
||||
* Map from keys that will be returned in the wrapped dialog SELECT events to
|
||||
* handler functions to be called to handle those events.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.Builder.prototype.buttonHandlers_;
|
||||
|
||||
|
||||
// *** Protected interface ************************************************** //
|
||||
|
||||
|
||||
/**
|
||||
* The DOM helper for the parent document.
|
||||
* @type {goog.dom.DomHelper}
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.dom;
|
||||
|
||||
|
||||
/**
|
||||
* Creates and returns the goog.ui.Dialog control that is being wrapped
|
||||
* by this object.
|
||||
* @return {goog.ui.Dialog} Created Dialog control.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.createDialogControl =
|
||||
goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Returns the HTML Button element for the OK button in this dialog.
|
||||
* @return {Element} The button element if found, else null.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.getOkButtonElement = function() {
|
||||
return this.getButtonElement(goog.ui.Dialog.DefaultButtonKeys.OK);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the HTML Button element for the Cancel button in this dialog.
|
||||
* @return {Element} The button element if found, else null.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.getCancelButtonElement = function() {
|
||||
return this.getButtonElement(goog.ui.Dialog.DefaultButtonKeys.CANCEL);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the HTML Button element for the button added to this dialog with
|
||||
* the given button id.
|
||||
* @param {string} buttonId The id of the button to get.
|
||||
* @return {Element} The button element if found, else null.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.getButtonElement = function(buttonId) {
|
||||
return this.dialogInternal_.getButtonSet().getButton(buttonId);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates and returns the event object to be used when dispatching the OK
|
||||
* event to listeners, or returns null to prevent the dialog from closing.
|
||||
* Subclasses should override this to return their own subclass of
|
||||
* goog.events.Event that includes all data a plugin would need from the dialog.
|
||||
* @param {goog.events.Event} e The event object dispatched by the wrapped
|
||||
* dialog.
|
||||
* @return {goog.events.Event} The event object to be used when dispatching the
|
||||
* OK event to listeners.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.createOkEvent = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Handles the event dispatched by the wrapped dialog control when the user
|
||||
* clicks the OK button. Attempts to create the OK event object and dispatches
|
||||
* it if successful.
|
||||
* @param {goog.ui.Dialog.Event} e wrapped dialog OK event object.
|
||||
* @return {boolean} Whether the default action (closing the dialog) should
|
||||
* still be executed. This will be false if the OK event could not be
|
||||
* created to be dispatched, or if any listener to that event returs false
|
||||
* or calls preventDefault.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.handleOk = function(e) {
|
||||
var eventObj = this.createOkEvent(e);
|
||||
if (eventObj) {
|
||||
return this.dispatchEvent(eventObj);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles the event dispatched by the wrapped dialog control when the user
|
||||
* clicks the Cancel button. Simply dispatches a CANCEL event.
|
||||
* @return {boolean} Returns false if any of the handlers called prefentDefault
|
||||
* on the event or returned false themselves.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.handleCancel = function() {
|
||||
return this.dispatchEvent(goog.ui.editor.AbstractDialog.EventType.CANCEL);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Disposes of the dialog. If the dialog is open, it will be hidden and
|
||||
* AFTER_HIDE will be dispatched.
|
||||
* @override
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.disposeInternal = function() {
|
||||
if (this.dialogInternal_) {
|
||||
this.hide();
|
||||
|
||||
this.dialogInternal_.dispose();
|
||||
this.dialogInternal_ = null;
|
||||
}
|
||||
|
||||
goog.ui.editor.AbstractDialog.superClass_.disposeInternal.call(this);
|
||||
};
|
||||
|
||||
|
||||
// *** Private implementation *********************************************** //
|
||||
|
||||
|
||||
/**
|
||||
* The wrapped dialog widget.
|
||||
* @type {goog.ui.Dialog}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.dialogInternal_;
|
||||
|
||||
|
||||
/**
|
||||
* Cleans up after the dialog is hidden and fires the AFTER_HIDE event. Should
|
||||
* be a listener for the wrapped dialog's AFTER_HIDE event.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.AbstractDialog.prototype.handleAfterHide_ = function() {
|
||||
this.dispatchEvent(goog.ui.editor.AbstractDialog.EventType.AFTER_HIDE);
|
||||
};
|
||||
525
nicer-api-docs/closure-library/closure/goog/ui/editor/bubble.js
Normal file
525
nicer-api-docs/closure-library/closure/goog/ui/editor/bubble.js
Normal file
@@ -0,0 +1,525 @@
|
||||
// Copyright 2005 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 Bubble component - handles display, hiding, etc. of the
|
||||
* actual bubble UI.
|
||||
*
|
||||
* This is used exclusively by code within the editor package, and should not
|
||||
* be used directly.
|
||||
*
|
||||
* @author robbyw@google.com (Robby Walker)
|
||||
* @author tildahl@google.com (Michael Tildahl)
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.editor.Bubble');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.dom.ViewportSizeMonitor');
|
||||
goog.require('goog.dom.classes');
|
||||
goog.require('goog.editor.style');
|
||||
goog.require('goog.events.EventHandler');
|
||||
goog.require('goog.events.EventTarget');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.log');
|
||||
goog.require('goog.math.Box');
|
||||
goog.require('goog.object');
|
||||
goog.require('goog.positioning');
|
||||
goog.require('goog.positioning.Corner');
|
||||
goog.require('goog.positioning.Overflow');
|
||||
goog.require('goog.positioning.OverflowStatus');
|
||||
goog.require('goog.string');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('goog.ui.PopupBase');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Property bubble UI element.
|
||||
* @param {Element} parent The parent element for this bubble.
|
||||
* @param {number} zIndex The z index to draw the bubble at.
|
||||
* @constructor
|
||||
* @extends {goog.events.EventTarget}
|
||||
*/
|
||||
goog.ui.editor.Bubble = function(parent, zIndex) {
|
||||
goog.base(this);
|
||||
|
||||
/**
|
||||
* Dom helper for the document the bubble should be shown in.
|
||||
* @type {!goog.dom.DomHelper}
|
||||
* @private
|
||||
*/
|
||||
this.dom_ = goog.dom.getDomHelper(parent);
|
||||
|
||||
/**
|
||||
* Event handler for this bubble.
|
||||
* @type {goog.events.EventHandler}
|
||||
* @private
|
||||
*/
|
||||
this.eventHandler_ = new goog.events.EventHandler(this);
|
||||
|
||||
/**
|
||||
* Object that monitors the application window for size changes.
|
||||
* @type {goog.dom.ViewportSizeMonitor}
|
||||
* @private
|
||||
*/
|
||||
this.viewPortSizeMonitor_ = new goog.dom.ViewportSizeMonitor(
|
||||
this.dom_.getWindow());
|
||||
|
||||
/**
|
||||
* Maps panel ids to panels.
|
||||
* @type {Object.<goog.ui.editor.Bubble.Panel_>}
|
||||
* @private
|
||||
*/
|
||||
this.panels_ = {};
|
||||
|
||||
/**
|
||||
* Container element for the entire bubble. This may contain elements related
|
||||
* to look and feel or styling of the bubble.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
this.bubbleContainer_ =
|
||||
this.dom_.createDom(goog.dom.TagName.DIV,
|
||||
{'className': goog.ui.editor.Bubble.BUBBLE_CLASSNAME});
|
||||
|
||||
goog.style.setElementShown(this.bubbleContainer_, false);
|
||||
goog.dom.appendChild(parent, this.bubbleContainer_);
|
||||
goog.style.setStyle(this.bubbleContainer_, 'zIndex', zIndex);
|
||||
|
||||
/**
|
||||
* Container element for the bubble panels - this should be some inner element
|
||||
* within (or equal to) bubbleContainer.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
this.bubbleContents_ = this.createBubbleDom(this.dom_, this.bubbleContainer_);
|
||||
|
||||
/**
|
||||
* Element showing the close box.
|
||||
* @type {!Element}
|
||||
* @private
|
||||
*/
|
||||
this.closeBox_ = this.dom_.createDom(goog.dom.TagName.DIV, {
|
||||
'className': goog.getCssName('tr_bubble_closebox'),
|
||||
'innerHTML': ' '
|
||||
});
|
||||
this.bubbleContents_.appendChild(this.closeBox_);
|
||||
|
||||
// We make bubbles unselectable so that clicking on them does not steal focus
|
||||
// or move the cursor away from the element the bubble is attached to.
|
||||
goog.editor.style.makeUnselectable(this.bubbleContainer_, this.eventHandler_);
|
||||
|
||||
/**
|
||||
* Popup that controls showing and hiding the bubble at the appropriate
|
||||
* position.
|
||||
* @type {goog.ui.PopupBase}
|
||||
* @private
|
||||
*/
|
||||
this.popup_ = new goog.ui.PopupBase(this.bubbleContainer_);
|
||||
};
|
||||
goog.inherits(goog.ui.editor.Bubble, goog.events.EventTarget);
|
||||
|
||||
|
||||
/**
|
||||
* The css class name of the bubble container element.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.editor.Bubble.BUBBLE_CLASSNAME = goog.getCssName('tr_bubble');
|
||||
|
||||
|
||||
/**
|
||||
* Creates and adds DOM for the bubble UI to the given container. This default
|
||||
* implementation just returns the container itself.
|
||||
* @param {!goog.dom.DomHelper} dom DOM helper to use.
|
||||
* @param {!Element} container Element to add the new elements to.
|
||||
* @return {!Element} The element where bubble content should be added.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.createBubbleDom = function(dom, container) {
|
||||
return container;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A logger for goog.ui.editor.Bubble.
|
||||
* @type {goog.log.Logger}
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.logger =
|
||||
goog.log.getLogger('goog.ui.editor.Bubble');
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.editor.Bubble.prototype.disposeInternal = function() {
|
||||
goog.base(this, 'disposeInternal');
|
||||
|
||||
goog.dom.removeNode(this.bubbleContainer_);
|
||||
this.bubbleContainer_ = null;
|
||||
|
||||
this.eventHandler_.dispose();
|
||||
this.eventHandler_ = null;
|
||||
|
||||
this.viewPortSizeMonitor_.dispose();
|
||||
this.viewPortSizeMonitor_ = null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {Element} The element that where the bubble's contents go.
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.getContentElement = function() {
|
||||
return this.bubbleContents_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {Element} The element that contains the bubble.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.getContainerElement = function() {
|
||||
return this.bubbleContainer_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {goog.events.EventHandler} The event handler.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.getEventHandler = function() {
|
||||
return this.eventHandler_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles user resizing of window.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.handleWindowResize_ = function() {
|
||||
if (this.isVisible()) {
|
||||
this.reposition();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether there is already a panel of the given type.
|
||||
* @param {string} type Type of panel to check.
|
||||
* @return {boolean} Whether there is already a panel of the given type.
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.hasPanelOfType = function(type) {
|
||||
return goog.object.some(this.panels_, function(panel) {
|
||||
return panel.type == type;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds a panel to the bubble.
|
||||
* @param {string} type The type of bubble panel this is. Should usually be
|
||||
* the same as the tagName of the targetElement. This ensures multiple
|
||||
* bubble panels don't appear for the same element.
|
||||
* @param {string} title The title of the panel.
|
||||
* @param {Element} targetElement The target element of the bubble.
|
||||
* @param {function(Element): void} contentFn Function that when called with
|
||||
* a container element, will add relevant panel content to it.
|
||||
* @param {boolean=} opt_preferTopPosition Whether to prefer placing the bubble
|
||||
* above the element instead of below it. Defaults to preferring below.
|
||||
* If any panel prefers the top position, the top position is used.
|
||||
* @return {string} The id of the panel.
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.addPanel = function(type, title, targetElement,
|
||||
contentFn, opt_preferTopPosition) {
|
||||
var id = goog.string.createUniqueString();
|
||||
var panel = new goog.ui.editor.Bubble.Panel_(this.dom_, id, type, title,
|
||||
targetElement, !opt_preferTopPosition);
|
||||
this.panels_[id] = panel;
|
||||
|
||||
// Insert the panel in string order of type. Technically we could use binary
|
||||
// search here but n is really small (probably 0 - 2) so it's not worth it.
|
||||
// The last child of bubbleContents_ is the close box so we take care not
|
||||
// to treat it as a panel element, and we also ensure it stays as the last
|
||||
// element. The intention here is not to create any artificial order, but
|
||||
// just to ensure that it is always consistent.
|
||||
var nextElement;
|
||||
for (var i = 0, len = this.bubbleContents_.childNodes.length - 1; i < len;
|
||||
i++) {
|
||||
var otherChild = this.bubbleContents_.childNodes[i];
|
||||
var otherPanel = this.panels_[otherChild.id];
|
||||
if (otherPanel.type > type) {
|
||||
nextElement = otherChild;
|
||||
break;
|
||||
}
|
||||
}
|
||||
goog.dom.insertSiblingBefore(panel.element,
|
||||
nextElement || this.bubbleContents_.lastChild);
|
||||
|
||||
contentFn(panel.getContentElement());
|
||||
goog.editor.style.makeUnselectable(panel.element, this.eventHandler_);
|
||||
|
||||
var numPanels = goog.object.getCount(this.panels_);
|
||||
if (numPanels == 1) {
|
||||
this.openBubble_();
|
||||
} else if (numPanels == 2) {
|
||||
goog.dom.classes.add(this.bubbleContainer_,
|
||||
goog.getCssName('tr_multi_bubble'));
|
||||
}
|
||||
this.reposition();
|
||||
|
||||
return id;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes the panel with the given id.
|
||||
* @param {string} id The id of the panel.
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.removePanel = function(id) {
|
||||
var panel = this.panels_[id];
|
||||
goog.dom.removeNode(panel.element);
|
||||
delete this.panels_[id];
|
||||
|
||||
var numPanels = goog.object.getCount(this.panels_);
|
||||
if (numPanels <= 1) {
|
||||
goog.dom.classes.remove(this.bubbleContainer_,
|
||||
goog.getCssName('tr_multi_bubble'));
|
||||
}
|
||||
|
||||
if (numPanels == 0) {
|
||||
this.closeBubble_();
|
||||
} else {
|
||||
this.reposition();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Opens the bubble.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.openBubble_ = function() {
|
||||
this.eventHandler_.
|
||||
listen(this.closeBox_, goog.events.EventType.CLICK,
|
||||
this.closeBubble_).
|
||||
listen(this.viewPortSizeMonitor_,
|
||||
goog.events.EventType.RESIZE, this.handleWindowResize_).
|
||||
listen(this.popup_, goog.ui.PopupBase.EventType.HIDE,
|
||||
this.handlePopupHide);
|
||||
|
||||
this.popup_.setVisible(true);
|
||||
this.reposition();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Closes the bubble.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.closeBubble_ = function() {
|
||||
this.popup_.setVisible(false);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles the popup's hide event by removing all panels and dispatching a
|
||||
* HIDE event.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.handlePopupHide = function() {
|
||||
// Remove the panel elements.
|
||||
for (var panelId in this.panels_) {
|
||||
goog.dom.removeNode(this.panels_[panelId].element);
|
||||
}
|
||||
|
||||
// Update the state to reflect no panels.
|
||||
this.panels_ = {};
|
||||
goog.dom.classes.remove(this.bubbleContainer_,
|
||||
goog.getCssName('tr_multi_bubble'));
|
||||
|
||||
this.eventHandler_.removeAll();
|
||||
this.dispatchEvent(goog.ui.Component.EventType.HIDE);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the visibility of the bubble.
|
||||
* @return {boolean} True if visible false if not.
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.isVisible = function() {
|
||||
return this.popup_.isVisible();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The vertical clearance in pixels between the bottom of the targetElement
|
||||
* and the edge of the bubble.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.Bubble.VERTICAL_CLEARANCE_ = goog.userAgent.IE ? 4 : 2;
|
||||
|
||||
|
||||
/**
|
||||
* Bubble's margin box to be passed to goog.positioning.
|
||||
* @type {goog.math.Box}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.Bubble.MARGIN_BOX_ = new goog.math.Box(
|
||||
goog.ui.editor.Bubble.VERTICAL_CLEARANCE_, 0,
|
||||
goog.ui.editor.Bubble.VERTICAL_CLEARANCE_, 0);
|
||||
|
||||
|
||||
/**
|
||||
* Positions and displays this bubble below its targetElement. Assumes that
|
||||
* the bubbleContainer is already contained in the document object it applies
|
||||
* to.
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.reposition = function() {
|
||||
var targetElement = null;
|
||||
var preferBottomPosition = true;
|
||||
for (var panelId in this.panels_) {
|
||||
var panel = this.panels_[panelId];
|
||||
// We don't care which targetElement we get, so we just take the last one.
|
||||
targetElement = panel.targetElement;
|
||||
preferBottomPosition = preferBottomPosition && panel.preferBottomPosition;
|
||||
}
|
||||
var status = goog.positioning.OverflowStatus.FAILED;
|
||||
|
||||
// Fix for bug when bubbleContainer and targetElement have
|
||||
// opposite directionality, the bubble should anchor to the END of
|
||||
// the targetElement instead of START.
|
||||
var reverseLayout = (goog.style.isRightToLeft(this.bubbleContainer_) !=
|
||||
goog.style.isRightToLeft(targetElement));
|
||||
|
||||
// Try to put the bubble at the bottom of the target unless the plugin has
|
||||
// requested otherwise.
|
||||
if (preferBottomPosition) {
|
||||
status = this.positionAtAnchor_(reverseLayout ?
|
||||
goog.positioning.Corner.BOTTOM_END :
|
||||
goog.positioning.Corner.BOTTOM_START,
|
||||
goog.positioning.Corner.TOP_START,
|
||||
goog.positioning.Overflow.ADJUST_X | goog.positioning.Overflow.FAIL_Y);
|
||||
}
|
||||
|
||||
if (status & goog.positioning.OverflowStatus.FAILED) {
|
||||
// Try to put it at the top of the target if there is not enough
|
||||
// space at the bottom.
|
||||
status = this.positionAtAnchor_(reverseLayout ?
|
||||
goog.positioning.Corner.TOP_END : goog.positioning.Corner.TOP_START,
|
||||
goog.positioning.Corner.BOTTOM_START,
|
||||
goog.positioning.Overflow.ADJUST_X | goog.positioning.Overflow.FAIL_Y);
|
||||
}
|
||||
|
||||
if (status & goog.positioning.OverflowStatus.FAILED) {
|
||||
// Put it at the bottom again with adjustment if there is no
|
||||
// enough space at the top.
|
||||
status = this.positionAtAnchor_(reverseLayout ?
|
||||
goog.positioning.Corner.BOTTOM_END :
|
||||
goog.positioning.Corner.BOTTOM_START,
|
||||
goog.positioning.Corner.TOP_START,
|
||||
goog.positioning.Overflow.ADJUST_X |
|
||||
goog.positioning.Overflow.ADJUST_Y);
|
||||
if (status & goog.positioning.OverflowStatus.FAILED) {
|
||||
goog.log.warning(this.logger,
|
||||
'reposition(): positionAtAnchor() failed with ' + status);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A helper for reposition() - positions the bubble in regards to the position
|
||||
* of the elements the bubble is attached to.
|
||||
* @param {goog.positioning.Corner} targetCorner The corner of
|
||||
* the target element.
|
||||
* @param {goog.positioning.Corner} bubbleCorner The corner of the bubble.
|
||||
* @param {number} overflow Overflow handling mode bitmap,
|
||||
* {@see goog.positioning.Overflow}.
|
||||
* @return {number} Status bitmap, {@see goog.positioning.OverflowStatus}.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.Bubble.prototype.positionAtAnchor_ = function(
|
||||
targetCorner, bubbleCorner, overflow) {
|
||||
var targetElement = null;
|
||||
for (var panelId in this.panels_) {
|
||||
// For now, we use the outermost element. This assumes the multiple
|
||||
// elements this panel is showing for contain each other - in the event
|
||||
// that is not generally the case this may need to be updated to pick
|
||||
// the lowest or highest element depending on targetCorner.
|
||||
var candidate = this.panels_[panelId].targetElement;
|
||||
if (!targetElement || goog.dom.contains(candidate, targetElement)) {
|
||||
targetElement = this.panels_[panelId].targetElement;
|
||||
}
|
||||
}
|
||||
return goog.positioning.positionAtAnchor(
|
||||
targetElement, targetCorner, this.bubbleContainer_,
|
||||
bubbleCorner, null, goog.ui.editor.Bubble.MARGIN_BOX_, overflow);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Private class used to describe a bubble panel.
|
||||
* @param {goog.dom.DomHelper} dom DOM helper used to create the panel.
|
||||
* @param {string} id ID of the panel.
|
||||
* @param {string} type Type of the panel.
|
||||
* @param {string} title Title of the panel.
|
||||
* @param {Element} targetElement Element the panel is showing for.
|
||||
* @param {boolean} preferBottomPosition Whether this panel prefers to show
|
||||
* below the target element.
|
||||
* @constructor
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.Bubble.Panel_ = function(dom, id, type, title, targetElement,
|
||||
preferBottomPosition) {
|
||||
/**
|
||||
* The type of bubble panel.
|
||||
* @type {string}
|
||||
*/
|
||||
this.type = type;
|
||||
|
||||
/**
|
||||
* The target element of this bubble panel.
|
||||
* @type {Element}
|
||||
*/
|
||||
this.targetElement = targetElement;
|
||||
|
||||
/**
|
||||
* Whether the panel prefers to be placed below the target element.
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.preferBottomPosition = preferBottomPosition;
|
||||
|
||||
/**
|
||||
* The element containing this panel.
|
||||
*/
|
||||
this.element = dom.createDom(goog.dom.TagName.DIV,
|
||||
{className: goog.getCssName('tr_bubble_panel'), id: id},
|
||||
dom.createDom(goog.dom.TagName.DIV,
|
||||
{className: goog.getCssName('tr_bubble_panel_title')},
|
||||
title + ':'), // TODO(robbyw): Does this work properly in bidi?
|
||||
dom.createDom(goog.dom.TagName.DIV,
|
||||
{className: goog.getCssName('tr_bubble_panel_content')}));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {Element} The element in the panel where content should go.
|
||||
*/
|
||||
goog.ui.editor.Bubble.Panel_.prototype.getContentElement = function() {
|
||||
return /** @type {Element} */ (this.element.lastChild);
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,123 @@
|
||||
// Copyright 2011 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.
|
||||
|
||||
goog.provide('goog.ui.editor.EquationEditorDialog');
|
||||
|
||||
goog.require('goog.editor.Command');
|
||||
goog.require('goog.ui.Dialog');
|
||||
goog.require('goog.ui.editor.AbstractDialog');
|
||||
goog.require('goog.ui.editor.EquationEditorOkEvent');
|
||||
goog.require('goog.ui.equation.TexEditor');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Equation editor dialog (based on goog.ui.editor.AbstractDialog).
|
||||
* @param {Object} context The context that this dialog runs in.
|
||||
* @param {goog.dom.DomHelper} domHelper DomHelper to be used to create the
|
||||
* dialog's dom structure.
|
||||
* @param {string} equation Initial equation.
|
||||
* @param {string} helpUrl URL pointing to help documentation.
|
||||
* @constructor
|
||||
* @extends {goog.ui.editor.AbstractDialog}
|
||||
*/
|
||||
goog.ui.editor.EquationEditorDialog = function(context, domHelper,
|
||||
equation, helpUrl) {
|
||||
goog.ui.editor.AbstractDialog.call(this, domHelper);
|
||||
this.equationEditor_ =
|
||||
new goog.ui.equation.TexEditor(context, helpUrl, domHelper);
|
||||
this.equationEditor_.render();
|
||||
this.equationEditor_.setEquation(equation);
|
||||
this.equationEditor_.addEventListener(goog.editor.Command.EQUATION,
|
||||
this.onChange_, false, this);
|
||||
};
|
||||
goog.inherits(goog.ui.editor.EquationEditorDialog,
|
||||
goog.ui.editor.AbstractDialog);
|
||||
|
||||
|
||||
/**
|
||||
* The equation editor actual UI.
|
||||
* @type {goog.ui.equation.TexEditor}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.EquationEditorDialog.prototype.equationEditor_;
|
||||
|
||||
|
||||
/**
|
||||
* The dialog's OK button element.
|
||||
* @type {Element?}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.EquationEditorDialog.prototype.okButton_;
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.editor.EquationEditorDialog.prototype.createDialogControl =
|
||||
function() {
|
||||
var builder = new goog.ui.editor.AbstractDialog.Builder(this);
|
||||
|
||||
/**
|
||||
* @desc The title of the equation editor dialog.
|
||||
*/
|
||||
var MSG_EE_DIALOG_TITLE = goog.getMsg('Equation Editor');
|
||||
|
||||
/**
|
||||
* @desc Button label for the equation editor dialog for adding
|
||||
* a new equation.
|
||||
*/
|
||||
var MSG_EE_BUTTON_SAVE_NEW = goog.getMsg('Insert equation');
|
||||
|
||||
/**
|
||||
* @desc Button label for the equation editor dialog for saving
|
||||
* a modified equation.
|
||||
*/
|
||||
var MSG_EE_BUTTON_SAVE_MODIFY = goog.getMsg('Save changes');
|
||||
|
||||
var okButtonText = this.equationEditor_.getEquation() ?
|
||||
MSG_EE_BUTTON_SAVE_MODIFY : MSG_EE_BUTTON_SAVE_NEW;
|
||||
|
||||
builder.setTitle(MSG_EE_DIALOG_TITLE)
|
||||
.setContent(this.equationEditor_.getElement())
|
||||
.addOkButton(okButtonText)
|
||||
.addCancelButton();
|
||||
|
||||
return builder.build();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
goog.ui.editor.EquationEditorDialog.prototype.createOkEvent = function(e) {
|
||||
if (this.equationEditor_.isValid()) {
|
||||
// Equation is not valid, don't close the dialog.
|
||||
return null;
|
||||
}
|
||||
var equationHtml = this.equationEditor_.getHtml();
|
||||
return new goog.ui.editor.EquationEditorOkEvent(equationHtml);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles CHANGE event fired when user changes equation.
|
||||
* @param {goog.ui.equation.ChangeEvent} e The event object.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.EquationEditorDialog.prototype.onChange_ = function(e) {
|
||||
if (!this.okButton_) {
|
||||
this.okButton_ = this.getButtonElement(
|
||||
goog.ui.Dialog.DefaultButtonKeys.OK);
|
||||
}
|
||||
this.okButton_.disabled = !e.isValid;
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright 2011 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.
|
||||
|
||||
goog.provide('goog.ui.editor.EquationEditorOkEvent');
|
||||
|
||||
goog.require('goog.events.Event');
|
||||
goog.require('goog.ui.editor.AbstractDialog');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* OK event object for the equation editor dialog.
|
||||
* @param {string} equationHtml html containing the equation to put in the
|
||||
* editable field.
|
||||
* @constructor
|
||||
* @extends {goog.events.Event}
|
||||
*/
|
||||
goog.ui.editor.EquationEditorOkEvent = function(equationHtml) {
|
||||
this.equationHtml = equationHtml;
|
||||
};
|
||||
goog.inherits(goog.ui.editor.EquationEditorOkEvent,
|
||||
goog.events.Event);
|
||||
|
||||
|
||||
/**
|
||||
* Event type.
|
||||
* @type {goog.ui.editor.AbstractDialog.EventType}
|
||||
* @override
|
||||
*/
|
||||
goog.ui.editor.EquationEditorOkEvent.prototype.type =
|
||||
goog.ui.editor.AbstractDialog.EventType.OK;
|
||||
|
||||
|
||||
/**
|
||||
* HTML containing the equation to put in the editable field.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.editor.EquationEditorOkEvent.prototype.equationHtml;
|
||||
1064
nicer-api-docs/closure-library/closure/goog/ui/editor/linkdialog.js
Normal file
1064
nicer-api-docs/closure-library/closure/goog/ui/editor/linkdialog.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,123 @@
|
||||
// Copyright 2008 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 Messages common to Editor UI components.
|
||||
*
|
||||
* @author robbyw@google.com (Robby Walker)
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.editor.messages');
|
||||
|
||||
|
||||
/** @desc Link button / bubble caption. */
|
||||
goog.ui.editor.messages.MSG_LINK_CAPTION = goog.getMsg('Link');
|
||||
|
||||
|
||||
/** @desc Title for the dialog that edits a link. */
|
||||
goog.ui.editor.messages.MSG_EDIT_LINK = goog.getMsg('Edit Link');
|
||||
|
||||
|
||||
/** @desc Prompt the user for the text of the link they've written. */
|
||||
goog.ui.editor.messages.MSG_TEXT_TO_DISPLAY = goog.getMsg('Text to display:');
|
||||
|
||||
|
||||
/** @desc Prompt the user for the URL of the link they've created. */
|
||||
goog.ui.editor.messages.MSG_LINK_TO = goog.getMsg('Link to:');
|
||||
|
||||
|
||||
/** @desc Prompt the user to type a web address for their link. */
|
||||
goog.ui.editor.messages.MSG_ON_THE_WEB = goog.getMsg('Web address');
|
||||
|
||||
|
||||
/** @desc More details on what linking to a web address involves.. */
|
||||
goog.ui.editor.messages.MSG_ON_THE_WEB_TIP = goog.getMsg(
|
||||
'Link to a page or file somewhere else on the web');
|
||||
|
||||
|
||||
/**
|
||||
* @desc Text for a button that allows the user to test the link that
|
||||
* they created.
|
||||
*/
|
||||
goog.ui.editor.messages.MSG_TEST_THIS_LINK = goog.getMsg('Test this link');
|
||||
|
||||
|
||||
/**
|
||||
* @desc Explanation for how to create a link with the link-editing dialog.
|
||||
*/
|
||||
goog.ui.editor.messages.MSG_TR_LINK_EXPLANATION = goog.getMsg(
|
||||
'{$startBold}Not sure what to put in the box?{$endBold} ' +
|
||||
'First, find the page on the web that you want to ' +
|
||||
'link to. (A {$searchEngineLink}search engine{$endLink} ' +
|
||||
'might be useful.) Then, copy the web address from ' +
|
||||
"the box in your browser's address bar, and paste it into " +
|
||||
'the box above.',
|
||||
{'startBold': '<b>',
|
||||
'endBold': '</b>',
|
||||
'searchEngineLink': "<a href='http://www.google.com/' target='_new'>",
|
||||
'endLink': '</a>'});
|
||||
|
||||
|
||||
/** @desc Prompt for the URL of a link that the user is creating. */
|
||||
goog.ui.editor.messages.MSG_WHAT_URL = goog.getMsg(
|
||||
'To what URL should this link go?');
|
||||
|
||||
|
||||
/**
|
||||
* @desc Prompt for an email address, so that the user can create a link
|
||||
* that sends an email.
|
||||
*/
|
||||
goog.ui.editor.messages.MSG_EMAIL_ADDRESS = goog.getMsg('Email address');
|
||||
|
||||
|
||||
/**
|
||||
* @desc Explanation of the prompt for an email address in a link.
|
||||
*/
|
||||
goog.ui.editor.messages.MSG_EMAIL_ADDRESS_TIP = goog.getMsg(
|
||||
'Link to an email address');
|
||||
|
||||
|
||||
/** @desc Error message when the user enters an invalid email address. */
|
||||
goog.ui.editor.messages.MSG_INVALID_EMAIL = goog.getMsg(
|
||||
'Invalid email address');
|
||||
|
||||
|
||||
/**
|
||||
* @desc When the user creates a mailto link, asks them what email
|
||||
* address clicking on this link will send mail to.
|
||||
*/
|
||||
goog.ui.editor.messages.MSG_WHAT_EMAIL = goog.getMsg(
|
||||
'To what email address should this link?');
|
||||
|
||||
|
||||
/**
|
||||
* @desc Warning about the dangers of creating links with email
|
||||
* addresses in them.
|
||||
*/
|
||||
goog.ui.editor.messages.MSG_EMAIL_EXPLANATION = goog.getMsg(
|
||||
'{$preb}Be careful.{$postb} ' +
|
||||
'Remember that any time you include an email address on a web page, ' +
|
||||
'nasty spammers can find it too.', {'preb': '<b>', 'postb': '</b>'});
|
||||
|
||||
|
||||
/**
|
||||
* @desc Label for the checkbox that allows the user to specify what when this
|
||||
* link is clicked, it should be opened in a new window.
|
||||
*/
|
||||
goog.ui.editor.messages.MSG_OPEN_IN_NEW_WINDOW = goog.getMsg(
|
||||
'Open this link in a new window');
|
||||
|
||||
|
||||
/** @desc Image bubble caption. */
|
||||
goog.ui.editor.messages.MSG_IMAGE_CAPTION = goog.getMsg('Image');
|
||||
198
nicer-api-docs/closure-library/closure/goog/ui/editor/tabpane.js
Normal file
198
nicer-api-docs/closure-library/closure/goog/ui/editor/tabpane.js
Normal file
@@ -0,0 +1,198 @@
|
||||
// Copyright 2010 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 Tabbed pane with style and functionality specific to
|
||||
* Editor dialogs.
|
||||
*
|
||||
* @author robbyw@google.com (Robby Walker)
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.editor.TabPane');
|
||||
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.dom.classes');
|
||||
goog.require('goog.events.EventHandler');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('goog.ui.Control');
|
||||
goog.require('goog.ui.Tab');
|
||||
goog.require('goog.ui.TabBar');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new Editor-style tab pane.
|
||||
* @param {goog.dom.DomHelper} dom The dom helper for the window to create this
|
||||
* tab pane in.
|
||||
* @param {string=} opt_caption Optional caption of the tab pane.
|
||||
* @constructor
|
||||
* @extends {goog.ui.Component}
|
||||
*/
|
||||
goog.ui.editor.TabPane = function(dom, opt_caption) {
|
||||
goog.base(this, dom);
|
||||
|
||||
/**
|
||||
* The event handler used to register events.
|
||||
* @type {goog.events.EventHandler}
|
||||
* @private
|
||||
*/
|
||||
this.eventHandler_ = new goog.events.EventHandler(this);
|
||||
this.registerDisposable(this.eventHandler_);
|
||||
|
||||
/**
|
||||
* The tab bar used to render the tabs.
|
||||
* @type {goog.ui.TabBar}
|
||||
* @private
|
||||
*/
|
||||
this.tabBar_ = new goog.ui.TabBar(goog.ui.TabBar.Location.START,
|
||||
undefined, this.dom_);
|
||||
this.tabBar_.setFocusable(false);
|
||||
|
||||
/**
|
||||
* The content element.
|
||||
* @private
|
||||
*/
|
||||
this.tabContent_ = this.dom_.createDom(goog.dom.TagName.DIV,
|
||||
{className: goog.getCssName('goog-tab-content')});
|
||||
|
||||
/**
|
||||
* The currently selected radio button.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
this.selectedRadio_ = null;
|
||||
|
||||
/**
|
||||
* The currently visible tab content.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
this.visibleContent_ = null;
|
||||
|
||||
|
||||
// Add the caption as the first element in the tab bar.
|
||||
if (opt_caption) {
|
||||
var captionControl = new goog.ui.Control(opt_caption, undefined,
|
||||
this.dom_);
|
||||
captionControl.addClassName(goog.getCssName('tr-tabpane-caption'));
|
||||
captionControl.setEnabled(false);
|
||||
this.tabBar_.addChild(captionControl, true);
|
||||
}
|
||||
};
|
||||
goog.inherits(goog.ui.editor.TabPane, goog.ui.Component);
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} The ID of the content element for the current tab.
|
||||
*/
|
||||
goog.ui.editor.TabPane.prototype.getCurrentTabId = function() {
|
||||
return this.tabBar_.getSelectedTab().getId();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Selects the tab with the given id.
|
||||
* @param {string} id Id of the tab to select.
|
||||
*/
|
||||
goog.ui.editor.TabPane.prototype.setSelectedTabId = function(id) {
|
||||
this.tabBar_.setSelectedTab(this.tabBar_.getChild(id));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds a tab to the tab pane.
|
||||
* @param {string} id The id of the tab to add.
|
||||
* @param {string} caption The caption of the tab.
|
||||
* @param {string} tooltip The tooltip for the tab.
|
||||
* @param {string} groupName for the radio button group.
|
||||
* @param {Element} content The content element to show when this tab is
|
||||
* selected.
|
||||
*/
|
||||
goog.ui.editor.TabPane.prototype.addTab = function(id, caption, tooltip,
|
||||
groupName, content) {
|
||||
var radio = this.dom_.createDom(goog.dom.TagName.INPUT,
|
||||
{
|
||||
name: groupName,
|
||||
type: 'radio'
|
||||
});
|
||||
|
||||
var tab = new goog.ui.Tab([radio, this.dom_.createTextNode(caption)],
|
||||
undefined, this.dom_);
|
||||
tab.setId(id);
|
||||
tab.setTooltip(tooltip);
|
||||
this.tabBar_.addChild(tab, true);
|
||||
|
||||
// When you navigate the radio buttons with TAB and then the Arrow keys on
|
||||
// Chrome and FF, you get a CLICK event on them, and the radio button
|
||||
// is selected. You don't get a SELECT at all. We listen for SELECT
|
||||
// nonetheless because it's possible that some browser will issue only
|
||||
// SELECT.
|
||||
this.eventHandler_.listen(radio,
|
||||
[goog.events.EventType.SELECT, goog.events.EventType.CLICK],
|
||||
goog.bind(this.tabBar_.setSelectedTab, this.tabBar_, tab));
|
||||
|
||||
content.id = id + '-tab';
|
||||
this.tabContent_.appendChild(content);
|
||||
goog.style.setElementShown(content, false);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.editor.TabPane.prototype.enterDocument = function() {
|
||||
goog.base(this, 'enterDocument');
|
||||
|
||||
// Get the root element and add a class name to it.
|
||||
var root = this.getElement();
|
||||
goog.dom.classes.add(root, goog.getCssName('tr-tabpane'));
|
||||
|
||||
// Add the tabs.
|
||||
this.addChild(this.tabBar_, true);
|
||||
this.eventHandler_.listen(this.tabBar_, goog.ui.Component.EventType.SELECT,
|
||||
this.handleTabSelect_);
|
||||
|
||||
// Add the tab content.
|
||||
root.appendChild(this.tabContent_);
|
||||
|
||||
// Add an element to clear the tab float.
|
||||
root.appendChild(
|
||||
this.dom_.createDom(goog.dom.TagName.DIV,
|
||||
{className: goog.getCssName('goog-tab-bar-clear')}));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles a tab change.
|
||||
* @param {goog.events.Event} e The browser change event.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.TabPane.prototype.handleTabSelect_ = function(e) {
|
||||
var tab = /** @type {goog.ui.Tab} */ (e.target);
|
||||
|
||||
// Show the tab content.
|
||||
if (this.visibleContent_) {
|
||||
goog.style.setElementShown(this.visibleContent_, false);
|
||||
}
|
||||
this.visibleContent_ = this.dom_.getElement(tab.getId() + '-tab');
|
||||
goog.style.setElementShown(this.visibleContent_, true);
|
||||
|
||||
// Select the appropriate radio button (and deselect the current one).
|
||||
if (this.selectedRadio_) {
|
||||
this.selectedRadio_.checked = false;
|
||||
}
|
||||
this.selectedRadio_ = tab.getElement().getElementsByTagName(
|
||||
goog.dom.TagName.INPUT)[0];
|
||||
this.selectedRadio_.checked = true;
|
||||
};
|
||||
@@ -0,0 +1,295 @@
|
||||
// Copyright 2008 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 A class for managing the editor toolbar.
|
||||
*
|
||||
* @author attila@google.com (Attila Bodis)
|
||||
* @author jparent@google.com (Julie Parent)
|
||||
* @see ../../demos/editor/editor.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.editor.ToolbarController');
|
||||
|
||||
goog.require('goog.editor.Field');
|
||||
goog.require('goog.events.EventHandler');
|
||||
goog.require('goog.events.EventTarget');
|
||||
goog.require('goog.ui.Component');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A class for managing the editor toolbar. Acts as a bridge between
|
||||
* a {@link goog.editor.Field} and a {@link goog.ui.Toolbar}.
|
||||
*
|
||||
* The {@code toolbar} argument must be an instance of {@link goog.ui.Toolbar}
|
||||
* or a subclass. This class doesn't care how the toolbar was created. As
|
||||
* long as one or more controls hosted in the toolbar have IDs that match
|
||||
* built-in {@link goog.editor.Command}s, they will function as expected. It is
|
||||
* the caller's responsibility to ensure that the toolbar is already rendered
|
||||
* or that it decorates an existing element.
|
||||
*
|
||||
*
|
||||
* @param {!goog.editor.Field} field Editable field to be controlled by the
|
||||
* toolbar.
|
||||
* @param {!goog.ui.Toolbar} toolbar Toolbar to control the editable field.
|
||||
* @constructor
|
||||
* @extends {goog.events.EventTarget}
|
||||
*/
|
||||
goog.ui.editor.ToolbarController = function(field, toolbar) {
|
||||
goog.events.EventTarget.call(this);
|
||||
|
||||
/**
|
||||
* Event handler to listen for field events and user actions.
|
||||
* @type {!goog.events.EventHandler}
|
||||
* @private
|
||||
*/
|
||||
this.handler_ = new goog.events.EventHandler(this);
|
||||
|
||||
/**
|
||||
* The field instance controlled by the toolbar.
|
||||
* @type {!goog.editor.Field}
|
||||
* @private
|
||||
*/
|
||||
this.field_ = field;
|
||||
|
||||
/**
|
||||
* The toolbar that controls the field.
|
||||
* @type {!goog.ui.Toolbar}
|
||||
* @private
|
||||
*/
|
||||
this.toolbar_ = toolbar;
|
||||
|
||||
/**
|
||||
* Editing commands whose state is to be queried when updating the toolbar.
|
||||
* @type {!Array.<string>}
|
||||
* @private
|
||||
*/
|
||||
this.queryCommands_ = [];
|
||||
|
||||
// Iterate over all buttons, and find those which correspond to
|
||||
// queryable commands. Add them to the list of commands to query on
|
||||
// each COMMAND_VALUE_CHANGE event.
|
||||
this.toolbar_.forEachChild(function(button) {
|
||||
if (button.queryable) {
|
||||
this.queryCommands_.push(this.getComponentId(button.getId()));
|
||||
}
|
||||
}, this);
|
||||
|
||||
// Make sure the toolbar doesn't steal keyboard focus.
|
||||
this.toolbar_.setFocusable(false);
|
||||
|
||||
// Hook up handlers that update the toolbar in response to field events,
|
||||
// and to execute editor commands in response to toolbar events.
|
||||
this.handler_.
|
||||
listen(this.field_, goog.editor.Field.EventType.COMMAND_VALUE_CHANGE,
|
||||
this.updateToolbar).
|
||||
listen(this.toolbar_, goog.ui.Component.EventType.ACTION,
|
||||
this.handleAction);
|
||||
};
|
||||
goog.inherits(goog.ui.editor.ToolbarController, goog.events.EventTarget);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the Closure component ID of the control that corresponds to the
|
||||
* given {@link goog.editor.Command} constant.
|
||||
* Subclasses may override this method if they want to use a custom mapping
|
||||
* scheme from commands to controls.
|
||||
* @param {string} command Editor command.
|
||||
* @return {string} Closure component ID of the corresponding toolbar
|
||||
* control, if any.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.ToolbarController.prototype.getComponentId = function(command) {
|
||||
// The default implementation assumes that the component ID is the same as
|
||||
// the command constant.
|
||||
return command;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the {@link goog.editor.Command} constant
|
||||
* that corresponds to the given Closure component ID. Subclasses may override
|
||||
* this method if they want to use a custom mapping scheme from controls to
|
||||
* commands.
|
||||
* @param {string} id Closure component ID of a toolbar control.
|
||||
* @return {string} Editor command or dialog constant corresponding to the
|
||||
* toolbar control, if any.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.ToolbarController.prototype.getCommand = function(id) {
|
||||
// The default implementation assumes that the component ID is the same as
|
||||
// the command constant.
|
||||
return id;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the event handler object for the editor toolbar. Useful for classes
|
||||
* that extend {@code goog.ui.editor.ToolbarController}.
|
||||
* @return {!goog.events.EventHandler} The event handler object.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.ToolbarController.prototype.getHandler = function() {
|
||||
return this.handler_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the field instance managed by the toolbar. Useful for
|
||||
* classes that extend {@code goog.ui.editor.ToolbarController}.
|
||||
* @return {!goog.editor.Field} The field managed by the toolbar.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.ToolbarController.prototype.getField = function() {
|
||||
return this.field_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the toolbar UI component that manages the editor. Useful for
|
||||
* classes that extend {@code goog.ui.editor.ToolbarController}.
|
||||
* @return {!goog.ui.Toolbar} The toolbar UI component.
|
||||
*/
|
||||
goog.ui.editor.ToolbarController.prototype.getToolbar = function() {
|
||||
return this.toolbar_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether the toolbar is visible.
|
||||
*/
|
||||
goog.ui.editor.ToolbarController.prototype.isVisible = function() {
|
||||
return this.toolbar_.isVisible();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Shows or hides the toolbar.
|
||||
* @param {boolean} visible Whether to show or hide the toolbar.
|
||||
*/
|
||||
goog.ui.editor.ToolbarController.prototype.setVisible = function(visible) {
|
||||
this.toolbar_.setVisible(visible);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether the toolbar is enabled.
|
||||
*/
|
||||
goog.ui.editor.ToolbarController.prototype.isEnabled = function() {
|
||||
return this.toolbar_.isEnabled();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Enables or disables the toolbar.
|
||||
* @param {boolean} enabled Whether to enable or disable the toolbar.
|
||||
*/
|
||||
goog.ui.editor.ToolbarController.prototype.setEnabled = function(enabled) {
|
||||
this.toolbar_.setEnabled(enabled);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Programmatically blurs the editor toolbar, un-highlighting the currently
|
||||
* highlighted item, and closing the currently open menu (if any).
|
||||
*/
|
||||
goog.ui.editor.ToolbarController.prototype.blur = function() {
|
||||
// We can't just call this.toolbar_.getElement().blur(), because the toolbar
|
||||
// element itself isn't focusable, so goog.ui.Container#handleBlur isn't
|
||||
// registered to handle blur events.
|
||||
this.toolbar_.handleBlur(null);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.editor.ToolbarController.prototype.disposeInternal = function() {
|
||||
goog.ui.editor.ToolbarController.superClass_.disposeInternal.call(this);
|
||||
if (this.handler_) {
|
||||
this.handler_.dispose();
|
||||
delete this.handler_;
|
||||
}
|
||||
if (this.toolbar_) {
|
||||
this.toolbar_.dispose();
|
||||
delete this.toolbar_;
|
||||
}
|
||||
delete this.field_;
|
||||
delete this.queryCommands_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Updates the toolbar in response to editor events. Specifically, updates
|
||||
* button states based on {@code COMMAND_VALUE_CHANGE} events, reflecting the
|
||||
* effective formatting of the selection.
|
||||
* @param {goog.events.Event} e Editor event to handle.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.ToolbarController.prototype.updateToolbar = function(e) {
|
||||
if (!this.toolbar_.isEnabled() ||
|
||||
!this.dispatchEvent(goog.ui.Component.EventType.CHANGE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var state;
|
||||
|
||||
/** @preserveTry */
|
||||
try {
|
||||
/** @type {Array.<string>} */
|
||||
e.commands; // Added by dispatchEvent.
|
||||
|
||||
// If the COMMAND_VALUE_CHANGE event specifies which commands changed
|
||||
// state, then we only need to update those ones, otherwise update all
|
||||
// commands.
|
||||
state = /** @type {Object} */ (
|
||||
this.field_.queryCommandValue(e.commands || this.queryCommands_));
|
||||
} catch (ex) {
|
||||
// TODO(attila): Find out when/why this happens.
|
||||
state = {};
|
||||
}
|
||||
|
||||
this.updateToolbarFromState(state);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Updates the toolbar to reflect a given state.
|
||||
* @param {Object} state Object mapping editor commands to values.
|
||||
*/
|
||||
goog.ui.editor.ToolbarController.prototype.updateToolbarFromState =
|
||||
function(state) {
|
||||
for (var command in state) {
|
||||
var button = this.toolbar_.getChild(this.getComponentId(command));
|
||||
if (button) {
|
||||
var value = state[command];
|
||||
if (button.updateFromValue) {
|
||||
button.updateFromValue(value);
|
||||
} else {
|
||||
button.setChecked(!!value);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles {@code ACTION} events dispatched by toolbar buttons in response to
|
||||
* user actions by executing the corresponding field command.
|
||||
* @param {goog.events.Event} e Action event to handle.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.editor.ToolbarController.prototype.handleAction = function(e) {
|
||||
var command = this.getCommand(e.target.getId());
|
||||
this.field_.execCommand(command, e.target.getValue());
|
||||
};
|
||||
@@ -0,0 +1,440 @@
|
||||
// Copyright 2008 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 Generic factory functions for creating the building blocks for
|
||||
* an editor toolbar.
|
||||
*
|
||||
* @author attila@google.com (Attila Bodis)
|
||||
* @author jparent@google.com (Julie Parent)
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.editor.ToolbarFactory');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.string');
|
||||
goog.require('goog.string.Unicode');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('goog.ui.Container');
|
||||
goog.require('goog.ui.Option');
|
||||
goog.require('goog.ui.Toolbar');
|
||||
goog.require('goog.ui.ToolbarButton');
|
||||
goog.require('goog.ui.ToolbarColorMenuButton');
|
||||
goog.require('goog.ui.ToolbarMenuButton');
|
||||
goog.require('goog.ui.ToolbarRenderer');
|
||||
goog.require('goog.ui.ToolbarSelect');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
/**
|
||||
* Takes a font spec (e.g. "Arial, Helvetica, sans-serif") and returns the
|
||||
* primary font name, normalized to lowercase (e.g. "arial").
|
||||
* @param {string} fontSpec Font specification.
|
||||
* @return {string} The primary font name, in lowercase.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.getPrimaryFont = function(fontSpec) {
|
||||
var i = fontSpec.indexOf(',');
|
||||
var fontName = (i != -1 ? fontSpec.substring(0, i) : fontSpec).toLowerCase();
|
||||
// Strip leading/trailing quotes from the font name (bug 1050118).
|
||||
return goog.string.stripQuotes(fontName, '"\'');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Bulk-adds fonts to the given font menu button. The argument must be an
|
||||
* array of font descriptor objects, each of which must have the following
|
||||
* attributes:
|
||||
* <ul>
|
||||
* <li>{@code caption} - Caption to show in the font menu (e.g. 'Tahoma')
|
||||
* <li>{@code value} - Value for the corresponding 'font-family' CSS style
|
||||
* (e.g. 'Tahoma, Arial, sans-serif')
|
||||
* </ul>
|
||||
* @param {!goog.ui.Select} button Font menu button.
|
||||
* @param {!Array.<{caption: string, value: string}>} fonts Array of
|
||||
* font descriptors.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.addFonts = function(button, fonts) {
|
||||
goog.array.forEach(fonts, function(font) {
|
||||
goog.ui.editor.ToolbarFactory.addFont(button, font.caption, font.value);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds a menu item to the given font menu button. The first font listed in
|
||||
* the {@code value} argument is considered the font ID, so adding two items
|
||||
* whose CSS style starts with the same font may lead to unpredictable results.
|
||||
* @param {!goog.ui.Select} button Font menu button.
|
||||
* @param {string} caption Caption to show for the font menu.
|
||||
* @param {string} value Value for the corresponding 'font-family' CSS style.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.addFont = function(button, caption, value) {
|
||||
// The font ID is the first font listed in the CSS style, normalized to
|
||||
// lowercase.
|
||||
var id = goog.ui.editor.ToolbarFactory.getPrimaryFont(value);
|
||||
|
||||
// Construct the option, and add it to the button.
|
||||
var option = new goog.ui.Option(caption, value, button.getDomHelper());
|
||||
option.setId(id);
|
||||
button.addItem(option);
|
||||
|
||||
// Captions are shown in their own font.
|
||||
option.getContentElement().style.fontFamily = value;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Bulk-adds font sizes to the given font size menu button. The argument must
|
||||
* be an array of font size descriptor objects, each of which must have the
|
||||
* following attributes:
|
||||
* <ul>
|
||||
* <li>{@code caption} - Caption to show in the font size menu (e.g. 'Huge')
|
||||
* <li>{@code value} - Value for the corresponding HTML font size (e.g. 6)
|
||||
* </ul>
|
||||
* @param {!goog.ui.Select} button Font size menu button.
|
||||
* @param {!Array.<{caption: string, value:number}>} sizes Array of font
|
||||
* size descriptors.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.addFontSizes = function(button, sizes) {
|
||||
goog.array.forEach(sizes, function(size) {
|
||||
goog.ui.editor.ToolbarFactory.addFontSize(button, size.caption, size.value);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds a menu item to the given font size menu button. The {@code value}
|
||||
* argument must be a legacy HTML font size in the 0-7 range.
|
||||
* @param {!goog.ui.Select} button Font size menu button.
|
||||
* @param {string} caption Caption to show in the font size menu.
|
||||
* @param {number} value Value for the corresponding HTML font size.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.addFontSize = function(button, caption, value) {
|
||||
// Construct the option, and add it to the button.
|
||||
var option = new goog.ui.Option(caption, value, button.getDomHelper());
|
||||
button.addItem(option);
|
||||
|
||||
// Adjust the font size of the menu item and the height of the checkbox
|
||||
// element after they've been rendered by addItem(). Captions are shown in
|
||||
// the corresponding font size, and lining up the checkbox is tricky.
|
||||
var content = option.getContentElement();
|
||||
content.style.fontSize =
|
||||
goog.ui.editor.ToolbarFactory.getPxFromLegacySize(value) + 'px';
|
||||
content.firstChild.style.height = '1.1em';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Converts a legacy font size specification into an equivalent pixel size.
|
||||
* For example, {@code <font size="6">} is {@code font-size: 32px;}, etc.
|
||||
* @param {number} fontSize Legacy font size spec in the 0-7 range.
|
||||
* @return {number} Equivalent pixel size.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.getPxFromLegacySize = function(fontSize) {
|
||||
return goog.ui.editor.ToolbarFactory.LEGACY_SIZE_TO_PX_MAP_[fontSize] || 10;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Converts a pixel font size specification into an equivalent legacy size.
|
||||
* For example, {@code font-size: 32px;} is {@code <font size="6">}, etc.
|
||||
* If the given pixel size doesn't exactly match one of the legacy sizes, -1 is
|
||||
* returned.
|
||||
* @param {number} px Pixel font size.
|
||||
* @return {number} Equivalent legacy size spec in the 0-7 range, or -1 if none
|
||||
* exists.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.getLegacySizeFromPx = function(px) {
|
||||
// Use lastIndexOf to get the largest legacy size matching the pixel size
|
||||
// (most notably returning 1 instead of 0 for 10px).
|
||||
return goog.array.lastIndexOf(
|
||||
goog.ui.editor.ToolbarFactory.LEGACY_SIZE_TO_PX_MAP_, px);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Map of legacy font sizes (0-7) to equivalent pixel sizes.
|
||||
* @type {Array.<number>}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.LEGACY_SIZE_TO_PX_MAP_ =
|
||||
[10, 10, 13, 16, 18, 24, 32, 48];
|
||||
|
||||
|
||||
/**
|
||||
* Bulk-adds format options to the given "Format block" menu button. The
|
||||
* argument must be an array of format option descriptor objects, each of
|
||||
* which must have the following attributes:
|
||||
* <ul>
|
||||
* <li>{@code caption} - Caption to show in the menu (e.g. 'Minor heading')
|
||||
* <li>{@code command} - Corresponding {@link goog.dom.TagName} (e.g.
|
||||
* 'H4')
|
||||
* </ul>
|
||||
* @param {!goog.ui.Select} button "Format block" menu button.
|
||||
* @param {!Array.<{caption: string, command: goog.dom.TagName}>} formats Array
|
||||
* of format option descriptors.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.addFormatOptions = function(button, formats) {
|
||||
goog.array.forEach(formats, function(format) {
|
||||
goog.ui.editor.ToolbarFactory.addFormatOption(button, format.caption,
|
||||
format.command);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds a menu item to the given "Format block" menu button.
|
||||
* @param {!goog.ui.Select} button "Format block" menu button.
|
||||
* @param {string} caption Caption to show in the menu.
|
||||
* @param {goog.dom.TagName} tag Corresponding block format tag.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.addFormatOption = function(button, caption, tag) {
|
||||
// Construct the option, and add it to the button.
|
||||
// TODO(attila): Create boring but functional menu item for now...
|
||||
var buttonDom = button.getDomHelper();
|
||||
var option = new goog.ui.Option(buttonDom.createDom(goog.dom.TagName.DIV,
|
||||
null, caption), tag, buttonDom);
|
||||
option.setId(tag);
|
||||
button.addItem(option);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a {@link goog.ui.Toolbar} containing the specified set of
|
||||
* toolbar buttons, and renders it into the given parent element. Each
|
||||
* item in the {@code items} array must a {@link goog.ui.Control}.
|
||||
* @param {!Array.<goog.ui.Control>} items Toolbar items; each must
|
||||
* be a {@link goog.ui.Control}.
|
||||
* @param {!Element} elem Toolbar parent element.
|
||||
* @param {boolean=} opt_isRightToLeft Whether the editor chrome is
|
||||
* right-to-left; defaults to the directionality of the toolbar parent
|
||||
* element.
|
||||
* @return {!goog.ui.Toolbar} Editor toolbar, rendered into the given parent
|
||||
* element.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.makeToolbar = function(items, elem,
|
||||
opt_isRightToLeft) {
|
||||
var domHelper = goog.dom.getDomHelper(elem);
|
||||
|
||||
// Create an empty horizontal toolbar using the default renderer.
|
||||
var toolbar = new goog.ui.Toolbar(goog.ui.ToolbarRenderer.getInstance(),
|
||||
goog.ui.Container.Orientation.HORIZONTAL, domHelper);
|
||||
|
||||
// Optimization: Explicitly test for the directionality of the parent
|
||||
// element here, so we can set it for both the toolbar and its children,
|
||||
// saving a lot of expensive calls to goog.style.isRightToLeft() during
|
||||
// rendering.
|
||||
var isRightToLeft = opt_isRightToLeft || goog.style.isRightToLeft(elem);
|
||||
toolbar.setRightToLeft(isRightToLeft);
|
||||
|
||||
// Optimization: Set the toolbar to non-focusable before it is rendered,
|
||||
// to avoid creating unnecessary keyboard event handler objects.
|
||||
toolbar.setFocusable(false);
|
||||
|
||||
for (var i = 0, button; button = items[i]; i++) {
|
||||
// Optimization: Set the button to non-focusable before it is rendered,
|
||||
// to avoid creating unnecessary keyboard event handler objects. Also set
|
||||
// the directionality of the button explicitly, to avoid expensive calls
|
||||
// to goog.style.isRightToLeft() during rendering.
|
||||
button.setSupportedState(goog.ui.Component.State.FOCUSED, false);
|
||||
button.setRightToLeft(isRightToLeft);
|
||||
toolbar.addChild(button, true);
|
||||
}
|
||||
|
||||
toolbar.render(elem);
|
||||
return toolbar;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a toolbar button with the given ID, tooltip, and caption. Applies
|
||||
* any custom CSS class names to the button's caption element.
|
||||
* @param {string} id Button ID; must equal a {@link goog.editor.Command} for
|
||||
* built-in buttons, anything else for custom buttons.
|
||||
* @param {string} tooltip Tooltip to be shown on hover.
|
||||
* @param {goog.ui.ControlContent} caption Button caption.
|
||||
* @param {string=} opt_classNames CSS class name(s) to apply to the caption
|
||||
* element.
|
||||
* @param {goog.ui.ButtonRenderer=} opt_renderer Button renderer; defaults to
|
||||
* {@link goog.ui.ToolbarButtonRenderer} if unspecified.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
|
||||
* creation; defaults to the current document if unspecified.
|
||||
* @return {!goog.ui.Button} A toolbar button.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.makeButton = function(id, tooltip, caption,
|
||||
opt_classNames, opt_renderer, opt_domHelper) {
|
||||
var button = new goog.ui.ToolbarButton(
|
||||
goog.ui.editor.ToolbarFactory.createContent_(caption, opt_classNames,
|
||||
opt_domHelper),
|
||||
opt_renderer,
|
||||
opt_domHelper);
|
||||
button.setId(id);
|
||||
button.setTooltip(tooltip);
|
||||
return button;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a toggle button with the given ID, tooltip, and caption. Applies
|
||||
* any custom CSS class names to the button's caption element. The button
|
||||
* returned has checkbox-like toggle semantics.
|
||||
* @param {string} id Button ID; must equal a {@link goog.editor.Command} for
|
||||
* built-in buttons, anything else for custom buttons.
|
||||
* @param {string} tooltip Tooltip to be shown on hover.
|
||||
* @param {goog.ui.ControlContent} caption Button caption.
|
||||
* @param {string=} opt_classNames CSS class name(s) to apply to the caption
|
||||
* element.
|
||||
* @param {goog.ui.ButtonRenderer=} opt_renderer Button renderer; defaults to
|
||||
* {@link goog.ui.ToolbarButtonRenderer} if unspecified.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
|
||||
* creation; defaults to the current document if unspecified.
|
||||
* @return {!goog.ui.Button} A toggle button.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.makeToggleButton = function(id, tooltip, caption,
|
||||
opt_classNames, opt_renderer, opt_domHelper) {
|
||||
var button = goog.ui.editor.ToolbarFactory.makeButton(id, tooltip, caption,
|
||||
opt_classNames, opt_renderer, opt_domHelper);
|
||||
button.setSupportedState(goog.ui.Component.State.CHECKED, true);
|
||||
return button;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a menu button with the given ID, tooltip, and caption. Applies
|
||||
* any custom CSS class names to the button's caption element. The button
|
||||
* returned doesn't have an actual menu attached; use {@link
|
||||
* goog.ui.MenuButton#setMenu} to attach a {@link goog.ui.Menu} to the
|
||||
* button.
|
||||
* @param {string} id Button ID; must equal a {@link goog.editor.Command} for
|
||||
* built-in buttons, anything else for custom buttons.
|
||||
* @param {string} tooltip Tooltip to be shown on hover.
|
||||
* @param {goog.ui.ControlContent} caption Button caption.
|
||||
* @param {string=} opt_classNames CSS class name(s) to apply to the caption
|
||||
* element.
|
||||
* @param {goog.ui.ButtonRenderer=} opt_renderer Button renderer; defaults to
|
||||
* {@link goog.ui.ToolbarMenuButtonRenderer} if unspecified.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
|
||||
* creation; defaults to the current document if unspecified.
|
||||
* @return {!goog.ui.MenuButton} A menu button.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.makeMenuButton = function(id, tooltip, caption,
|
||||
opt_classNames, opt_renderer, opt_domHelper) {
|
||||
var button = new goog.ui.ToolbarMenuButton(
|
||||
goog.ui.editor.ToolbarFactory.createContent_(caption, opt_classNames,
|
||||
opt_domHelper),
|
||||
null,
|
||||
opt_renderer,
|
||||
opt_domHelper);
|
||||
button.setId(id);
|
||||
button.setTooltip(tooltip);
|
||||
return button;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a select button with the given ID, tooltip, and caption. Applies
|
||||
* any custom CSS class names to the button's root element. The button
|
||||
* returned doesn't have an actual menu attached; use {@link
|
||||
* goog.ui.Select#setMenu} to attach a {@link goog.ui.Menu} containing
|
||||
* {@link goog.ui.Option}s to the select button.
|
||||
* @param {string} id Button ID; must equal a {@link goog.editor.Command} for
|
||||
* built-in buttons, anything else for custom buttons.
|
||||
* @param {string} tooltip Tooltip to be shown on hover.
|
||||
* @param {goog.ui.ControlContent} caption Button caption; used as the
|
||||
* default caption when nothing is selected.
|
||||
* @param {string=} opt_classNames CSS class name(s) to apply to the button's
|
||||
* root element.
|
||||
* @param {goog.ui.MenuButtonRenderer=} opt_renderer Button renderer;
|
||||
* defaults to {@link goog.ui.ToolbarMenuButtonRenderer} if unspecified.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
|
||||
* creation; defaults to the current document if unspecified.
|
||||
* @return {!goog.ui.Select} A select button.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.makeSelectButton = function(id, tooltip, caption,
|
||||
opt_classNames, opt_renderer, opt_domHelper) {
|
||||
var button = new goog.ui.ToolbarSelect(null, null,
|
||||
opt_renderer,
|
||||
opt_domHelper);
|
||||
if (opt_classNames) {
|
||||
// Unlike the other button types, for goog.ui.Select buttons we apply the
|
||||
// extra class names to the root element, because for select buttons the
|
||||
// caption isn't stable (as it changes each time the selection changes).
|
||||
goog.array.forEach(opt_classNames.split(/\s+/), button.addClassName,
|
||||
button);
|
||||
}
|
||||
button.addClassName(goog.getCssName('goog-toolbar-select'));
|
||||
button.setDefaultCaption(caption);
|
||||
button.setId(id);
|
||||
button.setTooltip(tooltip);
|
||||
return button;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a color menu button with the given ID, tooltip, and caption.
|
||||
* Applies any custom CSS class names to the button's caption element. The
|
||||
* button is created with a default color menu containing standard color
|
||||
* palettes.
|
||||
* @param {string} id Button ID; must equal a {@link goog.editor.Command} for
|
||||
* built-in toolbar buttons, but can be anything else for custom buttons.
|
||||
* @param {string} tooltip Tooltip to be shown on hover.
|
||||
* @param {goog.ui.ControlContent} caption Button caption.
|
||||
* @param {string=} opt_classNames CSS class name(s) to apply to the caption
|
||||
* element.
|
||||
* @param {goog.ui.ColorMenuButtonRenderer=} opt_renderer Button renderer;
|
||||
* defaults to {@link goog.ui.ToolbarColorMenuButtonRenderer}
|
||||
* if unspecified.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
|
||||
* creation; defaults to the current document if unspecified.
|
||||
* @return {!goog.ui.ColorMenuButton} A color menu button.
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.makeColorMenuButton = function(id, tooltip,
|
||||
caption, opt_classNames, opt_renderer, opt_domHelper) {
|
||||
var button = new goog.ui.ToolbarColorMenuButton(
|
||||
goog.ui.editor.ToolbarFactory.createContent_(caption, opt_classNames,
|
||||
opt_domHelper),
|
||||
null,
|
||||
opt_renderer,
|
||||
opt_domHelper);
|
||||
button.setId(id);
|
||||
button.setTooltip(tooltip);
|
||||
return button;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new DIV that wraps a button caption, optionally applying CSS
|
||||
* class names to it. Used as a helper function in button factory methods.
|
||||
* @param {goog.ui.ControlContent} caption Button caption.
|
||||
* @param {string=} opt_classNames CSS class name(s) to apply to the DIV that
|
||||
* wraps the caption (if any).
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper DOM helper, used for DOM
|
||||
* creation; defaults to the current document if unspecified.
|
||||
* @return {!Element} DIV that wraps the caption.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.editor.ToolbarFactory.createContent_ = function(caption, opt_classNames,
|
||||
opt_domHelper) {
|
||||
// FF2 doesn't like empty DIVs, especially when rendered right-to-left.
|
||||
if ((!caption || caption == '') && goog.userAgent.GECKO &&
|
||||
!goog.userAgent.isVersionOrHigher('1.9a')) {
|
||||
caption = goog.string.Unicode.NBSP;
|
||||
}
|
||||
return (opt_domHelper || goog.dom.getDomHelper()).createDom(
|
||||
goog.dom.TagName.DIV,
|
||||
opt_classNames ? {'class' : opt_classNames} : null, caption);
|
||||
};
|
||||
@@ -0,0 +1,72 @@
|
||||
// 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 Emoji implementation.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.emoji.Emoji');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates an emoji.
|
||||
*
|
||||
* A simple wrapper for an emoji.
|
||||
*
|
||||
* @param {string} url URL pointing to the source image for the emoji.
|
||||
* @param {string} id The id of the emoji, e.g., 'std.1'.
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.emoji.Emoji = function(url, id) {
|
||||
/**
|
||||
* The URL pointing to the source image for the emoji
|
||||
*
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.url_ = url;
|
||||
|
||||
/**
|
||||
* The id of the emoji
|
||||
*
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.id_ = id;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The name of the goomoji attribute, used for emoji image elements.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.emoji.Emoji.ATTRIBUTE = 'goomoji';
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} The URL for this emoji.
|
||||
*/
|
||||
goog.ui.emoji.Emoji.prototype.getUrl = function() {
|
||||
return this.url_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} The id of this emoji.
|
||||
*/
|
||||
goog.ui.emoji.Emoji.prototype.getId = function() {
|
||||
return this.id_;
|
||||
};
|
||||
@@ -0,0 +1,288 @@
|
||||
// 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 Emoji Palette implementation. This provides a UI widget for
|
||||
* choosing an emoji from a palette of possible choices. EmojiPalettes are
|
||||
* contained within EmojiPickers.
|
||||
*
|
||||
* See ../demos/popupemojipicker.html for an example of how to instantiate
|
||||
* an emoji picker.
|
||||
*
|
||||
* Based on goog.ui.ColorPicker (colorpicker.js).
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.emoji.EmojiPalette');
|
||||
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.net.ImageLoader');
|
||||
goog.require('goog.ui.Palette');
|
||||
goog.require('goog.ui.emoji.Emoji');
|
||||
goog.require('goog.ui.emoji.EmojiPaletteRenderer');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A page of emoji to be displayed in an EmojiPicker.
|
||||
*
|
||||
* @param {Array.<Array>} emoji List of emoji for this page.
|
||||
* @param {?string=} opt_urlPrefix Prefix that should be prepended to all URL.
|
||||
* @param {goog.ui.PaletteRenderer=} opt_renderer Renderer used to render or
|
||||
* decorate the palette; defaults to {@link goog.ui.PaletteRenderer}.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
|
||||
* @extends {goog.ui.Palette}
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.emoji.EmojiPalette = function(emoji,
|
||||
opt_urlPrefix,
|
||||
opt_renderer,
|
||||
opt_domHelper) {
|
||||
goog.ui.Palette.call(this,
|
||||
null,
|
||||
opt_renderer ||
|
||||
new goog.ui.emoji.EmojiPaletteRenderer(null),
|
||||
opt_domHelper);
|
||||
/**
|
||||
* All the different emoji that this palette can display. Maps emoji ids
|
||||
* (string) to the goog.ui.emoji.Emoji for that id.
|
||||
*
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
this.emojiCells_ = {};
|
||||
|
||||
/**
|
||||
* Map of emoji id to index into this.emojiCells_.
|
||||
*
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
this.emojiMap_ = {};
|
||||
|
||||
/**
|
||||
* List of the animated emoji in this palette. Each internal array is of type
|
||||
* [HTMLDivElement, goog.ui.emoji.Emoji], and represents the palette item
|
||||
* for that animated emoji, and the Emoji object.
|
||||
*
|
||||
* @type {Array.<Array.<(HTMLDivElement|goog.ui.emoji.Emoji)>>}
|
||||
* @private
|
||||
*/
|
||||
this.animatedEmoji_ = [];
|
||||
|
||||
this.urlPrefix_ = opt_urlPrefix || '';
|
||||
|
||||
/**
|
||||
* Palette items that are displayed on this page of the emoji picker. Each
|
||||
* item is a div wrapped around a div or an img.
|
||||
*
|
||||
* @type {Array.<HTMLDivElement>}
|
||||
* @private
|
||||
*/
|
||||
this.emoji_ = this.getEmojiArrayFromProperties_(emoji);
|
||||
|
||||
this.setContent(this.emoji_);
|
||||
};
|
||||
goog.inherits(goog.ui.emoji.EmojiPalette, goog.ui.Palette);
|
||||
|
||||
|
||||
/**
|
||||
* Indicates a prefix that should be prepended to all URLs of images in this
|
||||
* emojipalette. This provides an optimization if the URLs are long, so that
|
||||
* the client does not have to send a long string for each emoji.
|
||||
*
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPalette.prototype.urlPrefix_ = '';
|
||||
|
||||
|
||||
/**
|
||||
* Whether the emoji images have been loaded.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPalette.prototype.imagesLoaded_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* Image loader for loading animated emoji.
|
||||
*
|
||||
* @type {goog.net.ImageLoader}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPalette.prototype.imageLoader_;
|
||||
|
||||
|
||||
/**
|
||||
* Helps create an array of emoji palette items from an array of emoji
|
||||
* properties. Each element will be either a div with background-image set to
|
||||
* a sprite, or an img element pointing directly to an emoji, and all elements
|
||||
* are wrapped with an outer div for alignment issues (i.e., this allows
|
||||
* centering the inner div).
|
||||
*
|
||||
* @param {Object} emojiGroup The group of emoji for this page.
|
||||
* @return {Array.<HTMLDivElement>} The emoji items.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPalette.prototype.getEmojiArrayFromProperties_ =
|
||||
function(emojiGroup) {
|
||||
var emojiItems = [];
|
||||
|
||||
for (var i = 0; i < emojiGroup.length; i++) {
|
||||
var url = emojiGroup[i][0];
|
||||
var id = emojiGroup[i][1];
|
||||
var spriteInfo = emojiGroup[i][2];
|
||||
var displayUrl = spriteInfo ? spriteInfo.getUrl() :
|
||||
this.urlPrefix_ + url;
|
||||
|
||||
var item = this.getRenderer().createPaletteItem(
|
||||
this.getDomHelper(), id, spriteInfo, displayUrl);
|
||||
emojiItems.push(item);
|
||||
|
||||
var emoji = new goog.ui.emoji.Emoji(url, id);
|
||||
this.emojiCells_[id] = emoji;
|
||||
this.emojiMap_[id] = i;
|
||||
|
||||
// Keep track of sprited emoji that are animated, for later loading.
|
||||
if (spriteInfo && spriteInfo.isAnimated()) {
|
||||
this.animatedEmoji_.push([item, emoji]);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the image loader now so that tests can access it before it has
|
||||
// started loading images.
|
||||
if (this.animatedEmoji_.length > 0) {
|
||||
this.imageLoader_ = new goog.net.ImageLoader();
|
||||
}
|
||||
|
||||
this.imagesLoaded_ = true;
|
||||
return emojiItems;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sends off requests for all the animated emoji and replaces their static
|
||||
* sprites when the images are done downloading.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPalette.prototype.loadAnimatedEmoji = function() {
|
||||
if (this.animatedEmoji_.length > 0) {
|
||||
for (var i = 0; i < this.animatedEmoji_.length; i++) {
|
||||
var paletteItem = /** @type {Element} */ (this.animatedEmoji_[i][0]);
|
||||
var emoji =
|
||||
/** @type {goog.ui.emoji.Emoji} */ (this.animatedEmoji_[i][1]);
|
||||
var url = this.urlPrefix_ + emoji.getUrl();
|
||||
|
||||
this.imageLoader_.addImage(emoji.getId(), url);
|
||||
}
|
||||
|
||||
this.getHandler().listen(this.imageLoader_, goog.events.EventType.LOAD,
|
||||
this.handleImageLoad_);
|
||||
this.imageLoader_.start();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles image load events from the ImageLoader.
|
||||
*
|
||||
* @param {goog.events.Event} e The event object.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPalette.prototype.handleImageLoad_ = function(e) {
|
||||
var id = e.target.id;
|
||||
var url = e.target.src;
|
||||
// Just to be safe, we check to make sure we have an id and src url from
|
||||
// the event target, which the ImageLoader sets to an Image object.
|
||||
if (id && url) {
|
||||
var item = this.emoji_[this.emojiMap_[id]];
|
||||
if (item) {
|
||||
this.getRenderer().updateAnimatedPaletteItem(item, e.target);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the image loader that this palette uses. Used for testing.
|
||||
*
|
||||
* @return {goog.net.ImageLoader} the image loader.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPalette.prototype.getImageLoader = function() {
|
||||
return this.imageLoader_;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.emoji.EmojiPalette.prototype.disposeInternal = function() {
|
||||
goog.ui.emoji.EmojiPalette.superClass_.disposeInternal.call(this);
|
||||
|
||||
if (this.imageLoader_) {
|
||||
this.imageLoader_.dispose();
|
||||
this.imageLoader_ = null;
|
||||
}
|
||||
this.animatedEmoji_ = null;
|
||||
this.emojiCells_ = null;
|
||||
this.emojiMap_ = null;
|
||||
this.emoji_ = null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns a goomoji id from an img or the containing td, or null if none
|
||||
* exists for that element.
|
||||
*
|
||||
* @param {Element} el The element to get the Goomoji id from.
|
||||
* @return {?string} A goomoji id from an img or the containing td, or null if
|
||||
* none exists for that element.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPalette.prototype.getGoomojiIdFromElement_ = function(el) {
|
||||
if (!el) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var item = this.getRenderer().getContainingItem(this, el);
|
||||
return item ? item.getAttribute(goog.ui.emoji.Emoji.ATTRIBUTE) : null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {goog.ui.emoji.Emoji} The currently selected emoji from this palette.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPalette.prototype.getSelectedEmoji = function() {
|
||||
var elem = /** @type {Element} */ (this.getSelectedItem());
|
||||
var goomojiId = this.getGoomojiIdFromElement_(elem);
|
||||
return this.emojiCells_[goomojiId];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {number} The number of emoji managed by this palette.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPalette.prototype.getNumberOfEmoji = function() {
|
||||
return this.emojiCells_.length;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the index of the specified emoji within this palette.
|
||||
*
|
||||
* @param {string} id Id of the emoji to look up.
|
||||
* @return {number} The index of the specified emoji within this palette.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPalette.prototype.getEmojiIndex = function(id) {
|
||||
return this.emojiMap_[id];
|
||||
};
|
||||
@@ -0,0 +1,208 @@
|
||||
// Copyright 2008 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 Emoji Palette renderer implementation.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.emoji.EmojiPaletteRenderer');
|
||||
|
||||
goog.require('goog.a11y.aria');
|
||||
goog.require('goog.dom.NodeType');
|
||||
goog.require('goog.dom.classes');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.PaletteRenderer');
|
||||
goog.require('goog.ui.emoji.Emoji');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Renders an emoji palette.
|
||||
*
|
||||
* @param {?string} defaultImgUrl Url of the img that should be used to fill up
|
||||
* the cells in the emoji table, to prevent jittering. Will be stretched
|
||||
* to the emoji cell size. A good image is a transparent dot.
|
||||
* @constructor
|
||||
* @extends {goog.ui.PaletteRenderer}
|
||||
*/
|
||||
goog.ui.emoji.EmojiPaletteRenderer = function(defaultImgUrl) {
|
||||
goog.ui.PaletteRenderer.call(this);
|
||||
|
||||
this.defaultImgUrl_ = defaultImgUrl;
|
||||
};
|
||||
goog.inherits(goog.ui.emoji.EmojiPaletteRenderer, goog.ui.PaletteRenderer);
|
||||
|
||||
|
||||
/**
|
||||
* Globally unique ID sequence for cells rendered by this renderer class.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPaletteRenderer.cellId_ = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Url of the img that should be used for cells in the emoji palette that are
|
||||
* not filled with emoji, i.e., after all the emoji have already been placed
|
||||
* on a page.
|
||||
*
|
||||
* @type {?string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPaletteRenderer.prototype.defaultImgUrl_ = null;
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.emoji.EmojiPaletteRenderer.getCssClass = function() {
|
||||
return goog.getCssName('goog-ui-emojipalette');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a palette item from the given emoji data.
|
||||
*
|
||||
* @param {goog.dom.DomHelper} dom DOM helper for constructing DOM elements.
|
||||
* @param {string} id Goomoji id for the emoji.
|
||||
* @param {goog.ui.emoji.SpriteInfo} spriteInfo Spriting info for the emoji.
|
||||
* @param {string} displayUrl URL of the image served for this cell, whether
|
||||
* an individual emoji image or a sprite.
|
||||
* @return {HTMLDivElement} The palette item for this emoji.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPaletteRenderer.prototype.createPaletteItem =
|
||||
function(dom, id, spriteInfo, displayUrl) {
|
||||
var el;
|
||||
|
||||
if (spriteInfo) {
|
||||
var cssClass = spriteInfo.getCssClass();
|
||||
if (cssClass) {
|
||||
el = dom.createDom('div', cssClass);
|
||||
} else {
|
||||
el = this.buildElementFromSpriteMetadata(dom, spriteInfo, displayUrl);
|
||||
}
|
||||
} else {
|
||||
el = dom.createDom('img', {'src': displayUrl});
|
||||
}
|
||||
|
||||
var outerdiv =
|
||||
dom.createDom('div', goog.getCssName('goog-palette-cell-wrapper'), el);
|
||||
outerdiv.setAttribute(goog.ui.emoji.Emoji.ATTRIBUTE, id);
|
||||
return /** @type {HTMLDivElement} */ (outerdiv);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Modifies a palette item containing an animated emoji, in response to the
|
||||
* animated emoji being successfully downloaded.
|
||||
*
|
||||
* @param {Element} item The palette item to update.
|
||||
* @param {Image} animatedImg An Image object containing the animated emoji.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPaletteRenderer.prototype.updateAnimatedPaletteItem =
|
||||
function(item, animatedImg) {
|
||||
// An animated emoji is one that had sprite info for a static version and is
|
||||
// now being updated. See createPaletteItem for the structure of the palette
|
||||
// items we're modifying.
|
||||
|
||||
var inner = /** @type {Element} */ (item.firstChild);
|
||||
|
||||
// The first case is a palette item with a CSS class representing the sprite,
|
||||
// and an animated emoji.
|
||||
var classes = goog.dom.classes.get(inner);
|
||||
if (classes && classes.length == 1) {
|
||||
inner.className = '';
|
||||
}
|
||||
|
||||
goog.style.setStyle(inner, {
|
||||
'width': animatedImg.width,
|
||||
'height': animatedImg.height,
|
||||
'background-image': 'url(' + animatedImg.src + ')',
|
||||
'background-position': '0 0'
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Builds the inner contents of a palette item out of sprite metadata.
|
||||
*
|
||||
* @param {goog.dom.DomHelper} dom DOM helper for constructing DOM elements.
|
||||
* @param {goog.ui.emoji.SpriteInfo} spriteInfo The metadata to create the css
|
||||
* for the sprite.
|
||||
* @param {string} displayUrl The URL of the image for this cell.
|
||||
* @return {HTMLDivElement} The inner element for a palette item.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPaletteRenderer.prototype.buildElementFromSpriteMetadata =
|
||||
function(dom, spriteInfo, displayUrl) {
|
||||
var width = spriteInfo.getWidthCssValue();
|
||||
var height = spriteInfo.getHeightCssValue();
|
||||
var x = spriteInfo.getXOffsetCssValue();
|
||||
var y = spriteInfo.getYOffsetCssValue();
|
||||
|
||||
var el = dom.createDom('div');
|
||||
goog.style.setStyle(el, {
|
||||
'width': width,
|
||||
'height': height,
|
||||
'background-image': 'url(' + displayUrl + ')',
|
||||
'background-repeat': 'no-repeat',
|
||||
'background-position': x + ' ' + y
|
||||
});
|
||||
|
||||
return /** @type {HTMLDivElement} */ (el);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.emoji.EmojiPaletteRenderer.prototype.createCell = function(node, dom) {
|
||||
// Create a cell with the default img if we're out of items, in order to
|
||||
// prevent jitter in the table. If there's no default img url, just create an
|
||||
// empty div, to prevent trying to fetch a null url.
|
||||
if (!node) {
|
||||
var elem = this.defaultImgUrl_ ?
|
||||
dom.createDom('img', {'src': this.defaultImgUrl_}) :
|
||||
dom.createDom('div');
|
||||
node = dom.createDom('div', goog.getCssName('goog-palette-cell-wrapper'),
|
||||
elem);
|
||||
}
|
||||
|
||||
var cell = dom.createDom('td', {
|
||||
'class': goog.getCssName(this.getCssClass(), 'cell'),
|
||||
// Cells must have an ID, for accessibility, so we generate one here.
|
||||
'id': this.getCssClass() + '-cell-' +
|
||||
goog.ui.emoji.EmojiPaletteRenderer.cellId_++
|
||||
}, node);
|
||||
goog.a11y.aria.setRole(cell, 'gridcell');
|
||||
return cell;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the item corresponding to the given node, or null if the node is
|
||||
* neither a palette cell nor part of a palette item.
|
||||
* @param {goog.ui.Palette} palette Palette in which to look for the item.
|
||||
* @param {Node} node Node to look for.
|
||||
* @return {Node} The corresponding palette item (null if not found).
|
||||
* @override
|
||||
*/
|
||||
goog.ui.emoji.EmojiPaletteRenderer.prototype.getContainingItem =
|
||||
function(palette, node) {
|
||||
var root = palette.getElement();
|
||||
while (node && node.nodeType == goog.dom.NodeType.ELEMENT && node != root) {
|
||||
if (node.tagName == 'TD') {
|
||||
return node.firstChild;
|
||||
}
|
||||
node = node.parentNode;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
@@ -0,0 +1,804 @@
|
||||
// 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 Emoji Picker implementation. This provides a UI widget for
|
||||
* choosing an emoji from a grid of possible choices.
|
||||
*
|
||||
* @see ../demos/popupemojipicker.html for an example of how to instantiate
|
||||
* an emoji picker.
|
||||
*
|
||||
* Based on goog.ui.ColorPicker (colorpicker.js).
|
||||
*
|
||||
* @see ../../demos/popupemojipicker.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.emoji.EmojiPicker');
|
||||
|
||||
goog.require('goog.log');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('goog.ui.TabPane');
|
||||
goog.require('goog.ui.emoji.Emoji');
|
||||
goog.require('goog.ui.emoji.EmojiPalette');
|
||||
goog.require('goog.ui.emoji.EmojiPaletteRenderer');
|
||||
goog.require('goog.ui.emoji.ProgressiveEmojiPaletteRenderer');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new, empty emoji picker. An emoji picker is a grid of emoji, each
|
||||
* cell of the grid containing a single emoji. The picker may contain multiple
|
||||
* pages of emoji.
|
||||
*
|
||||
* When a user selects an emoji, by either clicking or pressing enter, the
|
||||
* picker fires a goog.ui.Component.EventType.ACTION event with the id. The
|
||||
* client listens on this event and in the handler can retrieve the id of the
|
||||
* selected emoji and do something with it, for instance, inserting an image
|
||||
* tag into a rich text control. An emoji picker does not maintain state. That
|
||||
* is, once an emoji is selected, the emoji picker does not remember which emoji
|
||||
* was selected.
|
||||
*
|
||||
* The emoji picker is implemented as a tabpane with each tabpage being a table.
|
||||
* Each of the tables are the same size to prevent jittering when switching
|
||||
* between pages.
|
||||
*
|
||||
* @param {string} defaultImgUrl Url of the img that should be used to fill up
|
||||
* the cells in the emoji table, to prevent jittering. Should be the same
|
||||
* size as the emoji.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
|
||||
* @extends {goog.ui.Component}
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker = function(defaultImgUrl, opt_domHelper) {
|
||||
goog.ui.Component.call(this, opt_domHelper);
|
||||
|
||||
this.defaultImgUrl_ = defaultImgUrl;
|
||||
|
||||
/**
|
||||
* Emoji that this picker displays.
|
||||
*
|
||||
* @type {Array.<Object>}
|
||||
* @private
|
||||
*/
|
||||
this.emoji_ = [];
|
||||
|
||||
/**
|
||||
* Pages of this emoji picker.
|
||||
*
|
||||
* @type {Array.<goog.ui.emoji.EmojiPalette>}
|
||||
* @private
|
||||
*/
|
||||
this.pages_ = [];
|
||||
|
||||
/**
|
||||
* Keeps track of which pages in the picker have been loaded. Used for delayed
|
||||
* loading of tabs.
|
||||
*
|
||||
* @type {Array.<boolean>}
|
||||
* @private
|
||||
*/
|
||||
this.pageLoadStatus_ = [];
|
||||
|
||||
/**
|
||||
* Tabpane to hold the pages of this emojipicker.
|
||||
*
|
||||
* @type {goog.ui.TabPane}
|
||||
* @private
|
||||
*/
|
||||
this.tabPane_ = null;
|
||||
|
||||
this.getHandler().listen(this, goog.ui.Component.EventType.ACTION,
|
||||
this.onEmojiPaletteAction_);
|
||||
};
|
||||
goog.inherits(goog.ui.emoji.EmojiPicker, goog.ui.Component);
|
||||
|
||||
|
||||
/**
|
||||
* Default number of rows per grid of emoji.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.DEFAULT_NUM_ROWS = 5;
|
||||
|
||||
|
||||
/**
|
||||
* Default number of columns per grid of emoji.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.DEFAULT_NUM_COLS = 10;
|
||||
|
||||
|
||||
/**
|
||||
* Default location of the tabs in relation to the emoji grids.
|
||||
*
|
||||
* @type {goog.ui.TabPane.TabLocation}
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.DEFAULT_TAB_LOCATION =
|
||||
goog.ui.TabPane.TabLocation.TOP;
|
||||
|
||||
|
||||
/**
|
||||
* Number of rows per grid of emoji.
|
||||
*
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.numRows_ =
|
||||
goog.ui.emoji.EmojiPicker.DEFAULT_NUM_ROWS;
|
||||
|
||||
|
||||
/**
|
||||
* Number of columns per grid of emoji.
|
||||
*
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.numCols_ =
|
||||
goog.ui.emoji.EmojiPicker.DEFAULT_NUM_COLS;
|
||||
|
||||
|
||||
/**
|
||||
* Whether the number of rows in the picker should be automatically determined
|
||||
* by the specified number of columns so as to minimize/eliminate jitter when
|
||||
* switching between tabs.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.autoSizeByColumnCount_ = true;
|
||||
|
||||
|
||||
/**
|
||||
* Location of the tabs for the picker tabpane.
|
||||
*
|
||||
* @type {goog.ui.TabPane.TabLocation}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.tabLocation_ =
|
||||
goog.ui.emoji.EmojiPicker.DEFAULT_TAB_LOCATION;
|
||||
|
||||
|
||||
/**
|
||||
* Whether the component is focusable.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.focusable_ = true;
|
||||
|
||||
|
||||
/**
|
||||
* Url of the img that should be used for cells in the emoji picker that are
|
||||
* not filled with emoji, i.e., after all the emoji have already been placed
|
||||
* on a page.
|
||||
*
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.defaultImgUrl_;
|
||||
|
||||
|
||||
/**
|
||||
* If present, indicates a prefix that should be prepended to all URLs
|
||||
* of images in this emojipicker. This provides an optimization if the URLs
|
||||
* are long, so that the client does not have to send a long string for each
|
||||
* emoji.
|
||||
*
|
||||
* @type {string|undefined}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.urlPrefix_;
|
||||
|
||||
|
||||
/**
|
||||
* If true, delay loading the images for the emojipalettes until after
|
||||
* construction. This gives a better user experience before the images are in
|
||||
* the cache, since other widgets waiting for construction of the emojipalettes
|
||||
* won't have to wait for all the images (which may be a substantial amount) to
|
||||
* load.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.delayedLoad_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* Whether to use progressive rendering in the emojipicker's palette, if using
|
||||
* sprited imgs. If true, then uses img tags, which most browsers render
|
||||
* progressively (i.e., as the data comes in). If false, then uses div tags
|
||||
* with the background-image, which some newer browsers render progressively
|
||||
* but older ones do not.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.progressiveRender_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* Whether to require the caller to manually specify when to start loading
|
||||
* animated emoji. This is primarily for unittests to be able to test the
|
||||
* structure of the emojipicker palettes before and after the animated emoji
|
||||
* have been loaded.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.manualLoadOfAnimatedEmoji_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* Index of the active page in the picker.
|
||||
*
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.activePage_ = -1;
|
||||
|
||||
|
||||
/**
|
||||
* Adds a group of emoji to the picker.
|
||||
*
|
||||
* @param {string|Element} title Title for the group.
|
||||
* @param {Array.<Array.<string>>} emojiGroup A new group of emoji to be added
|
||||
* Each internal array contains [emojiUrl, emojiId].
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.addEmojiGroup =
|
||||
function(title, emojiGroup) {
|
||||
this.emoji_.push({title: title, emoji: emojiGroup});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the number of rows per grid in the emoji picker.
|
||||
*
|
||||
* @return {number} number of rows per grid.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.getNumRows = function() {
|
||||
return this.numRows_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the number of columns per grid in the emoji picker.
|
||||
*
|
||||
* @return {number} number of columns per grid.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.getNumColumns = function() {
|
||||
return this.numCols_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the number of rows per grid in the emoji picker. This should only be
|
||||
* called before the picker has been rendered.
|
||||
*
|
||||
* @param {number} numRows Number of rows per grid.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.setNumRows = function(numRows) {
|
||||
this.numRows_ = numRows;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the number of columns per grid in the emoji picker. This should only be
|
||||
* called before the picker has been rendered.
|
||||
*
|
||||
* @param {number} numCols Number of columns per grid.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.setNumColumns = function(numCols) {
|
||||
this.numCols_ = numCols;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether to automatically size the emojipicker based on the number of
|
||||
* columns and the number of emoji in each group, so as to reduce jitter.
|
||||
*
|
||||
* @param {boolean} autoSize Whether to automatically size the picker.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.setAutoSizeByColumnCount =
|
||||
function(autoSize) {
|
||||
this.autoSizeByColumnCount_ = autoSize;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the location of the tabs in relation to the emoji grids. This should
|
||||
* only be called before the picker has been rendered.
|
||||
*
|
||||
* @param {goog.ui.TabPane.TabLocation} tabLocation The location of the tabs.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.setTabLocation = function(tabLocation) {
|
||||
this.tabLocation_ = tabLocation;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether loading of images should be delayed until after dom creation.
|
||||
* Thus, this function must be called before {@link #createDom}. If set to true,
|
||||
* the client must call {@link #loadImages} when they wish the images to be
|
||||
* loaded.
|
||||
*
|
||||
* @param {boolean} shouldDelay Whether to delay loading the images.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.setDelayedLoad = function(shouldDelay) {
|
||||
this.delayedLoad_ = shouldDelay;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether to require the caller to manually specify when to start loading
|
||||
* animated emoji. This is primarily for unittests to be able to test the
|
||||
* structure of the emojipicker palettes before and after the animated emoji
|
||||
* have been loaded. This only affects sprited emojipickers with sprite data
|
||||
* for animated emoji.
|
||||
*
|
||||
* @param {boolean} manual Whether to load animated emoji manually.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.setManualLoadOfAnimatedEmoji =
|
||||
function(manual) {
|
||||
this.manualLoadOfAnimatedEmoji_ = manual;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the component is focusable, false otherwise. The default
|
||||
* is true. Focusable components always have a tab index and allocate a key
|
||||
* handler to handle keyboard events while focused.
|
||||
* @return {boolean} Whether the component is focusable.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.isFocusable = function() {
|
||||
return this.focusable_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether the component is focusable. The default is true.
|
||||
* Focusable components always have a tab index and allocate a key handler to
|
||||
* handle keyboard events while focused.
|
||||
* @param {boolean} focusable Whether the component is focusable.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.setFocusable = function(focusable) {
|
||||
this.focusable_ = focusable;
|
||||
for (var i = 0; i < this.pages_.length; i++) {
|
||||
if (this.pages_[i]) {
|
||||
this.pages_[i].setSupportedState(goog.ui.Component.State.FOCUSED,
|
||||
focusable);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the URL prefix for the emoji URLs.
|
||||
*
|
||||
* @param {string} urlPrefix Prefix that should be prepended to all URLs.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.setUrlPrefix = function(urlPrefix) {
|
||||
this.urlPrefix_ = urlPrefix;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the progressive rendering aspect of this emojipicker. Must be called
|
||||
* before createDom to have an effect.
|
||||
*
|
||||
* @param {boolean} progressive Whether this picker should render progressively.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.setProgressiveRender =
|
||||
function(progressive) {
|
||||
this.progressiveRender_ = progressive;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Logger for the emoji picker.
|
||||
*
|
||||
* @type {goog.log.Logger}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.logger_ =
|
||||
goog.log.getLogger('goog.ui.emoji.EmojiPicker');
|
||||
|
||||
|
||||
/**
|
||||
* Adjusts the number of rows to be the maximum row count out of all the emoji
|
||||
* groups, in order to prevent jitter in switching among the tabs.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.adjustNumRowsIfNecessary_ = function() {
|
||||
var currentMax = 0;
|
||||
|
||||
for (var i = 0; i < this.emoji_.length; i++) {
|
||||
var numEmoji = this.emoji_[i].emoji.length;
|
||||
var rowsNeeded = Math.ceil(numEmoji / this.numCols_);
|
||||
if (rowsNeeded > currentMax) {
|
||||
currentMax = rowsNeeded;
|
||||
}
|
||||
}
|
||||
|
||||
this.setNumRows(currentMax);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Causes the emoji imgs to be loaded into the picker. Used for delayed loading.
|
||||
* No-op if delayed loading is not set.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.loadImages = function() {
|
||||
if (!this.delayedLoad_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load the first page only
|
||||
this.loadPage_(0);
|
||||
this.activePage_ = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @suppress {deprecated} Using deprecated goog.ui.TabPane.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.createDom = function() {
|
||||
this.setElementInternal(this.getDomHelper().createDom('div'));
|
||||
|
||||
if (this.autoSizeByColumnCount_) {
|
||||
this.adjustNumRowsIfNecessary_();
|
||||
}
|
||||
|
||||
if (this.emoji_.length == 0) {
|
||||
throw Error('Must add some emoji to the picker');
|
||||
}
|
||||
|
||||
// If there is more than one group of emoji, we construct a tabpane
|
||||
if (this.emoji_.length > 1) {
|
||||
// Give the tabpane a div to use as its content element, since tabpane
|
||||
// overwrites the CSS class of the element it's passed
|
||||
var div = this.getDomHelper().createDom('div');
|
||||
this.getElement().appendChild(div);
|
||||
this.tabPane_ = new goog.ui.TabPane(div,
|
||||
this.tabLocation_,
|
||||
this.getDomHelper(),
|
||||
true /* use MOUSEDOWN */);
|
||||
}
|
||||
|
||||
this.renderer_ = this.progressiveRender_ ?
|
||||
new goog.ui.emoji.ProgressiveEmojiPaletteRenderer(this.defaultImgUrl_) :
|
||||
new goog.ui.emoji.EmojiPaletteRenderer(this.defaultImgUrl_);
|
||||
|
||||
for (var i = 0; i < this.emoji_.length; i++) {
|
||||
var emoji = this.emoji_[i].emoji;
|
||||
var page = this.delayedLoad_ ?
|
||||
this.createPlaceholderEmojiPage_(emoji) :
|
||||
this.createEmojiPage_(emoji, i);
|
||||
this.pages_.push(page);
|
||||
}
|
||||
|
||||
this.activePage_ = 0;
|
||||
this.getElement().tabIndex = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Used by unittests to manually load the animated emoji for this picker.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.manuallyLoadAnimatedEmoji = function() {
|
||||
for (var i = 0; i < this.pages_.length; i++) {
|
||||
this.pages_[i].loadAnimatedEmoji();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a page if it has not already been loaded. This has the side effects
|
||||
* of setting the load status of the page to true.
|
||||
*
|
||||
* @param {Array.<Array.<string>>} emoji Emoji for this page. See
|
||||
* {@link addEmojiGroup} for more details.
|
||||
* @param {number} index Index of the page in the emojipicker.
|
||||
* @return {goog.ui.emoji.EmojiPalette} the emoji page.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.createEmojiPage_ = function(emoji, index) {
|
||||
// Safeguard against trying to create the same page twice
|
||||
if (this.pageLoadStatus_[index]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var palette = new goog.ui.emoji.EmojiPalette(emoji,
|
||||
this.urlPrefix_,
|
||||
this.renderer_,
|
||||
this.getDomHelper());
|
||||
if (!this.manualLoadOfAnimatedEmoji_) {
|
||||
palette.loadAnimatedEmoji();
|
||||
}
|
||||
palette.setSize(this.numCols_, this.numRows_);
|
||||
palette.setSupportedState(goog.ui.Component.State.FOCUSED, this.focusable_);
|
||||
palette.createDom();
|
||||
palette.setParent(this);
|
||||
|
||||
this.pageLoadStatus_[index] = true;
|
||||
|
||||
return palette;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of emoji whose real URLs have been replaced with the
|
||||
* default img URL. Used for delayed loading.
|
||||
*
|
||||
* @param {Array.<Array.<string>>} emoji Original emoji array.
|
||||
* @return {Array.<Array.<string>>} emoji array with all emoji pointing to the
|
||||
* default img.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.getPlaceholderEmoji_ = function(emoji) {
|
||||
var placeholderEmoji = [];
|
||||
|
||||
for (var i = 0; i < emoji.length; i++) {
|
||||
placeholderEmoji.push([this.defaultImgUrl_, emoji[i][1]]);
|
||||
}
|
||||
|
||||
return placeholderEmoji;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates an emoji page using placeholder emoji pointing to the default
|
||||
* img instead of the real emoji. Used for delayed loading.
|
||||
*
|
||||
* @param {Array.<Array.<string>>} emoji Emoji for this page. See
|
||||
* {@link addEmojiGroup} for more details.
|
||||
* @return {goog.ui.emoji.EmojiPalette} the emoji page.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.createPlaceholderEmojiPage_ =
|
||||
function(emoji) {
|
||||
var placeholderEmoji = this.getPlaceholderEmoji_(emoji);
|
||||
|
||||
var palette = new goog.ui.emoji.EmojiPalette(placeholderEmoji,
|
||||
null, // no url prefix
|
||||
this.renderer_,
|
||||
this.getDomHelper());
|
||||
palette.setSize(this.numCols_, this.numRows_);
|
||||
palette.setSupportedState(goog.ui.Component.State.FOCUSED, this.focusable_);
|
||||
palette.createDom();
|
||||
palette.setParent(this);
|
||||
|
||||
return palette;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* EmojiPickers cannot be used to decorate pre-existing html, since the
|
||||
* structure they build is fairly complicated.
|
||||
* @param {Element} element Element to decorate.
|
||||
* @return {boolean} Returns always false.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.canDecorate = function(element) {
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @suppress {deprecated} Using deprecated goog.ui.TabPane.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.enterDocument = function() {
|
||||
goog.ui.emoji.EmojiPicker.superClass_.enterDocument.call(this);
|
||||
|
||||
for (var i = 0; i < this.pages_.length; i++) {
|
||||
this.pages_[i].enterDocument();
|
||||
var pageElement = this.pages_[i].getElement();
|
||||
|
||||
// Add a new tab to the tabpane if there's more than one group of emoji.
|
||||
// If there is just one group of emoji, then we simply use the single
|
||||
// page's element as the content for the picker
|
||||
if (this.pages_.length > 1) {
|
||||
// Create a simple default title containg the page number if the title
|
||||
// was not provided in the emoji group params
|
||||
var title = this.emoji_[i].title || (i + 1);
|
||||
this.tabPane_.addPage(new goog.ui.TabPane.TabPage(
|
||||
pageElement, title, this.getDomHelper()));
|
||||
} else {
|
||||
this.getElement().appendChild(pageElement);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize listeners. Note that we need to initialize this listener
|
||||
// after createDom, because addPage causes the goog.ui.TabPane.Events.CHANGE
|
||||
// event to fire, but we only want the handler (which loads delayed images)
|
||||
// to run after the picker has been constructed.
|
||||
if (this.tabPane_) {
|
||||
this.getHandler().listen(
|
||||
this.tabPane_, goog.ui.TabPane.Events.CHANGE, this.onPageChanged_);
|
||||
|
||||
// Make the tabpane unselectable so that changing tabs doesn't disturb the
|
||||
// cursor
|
||||
goog.style.setUnselectable(this.tabPane_.getElement(), true);
|
||||
}
|
||||
|
||||
this.getElement().unselectable = 'on';
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.emoji.EmojiPicker.prototype.exitDocument = function() {
|
||||
goog.ui.emoji.EmojiPicker.superClass_.exitDocument.call(this);
|
||||
for (var i = 0; i < this.pages_.length; i++) {
|
||||
this.pages_[i].exitDocument();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.emoji.EmojiPicker.prototype.disposeInternal = function() {
|
||||
goog.ui.emoji.EmojiPicker.superClass_.disposeInternal.call(this);
|
||||
|
||||
if (this.tabPane_) {
|
||||
this.tabPane_.dispose();
|
||||
this.tabPane_ = null;
|
||||
}
|
||||
|
||||
for (var i = 0; i < this.pages_.length; i++) {
|
||||
this.pages_[i].dispose();
|
||||
}
|
||||
this.pages_.length = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} CSS class for the root element of EmojiPicker.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.getCssClass = function() {
|
||||
return goog.getCssName('goog-ui-emojipicker');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the currently selected emoji from this picker. If the picker is
|
||||
* using the URL prefix optimization, allocates a new emoji object with the
|
||||
* full URL. This method is meant to be used by clients of the emojipicker,
|
||||
* e.g., in a listener on goog.ui.component.EventType.ACTION that wants to use
|
||||
* the just-selected emoji.
|
||||
*
|
||||
* @return {goog.ui.emoji.Emoji} The currently selected emoji from this picker.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.getSelectedEmoji = function() {
|
||||
return this.urlPrefix_ ?
|
||||
new goog.ui.emoji.Emoji(this.urlPrefix_ + this.selectedEmoji_.getId(),
|
||||
this.selectedEmoji_.getId()) :
|
||||
this.selectedEmoji_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of emoji groups in this picker.
|
||||
*
|
||||
* @return {number} The number of emoji groups in this picker.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.getNumEmojiGroups = function() {
|
||||
return this.emoji_.length;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns a page from the picker. This should be considered protected, and is
|
||||
* ONLY FOR TESTING.
|
||||
*
|
||||
* @param {number} index Index of the page to return.
|
||||
* @return {goog.ui.emoji.EmojiPalette?} the page at the specified index or null
|
||||
* if none exists.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.getPage = function(index) {
|
||||
return this.pages_[index];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns all the pages from the picker. This should be considered protected,
|
||||
* and is ONLY FOR TESTING.
|
||||
*
|
||||
* @return {Array.<goog.ui.emoji.EmojiPalette>?} the pages in the picker or
|
||||
* null if none exist.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.getPages = function() {
|
||||
return this.pages_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the tabpane if this is a multipage picker. This should be considered
|
||||
* protected, and is ONLY FOR TESTING.
|
||||
*
|
||||
* @return {goog.ui.TabPane} the tabpane if it is a multipage picker,
|
||||
* or null if it does not exist or is a single page picker.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.getTabPane = function() {
|
||||
return this.tabPane_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {goog.ui.emoji.EmojiPalette} The active page of the emoji picker.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.getActivePage_ = function() {
|
||||
return this.pages_[this.activePage_];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles actions from the EmojiPalettes that this picker contains.
|
||||
*
|
||||
* @param {goog.ui.Component.EventType} e The event object.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.onEmojiPaletteAction_ = function(e) {
|
||||
this.selectedEmoji_ = this.getActivePage_().getSelectedEmoji();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles changes in the active page in the tabpane.
|
||||
*
|
||||
* @param {goog.ui.TabPaneEvent} e The event object.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.onPageChanged_ = function(e) {
|
||||
var index = /** @type {number} */ (e.page.getIndex());
|
||||
this.loadPage_(index);
|
||||
this.activePage_ = index;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Loads a page into the picker if it has not yet been loaded.
|
||||
*
|
||||
* @param {number} index Index of the page to load.
|
||||
* @private
|
||||
* @suppress {deprecated} Using deprecated goog.ui.TabPane.
|
||||
*/
|
||||
goog.ui.emoji.EmojiPicker.prototype.loadPage_ = function(index) {
|
||||
if (index < 0 || index > this.pages_.length) {
|
||||
throw Error('Index out of bounds');
|
||||
}
|
||||
|
||||
if (!this.pageLoadStatus_[index]) {
|
||||
var oldPage = this.pages_[index];
|
||||
this.pages_[index] = this.createEmojiPage_(this.emoji_[index].emoji,
|
||||
index);
|
||||
this.pages_[index].enterDocument();
|
||||
var pageElement = this.pages_[index].getElement();
|
||||
if (this.pages_.length > 1) {
|
||||
this.tabPane_.removePage(index);
|
||||
var title = this.emoji_[index].title || (index + 1);
|
||||
this.tabPane_.addPage(new goog.ui.TabPane.TabPage(
|
||||
pageElement, title, this.getDomHelper()), index);
|
||||
this.tabPane_.setSelectedIndex(index);
|
||||
} else {
|
||||
var el = this.getElement();
|
||||
el.appendChild(pageElement);
|
||||
}
|
||||
if (oldPage) {
|
||||
oldPage.dispose();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,410 @@
|
||||
// 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 Popup Emoji Picker implementation. This provides a UI widget
|
||||
* for choosing an emoji from a grid of possible choices. The widget is a popup,
|
||||
* so it is suitable for a toolbar, for instance the TrogEdit toolbar.
|
||||
*
|
||||
* @see ../demos/popupemojipicker.html for an example of how to instantiate
|
||||
* an emoji picker.
|
||||
*
|
||||
* See goog.ui.emoji.EmojiPicker in emojipicker.js for more details.
|
||||
*
|
||||
* Based on goog.ui.PopupColorPicker (popupcolorpicker.js).
|
||||
*
|
||||
* @see ../../demos/popupemojipicker.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.emoji.PopupEmojiPicker');
|
||||
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.positioning.AnchoredPosition');
|
||||
goog.require('goog.positioning.Corner');
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('goog.ui.Popup');
|
||||
goog.require('goog.ui.emoji.EmojiPicker');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a popup emoji picker widget.
|
||||
*
|
||||
* @param {string} defaultImgUrl Url of the img that should be used to fill up
|
||||
* the cells in the emoji table, to prevent jittering. Should be the same
|
||||
* size as the emoji.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
|
||||
* @extends {goog.ui.Component}
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker =
|
||||
function(defaultImgUrl, opt_domHelper) {
|
||||
goog.ui.Component.call(this, opt_domHelper);
|
||||
|
||||
this.emojiPicker_ = new goog.ui.emoji.EmojiPicker(defaultImgUrl,
|
||||
opt_domHelper);
|
||||
this.addChild(this.emojiPicker_);
|
||||
|
||||
this.getHandler().listen(this.emojiPicker_,
|
||||
goog.ui.Component.EventType.ACTION, this.onEmojiPicked_);
|
||||
};
|
||||
goog.inherits(goog.ui.emoji.PopupEmojiPicker, goog.ui.Component);
|
||||
|
||||
|
||||
/**
|
||||
* Instance of an emoji picker control.
|
||||
* @type {goog.ui.emoji.EmojiPicker}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.emojiPicker_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Instance of goog.ui.Popup used to manage the behavior of the emoji picker.
|
||||
* @type {goog.ui.Popup}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.popup_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Reference to the element that triggered the last popup.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.lastTarget_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* Whether the emoji picker can accept focus.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.focusable_ = true;
|
||||
|
||||
|
||||
/**
|
||||
* If true, then the emojipicker will toggle off if it is already visible.
|
||||
* Default is true.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.toggleMode_ = true;
|
||||
|
||||
|
||||
/**
|
||||
* Adds a group of emoji to the picker.
|
||||
*
|
||||
* @param {string|Element} title Title for the group.
|
||||
* @param {Array.<Array>} emojiGroup A new group of emoji to be added. Each
|
||||
* internal array contains [emojiUrl, emojiId].
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.addEmojiGroup =
|
||||
function(title, emojiGroup) {
|
||||
this.emojiPicker_.addEmojiGroup(title, emojiGroup);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether the emoji picker should toggle if it is already open.
|
||||
* @param {boolean} toggle The toggle mode to use.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.setToggleMode = function(toggle) {
|
||||
this.toggleMode_ = toggle;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets whether the emojipicker is in toggle mode
|
||||
* @return {boolean} toggle.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.getToggleMode = function() {
|
||||
return this.toggleMode_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether loading of images should be delayed until after dom creation.
|
||||
* Thus, this function must be called before {@link #createDom}. If set to true,
|
||||
* the client must call {@link #loadImages} when they wish the images to be
|
||||
* loaded.
|
||||
*
|
||||
* @param {boolean} shouldDelay Whether to delay loading the images.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.setDelayedLoad =
|
||||
function(shouldDelay) {
|
||||
if (this.emojiPicker_) {
|
||||
this.emojiPicker_.setDelayedLoad(shouldDelay);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether the emoji picker can accept focus.
|
||||
* @param {boolean} focusable Whether the emoji picker should accept focus.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.setFocusable = function(focusable) {
|
||||
this.focusable_ = focusable;
|
||||
if (this.emojiPicker_) {
|
||||
// TODO(user): In next revision sort the behavior of passing state to
|
||||
// children correctly
|
||||
this.emojiPicker_.setFocusable(focusable);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the URL prefix for the emoji URLs.
|
||||
*
|
||||
* @param {string} urlPrefix Prefix that should be prepended to all URLs.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.setUrlPrefix = function(urlPrefix) {
|
||||
this.emojiPicker_.setUrlPrefix(urlPrefix);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the location of the tabs in relation to the emoji grids. This should
|
||||
* only be called before the picker has been rendered.
|
||||
*
|
||||
* @param {goog.ui.TabPane.TabLocation} tabLocation The location of the tabs.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.setTabLocation =
|
||||
function(tabLocation) {
|
||||
this.emojiPicker_.setTabLocation(tabLocation);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the number of rows per grid in the emoji picker. This should only be
|
||||
* called before the picker has been rendered.
|
||||
*
|
||||
* @param {number} numRows Number of rows per grid.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.setNumRows = function(numRows) {
|
||||
this.emojiPicker_.setNumRows(numRows);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the number of columns per grid in the emoji picker. This should only be
|
||||
* called before the picker has been rendered.
|
||||
*
|
||||
* @param {number} numCols Number of columns per grid.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.setNumColumns = function(numCols) {
|
||||
this.emojiPicker_.setNumColumns(numCols);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the progressive rendering aspect of this emojipicker. Must be called
|
||||
* before createDom to have an effect.
|
||||
*
|
||||
* @param {boolean} progressive Whether the picker should render progressively.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.setProgressiveRender =
|
||||
function(progressive) {
|
||||
if (this.emojiPicker_) {
|
||||
this.emojiPicker_.setProgressiveRender(progressive);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of emoji groups in this picker.
|
||||
*
|
||||
* @return {number} The number of emoji groups in this picker.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.getNumEmojiGroups = function() {
|
||||
return this.emojiPicker_.getNumEmojiGroups();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Causes the emoji imgs to be loaded into the picker. Used for delayed loading.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.loadImages = function() {
|
||||
if (this.emojiPicker_) {
|
||||
this.emojiPicker_.loadImages();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.createDom = function() {
|
||||
goog.ui.emoji.PopupEmojiPicker.superClass_.createDom.call(this);
|
||||
|
||||
this.emojiPicker_.createDom();
|
||||
|
||||
this.getElement().className = goog.getCssName('goog-ui-popupemojipicker');
|
||||
this.getElement().appendChild(this.emojiPicker_.getElement());
|
||||
|
||||
this.popup_ = new goog.ui.Popup(this.getElement());
|
||||
this.getElement().unselectable = 'on';
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.disposeInternal = function() {
|
||||
goog.ui.emoji.PopupEmojiPicker.superClass_.disposeInternal.call(this);
|
||||
this.emojiPicker_ = null;
|
||||
this.lastTarget_ = null;
|
||||
if (this.popup_) {
|
||||
this.popup_.dispose();
|
||||
this.popup_ = null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Attaches the popup emoji picker to an element.
|
||||
*
|
||||
* @param {Element} element The element to attach to.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.attach = function(element) {
|
||||
// TODO(user): standardize event type, popups should use MOUSEDOWN, but
|
||||
// currently apps are using click.
|
||||
this.getHandler().listen(element, goog.events.EventType.CLICK, this.show_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Detatches the popup emoji picker from an element.
|
||||
*
|
||||
* @param {Element} element The element to detach from.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.detach = function(element) {
|
||||
this.getHandler().unlisten(element, goog.events.EventType.CLICK, this.show_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {goog.ui.emoji.EmojiPicker} The emoji picker instance.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.getEmojiPicker = function() {
|
||||
return this.emojiPicker_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the Popup dismisses itself when the user clicks outside of
|
||||
* it.
|
||||
* @return {boolean} Whether the Popup autohides on an external click.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.getAutoHide = function() {
|
||||
return !!this.popup_ && this.popup_.getAutoHide();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether the Popup dismisses itself when the user clicks outside of it -
|
||||
* must be called after the Popup has been created (in createDom()),
|
||||
* otherwise it does nothing.
|
||||
*
|
||||
* @param {boolean} autoHide Whether to autohide on an external click.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.setAutoHide = function(autoHide) {
|
||||
if (this.popup_) {
|
||||
this.popup_.setAutoHide(autoHide);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the region inside which the Popup dismisses itself when the user
|
||||
* clicks, or null if it was not set. Null indicates the entire document is
|
||||
* the autohide region.
|
||||
* @return {Element} The DOM element for autohide, or null if it hasn't been
|
||||
* set.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.getAutoHideRegion = function() {
|
||||
return this.popup_ && this.popup_.getAutoHideRegion();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the region inside which the Popup dismisses itself when the user
|
||||
* clicks - must be called after the Popup has been created (in createDom()),
|
||||
* otherwise it does nothing.
|
||||
*
|
||||
* @param {Element} element The DOM element for autohide.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.setAutoHideRegion = function(element) {
|
||||
if (this.popup_) {
|
||||
this.popup_.setAutoHideRegion(element);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the {@link goog.ui.PopupBase} from this picker. Returns null if the
|
||||
* popup has not yet been created.
|
||||
*
|
||||
* NOTE: This should *ONLY* be called from tests. If called before createDom(),
|
||||
* this should return null.
|
||||
*
|
||||
* @return {goog.ui.PopupBase?} The popup, or null if it hasn't been created.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.getPopup = function() {
|
||||
return this.popup_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {Element} The last element that triggered the popup.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.getLastTarget = function() {
|
||||
return this.lastTarget_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {goog.ui.emoji.Emoji} The currently selected emoji.
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.getSelectedEmoji = function() {
|
||||
return this.emojiPicker_.getSelectedEmoji();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles click events on the element this picker is attached to and shows the
|
||||
* emoji picker in a popup.
|
||||
*
|
||||
* @param {goog.events.BrowserEvent} e The browser event.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.show_ = function(e) {
|
||||
if (this.popup_.isOrWasRecentlyVisible() && this.toggleMode_ &&
|
||||
this.lastTarget_ == e.currentTarget) {
|
||||
this.popup_.setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
this.lastTarget_ = /** @type {Element} */ (e.currentTarget);
|
||||
this.popup_.setPosition(new goog.positioning.AnchoredPosition(
|
||||
this.lastTarget_, goog.positioning.Corner.BOTTOM_LEFT));
|
||||
this.popup_.setVisible(true);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles selection of an emoji.
|
||||
*
|
||||
* @param {goog.events.Event} e The event object.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.PopupEmojiPicker.prototype.onEmojiPicked_ = function(e) {
|
||||
this.popup_.setVisible(false);
|
||||
};
|
||||
@@ -0,0 +1,98 @@
|
||||
// Copyright 2008 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 Progressive Emoji Palette renderer implementation.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.emoji.ProgressiveEmojiPaletteRenderer');
|
||||
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.emoji.EmojiPaletteRenderer');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Progressively renders an emoji palette. The progressive renderer tries to
|
||||
* use img tags instead of background-image for sprited emoji, since most
|
||||
* browsers render img tags progressively (i.e., as the data comes in), while
|
||||
* only very new browsers render background-image progressively.
|
||||
*
|
||||
* @param {string} defaultImgUrl Url of the img that should be used to fill up
|
||||
* the cells in the emoji table, to prevent jittering. Will be stretched
|
||||
* to the emoji cell size. A good image is a transparent dot.
|
||||
* @constructor
|
||||
* @extends {goog.ui.emoji.EmojiPaletteRenderer}
|
||||
*/
|
||||
goog.ui.emoji.ProgressiveEmojiPaletteRenderer = function(defaultImgUrl) {
|
||||
goog.ui.emoji.EmojiPaletteRenderer.call(this, defaultImgUrl);
|
||||
};
|
||||
goog.inherits(goog.ui.emoji.ProgressiveEmojiPaletteRenderer,
|
||||
goog.ui.emoji.EmojiPaletteRenderer);
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.emoji.ProgressiveEmojiPaletteRenderer.prototype.
|
||||
buildElementFromSpriteMetadata = function(dom, spriteInfo, displayUrl) {
|
||||
var width = spriteInfo.getWidthCssValue();
|
||||
var height = spriteInfo.getHeightCssValue();
|
||||
var x = spriteInfo.getXOffsetCssValue();
|
||||
var y = spriteInfo.getYOffsetCssValue();
|
||||
// Need this extra div for proper vertical centering.
|
||||
var inner = dom.createDom('img', {'src': displayUrl});
|
||||
var el = /** @type {HTMLDivElement} */ (dom.createDom('div',
|
||||
goog.getCssName('goog-palette-cell-extra'), inner));
|
||||
goog.style.setStyle(el, {
|
||||
'width': width,
|
||||
'height': height,
|
||||
'overflow': 'hidden',
|
||||
'position': 'relative'
|
||||
});
|
||||
goog.style.setStyle(inner, {
|
||||
'left': x,
|
||||
'top': y,
|
||||
'position': 'absolute'
|
||||
});
|
||||
|
||||
return el;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.emoji.ProgressiveEmojiPaletteRenderer.prototype.
|
||||
updateAnimatedPaletteItem = function(item, animatedImg) {
|
||||
// Just to be safe, we check for the existence of the img element within this
|
||||
// palette item before attempting to modify it.
|
||||
var img;
|
||||
var el = item.firstChild;
|
||||
while (el) {
|
||||
if ('IMG' == el.tagName) {
|
||||
img = /** @type {Element} */ (el);
|
||||
break;
|
||||
}
|
||||
el = el.firstChild;
|
||||
}
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
|
||||
img.width = animatedImg.width;
|
||||
img.height = animatedImg.height;
|
||||
goog.style.setStyle(img, {
|
||||
'left': 0,
|
||||
'top': 0
|
||||
});
|
||||
img.src = animatedImg.src;
|
||||
};
|
||||
@@ -0,0 +1,212 @@
|
||||
// Copyright 2008 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 SpriteInfo implementation. This is a simple wrapper class to
|
||||
* hold CSS metadata needed for sprited emoji.
|
||||
*
|
||||
* @see ../demos/popupemojipicker.html or emojipicker_test.html for examples
|
||||
* of how to use this class.
|
||||
*
|
||||
*/
|
||||
goog.provide('goog.ui.emoji.SpriteInfo');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a SpriteInfo object with the specified properties. If the image is
|
||||
* sprited via CSS, then only the first parameter needs a value. If the image
|
||||
* is sprited via metadata, then the first parameter should be left null.
|
||||
*
|
||||
* @param {?string} cssClass CSS class to properly display the sprited image.
|
||||
* @param {string=} opt_url Url of the sprite image.
|
||||
* @param {number=} opt_width Width of the image being sprited.
|
||||
* @param {number=} opt_height Height of the image being sprited.
|
||||
* @param {number=} opt_xOffset Positive x offset of the image being sprited
|
||||
* within the sprite.
|
||||
* @param {number=} opt_yOffset Positive y offset of the image being sprited
|
||||
* within the sprite.
|
||||
* @param {boolean=} opt_animated Whether the sprite is animated.
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo = function(cssClass, opt_url, opt_width, opt_height,
|
||||
opt_xOffset, opt_yOffset, opt_animated) {
|
||||
if (cssClass != null) {
|
||||
this.cssClass_ = cssClass;
|
||||
} else {
|
||||
if (opt_url == undefined || opt_width === undefined ||
|
||||
opt_height === undefined || opt_xOffset == undefined ||
|
||||
opt_yOffset === undefined) {
|
||||
throw Error('Sprite info is not fully specified');
|
||||
}
|
||||
|
||||
this.url_ = opt_url;
|
||||
this.width_ = opt_width;
|
||||
this.height_ = opt_height;
|
||||
this.xOffset_ = opt_xOffset;
|
||||
this.yOffset_ = opt_yOffset;
|
||||
}
|
||||
|
||||
this.animated_ = !!opt_animated;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Name of the CSS class to properly display the sprited image.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.prototype.cssClass_;
|
||||
|
||||
|
||||
/**
|
||||
* Url of the sprite image.
|
||||
* @type {string|undefined}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.prototype.url_;
|
||||
|
||||
|
||||
/**
|
||||
* Width of the image being sprited.
|
||||
* @type {number|undefined}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.prototype.width_;
|
||||
|
||||
|
||||
/**
|
||||
* Height of the image being sprited.
|
||||
* @type {number|undefined}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.prototype.height_;
|
||||
|
||||
|
||||
/**
|
||||
* Positive x offset of the image being sprited within the sprite.
|
||||
* @type {number|undefined}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.prototype.xOffset_;
|
||||
|
||||
|
||||
/**
|
||||
* Positive y offset of the image being sprited within the sprite.
|
||||
* @type {number|undefined}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.prototype.yOffset_;
|
||||
|
||||
|
||||
/**
|
||||
* Whether the emoji specified by the sprite is animated.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.prototype.animated_;
|
||||
|
||||
|
||||
/**
|
||||
* Returns the css class of the sprited image.
|
||||
* @return {?string} Name of the CSS class to properly display the sprited
|
||||
* image.
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.prototype.getCssClass = function() {
|
||||
return this.cssClass_ || null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the url of the sprite image.
|
||||
* @return {?string} Url of the sprite image.
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.prototype.getUrl = function() {
|
||||
return this.url_ || null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the emoji specified by this sprite is animated.
|
||||
* @return {boolean} Whether the emoji is animated.
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.prototype.isAnimated = function() {
|
||||
return this.animated_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the width of the image being sprited, appropriate for a CSS value.
|
||||
* @return {string} The width of the image being sprited.
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.prototype.getWidthCssValue = function() {
|
||||
return goog.ui.emoji.SpriteInfo.getCssPixelValue_(this.width_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the height of the image being sprited, appropriate for a CSS value.
|
||||
* @return {string} The height of the image being sprited.
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.prototype.getHeightCssValue = function() {
|
||||
return goog.ui.emoji.SpriteInfo.getCssPixelValue_(this.height_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the x offset of the image being sprited within the sprite,
|
||||
* appropriate for a CSS value.
|
||||
* @return {string} The x offset of the image being sprited within the sprite.
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.prototype.getXOffsetCssValue = function() {
|
||||
return goog.ui.emoji.SpriteInfo.getOffsetCssValue_(this.xOffset_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the positive y offset of the image being sprited within the sprite,
|
||||
* appropriate for a CSS value.
|
||||
* @return {string} The y offset of the image being sprited within the sprite.
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.prototype.getYOffsetCssValue = function() {
|
||||
return goog.ui.emoji.SpriteInfo.getOffsetCssValue_(this.yOffset_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns a string appropriate for use as a CSS value. If the value is zero,
|
||||
* then there is no unit appended.
|
||||
*
|
||||
* @param {number|undefined} value A number to be turned into a
|
||||
* CSS size/location value.
|
||||
* @return {string} A string appropriate for use as a CSS value.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.getCssPixelValue_ = function(value) {
|
||||
return !value ? '0' : value + 'px';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns a string appropriate for use as a CSS value for a position offset,
|
||||
* such as the position argument for sprites.
|
||||
*
|
||||
* @param {number|undefined} posOffset A positive offset for a position.
|
||||
* @return {string} A string appropriate for use as a CSS value.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.emoji.SpriteInfo.getOffsetCssValue_ = function(posOffset) {
|
||||
var offset = goog.ui.emoji.SpriteInfo.getCssPixelValue_(posOffset);
|
||||
return offset == '0' ? offset : '-' + offset;
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright 2009 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.
|
||||
|
||||
goog.provide('goog.ui.equation.ArrowPalette');
|
||||
|
||||
goog.require('goog.math.Size');
|
||||
goog.require('goog.ui.equation.Palette');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new arrows palette.
|
||||
* @param {goog.ui.equation.PaletteManager} paletteManager The
|
||||
* manager of the palette.
|
||||
* @extends {goog.ui.equation.Palette}
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.equation.ArrowPalette = function(paletteManager) {
|
||||
goog.ui.equation.Palette.call(this, paletteManager,
|
||||
goog.ui.equation.Palette.Type.ARROW,
|
||||
0, 150, 18, 18,
|
||||
['\\leftarrow',
|
||||
'\\rightarrow',
|
||||
'\\leftrightarrow',
|
||||
'\\Leftarrow',
|
||||
'\\Rightarrow',
|
||||
'\\Leftrightarrow',
|
||||
'\\uparrow',
|
||||
'\\downarrow',
|
||||
'\\updownarrow',
|
||||
'\\Uparrow',
|
||||
'\\Downarrow',
|
||||
'\\Updownarrow']);
|
||||
this.setSize(new goog.math.Size(12, 1));
|
||||
};
|
||||
goog.inherits(goog.ui.equation.ArrowPalette, goog.ui.equation.Palette);
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright 2011 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.
|
||||
|
||||
goog.provide('goog.ui.equation.ChangeEvent');
|
||||
|
||||
goog.require('goog.events.Event');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Event fired when equation changes.
|
||||
* @constructor
|
||||
* @param {boolean} isValid Whether the equation is valid.
|
||||
* @extends {goog.events.Event}
|
||||
*/
|
||||
goog.ui.equation.ChangeEvent = function(isValid) {
|
||||
goog.events.Event.call(this, 'change');
|
||||
|
||||
/**
|
||||
* Whether equation is valid.
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.isValid = isValid;
|
||||
};
|
||||
goog.inherits(goog.ui.equation.ChangeEvent, goog.events.Event);
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// Copyright 2009 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.
|
||||
|
||||
goog.provide('goog.ui.equation.ComparisonPalette');
|
||||
|
||||
goog.require('goog.math.Size');
|
||||
goog.require('goog.ui.equation.Palette');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new comparison palette.
|
||||
* @param {goog.ui.equation.PaletteManager} paletteManager The
|
||||
* manager of the palette.
|
||||
* @extends {goog.ui.equation.Palette}
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.equation.ComparisonPalette = function(paletteManager) {
|
||||
goog.ui.equation.Palette.call(this, paletteManager,
|
||||
goog.ui.equation.Palette.Type.COMPARISON,
|
||||
0, 70, 18, 18,
|
||||
['\\leq',
|
||||
'\\geq',
|
||||
'\\prec',
|
||||
'\\succ',
|
||||
'\\preceq',
|
||||
'\\succeq',
|
||||
'\\ll',
|
||||
'\\gg',
|
||||
'\\equiv',
|
||||
'\\sim',
|
||||
'\\\simeq',
|
||||
'\\\asymp',
|
||||
'\\approx',
|
||||
'\\ne',
|
||||
'\\\subset',
|
||||
'\\supset',
|
||||
'\\subseteq',
|
||||
'\\supseteq',
|
||||
'\\in',
|
||||
'\\ni',
|
||||
'\\notin']);
|
||||
this.setSize(new goog.math.Size(7, 3));
|
||||
};
|
||||
goog.inherits(goog.ui.equation.ComparisonPalette, goog.ui.equation.Palette);
|
||||
@@ -0,0 +1,93 @@
|
||||
// Copyright 2008 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.
|
||||
|
||||
goog.provide('goog.ui.equation.EditorPane');
|
||||
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.Component');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* An abstract equation editor tab pane.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
|
||||
* @constructor
|
||||
* @extends {goog.ui.Component}
|
||||
*/
|
||||
goog.ui.equation.EditorPane = function(opt_domHelper) {
|
||||
goog.ui.Component.call(this, opt_domHelper);
|
||||
};
|
||||
goog.inherits(goog.ui.equation.EditorPane, goog.ui.Component);
|
||||
|
||||
|
||||
/**
|
||||
* A link to any available help documentation to be displayed in a "Learn more"
|
||||
* link. If not set through the equationeditor plugin constructor, the link
|
||||
* will be omitted.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.equation.EditorPane.prototype.helpUrl_ = '';
|
||||
|
||||
|
||||
/**
|
||||
* Sets the visibility of this tab pane.
|
||||
* @param {boolean} visible Whether this tab should become visible.
|
||||
*/
|
||||
goog.ui.equation.EditorPane.prototype.setVisible =
|
||||
function(visible) {
|
||||
goog.style.setElementShown(this.getElement(), visible);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the equation to show in this tab pane.
|
||||
* @param {string} equation The equation.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.equation.EditorPane.prototype.setEquation = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} The equation shown in this tab pane.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.equation.EditorPane.prototype.getEquation = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Sets the help link URL to show in this tab pane.
|
||||
* @param {string} url The help link URL.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.equation.EditorPane.prototype.setHelpUrl = function(url) {
|
||||
this.helpUrl_ = url;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} The help link URL.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.equation.EditorPane.prototype.getHelpUrl = function() {
|
||||
return this.helpUrl_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether the equation was modified.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.equation.EditorPane.prototype.isModified = goog.abstractMethod;
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
// Copyright 2008 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.
|
||||
|
||||
goog.provide('goog.ui.equation.EquationEditor');
|
||||
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('goog.ui.TabBar');
|
||||
goog.require('goog.ui.equation.ImageRenderer');
|
||||
goog.require('goog.ui.equation.TexPane');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* User interface for equation editor plugin.
|
||||
* @constructor
|
||||
* @param {Object} context The context that this equation editor runs in.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper DomHelper to use.
|
||||
* @param {string=} opt_helpUrl Help document URL to use in the "Learn more"
|
||||
* link.
|
||||
* @extends {goog.ui.Component}
|
||||
*/
|
||||
goog.ui.equation.EquationEditor = function(context, opt_domHelper,
|
||||
opt_helpUrl) {
|
||||
goog.base(this, opt_domHelper);
|
||||
|
||||
/**
|
||||
* The context this editor runs in.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
this.context_ = context;
|
||||
|
||||
/**
|
||||
* Help document URL to use in the "Learn more" link.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.helpUrl_ = opt_helpUrl || '';
|
||||
};
|
||||
goog.inherits(goog.ui.equation.EquationEditor, goog.ui.Component);
|
||||
|
||||
|
||||
/**
|
||||
* Constants for event names.
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.ui.equation.EquationEditor.EventType = {
|
||||
/**
|
||||
* Dispatched when equation changes.
|
||||
*/
|
||||
CHANGE: 'change'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The index of the last active tab. Zero means first tab.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.equation.EquationEditor.prototype.activeTabIndex_ = 0;
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.equation.EquationEditor.prototype.createDom = function() {
|
||||
goog.base(this, 'createDom');
|
||||
this.createDom_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates main editor contents.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.equation.EquationEditor.prototype.createDom_ = function() {
|
||||
var contentElement = this.getElement();
|
||||
|
||||
/** @desc Title of the visual equation editor tab. */
|
||||
var MSG_VISUAL_EDITOR = goog.getMsg('Editor');
|
||||
|
||||
/** @desc Title of the TeX equation editor tab. */
|
||||
var MSG_TEX_EDITOR = goog.getMsg('TeX');
|
||||
|
||||
// Create the main tabs
|
||||
var dom = this.dom_;
|
||||
var tabTop = dom.createDom('div',
|
||||
{'class': 'goog-tab-bar goog-tab-bar-top'},
|
||||
dom.createDom('div',
|
||||
{'class': 'goog-tab goog-tab-selected'}, MSG_VISUAL_EDITOR),
|
||||
dom.createDom('div', {'class': 'goog-tab'}, MSG_TEX_EDITOR));
|
||||
var tabClear = dom.createDom('div', {'class': 'goog-tab-bar-clear'});
|
||||
var tabContent = dom.createDom('div', {'class': 'ee-content'});
|
||||
dom.appendChild(contentElement, tabTop);
|
||||
dom.appendChild(contentElement, tabClear);
|
||||
dom.appendChild(contentElement, tabContent);
|
||||
|
||||
var tabBar = new goog.ui.TabBar();
|
||||
tabBar.decorate(tabTop);
|
||||
|
||||
/**
|
||||
* The tab bar.
|
||||
* @type {!goog.ui.TabBar}
|
||||
* @private
|
||||
*/
|
||||
this.tabBar_ = tabBar;
|
||||
|
||||
goog.events.listen(tabBar, goog.ui.Component.EventType.SELECT,
|
||||
goog.bind(this.handleTabSelect_, this));
|
||||
|
||||
var texEditor = new goog.ui.equation.TexPane(this.context_,
|
||||
this.helpUrl_, this.dom_);
|
||||
this.addChild(texEditor);
|
||||
texEditor.render(tabContent);
|
||||
|
||||
this.setVisibleTab_(0); // Make first tab visible
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the visibility of the editor.
|
||||
* @param {boolean} visible Whether the editor should be visible.
|
||||
*/
|
||||
goog.ui.equation.EquationEditor.prototype.setVisible = function(visible) {
|
||||
// Show active tab if visible, or none if not
|
||||
this.setVisibleTab_(visible ? this.activeTabIndex_ : -1);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the tab at the selected index as visible and all the rest as not
|
||||
* visible.
|
||||
* @param {number} tabIndex The tab index that is visible. -1 means no
|
||||
* tab is visible.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.equation.EquationEditor.prototype.setVisibleTab_ = function(tabIndex) {
|
||||
for (var i = 0; i < this.getChildCount(); i++) {
|
||||
this.getChildAt(i).setVisible(i == tabIndex);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.equation.EquationEditor.prototype.decorateInternal = function(element) {
|
||||
this.setElementInternal(element);
|
||||
this.createDom_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the encoded equation.
|
||||
* @return {string} The encoded equation.
|
||||
*/
|
||||
goog.ui.equation.EquationEditor.prototype.getEquation = function() {
|
||||
var sel = this.tabBar_.getSelectedTabIndex();
|
||||
return this.getChildAt(sel).getEquation();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} The html code to embed in the document.
|
||||
*/
|
||||
goog.ui.equation.EquationEditor.prototype.getHtml = function() {
|
||||
return goog.ui.equation.ImageRenderer.getHtml(this.getEquation());
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether the current equation is valid and can be used in a document.
|
||||
* @return {boolean} Whether the equation is valid.
|
||||
*/
|
||||
goog.ui.equation.EquationEditor.prototype.isValid = function() {
|
||||
return goog.ui.equation.ImageRenderer.isEquationTooLong(
|
||||
this.getEquation());
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles a tab selection by the user.
|
||||
* @param {goog.events.Event} e The event.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.equation.EquationEditor.prototype.handleTabSelect_ = function(e) {
|
||||
var sel = this.tabBar_.getSelectedTabIndex();
|
||||
if (sel != this.activeTabIndex_) {
|
||||
this.activeTabIndex_ = sel;
|
||||
this.setVisibleTab_(sel);
|
||||
}
|
||||
|
||||
// TODO(user) Pass equation from the tab to the other is modified
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Parse an equation and draw it.
|
||||
* Clears any previous displayed equation.
|
||||
* @param {string} equation The equation text to parse.
|
||||
*/
|
||||
goog.ui.equation.EquationEditor.prototype.setEquation = function(equation) {
|
||||
var sel = this.tabBar_.getSelectedTabIndex();
|
||||
this.getChildAt(sel).setEquation(equation);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.equation.EquationEditor.prototype.disposeInternal = function() {
|
||||
this.context_ = null;
|
||||
goog.base(this, 'disposeInternal');
|
||||
};
|
||||
@@ -0,0 +1,137 @@
|
||||
// Copyright 2008 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.
|
||||
|
||||
goog.provide('goog.ui.equation.EquationEditorDialog');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.classes');
|
||||
goog.require('goog.ui.Dialog');
|
||||
goog.require('goog.ui.equation.EquationEditor');
|
||||
goog.require('goog.ui.equation.PaletteManager');
|
||||
goog.require('goog.ui.equation.TexEditor');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* User interface for equation editor plugin standalone tests.
|
||||
* @constructor
|
||||
* @param {string=} opt_equation Encoded equation. If not specified, starts with
|
||||
* an empty equation.
|
||||
* @extends {goog.ui.Dialog}
|
||||
*/
|
||||
goog.ui.equation.EquationEditorDialog = function(opt_equation) {
|
||||
goog.ui.Dialog.call(this);
|
||||
this.setTitle('Equation Editor');
|
||||
|
||||
var buttonSet = new goog.ui.Dialog.ButtonSet();
|
||||
buttonSet.set(goog.ui.Dialog.DefaultButtonKeys.OK,
|
||||
opt_equation ? 'Save changes' : 'Insert equation',
|
||||
true);
|
||||
buttonSet.set(goog.ui.Dialog.DefaultButtonKeys.CANCEL,
|
||||
'Cancel', false, true);
|
||||
this.setButtonSet(buttonSet);
|
||||
|
||||
// Create the main editor contents.
|
||||
var contentElement = this.getContentElement();
|
||||
var domHelper = goog.dom.getDomHelper(contentElement);
|
||||
var context = this.populateContext_();
|
||||
|
||||
/**
|
||||
* The equation editor main API.
|
||||
* @type {goog.ui.equation.TexEditor}
|
||||
* @private
|
||||
*/
|
||||
this.equationEditor_ =
|
||||
new goog.ui.equation.TexEditor(context, '', domHelper);
|
||||
|
||||
this.equationEditor_.addEventListener(
|
||||
goog.ui.equation.EquationEditor.EventType.CHANGE,
|
||||
this.onChange_, false, this);
|
||||
|
||||
this.equationEditor_.render(this.getContentElement());
|
||||
this.setEquation(opt_equation || '');
|
||||
|
||||
goog.dom.classes.add(this.getDialogElement(), 'ee-modal-dialog');
|
||||
};
|
||||
goog.inherits(goog.ui.equation.EquationEditorDialog, goog.ui.Dialog);
|
||||
|
||||
|
||||
/**
|
||||
* The dialog's OK button element.
|
||||
* @type {Element?}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.equation.EquationEditorDialog.prototype.okButton_;
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.equation.EquationEditorDialog.prototype.setVisible = function(visible) {
|
||||
goog.base(this, 'setVisible', visible);
|
||||
this.equationEditor_.setVisible(visible);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Populates the context of this dialog.
|
||||
* @return {Object} The context that this dialog runs in.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.equation.EquationEditorDialog.prototype.populateContext_ = function() {
|
||||
var context = {};
|
||||
context.paletteManager = new goog.ui.equation.PaletteManager(
|
||||
this.getDomHelper());
|
||||
return context;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles CHANGE event fired when user changes equation.
|
||||
* @param {goog.ui.equation.ChangeEvent} e The event object.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.equation.EquationEditorDialog.prototype.onChange_ = function(e) {
|
||||
if (!this.okButton_) {
|
||||
this.okButton_ = this.getButtonSet().getButton(
|
||||
goog.ui.Dialog.DefaultButtonKeys.OK);
|
||||
}
|
||||
this.okButton_.disabled = !e.isValid;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the encoded equation.
|
||||
* @return {string} The encoded equation.
|
||||
*/
|
||||
goog.ui.equation.EquationEditorDialog.prototype.getEquation = function() {
|
||||
return this.equationEditor_.getEquation();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the encoded equation.
|
||||
* @param {string} equation The encoded equation.
|
||||
*/
|
||||
goog.ui.equation.EquationEditorDialog.prototype.setEquation =
|
||||
function(equation) {
|
||||
this.equationEditor_.setEquation(equation);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} The html code to embed in the document.
|
||||
*/
|
||||
goog.ui.equation.EquationEditorDialog.prototype.getHtml = function() {
|
||||
return this.equationEditor_.getHtml();
|
||||
};
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
// Copyright 2009 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.
|
||||
|
||||
goog.provide('goog.ui.equation.GreekPalette');
|
||||
|
||||
goog.require('goog.math.Size');
|
||||
goog.require('goog.ui.equation.Palette');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new Greek symbols palette.
|
||||
* @param {goog.ui.equation.PaletteManager} paletteManager The
|
||||
* manager of the palette.
|
||||
* @extends {goog.ui.equation.Palette}
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.equation.GreekPalette = function(paletteManager) {
|
||||
goog.ui.equation.Palette.call(this, paletteManager,
|
||||
goog.ui.equation.Palette.Type.GREEK,
|
||||
0, 30, 18, 18,
|
||||
['\\alpha',
|
||||
'\\beta',
|
||||
'\\gamma',
|
||||
'\\delta',
|
||||
'\\epsilon',
|
||||
'\\varepsilon',
|
||||
'\\zeta',
|
||||
'\\eta',
|
||||
'\\theta',
|
||||
'\\vartheta',
|
||||
'\\iota',
|
||||
'\\kappa',
|
||||
'\\lambda',
|
||||
'\\mu',
|
||||
'\\nu',
|
||||
'\\xi',
|
||||
'\\pi',
|
||||
'\\varpi',
|
||||
'\\rho',
|
||||
'\\varrho',
|
||||
'\\sigma',
|
||||
'\\varsigma',
|
||||
'\\tau',
|
||||
'\\upsilon',
|
||||
'\\phi',
|
||||
'\\varphi',
|
||||
'\\chi',
|
||||
'\\psi',
|
||||
'\\omega',
|
||||
'\\Gamma',
|
||||
'\\Delta',
|
||||
'\\Theta',
|
||||
'\\Lambda',
|
||||
'\\Xi',
|
||||
'\\Pi',
|
||||
'\\Sigma',
|
||||
'\\Upsilon',
|
||||
'\\Phi',
|
||||
'\\Psi',
|
||||
'\\Omega']);
|
||||
this.setSize(new goog.math.Size(7, 6));
|
||||
};
|
||||
goog.inherits(goog.ui.equation.GreekPalette, goog.ui.equation.Palette);
|
||||
@@ -0,0 +1,190 @@
|
||||
// Copyright 2009 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 Functions for rendering the equation images.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.equation.ImageRenderer');
|
||||
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.dom.classes');
|
||||
goog.require('goog.string');
|
||||
goog.require('goog.uri.utils');
|
||||
|
||||
|
||||
/**
|
||||
* The server name which renders the equations.
|
||||
* We use https as equations may be embedded in https pages
|
||||
* and using https prevents mixed content warnings. Note that
|
||||
* https equations work only on google.com domains.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.equation.ImageRenderer.SERVER_NAME_ =
|
||||
'https://www.google.com';
|
||||
|
||||
|
||||
/**
|
||||
* The longest equation which may be displayed, in characters.
|
||||
* @type {number}
|
||||
*/
|
||||
goog.ui.equation.ImageRenderer.MAX_EQUATION_LENGTH = 200;
|
||||
|
||||
|
||||
/**
|
||||
* Class to put on our equations IMG elements.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.equation.ImageRenderer.EE_IMG_CLASS = 'ee_img';
|
||||
|
||||
|
||||
/**
|
||||
* Non-standard to put on our equations IMG elements. Useful when classes need
|
||||
* to be scrubbed from the user-generated HTML, but non-standard attributes
|
||||
* can be white-listed.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.equation.ImageRenderer.EE_IMG_ATTR = 'eeimg';
|
||||
|
||||
|
||||
/**
|
||||
* Vertical alignment for the equations IMG elements.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.equation.ImageRenderer.EE_IMG_VERTICAL_ALIGN = 'middle';
|
||||
|
||||
|
||||
/**
|
||||
* The default background color as used in the img url, which is fully
|
||||
* transparent white.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.equation.ImageRenderer.BACKGROUND_COLOR = 'FFFFFF00';
|
||||
|
||||
|
||||
/**
|
||||
* The default foreground color as used in the img url, which is black.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.equation.ImageRenderer.FOREGROUND_COLOR = '000000';
|
||||
|
||||
|
||||
/**
|
||||
* Class to put on IMG elements to keep the resize property bubble from
|
||||
* appearing. This is different from PLACEHOLDER_IMG_CLASS because it's
|
||||
* reasonable in some cases to be able to resize a placeholder (which should
|
||||
* be reflected when the placeholder is replaced with the other content).
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.equation.ImageRenderer.NO_RESIZE_IMG_CLASS =
|
||||
goog.getCssName('tr_noresize');
|
||||
|
||||
|
||||
/**
|
||||
* Returns the equation image src url given the equation.
|
||||
* @param {string} equation The equation.
|
||||
* @return {string} The equation image src url (empty string in case the
|
||||
* equation was empty).
|
||||
*/
|
||||
goog.ui.equation.ImageRenderer.getImageUrl = function(equation) {
|
||||
if (!equation) {
|
||||
return '';
|
||||
}
|
||||
|
||||
var url = goog.ui.equation.ImageRenderer.SERVER_NAME_ +
|
||||
'/chart?cht=tx' +
|
||||
'&chf=bg,s,' +
|
||||
goog.ui.equation.ImageRenderer.BACKGROUND_COLOR +
|
||||
'&chco=' +
|
||||
goog.ui.equation.ImageRenderer.FOREGROUND_COLOR +
|
||||
'&chl=' +
|
||||
encodeURIComponent(equation);
|
||||
return url;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the equation string src for given image url.
|
||||
* @param {string} imageUrl The image url.
|
||||
* @return {string?} The equation string, null if imageUrl cannot be parsed.
|
||||
*/
|
||||
goog.ui.equation.ImageRenderer.getEquationFromImageUrl = function(imageUrl) {
|
||||
return goog.uri.utils.getParamValue(imageUrl, 'chl');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the equation string from the given equation IMG node. Returns empty
|
||||
* string if the src attribute of the is not a valid equation url.
|
||||
* @param {Element} equationNode The equation IMG element.
|
||||
* @return {string} The equation string.
|
||||
*/
|
||||
goog.ui.equation.ImageRenderer.getEquationFromImage = function(equationNode) {
|
||||
var url = equationNode.getAttribute('src');
|
||||
if (!url) {
|
||||
// Should never happen.
|
||||
return '';
|
||||
}
|
||||
return goog.ui.equation.ImageRenderer.getEquationFromImageUrl(
|
||||
url) || '';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether given node is an equation element.
|
||||
* @param {Node} node The node to check.
|
||||
* @return {boolean} Whether given node is an equation element.
|
||||
*/
|
||||
goog.ui.equation.ImageRenderer.isEquationElement = function(node) {
|
||||
return node.nodeName == goog.dom.TagName.IMG &&
|
||||
(node.getAttribute(
|
||||
goog.ui.equation.ImageRenderer.EE_IMG_ATTR) ||
|
||||
goog.dom.classes.has(node,
|
||||
goog.ui.equation.ImageRenderer.EE_IMG_CLASS));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the html for the html image tag for the given equation.
|
||||
* @param {string} equation The equation.
|
||||
* @return {string} The html code to embed in the document.
|
||||
*/
|
||||
goog.ui.equation.ImageRenderer.getHtml = function(equation) {
|
||||
var imageSrc =
|
||||
goog.ui.equation.ImageRenderer.getImageUrl(equation);
|
||||
if (!imageSrc) {
|
||||
return '';
|
||||
}
|
||||
return '<img src="' + imageSrc + '" ' +
|
||||
'alt="' + goog.string.htmlEscape(equation) + '" ' +
|
||||
'class="' + goog.ui.equation.ImageRenderer.EE_IMG_CLASS +
|
||||
' ' + goog.ui.equation.ImageRenderer.NO_RESIZE_IMG_CLASS +
|
||||
'" ' + goog.ui.equation.ImageRenderer.EE_IMG_ATTR + '="1" ' +
|
||||
'style="vertical-align: ' +
|
||||
goog.ui.equation.ImageRenderer.EE_IMG_VERTICAL_ALIGN + '">';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether equation is too long to be displayed.
|
||||
* @param {string} equation The equation to test.
|
||||
* @return {boolean} Whether the equation is too long.
|
||||
*/
|
||||
goog.ui.equation.ImageRenderer.isEquationTooLong = function(equation) {
|
||||
return equation.length >
|
||||
goog.ui.equation.ImageRenderer.MAX_EQUATION_LENGTH;
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright 2009 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.
|
||||
|
||||
goog.provide('goog.ui.equation.MathPalette');
|
||||
|
||||
goog.require('goog.math.Size');
|
||||
goog.require('goog.ui.equation.Palette');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new math palette.
|
||||
* @param {goog.ui.equation.PaletteManager} paletteManager The
|
||||
* manager of the palette.
|
||||
* @extends {goog.ui.equation.Palette}
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.equation.MathPalette = function(paletteManager) {
|
||||
goog.ui.equation.Palette.call(this, paletteManager,
|
||||
goog.ui.equation.Palette.Type.MATH,
|
||||
0, 90, 30, 56,
|
||||
['x_{a}',
|
||||
'x^{b}',
|
||||
'x_{a}^{b}',
|
||||
'\\bar{x}',
|
||||
'\\tilde{x}',
|
||||
'\\frac{a}{b}',
|
||||
'\\sqrt{x}',
|
||||
'\\sqrt[n]{x}',
|
||||
'\\bigcap_{a}^{b}',
|
||||
'\\bigcup_{a}^{b}',
|
||||
'\\prod_{a}^{b}',
|
||||
'\\coprod_{a}^{b}',
|
||||
'\\left( x \\right)',
|
||||
'\\left[ x \\right]',
|
||||
'\\left\\{ x \\right\\}',
|
||||
'\\left| x \\right|',
|
||||
'\\int_{a}^{b}',
|
||||
'\\oint_{a}^{b}',
|
||||
'\\sum_{a}^{b}{x}',
|
||||
'\\lim_{a \\rightarrow b}{x}']);
|
||||
this.setSize(new goog.math.Size(10, 2));
|
||||
};
|
||||
goog.inherits(goog.ui.equation.MathPalette, goog.ui.equation.Palette);
|
||||
@@ -0,0 +1,86 @@
|
||||
// Copyright 2009 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.
|
||||
|
||||
goog.provide('goog.ui.equation.MenuPalette');
|
||||
goog.provide('goog.ui.equation.MenuPaletteRenderer');
|
||||
|
||||
goog.require('goog.math.Size');
|
||||
goog.require('goog.ui.PaletteRenderer');
|
||||
goog.require('goog.ui.equation.Palette');
|
||||
goog.require('goog.ui.equation.PaletteRenderer');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new menu palette.
|
||||
* @param {goog.ui.equation.PaletteManager} paletteManager The
|
||||
* manager of the palette.
|
||||
* @extends {goog.ui.equation.Palette}
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.equation.MenuPalette = function(paletteManager) {
|
||||
goog.ui.equation.Palette.call(this, paletteManager,
|
||||
goog.ui.equation.Palette.Type.MENU,
|
||||
0, 0, 46, 18,
|
||||
[goog.ui.equation.Palette.Type.GREEK,
|
||||
goog.ui.equation.Palette.Type.SYMBOL,
|
||||
goog.ui.equation.Palette.Type.COMPARISON,
|
||||
goog.ui.equation.Palette.Type.MATH,
|
||||
goog.ui.equation.Palette.Type.ARROW],
|
||||
goog.ui.equation.MenuPaletteRenderer.getInstance());
|
||||
this.setSize(new goog.math.Size(5, 1));
|
||||
};
|
||||
goog.inherits(goog.ui.equation.MenuPalette, goog.ui.equation.Palette);
|
||||
|
||||
|
||||
/**
|
||||
* The CSS class name for the palette.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.equation.MenuPalette.CSS_CLASS = 'ee-menu-palette';
|
||||
|
||||
|
||||
/**
|
||||
* Overrides the setVisible method to make menu palette always visible.
|
||||
* @param {boolean} visible Whether to show or hide the component.
|
||||
* @param {boolean=} opt_force If true, doesn't check whether the component
|
||||
* already has the requested visibility, and doesn't dispatch any events.
|
||||
* @return {boolean} Whether the visibility was changed.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.equation.MenuPalette.prototype.setVisible = function(
|
||||
visible, opt_force) {
|
||||
return goog.base(this, 'setVisible', true, opt_force);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The renderer for menu palette.
|
||||
* @extends {goog.ui.equation.PaletteRenderer}
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.equation.MenuPaletteRenderer = function() {
|
||||
goog.ui.PaletteRenderer.call(this);
|
||||
};
|
||||
goog.inherits(goog.ui.equation.MenuPaletteRenderer,
|
||||
goog.ui.equation.PaletteRenderer);
|
||||
goog.addSingletonGetter(goog.ui.equation.MenuPaletteRenderer);
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.equation.MenuPaletteRenderer.prototype.getCssClass =
|
||||
function() {
|
||||
return goog.ui.equation.MenuPalette.CSS_CLASS;
|
||||
};
|
||||
@@ -0,0 +1,288 @@
|
||||
// Copyright 2009 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 A palette of icons.
|
||||
* The icons are generated from a single sprite image that
|
||||
* is used for multiple palettes.
|
||||
* All icons of a single palette must be on the same sprite row
|
||||
* (same y coordinate) and all have the same width.
|
||||
* Each item has an associated action command that should be taken
|
||||
* when certain event is dispatched.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.equation.Palette');
|
||||
goog.provide('goog.ui.equation.PaletteEvent');
|
||||
goog.provide('goog.ui.equation.PaletteRenderer');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.events.Event');
|
||||
goog.require('goog.ui.Palette');
|
||||
goog.require('goog.ui.PaletteRenderer');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new palette.
|
||||
* @param {goog.ui.equation.PaletteManager} paletteManager The
|
||||
* manager of the palette.
|
||||
* @param {goog.ui.equation.Palette.Type} type The type of the
|
||||
* palette.
|
||||
* @param {number} spriteX Coordinate of first icon in sprite.
|
||||
* @param {number} spriteY Coordinate of top of all icons in sprite.
|
||||
* @param {number} itemWidth Pixel width of each palette icon.
|
||||
* @param {number} itemHeight Pixel height of each palette icon.
|
||||
* @param {Array.<string>=} opt_actions An optional action list for palette
|
||||
* elements. The number of actions determine the number of palette
|
||||
* elements.
|
||||
* @param {goog.ui.PaletteRenderer=} opt_renderer Optional customized renderer,
|
||||
* defaults to {@link goog.ui.PaletteRenderer}.
|
||||
* @extends {goog.ui.Palette}
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.equation.Palette = function(paletteManager, type, spriteX,
|
||||
spriteY, itemWidth, itemHeight, opt_actions, opt_renderer) {
|
||||
|
||||
/**
|
||||
* The type of the palette.
|
||||
* @type {goog.ui.equation.Palette.Type}
|
||||
* @private
|
||||
*/
|
||||
this.type_ = type;
|
||||
|
||||
/**
|
||||
* The palette actions.
|
||||
* @type {Array.<string>}
|
||||
* @private
|
||||
*/
|
||||
this.actions_ = opt_actions || [];
|
||||
|
||||
var renderer =
|
||||
opt_renderer ||
|
||||
goog.ui.equation.PaletteRenderer.getInstance();
|
||||
|
||||
// Create a div element for each icon.
|
||||
var elements = [];
|
||||
var x = - spriteX;
|
||||
var y = - spriteY;
|
||||
for (var i = 0; i < opt_actions.length; i++) {
|
||||
elements.push(paletteManager.getDomHelper().createDom(
|
||||
goog.dom.TagName.DIV,
|
||||
{'class': renderer.getItemCssClass(),
|
||||
'style': 'width:' + itemWidth +
|
||||
'px;height:' + itemHeight +
|
||||
'px;' +
|
||||
'background-position:' +
|
||||
x + 'px ' + y + 'px;'}));
|
||||
x -= itemWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* The palette manager that manages all the palettes.
|
||||
* @type {goog.ui.equation.PaletteManager}
|
||||
* @private
|
||||
*/
|
||||
this.paletteManager_ = paletteManager;
|
||||
|
||||
goog.ui.Palette.call(this, elements, renderer, paletteManager.getDomHelper());
|
||||
};
|
||||
goog.inherits(goog.ui.equation.Palette, goog.ui.Palette);
|
||||
|
||||
|
||||
/**
|
||||
* The type of possible palettes. They are made short to minimize JS size.
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.ui.equation.Palette.Type = {
|
||||
MENU: 'mn',
|
||||
GREEK: 'g',
|
||||
SYMBOL: 's',
|
||||
COMPARISON: 'c',
|
||||
MATH: 'm',
|
||||
ARROW: 'a'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The CSS class name for the palette.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.equation.Palette.CSS_CLASS = 'ee-palette';
|
||||
|
||||
|
||||
/**
|
||||
* Returns the type of the palette.
|
||||
* @return {goog.ui.equation.Palette.Type} The type of the palette.
|
||||
*/
|
||||
goog.ui.equation.Palette.prototype.getType = function() {
|
||||
return this.type_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the palette manager.
|
||||
* @return {goog.ui.equation.PaletteManager} The palette manager
|
||||
* that manages all the palette.
|
||||
*/
|
||||
goog.ui.equation.Palette.prototype.getPaletteManager = function() {
|
||||
return this.paletteManager_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns actions for this palette.
|
||||
* @return {Array.<string>} The palette actions.
|
||||
*/
|
||||
goog.ui.equation.Palette.prototype.getActions = function() {
|
||||
return this.actions_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the action for a given index.
|
||||
* @param {number} index The index of the action to be retrieved.
|
||||
* @return {string?} The action for given index, or {@code null} if action is
|
||||
* not found.
|
||||
*/
|
||||
goog.ui.equation.Palette.prototype.getAction = function(index) {
|
||||
return (index >= 0 && index < this.actions_.length) ?
|
||||
this.actions_[index] : null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles mouseup events. Overrides {@link goog.ui.Palette#handleMouseUp}
|
||||
* by dispatching a {@link goog.ui.equation.PaletteEvent}.
|
||||
* @param {goog.events.Event} e Mouse event to handle.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.equation.Palette.prototype.handleMouseUp = function(e) {
|
||||
goog.base(this, 'handleMouseUp', e);
|
||||
|
||||
this.paletteManager_.dispatchEvent(
|
||||
new goog.ui.equation.PaletteEvent(
|
||||
goog.ui.equation.PaletteEvent.Type.ACTION, this));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles mouse out events. Overrides {@link goog.ui.Palette#handleMouseOut}
|
||||
* by deactivate the palette.
|
||||
* @param {goog.events.BrowserEvent} e Mouse event to handle.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.equation.Palette.prototype.handleMouseOut = function(e) {
|
||||
goog.base(this, 'handleMouseOut', e);
|
||||
|
||||
// Ignore mouse moves between descendants.
|
||||
if (e.relatedTarget &&
|
||||
!goog.dom.contains(this.getElement(), e.relatedTarget)) {
|
||||
this.paletteManager_.deactivate();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles mouse over events. Overrides {@link goog.ui.Palette#handleMouseOver}
|
||||
* by stop deactivating the palette. When mouse leaves the palettes, the
|
||||
* palettes will be deactivated after a centain period of time. Reentering the
|
||||
* palettes inside this time will stop the timer and cancel the deactivation.
|
||||
* @param {goog.events.BrowserEvent} e Mouse event to handle.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.equation.Palette.prototype.handleMouseOver = function(e) {
|
||||
goog.base(this, 'handleMouseOver', e);
|
||||
|
||||
// Ignore mouse moves between descendants.
|
||||
if (e.relatedTarget &&
|
||||
!goog.dom.contains(this.getElement(), e.relatedTarget)) {
|
||||
|
||||
// Stop the timer to deactivate the palettes.
|
||||
this.paletteManager_.stopDeactivation();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The event that palettes dispatches.
|
||||
* @param {string} type Type of the event.
|
||||
* @param {goog.ui.equation.Palette} palette The palette that the
|
||||
* event is fired on.
|
||||
* @param {Element=} opt_target The optional target of the event.
|
||||
* @constructor
|
||||
* @extends {goog.events.Event}
|
||||
*/
|
||||
goog.ui.equation.PaletteEvent = function(type, palette, opt_target) {
|
||||
goog.events.Event.call(this, type, opt_target);
|
||||
|
||||
/**
|
||||
* The palette the event is fired from.
|
||||
* @type {goog.ui.equation.Palette}
|
||||
* @private
|
||||
*/
|
||||
this.palette_ = palette;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The type of events that can be fired on palettes.
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.ui.equation.PaletteEvent.Type = {
|
||||
|
||||
// Take the action that is associated with the palette item.
|
||||
ACTION: 'a'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the palette that this event is fired from.
|
||||
* @return {goog.ui.equation.Palette} The palette this event is
|
||||
* fired from.
|
||||
*/
|
||||
goog.ui.equation.PaletteEvent.prototype.getPalette = function() {
|
||||
return this.palette_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The renderer for palette.
|
||||
* @extends {goog.ui.PaletteRenderer}
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.equation.PaletteRenderer = function() {
|
||||
goog.ui.PaletteRenderer.call(this);
|
||||
};
|
||||
goog.inherits(goog.ui.equation.PaletteRenderer, goog.ui.PaletteRenderer);
|
||||
goog.addSingletonGetter(goog.ui.equation.PaletteRenderer);
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.equation.PaletteRenderer.prototype.getCssClass =
|
||||
function() {
|
||||
return goog.ui.equation.Palette.CSS_CLASS;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the CSS class name for the palette item.
|
||||
* @return {string} The CSS class name of the palette item.
|
||||
*/
|
||||
goog.ui.equation.PaletteRenderer.prototype.getItemCssClass = function() {
|
||||
return this.getCssClass() + '-item';
|
||||
};
|
||||
@@ -0,0 +1,203 @@
|
||||
// Copyright 2009 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.
|
||||
|
||||
goog.provide('goog.ui.equation.PaletteManager');
|
||||
|
||||
goog.require('goog.Timer');
|
||||
goog.require('goog.events.EventHandler');
|
||||
goog.require('goog.events.EventTarget');
|
||||
goog.require('goog.ui.equation.ArrowPalette');
|
||||
goog.require('goog.ui.equation.ComparisonPalette');
|
||||
goog.require('goog.ui.equation.GreekPalette');
|
||||
goog.require('goog.ui.equation.MathPalette');
|
||||
goog.require('goog.ui.equation.MenuPalette');
|
||||
goog.require('goog.ui.equation.Palette');
|
||||
goog.require('goog.ui.equation.SymbolPalette');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructs the palette manager that manages all the palettes in Equation
|
||||
* Editor.
|
||||
* @param {!goog.dom.DomHelper} domHelper The DOM helper to be used for
|
||||
* document interaction.
|
||||
* @constructor
|
||||
* @extends {goog.events.EventTarget}
|
||||
*/
|
||||
goog.ui.equation.PaletteManager = function(domHelper) {
|
||||
goog.events.EventTarget.call(this);
|
||||
|
||||
/** @private {!goog.dom.DomHelper} */
|
||||
this.domHelper_ = domHelper;
|
||||
|
||||
/**
|
||||
* The map of palette type and instance pair.
|
||||
* @type {Object.<string, goog.ui.equation.Palette>}
|
||||
* @private
|
||||
*/
|
||||
this.paletteMap_ = {};
|
||||
|
||||
/**
|
||||
* The current active palette.
|
||||
* @type {goog.ui.equation.Palette}
|
||||
* @private
|
||||
*/
|
||||
this.activePalette_ = null;
|
||||
|
||||
/**
|
||||
* The event handler for managing events.
|
||||
* @type {goog.events.EventHandler}
|
||||
* @private
|
||||
*/
|
||||
this.eventHandler_ = new goog.events.EventHandler(this);
|
||||
|
||||
/**
|
||||
* The timer used to add grace period when deactivate palettes.
|
||||
* @type {goog.Timer}
|
||||
* @private
|
||||
*/
|
||||
this.deactivationTimer_ = new goog.Timer(300);
|
||||
|
||||
this.eventHandler_.listen(this.deactivationTimer_, goog.Timer.TICK,
|
||||
this.handleDeactivation_);
|
||||
|
||||
};
|
||||
goog.inherits(goog.ui.equation.PaletteManager,
|
||||
goog.events.EventTarget);
|
||||
|
||||
|
||||
/**
|
||||
* Clears the deactivation timer. This is used to prevent palette manager
|
||||
* deactivation when mouse pointer is moved outside palettes and moved back
|
||||
* quickly inside a grace period.
|
||||
*/
|
||||
goog.ui.equation.PaletteManager.prototype.stopDeactivation = function() {
|
||||
this.deactivationTimer_.stop();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the palette instance of given type.
|
||||
* @param {goog.ui.equation.Palette.Type} type The type of palette
|
||||
* to get.
|
||||
* @return {goog.ui.equation.Palette} The palette instance of given
|
||||
* type. A new instance will be created. If the instance doesn't exist.
|
||||
*/
|
||||
goog.ui.equation.PaletteManager.prototype.getPalette =
|
||||
function(type) {
|
||||
var paletteMap = this.paletteMap_;
|
||||
var palette = paletteMap[type];
|
||||
if (!palette) {
|
||||
switch (type) {
|
||||
case goog.ui.equation.Palette.Type.MENU:
|
||||
palette = new goog.ui.equation.MenuPalette(this);
|
||||
break;
|
||||
case goog.ui.equation.Palette.Type.GREEK:
|
||||
palette = new goog.ui.equation.GreekPalette(this);
|
||||
break;
|
||||
case goog.ui.equation.Palette.Type.SYMBOL:
|
||||
palette = new goog.ui.equation.SymbolPalette(this);
|
||||
break;
|
||||
case goog.ui.equation.Palette.Type.COMPARISON:
|
||||
palette = new goog.ui.equation.ComparisonPalette(this);
|
||||
break;
|
||||
case goog.ui.equation.Palette.Type.MATH:
|
||||
palette = new goog.ui.equation.MathPalette(this);
|
||||
break;
|
||||
case goog.ui.equation.Palette.Type.ARROW:
|
||||
palette = new goog.ui.equation.ArrowPalette(this);
|
||||
break;
|
||||
default:
|
||||
throw new Error('Invalid palette type!');
|
||||
}
|
||||
paletteMap[type] = palette;
|
||||
}
|
||||
return palette;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the palette instance of given type to be the active one.
|
||||
* @param {goog.ui.equation.Palette.Type} type The type of the
|
||||
* palette to set active.
|
||||
* @return {goog.ui.equation.Palette} The palette instance of given
|
||||
* type. A new instance will be created, if the instance doesn't exist.
|
||||
*/
|
||||
goog.ui.equation.PaletteManager.prototype.setActive =
|
||||
function(type) {
|
||||
var palette = this.activePalette_;
|
||||
if (palette) {
|
||||
palette.setVisible(false);
|
||||
}
|
||||
|
||||
palette = this.getPalette(type);
|
||||
this.activePalette_ = palette;
|
||||
palette.setVisible(true);
|
||||
|
||||
return palette;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the active palette.
|
||||
* @return {goog.ui.equation.Palette} The active palette.
|
||||
*/
|
||||
goog.ui.equation.PaletteManager.prototype.getActive = function() {
|
||||
return this.activePalette_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Starts the deactivation of open palette.
|
||||
* This method has a slight delay before doing the real deactivation. This
|
||||
* helps prevent sudden disappearing of palettes when user moves mouse outside
|
||||
* them just briefly (and maybe accidentally). If you really want to deactivate
|
||||
* the active palette, use {@link #deactivateNow()} instead.
|
||||
*/
|
||||
goog.ui.equation.PaletteManager.prototype.deactivate = function() {
|
||||
this.deactivationTimer_.start();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Deactivate the open palette immediately.
|
||||
*/
|
||||
goog.ui.equation.PaletteManager.prototype.deactivateNow = function() {
|
||||
this.handleDeactivation_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Internal process of deactivation of the manager.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.equation.PaletteManager.prototype.handleDeactivation_ = function() {
|
||||
this.setActive(goog.ui.equation.Palette.Type.MENU);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {!goog.dom.DomHelper} This object's DOM helper.
|
||||
*/
|
||||
goog.ui.equation.PaletteManager.prototype.getDomHelper = function() {
|
||||
return this.domHelper_;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.equation.PaletteManager.prototype.disposeInternal = function() {
|
||||
goog.base(this, 'disposeInternal');
|
||||
this.activePalette_ = null;
|
||||
this.paletteMap_ = null;
|
||||
};
|
||||
@@ -0,0 +1,74 @@
|
||||
// Copyright 2009 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 A palette of symbols.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.equation.SymbolPalette');
|
||||
|
||||
goog.require('goog.math.Size');
|
||||
goog.require('goog.ui.equation.Palette');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new symbols palette.
|
||||
* @param {goog.ui.equation.PaletteManager} paletteManager The
|
||||
* manager of the palette.
|
||||
* @extends {goog.ui.equation.Palette}
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.equation.SymbolPalette = function(paletteManager) {
|
||||
goog.ui.equation.Palette.call(this, paletteManager,
|
||||
goog.ui.equation.Palette.Type.SYMBOL,
|
||||
0, 50, 18, 18,
|
||||
['\\times',
|
||||
'\\div',
|
||||
'\\cdot',
|
||||
'\\pm',
|
||||
'\\mp',
|
||||
'\\ast',
|
||||
'\\star',
|
||||
'\\circ',
|
||||
'\\bullet',
|
||||
'\\oplus',
|
||||
'\\ominus',
|
||||
'\\oslash',
|
||||
'\\otimes',
|
||||
'\\odot',
|
||||
'\\dagger',
|
||||
'\\ddagger',
|
||||
'\\vee',
|
||||
'\\wedge',
|
||||
'\\cap',
|
||||
'\\cup',
|
||||
'\\aleph',
|
||||
'\\Re',
|
||||
'\\Im',
|
||||
'\\top',
|
||||
'\\bot',
|
||||
'\\infty',
|
||||
'\\partial',
|
||||
'\\forall',
|
||||
'\\exists',
|
||||
'\\neg',
|
||||
'\\angle',
|
||||
'\\triangle',
|
||||
'\\diamond']);
|
||||
|
||||
this.setSize(new goog.math.Size(7, 5));
|
||||
};
|
||||
goog.inherits(goog.ui.equation.SymbolPalette, goog.ui.equation.Palette);
|
||||
@@ -0,0 +1,141 @@
|
||||
// Copyright 2009 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.
|
||||
|
||||
goog.provide('goog.ui.equation.TexEditor');
|
||||
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('goog.ui.equation.ImageRenderer');
|
||||
goog.require('goog.ui.equation.TexPane');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* User interface for equation editor plugin.
|
||||
* @constructor
|
||||
* @param {Object} context The context that this Tex editor runs in.
|
||||
* @param {string} helpUrl URL pointing to help documentation.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper DomHelper to use.
|
||||
* @extends {goog.ui.Component}
|
||||
*/
|
||||
goog.ui.equation.TexEditor = function(context, helpUrl, opt_domHelper) {
|
||||
goog.ui.Component.call(this, opt_domHelper);
|
||||
|
||||
/**
|
||||
* The context that this Tex editor runs in.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
this.context_ = context;
|
||||
|
||||
/**
|
||||
* A URL pointing to help documentation.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.helpUrl_ = helpUrl;
|
||||
};
|
||||
goog.inherits(goog.ui.equation.TexEditor, goog.ui.Component);
|
||||
|
||||
|
||||
/**
|
||||
* The TeX editor pane.
|
||||
* @type {goog.ui.equation.TexPane}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.equation.TexEditor.prototype.texPane_ = null;
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.equation.TexEditor.prototype.createDom = function() {
|
||||
goog.base(this, 'createDom');
|
||||
this.createDom_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates main editor contents.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.equation.TexEditor.prototype.createDom_ = function() {
|
||||
var contentElement = this.getElement();
|
||||
this.texPane_ = new goog.ui.equation.TexPane(this.context_,
|
||||
this.helpUrl_, this.dom_);
|
||||
this.addChild(this.texPane_);
|
||||
this.texPane_.render(contentElement);
|
||||
this.texPane_.setVisible(true);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.equation.TexEditor.prototype.decorateInternal = function(element) {
|
||||
this.setElementInternal(element);
|
||||
this.createDom_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the encoded equation.
|
||||
* @return {string} The encoded equation.
|
||||
*/
|
||||
goog.ui.equation.TexEditor.prototype.getEquation = function() {
|
||||
return this.texPane_.getEquation();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Parse an equation and draw it.
|
||||
* Clears any previous displayed equation.
|
||||
* @param {string} equation The equation text to parse.
|
||||
*/
|
||||
goog.ui.equation.TexEditor.prototype.setEquation = function(equation) {
|
||||
this.texPane_.setEquation(equation);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} The html code to embed in the document.
|
||||
*/
|
||||
goog.ui.equation.TexEditor.prototype.getHtml = function() {
|
||||
return goog.ui.equation.ImageRenderer.getHtml(this.getEquation());
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether the current equation is valid and can be used in a document.
|
||||
* @return {boolean} Whether the equation valid.
|
||||
*/
|
||||
goog.ui.equation.TexEditor.prototype.isValid = function() {
|
||||
return goog.ui.equation.ImageRenderer.isEquationTooLong(
|
||||
this.getEquation());
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the visibility of the editor.
|
||||
* @param {boolean} visible Whether the editor should be visible.
|
||||
*/
|
||||
goog.ui.equation.TexEditor.prototype.setVisible = function(visible) {
|
||||
this.texPane_.setVisible(visible);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.equation.TexEditor.prototype.disposeInternal = function() {
|
||||
if (this.texPane_) {
|
||||
this.texPane_.dispose();
|
||||
}
|
||||
this.context_ = null;
|
||||
goog.base(this, 'disposeInternal');
|
||||
};
|
||||
|
||||
@@ -0,0 +1,444 @@
|
||||
// Copyright 2008 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.
|
||||
|
||||
goog.provide('goog.ui.equation.TexPane');
|
||||
|
||||
goog.require('goog.Timer');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.dom.selection');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.events.InputHandler');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.equation.ChangeEvent');
|
||||
goog.require('goog.ui.equation.EditorPane');
|
||||
goog.require('goog.ui.equation.ImageRenderer');
|
||||
goog.require('goog.ui.equation.Palette');
|
||||
goog.require('goog.ui.equation.PaletteEvent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* User interface for TeX equation editor tab pane.
|
||||
* @param {Object} context The context this Tex editor pane runs in.
|
||||
* @param {string} helpUrl The help link URL.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
|
||||
* @constructor
|
||||
* @extends {goog.ui.equation.EditorPane}
|
||||
*/
|
||||
goog.ui.equation.TexPane = function(
|
||||
context, helpUrl, opt_domHelper) {
|
||||
goog.ui.equation.EditorPane.call(this, opt_domHelper);
|
||||
|
||||
this.setHelpUrl(helpUrl);
|
||||
|
||||
/**
|
||||
* The palette manager instance.
|
||||
* @type {goog.ui.equation.PaletteManager}
|
||||
* @private
|
||||
*/
|
||||
this.paletteManager_ =
|
||||
/** @type {goog.ui.equation.PaletteManager} */(
|
||||
context.paletteManager);
|
||||
};
|
||||
goog.inherits(goog.ui.equation.TexPane,
|
||||
goog.ui.equation.EditorPane);
|
||||
|
||||
|
||||
/**
|
||||
* The CSS class name for the preview container.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.equation.TexPane.PREVIEW_CONTAINER_CSS_CLASS =
|
||||
'ee-preview-container';
|
||||
|
||||
|
||||
/**
|
||||
* The CSS class name for section titles.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.equation.TexPane.SECTION_TITLE_CSS_CLASS =
|
||||
'ee-section-title';
|
||||
|
||||
|
||||
/**
|
||||
* The CSS class name for section titles that float left.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.equation.TexPane.SECTION_TITLE_FLOAT_CSS_CLASS =
|
||||
'ee-section-title-floating';
|
||||
|
||||
|
||||
/**
|
||||
* The CSS id name for the link to "Learn more".
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.equation.TexPane.SECTION_LEARN_MORE_CSS_ID =
|
||||
'ee-section-learn-more';
|
||||
|
||||
|
||||
/**
|
||||
* The CSS class name for the Tex editor.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.equation.TexPane.TEX_EDIT_CSS_CLASS = 'ee-tex';
|
||||
|
||||
|
||||
/**
|
||||
* The CSS class name for the preview container.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.equation.TexPane.WARNING_CLASS =
|
||||
'ee-warning';
|
||||
|
||||
|
||||
/**
|
||||
* The content div of the TeX editor.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.equation.TexPane.prototype.texEditorElement_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* The container div for the server-generated image of the equation.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.equation.TexPane.prototype.previewContainer_;
|
||||
|
||||
|
||||
/**
|
||||
* An inner container used to layout all the elements in Tex Editor.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.equation.TexPane.prototype.innerContainer_;
|
||||
|
||||
|
||||
/**
|
||||
* The textarea for free form TeX.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.equation.TexPane.prototype.texEdit_;
|
||||
|
||||
|
||||
/**
|
||||
* The input handler for Tex editor.
|
||||
* @type {goog.events.InputHandler}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.equation.TexPane.prototype.texInputHandler_;
|
||||
|
||||
|
||||
/**
|
||||
* The last text that was renderred as an image.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.equation.TexPane.prototype.lastRenderredText_ = '';
|
||||
|
||||
|
||||
/**
|
||||
* A sequence number for text change events. Used to delay drawing
|
||||
* until the user paused typing.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.equation.TexPane.prototype.changeSequence_ = 0;
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.equation.TexPane.prototype.createDom = function() {
|
||||
|
||||
/** @desc Title for TeX editor tab in the equation editor dialog. */
|
||||
var MSG_EE_TEX_EQUATION = goog.getMsg('TeX Equation');
|
||||
|
||||
/** @desc Title for equation preview image in the equation editor dialog. */
|
||||
var MSG_EE_TEX_PREVIEW = goog.getMsg('Preview');
|
||||
|
||||
/** @desc Link text that leads to an info page about the equation dialog. */
|
||||
var MSG_EE_LEARN_MORE = goog.getMsg('Learn more');
|
||||
|
||||
var domHelper = this.dom_;
|
||||
var innerContainer;
|
||||
var texEditorEl = domHelper.createDom(goog.dom.TagName.DIV,
|
||||
{'style': 'display: none;'},
|
||||
domHelper.createDom(goog.dom.TagName.SPAN,
|
||||
{'class':
|
||||
goog.ui.equation.TexPane.SECTION_TITLE_CSS_CLASS +
|
||||
' ' +
|
||||
goog.ui.equation.TexPane.SECTION_TITLE_FLOAT_CSS_CLASS},
|
||||
MSG_EE_TEX_EQUATION),
|
||||
this.getHelpUrl() ?
|
||||
domHelper.createDom(goog.dom.TagName.A,
|
||||
{'id':
|
||||
goog.ui.equation.TexPane.SECTION_LEARN_MORE_CSS_ID,
|
||||
'target': '_blank', 'href': this.getHelpUrl()},
|
||||
MSG_EE_LEARN_MORE) : null,
|
||||
domHelper.createDom(goog.dom.TagName.DIV,
|
||||
{'style': 'clear: both;'}),
|
||||
innerContainer = this.innerContainer_ =
|
||||
domHelper.createDom(goog.dom.TagName.DIV,
|
||||
{'style': 'position: relative'}));
|
||||
|
||||
// Create menu palette.
|
||||
var menuPalette =
|
||||
this.paletteManager_.setActive(
|
||||
goog.ui.equation.Palette.Type.MENU);
|
||||
|
||||
// Render the menu palette.
|
||||
menuPalette.render(innerContainer);
|
||||
|
||||
innerContainer.appendChild(domHelper.createDom(goog.dom.TagName.DIV,
|
||||
{'style': 'clear:both'}));
|
||||
|
||||
var texEdit = this.texEdit_ = domHelper.createDom('textarea',
|
||||
{'class': goog.ui.equation.TexPane.TEX_EDIT_CSS_CLASS,
|
||||
'dir': 'ltr'});
|
||||
|
||||
innerContainer.appendChild(texEdit);
|
||||
|
||||
innerContainer.appendChild(
|
||||
domHelper.createDom(goog.dom.TagName.DIV,
|
||||
{'class':
|
||||
goog.ui.equation.TexPane.SECTION_TITLE_CSS_CLASS},
|
||||
MSG_EE_TEX_PREVIEW));
|
||||
|
||||
var previewContainer = this.previewContainer_ = domHelper.createDom(
|
||||
goog.dom.TagName.DIV,
|
||||
{'class':
|
||||
goog.ui.equation.TexPane.PREVIEW_CONTAINER_CSS_CLASS});
|
||||
|
||||
innerContainer.appendChild(previewContainer);
|
||||
|
||||
this.setElementInternal(texEditorEl);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.equation.TexPane.prototype.enterDocument = function() {
|
||||
this.texInputHandler_ = new goog.events.InputHandler(this.texEdit_);
|
||||
|
||||
// Listen to changes in the edit box to redraw equation.
|
||||
goog.events.listen(this.texInputHandler_,
|
||||
goog.events.InputHandler.EventType.INPUT,
|
||||
this.handleTexChange_, false, this);
|
||||
|
||||
// Add a keyup listener for Safari that does not support the INPUT event,
|
||||
// and for users pasting with ctrl+v, which does not generate an INPUT event
|
||||
// in some browsers.
|
||||
this.getHandler().listen(
|
||||
this.texEdit_, goog.events.EventType.KEYDOWN, this.handleTexChange_);
|
||||
|
||||
// Listen to the action event on the active palette.
|
||||
this.getHandler().listen(this.paletteManager_,
|
||||
goog.ui.equation.PaletteEvent.Type.ACTION,
|
||||
this.handlePaletteAction_, false, this);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.equation.TexPane.prototype.setVisible = function(visible) {
|
||||
goog.base(this, 'setVisible', visible);
|
||||
if (visible) {
|
||||
goog.Timer.callOnce(this.focusTexEdit_, 0, this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the focus to the TeX edit box.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.equation.TexPane.prototype.focusTexEdit_ = function() {
|
||||
this.texEdit_.focus();
|
||||
goog.dom.selection.setCursorPosition(this.texEdit_,
|
||||
this.texEdit_.value.length);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles input change within the TeX textarea.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.equation.TexPane.prototype.handleEquationChange_ = function() {
|
||||
var text = this.getEquation();
|
||||
if (text == this.lastRenderredText_) {
|
||||
return; // No change, no need to re-draw
|
||||
}
|
||||
|
||||
this.lastRenderredText_ = text;
|
||||
var isEquationValid =
|
||||
!goog.ui.equation.ImageRenderer.isEquationTooLong(text);
|
||||
|
||||
// Dispatch change so that dialog might update the state of its buttons.
|
||||
this.dispatchEvent(
|
||||
new goog.ui.equation.ChangeEvent(
|
||||
isEquationValid));
|
||||
|
||||
var container = this.previewContainer_;
|
||||
var dom = goog.dom.getDomHelper(container);
|
||||
dom.removeChildren(container);
|
||||
|
||||
if (text) {
|
||||
var childNode;
|
||||
if (isEquationValid) {
|
||||
// Show equation image.
|
||||
|
||||
var imgSrc = goog.ui.equation.ImageRenderer.getImageUrl(text);
|
||||
childNode = dom.createDom(goog.dom.TagName.IMG, {'src': imgSrc});
|
||||
} else {
|
||||
// Show a warning message.
|
||||
|
||||
/**
|
||||
* @desc A warning message shown when equation the user entered is too
|
||||
* long to display.
|
||||
*/
|
||||
var MSG_EE_TEX_EQUATION_TOO_LONG =
|
||||
goog.getMsg('Equation is too long');
|
||||
|
||||
childNode = dom.createDom(goog.dom.TagName.DIV,
|
||||
{'class': goog.ui.equation.TexPane.WARNING_CLASS},
|
||||
MSG_EE_TEX_EQUATION_TOO_LONG);
|
||||
}
|
||||
dom.appendChild(container, childNode);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles a change to the equation text.
|
||||
* Queues a request to handle input change within the TeX textarea.
|
||||
* Refreshing the image is done only after a short timeout, to combine
|
||||
* fast typing events into one draw.
|
||||
* @param {goog.events.Event} e The keyboard event.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.equation.TexPane.prototype.handleTexChange_ = function(e) {
|
||||
this.changeSequence_++;
|
||||
goog.Timer.callOnce(
|
||||
goog.bind(this.handleTexChangeTimer_, this, this.changeSequence_),
|
||||
500);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles a timer timeout on delayed text change redraw.
|
||||
* @param {number} seq The change sequence number when the timer started.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.equation.TexPane.prototype.handleTexChangeTimer_ =
|
||||
function(seq) {
|
||||
// Draw only if this was the last change. If not, just wait for the last.
|
||||
if (seq == this.changeSequence_) {
|
||||
this.handleEquationChange_();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles an action generated by a palette click.
|
||||
* @param {goog.ui.equation.PaletteEvent} e The event object.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.equation.TexPane.prototype.handlePaletteAction_ = function(e) {
|
||||
var palette = e.getPalette();
|
||||
var paletteManager = this.paletteManager_;
|
||||
var activePalette = paletteManager.getActive();
|
||||
var texEdit = this.texEdit_;
|
||||
|
||||
// This is a click on the menu palette.
|
||||
if (palette.getType() == goog.ui.equation.Palette.Type.MENU) {
|
||||
var idx = palette.getHighlightedIndex();
|
||||
var action = (idx != -1) ? palette.getAction(idx) : null;
|
||||
|
||||
// Current palette is not menu. This means there's a palette popping up.
|
||||
if (activePalette != palette && activePalette.getType() == action) {
|
||||
|
||||
// Deactivate the palette.
|
||||
paletteManager.deactivateNow();
|
||||
return;
|
||||
}
|
||||
|
||||
// We are clicking on the menu palette and there's no sub palette opening.
|
||||
// Then we just open the one corresponding to the item under the mouse.
|
||||
if (action) {
|
||||
var subPalette = this.paletteManager_.setActive(
|
||||
/** @type {goog.ui.equation.Palette.Type} */ (action));
|
||||
if (!subPalette.getElement()) {
|
||||
subPalette.render(this.innerContainer_);
|
||||
}
|
||||
var el = subPalette.getElement();
|
||||
goog.style.setPosition(el, 0, - el.clientHeight);
|
||||
}
|
||||
} else {
|
||||
activePalette = this.paletteManager_.getActive();
|
||||
var action = activePalette.getAction(activePalette.getHighlightedIndex());
|
||||
|
||||
// If the click is on white space in the palette, do nothing.
|
||||
if (!action) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Do actual insert async because IE8 does not move the selection
|
||||
// position and inserts in the wrong place if called in flow.
|
||||
// See bug 2066876
|
||||
goog.Timer.callOnce(goog.bind(this.insert_, this, action + ' '), 0);
|
||||
}
|
||||
|
||||
// Let the tex editor always catch the focus.
|
||||
texEdit.focus();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Inserts text into the equation at the current cursor position.
|
||||
* Moves the cursor to after the inserted text.
|
||||
* @param {string} text Text to insert.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.equation.TexPane.prototype.insert_ = function(text) {
|
||||
var texEdit = this.texEdit_;
|
||||
var pos = goog.dom.selection.getStart(texEdit);
|
||||
var equation = texEdit['value'];
|
||||
equation = equation.substring(0, pos) + text + equation.substring(pos);
|
||||
texEdit['value'] = equation;
|
||||
goog.dom.selection.setCursorPosition(texEdit, pos + text.length);
|
||||
this.handleEquationChange_();
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.equation.TexPane.prototype.getEquation = function() {
|
||||
return this.texEdit_['value'];
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.equation.TexPane.prototype.setEquation =
|
||||
function(equation) {
|
||||
this.texEdit_['value'] = equation;
|
||||
this.handleEquationChange_();
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.equation.TexPane.prototype.disposeInternal = function() {
|
||||
this.texInputHandler_.dispose();
|
||||
this.paletteManager_ = null;
|
||||
goog.base(this, 'disposeInternal');
|
||||
};
|
||||
575
nicer-api-docs/closure-library/closure/goog/ui/filteredmenu.js
Normal file
575
nicer-api-docs/closure-library/closure/goog/ui/filteredmenu.js
Normal file
@@ -0,0 +1,575 @@
|
||||
// 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 Menu where items can be filtered based on user keyboard input.
|
||||
* If a filter is specified only the items matching it will be displayed.
|
||||
*
|
||||
* @author eae@google.com (Emil A Eklund)
|
||||
* @see ../demos/filteredmenu.html
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.ui.FilteredMenu');
|
||||
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.events.InputHandler');
|
||||
goog.require('goog.events.KeyCodes');
|
||||
goog.require('goog.string');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('goog.ui.FilterObservingMenuItem');
|
||||
goog.require('goog.ui.Menu');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Filtered menu class.
|
||||
* @param {goog.ui.MenuRenderer=} opt_renderer Renderer used to render filtered
|
||||
* menu; defaults to {@link goog.ui.MenuRenderer}.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
|
||||
* @constructor
|
||||
* @extends {goog.ui.Menu}
|
||||
*/
|
||||
goog.ui.FilteredMenu = function(opt_renderer, opt_domHelper) {
|
||||
goog.ui.Menu.call(this, opt_domHelper, opt_renderer);
|
||||
};
|
||||
goog.inherits(goog.ui.FilteredMenu, goog.ui.Menu);
|
||||
|
||||
|
||||
/**
|
||||
* Events fired by component.
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.ui.FilteredMenu.EventType = {
|
||||
/** Dispatched after the component filter criteria has been changed. */
|
||||
FILTER_CHANGED: 'filterchange'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Filter input element.
|
||||
* @type {Element|undefined}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.filterInput_;
|
||||
|
||||
|
||||
/**
|
||||
* The input handler that provides the input event.
|
||||
* @type {goog.events.InputHandler|undefined}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.inputHandler_;
|
||||
|
||||
|
||||
/**
|
||||
* Maximum number of characters for filter input.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.maxLength_ = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Label displayed in the filter input when no text has been entered.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.label_ = '';
|
||||
|
||||
|
||||
/**
|
||||
* Label element.
|
||||
* @type {Element|undefined}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.labelEl_;
|
||||
|
||||
|
||||
/**
|
||||
* Whether multiple items can be entered comma separated.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.allowMultiple_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* List of items entered in the search box if multiple entries are allowed.
|
||||
* @type {Array.<string>|undefined}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.enteredItems_;
|
||||
|
||||
|
||||
/**
|
||||
* Index of first item that should be affected by the filter. Menu items with
|
||||
* a lower index will not be affected by the filter.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.filterFromIndex_ = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Filter applied to the menu.
|
||||
* @type {string|undefined|null}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.filterStr_;
|
||||
|
||||
|
||||
/**
|
||||
* Map of child nodes that shouldn't be affected by filtering.
|
||||
* @type {Object|undefined}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.persistentChildren_;
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.FilteredMenu.prototype.createDom = function() {
|
||||
goog.ui.FilteredMenu.superClass_.createDom.call(this);
|
||||
|
||||
var dom = this.getDomHelper();
|
||||
var el = dom.createDom('div',
|
||||
goog.getCssName(this.getRenderer().getCssClass(), 'filter'),
|
||||
this.labelEl_ = dom.createDom('div', null, this.label_),
|
||||
this.filterInput_ = dom.createDom('input', {'type': 'text'}));
|
||||
var element = this.getElement();
|
||||
dom.appendChild(element, el);
|
||||
this.contentElement_ = dom.createDom('div',
|
||||
goog.getCssName(this.getRenderer().getCssClass(), 'content'));
|
||||
dom.appendChild(element, this.contentElement_);
|
||||
|
||||
this.initFilterInput_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Helper method that initializes the filter input element.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.initFilterInput_ = function() {
|
||||
this.setFocusable(true);
|
||||
this.setKeyEventTarget(this.filterInput_);
|
||||
|
||||
// Workaround for mozilla bug #236791.
|
||||
if (goog.userAgent.GECKO) {
|
||||
this.filterInput_.setAttribute('autocomplete', 'off');
|
||||
}
|
||||
|
||||
if (this.maxLength_) {
|
||||
this.filterInput_.maxLength = this.maxLength_;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets up listeners and prepares the filter functionality.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.setUpFilterListeners_ = function() {
|
||||
if (!this.inputHandler_ && this.filterInput_) {
|
||||
this.inputHandler_ = new goog.events.InputHandler(
|
||||
/** @type {Element} */ (this.filterInput_));
|
||||
goog.style.setUnselectable(this.filterInput_, false);
|
||||
goog.events.listen(this.inputHandler_,
|
||||
goog.events.InputHandler.EventType.INPUT,
|
||||
this.handleFilterEvent, false, this);
|
||||
goog.events.listen(this.filterInput_.parentNode,
|
||||
goog.events.EventType.CLICK,
|
||||
this.onFilterLabelClick_, false, this);
|
||||
if (this.allowMultiple_) {
|
||||
this.enteredItems_ = [];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Tears down listeners and resets the filter functionality.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.tearDownFilterListeners_ = function() {
|
||||
if (this.inputHandler_) {
|
||||
goog.events.unlisten(this.inputHandler_,
|
||||
goog.events.InputHandler.EventType.INPUT,
|
||||
this.handleFilterEvent, false, this);
|
||||
goog.events.unlisten(this.filterInput_.parentNode,
|
||||
goog.events.EventType.CLICK,
|
||||
this.onFilterLabelClick_, false, this);
|
||||
|
||||
this.inputHandler_.dispose();
|
||||
this.inputHandler_ = undefined;
|
||||
this.enteredItems_ = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.FilteredMenu.prototype.setVisible = function(show, opt_force, opt_e) {
|
||||
var visibilityChanged = goog.ui.FilteredMenu.superClass_.setVisible.call(this,
|
||||
show, opt_force, opt_e);
|
||||
if (visibilityChanged && show && this.isInDocument()) {
|
||||
this.setFilter('');
|
||||
this.setUpFilterListeners_();
|
||||
} else if (visibilityChanged && !show) {
|
||||
this.tearDownFilterListeners_();
|
||||
}
|
||||
|
||||
return visibilityChanged;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.FilteredMenu.prototype.disposeInternal = function() {
|
||||
this.tearDownFilterListeners_();
|
||||
this.filterInput_ = undefined;
|
||||
this.labelEl_ = undefined;
|
||||
goog.ui.FilteredMenu.superClass_.disposeInternal.call(this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the filter label (the label displayed in the filter input element if no
|
||||
* text has been entered).
|
||||
* @param {?string} label Label text.
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.setFilterLabel = function(label) {
|
||||
this.label_ = label || '';
|
||||
if (this.labelEl_) {
|
||||
goog.dom.setTextContent(this.labelEl_, this.label_);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {string} The filter label.
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.getFilterLabel = function() {
|
||||
return this.label_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the filter string.
|
||||
* @param {?string} str Filter string.
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.setFilter = function(str) {
|
||||
if (this.filterInput_) {
|
||||
this.filterInput_.value = str;
|
||||
this.filterItems_(str);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the filter string.
|
||||
* @return {string} Current filter or an an empty string.
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.getFilter = function() {
|
||||
return this.filterInput_ && goog.isString(this.filterInput_.value) ?
|
||||
this.filterInput_.value : '';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the index of first item that should be affected by the filter. Menu
|
||||
* items with a lower index will not be affected by the filter.
|
||||
* @param {number} index Index of first item that should be affected by filter.
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.setFilterFromIndex = function(index) {
|
||||
this.filterFromIndex_ = index;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the index of first item that is affected by the filter.
|
||||
* @return {number} Index of first item that is affected by filter.
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.getFilterFromIndex = function() {
|
||||
return this.filterFromIndex_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets a list of items entered in the search box.
|
||||
* @return {Array.<string>} The entered items.
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.getEnteredItems = function() {
|
||||
return this.enteredItems_ || [];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether multiple items can be entered comma separated.
|
||||
* @param {boolean} b Whether multiple items can be entered.
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.setAllowMultiple = function(b) {
|
||||
this.allowMultiple_ = b;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether multiple items can be entered comma separated.
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.getAllowMultiple = function() {
|
||||
return this.allowMultiple_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether the specified child should be affected (shown/hidden) by the
|
||||
* filter criteria.
|
||||
* @param {goog.ui.Component} child Child to change.
|
||||
* @param {boolean} persistent Whether the child should be persistent.
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.setPersistentVisibility = function(child,
|
||||
persistent) {
|
||||
if (!this.persistentChildren_) {
|
||||
this.persistentChildren_ = {};
|
||||
}
|
||||
this.persistentChildren_[child.getId()] = persistent;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the specified child should be affected (shown/hidden) by the
|
||||
* filter criteria.
|
||||
* @param {goog.ui.Component} child Menu item to check.
|
||||
* @return {boolean} Whether the menu item is persistent.
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.hasPersistentVisibility = function(child) {
|
||||
return !!(this.persistentChildren_ &&
|
||||
this.persistentChildren_[child.getId()]);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles filter input events.
|
||||
* @param {goog.events.BrowserEvent} e The event object.
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.handleFilterEvent = function(e) {
|
||||
this.filterItems_(this.filterInput_.value);
|
||||
|
||||
// Highlight the first visible item unless there's already a highlighted item.
|
||||
var highlighted = this.getHighlighted();
|
||||
if (!highlighted || !highlighted.isVisible()) {
|
||||
this.highlightFirst();
|
||||
}
|
||||
this.dispatchEvent(goog.ui.FilteredMenu.EventType.FILTER_CHANGED);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Shows/hides elements based on the supplied filter.
|
||||
* @param {?string} str Filter string.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.filterItems_ = function(str) {
|
||||
// Do nothing unless the filter string has changed.
|
||||
if (this.filterStr_ == str) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.labelEl_) {
|
||||
this.labelEl_.style.visibility = str == '' ? 'visible' : 'hidden';
|
||||
}
|
||||
|
||||
if (this.allowMultiple_ && this.enteredItems_) {
|
||||
// Matches all non space characters after the last comma.
|
||||
var lastWordRegExp = /^(.+),[ ]*([^,]*)$/;
|
||||
var matches = str.match(lastWordRegExp);
|
||||
// matches[1] is the string up to, but not including, the last comma and
|
||||
// matches[2] the part after the last comma. If there are no non-space
|
||||
// characters after the last comma matches[2] is undefined.
|
||||
var items = matches && matches[1] ? matches[1].split(',') : [];
|
||||
|
||||
// If the number of comma separated items has changes recreate the
|
||||
// entered items array and fire a change event.
|
||||
if (str.substr(str.length - 1, 1) == ',' ||
|
||||
items.length != this.enteredItems_.length) {
|
||||
var lastItem = items[items.length - 1] || '';
|
||||
|
||||
// Auto complete text in input box based on the highlighted item.
|
||||
if (this.getHighlighted() && lastItem != '') {
|
||||
var caption = this.getHighlighted().getCaption();
|
||||
if (caption.toLowerCase().indexOf(lastItem.toLowerCase()) == 0) {
|
||||
items[items.length - 1] = caption;
|
||||
this.filterInput_.value = items.join(',') + ',';
|
||||
}
|
||||
}
|
||||
this.enteredItems_ = items;
|
||||
this.dispatchEvent(goog.ui.Component.EventType.CHANGE);
|
||||
this.setHighlightedIndex(-1);
|
||||
}
|
||||
|
||||
if (matches) {
|
||||
str = matches.length > 2 ? goog.string.trim(matches[2]) : '';
|
||||
}
|
||||
}
|
||||
|
||||
var matcher = new RegExp('(^|[- ,_/.:])' +
|
||||
goog.string.regExpEscape(str), 'i');
|
||||
for (var child, i = this.filterFromIndex_; child = this.getChildAt(i); i++) {
|
||||
if (child instanceof goog.ui.FilterObservingMenuItem) {
|
||||
child.callObserver(str);
|
||||
} else if (!this.hasPersistentVisibility(child)) {
|
||||
// Only show items matching the filter and highlight the part of the
|
||||
// caption that matches.
|
||||
var caption = child.getCaption();
|
||||
if (caption) {
|
||||
var matchArray = caption.match(matcher);
|
||||
if (str == '' || matchArray) {
|
||||
child.setVisible(true);
|
||||
var pos = caption.indexOf(matchArray[0]);
|
||||
|
||||
// If position is non zero increase by one to skip the separator.
|
||||
if (pos) {
|
||||
pos++;
|
||||
}
|
||||
|
||||
if (str == '') {
|
||||
child.setContent(caption);
|
||||
} else {
|
||||
child.setContent(this.getDomHelper().createDom('span', null,
|
||||
caption.substr(0, pos),
|
||||
this.getDomHelper().createDom(
|
||||
'b', null, caption.substr(pos, str.length)),
|
||||
caption.substr(pos + str.length,
|
||||
caption.length - str.length - pos)));
|
||||
}
|
||||
} else {
|
||||
child.setVisible(false);
|
||||
}
|
||||
} else {
|
||||
|
||||
// Hide separators and other items without a caption if a filter string
|
||||
// has been entered.
|
||||
child.setVisible(str == '');
|
||||
}
|
||||
}
|
||||
}
|
||||
this.filterStr_ = str;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles the menu's behavior for a key event. The highlighted menu item will
|
||||
* be given the opportunity to handle the key behavior.
|
||||
* @param {goog.events.KeyEvent} e A browser event.
|
||||
* @return {boolean} Whether the event was handled.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.handleKeyEventInternal = function(e) {
|
||||
// Home, end and the arrow keys are normally used to change the selected menu
|
||||
// item. Return false here to prevent the menu from preventing the default
|
||||
// behavior for HOME, END and any key press with a modifier.
|
||||
if (e.shiftKey || e.ctrlKey || e.altKey ||
|
||||
e.keyCode == goog.events.KeyCodes.HOME ||
|
||||
e.keyCode == goog.events.KeyCodes.END) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (e.keyCode == goog.events.KeyCodes.ESC) {
|
||||
this.dispatchEvent(goog.ui.Component.EventType.BLUR);
|
||||
return true;
|
||||
}
|
||||
|
||||
return goog.ui.FilteredMenu.superClass_.handleKeyEventInternal.call(this, e);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the highlighted index, unless the HIGHLIGHT event is intercepted and
|
||||
* cancelled. -1 = no highlight. Also scrolls the menu item into view.
|
||||
* @param {number} index Index of menu item to highlight.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.setHighlightedIndex = function(index) {
|
||||
goog.ui.FilteredMenu.superClass_.setHighlightedIndex.call(this, index);
|
||||
var contentEl = this.getContentElement();
|
||||
var el = this.getHighlighted() ? this.getHighlighted().getElement() : null;
|
||||
|
||||
if (el && goog.dom.contains(contentEl, el)) {
|
||||
var contentTop = goog.userAgent.IE && !goog.userAgent.isVersionOrHigher(8) ?
|
||||
0 : contentEl.offsetTop;
|
||||
|
||||
// IE (tested on IE8) sometime does not scroll enough by about
|
||||
// 1px. So we add 1px to the scroll amount. This still looks ok in
|
||||
// other browser except for the most degenerate case (menu height <=
|
||||
// item height).
|
||||
|
||||
// Scroll down if the highlighted item is below the bottom edge.
|
||||
var diff = (el.offsetTop + el.offsetHeight - contentTop) -
|
||||
(contentEl.clientHeight + contentEl.scrollTop) + 1;
|
||||
contentEl.scrollTop += Math.max(diff, 0);
|
||||
|
||||
// Scroll up if the highlighted item is above the top edge.
|
||||
diff = contentEl.scrollTop - (el.offsetTop - contentTop) + 1;
|
||||
contentEl.scrollTop -= Math.max(diff, 0);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles clicks on the filter label. Focuses the input element.
|
||||
* @param {goog.events.BrowserEvent} e A browser event.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.onFilterLabelClick_ = function(e) {
|
||||
this.filterInput_.focus();
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.FilteredMenu.prototype.getContentElement = function() {
|
||||
return this.contentElement_ || this.getElement();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the filter input element.
|
||||
* @return {Element} Input element.
|
||||
*/
|
||||
goog.ui.FilteredMenu.prototype.getFilterInputElement = function() {
|
||||
return this.filterInput_ || null;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.FilteredMenu.prototype.decorateInternal = function(element) {
|
||||
this.setElementInternal(element);
|
||||
|
||||
// Decorate the menu content.
|
||||
this.decorateContent(element);
|
||||
|
||||
// Locate internally managed elements.
|
||||
var el = this.getDomHelper().getElementsByTagNameAndClass('div',
|
||||
goog.getCssName(this.getRenderer().getCssClass(), 'filter'), element)[0];
|
||||
this.labelEl_ = goog.dom.getFirstElementChild(el);
|
||||
this.filterInput_ = goog.dom.getNextElementSibling(this.labelEl_);
|
||||
this.contentElement_ = goog.dom.getNextElementSibling(el);
|
||||
|
||||
// Decorate additional menu items (like 'apply').
|
||||
this.getRenderer().decorateChildren(this, el.parentNode,
|
||||
this.contentElement_);
|
||||
|
||||
this.initFilterInput_();
|
||||
};
|
||||
@@ -0,0 +1,97 @@
|
||||
// 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 Menu item observing the filter text in a
|
||||
* {@link goog.ui.FilteredMenu}. The observer method is called when the filter
|
||||
* text changes and allows the menu item to update its content and state based
|
||||
* on the filter.
|
||||
*
|
||||
* @author eae@google.com (Emil A Eklund)
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.FilterObservingMenuItem');
|
||||
|
||||
goog.require('goog.ui.FilterObservingMenuItemRenderer');
|
||||
goog.require('goog.ui.MenuItem');
|
||||
goog.require('goog.ui.registry');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Class representing a filter observing menu item.
|
||||
*
|
||||
* @param {goog.ui.ControlContent} content Text caption or DOM structure to
|
||||
* display as the content of the item (use to add icons or styling to
|
||||
* menus).
|
||||
* @param {*=} opt_model Data/model associated with the menu item.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper used for
|
||||
* document interactions.
|
||||
* @param {goog.ui.MenuItemRenderer=} opt_renderer Optional renderer.
|
||||
* @constructor
|
||||
* @extends {goog.ui.MenuItem}
|
||||
*/
|
||||
goog.ui.FilterObservingMenuItem = function(content, opt_model, opt_domHelper,
|
||||
opt_renderer) {
|
||||
goog.ui.MenuItem.call(this, content, opt_model, opt_domHelper,
|
||||
opt_renderer || new goog.ui.FilterObservingMenuItemRenderer());
|
||||
};
|
||||
goog.inherits(goog.ui.FilterObservingMenuItem, goog.ui.MenuItem);
|
||||
|
||||
|
||||
/**
|
||||
* Function called when the filter text changes.
|
||||
* @type {Function} function(goog.ui.FilterObservingMenuItem, string)
|
||||
* @private
|
||||
*/
|
||||
goog.ui.FilterObservingMenuItem.prototype.observer_ = null;
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.FilterObservingMenuItem.prototype.enterDocument = function() {
|
||||
goog.ui.FilterObservingMenuItem.superClass_.enterDocument.call(this);
|
||||
this.callObserver();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the observer functions.
|
||||
* @param {Function} f function(goog.ui.FilterObservingMenuItem, string).
|
||||
*/
|
||||
goog.ui.FilterObservingMenuItem.prototype.setObserver = function(f) {
|
||||
this.observer_ = f;
|
||||
this.callObserver();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Calls the observer function if one has been specified.
|
||||
* @param {?string=} opt_str Filter string.
|
||||
*/
|
||||
goog.ui.FilterObservingMenuItem.prototype.callObserver = function(opt_str) {
|
||||
if (this.observer_) {
|
||||
this.observer_(this, opt_str || '');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Register a decorator factory function for
|
||||
// goog.ui.FilterObservingMenuItemRenderer.
|
||||
goog.ui.registry.setDecoratorByClassName(
|
||||
goog.ui.FilterObservingMenuItemRenderer.CSS_CLASS,
|
||||
function() {
|
||||
// FilterObservingMenuItem defaults to using
|
||||
// FilterObservingMenuItemRenderer.
|
||||
return new goog.ui.FilterObservingMenuItem(null);
|
||||
});
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright 2008 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 Menu item observing the filter text in a
|
||||
* {@link goog.ui.FilteredMenu}. The observer method is called when the filter
|
||||
* text changes and allows the menu item to update its content and state based
|
||||
* on the filter.
|
||||
*
|
||||
* @author eae@google.com (Emil A Eklund)
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.FilterObservingMenuItemRenderer');
|
||||
|
||||
goog.require('goog.ui.MenuItemRenderer');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Default renderer for {@link goog.ui.FilterObservingMenuItem}s. Each item has
|
||||
* the following structure:
|
||||
* <div class="goog-filterobsmenuitem"><div>...(content)...</div></div>
|
||||
*
|
||||
* @constructor
|
||||
* @extends {goog.ui.MenuItemRenderer}
|
||||
*/
|
||||
goog.ui.FilterObservingMenuItemRenderer = function() {
|
||||
goog.ui.MenuItemRenderer.call(this);
|
||||
};
|
||||
goog.inherits(goog.ui.FilterObservingMenuItemRenderer,
|
||||
goog.ui.MenuItemRenderer);
|
||||
goog.addSingletonGetter(goog.ui.FilterObservingMenuItemRenderer);
|
||||
|
||||
|
||||
/**
|
||||
* CSS class name the renderer applies to menu item elements.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.FilterObservingMenuItemRenderer.CSS_CLASS =
|
||||
goog.getCssName('goog-filterobsmenuitem');
|
||||
|
||||
|
||||
/**
|
||||
* Returns the CSS class to be applied to menu items rendered using this
|
||||
* renderer.
|
||||
* @return {string} Renderer-specific CSS class.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.FilterObservingMenuItemRenderer.prototype.getCssClass = function() {
|
||||
return goog.ui.FilterObservingMenuItemRenderer.CSS_CLASS;
|
||||
};
|
||||
@@ -0,0 +1,146 @@
|
||||
// Copyright 2008 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 Similiar functionality of {@link goog.ui.ButtonRenderer},
|
||||
* but uses a <div> element instead of a <button> or <input> element.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.FlatButtonRenderer');
|
||||
|
||||
goog.require('goog.a11y.aria.Role');
|
||||
goog.require('goog.dom.classes');
|
||||
goog.require('goog.ui.Button');
|
||||
goog.require('goog.ui.ButtonRenderer');
|
||||
goog.require('goog.ui.INLINE_BLOCK_CLASSNAME');
|
||||
goog.require('goog.ui.registry');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Flat renderer for {@link goog.ui.Button}s. Flat buttons can contain
|
||||
* almost arbitrary HTML content, will flow like inline elements, but can be
|
||||
* styled like block-level elements.
|
||||
* @constructor
|
||||
* @extends {goog.ui.ButtonRenderer}
|
||||
*/
|
||||
goog.ui.FlatButtonRenderer = function() {
|
||||
goog.ui.ButtonRenderer.call(this);
|
||||
};
|
||||
goog.inherits(goog.ui.FlatButtonRenderer, goog.ui.ButtonRenderer);
|
||||
goog.addSingletonGetter(goog.ui.FlatButtonRenderer);
|
||||
|
||||
|
||||
/**
|
||||
* Default CSS class to be applied to the root element of components rendered
|
||||
* by this renderer.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.FlatButtonRenderer.CSS_CLASS = goog.getCssName('goog-flat-button');
|
||||
|
||||
|
||||
/**
|
||||
* Returns the control's contents wrapped in a div element, with
|
||||
* the renderer's own CSS class and additional state-specific classes applied
|
||||
* to it, and the button's disabled attribute set or cleared as needed.
|
||||
* Overrides {@link goog.ui.ButtonRenderer#createDom}.
|
||||
* @param {goog.ui.Control} button Button to render.
|
||||
* @return {Element} Root element for the button.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.FlatButtonRenderer.prototype.createDom = function(button) {
|
||||
var classNames = this.getClassNames(button);
|
||||
var attributes = {
|
||||
'class': goog.ui.INLINE_BLOCK_CLASSNAME + ' ' + classNames.join(' ')
|
||||
};
|
||||
var element = button.getDomHelper().createDom(
|
||||
'div', attributes, button.getContent());
|
||||
this.setTooltip(element, button.getTooltip());
|
||||
this.setAriaStates(button, element);
|
||||
return element;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the ARIA role to be applied to flat buttons.
|
||||
* @return {goog.a11y.aria.Role|undefined} ARIA role.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.FlatButtonRenderer.prototype.getAriaRole = function() {
|
||||
return goog.a11y.aria.Role.BUTTON;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if this renderer can decorate the element. Overrides
|
||||
* {@link goog.ui.ButtonRenderer#canDecorate} by returning true if the
|
||||
* element is a DIV, false otherwise.
|
||||
* @param {Element} element Element to decorate.
|
||||
* @return {boolean} Whether the renderer can decorate the element.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.FlatButtonRenderer.prototype.canDecorate = function(element) {
|
||||
return element.tagName == 'DIV';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Takes an existing element and decorates it with the flat button control.
|
||||
* Initializes the control's ID, content, tooltip, value, and state based
|
||||
* on the ID of the element, its child nodes, and its CSS classes, respectively.
|
||||
* Returns the element. Overrides {@link goog.ui.ButtonRenderer#decorate}.
|
||||
* @param {goog.ui.Control} button Button instance to decorate the element.
|
||||
* @param {Element} element Element to decorate.
|
||||
* @return {Element} Decorated element.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.FlatButtonRenderer.prototype.decorate = function(button, element) {
|
||||
goog.dom.classes.add(element, goog.ui.INLINE_BLOCK_CLASSNAME);
|
||||
return goog.ui.FlatButtonRenderer.superClass_.decorate.call(this, button,
|
||||
element);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Flat buttons can't use the value attribute since they are div elements.
|
||||
* Overrides {@link goog.ui.ButtonRenderer#getValue} to prevent trying to
|
||||
* access the element's value.
|
||||
* @param {Element} element The button control's root element.
|
||||
* @return {string} Value not valid for flat buttons.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.FlatButtonRenderer.prototype.getValue = function(element) {
|
||||
// Flat buttons don't store their value in the DOM.
|
||||
return '';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the CSS class to be applied to the root element of components
|
||||
* rendered using this renderer.
|
||||
* @return {string} Renderer-specific CSS class.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.FlatButtonRenderer.prototype.getCssClass = function() {
|
||||
return goog.ui.FlatButtonRenderer.CSS_CLASS;
|
||||
};
|
||||
|
||||
|
||||
// Register a decorator factory function for Flat Buttons.
|
||||
goog.ui.registry.setDecoratorByClassName(goog.ui.FlatButtonRenderer.CSS_CLASS,
|
||||
function() {
|
||||
// Uses goog.ui.Button, but with FlatButtonRenderer.
|
||||
return new goog.ui.Button(null, goog.ui.FlatButtonRenderer.getInstance());
|
||||
});
|
||||
@@ -0,0 +1,238 @@
|
||||
// Copyright 2008 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 Similiar functionality of {@link goog.ui.MenuButtonRenderer},
|
||||
* but inherits from {@link goog.ui.FlatButtonRenderer} instead of
|
||||
* {@link goog.ui.CustomButtonRenderer}. This creates a simpler menu button
|
||||
* that will look more like a traditional <select> menu.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.FlatMenuButtonRenderer');
|
||||
|
||||
goog.require('goog.a11y.aria');
|
||||
goog.require('goog.a11y.aria.State');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.string');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('goog.ui.FlatButtonRenderer');
|
||||
goog.require('goog.ui.INLINE_BLOCK_CLASSNAME');
|
||||
goog.require('goog.ui.Menu');
|
||||
goog.require('goog.ui.MenuButton');
|
||||
goog.require('goog.ui.MenuRenderer');
|
||||
goog.require('goog.ui.registry');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Flat Menu Button renderer. Creates a simpler version of
|
||||
* {@link goog.ui.MenuButton} that doesn't look like a button and
|
||||
* doesn't have rounded corners. Uses just a <div> and looks more like
|
||||
* a traditional <select> element.
|
||||
* @constructor
|
||||
* @extends {goog.ui.FlatButtonRenderer}
|
||||
*/
|
||||
goog.ui.FlatMenuButtonRenderer = function() {
|
||||
goog.ui.FlatButtonRenderer.call(this);
|
||||
};
|
||||
goog.inherits(goog.ui.FlatMenuButtonRenderer, goog.ui.FlatButtonRenderer);
|
||||
goog.addSingletonGetter(goog.ui.FlatMenuButtonRenderer);
|
||||
|
||||
|
||||
/**
|
||||
* Default CSS class to be applied to the root element of components rendered
|
||||
* by this renderer.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.ui.FlatMenuButtonRenderer.CSS_CLASS =
|
||||
goog.getCssName('goog-flat-menu-button');
|
||||
|
||||
|
||||
/**
|
||||
* Returns the button's contents wrapped in the following DOM structure:
|
||||
* <div class="goog-inline-block goog-flat-menu-button">
|
||||
* <div class="goog-inline-block goog-flat-menu-button-caption">
|
||||
* Contents...
|
||||
* </div>
|
||||
* <div class="goog-inline-block goog-flat-menu-button-dropdown">
|
||||
*
|
||||
* </div>
|
||||
* </div>
|
||||
* Overrides {@link goog.ui.FlatButtonRenderer#createDom}.
|
||||
* @param {goog.ui.Control} control Button to render.
|
||||
* @return {Element} Root element for the button.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.FlatMenuButtonRenderer.prototype.createDom = function(control) {
|
||||
var button = /** @type {goog.ui.Button} */ (control);
|
||||
var classNames = this.getClassNames(button);
|
||||
var attributes = {
|
||||
'class': goog.ui.INLINE_BLOCK_CLASSNAME + ' ' + classNames.join(' ')
|
||||
};
|
||||
var element = button.getDomHelper().createDom('div', attributes,
|
||||
[this.createCaption(button.getContent(), button.getDomHelper()),
|
||||
this.createDropdown(button.getDomHelper())]);
|
||||
this.setTooltip(
|
||||
element, /** @type {!string}*/ (button.getTooltip()));
|
||||
return element;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Takes the button's root element and returns the parent element of the
|
||||
* button's contents.
|
||||
* @param {Element} element Root element of the button whose content
|
||||
* element is to be returned.
|
||||
* @return {Element} The button's content element (if any).
|
||||
* @override
|
||||
*/
|
||||
goog.ui.FlatMenuButtonRenderer.prototype.getContentElement = function(element) {
|
||||
return element && /** @type {Element} */ (element.firstChild);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Updates the flat menu button's ARIA (accessibility) state so that
|
||||
* aria-expanded does not appear when the button is "opened."
|
||||
* @param {Element} element Element whose ARIA state is to be updated.
|
||||
* @param {goog.ui.Component.State} state Component state being enabled or
|
||||
* disabled.
|
||||
* @param {boolean} enable Whether the state is being enabled or disabled.
|
||||
* @protected
|
||||
* @override
|
||||
*/
|
||||
goog.ui.FlatMenuButtonRenderer.prototype.updateAriaState = function(
|
||||
element, state, enable) {
|
||||
// If button has OPENED state, do not assign an ARIA state. Usually
|
||||
// aria-expanded would be assigned, but aria-expanded is not a valid state
|
||||
// for a menu button.
|
||||
goog.asserts.assertObject(
|
||||
element, 'The flat button menu DOM element cannot be null.');
|
||||
goog.asserts.assert(goog.string.isEmpty(
|
||||
goog.a11y.aria.getState(element, goog.a11y.aria.State.EXPANDED)),
|
||||
'Menu buttons do not support the ARIA expanded attribute. ' +
|
||||
'Please use ARIA disabled instead.');
|
||||
if (state != goog.ui.Component.State.OPENED) {
|
||||
goog.base(this, 'updateAriaState', element, state, enable);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Takes an element, decorates it with the menu button control, and returns
|
||||
* the element. Overrides {@link goog.ui.CustomButtonRenderer#decorate} by
|
||||
* looking for a child element that can be decorated by a menu, and if it
|
||||
* finds one, decorates it and attaches it to the menu button.
|
||||
* @param {goog.ui.Control} button Menu button to decorate the element.
|
||||
* @param {Element} element Element to decorate.
|
||||
* @return {Element} Decorated element.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.FlatMenuButtonRenderer.prototype.decorate = function(button, element) {
|
||||
// TODO(user): MenuButtonRenderer uses the exact same code.
|
||||
// Refactor this block to its own module where both can use it.
|
||||
var menuElem = goog.dom.getElementsByTagNameAndClass(
|
||||
'*', goog.ui.MenuRenderer.CSS_CLASS, element)[0];
|
||||
if (menuElem) {
|
||||
// Move the menu element directly under the body, but hide it first; see
|
||||
// bug 1089244.
|
||||
goog.style.setElementShown(menuElem, false);
|
||||
button.getDomHelper().getDocument().body.appendChild(menuElem);
|
||||
|
||||
// Decorate the menu and attach it to the button.
|
||||
var menu = new goog.ui.Menu();
|
||||
menu.decorate(menuElem);
|
||||
button.setMenu(menu);
|
||||
}
|
||||
|
||||
// Add the caption if it's not already there.
|
||||
var captionElem = goog.dom.getElementsByTagNameAndClass(
|
||||
'*', goog.getCssName(this.getCssClass(), 'caption'), element)[0];
|
||||
if (!captionElem) {
|
||||
element.appendChild(
|
||||
this.createCaption(element.childNodes, button.getDomHelper()));
|
||||
}
|
||||
|
||||
// Add the dropdown icon if it's not already there.
|
||||
var dropdownElem = goog.dom.getElementsByTagNameAndClass(
|
||||
'*', goog.getCssName(this.getCssClass(), 'dropdown'), element)[0];
|
||||
if (!dropdownElem) {
|
||||
element.appendChild(this.createDropdown(button.getDomHelper()));
|
||||
}
|
||||
|
||||
// Let the superclass do the rest.
|
||||
return goog.ui.FlatMenuButtonRenderer.superClass_.decorate.call(this, button,
|
||||
element);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Takes a text caption or existing DOM structure, and returns it wrapped in
|
||||
* an appropriately-styled DIV. Creates the following DOM structure:
|
||||
* <div class="goog-inline-block goog-flat-menu-button-caption">
|
||||
* Contents...
|
||||
* </div>
|
||||
* @param {goog.ui.ControlContent} content Text caption or DOM structure to wrap
|
||||
* in a box.
|
||||
* @param {goog.dom.DomHelper} dom DOM helper, used for document interaction.
|
||||
* @return {Element} Caption element.
|
||||
*/
|
||||
goog.ui.FlatMenuButtonRenderer.prototype.createCaption = function(content,
|
||||
dom) {
|
||||
return dom.createDom('div',
|
||||
goog.ui.INLINE_BLOCK_CLASSNAME + ' ' +
|
||||
goog.getCssName(this.getCssClass(), 'caption'), content);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns an appropriately-styled DIV containing a dropdown arrow element.
|
||||
* Creates the following DOM structure:
|
||||
* <div class="goog-inline-block goog-flat-menu-button-dropdown">
|
||||
*
|
||||
* </div>
|
||||
* @param {goog.dom.DomHelper} dom DOM helper, used for document interaction.
|
||||
* @return {Element} Dropdown element.
|
||||
*/
|
||||
goog.ui.FlatMenuButtonRenderer.prototype.createDropdown = function(dom) {
|
||||
// 00A0 is
|
||||
return dom.createDom('div',
|
||||
goog.ui.INLINE_BLOCK_CLASSNAME + ' ' +
|
||||
goog.getCssName(this.getCssClass(), 'dropdown'), '\u00A0');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the CSS class to be applied to the root element of components
|
||||
* rendered using this renderer.
|
||||
* @return {string} Renderer-specific CSS class.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.FlatMenuButtonRenderer.prototype.getCssClass = function() {
|
||||
return goog.ui.FlatMenuButtonRenderer.CSS_CLASS;
|
||||
};
|
||||
|
||||
|
||||
// Register a decorator factory function for Flat Menu Buttons.
|
||||
goog.ui.registry.setDecoratorByClassName(
|
||||
goog.ui.FlatMenuButtonRenderer.CSS_CLASS,
|
||||
function() {
|
||||
// Uses goog.ui.MenuButton, but with FlatMenuButtonRenderer.
|
||||
return new goog.ui.MenuButton(null, null,
|
||||
goog.ui.FlatMenuButtonRenderer.getInstance());
|
||||
});
|
||||
|
||||
109
nicer-api-docs/closure-library/closure/goog/ui/formpost.js
Normal file
109
nicer-api-docs/closure-library/closure/goog/ui/formpost.js
Normal file
@@ -0,0 +1,109 @@
|
||||
// Copyright 2008 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 for making the browser submit a hidden form, which can
|
||||
* be used to effect a POST from JavaScript.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.FormPost');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.string');
|
||||
goog.require('goog.string.StringBuffer');
|
||||
goog.require('goog.ui.Component');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a formpost object.
|
||||
* @constructor
|
||||
* @extends {goog.ui.Component}
|
||||
* @param {goog.dom.DomHelper=} opt_dom The DOM helper.
|
||||
*/
|
||||
goog.ui.FormPost = function(opt_dom) {
|
||||
goog.ui.Component.call(this, opt_dom);
|
||||
};
|
||||
goog.inherits(goog.ui.FormPost, goog.ui.Component);
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.FormPost.prototype.createDom = function() {
|
||||
this.setElementInternal(this.getDomHelper().createDom(goog.dom.TagName.FORM,
|
||||
{'method': 'POST', 'style': 'display:none'}));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a POST request and directs the browser as if a form were
|
||||
* submitted.
|
||||
* @param {Object} parameters Object with parameter values. Values can be
|
||||
* strings, numbers, or arrays of strings or numbers.
|
||||
* @param {string=} opt_url The destination URL. If not specified, uses the
|
||||
* current URL for window for the DOM specified in the constructor.
|
||||
* @param {string=} opt_target An optional name of a window in which to open the
|
||||
* URL. If not specified, uses the window for the DOM specified in the
|
||||
* constructor.
|
||||
*/
|
||||
goog.ui.FormPost.prototype.post = function(parameters, opt_url, opt_target) {
|
||||
var form = this.getElement();
|
||||
if (!form) {
|
||||
this.render();
|
||||
form = this.getElement();
|
||||
}
|
||||
form.action = opt_url || '';
|
||||
form.target = opt_target || '';
|
||||
this.setParameters_(form, parameters);
|
||||
form.submit();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates hidden inputs in a form to match parameters.
|
||||
* @param {Element} form The form element.
|
||||
* @param {Object} parameters Object with parameter values. Values can be
|
||||
* strings, numbers, or arrays of strings or numbers.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.FormPost.prototype.setParameters_ = function(form, parameters) {
|
||||
var name, value, sb = new goog.string.StringBuffer();
|
||||
for (name in parameters) {
|
||||
value = parameters[name];
|
||||
if (goog.isArrayLike(value)) {
|
||||
goog.array.forEach(value, goog.bind(this.appendInput_, this, sb, name));
|
||||
} else {
|
||||
this.appendInput_(sb, name, value);
|
||||
}
|
||||
}
|
||||
form.innerHTML = sb.toString();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Appends a hidden <INPUT> tag to a string buffer.
|
||||
* @param {goog.string.StringBuffer} out A string buffer.
|
||||
* @param {string} name The name of the input.
|
||||
* @param {string} value The value of the input.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.FormPost.prototype.appendInput_ = function(out, name, value) {
|
||||
out.append(
|
||||
'<input type="hidden" name="',
|
||||
goog.string.htmlEscape(name),
|
||||
'" value="',
|
||||
goog.string.htmlEscape(value),
|
||||
'">');
|
||||
};
|
||||
1011
nicer-api-docs/closure-library/closure/goog/ui/gauge.js
Normal file
1011
nicer-api-docs/closure-library/closure/goog/ui/gauge.js
Normal file
File diff suppressed because it is too large
Load Diff
169
nicer-api-docs/closure-library/closure/goog/ui/gaugetheme.js
Normal file
169
nicer-api-docs/closure-library/closure/goog/ui/gaugetheme.js
Normal file
@@ -0,0 +1,169 @@
|
||||
// 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 The color theme used by a gauge (goog.ui.Guage).
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.ui.GaugeTheme');
|
||||
|
||||
|
||||
goog.require('goog.graphics.LinearGradient');
|
||||
goog.require('goog.graphics.SolidFill');
|
||||
goog.require('goog.graphics.Stroke');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A class for the default color theme for a Gauge.
|
||||
* Users can extend this class to provide a custom color theme, and apply the
|
||||
* custom color theme by calling {@link goog.ui.Gauge#setTheme}.
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.GaugeTheme = function() {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the stroke for the external border of the gauge.
|
||||
* @return {goog.graphics.Stroke} The stroke to use.
|
||||
*/
|
||||
goog.ui.GaugeTheme.prototype.getExternalBorderStroke = function() {
|
||||
return new goog.graphics.Stroke(1, '#333333');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the fill for the external border of the gauge.
|
||||
* @param {number} cx X coordinate of the center of the gauge.
|
||||
* @param {number} cy Y coordinate of the center of the gauge.
|
||||
* @param {number} r Radius of the gauge.
|
||||
* @return {goog.graphics.Fill} The fill to use.
|
||||
*/
|
||||
goog.ui.GaugeTheme.prototype.getExternalBorderFill = function(cx, cy, r) {
|
||||
return new goog.graphics.LinearGradient(cx + r, cy - r, cx - r, cy + r,
|
||||
'#f7f7f7', '#cccccc');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the stroke for the internal border of the gauge.
|
||||
* @return {goog.graphics.Stroke} The stroke to use.
|
||||
*/
|
||||
goog.ui.GaugeTheme.prototype.getInternalBorderStroke = function() {
|
||||
return new goog.graphics.Stroke(2, '#e0e0e0');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the fill for the internal border of the gauge.
|
||||
* @param {number} cx X coordinate of the center of the gauge.
|
||||
* @param {number} cy Y coordinate of the center of the gauge.
|
||||
* @param {number} r Radius of the gauge.
|
||||
* @return {goog.graphics.Fill} The fill to use.
|
||||
*/
|
||||
goog.ui.GaugeTheme.prototype.getInternalBorderFill = function(cx, cy, r) {
|
||||
return new goog.graphics.SolidFill('#f7f7f7');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the stroke for the major ticks of the gauge.
|
||||
* @return {goog.graphics.Stroke} The stroke to use.
|
||||
*/
|
||||
goog.ui.GaugeTheme.prototype.getMajorTickStroke = function() {
|
||||
return new goog.graphics.Stroke(2, '#333333');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the stroke for the minor ticks of the gauge.
|
||||
* @return {goog.graphics.Stroke} The stroke to use.
|
||||
*/
|
||||
goog.ui.GaugeTheme.prototype.getMinorTickStroke = function() {
|
||||
return new goog.graphics.Stroke(1, '#666666');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the stroke for the hinge at the center of the gauge.
|
||||
* @return {goog.graphics.Stroke} The stroke to use.
|
||||
*/
|
||||
goog.ui.GaugeTheme.prototype.getHingeStroke = function() {
|
||||
return new goog.graphics.Stroke(1, '#666666');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the fill for the hinge at the center of the gauge.
|
||||
* @param {number} cx X coordinate of the center of the gauge.
|
||||
* @param {number} cy Y coordinate of the center of the gauge.
|
||||
* @param {number} r Radius of the hinge.
|
||||
* @return {goog.graphics.Fill} The fill to use.
|
||||
*/
|
||||
goog.ui.GaugeTheme.prototype.getHingeFill = function(cx, cy, r) {
|
||||
return new goog.graphics.LinearGradient(cx + r, cy - r, cx - r, cy + r,
|
||||
'#4684ee', '#3776d6');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the stroke for the gauge needle.
|
||||
* @return {goog.graphics.Stroke} The stroke to use.
|
||||
*/
|
||||
goog.ui.GaugeTheme.prototype.getNeedleStroke = function() {
|
||||
return new goog.graphics.Stroke(1, '#c63310');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the fill for the hinge at the center of the gauge.
|
||||
* @param {number} cx X coordinate of the center of the gauge.
|
||||
* @param {number} cy Y coordinate of the center of the gauge.
|
||||
* @param {number} r Radius of the gauge.
|
||||
* @return {goog.graphics.Fill} The fill to use.
|
||||
*/
|
||||
goog.ui.GaugeTheme.prototype.getNeedleFill = function(cx, cy, r) {
|
||||
// Make needle a bit transparent so that text underneeth is still visible.
|
||||
return new goog.graphics.SolidFill('#dc3912', 0.7);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the color for the gauge title.
|
||||
* @return {string} The color to use.
|
||||
*/
|
||||
goog.ui.GaugeTheme.prototype.getTitleColor = function() {
|
||||
return '#333333';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the color for the gauge value.
|
||||
* @return {string} The color to use.
|
||||
*/
|
||||
goog.ui.GaugeTheme.prototype.getValueColor = function() {
|
||||
return 'black';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the color for the labels (formatted values) of tick marks.
|
||||
* @return {string} The color to use.
|
||||
*/
|
||||
goog.ui.GaugeTheme.prototype.getTickLabelColor = function() {
|
||||
return '#333333';
|
||||
};
|
||||
456
nicer-api-docs/closure-library/closure/goog/ui/hovercard.js
Normal file
456
nicer-api-docs/closure-library/closure/goog/ui/hovercard.js
Normal file
@@ -0,0 +1,456 @@
|
||||
// Copyright 2008 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 Show hovercards with a delay after the mouse moves over an
|
||||
* element of a specified type and with a specific attribute.
|
||||
*
|
||||
* @see ../demos/hovercard.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.HoverCard');
|
||||
goog.provide('goog.ui.HoverCard.EventType');
|
||||
goog.provide('goog.ui.HoverCard.TriggerEvent');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.Event');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.ui.AdvancedTooltip');
|
||||
goog.require('goog.ui.PopupBase');
|
||||
goog.require('goog.ui.Tooltip');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create a hover card object. Hover cards extend tooltips in that they don't
|
||||
* have to be manually attached to each element that can cause them to display.
|
||||
* Instead, you can create a function that gets called when the mouse goes over
|
||||
* any element on your page, and returns whether or not the hovercard should be
|
||||
* shown for that element.
|
||||
*
|
||||
* Alternatively, you can define a map of tag names to the attribute name each
|
||||
* tag should have for that tag to trigger the hover card. See example below.
|
||||
*
|
||||
* Hovercards can also be triggered manually by calling
|
||||
* {@code triggerForElement}, shown without a delay by calling
|
||||
* {@code showForElement}, or triggered over other elements by calling
|
||||
* {@code attach}. For the latter two cases, the application is responsible
|
||||
* for calling {@code detach} when finished.
|
||||
*
|
||||
* HoverCard objects fire a TRIGGER event when the mouse moves over an element
|
||||
* that can trigger a hovercard, and BEFORE_SHOW when the hovercard is
|
||||
* about to be shown. Clients can respond to these events and can prevent the
|
||||
* hovercard from being triggered or shown.
|
||||
*
|
||||
* @param {Function|Object} isAnchor Function that returns true if a given
|
||||
* element should trigger the hovercard. Alternatively, it can be a map of
|
||||
* tag names to the attribute that the tag should have in order to trigger
|
||||
* the hovercard, e.g., {A: 'href'} for all links. Tag names must be all
|
||||
* upper case; attribute names are case insensitive.
|
||||
* @param {boolean=} opt_checkDescendants Use false for a performance gain if
|
||||
* you are sure that none of your triggering elements have child elements.
|
||||
* Default is true.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper to use for
|
||||
* creating and rendering the hovercard element.
|
||||
* @param {Document=} opt_triggeringDocument Optional document to use in place
|
||||
* of the one included in the DomHelper for finding triggering elements.
|
||||
* Defaults to the document included in the DomHelper.
|
||||
* @constructor
|
||||
* @extends {goog.ui.AdvancedTooltip}
|
||||
*/
|
||||
goog.ui.HoverCard = function(isAnchor, opt_checkDescendants, opt_domHelper,
|
||||
opt_triggeringDocument) {
|
||||
goog.ui.AdvancedTooltip.call(this, null, null, opt_domHelper);
|
||||
|
||||
if (goog.isFunction(isAnchor)) {
|
||||
// Override default implementation of {@code isAnchor_}.
|
||||
this.isAnchor_ = isAnchor;
|
||||
} else {
|
||||
|
||||
/**
|
||||
* Map of tag names to attribute names that will trigger a hovercard.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
this.anchors_ = isAnchor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether anchors may have child elements. If true, then we need to check
|
||||
* the parent chain of any mouse over event to see if any of those elements
|
||||
* could be anchors. Default is true.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.checkDescendants_ = opt_checkDescendants != false;
|
||||
|
||||
/**
|
||||
* Array of anchor elements that should be detached when we are no longer
|
||||
* associated with them.
|
||||
* @type {!Array.<Element>}
|
||||
* @private
|
||||
*/
|
||||
this.tempAttachedAnchors_ = [];
|
||||
|
||||
/**
|
||||
* Document containing the triggering elements, to which we listen for
|
||||
* mouseover events.
|
||||
* @type {Document}
|
||||
* @private
|
||||
*/
|
||||
this.document_ = opt_triggeringDocument || (opt_domHelper ?
|
||||
opt_domHelper.getDocument() : goog.dom.getDocument());
|
||||
|
||||
goog.events.listen(this.document_, goog.events.EventType.MOUSEOVER,
|
||||
this.handleTriggerMouseOver_, false, this);
|
||||
};
|
||||
goog.inherits(goog.ui.HoverCard, goog.ui.AdvancedTooltip);
|
||||
|
||||
|
||||
/**
|
||||
* Enum for event type fired by HoverCard.
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.ui.HoverCard.EventType = {
|
||||
TRIGGER: 'trigger',
|
||||
CANCEL_TRIGGER: 'canceltrigger',
|
||||
BEFORE_SHOW: goog.ui.PopupBase.EventType.BEFORE_SHOW,
|
||||
SHOW: goog.ui.PopupBase.EventType.SHOW,
|
||||
BEFORE_HIDE: goog.ui.PopupBase.EventType.BEFORE_HIDE,
|
||||
HIDE: goog.ui.PopupBase.EventType.HIDE
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.HoverCard.prototype.disposeInternal = function() {
|
||||
goog.ui.HoverCard.superClass_.disposeInternal.call(this);
|
||||
|
||||
goog.events.unlisten(this.document_, goog.events.EventType.MOUSEOVER,
|
||||
this.handleTriggerMouseOver_, false, this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Anchor of hovercard currently being shown. This may be different from
|
||||
* {@code anchor} property if a second hovercard is triggered, when
|
||||
* {@code anchor} becomes the second hovercard while {@code currentAnchor_}
|
||||
* is still the old (but currently displayed) anchor.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.HoverCard.prototype.currentAnchor_;
|
||||
|
||||
|
||||
/**
|
||||
* Maximum number of levels to search up the dom when checking descendants.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.HoverCard.prototype.maxSearchSteps_;
|
||||
|
||||
|
||||
/**
|
||||
* This function can be overridden by passing a function as the first parameter
|
||||
* to the constructor.
|
||||
* @param {Node} node Node to test.
|
||||
* @return {boolean} Whether or not hovercard should be shown.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.HoverCard.prototype.isAnchor_ = function(node) {
|
||||
return node.tagName in this.anchors_ &&
|
||||
!!node.getAttribute(this.anchors_[node.tagName]);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* If the user mouses over an element with the correct tag and attribute, then
|
||||
* trigger the hovercard for that element. If anchors could have children, then
|
||||
* we also need to check the parent chain of the given element.
|
||||
* @param {goog.events.Event} e Mouse over event.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.HoverCard.prototype.handleTriggerMouseOver_ = function(e) {
|
||||
var target = /** @type {Element} */ (e.target);
|
||||
// Target might be null when hovering over disabled input textboxes in IE.
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
if (this.isAnchor_(target)) {
|
||||
this.setPosition(null);
|
||||
this.triggerForElement(target);
|
||||
} else if (this.checkDescendants_) {
|
||||
var trigger = goog.dom.getAncestor(target,
|
||||
goog.bind(this.isAnchor_, this),
|
||||
false,
|
||||
this.maxSearchSteps_);
|
||||
if (trigger) {
|
||||
this.setPosition(null);
|
||||
this.triggerForElement(/** @type {Element} */ (trigger));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Triggers the hovercard to show after a delay.
|
||||
* @param {Element} anchorElement Element that is triggering the hovercard.
|
||||
* @param {goog.positioning.AbstractPosition=} opt_pos Position to display
|
||||
* hovercard.
|
||||
* @param {Object=} opt_data Data to pass to the onTrigger event.
|
||||
*/
|
||||
goog.ui.HoverCard.prototype.triggerForElement = function(anchorElement,
|
||||
opt_pos, opt_data) {
|
||||
if (anchorElement == this.currentAnchor_) {
|
||||
// Element is already showing, just make sure it doesn't hide.
|
||||
this.clearHideTimer();
|
||||
return;
|
||||
}
|
||||
if (anchorElement == this.anchor) {
|
||||
// Hovercard is pending, no need to retrigger.
|
||||
return;
|
||||
}
|
||||
|
||||
// If a previous hovercard was being triggered, cancel it.
|
||||
this.maybeCancelTrigger_();
|
||||
|
||||
// Create a new event for this trigger
|
||||
var triggerEvent = new goog.ui.HoverCard.TriggerEvent(
|
||||
goog.ui.HoverCard.EventType.TRIGGER, this, anchorElement, opt_data);
|
||||
|
||||
if (!this.getElements().contains(anchorElement)) {
|
||||
this.attach(anchorElement);
|
||||
this.tempAttachedAnchors_.push(anchorElement);
|
||||
}
|
||||
this.anchor = anchorElement;
|
||||
if (!this.onTrigger(triggerEvent)) {
|
||||
this.onCancelTrigger();
|
||||
return;
|
||||
}
|
||||
var pos = opt_pos || this.position_;
|
||||
this.startShowTimer(anchorElement,
|
||||
/** @type {goog.positioning.AbstractPosition} */ (pos));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the current anchor element at the time that the hovercard is shown.
|
||||
* @param {Element} anchor New current anchor element, or null if there is
|
||||
* no current anchor.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.HoverCard.prototype.setCurrentAnchor_ = function(anchor) {
|
||||
if (anchor != this.currentAnchor_) {
|
||||
this.detachTempAnchor_(this.currentAnchor_);
|
||||
}
|
||||
this.currentAnchor_ = anchor;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* If given anchor is in the list of temporarily attached anchors, then
|
||||
* detach and remove from the list.
|
||||
* @param {Element|undefined} anchor Anchor element that we may want to detach
|
||||
* from.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.HoverCard.prototype.detachTempAnchor_ = function(anchor) {
|
||||
var pos = goog.array.indexOf(this.tempAttachedAnchors_, anchor);
|
||||
if (pos != -1) {
|
||||
this.detach(anchor);
|
||||
this.tempAttachedAnchors_.splice(pos, 1);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Called when an element triggers the hovercard. This will return false
|
||||
* if an event handler sets preventDefault to true, which will prevent
|
||||
* the hovercard from being shown.
|
||||
* @param {!goog.ui.HoverCard.TriggerEvent} triggerEvent Event object to use
|
||||
* for trigger event.
|
||||
* @return {boolean} Whether hovercard should be shown or cancelled.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.HoverCard.prototype.onTrigger = function(triggerEvent) {
|
||||
return this.dispatchEvent(triggerEvent);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Abort pending hovercard showing, if any.
|
||||
*/
|
||||
goog.ui.HoverCard.prototype.cancelTrigger = function() {
|
||||
this.clearShowTimer();
|
||||
this.onCancelTrigger();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* If hovercard is in the process of being triggered, then cancel it.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.HoverCard.prototype.maybeCancelTrigger_ = function() {
|
||||
if (this.getState() == goog.ui.Tooltip.State.WAITING_TO_SHOW ||
|
||||
this.getState() == goog.ui.Tooltip.State.UPDATING) {
|
||||
this.cancelTrigger();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This method gets called when we detect that a trigger event will not lead
|
||||
* to the hovercard being shown.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.HoverCard.prototype.onCancelTrigger = function() {
|
||||
var event = new goog.ui.HoverCard.TriggerEvent(
|
||||
goog.ui.HoverCard.EventType.CANCEL_TRIGGER, this, this.anchor || null);
|
||||
this.dispatchEvent(event);
|
||||
this.detachTempAnchor_(this.anchor);
|
||||
delete this.anchor;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the DOM element that triggered the current hovercard. Note that in
|
||||
* the TRIGGER or CANCEL_TRIGGER events, the current hovercard's anchor may not
|
||||
* be the one that caused the event, so use the event's anchor property instead.
|
||||
* @return {Element} Object that caused the currently displayed hovercard (or
|
||||
* pending hovercard if none is displayed) to be triggered.
|
||||
*/
|
||||
goog.ui.HoverCard.prototype.getAnchorElement = function() {
|
||||
// this.currentAnchor_ is only set if the hovercard is showing. If it isn't
|
||||
// showing yet, then use this.anchor as the pending anchor.
|
||||
return /** @type {Element} */ (this.currentAnchor_ || this.anchor);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Make sure we detach from temp anchor when we are done displaying hovercard.
|
||||
* @protected
|
||||
* @suppress {underscore}
|
||||
* @override
|
||||
*/
|
||||
goog.ui.HoverCard.prototype.onHide_ = function() {
|
||||
goog.ui.HoverCard.superClass_.onHide_.call(this);
|
||||
this.setCurrentAnchor_(null);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This mouse over event is only received if the anchor is already attached.
|
||||
* If it was attached manually, then it may need to be triggered.
|
||||
* @param {goog.events.BrowserEvent} event Mouse over event.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.HoverCard.prototype.handleMouseOver = function(event) {
|
||||
// If this is a child of a triggering element, find the triggering element.
|
||||
var trigger = this.getAnchorFromElement(
|
||||
/** @type {Element} */ (event.target));
|
||||
|
||||
// If we moused over an element different from the one currently being
|
||||
// triggered (if any), then trigger this new element.
|
||||
if (trigger && trigger != this.anchor) {
|
||||
this.triggerForElement(trigger);
|
||||
return;
|
||||
}
|
||||
|
||||
goog.ui.HoverCard.superClass_.handleMouseOver.call(this, event);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* If the mouse moves out of the trigger while we're being triggered, then
|
||||
* cancel it.
|
||||
* @param {goog.events.BrowserEvent} event Mouse out or blur event.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.HoverCard.prototype.handleMouseOutAndBlur = function(event) {
|
||||
// Get ready to see if a trigger should be cancelled.
|
||||
var anchor = this.anchor;
|
||||
var state = this.getState();
|
||||
goog.ui.HoverCard.superClass_.handleMouseOutAndBlur.call(this, event);
|
||||
if (state != this.getState() &&
|
||||
(state == goog.ui.Tooltip.State.WAITING_TO_SHOW ||
|
||||
state == goog.ui.Tooltip.State.UPDATING)) {
|
||||
// Tooltip's handleMouseOutAndBlur method sets anchor to null. Reset
|
||||
// so that the cancel trigger event will have the right data, and so that
|
||||
// it will be properly detached.
|
||||
this.anchor = anchor;
|
||||
this.onCancelTrigger(); // This will remove and detach the anchor.
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Called by timer from mouse over handler. If this is called and the hovercard
|
||||
* is not shown for whatever reason, then send a cancel trigger event.
|
||||
* @param {Element} el Element to show tooltip for.
|
||||
* @param {goog.positioning.AbstractPosition=} opt_pos Position to display popup
|
||||
* at.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.HoverCard.prototype.maybeShow = function(el, opt_pos) {
|
||||
goog.ui.HoverCard.superClass_.maybeShow.call(this, el, opt_pos);
|
||||
|
||||
if (!this.isVisible()) {
|
||||
this.cancelTrigger();
|
||||
} else {
|
||||
this.setCurrentAnchor_(el);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the max number of levels to search up the dom if checking descendants.
|
||||
* @param {number} maxSearchSteps Maximum number of levels to search up the
|
||||
* dom if checking descendants.
|
||||
*/
|
||||
goog.ui.HoverCard.prototype.setMaxSearchSteps = function(maxSearchSteps) {
|
||||
if (!maxSearchSteps) {
|
||||
this.checkDescendants_ = false;
|
||||
} else if (this.checkDescendants_) {
|
||||
this.maxSearchSteps_ = maxSearchSteps;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create a trigger event for specified anchor and optional data.
|
||||
* @param {goog.ui.HoverCard.EventType} type Event type.
|
||||
* @param {goog.ui.HoverCard} target Hovercard that is triggering the event.
|
||||
* @param {Element} anchor Element that triggered event.
|
||||
* @param {Object=} opt_data Optional data to be available in the TRIGGER event.
|
||||
* @constructor
|
||||
* @extends {goog.events.Event}
|
||||
*/
|
||||
goog.ui.HoverCard.TriggerEvent = function(type, target, anchor, opt_data) {
|
||||
goog.events.Event.call(this, type, target);
|
||||
|
||||
/**
|
||||
* Element that triggered the hovercard event.
|
||||
* @type {Element}
|
||||
*/
|
||||
this.anchor = anchor;
|
||||
|
||||
/**
|
||||
* Optional data to be passed to the listener.
|
||||
* @type {Object|undefined}
|
||||
*/
|
||||
this.data = opt_data;
|
||||
};
|
||||
goog.inherits(goog.ui.HoverCard.TriggerEvent, goog.events.Event);
|
||||
298
nicer-api-docs/closure-library/closure/goog/ui/hsvapalette.js
Normal file
298
nicer-api-docs/closure-library/closure/goog/ui/hsvapalette.js
Normal file
@@ -0,0 +1,298 @@
|
||||
// Copyright 2008 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 An HSVA (hue/saturation/value/alpha) color palette/picker
|
||||
* implementation.
|
||||
* Without the styles from the demo css file, only a hex color label and input
|
||||
* field show up.
|
||||
*
|
||||
* @see ../demos/hsvapalette.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.HsvaPalette');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.color.alpha');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('goog.ui.HsvPalette');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates an HSVA palette. Allows a user to select the hue, saturation,
|
||||
* value/brightness and alpha/opacity.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
|
||||
* @param {string=} opt_color Optional initial color, without alpha (default is
|
||||
* red).
|
||||
* @param {number=} opt_alpha Optional initial alpha (default is 1).
|
||||
* @param {string=} opt_class Optional base for creating classnames (default is
|
||||
* 'goog-hsva-palette').
|
||||
* @extends {goog.ui.HsvPalette}
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.HsvaPalette = function(opt_domHelper, opt_color, opt_alpha, opt_class) {
|
||||
goog.base(this, opt_domHelper, opt_color, opt_class);
|
||||
|
||||
/**
|
||||
* Alpha transparency of the currently selected color, in [0, 1]. When
|
||||
* undefined, the palette will behave as a non-transparent HSV palette,
|
||||
* assuming full opacity.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.alpha_ = goog.isDef(opt_alpha) ? opt_alpha : 1;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
this.className = opt_class || goog.getCssName('goog-hsva-palette');
|
||||
|
||||
/**
|
||||
* The document which is being listened to.
|
||||
* type {HTMLDocument}
|
||||
* @private
|
||||
*/
|
||||
this.document_ = opt_domHelper ? opt_domHelper.getDocument() :
|
||||
goog.dom.getDomHelper().getDocument();
|
||||
};
|
||||
goog.inherits(goog.ui.HsvaPalette, goog.ui.HsvPalette);
|
||||
|
||||
|
||||
/**
|
||||
* DOM element representing the alpha background image.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.HsvaPalette.prototype.aImageEl_;
|
||||
|
||||
|
||||
/**
|
||||
* DOM element representing the alpha handle.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.HsvaPalette.prototype.aHandleEl_;
|
||||
|
||||
|
||||
/**
|
||||
* DOM element representing the swatch backdrop image.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.HsvaPalette.prototype.swatchBackdropEl_;
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.HsvaPalette.prototype.getAlpha = function() {
|
||||
return this.alpha_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets which color is selected and update the UI. The passed color should be
|
||||
* in #rrggbb format. The alpha value will be set to 1.
|
||||
* @param {number} alpha The selected alpha value, in [0, 1].
|
||||
*/
|
||||
goog.ui.HsvaPalette.prototype.setAlpha = function(alpha) {
|
||||
this.setColorAlphaHelper_(this.color_, alpha);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets which color is selected and update the UI. The passed color should be
|
||||
* in #rrggbb format. The alpha value will be set to 1.
|
||||
* @param {string} color The selected color.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.HsvaPalette.prototype.setColor = function(color) {
|
||||
this.setColorAlphaHelper_(color, 1);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the color that is currently selected in this color picker, in #rrggbbaa
|
||||
* format.
|
||||
* @return {string} The string of the selected color with alpha.
|
||||
*/
|
||||
goog.ui.HsvaPalette.prototype.getColorRgbaHex = function() {
|
||||
var alphaHex = Math.floor(this.alpha_ * 255).toString(16);
|
||||
return this.color_ + (alphaHex.length == 1 ? '0' + alphaHex : alphaHex);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets which color is selected and update the UI. The passed color should be
|
||||
* in #rrggbbaa format. The alpha value will be set to 1.
|
||||
* @param {string} color The selected color with alpha.
|
||||
*/
|
||||
goog.ui.HsvaPalette.prototype.setColorRgbaHex = function(color) {
|
||||
var parsed = goog.ui.HsvaPalette.parseColorRgbaHex_(color);
|
||||
this.setColorAlphaHelper_(parsed[0], parsed[1]);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets which color and alpha value are selected and update the UI. The passed
|
||||
* color should be in #rrggbb format.
|
||||
* @param {string} color The selected color in #rrggbb format.
|
||||
* @param {number} alpha The selected alpha value, in [0, 1].
|
||||
* @private
|
||||
*/
|
||||
goog.ui.HsvaPalette.prototype.setColorAlphaHelper_ = function(color, alpha) {
|
||||
var colorChange = this.color_ != color;
|
||||
var alphaChange = this.alpha_ != alpha;
|
||||
this.alpha_ = alpha;
|
||||
this.color_ = color;
|
||||
if (colorChange) {
|
||||
// This is to prevent multiple event dispatches.
|
||||
goog.ui.HsvaPalette.superClass_.setColor_.call(this, color);
|
||||
}
|
||||
if (colorChange || alphaChange) {
|
||||
this.updateUi();
|
||||
this.dispatchEvent(goog.ui.Component.EventType.ACTION);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.HsvaPalette.prototype.createDom = function() {
|
||||
goog.ui.HsvaPalette.superClass_.createDom.call(this);
|
||||
|
||||
var dom = this.getDomHelper();
|
||||
this.aImageEl_ = dom.createDom(
|
||||
goog.dom.TagName.DIV, goog.getCssName(this.className, 'a-image'));
|
||||
this.aHandleEl_ = dom.createDom(
|
||||
goog.dom.TagName.DIV, goog.getCssName(this.className, 'a-handle'));
|
||||
this.swatchBackdropEl_ = dom.createDom(
|
||||
goog.dom.TagName.DIV, goog.getCssName(this.className, 'swatch-backdrop'));
|
||||
dom.appendChild(this.element_, this.aImageEl_);
|
||||
dom.appendChild(this.element_, this.aHandleEl_);
|
||||
dom.appendChild(this.element_, this.swatchBackdropEl_);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.HsvaPalette.prototype.disposeInternal = function() {
|
||||
goog.ui.HsvaPalette.superClass_.disposeInternal.call(this);
|
||||
|
||||
delete this.aImageEl_;
|
||||
delete this.aHandleEl_;
|
||||
delete this.swatchBackdropEl_;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.HsvaPalette.prototype.updateUi = function() {
|
||||
goog.base(this, 'updateUi');
|
||||
if (this.isInDocument()) {
|
||||
var a = this.alpha_ * 255;
|
||||
var top = this.aImageEl_.offsetTop -
|
||||
Math.floor(this.aHandleEl_.offsetHeight / 2) +
|
||||
this.aImageEl_.offsetHeight * ((255 - a) / 255);
|
||||
this.aHandleEl_.style.top = top + 'px';
|
||||
this.aImageEl_.style.backgroundColor = this.color_;
|
||||
goog.style.setOpacity(this.swatchElement, a / 255);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.HsvaPalette.prototype.updateInput = function() {
|
||||
if (!goog.array.equals([this.color_, this.alpha_],
|
||||
goog.ui.HsvaPalette.parseUserInput_(this.inputElement.value))) {
|
||||
this.inputElement.value = this.getColorRgbaHex();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.HsvaPalette.prototype.handleMouseDown = function(e) {
|
||||
goog.base(this, 'handleMouseDown', e);
|
||||
if (e.target == this.aImageEl_ || e.target == this.aHandleEl_) {
|
||||
// Setup value change listeners
|
||||
var b = goog.style.getBounds(this.valueBackgroundImageElement);
|
||||
this.handleMouseMoveA_(b, e);
|
||||
this.mouseMoveListener_ = goog.events.listen(this.document_,
|
||||
goog.events.EventType.MOUSEMOVE,
|
||||
goog.bind(this.handleMouseMoveA_, this, b));
|
||||
this.mouseUpListener_ = goog.events.listen(this.document_,
|
||||
goog.events.EventType.MOUSEUP, this.handleMouseUp, false, this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles mousemove events on the document once a drag operation on the alpha
|
||||
* slider has started.
|
||||
* @param {goog.math.Rect} b Boundaries of the value slider object at the start
|
||||
* of the drag operation.
|
||||
* @param {goog.events.Event} e Event object.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.HsvaPalette.prototype.handleMouseMoveA_ = function(b, e) {
|
||||
e.preventDefault();
|
||||
var vportPos = this.getDomHelper().getDocumentScroll();
|
||||
var newA = (b.top + b.height - Math.min(
|
||||
Math.max(vportPos.y + e.clientY, b.top),
|
||||
b.top + b.height)) / b.height;
|
||||
this.setAlpha(newA);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.HsvaPalette.prototype.handleInput = function(e) {
|
||||
var parsed = goog.ui.HsvaPalette.parseUserInput_(this.inputElement.value);
|
||||
if (parsed) {
|
||||
this.setColorAlphaHelper_(parsed[0], parsed[1]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Parses an #rrggbb or #rrggbbaa color string.
|
||||
* @param {string} value User-entered color value.
|
||||
* @return {Array} A two element array [color, alpha], where color is #rrggbb
|
||||
* and alpha is in [0, 1]. Null if the argument was invalid.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.HsvaPalette.parseUserInput_ = function(value) {
|
||||
if (/^#[0-9a-f]{8}$/i.test(value)) {
|
||||
return goog.ui.HsvaPalette.parseColorRgbaHex_(value);
|
||||
} else if (/^#[0-9a-f]{6}$/i.test(value)) {
|
||||
return [value, 1];
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Parses a #rrggbbaa color string.
|
||||
* @param {string} color The color and alpha in #rrggbbaa format.
|
||||
* @return {Array} A two element array [color, alpha], where color is #rrggbb
|
||||
* and alpha is in [0, 1].
|
||||
* @private
|
||||
*/
|
||||
goog.ui.HsvaPalette.parseColorRgbaHex_ = function(color) {
|
||||
var hex = goog.color.alpha.parse(color).hex;
|
||||
return [
|
||||
goog.color.alpha.extractHexColor(hex),
|
||||
parseInt(goog.color.alpha.extractAlpha(hex), 16) / 255
|
||||
];
|
||||
};
|
||||
505
nicer-api-docs/closure-library/closure/goog/ui/hsvpalette.js
Normal file
505
nicer-api-docs/closure-library/closure/goog/ui/hsvpalette.js
Normal file
@@ -0,0 +1,505 @@
|
||||
// Copyright 2008 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 An HSV (hue/saturation/value) color palette/picker
|
||||
* implementation. Inspired by examples like
|
||||
* http://johndyer.name/lab/colorpicker/ and the author's initial work. This
|
||||
* control allows for more control in picking colors than a simple swatch-based
|
||||
* palette. Without the styles from the demo css file, only a hex color label
|
||||
* and input field show up.
|
||||
*
|
||||
* @author arv@google.com (Erik Arvidsson)
|
||||
* @author smcbride@google.com (Sean McBride)
|
||||
* @author manucornet@google.com (Manu Cornet)
|
||||
* @see ../demos/hsvpalette.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.ui.HsvPalette');
|
||||
|
||||
goog.require('goog.color');
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.events.EventType');
|
||||
goog.require('goog.events.InputHandler');
|
||||
goog.require('goog.style');
|
||||
goog.require('goog.style.bidi');
|
||||
goog.require('goog.ui.Component');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates an HSV palette. Allows a user to select the hue, saturation and
|
||||
* value/brightness.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
|
||||
* @param {string=} opt_color Optional initial color (default is red).
|
||||
* @param {string=} opt_class Optional base for creating classnames (default is
|
||||
* goog.getCssName('goog-hsv-palette')).
|
||||
* @extends {goog.ui.Component}
|
||||
* @constructor
|
||||
*/
|
||||
goog.ui.HsvPalette = function(opt_domHelper, opt_color, opt_class) {
|
||||
goog.ui.Component.call(this, opt_domHelper);
|
||||
|
||||
this.setColor_(opt_color || '#f00');
|
||||
|
||||
/**
|
||||
* The base class name for the component.
|
||||
* @type {string}
|
||||
* @protected
|
||||
*/
|
||||
this.className = opt_class || goog.getCssName('goog-hsv-palette');
|
||||
|
||||
/**
|
||||
* The document which is being listened to.
|
||||
* type {HTMLDocument}
|
||||
* @private
|
||||
*/
|
||||
this.document_ = this.getDomHelper().getDocument();
|
||||
};
|
||||
goog.inherits(goog.ui.HsvPalette, goog.ui.Component);
|
||||
// TODO(user): Make this inherit from goog.ui.Control and split this into
|
||||
// a control and a renderer.
|
||||
|
||||
|
||||
/**
|
||||
* DOM element representing the hue/saturation background image.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.HsvPalette.prototype.hsImageEl_;
|
||||
|
||||
|
||||
/**
|
||||
* DOM element representing the hue/saturation handle.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.HsvPalette.prototype.hsHandleEl_;
|
||||
|
||||
|
||||
/**
|
||||
* DOM element representing the value background image.
|
||||
* @type {Element}
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.HsvPalette.prototype.valueBackgroundImageElement;
|
||||
|
||||
|
||||
/**
|
||||
* DOM element representing the value handle.
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.HsvPalette.prototype.vHandleEl_;
|
||||
|
||||
|
||||
/**
|
||||
* DOM element representing the current color swatch.
|
||||
* @type {Element}
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.HsvPalette.prototype.swatchElement;
|
||||
|
||||
|
||||
/**
|
||||
* DOM element representing the hex color input text field.
|
||||
* @type {Element}
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.HsvPalette.prototype.inputElement;
|
||||
|
||||
|
||||
/**
|
||||
* Input handler object for the hex value input field.
|
||||
* @type {goog.events.InputHandler}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.HsvPalette.prototype.inputHandler_;
|
||||
|
||||
|
||||
/**
|
||||
* Listener key for the mousemove event (during a drag operation).
|
||||
* @type {goog.events.Key}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.HsvPalette.prototype.mouseMoveListener_;
|
||||
|
||||
|
||||
/**
|
||||
* Listener key for the mouseup event (during a drag operation).
|
||||
* @type {goog.events.Key}
|
||||
* @private
|
||||
*/
|
||||
goog.ui.HsvPalette.prototype.mouseUpListener_;
|
||||
|
||||
|
||||
/**
|
||||
* Gets the color that is currently selected in this color picker.
|
||||
* @return {string} The string of the selected color.
|
||||
*/
|
||||
goog.ui.HsvPalette.prototype.getColor = function() {
|
||||
return this.color_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Alpha transparency of the currently selected color, in [0, 1].
|
||||
* For the HSV palette this always returns 1. The HSVA palette overrides
|
||||
* this method.
|
||||
* @return {number} The current alpha value.
|
||||
*/
|
||||
goog.ui.HsvPalette.prototype.getAlpha = function() {
|
||||
return 1;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Updates the text entry field.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.HsvPalette.prototype.updateInput = function() {
|
||||
var parsed;
|
||||
try {
|
||||
parsed = goog.color.parse(this.inputElement.value).hex;
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
if (this.color_ != parsed) {
|
||||
this.inputElement.value = this.color_;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets which color is selected and update the UI.
|
||||
* @param {string} color The selected color.
|
||||
*/
|
||||
goog.ui.HsvPalette.prototype.setColor = function(color) {
|
||||
if (color != this.color_) {
|
||||
this.setColor_(color);
|
||||
this.updateUi();
|
||||
this.dispatchEvent(goog.ui.Component.EventType.ACTION);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets which color is selected.
|
||||
* @param {string} color The selected color.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.HsvPalette.prototype.setColor_ = function(color) {
|
||||
var rgbHex = goog.color.parse(color).hex;
|
||||
var rgbArray = goog.color.hexToRgb(rgbHex);
|
||||
this.hsv_ = goog.color.rgbArrayToHsv(rgbArray);
|
||||
// Hue is divided by 360 because the documentation for goog.color is currently
|
||||
// incorrect.
|
||||
// TODO(user): Fix this, see http://1324469 .
|
||||
this.hsv_[0] = this.hsv_[0] / 360;
|
||||
this.color_ = rgbHex;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Alters the hue, saturation, and/or value of the currently selected color and
|
||||
* updates the UI.
|
||||
* @param {?number=} opt_hue (optional) hue in [0, 1].
|
||||
* @param {?number=} opt_saturation (optional) saturation in [0, 1].
|
||||
* @param {?number=} opt_value (optional) value in [0, 255].
|
||||
*/
|
||||
goog.ui.HsvPalette.prototype.setHsv = function(opt_hue,
|
||||
opt_saturation,
|
||||
opt_value) {
|
||||
if (opt_hue != null || opt_saturation != null || opt_value != null) {
|
||||
this.setHsv_(opt_hue, opt_saturation, opt_value);
|
||||
this.updateUi();
|
||||
this.dispatchEvent(goog.ui.Component.EventType.ACTION);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Alters the hue, saturation, and/or value of the currently selected color.
|
||||
* @param {?number=} opt_hue (optional) hue in [0, 1].
|
||||
* @param {?number=} opt_saturation (optional) saturation in [0, 1].
|
||||
* @param {?number=} opt_value (optional) value in [0, 255].
|
||||
* @private
|
||||
*/
|
||||
goog.ui.HsvPalette.prototype.setHsv_ = function(opt_hue,
|
||||
opt_saturation,
|
||||
opt_value) {
|
||||
this.hsv_[0] = (opt_hue != null) ? opt_hue : this.hsv_[0];
|
||||
this.hsv_[1] = (opt_saturation != null) ? opt_saturation : this.hsv_[1];
|
||||
this.hsv_[2] = (opt_value != null) ? opt_value : this.hsv_[2];
|
||||
// Hue is multiplied by 360 because the documentation for goog.color is
|
||||
// currently incorrect.
|
||||
// TODO(user): Fix this, see http://1324469 .
|
||||
this.color_ = goog.color.hsvArrayToHex([
|
||||
this.hsv_[0] * 360,
|
||||
this.hsv_[1],
|
||||
this.hsv_[2]
|
||||
]);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* HsvPalettes cannot be used to decorate pre-existing html, since the
|
||||
* structure they build is fairly complicated.
|
||||
* @param {Element} element Element to decorate.
|
||||
* @return {boolean} Returns always false.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.HsvPalette.prototype.canDecorate = function(element) {
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.HsvPalette.prototype.createDom = function() {
|
||||
var dom = this.getDomHelper();
|
||||
var noalpha = (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('7')) ?
|
||||
' ' + goog.getCssName(this.className, 'noalpha') : '';
|
||||
|
||||
var backdrop = dom.createDom(goog.dom.TagName.DIV,
|
||||
goog.getCssName(this.className, 'hs-backdrop'));
|
||||
|
||||
this.hsHandleEl_ = dom.createDom(goog.dom.TagName.DIV,
|
||||
goog.getCssName(this.className, 'hs-handle'));
|
||||
|
||||
this.hsImageEl_ = dom.createDom(goog.dom.TagName.DIV,
|
||||
goog.getCssName(this.className, 'hs-image'),
|
||||
this.hsHandleEl_);
|
||||
|
||||
this.valueBackgroundImageElement = dom.createDom(
|
||||
goog.dom.TagName.DIV,
|
||||
goog.getCssName(this.className, 'v-image'));
|
||||
|
||||
this.vHandleEl_ = dom.createDom(
|
||||
goog.dom.TagName.DIV,
|
||||
goog.getCssName(this.className, 'v-handle'));
|
||||
|
||||
this.swatchElement = dom.createDom(goog.dom.TagName.DIV,
|
||||
goog.getCssName(this.className, 'swatch'));
|
||||
|
||||
this.inputElement = dom.createDom('input', {
|
||||
'class': goog.getCssName(this.className, 'input'),
|
||||
'type': 'text', 'dir': 'ltr'
|
||||
});
|
||||
|
||||
var labelElement = dom.createDom('label', null, this.inputElement);
|
||||
|
||||
var element = dom.createDom(goog.dom.TagName.DIV,
|
||||
this.className + noalpha,
|
||||
backdrop,
|
||||
this.hsImageEl_,
|
||||
this.valueBackgroundImageElement,
|
||||
this.vHandleEl_,
|
||||
this.swatchElement,
|
||||
labelElement);
|
||||
|
||||
this.setElementInternal(element);
|
||||
|
||||
// TODO(arv): Set tabIndex
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Renders the color picker inside the provided element. This will override the
|
||||
* current content of the element.
|
||||
* @override
|
||||
*/
|
||||
goog.ui.HsvPalette.prototype.enterDocument = function() {
|
||||
goog.ui.HsvPalette.superClass_.enterDocument.call(this);
|
||||
|
||||
// TODO(user): Accessibility.
|
||||
|
||||
this.updateUi();
|
||||
|
||||
var handler = this.getHandler();
|
||||
handler.listen(this.getElement(), goog.events.EventType.MOUSEDOWN,
|
||||
this.handleMouseDown, false, this);
|
||||
|
||||
// Cannot create InputHandler in createDom because IE throws an exception
|
||||
// on document.activeElement
|
||||
if (!this.inputHandler_) {
|
||||
this.inputHandler_ = new goog.events.InputHandler(this.inputElement);
|
||||
}
|
||||
|
||||
handler.listen(this.inputHandler_,
|
||||
goog.events.InputHandler.EventType.INPUT, this.handleInput, false, this);
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.ui.HsvPalette.prototype.disposeInternal = function() {
|
||||
goog.ui.HsvPalette.superClass_.disposeInternal.call(this);
|
||||
|
||||
delete this.hsImageEl_;
|
||||
delete this.hsHandleEl_;
|
||||
delete this.valueBackgroundImageElement;
|
||||
delete this.vHandleEl_;
|
||||
delete this.swatchElement;
|
||||
delete this.inputElement;
|
||||
if (this.inputHandler_) {
|
||||
this.inputHandler_.dispose();
|
||||
delete this.inputHandler_;
|
||||
}
|
||||
goog.events.unlistenByKey(this.mouseMoveListener_);
|
||||
goog.events.unlistenByKey(this.mouseUpListener_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Updates the position, opacity, and styles for the UI representation of the
|
||||
* palette.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.HsvPalette.prototype.updateUi = function() {
|
||||
if (this.isInDocument()) {
|
||||
var h = this.hsv_[0];
|
||||
var s = this.hsv_[1];
|
||||
var v = this.hsv_[2];
|
||||
|
||||
var left = this.hsImageEl_.offsetWidth * h;
|
||||
|
||||
// We don't use a flipped gradient image in RTL, so we need to flip the
|
||||
// offset in RTL so that it still hovers over the correct color on the
|
||||
// gradiant.
|
||||
if (this.isRightToLeft()) {
|
||||
left = this.hsImageEl_.offsetWidth - left;
|
||||
}
|
||||
|
||||
// We also need to account for the handle size.
|
||||
var handleOffset = Math.ceil(this.hsHandleEl_.offsetWidth / 2);
|
||||
left -= handleOffset;
|
||||
|
||||
var top = this.hsImageEl_.offsetHeight * (1 - s);
|
||||
// Account for the handle size.
|
||||
top -= Math.ceil(this.hsHandleEl_.offsetHeight / 2);
|
||||
|
||||
goog.style.bidi.setPosition(this.hsHandleEl_, left, top,
|
||||
this.isRightToLeft());
|
||||
|
||||
top = this.valueBackgroundImageElement.offsetTop -
|
||||
Math.floor(this.vHandleEl_.offsetHeight / 2) +
|
||||
this.valueBackgroundImageElement.offsetHeight * ((255 - v) / 255);
|
||||
|
||||
this.vHandleEl_.style.top = top + 'px';
|
||||
goog.style.setOpacity(this.hsImageEl_, (v / 255));
|
||||
|
||||
goog.style.setStyle(this.valueBackgroundImageElement, 'background-color',
|
||||
goog.color.hsvToHex(this.hsv_[0] * 360, this.hsv_[1], 255));
|
||||
|
||||
goog.style.setStyle(this.swatchElement, 'background-color', this.color_);
|
||||
goog.style.setStyle(this.swatchElement, 'color',
|
||||
(this.hsv_[2] > 255 / 2) ? '#000' : '#fff');
|
||||
this.updateInput();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles mousedown events on palette UI elements.
|
||||
* @param {goog.events.BrowserEvent} e Event object.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.HsvPalette.prototype.handleMouseDown = function(e) {
|
||||
if (e.target == this.valueBackgroundImageElement ||
|
||||
e.target == this.vHandleEl_) {
|
||||
// Setup value change listeners
|
||||
var b = goog.style.getBounds(this.valueBackgroundImageElement);
|
||||
this.handleMouseMoveV_(b, e);
|
||||
this.mouseMoveListener_ = goog.events.listen(this.document_,
|
||||
goog.events.EventType.MOUSEMOVE,
|
||||
goog.bind(this.handleMouseMoveV_, this, b));
|
||||
this.mouseUpListener_ = goog.events.listen(this.document_,
|
||||
goog.events.EventType.MOUSEUP, this.handleMouseUp, false, this);
|
||||
} else if (e.target == this.hsImageEl_ || e.target == this.hsHandleEl_) {
|
||||
// Setup hue/saturation change listeners
|
||||
var b = goog.style.getBounds(this.hsImageEl_);
|
||||
this.handleMouseMoveHs_(b, e);
|
||||
this.mouseMoveListener_ = goog.events.listen(this.document_,
|
||||
goog.events.EventType.MOUSEMOVE,
|
||||
goog.bind(this.handleMouseMoveHs_, this, b));
|
||||
this.mouseUpListener_ = goog.events.listen(this.document_,
|
||||
goog.events.EventType.MOUSEUP, this.handleMouseUp, false, this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles mousemove events on the document once a drag operation on the value
|
||||
* slider has started.
|
||||
* @param {goog.math.Rect} b Boundaries of the value slider object at the start
|
||||
* of the drag operation.
|
||||
* @param {goog.events.BrowserEvent} e Event object.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.HsvPalette.prototype.handleMouseMoveV_ = function(b, e) {
|
||||
e.preventDefault();
|
||||
var vportPos = this.getDomHelper().getDocumentScroll();
|
||||
|
||||
var height = Math.min(
|
||||
Math.max(vportPos.y + e.clientY, b.top),
|
||||
b.top + b.height);
|
||||
|
||||
var newV = Math.round(
|
||||
255 * (b.top + b.height - height) / b.height);
|
||||
|
||||
this.setHsv(null, null, newV);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles mousemove events on the document once a drag operation on the
|
||||
* hue/saturation slider has started.
|
||||
* @param {goog.math.Rect} b Boundaries of the value slider object at the start
|
||||
* of the drag operation.
|
||||
* @param {goog.events.BrowserEvent} e Event object.
|
||||
* @private
|
||||
*/
|
||||
goog.ui.HsvPalette.prototype.handleMouseMoveHs_ = function(b, e) {
|
||||
e.preventDefault();
|
||||
var vportPos = this.getDomHelper().getDocumentScroll();
|
||||
var newH = (Math.min(Math.max(vportPos.x + e.clientX, b.left),
|
||||
b.left + b.width) - b.left) / b.width;
|
||||
var newS = (-Math.min(Math.max(vportPos.y + e.clientY, b.top),
|
||||
b.top + b.height) + b.top + b.height) / b.height;
|
||||
this.setHsv(newH, newS, null);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles mouseup events on the document, which ends a drag operation.
|
||||
* @param {goog.events.Event} e Event object.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.HsvPalette.prototype.handleMouseUp = function(e) {
|
||||
goog.events.unlistenByKey(this.mouseMoveListener_);
|
||||
goog.events.unlistenByKey(this.mouseUpListener_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handles input events on the hex value input field.
|
||||
* @param {goog.events.Event} e Event object.
|
||||
* @protected
|
||||
*/
|
||||
goog.ui.HsvPalette.prototype.handleInput = function(e) {
|
||||
if (/^#[0-9a-f]{6}$/i.test(this.inputElement.value)) {
|
||||
this.setColor(this.inputElement.value);
|
||||
}
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user