From 489a965500d4101b147a4a106fdbef051f97dcc7 Mon Sep 17 00:00:00 2001
From: toyomoyo @I2z;yA7(lgGa&E3x|;RV@bB*M9w!
z+hA1ns)v#B*^{x*P0SkNT`$lbbO1
zME0ERVyeQj0n}Jv{oq}t;(WYbiBvHA{v>|sAOH9VUq1Wx=i{&V_yqRn;}h7QkI%<
FNSYztt>#^x{9&}1iB)CrBSv~c-4}XJO4VNu`kuHu8xMR`H+r@WMy3&f@(oD
zV-IQUBfyT>ItFjOyTqV&0>y3{a4&QaiC7u|8A%bSB@^2+!6JcGyO0!VK!4kJZHk>1
znNk-3?ybKKaCMsmp|tx{qUMoqf7#pY9YA&lk%ZZwrPU|5J9327sG0NZzDc%Dr%025
zqCIwz@d$Rqv$k9j0=G{sMPM7F99180rOMfIOX{5lSZj~hBGDq+p)@>{DBd>D(nB2@
zz1GjEM9YEo=n!M0*-1Ij*Dw%_*w@JncJ0>&i~KC^eP`rF2U)|W{@J)VA!A!|1Qar*
z*jjIw0i|Z_ef0$;GbCeMEUibRZ?mK78J{kaZ6ym=Hg~jl6AQ>=HEZjAwYpG94|(1j
zW6N|q-Bz=DfN-cDdxU)xSwru&TsGqlj#fXTK+$96&LKfghSJ8J%i;Ld466Ic7^EwK
zuHB7AWfjo^qGj`m7f~6w&sVL%jGPcDOR5_pYDq3q8i?$oc2L&4#06v<$EoipXGE
zNe;3`$sh*=YVSyj90(jorckgu1=C1Ik&MB+)?!7BTn*jE=l~4l2WgpM$d0D|LtS8)
zRn(GZ-@YY{fb$?L^I#iGmZ(nqu
Ijz>u$oivp7#D1d0NoTZEjF9$;1MK(IMCCG
zx#4mA491Rzkwx{-qhlU*CEw$JHpIZDM;^H+`-KBjpLq;>6vPe?U29I&lkB=&7dC0T
zkL-zu_B9&G9&wIzIR`R>lTzb(!J|=U9R-`&X&KV;#bWceXp7Ay_biXxa!>Y)!Gw|^
zRzPXZRpL4+;23w5y4e9ilxJLLJ(vJZZMK~=hmp~M@T|Zw#n?zU2c03D=h^@)oM4jt
zsnM*VVdlh?mfl95dUI}PN`okEK2kL0v3MjOvN9u+K?D|Fd;ZM@K@!6VE-4WpdRjVS
z`e9&)&ImiJgbn!Hp)7H-n8gVs%>dH{es0F7IG{|}g8|!fpqD#of!gVkQq&1hX3_GZ
z_3_C-5c<`Ii&6%I$Pr){sa
- *
- *
- * @class AutoComplete
- * @constructor
- * @param elInput {HTMLElement} DOM element reference of an input field.
- * @param elInput {String} String ID of an input field.
- * @param elContainer {HTMLElement} DOM element reference of an existing DIV.
- * @param elContainer {String} String ID of an existing DIV.
- * @param oDataSource {Object} Instance of YAHOO.widget.DataSource for query/results.
- * @param oConfigs {Object} (optional) Object literal of configuration params.
- */
-YAHOO.widget.AutoComplete = function(elInput,elContainer,oDataSource,oConfigs) {
- if(elInput && elContainer && oDataSource) {
- // Validate DataSource
- if (oDataSource && (oDataSource instanceof YAHOO.widget.DataSource)) {
- this.dataSource = oDataSource;
- }
- else {
- YAHOO.log("Could not instantiate AutoComplete due to an invalid DataSource", "error", this.toString());
- return;
- }
-
- // Validate input element
- if(YAHOO.util.Dom.inDocument(elInput)) {
- if(typeof elInput == "string") {
- this._sName = "instance" + YAHOO.widget.AutoComplete._nIndex + " " + elInput;
- this._oTextbox = document.getElementById(elInput);
- }
- else {
- this._sName = (elInput.id) ?
- "instance" + YAHOO.widget.AutoComplete._nIndex + " " + elInput.id:
- "instance" + YAHOO.widget.AutoComplete._nIndex;
- this._oTextbox = elInput;
- }
- }
- else {
- YAHOO.log("Could not instantiate AutoComplete due to an invalid input element", "error", this.toString());
- return;
- }
-
- // Validate container element
- if(YAHOO.util.Dom.inDocument(elContainer)) {
- if(typeof elContainer == "string") {
- this._oContainer = document.getElementById(elContainer);
- }
- else {
- this._oContainer = elContainer;
- }
- if(this._oContainer.style.display == "none") {
- YAHOO.log("The container may not display properly if display is set to \"none\" in CSS", "warn", this.toString());
- }
- }
- else {
- YAHOO.log("Could not instantiate AutoComplete due to an invalid container element", "error", this.toString());
- return;
- }
-
- // Set any config params passed in to override defaults
- if (typeof oConfigs == "object") {
- for(var sConfig in oConfigs) {
- if (sConfig) {
- this[sConfig] = oConfigs[sConfig];
- }
- }
- }
-
- // Initialization sequence
- this._initContainer();
- this._initProps();
- this._initList();
- this._initContainerHelpers();
-
- // Set up events
- var oSelf = this;
- var oTextbox = this._oTextbox;
- // Events are actually for the content module within the container
- var oContent = this._oContainer._oContent;
-
- // Dom events
- YAHOO.util.Event.addListener(oTextbox,"keyup",oSelf._onTextboxKeyUp,oSelf);
- YAHOO.util.Event.addListener(oTextbox,"keydown",oSelf._onTextboxKeyDown,oSelf);
- YAHOO.util.Event.addListener(oTextbox,"focus",oSelf._onTextboxFocus,oSelf);
- YAHOO.util.Event.addListener(oTextbox,"blur",oSelf._onTextboxBlur,oSelf);
- YAHOO.util.Event.addListener(oContent,"mouseover",oSelf._onContainerMouseover,oSelf);
- YAHOO.util.Event.addListener(oContent,"mouseout",oSelf._onContainerMouseout,oSelf);
- YAHOO.util.Event.addListener(oContent,"scroll",oSelf._onContainerScroll,oSelf);
- YAHOO.util.Event.addListener(oContent,"resize",oSelf._onContainerResize,oSelf);
- if(oTextbox.form) {
- YAHOO.util.Event.addListener(oTextbox.form,"submit",oSelf._onFormSubmit,oSelf);
- }
- YAHOO.util.Event.addListener(oTextbox,"keypress",oSelf._onTextboxKeyPress,oSelf);
-
- // Custom events
- this.textboxFocusEvent = new YAHOO.util.CustomEvent("textboxFocus", this);
- this.textboxKeyEvent = new YAHOO.util.CustomEvent("textboxKey", this);
- this.dataRequestEvent = new YAHOO.util.CustomEvent("dataRequest", this);
- this.dataReturnEvent = new YAHOO.util.CustomEvent("dataReturn", this);
- this.dataErrorEvent = new YAHOO.util.CustomEvent("dataError", this);
- this.containerExpandEvent = new YAHOO.util.CustomEvent("containerExpand", this);
- this.typeAheadEvent = new YAHOO.util.CustomEvent("typeAhead", this);
- this.itemMouseOverEvent = new YAHOO.util.CustomEvent("itemMouseOver", this);
- this.itemMouseOutEvent = new YAHOO.util.CustomEvent("itemMouseOut", this);
- this.itemArrowToEvent = new YAHOO.util.CustomEvent("itemArrowTo", this);
- this.itemArrowFromEvent = new YAHOO.util.CustomEvent("itemArrowFrom", this);
- this.itemSelectEvent = new YAHOO.util.CustomEvent("itemSelect", this);
- this.unmatchedItemSelectEvent = new YAHOO.util.CustomEvent("unmatchedItemSelect", this);
- this.selectionEnforceEvent = new YAHOO.util.CustomEvent("selectionEnforce", this);
- this.containerCollapseEvent = new YAHOO.util.CustomEvent("containerCollapse", this);
- this.textboxBlurEvent = new YAHOO.util.CustomEvent("textboxBlur", this);
-
- // Finish up
- oTextbox.setAttribute("autocomplete","off");
- YAHOO.widget.AutoComplete._nIndex++;
- YAHOO.log("AutoComplete initialized","info",this.toString());
- }
- // Required arguments were not found
- else {
- YAHOO.log("Could not instantiate AutoComplete due invalid arguments", "error", this.toString());
- }
-};
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// Public member variables
-//
-/////////////////////////////////////////////////////////////////////////////
-
-/**
- * The DataSource object that encapsulates the data used for auto completion.
- * This object should be an inherited object from YAHOO.widget.DataSource.
- *
- * @property dataSource
- * @type Object
- */
-YAHOO.widget.AutoComplete.prototype.dataSource = null;
-
-/**
- * Number of characters that must be entered before querying for results. A negative value
- * effectively turns off the widget. A value of 0 allows queries of null or empty string
- * values.
- *
- * @property minQueryLength
- * @type Number
- * @default 1
- */
-YAHOO.widget.AutoComplete.prototype.minQueryLength = 1;
-
-/**
- * Maximum number of results to display in results container.
- *
- * @property maxResultsDisplayed
- * @type Number
- * @default 10
- */
-YAHOO.widget.AutoComplete.prototype.maxResultsDisplayed = 10;
-
-/**
- * Number of seconds to delay before submitting a query request. If a query
- * request is received before a previous one has completed its delay, the
- * previous request is cancelled and the new request is set to the delay.
- *
- * @property queryDelay
- * @type Number
- * @default 0.5
- */
-YAHOO.widget.AutoComplete.prototype.queryDelay = 0.5;
-
-/**
- * Class name of a highlighted item within results container.
- *
- * @property highlighClassName
- * @type String
- * @default "yui-ac-highlight"
- */
-YAHOO.widget.AutoComplete.prototype.highlightClassName = "yui-ac-highlight";
-
-/**
- * Class name of a pre-highlighted item within results container.
- *
- * @property prehighlightClassName
- * @type String
- */
-YAHOO.widget.AutoComplete.prototype.prehighlightClassName = null;
-
-/**
- * Query delimiter. A single character separator for multiple delimited
- * selections. Multiple delimiter characteres may be defined as an array of
- * strings. A null value or empty string indicates that query results cannot
- * be delimited. This feature is not recommended if you need forceSelection to
- * be true.
- *
- * @property delimChar
- * @type String | String[]
- */
-YAHOO.widget.AutoComplete.prototype.delimChar = null;
-
-/**
- * Whether or not the first item in results container should be automatically highlighted
- * on expand.
- *
- * @property autoHighlight
- * @type Boolean
- * @default true
- */
-YAHOO.widget.AutoComplete.prototype.autoHighlight = true;
-
-/**
- * Whether or not the input field should be automatically updated
- * with the first query result as the user types, auto-selecting the substring
- * that the user has not typed.
- *
- * @property typeAhead
- * @type Boolean
- * @default false
- */
-YAHOO.widget.AutoComplete.prototype.typeAhead = false;
-
-/**
- * Whether or not to animate the expansion/collapse of the results container in the
- * horizontal direction.
- *
- * @property animHoriz
- * @type Boolean
- * @default false
- */
-YAHOO.widget.AutoComplete.prototype.animHoriz = false;
-
-/**
- * Whether or not to animate the expansion/collapse of the results container in the
- * vertical direction.
- *
- * @property animVert
- * @type Boolean
- * @default true
- */
-YAHOO.widget.AutoComplete.prototype.animVert = true;
-
-/**
- * Speed of container expand/collapse animation, in seconds..
- *
- * @property animSpeed
- * @type Number
- * @default 0.3
- */
-YAHOO.widget.AutoComplete.prototype.animSpeed = 0.3;
-
-/**
- * Whether or not to force the user's selection to match one of the query
- * results. Enabling this feature essentially transforms the input field into a
- * <select> field. This feature is not recommended with delimiter character(s)
- * defined.
- *
- * @property forceSelection
- * @type Boolean
- * @default false
- */
-YAHOO.widget.AutoComplete.prototype.forceSelection = false;
-
-/**
- * Whether or not to allow browsers to cache user-typed input in the input
- * field. Disabling this feature will prevent the widget from setting the
- * autocomplete="off" on the input field. When autocomplete="off"
- * and users click the back button after form submission, user-typed input can
- * be prefilled by the browser from its cache. This caching of user input may
- * not be desired for sensitive data, such as credit card numbers, in which
- * case, implementers should consider setting allowBrowserAutocomplete to false.
- *
- * @property allowBrowserAutocomplete
- * @type Boolean
- * @default true
- */
-YAHOO.widget.AutoComplete.prototype.allowBrowserAutocomplete = true;
-
-/**
- * Whether or not the results container should always be displayed.
- * Enabling this feature displays the container when the widget is instantiated
- * and prevents the toggling of the container to a collapsed state.
- *
- * @property alwaysShowContainer
- * @type Boolean
- * @default false
- */
-YAHOO.widget.AutoComplete.prototype.alwaysShowContainer = false;
-
-/**
- * Whether or not to use an iFrame to layer over Windows form elements in
- * IE. Set to true only when the results container will be on top of a
- * <select> field in IE and thus exposed to the IE z-index bug (i.e.,
- * 5.5 < IE < 7).
- *
- * @property useIFrame
- * @type Boolean
- * @default false
- */
-YAHOO.widget.AutoComplete.prototype.useIFrame = false;
-
-/**
- * Whether or not the results container should have a shadow.
- *
- * @property useShadow
- * @type Boolean
- * @default false
- */
-YAHOO.widget.AutoComplete.prototype.useShadow = false;
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// Public methods
-//
-/////////////////////////////////////////////////////////////////////////////
-
- /**
- * Public accessor to the unique name of the AutoComplete instance.
- *
- * @method toString
- * @return {String} Unique name of the AutoComplete instance.
- */
-YAHOO.widget.AutoComplete.prototype.toString = function() {
- return "AutoComplete " + this._sName;
-};
-
- /**
- * Returns true if container is in an expanded state, false otherwise.
- *
- * @method isContainerOpen
- * @return {Boolean} Returns true if container is in an expanded state, false otherwise.
- */
-YAHOO.widget.AutoComplete.prototype.isContainerOpen = function() {
- return this._bContainerOpen;
-};
-
-/**
- * Public accessor to the internal array of DOM <li> elements that
- * display query results within the results container.
- *
- * @method getListItems
- * @return {HTMLElement[]} Array of <li> elements within the results container.
- */
-YAHOO.widget.AutoComplete.prototype.getListItems = function() {
- return this._aListItems;
-};
-
-/**
- * Public accessor to the data held in an <li> element of the
- * results container.
- *
- * @method getListItemData
- * @return {Object | Array} Object or array of result data or null
- */
-YAHOO.widget.AutoComplete.prototype.getListItemData = function(oListItem) {
- if(oListItem._oResultData) {
- return oListItem._oResultData;
- }
- else {
- return false;
- }
-};
-
-/**
- * Sets HTML markup for the results container header. This markup will be
- * inserted within a <div> tag with a class of "ac_hd".
- *
- * @method setHeader
- * @param sHeader {String} HTML markup for results container header.
- */
-YAHOO.widget.AutoComplete.prototype.setHeader = function(sHeader) {
- if(sHeader) {
- if(this._oContainer._oContent._oHeader) {
- this._oContainer._oContent._oHeader.innerHTML = sHeader;
- this._oContainer._oContent._oHeader.style.display = "block";
- }
- }
- else {
- this._oContainer._oContent._oHeader.innerHTML = "";
- this._oContainer._oContent._oHeader.style.display = "none";
- }
-};
-
-/**
- * Sets HTML markup for the results container footer. This markup will be
- * inserted within a <div> tag with a class of "ac_ft".
- *
- * @method setFooter
- * @param sFooter {String} HTML markup for results container footer.
- */
-YAHOO.widget.AutoComplete.prototype.setFooter = function(sFooter) {
- if(sFooter) {
- if(this._oContainer._oContent._oFooter) {
- this._oContainer._oContent._oFooter.innerHTML = sFooter;
- this._oContainer._oContent._oFooter.style.display = "block";
- }
- }
- else {
- this._oContainer._oContent._oFooter.innerHTML = "";
- this._oContainer._oContent._oFooter.style.display = "none";
- }
-};
-
-/**
- * Sets HTML markup for the results container body. This markup will be
- * inserted within a <div> tag with a class of "ac_bd".
- *
- * @method setBody
- * @param sHeader {String} HTML markup for results container body.
- */
-YAHOO.widget.AutoComplete.prototype.setBody = function(sBody) {
- if(sBody) {
- if(this._oContainer._oContent._oBody) {
- this._oContainer._oContent._oBody.innerHTML = sBody;
- this._oContainer._oContent._oBody.style.display = "block";
- this._oContainer._oContent.style.display = "block";
- }
- }
- else {
- this._oContainer._oContent._oBody.innerHTML = "";
- this._oContainer._oContent.style.display = "none";
- }
- this._maxResultsDisplayed = 0;
-};
-
-/**
- * Overridable method that converts a result item object into HTML markup
- * for display. Return data values are accessible via the oResultItem object,
- * and the key return value will always be oResultItem[0]. Markup will be
- * displayed within <li> element tags in the container.
- *
- * @method formatResult
- * @param oResultItem {Object} Result item representing one query result. Data is held in an array.
- * @param sQuery {String} The current query string.
- * @return {String} HTML markup of formatted result data.
- */
-YAHOO.widget.AutoComplete.prototype.formatResult = function(oResultItem, sQuery) {
- var sResult = oResultItem[0];
- if(sResult) {
- return sResult;
- }
- else {
- return "";
- }
-};
-
-/**
- * Overridable method called before container expands allows implementers to access data
- * and DOM elements.
- *
- * @method doBeforeExpandContainer
- * @return {Boolean} Return true to continue expanding container, false to cancel the expand.
- */
-YAHOO.widget.AutoComplete.prototype.doBeforeExpandContainer = function(oResultItem, sQuery) {
- return true;
-};
-
-/**
- * Makes query request to the DataSource.
- *
- * @method sendQuery
- * @param sQuery {String} Query string.
- */
-YAHOO.widget.AutoComplete.prototype.sendQuery = function(sQuery) {
- this._sendQuery(sQuery);
-};
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// Public events
-//
-/////////////////////////////////////////////////////////////////////////////
-
-/**
- * Fired when the input field receives focus.
- *
- * @event textboxFocusEvent
- * @param oSelf {Object} The AutoComplete instance.
- */
-YAHOO.widget.AutoComplete.prototype.textboxFocusEvent = null;
-
-/**
- * Fired when the input field receives key input.
- *
- * @event textboxKeyEvent
- * @param oSelf {Object} The AutoComplete instance.
- * @param nKeycode {Number} The keycode number.
- */
-YAHOO.widget.AutoComplete.prototype.textboxKeyEvent = null;
-
-/**
- * Fired when the AutoComplete instance makes a query to the DataSource.
- *
- * @event dataRequestEvent
- * @param oSelf {Object} The AutoComplete instance.
- * @param sQuery {String} The query string.
- */
-YAHOO.widget.AutoComplete.prototype.dataRequestEvent = null;
-
-/**
- * Fired when the AutoComplete instance receives query results from the data
- * source.
- *
- * @event dataReturnEvent
- * @param oSelf {Object} The AutoComplete instance.
- * @param sQuery {String} The query string.
- * @param aResults {Array} Results array.
- */
-YAHOO.widget.AutoComplete.prototype.dataReturnEvent = null;
-
-/**
- * Fired when the AutoComplete instance does not receive query results from the
- * DataSource due to an error.
- *
- * @event dataErrorEvent
- * @param oSelf {Object} The AutoComplete instance.
- * @param sQuery {String} The query string.
- */
-YAHOO.widget.AutoComplete.prototype.dataErrorEvent = null;
-
-/**
- * Fired when the results container is expanded.
- *
- * @event containerExpandEvent
- * @param oSelf {Object} The AutoComplete instance.
- */
-YAHOO.widget.AutoComplete.prototype.containerExpandEvent = null;
-
-/**
- * Fired when the input field has been prefilled by the type-ahead
- * feature.
- *
- * @event typeAheadEvent
- * @param oSelf {Object} The AutoComplete instance.
- * @param sQuery {String} The query string.
- * @param sPrefill {String} The prefill string.
- */
-YAHOO.widget.AutoComplete.prototype.typeAheadEvent = null;
-
-/**
- * Fired when result item has been moused over.
- *
- * @event itemMouseOverEvent
- * @param oSelf {Object} The AutoComplete instance.
- * @param elItem {HTMLElement} The <li> element item moused to.
- */
-YAHOO.widget.AutoComplete.prototype.itemMouseOverEvent = null;
-
-/**
- * Fired when result item has been moused out.
- *
- * @event itemMouseOutEvent
- * @param oSelf {Object} The AutoComplete instance.
- * @param elItem {HTMLElement} The <li> element item moused from.
- */
-YAHOO.widget.AutoComplete.prototype.itemMouseOutEvent = null;
-
-/**
- * Fired when result item has been arrowed to.
- *
- * @event itemArrowToEvent
- * @param oSelf {Object} The AutoComplete instance.
- * @param elItem {HTMLElement} The <li> element item arrowed to.
- */
-YAHOO.widget.AutoComplete.prototype.itemArrowToEvent = null;
-
-/**
- * Fired when result item has been arrowed away from.
- *
- * @event itemArrowFromEvent
- * @param oSelf {Object} The AutoComplete instance.
- * @param elItem {HTMLElement} The <li> element item arrowed from.
- */
-YAHOO.widget.AutoComplete.prototype.itemArrowFromEvent = null;
-
-/**
- * Fired when an item is selected via mouse click, ENTER key, or TAB key.
- *
- * @event itemSelectEvent
- * @param oSelf {Object} The AutoComplete instance.
- * @param elItem {HTMLElement} The selected <li> element item.
- * @param oData {Object} The data returned for the item, either as an object,
- * or mapped from the schema into an array.
- */
-YAHOO.widget.AutoComplete.prototype.itemSelectEvent = null;
-
-/**
- * Fired when a user selection does not match any of the displayed result items.
- * Note that this event may not behave as expected when delimiter characters
- * have been defined.
- *
- * @event unmatchedItemSelectEvent
- * @param oSelf {Object} The AutoComplete instance.
- * @param sQuery {String} The user-typed query string.
- */
-YAHOO.widget.AutoComplete.prototype.unmatchedItemSelectEvent = null;
-
-/**
- * Fired if forceSelection is enabled and the user's input has been cleared
- * because it did not match one of the returned query results.
- *
- * @event selectionEnforceEvent
- * @param oSelf {Object} The AutoComplete instance.
- */
-YAHOO.widget.AutoComplete.prototype.selectionEnforceEvent = null;
-
-/**
- * Fired when the results container is collapsed.
- *
- * @event containerCollapseEvent
- * @param oSelf {Object} The AutoComplete instance.
- */
-YAHOO.widget.AutoComplete.prototype.containerCollapseEvent = null;
-
-/**
- * Fired when the input field loses focus.
- *
- * @event textboxBlurEvent
- * @param oSelf {Object} The AutoComplete instance.
- */
-YAHOO.widget.AutoComplete.prototype.textboxBlurEvent = null;
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// Private member variables
-//
-/////////////////////////////////////////////////////////////////////////////
-
-/**
- * Internal class variable to index multiple AutoComplete instances.
- *
- * @property _nIndex
- * @type Number
- * @default 0
- * @private
- */
-YAHOO.widget.AutoComplete._nIndex = 0;
-
-/**
- * Name of AutoComplete instance.
- *
- * @property _sName
- * @type String
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._sName = null;
-
-/**
- * Text input field DOM element.
- *
- * @property _oTextbox
- * @type HTMLElement
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._oTextbox = null;
-
-/**
- * Whether or not the input field is currently in focus. If query results come back
- * but the user has already moved on, do not proceed with auto complete behavior.
- *
- * @property _bFocused
- * @type Boolean
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._bFocused = true;
-
-/**
- * Animation instance for container expand/collapse.
- *
- * @property _oAnim
- * @type Boolean
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._oAnim = null;
-
-/**
- * Container DOM element.
- *
- * @property _oContainer
- * @type HTMLElement
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._oContainer = null;
-
-/**
- * Whether or not the results container is currently open.
- *
- * @property _bContainerOpen
- * @type Boolean
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._bContainerOpen = false;
-
-/**
- * Whether or not the mouse is currently over the results
- * container. This is necessary in order to prevent clicks on container items
- * from being text input field blur events.
- *
- * @property _bOverContainer
- * @type Boolean
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._bOverContainer = false;
-
-/**
- * Array of <li> elements references that contain query results within the
- * results container.
- *
- * @property _aListItems
- * @type Array
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._aListItems = null;
-
-/**
- * Number of <li> elements currently displayed in results container.
- *
- * @property _nDisplayedItems
- * @type Number
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._nDisplayedItems = 0;
-
-/**
- * Internal count of <li> elements displayed and hidden in results container.
- *
- * @property _maxResultsDisplayed
- * @type Number
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._maxResultsDisplayed = 0;
-
-/**
- * Current query string
- *
- * @property _sCurQuery
- * @type String
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._sCurQuery = null;
-
-/**
- * Past queries this session (for saving delimited queries).
- *
- * @property _sSavedQuery
- * @type String
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._sSavedQuery = null;
-
-/**
- * Pointer to the currently highlighted <li> element in the container.
- *
- * @property _oCurItem
- * @type HTMLElement
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._oCurItem = null;
-
-/**
- * Whether or not an item has been selected since the container was populated
- * with results. Reset to false by _populateList, and set to true when item is
- * selected.
- *
- * @property _bItemSelected
- * @type Boolean
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._bItemSelected = false;
-
-/**
- * Key code of the last key pressed in textbox.
- *
- * @property _nKeyCode
- * @type Number
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._nKeyCode = null;
-
-/**
- * Delay timeout ID.
- *
- * @property _nDelayID
- * @type Number
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._nDelayID = -1;
-
-/**
- * Src to iFrame used when useIFrame = true. Supports implementations over SSL
- * as well.
- *
- * @property _iFrameSrc
- * @type String
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._iFrameSrc = "javascript:false;";
-
-/**
- * For users typing via certain IMEs, queries must be triggered by intervals,
- * since key events yet supported across all browsers for all IMEs.
- *
- * @property _queryInterval
- * @type Object
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._queryInterval = null;
-
-/**
- * Internal tracker to last known textbox value, used to determine whether or not
- * to trigger a query via interval for certain IME users.
- *
- * @event _sLastTextboxValue
- * @type String
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._sLastTextboxValue = null;
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// Private methods
-//
-/////////////////////////////////////////////////////////////////////////////
-
-/**
- * Updates and validates latest public config properties.
- *
- * @method __initProps
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._initProps = function() {
- // Correct any invalid values
- var minQueryLength = this.minQueryLength;
- if(isNaN(minQueryLength) || (minQueryLength < 1)) {
- minQueryLength = 1;
- }
- var maxResultsDisplayed = this.maxResultsDisplayed;
- if(isNaN(this.maxResultsDisplayed) || (this.maxResultsDisplayed < 1)) {
- this.maxResultsDisplayed = 10;
- }
- var queryDelay = this.queryDelay;
- if(isNaN(this.queryDelay) || (this.queryDelay < 0)) {
- this.queryDelay = 0.5;
- }
- var aDelimChar = (this.delimChar) ? this.delimChar : null;
- if(aDelimChar) {
- if(typeof aDelimChar == "string") {
- this.delimChar = [aDelimChar];
- }
- else if(aDelimChar.constructor != Array) {
- this.delimChar = null;
- }
- }
- var animSpeed = this.animSpeed;
- if((this.animHoriz || this.animVert) && YAHOO.util.Anim) {
- if(isNaN(animSpeed) || (animSpeed < 0)) {
- animSpeed = 0.3;
- }
- if(!this._oAnim ) {
- oAnim = new YAHOO.util.Anim(this._oContainer._oContent, {}, this.animSpeed);
- this._oAnim = oAnim;
- }
- else {
- this._oAnim.duration = animSpeed;
- }
- }
- if(this.forceSelection && this.delimChar) {
- YAHOO.log("The forceSelection feature has been enabled with delimChar defined.","warn", this.toString());
- }
-};
-
-/**
- * Initializes the results container helpers if they are enabled and do
- * not exist
- *
- * @method _initContainerHelpers
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._initContainerHelpers = function() {
- if(this.useShadow && !this._oContainer._oShadow) {
- var oShadow = document.createElement("div");
- oShadow.className = "yui-ac-shadow";
- this._oContainer._oShadow = this._oContainer.appendChild(oShadow);
- }
- if(this.useIFrame && !this._oContainer._oIFrame) {
- var oIFrame = document.createElement("iframe");
- oIFrame.src = this._iFrameSrc;
- oIFrame.frameBorder = 0;
- oIFrame.scrolling = "no";
- oIFrame.style.position = "absolute";
- oIFrame.style.width = "100%";
- oIFrame.style.height = "100%";
- oIFrame.tabIndex = -1;
- this._oContainer._oIFrame = this._oContainer.appendChild(oIFrame);
- }
-};
-
-/**
- * Initializes the results container once at object creation
- *
- * @method _initContainer
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._initContainer = function() {
- if(!this._oContainer._oContent) {
- // The oContent div helps size the iframe and shadow properly
- var oContent = document.createElement("div");
- oContent.className = "yui-ac-content";
- oContent.style.display = "none";
- this._oContainer._oContent = this._oContainer.appendChild(oContent);
-
- var oHeader = document.createElement("div");
- oHeader.className = "yui-ac-hd";
- oHeader.style.display = "none";
- this._oContainer._oContent._oHeader = this._oContainer._oContent.appendChild(oHeader);
-
- var oBody = document.createElement("div");
- oBody.className = "yui-ac-bd";
- this._oContainer._oContent._oBody = this._oContainer._oContent.appendChild(oBody);
-
- var oFooter = document.createElement("div");
- oFooter.className = "yui-ac-ft";
- oFooter.style.display = "none";
- this._oContainer._oContent._oFooter = this._oContainer._oContent.appendChild(oFooter);
- }
- else {
- YAHOO.log("Could not initialize the container","warn",this.toString());
- }
-};
-
-/**
- * Clears out contents of container body and creates up to
- * YAHOO.widget.AutoComplete#maxResultsDisplayed <li> elements in an
- * <ul> element.
- *
- * @method _initList
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._initList = function() {
- this._aListItems = [];
- while(this._oContainer._oContent._oBody.hasChildNodes()) {
- var oldListItems = this.getListItems();
- if(oldListItems) {
- for(var oldi = oldListItems.length-1; oldi >= 0; i--) {
- oldListItems[oldi] = null;
- }
- }
- this._oContainer._oContent._oBody.innerHTML = "";
- }
-
- var oList = document.createElement("ul");
- oList = this._oContainer._oContent._oBody.appendChild(oList);
- for(var i=0; i
+ *
+ *
+ * @class AutoComplete
+ * @constructor
+ * @param elInput {HTMLElement} DOM element reference of an input field.
+ * @param elInput {String} String ID of an input field.
+ * @param elContainer {HTMLElement} DOM element reference of an existing DIV.
+ * @param elContainer {String} String ID of an existing DIV.
+ * @param oDataSource {YAHOO.widget.DataSource} DataSource instance.
+ * @param oConfigs {Object} (optional) Object literal of configuration params.
+ */
+YAHOO.widget.AutoComplete = function(elInput,elContainer,oDataSource,oConfigs) {
+ if(elInput && elContainer && oDataSource) {
+ // Validate DataSource
+ if(oDataSource instanceof YAHOO.widget.DataSource) {
+ this.dataSource = oDataSource;
+ }
+ else {
+ YAHOO.log("Could not instantiate AutoComplete due to an invalid DataSource", "error", this.toString());
+ return;
+ }
+
+ // Validate input element
+ if(YAHOO.util.Dom.inDocument(elInput)) {
+ if(YAHOO.lang.isString(elInput)) {
+ this._sName = "instance" + YAHOO.widget.AutoComplete._nIndex + " " + elInput;
+ this._oTextbox = document.getElementById(elInput);
+ }
+ else {
+ this._sName = (elInput.id) ?
+ "instance" + YAHOO.widget.AutoComplete._nIndex + " " + elInput.id:
+ "instance" + YAHOO.widget.AutoComplete._nIndex;
+ this._oTextbox = elInput;
+ }
+ YAHOO.util.Dom.addClass(this._oTextbox, "yui-ac-input");
+ }
+ else {
+ YAHOO.log("Could not instantiate AutoComplete due to an invalid input element", "error", this.toString());
+ return;
+ }
+
+ // Validate container element
+ if(YAHOO.util.Dom.inDocument(elContainer)) {
+ if(YAHOO.lang.isString(elContainer)) {
+ this._oContainer = document.getElementById(elContainer);
+ }
+ else {
+ this._oContainer = elContainer;
+ }
+ if(this._oContainer.style.display == "none") {
+ YAHOO.log("The container may not display properly if display is set to \"none\" in CSS", "warn", this.toString());
+ }
+
+ // For skinning
+ var elParent = this._oContainer.parentNode;
+ var elTag = elParent.tagName.toLowerCase();
+ while(elParent && (elParent != "document")) {
+ if(elTag == "div") {
+ YAHOO.util.Dom.addClass(elParent, "yui-ac");
+ break;
+ }
+ else {
+ elParent = elParent.parentNode;
+ elTag = elParent.tagName.toLowerCase();
+ }
+ }
+ if(elTag != "div") {
+ YAHOO.log("Could not find an appropriate parent container for skinning", "warn", this.toString());
+ }
+ }
+ else {
+ YAHOO.log("Could not instantiate AutoComplete due to an invalid container element", "error", this.toString());
+ return;
+ }
+
+ // Set any config params passed in to override defaults
+ if(oConfigs && (oConfigs.constructor == Object)) {
+ for(var sConfig in oConfigs) {
+ if(sConfig) {
+ this[sConfig] = oConfigs[sConfig];
+ }
+ }
+ }
+
+ // Initialization sequence
+ this._initContainer();
+ this._initProps();
+ this._initList();
+ this._initContainerHelpers();
+
+ // Set up events
+ var oSelf = this;
+ var oTextbox = this._oTextbox;
+ // Events are actually for the content module within the container
+ var oContent = this._oContainer._oContent;
+
+ // Dom events
+ YAHOO.util.Event.addListener(oTextbox,"keyup",oSelf._onTextboxKeyUp,oSelf);
+ YAHOO.util.Event.addListener(oTextbox,"keydown",oSelf._onTextboxKeyDown,oSelf);
+ YAHOO.util.Event.addListener(oTextbox,"focus",oSelf._onTextboxFocus,oSelf);
+ YAHOO.util.Event.addListener(oTextbox,"blur",oSelf._onTextboxBlur,oSelf);
+ YAHOO.util.Event.addListener(oContent,"mouseover",oSelf._onContainerMouseover,oSelf);
+ YAHOO.util.Event.addListener(oContent,"mouseout",oSelf._onContainerMouseout,oSelf);
+ YAHOO.util.Event.addListener(oContent,"scroll",oSelf._onContainerScroll,oSelf);
+ YAHOO.util.Event.addListener(oContent,"resize",oSelf._onContainerResize,oSelf);
+ if(oTextbox.form) {
+ YAHOO.util.Event.addListener(oTextbox.form,"submit",oSelf._onFormSubmit,oSelf);
+ }
+ YAHOO.util.Event.addListener(oTextbox,"keypress",oSelf._onTextboxKeyPress,oSelf);
+
+ // Custom events
+ this.textboxFocusEvent = new YAHOO.util.CustomEvent("textboxFocus", this);
+ this.textboxKeyEvent = new YAHOO.util.CustomEvent("textboxKey", this);
+ this.dataRequestEvent = new YAHOO.util.CustomEvent("dataRequest", this);
+ this.dataReturnEvent = new YAHOO.util.CustomEvent("dataReturn", this);
+ this.dataErrorEvent = new YAHOO.util.CustomEvent("dataError", this);
+ this.containerExpandEvent = new YAHOO.util.CustomEvent("containerExpand", this);
+ this.typeAheadEvent = new YAHOO.util.CustomEvent("typeAhead", this);
+ this.itemMouseOverEvent = new YAHOO.util.CustomEvent("itemMouseOver", this);
+ this.itemMouseOutEvent = new YAHOO.util.CustomEvent("itemMouseOut", this);
+ this.itemArrowToEvent = new YAHOO.util.CustomEvent("itemArrowTo", this);
+ this.itemArrowFromEvent = new YAHOO.util.CustomEvent("itemArrowFrom", this);
+ this.itemSelectEvent = new YAHOO.util.CustomEvent("itemSelect", this);
+ this.unmatchedItemSelectEvent = new YAHOO.util.CustomEvent("unmatchedItemSelect", this);
+ this.selectionEnforceEvent = new YAHOO.util.CustomEvent("selectionEnforce", this);
+ this.containerCollapseEvent = new YAHOO.util.CustomEvent("containerCollapse", this);
+ this.textboxBlurEvent = new YAHOO.util.CustomEvent("textboxBlur", this);
+
+ // Finish up
+ oTextbox.setAttribute("autocomplete","off");
+ YAHOO.widget.AutoComplete._nIndex++;
+ YAHOO.log("AutoComplete initialized","info",this.toString());
+ }
+ // Required arguments were not found
+ else {
+ YAHOO.log("Could not instantiate AutoComplete due invalid arguments", "error", this.toString());
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Public member variables
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * The DataSource object that encapsulates the data used for auto completion.
+ * This object should be an inherited object from YAHOO.widget.DataSource.
+ *
+ * @property dataSource
+ * @type YAHOO.widget.DataSource
+ */
+YAHOO.widget.AutoComplete.prototype.dataSource = null;
+
+/**
+ * Number of characters that must be entered before querying for results. A negative value
+ * effectively turns off the widget. A value of 0 allows queries of null or empty string
+ * values.
+ *
+ * @property minQueryLength
+ * @type Number
+ * @default 1
+ */
+YAHOO.widget.AutoComplete.prototype.minQueryLength = 1;
+
+/**
+ * Maximum number of results to display in results container.
+ *
+ * @property maxResultsDisplayed
+ * @type Number
+ * @default 10
+ */
+YAHOO.widget.AutoComplete.prototype.maxResultsDisplayed = 10;
+
+/**
+ * Number of seconds to delay before submitting a query request. If a query
+ * request is received before a previous one has completed its delay, the
+ * previous request is cancelled and the new request is set to the delay.
+ * Implementers should take care when setting this value very low (i.e., less
+ * than 0.2) with low latency DataSources and the typeAhead feature enabled, as
+ * fast typers may see unexpected behavior.
+ *
+ * @property queryDelay
+ * @type Number
+ * @default 0.2
+ */
+YAHOO.widget.AutoComplete.prototype.queryDelay = 0.2;
+
+/**
+ * Class name of a highlighted item within results container.
+ *
+ * @property highlightClassName
+ * @type String
+ * @default "yui-ac-highlight"
+ */
+YAHOO.widget.AutoComplete.prototype.highlightClassName = "yui-ac-highlight";
+
+/**
+ * Class name of a pre-highlighted item within results container.
+ *
+ * @property prehighlightClassName
+ * @type String
+ */
+YAHOO.widget.AutoComplete.prototype.prehighlightClassName = null;
+
+/**
+ * Query delimiter. A single character separator for multiple delimited
+ * selections. Multiple delimiter characteres may be defined as an array of
+ * strings. A null value or empty string indicates that query results cannot
+ * be delimited. This feature is not recommended if you need forceSelection to
+ * be true.
+ *
+ * @property delimChar
+ * @type String | String[]
+ */
+YAHOO.widget.AutoComplete.prototype.delimChar = null;
+
+/**
+ * Whether or not the first item in results container should be automatically highlighted
+ * on expand.
+ *
+ * @property autoHighlight
+ * @type Boolean
+ * @default true
+ */
+YAHOO.widget.AutoComplete.prototype.autoHighlight = true;
+
+/**
+ * Whether or not the input field should be automatically updated
+ * with the first query result as the user types, auto-selecting the substring
+ * that the user has not typed.
+ *
+ * @property typeAhead
+ * @type Boolean
+ * @default false
+ */
+YAHOO.widget.AutoComplete.prototype.typeAhead = false;
+
+/**
+ * Whether or not to animate the expansion/collapse of the results container in the
+ * horizontal direction.
+ *
+ * @property animHoriz
+ * @type Boolean
+ * @default false
+ */
+YAHOO.widget.AutoComplete.prototype.animHoriz = false;
+
+/**
+ * Whether or not to animate the expansion/collapse of the results container in the
+ * vertical direction.
+ *
+ * @property animVert
+ * @type Boolean
+ * @default true
+ */
+YAHOO.widget.AutoComplete.prototype.animVert = true;
+
+/**
+ * Speed of container expand/collapse animation, in seconds..
+ *
+ * @property animSpeed
+ * @type Number
+ * @default 0.3
+ */
+YAHOO.widget.AutoComplete.prototype.animSpeed = 0.3;
+
+/**
+ * Whether or not to force the user's selection to match one of the query
+ * results. Enabling this feature essentially transforms the input field into a
+ * <select> field. This feature is not recommended with delimiter character(s)
+ * defined.
+ *
+ * @property forceSelection
+ * @type Boolean
+ * @default false
+ */
+YAHOO.widget.AutoComplete.prototype.forceSelection = false;
+
+/**
+ * Whether or not to allow browsers to cache user-typed input in the input
+ * field. Disabling this feature will prevent the widget from setting the
+ * autocomplete="off" on the input field. When autocomplete="off"
+ * and users click the back button after form submission, user-typed input can
+ * be prefilled by the browser from its cache. This caching of user input may
+ * not be desired for sensitive data, such as credit card numbers, in which
+ * case, implementers should consider setting allowBrowserAutocomplete to false.
+ *
+ * @property allowBrowserAutocomplete
+ * @type Boolean
+ * @default true
+ */
+YAHOO.widget.AutoComplete.prototype.allowBrowserAutocomplete = true;
+
+/**
+ * Whether or not the results container should always be displayed.
+ * Enabling this feature displays the container when the widget is instantiated
+ * and prevents the toggling of the container to a collapsed state.
+ *
+ * @property alwaysShowContainer
+ * @type Boolean
+ * @default false
+ */
+YAHOO.widget.AutoComplete.prototype.alwaysShowContainer = false;
+
+/**
+ * Whether or not to use an iFrame to layer over Windows form elements in
+ * IE. Set to true only when the results container will be on top of a
+ * <select> field in IE and thus exposed to the IE z-index bug (i.e.,
+ * 5.5 < IE < 7).
+ *
+ * @property useIFrame
+ * @type Boolean
+ * @default false
+ */
+YAHOO.widget.AutoComplete.prototype.useIFrame = false;
+
+/**
+ * Whether or not the results container should have a shadow.
+ *
+ * @property useShadow
+ * @type Boolean
+ * @default false
+ */
+YAHOO.widget.AutoComplete.prototype.useShadow = false;
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Public methods
+//
+/////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Public accessor to the unique name of the AutoComplete instance.
+ *
+ * @method toString
+ * @return {String} Unique name of the AutoComplete instance.
+ */
+YAHOO.widget.AutoComplete.prototype.toString = function() {
+ return "AutoComplete " + this._sName;
+};
+
+ /**
+ * Returns true if container is in an expanded state, false otherwise.
+ *
+ * @method isContainerOpen
+ * @return {Boolean} Returns true if container is in an expanded state, false otherwise.
+ */
+YAHOO.widget.AutoComplete.prototype.isContainerOpen = function() {
+ return this._bContainerOpen;
+};
+
+/**
+ * Public accessor to the internal array of DOM <li> elements that
+ * display query results within the results container.
+ *
+ * @method getListItems
+ * @return {HTMLElement[]} Array of <li> elements within the results container.
+ */
+YAHOO.widget.AutoComplete.prototype.getListItems = function() {
+ return this._aListItems;
+};
+
+/**
+ * Public accessor to the data held in an <li> element of the
+ * results container.
+ *
+ * @method getListItemData
+ * @return {Object | Object[]} Object or array of result data or null
+ */
+YAHOO.widget.AutoComplete.prototype.getListItemData = function(oListItem) {
+ if(oListItem._oResultData) {
+ return oListItem._oResultData;
+ }
+ else {
+ return false;
+ }
+};
+
+/**
+ * Sets HTML markup for the results container header. This markup will be
+ * inserted within a <div> tag with a class of "yui-ac-hd".
+ *
+ * @method setHeader
+ * @param sHeader {String} HTML markup for results container header.
+ */
+YAHOO.widget.AutoComplete.prototype.setHeader = function(sHeader) {
+ if(sHeader) {
+ if(this._oContainer._oContent._oHeader) {
+ this._oContainer._oContent._oHeader.innerHTML = sHeader;
+ this._oContainer._oContent._oHeader.style.display = "block";
+ }
+ }
+ else {
+ this._oContainer._oContent._oHeader.innerHTML = "";
+ this._oContainer._oContent._oHeader.style.display = "none";
+ }
+};
+
+/**
+ * Sets HTML markup for the results container footer. This markup will be
+ * inserted within a <div> tag with a class of "yui-ac-ft".
+ *
+ * @method setFooter
+ * @param sFooter {String} HTML markup for results container footer.
+ */
+YAHOO.widget.AutoComplete.prototype.setFooter = function(sFooter) {
+ if(sFooter) {
+ if(this._oContainer._oContent._oFooter) {
+ this._oContainer._oContent._oFooter.innerHTML = sFooter;
+ this._oContainer._oContent._oFooter.style.display = "block";
+ }
+ }
+ else {
+ this._oContainer._oContent._oFooter.innerHTML = "";
+ this._oContainer._oContent._oFooter.style.display = "none";
+ }
+};
+
+/**
+ * Sets HTML markup for the results container body. This markup will be
+ * inserted within a <div> tag with a class of "yui-ac-bd".
+ *
+ * @method setBody
+ * @param sBody {String} HTML markup for results container body.
+ */
+YAHOO.widget.AutoComplete.prototype.setBody = function(sBody) {
+ if(sBody) {
+ if(this._oContainer._oContent._oBody) {
+ this._oContainer._oContent._oBody.innerHTML = sBody;
+ this._oContainer._oContent._oBody.style.display = "block";
+ this._oContainer._oContent.style.display = "block";
+ }
+ }
+ else {
+ this._oContainer._oContent._oBody.innerHTML = "";
+ this._oContainer._oContent.style.display = "none";
+ }
+ this._maxResultsDisplayed = 0;
+};
+
+/**
+ * Overridable method that converts a result item object into HTML markup
+ * for display. Return data values are accessible via the oResultItem object,
+ * and the key return value will always be oResultItem[0]. Markup will be
+ * displayed within <li> element tags in the container.
+ *
+ * @method formatResult
+ * @param oResultItem {Object} Result item representing one query result. Data is held in an array.
+ * @param sQuery {String} The current query string.
+ * @return {String} HTML markup of formatted result data.
+ */
+YAHOO.widget.AutoComplete.prototype.formatResult = function(oResultItem, sQuery) {
+ var sResult = oResultItem[0];
+ if(sResult) {
+ return sResult;
+ }
+ else {
+ return "";
+ }
+};
+
+/**
+ * Overridable method called before container expands allows implementers to access data
+ * and DOM elements.
+ *
+ * @method doBeforeExpandContainer
+ * @param oTextbox {HTMLElement} The text input box.
+ * @param oContainer {HTMLElement} The container element.
+ * @param sQuery {String} The query string.
+ * @param aResults {Object[]} An array of query results.
+ * @return {Boolean} Return true to continue expanding container, false to cancel the expand.
+ */
+YAHOO.widget.AutoComplete.prototype.doBeforeExpandContainer = function(oTextbox, oContainer, sQuery, aResults) {
+ return true;
+};
+
+/**
+ * Makes query request to the DataSource.
+ *
+ * @method sendQuery
+ * @param sQuery {String} Query string.
+ */
+YAHOO.widget.AutoComplete.prototype.sendQuery = function(sQuery) {
+ this._sendQuery(sQuery);
+};
+
+/**
+ * Overridable method gives implementers access to the query before it gets sent.
+ *
+ * @method doBeforeSendQuery
+ * @param sQuery {String} Query string.
+ * @return {String} Query string.
+ */
+YAHOO.widget.AutoComplete.prototype.doBeforeSendQuery = function(sQuery) {
+ return sQuery;
+};
+
+/**
+ * Nulls out the entire AutoComplete instance and related objects, removes attached
+ * event listeners, and clears out DOM elements inside the container. After
+ * calling this method, the instance reference should be expliclitly nulled by
+ * implementer, as in myDataTable = null. Use with caution!
+ *
+ * @method destroy
+ */
+YAHOO.widget.AutoComplete.prototype.destroy = function() {
+ var instanceName = this.toString();
+ var elInput = this._oTextbox;
+ var elContainer = this._oContainer;
+
+ // Unhook custom events
+ this.textboxFocusEvent.unsubscribe();
+ this.textboxKeyEvent.unsubscribe();
+ this.dataRequestEvent.unsubscribe();
+ this.dataReturnEvent.unsubscribe();
+ this.dataErrorEvent.unsubscribe();
+ this.containerExpandEvent.unsubscribe();
+ this.typeAheadEvent.unsubscribe();
+ this.itemMouseOverEvent.unsubscribe();
+ this.itemMouseOutEvent.unsubscribe();
+ this.itemArrowToEvent.unsubscribe();
+ this.itemArrowFromEvent.unsubscribe();
+ this.itemSelectEvent.unsubscribe();
+ this.unmatchedItemSelectEvent.unsubscribe();
+ this.selectionEnforceEvent.unsubscribe();
+ this.containerCollapseEvent.unsubscribe();
+ this.textboxBlurEvent.unsubscribe();
+
+ // Unhook DOM events
+ YAHOO.util.Event.purgeElement(elInput, true);
+ YAHOO.util.Event.purgeElement(elContainer, true);
+
+ // Remove DOM elements
+ elContainer.innerHTML = "";
+
+ // Null out objects
+ for(var key in this) {
+ if(this.hasOwnProperty(key)) {
+ this[key] = null;
+ }
+ }
+
+ YAHOO.log("AutoComplete instance destroyed: " + instanceName);
+};
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Public events
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Fired when the input field receives focus.
+ *
+ * @event textboxFocusEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ */
+YAHOO.widget.AutoComplete.prototype.textboxFocusEvent = null;
+
+/**
+ * Fired when the input field receives key input.
+ *
+ * @event textboxKeyEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ * @param nKeycode {Number} The keycode number.
+ */
+YAHOO.widget.AutoComplete.prototype.textboxKeyEvent = null;
+
+/**
+ * Fired when the AutoComplete instance makes a query to the DataSource.
+ *
+ * @event dataRequestEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ * @param sQuery {String} The query string.
+ */
+YAHOO.widget.AutoComplete.prototype.dataRequestEvent = null;
+
+/**
+ * Fired when the AutoComplete instance receives query results from the data
+ * source.
+ *
+ * @event dataReturnEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ * @param sQuery {String} The query string.
+ * @param aResults {Object[]} Results array.
+ */
+YAHOO.widget.AutoComplete.prototype.dataReturnEvent = null;
+
+/**
+ * Fired when the AutoComplete instance does not receive query results from the
+ * DataSource due to an error.
+ *
+ * @event dataErrorEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ * @param sQuery {String} The query string.
+ */
+YAHOO.widget.AutoComplete.prototype.dataErrorEvent = null;
+
+/**
+ * Fired when the results container is expanded.
+ *
+ * @event containerExpandEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ */
+YAHOO.widget.AutoComplete.prototype.containerExpandEvent = null;
+
+/**
+ * Fired when the input field has been prefilled by the type-ahead
+ * feature.
+ *
+ * @event typeAheadEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ * @param sQuery {String} The query string.
+ * @param sPrefill {String} The prefill string.
+ */
+YAHOO.widget.AutoComplete.prototype.typeAheadEvent = null;
+
+/**
+ * Fired when result item has been moused over.
+ *
+ * @event itemMouseOverEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ * @param elItem {HTMLElement} The <li> element item moused to.
+ */
+YAHOO.widget.AutoComplete.prototype.itemMouseOverEvent = null;
+
+/**
+ * Fired when result item has been moused out.
+ *
+ * @event itemMouseOutEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ * @param elItem {HTMLElement} The <li> element item moused from.
+ */
+YAHOO.widget.AutoComplete.prototype.itemMouseOutEvent = null;
+
+/**
+ * Fired when result item has been arrowed to.
+ *
+ * @event itemArrowToEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ * @param elItem {HTMLElement} The <li> element item arrowed to.
+ */
+YAHOO.widget.AutoComplete.prototype.itemArrowToEvent = null;
+
+/**
+ * Fired when result item has been arrowed away from.
+ *
+ * @event itemArrowFromEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ * @param elItem {HTMLElement} The <li> element item arrowed from.
+ */
+YAHOO.widget.AutoComplete.prototype.itemArrowFromEvent = null;
+
+/**
+ * Fired when an item is selected via mouse click, ENTER key, or TAB key.
+ *
+ * @event itemSelectEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ * @param elItem {HTMLElement} The selected <li> element item.
+ * @param oData {Object} The data returned for the item, either as an object,
+ * or mapped from the schema into an array.
+ */
+YAHOO.widget.AutoComplete.prototype.itemSelectEvent = null;
+
+/**
+ * Fired when a user selection does not match any of the displayed result items.
+ * Note that this event may not behave as expected when delimiter characters
+ * have been defined.
+ *
+ * @event unmatchedItemSelectEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ * @param sQuery {String} The user-typed query string.
+ */
+YAHOO.widget.AutoComplete.prototype.unmatchedItemSelectEvent = null;
+
+/**
+ * Fired if forceSelection is enabled and the user's input has been cleared
+ * because it did not match one of the returned query results.
+ *
+ * @event selectionEnforceEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ */
+YAHOO.widget.AutoComplete.prototype.selectionEnforceEvent = null;
+
+/**
+ * Fired when the results container is collapsed.
+ *
+ * @event containerCollapseEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ */
+YAHOO.widget.AutoComplete.prototype.containerCollapseEvent = null;
+
+/**
+ * Fired when the input field loses focus.
+ *
+ * @event textboxBlurEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ */
+YAHOO.widget.AutoComplete.prototype.textboxBlurEvent = null;
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Private member variables
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Internal class variable to index multiple AutoComplete instances.
+ *
+ * @property _nIndex
+ * @type Number
+ * @default 0
+ * @private
+ */
+YAHOO.widget.AutoComplete._nIndex = 0;
+
+/**
+ * Name of AutoComplete instance.
+ *
+ * @property _sName
+ * @type String
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._sName = null;
+
+/**
+ * Text input field DOM element.
+ *
+ * @property _oTextbox
+ * @type HTMLElement
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._oTextbox = null;
+
+/**
+ * Whether or not the input field is currently in focus. If query results come back
+ * but the user has already moved on, do not proceed with auto complete behavior.
+ *
+ * @property _bFocused
+ * @type Boolean
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._bFocused = true;
+
+/**
+ * Animation instance for container expand/collapse.
+ *
+ * @property _oAnim
+ * @type Boolean
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._oAnim = null;
+
+/**
+ * Container DOM element.
+ *
+ * @property _oContainer
+ * @type HTMLElement
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._oContainer = null;
+
+/**
+ * Whether or not the results container is currently open.
+ *
+ * @property _bContainerOpen
+ * @type Boolean
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._bContainerOpen = false;
+
+/**
+ * Whether or not the mouse is currently over the results
+ * container. This is necessary in order to prevent clicks on container items
+ * from being text input field blur events.
+ *
+ * @property _bOverContainer
+ * @type Boolean
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._bOverContainer = false;
+
+/**
+ * Array of <li> elements references that contain query results within the
+ * results container.
+ *
+ * @property _aListItems
+ * @type HTMLElement[]
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._aListItems = null;
+
+/**
+ * Number of <li> elements currently displayed in results container.
+ *
+ * @property _nDisplayedItems
+ * @type Number
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._nDisplayedItems = 0;
+
+/**
+ * Internal count of <li> elements displayed and hidden in results container.
+ *
+ * @property _maxResultsDisplayed
+ * @type Number
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._maxResultsDisplayed = 0;
+
+/**
+ * Current query string
+ *
+ * @property _sCurQuery
+ * @type String
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._sCurQuery = null;
+
+/**
+ * Past queries this session (for saving delimited queries).
+ *
+ * @property _sSavedQuery
+ * @type String
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._sSavedQuery = null;
+
+/**
+ * Pointer to the currently highlighted <li> element in the container.
+ *
+ * @property _oCurItem
+ * @type HTMLElement
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._oCurItem = null;
+
+/**
+ * Whether or not an item has been selected since the container was populated
+ * with results. Reset to false by _populateList, and set to true when item is
+ * selected.
+ *
+ * @property _bItemSelected
+ * @type Boolean
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._bItemSelected = false;
+
+/**
+ * Key code of the last key pressed in textbox.
+ *
+ * @property _nKeyCode
+ * @type Number
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._nKeyCode = null;
+
+/**
+ * Delay timeout ID.
+ *
+ * @property _nDelayID
+ * @type Number
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._nDelayID = -1;
+
+/**
+ * Src to iFrame used when useIFrame = true. Supports implementations over SSL
+ * as well.
+ *
+ * @property _iFrameSrc
+ * @type String
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._iFrameSrc = "javascript:false;";
+
+/**
+ * For users typing via certain IMEs, queries must be triggered by intervals,
+ * since key events yet supported across all browsers for all IMEs.
+ *
+ * @property _queryInterval
+ * @type Object
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._queryInterval = null;
+
+/**
+ * Internal tracker to last known textbox value, used to determine whether or not
+ * to trigger a query via interval for certain IME users.
+ *
+ * @event _sLastTextboxValue
+ * @type String
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._sLastTextboxValue = null;
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Private methods
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Updates and validates latest public config properties.
+ *
+ * @method __initProps
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._initProps = function() {
+ // Correct any invalid values
+ var minQueryLength = this.minQueryLength;
+ if(!YAHOO.lang.isNumber(minQueryLength)) {
+ this.minQueryLength = 1;
+ }
+ var maxResultsDisplayed = this.maxResultsDisplayed;
+ if(!YAHOO.lang.isNumber(maxResultsDisplayed) || (maxResultsDisplayed < 1)) {
+ this.maxResultsDisplayed = 10;
+ }
+ var queryDelay = this.queryDelay;
+ if(!YAHOO.lang.isNumber(queryDelay) || (queryDelay < 0)) {
+ this.queryDelay = 0.2;
+ }
+ var delimChar = this.delimChar;
+ if(YAHOO.lang.isString(delimChar)) {
+ this.delimChar = [delimChar];
+ }
+ else if(!YAHOO.lang.isArray(delimChar)) {
+ this.delimChar = null;
+ }
+ var animSpeed = this.animSpeed;
+ if((this.animHoriz || this.animVert) && YAHOO.util.Anim) {
+ if(!YAHOO.lang.isNumber(animSpeed) || (animSpeed < 0)) {
+ this.animSpeed = 0.3;
+ }
+ if(!this._oAnim ) {
+ this._oAnim = new YAHOO.util.Anim(this._oContainer._oContent, {}, this.animSpeed);
+ }
+ else {
+ this._oAnim.duration = this.animSpeed;
+ }
+ }
+ if(this.forceSelection && delimChar) {
+ YAHOO.log("The forceSelection feature has been enabled with delimChar defined.","warn", this.toString());
+ }
+};
+
+/**
+ * Initializes the results container helpers if they are enabled and do
+ * not exist
+ *
+ * @method _initContainerHelpers
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._initContainerHelpers = function() {
+ if(this.useShadow && !this._oContainer._oShadow) {
+ var oShadow = document.createElement("div");
+ oShadow.className = "yui-ac-shadow";
+ this._oContainer._oShadow = this._oContainer.appendChild(oShadow);
+ }
+ if(this.useIFrame && !this._oContainer._oIFrame) {
+ var oIFrame = document.createElement("iframe");
+ oIFrame.src = this._iFrameSrc;
+ oIFrame.frameBorder = 0;
+ oIFrame.scrolling = "no";
+ oIFrame.style.position = "absolute";
+ oIFrame.style.width = "100%";
+ oIFrame.style.height = "100%";
+ oIFrame.tabIndex = -1;
+ this._oContainer._oIFrame = this._oContainer.appendChild(oIFrame);
+ }
+};
+
+/**
+ * Initializes the results container once at object creation
+ *
+ * @method _initContainer
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._initContainer = function() {
+ YAHOO.util.Dom.addClass(this._oContainer, "yui-ac-container");
+
+ if(!this._oContainer._oContent) {
+ // The oContent div helps size the iframe and shadow properly
+ var oContent = document.createElement("div");
+ oContent.className = "yui-ac-content";
+ oContent.style.display = "none";
+ this._oContainer._oContent = this._oContainer.appendChild(oContent);
+
+ var oHeader = document.createElement("div");
+ oHeader.className = "yui-ac-hd";
+ oHeader.style.display = "none";
+ this._oContainer._oContent._oHeader = this._oContainer._oContent.appendChild(oHeader);
+
+ var oBody = document.createElement("div");
+ oBody.className = "yui-ac-bd";
+ this._oContainer._oContent._oBody = this._oContainer._oContent.appendChild(oBody);
+
+ var oFooter = document.createElement("div");
+ oFooter.className = "yui-ac-ft";
+ oFooter.style.display = "none";
+ this._oContainer._oContent._oFooter = this._oContainer._oContent.appendChild(oFooter);
+ }
+ else {
+ YAHOO.log("Could not initialize the container","warn",this.toString());
+ }
+};
+
+/**
+ * Clears out contents of container body and creates up to
+ * YAHOO.widget.AutoComplete#maxResultsDisplayed <li> elements in an
+ * <ul> element.
+ *
+ * @method _initList
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._initList = function() {
+ this._aListItems = [];
+ while(this._oContainer._oContent._oBody.hasChildNodes()) {
+ var oldListItems = this.getListItems();
+ if(oldListItems) {
+ for(var oldi = oldListItems.length-1; oldi >= 0; oldi--) {
+ oldListItems[oldi] = null;
+ }
+ }
+ this._oContainer._oContent._oBody.innerHTML = "";
+ }
+
+ var oList = document.createElement("ul");
+ oList = this._oContainer._oContent._oBody.appendChild(oList);
+ for(var i=0; i
- *
- *
- * @class AutoComplete
- * @constructor
- * @param elInput {HTMLElement} DOM element reference of an input field.
- * @param elInput {String} String ID of an input field.
- * @param elContainer {HTMLElement} DOM element reference of an existing DIV.
- * @param elContainer {String} String ID of an existing DIV.
- * @param oDataSource {Object} Instance of YAHOO.widget.DataSource for query/results.
- * @param oConfigs {Object} (optional) Object literal of configuration params.
- */
-YAHOO.widget.AutoComplete = function(elInput,elContainer,oDataSource,oConfigs) {
- if(elInput && elContainer && oDataSource) {
- // Validate DataSource
- if (oDataSource && (oDataSource instanceof YAHOO.widget.DataSource)) {
- this.dataSource = oDataSource;
- }
- else {
- return;
- }
-
- // Validate input element
- if(YAHOO.util.Dom.inDocument(elInput)) {
- if(typeof elInput == "string") {
- this._sName = "instance" + YAHOO.widget.AutoComplete._nIndex + " " + elInput;
- this._oTextbox = document.getElementById(elInput);
- }
- else {
- this._sName = (elInput.id) ?
- "instance" + YAHOO.widget.AutoComplete._nIndex + " " + elInput.id:
- "instance" + YAHOO.widget.AutoComplete._nIndex;
- this._oTextbox = elInput;
- }
- }
- else {
- return;
- }
-
- // Validate container element
- if(YAHOO.util.Dom.inDocument(elContainer)) {
- if(typeof elContainer == "string") {
- this._oContainer = document.getElementById(elContainer);
- }
- else {
- this._oContainer = elContainer;
- }
- if(this._oContainer.style.display == "none") {
- }
- }
- else {
- return;
- }
-
- // Set any config params passed in to override defaults
- if (typeof oConfigs == "object") {
- for(var sConfig in oConfigs) {
- if (sConfig) {
- this[sConfig] = oConfigs[sConfig];
- }
- }
- }
-
- // Initialization sequence
- this._initContainer();
- this._initProps();
- this._initList();
- this._initContainerHelpers();
-
- // Set up events
- var oSelf = this;
- var oTextbox = this._oTextbox;
- // Events are actually for the content module within the container
- var oContent = this._oContainer._oContent;
-
- // Dom events
- YAHOO.util.Event.addListener(oTextbox,"keyup",oSelf._onTextboxKeyUp,oSelf);
- YAHOO.util.Event.addListener(oTextbox,"keydown",oSelf._onTextboxKeyDown,oSelf);
- YAHOO.util.Event.addListener(oTextbox,"focus",oSelf._onTextboxFocus,oSelf);
- YAHOO.util.Event.addListener(oTextbox,"blur",oSelf._onTextboxBlur,oSelf);
- YAHOO.util.Event.addListener(oContent,"mouseover",oSelf._onContainerMouseover,oSelf);
- YAHOO.util.Event.addListener(oContent,"mouseout",oSelf._onContainerMouseout,oSelf);
- YAHOO.util.Event.addListener(oContent,"scroll",oSelf._onContainerScroll,oSelf);
- YAHOO.util.Event.addListener(oContent,"resize",oSelf._onContainerResize,oSelf);
- if(oTextbox.form) {
- YAHOO.util.Event.addListener(oTextbox.form,"submit",oSelf._onFormSubmit,oSelf);
- }
- YAHOO.util.Event.addListener(oTextbox,"keypress",oSelf._onTextboxKeyPress,oSelf);
-
- // Custom events
- this.textboxFocusEvent = new YAHOO.util.CustomEvent("textboxFocus", this);
- this.textboxKeyEvent = new YAHOO.util.CustomEvent("textboxKey", this);
- this.dataRequestEvent = new YAHOO.util.CustomEvent("dataRequest", this);
- this.dataReturnEvent = new YAHOO.util.CustomEvent("dataReturn", this);
- this.dataErrorEvent = new YAHOO.util.CustomEvent("dataError", this);
- this.containerExpandEvent = new YAHOO.util.CustomEvent("containerExpand", this);
- this.typeAheadEvent = new YAHOO.util.CustomEvent("typeAhead", this);
- this.itemMouseOverEvent = new YAHOO.util.CustomEvent("itemMouseOver", this);
- this.itemMouseOutEvent = new YAHOO.util.CustomEvent("itemMouseOut", this);
- this.itemArrowToEvent = new YAHOO.util.CustomEvent("itemArrowTo", this);
- this.itemArrowFromEvent = new YAHOO.util.CustomEvent("itemArrowFrom", this);
- this.itemSelectEvent = new YAHOO.util.CustomEvent("itemSelect", this);
- this.unmatchedItemSelectEvent = new YAHOO.util.CustomEvent("unmatchedItemSelect", this);
- this.selectionEnforceEvent = new YAHOO.util.CustomEvent("selectionEnforce", this);
- this.containerCollapseEvent = new YAHOO.util.CustomEvent("containerCollapse", this);
- this.textboxBlurEvent = new YAHOO.util.CustomEvent("textboxBlur", this);
-
- // Finish up
- oTextbox.setAttribute("autocomplete","off");
- YAHOO.widget.AutoComplete._nIndex++;
- }
- // Required arguments were not found
- else {
- }
-};
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// Public member variables
-//
-/////////////////////////////////////////////////////////////////////////////
-
-/**
- * The DataSource object that encapsulates the data used for auto completion.
- * This object should be an inherited object from YAHOO.widget.DataSource.
- *
- * @property dataSource
- * @type Object
- */
-YAHOO.widget.AutoComplete.prototype.dataSource = null;
-
-/**
- * Number of characters that must be entered before querying for results. A negative value
- * effectively turns off the widget. A value of 0 allows queries of null or empty string
- * values.
- *
- * @property minQueryLength
- * @type Number
- * @default 1
- */
-YAHOO.widget.AutoComplete.prototype.minQueryLength = 1;
-
-/**
- * Maximum number of results to display in results container.
- *
- * @property maxResultsDisplayed
- * @type Number
- * @default 10
- */
-YAHOO.widget.AutoComplete.prototype.maxResultsDisplayed = 10;
-
-/**
- * Number of seconds to delay before submitting a query request. If a query
- * request is received before a previous one has completed its delay, the
- * previous request is cancelled and the new request is set to the delay.
- *
- * @property queryDelay
- * @type Number
- * @default 0.5
- */
-YAHOO.widget.AutoComplete.prototype.queryDelay = 0.5;
-
-/**
- * Class name of a highlighted item within results container.
- *
- * @property highlighClassName
- * @type String
- * @default "yui-ac-highlight"
- */
-YAHOO.widget.AutoComplete.prototype.highlightClassName = "yui-ac-highlight";
-
-/**
- * Class name of a pre-highlighted item within results container.
- *
- * @property prehighlightClassName
- * @type String
- */
-YAHOO.widget.AutoComplete.prototype.prehighlightClassName = null;
-
-/**
- * Query delimiter. A single character separator for multiple delimited
- * selections. Multiple delimiter characteres may be defined as an array of
- * strings. A null value or empty string indicates that query results cannot
- * be delimited. This feature is not recommended if you need forceSelection to
- * be true.
- *
- * @property delimChar
- * @type String | String[]
- */
-YAHOO.widget.AutoComplete.prototype.delimChar = null;
-
-/**
- * Whether or not the first item in results container should be automatically highlighted
- * on expand.
- *
- * @property autoHighlight
- * @type Boolean
- * @default true
- */
-YAHOO.widget.AutoComplete.prototype.autoHighlight = true;
-
-/**
- * Whether or not the input field should be automatically updated
- * with the first query result as the user types, auto-selecting the substring
- * that the user has not typed.
- *
- * @property typeAhead
- * @type Boolean
- * @default false
- */
-YAHOO.widget.AutoComplete.prototype.typeAhead = false;
-
-/**
- * Whether or not to animate the expansion/collapse of the results container in the
- * horizontal direction.
- *
- * @property animHoriz
- * @type Boolean
- * @default false
- */
-YAHOO.widget.AutoComplete.prototype.animHoriz = false;
-
-/**
- * Whether or not to animate the expansion/collapse of the results container in the
- * vertical direction.
- *
- * @property animVert
- * @type Boolean
- * @default true
- */
-YAHOO.widget.AutoComplete.prototype.animVert = true;
-
-/**
- * Speed of container expand/collapse animation, in seconds..
- *
- * @property animSpeed
- * @type Number
- * @default 0.3
- */
-YAHOO.widget.AutoComplete.prototype.animSpeed = 0.3;
-
-/**
- * Whether or not to force the user's selection to match one of the query
- * results. Enabling this feature essentially transforms the input field into a
- * <select> field. This feature is not recommended with delimiter character(s)
- * defined.
- *
- * @property forceSelection
- * @type Boolean
- * @default false
- */
-YAHOO.widget.AutoComplete.prototype.forceSelection = false;
-
-/**
- * Whether or not to allow browsers to cache user-typed input in the input
- * field. Disabling this feature will prevent the widget from setting the
- * autocomplete="off" on the input field. When autocomplete="off"
- * and users click the back button after form submission, user-typed input can
- * be prefilled by the browser from its cache. This caching of user input may
- * not be desired for sensitive data, such as credit card numbers, in which
- * case, implementers should consider setting allowBrowserAutocomplete to false.
- *
- * @property allowBrowserAutocomplete
- * @type Boolean
- * @default true
- */
-YAHOO.widget.AutoComplete.prototype.allowBrowserAutocomplete = true;
-
-/**
- * Whether or not the results container should always be displayed.
- * Enabling this feature displays the container when the widget is instantiated
- * and prevents the toggling of the container to a collapsed state.
- *
- * @property alwaysShowContainer
- * @type Boolean
- * @default false
- */
-YAHOO.widget.AutoComplete.prototype.alwaysShowContainer = false;
-
-/**
- * Whether or not to use an iFrame to layer over Windows form elements in
- * IE. Set to true only when the results container will be on top of a
- * <select> field in IE and thus exposed to the IE z-index bug (i.e.,
- * 5.5 < IE < 7).
- *
- * @property useIFrame
- * @type Boolean
- * @default false
- */
-YAHOO.widget.AutoComplete.prototype.useIFrame = false;
-
-/**
- * Whether or not the results container should have a shadow.
- *
- * @property useShadow
- * @type Boolean
- * @default false
- */
-YAHOO.widget.AutoComplete.prototype.useShadow = false;
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// Public methods
-//
-/////////////////////////////////////////////////////////////////////////////
-
- /**
- * Public accessor to the unique name of the AutoComplete instance.
- *
- * @method toString
- * @return {String} Unique name of the AutoComplete instance.
- */
-YAHOO.widget.AutoComplete.prototype.toString = function() {
- return "AutoComplete " + this._sName;
-};
-
- /**
- * Returns true if container is in an expanded state, false otherwise.
- *
- * @method isContainerOpen
- * @return {Boolean} Returns true if container is in an expanded state, false otherwise.
- */
-YAHOO.widget.AutoComplete.prototype.isContainerOpen = function() {
- return this._bContainerOpen;
-};
-
-/**
- * Public accessor to the internal array of DOM <li> elements that
- * display query results within the results container.
- *
- * @method getListItems
- * @return {HTMLElement[]} Array of <li> elements within the results container.
- */
-YAHOO.widget.AutoComplete.prototype.getListItems = function() {
- return this._aListItems;
-};
-
-/**
- * Public accessor to the data held in an <li> element of the
- * results container.
- *
- * @method getListItemData
- * @return {Object | Array} Object or array of result data or null
- */
-YAHOO.widget.AutoComplete.prototype.getListItemData = function(oListItem) {
- if(oListItem._oResultData) {
- return oListItem._oResultData;
- }
- else {
- return false;
- }
-};
-
-/**
- * Sets HTML markup for the results container header. This markup will be
- * inserted within a <div> tag with a class of "ac_hd".
- *
- * @method setHeader
- * @param sHeader {String} HTML markup for results container header.
- */
-YAHOO.widget.AutoComplete.prototype.setHeader = function(sHeader) {
- if(sHeader) {
- if(this._oContainer._oContent._oHeader) {
- this._oContainer._oContent._oHeader.innerHTML = sHeader;
- this._oContainer._oContent._oHeader.style.display = "block";
- }
- }
- else {
- this._oContainer._oContent._oHeader.innerHTML = "";
- this._oContainer._oContent._oHeader.style.display = "none";
- }
-};
-
-/**
- * Sets HTML markup for the results container footer. This markup will be
- * inserted within a <div> tag with a class of "ac_ft".
- *
- * @method setFooter
- * @param sFooter {String} HTML markup for results container footer.
- */
-YAHOO.widget.AutoComplete.prototype.setFooter = function(sFooter) {
- if(sFooter) {
- if(this._oContainer._oContent._oFooter) {
- this._oContainer._oContent._oFooter.innerHTML = sFooter;
- this._oContainer._oContent._oFooter.style.display = "block";
- }
- }
- else {
- this._oContainer._oContent._oFooter.innerHTML = "";
- this._oContainer._oContent._oFooter.style.display = "none";
- }
-};
-
-/**
- * Sets HTML markup for the results container body. This markup will be
- * inserted within a <div> tag with a class of "ac_bd".
- *
- * @method setBody
- * @param sHeader {String} HTML markup for results container body.
- */
-YAHOO.widget.AutoComplete.prototype.setBody = function(sBody) {
- if(sBody) {
- if(this._oContainer._oContent._oBody) {
- this._oContainer._oContent._oBody.innerHTML = sBody;
- this._oContainer._oContent._oBody.style.display = "block";
- this._oContainer._oContent.style.display = "block";
- }
- }
- else {
- this._oContainer._oContent._oBody.innerHTML = "";
- this._oContainer._oContent.style.display = "none";
- }
- this._maxResultsDisplayed = 0;
-};
-
-/**
- * Overridable method that converts a result item object into HTML markup
- * for display. Return data values are accessible via the oResultItem object,
- * and the key return value will always be oResultItem[0]. Markup will be
- * displayed within <li> element tags in the container.
- *
- * @method formatResult
- * @param oResultItem {Object} Result item representing one query result. Data is held in an array.
- * @param sQuery {String} The current query string.
- * @return {String} HTML markup of formatted result data.
- */
-YAHOO.widget.AutoComplete.prototype.formatResult = function(oResultItem, sQuery) {
- var sResult = oResultItem[0];
- if(sResult) {
- return sResult;
- }
- else {
- return "";
- }
-};
-
-/**
- * Overridable method called before container expands allows implementers to access data
- * and DOM elements.
- *
- * @method doBeforeExpandContainer
- * @return {Boolean} Return true to continue expanding container, false to cancel the expand.
- */
-YAHOO.widget.AutoComplete.prototype.doBeforeExpandContainer = function(oResultItem, sQuery) {
- return true;
-};
-
-/**
- * Makes query request to the DataSource.
- *
- * @method sendQuery
- * @param sQuery {String} Query string.
- */
-YAHOO.widget.AutoComplete.prototype.sendQuery = function(sQuery) {
- this._sendQuery(sQuery);
-};
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// Public events
-//
-/////////////////////////////////////////////////////////////////////////////
-
-/**
- * Fired when the input field receives focus.
- *
- * @event textboxFocusEvent
- * @param oSelf {Object} The AutoComplete instance.
- */
-YAHOO.widget.AutoComplete.prototype.textboxFocusEvent = null;
-
-/**
- * Fired when the input field receives key input.
- *
- * @event textboxKeyEvent
- * @param oSelf {Object} The AutoComplete instance.
- * @param nKeycode {Number} The keycode number.
- */
-YAHOO.widget.AutoComplete.prototype.textboxKeyEvent = null;
-
-/**
- * Fired when the AutoComplete instance makes a query to the DataSource.
- *
- * @event dataRequestEvent
- * @param oSelf {Object} The AutoComplete instance.
- * @param sQuery {String} The query string.
- */
-YAHOO.widget.AutoComplete.prototype.dataRequestEvent = null;
-
-/**
- * Fired when the AutoComplete instance receives query results from the data
- * source.
- *
- * @event dataReturnEvent
- * @param oSelf {Object} The AutoComplete instance.
- * @param sQuery {String} The query string.
- * @param aResults {Array} Results array.
- */
-YAHOO.widget.AutoComplete.prototype.dataReturnEvent = null;
-
-/**
- * Fired when the AutoComplete instance does not receive query results from the
- * DataSource due to an error.
- *
- * @event dataErrorEvent
- * @param oSelf {Object} The AutoComplete instance.
- * @param sQuery {String} The query string.
- */
-YAHOO.widget.AutoComplete.prototype.dataErrorEvent = null;
-
-/**
- * Fired when the results container is expanded.
- *
- * @event containerExpandEvent
- * @param oSelf {Object} The AutoComplete instance.
- */
-YAHOO.widget.AutoComplete.prototype.containerExpandEvent = null;
-
-/**
- * Fired when the input field has been prefilled by the type-ahead
- * feature.
- *
- * @event typeAheadEvent
- * @param oSelf {Object} The AutoComplete instance.
- * @param sQuery {String} The query string.
- * @param sPrefill {String} The prefill string.
- */
-YAHOO.widget.AutoComplete.prototype.typeAheadEvent = null;
-
-/**
- * Fired when result item has been moused over.
- *
- * @event itemMouseOverEvent
- * @param oSelf {Object} The AutoComplete instance.
- * @param elItem {HTMLElement} The <li> element item moused to.
- */
-YAHOO.widget.AutoComplete.prototype.itemMouseOverEvent = null;
-
-/**
- * Fired when result item has been moused out.
- *
- * @event itemMouseOutEvent
- * @param oSelf {Object} The AutoComplete instance.
- * @param elItem {HTMLElement} The <li> element item moused from.
- */
-YAHOO.widget.AutoComplete.prototype.itemMouseOutEvent = null;
-
-/**
- * Fired when result item has been arrowed to.
- *
- * @event itemArrowToEvent
- * @param oSelf {Object} The AutoComplete instance.
- * @param elItem {HTMLElement} The <li> element item arrowed to.
- */
-YAHOO.widget.AutoComplete.prototype.itemArrowToEvent = null;
-
-/**
- * Fired when result item has been arrowed away from.
- *
- * @event itemArrowFromEvent
- * @param oSelf {Object} The AutoComplete instance.
- * @param elItem {HTMLElement} The <li> element item arrowed from.
- */
-YAHOO.widget.AutoComplete.prototype.itemArrowFromEvent = null;
-
-/**
- * Fired when an item is selected via mouse click, ENTER key, or TAB key.
- *
- * @event itemSelectEvent
- * @param oSelf {Object} The AutoComplete instance.
- * @param elItem {HTMLElement} The selected <li> element item.
- * @param oData {Object} The data returned for the item, either as an object,
- * or mapped from the schema into an array.
- */
-YAHOO.widget.AutoComplete.prototype.itemSelectEvent = null;
-
-/**
- * Fired when a user selection does not match any of the displayed result items.
- * Note that this event may not behave as expected when delimiter characters
- * have been defined.
- *
- * @event unmatchedItemSelectEvent
- * @param oSelf {Object} The AutoComplete instance.
- * @param sQuery {String} The user-typed query string.
- */
-YAHOO.widget.AutoComplete.prototype.unmatchedItemSelectEvent = null;
-
-/**
- * Fired if forceSelection is enabled and the user's input has been cleared
- * because it did not match one of the returned query results.
- *
- * @event selectionEnforceEvent
- * @param oSelf {Object} The AutoComplete instance.
- */
-YAHOO.widget.AutoComplete.prototype.selectionEnforceEvent = null;
-
-/**
- * Fired when the results container is collapsed.
- *
- * @event containerCollapseEvent
- * @param oSelf {Object} The AutoComplete instance.
- */
-YAHOO.widget.AutoComplete.prototype.containerCollapseEvent = null;
-
-/**
- * Fired when the input field loses focus.
- *
- * @event textboxBlurEvent
- * @param oSelf {Object} The AutoComplete instance.
- */
-YAHOO.widget.AutoComplete.prototype.textboxBlurEvent = null;
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// Private member variables
-//
-/////////////////////////////////////////////////////////////////////////////
-
-/**
- * Internal class variable to index multiple AutoComplete instances.
- *
- * @property _nIndex
- * @type Number
- * @default 0
- * @private
- */
-YAHOO.widget.AutoComplete._nIndex = 0;
-
-/**
- * Name of AutoComplete instance.
- *
- * @property _sName
- * @type String
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._sName = null;
-
-/**
- * Text input field DOM element.
- *
- * @property _oTextbox
- * @type HTMLElement
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._oTextbox = null;
-
-/**
- * Whether or not the input field is currently in focus. If query results come back
- * but the user has already moved on, do not proceed with auto complete behavior.
- *
- * @property _bFocused
- * @type Boolean
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._bFocused = true;
-
-/**
- * Animation instance for container expand/collapse.
- *
- * @property _oAnim
- * @type Boolean
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._oAnim = null;
-
-/**
- * Container DOM element.
- *
- * @property _oContainer
- * @type HTMLElement
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._oContainer = null;
-
-/**
- * Whether or not the results container is currently open.
- *
- * @property _bContainerOpen
- * @type Boolean
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._bContainerOpen = false;
-
-/**
- * Whether or not the mouse is currently over the results
- * container. This is necessary in order to prevent clicks on container items
- * from being text input field blur events.
- *
- * @property _bOverContainer
- * @type Boolean
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._bOverContainer = false;
-
-/**
- * Array of <li> elements references that contain query results within the
- * results container.
- *
- * @property _aListItems
- * @type Array
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._aListItems = null;
-
-/**
- * Number of <li> elements currently displayed in results container.
- *
- * @property _nDisplayedItems
- * @type Number
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._nDisplayedItems = 0;
-
-/**
- * Internal count of <li> elements displayed and hidden in results container.
- *
- * @property _maxResultsDisplayed
- * @type Number
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._maxResultsDisplayed = 0;
-
-/**
- * Current query string
- *
- * @property _sCurQuery
- * @type String
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._sCurQuery = null;
-
-/**
- * Past queries this session (for saving delimited queries).
- *
- * @property _sSavedQuery
- * @type String
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._sSavedQuery = null;
-
-/**
- * Pointer to the currently highlighted <li> element in the container.
- *
- * @property _oCurItem
- * @type HTMLElement
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._oCurItem = null;
-
-/**
- * Whether or not an item has been selected since the container was populated
- * with results. Reset to false by _populateList, and set to true when item is
- * selected.
- *
- * @property _bItemSelected
- * @type Boolean
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._bItemSelected = false;
-
-/**
- * Key code of the last key pressed in textbox.
- *
- * @property _nKeyCode
- * @type Number
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._nKeyCode = null;
-
-/**
- * Delay timeout ID.
- *
- * @property _nDelayID
- * @type Number
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._nDelayID = -1;
-
-/**
- * Src to iFrame used when useIFrame = true. Supports implementations over SSL
- * as well.
- *
- * @property _iFrameSrc
- * @type String
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._iFrameSrc = "javascript:false;";
-
-/**
- * For users typing via certain IMEs, queries must be triggered by intervals,
- * since key events yet supported across all browsers for all IMEs.
- *
- * @property _queryInterval
- * @type Object
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._queryInterval = null;
-
-/**
- * Internal tracker to last known textbox value, used to determine whether or not
- * to trigger a query via interval for certain IME users.
- *
- * @event _sLastTextboxValue
- * @type String
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._sLastTextboxValue = null;
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// Private methods
-//
-/////////////////////////////////////////////////////////////////////////////
-
-/**
- * Updates and validates latest public config properties.
- *
- * @method __initProps
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._initProps = function() {
- // Correct any invalid values
- var minQueryLength = this.minQueryLength;
- if(isNaN(minQueryLength) || (minQueryLength < 1)) {
- minQueryLength = 1;
- }
- var maxResultsDisplayed = this.maxResultsDisplayed;
- if(isNaN(this.maxResultsDisplayed) || (this.maxResultsDisplayed < 1)) {
- this.maxResultsDisplayed = 10;
- }
- var queryDelay = this.queryDelay;
- if(isNaN(this.queryDelay) || (this.queryDelay < 0)) {
- this.queryDelay = 0.5;
- }
- var aDelimChar = (this.delimChar) ? this.delimChar : null;
- if(aDelimChar) {
- if(typeof aDelimChar == "string") {
- this.delimChar = [aDelimChar];
- }
- else if(aDelimChar.constructor != Array) {
- this.delimChar = null;
- }
- }
- var animSpeed = this.animSpeed;
- if((this.animHoriz || this.animVert) && YAHOO.util.Anim) {
- if(isNaN(animSpeed) || (animSpeed < 0)) {
- animSpeed = 0.3;
- }
- if(!this._oAnim ) {
- oAnim = new YAHOO.util.Anim(this._oContainer._oContent, {}, this.animSpeed);
- this._oAnim = oAnim;
- }
- else {
- this._oAnim.duration = animSpeed;
- }
- }
- if(this.forceSelection && this.delimChar) {
- }
-};
-
-/**
- * Initializes the results container helpers if they are enabled and do
- * not exist
- *
- * @method _initContainerHelpers
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._initContainerHelpers = function() {
- if(this.useShadow && !this._oContainer._oShadow) {
- var oShadow = document.createElement("div");
- oShadow.className = "yui-ac-shadow";
- this._oContainer._oShadow = this._oContainer.appendChild(oShadow);
- }
- if(this.useIFrame && !this._oContainer._oIFrame) {
- var oIFrame = document.createElement("iframe");
- oIFrame.src = this._iFrameSrc;
- oIFrame.frameBorder = 0;
- oIFrame.scrolling = "no";
- oIFrame.style.position = "absolute";
- oIFrame.style.width = "100%";
- oIFrame.style.height = "100%";
- oIFrame.tabIndex = -1;
- this._oContainer._oIFrame = this._oContainer.appendChild(oIFrame);
- }
-};
-
-/**
- * Initializes the results container once at object creation
- *
- * @method _initContainer
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._initContainer = function() {
- if(!this._oContainer._oContent) {
- // The oContent div helps size the iframe and shadow properly
- var oContent = document.createElement("div");
- oContent.className = "yui-ac-content";
- oContent.style.display = "none";
- this._oContainer._oContent = this._oContainer.appendChild(oContent);
-
- var oHeader = document.createElement("div");
- oHeader.className = "yui-ac-hd";
- oHeader.style.display = "none";
- this._oContainer._oContent._oHeader = this._oContainer._oContent.appendChild(oHeader);
-
- var oBody = document.createElement("div");
- oBody.className = "yui-ac-bd";
- this._oContainer._oContent._oBody = this._oContainer._oContent.appendChild(oBody);
-
- var oFooter = document.createElement("div");
- oFooter.className = "yui-ac-ft";
- oFooter.style.display = "none";
- this._oContainer._oContent._oFooter = this._oContainer._oContent.appendChild(oFooter);
- }
- else {
- }
-};
-
-/**
- * Clears out contents of container body and creates up to
- * YAHOO.widget.AutoComplete#maxResultsDisplayed <li> elements in an
- * <ul> element.
- *
- * @method _initList
- * @private
- */
-YAHOO.widget.AutoComplete.prototype._initList = function() {
- this._aListItems = [];
- while(this._oContainer._oContent._oBody.hasChildNodes()) {
- var oldListItems = this.getListItems();
- if(oldListItems) {
- for(var oldi = oldListItems.length-1; oldi >= 0; i--) {
- oldListItems[oldi] = null;
- }
- }
- this._oContainer._oContent._oBody.innerHTML = "";
- }
-
- var oList = document.createElement("ul");
- oList = this._oContainer._oContent._oBody.appendChild(oList);
- for(var i=0; i
+ *
+ *
+ * @class AutoComplete
+ * @constructor
+ * @param elInput {HTMLElement} DOM element reference of an input field.
+ * @param elInput {String} String ID of an input field.
+ * @param elContainer {HTMLElement} DOM element reference of an existing DIV.
+ * @param elContainer {String} String ID of an existing DIV.
+ * @param oDataSource {YAHOO.widget.DataSource} DataSource instance.
+ * @param oConfigs {Object} (optional) Object literal of configuration params.
+ */
+YAHOO.widget.AutoComplete = function(elInput,elContainer,oDataSource,oConfigs) {
+ if(elInput && elContainer && oDataSource) {
+ // Validate DataSource
+ if(oDataSource instanceof YAHOO.widget.DataSource) {
+ this.dataSource = oDataSource;
+ }
+ else {
+ return;
+ }
+
+ // Validate input element
+ if(YAHOO.util.Dom.inDocument(elInput)) {
+ if(YAHOO.lang.isString(elInput)) {
+ this._sName = "instance" + YAHOO.widget.AutoComplete._nIndex + " " + elInput;
+ this._oTextbox = document.getElementById(elInput);
+ }
+ else {
+ this._sName = (elInput.id) ?
+ "instance" + YAHOO.widget.AutoComplete._nIndex + " " + elInput.id:
+ "instance" + YAHOO.widget.AutoComplete._nIndex;
+ this._oTextbox = elInput;
+ }
+ YAHOO.util.Dom.addClass(this._oTextbox, "yui-ac-input");
+ }
+ else {
+ return;
+ }
+
+ // Validate container element
+ if(YAHOO.util.Dom.inDocument(elContainer)) {
+ if(YAHOO.lang.isString(elContainer)) {
+ this._oContainer = document.getElementById(elContainer);
+ }
+ else {
+ this._oContainer = elContainer;
+ }
+ if(this._oContainer.style.display == "none") {
+ }
+
+ // For skinning
+ var elParent = this._oContainer.parentNode;
+ var elTag = elParent.tagName.toLowerCase();
+ while(elParent && (elParent != "document")) {
+ if(elTag == "div") {
+ YAHOO.util.Dom.addClass(elParent, "yui-ac");
+ break;
+ }
+ else {
+ elParent = elParent.parentNode;
+ elTag = elParent.tagName.toLowerCase();
+ }
+ }
+ if(elTag != "div") {
+ }
+ }
+ else {
+ return;
+ }
+
+ // Set any config params passed in to override defaults
+ if(oConfigs && (oConfigs.constructor == Object)) {
+ for(var sConfig in oConfigs) {
+ if(sConfig) {
+ this[sConfig] = oConfigs[sConfig];
+ }
+ }
+ }
+
+ // Initialization sequence
+ this._initContainer();
+ this._initProps();
+ this._initList();
+ this._initContainerHelpers();
+
+ // Set up events
+ var oSelf = this;
+ var oTextbox = this._oTextbox;
+ // Events are actually for the content module within the container
+ var oContent = this._oContainer._oContent;
+
+ // Dom events
+ YAHOO.util.Event.addListener(oTextbox,"keyup",oSelf._onTextboxKeyUp,oSelf);
+ YAHOO.util.Event.addListener(oTextbox,"keydown",oSelf._onTextboxKeyDown,oSelf);
+ YAHOO.util.Event.addListener(oTextbox,"focus",oSelf._onTextboxFocus,oSelf);
+ YAHOO.util.Event.addListener(oTextbox,"blur",oSelf._onTextboxBlur,oSelf);
+ YAHOO.util.Event.addListener(oContent,"mouseover",oSelf._onContainerMouseover,oSelf);
+ YAHOO.util.Event.addListener(oContent,"mouseout",oSelf._onContainerMouseout,oSelf);
+ YAHOO.util.Event.addListener(oContent,"scroll",oSelf._onContainerScroll,oSelf);
+ YAHOO.util.Event.addListener(oContent,"resize",oSelf._onContainerResize,oSelf);
+ if(oTextbox.form) {
+ YAHOO.util.Event.addListener(oTextbox.form,"submit",oSelf._onFormSubmit,oSelf);
+ }
+ YAHOO.util.Event.addListener(oTextbox,"keypress",oSelf._onTextboxKeyPress,oSelf);
+
+ // Custom events
+ this.textboxFocusEvent = new YAHOO.util.CustomEvent("textboxFocus", this);
+ this.textboxKeyEvent = new YAHOO.util.CustomEvent("textboxKey", this);
+ this.dataRequestEvent = new YAHOO.util.CustomEvent("dataRequest", this);
+ this.dataReturnEvent = new YAHOO.util.CustomEvent("dataReturn", this);
+ this.dataErrorEvent = new YAHOO.util.CustomEvent("dataError", this);
+ this.containerExpandEvent = new YAHOO.util.CustomEvent("containerExpand", this);
+ this.typeAheadEvent = new YAHOO.util.CustomEvent("typeAhead", this);
+ this.itemMouseOverEvent = new YAHOO.util.CustomEvent("itemMouseOver", this);
+ this.itemMouseOutEvent = new YAHOO.util.CustomEvent("itemMouseOut", this);
+ this.itemArrowToEvent = new YAHOO.util.CustomEvent("itemArrowTo", this);
+ this.itemArrowFromEvent = new YAHOO.util.CustomEvent("itemArrowFrom", this);
+ this.itemSelectEvent = new YAHOO.util.CustomEvent("itemSelect", this);
+ this.unmatchedItemSelectEvent = new YAHOO.util.CustomEvent("unmatchedItemSelect", this);
+ this.selectionEnforceEvent = new YAHOO.util.CustomEvent("selectionEnforce", this);
+ this.containerCollapseEvent = new YAHOO.util.CustomEvent("containerCollapse", this);
+ this.textboxBlurEvent = new YAHOO.util.CustomEvent("textboxBlur", this);
+
+ // Finish up
+ oTextbox.setAttribute("autocomplete","off");
+ YAHOO.widget.AutoComplete._nIndex++;
+ }
+ // Required arguments were not found
+ else {
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Public member variables
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * The DataSource object that encapsulates the data used for auto completion.
+ * This object should be an inherited object from YAHOO.widget.DataSource.
+ *
+ * @property dataSource
+ * @type YAHOO.widget.DataSource
+ */
+YAHOO.widget.AutoComplete.prototype.dataSource = null;
+
+/**
+ * Number of characters that must be entered before querying for results. A negative value
+ * effectively turns off the widget. A value of 0 allows queries of null or empty string
+ * values.
+ *
+ * @property minQueryLength
+ * @type Number
+ * @default 1
+ */
+YAHOO.widget.AutoComplete.prototype.minQueryLength = 1;
+
+/**
+ * Maximum number of results to display in results container.
+ *
+ * @property maxResultsDisplayed
+ * @type Number
+ * @default 10
+ */
+YAHOO.widget.AutoComplete.prototype.maxResultsDisplayed = 10;
+
+/**
+ * Number of seconds to delay before submitting a query request. If a query
+ * request is received before a previous one has completed its delay, the
+ * previous request is cancelled and the new request is set to the delay.
+ * Implementers should take care when setting this value very low (i.e., less
+ * than 0.2) with low latency DataSources and the typeAhead feature enabled, as
+ * fast typers may see unexpected behavior.
+ *
+ * @property queryDelay
+ * @type Number
+ * @default 0.2
+ */
+YAHOO.widget.AutoComplete.prototype.queryDelay = 0.2;
+
+/**
+ * Class name of a highlighted item within results container.
+ *
+ * @property highlightClassName
+ * @type String
+ * @default "yui-ac-highlight"
+ */
+YAHOO.widget.AutoComplete.prototype.highlightClassName = "yui-ac-highlight";
+
+/**
+ * Class name of a pre-highlighted item within results container.
+ *
+ * @property prehighlightClassName
+ * @type String
+ */
+YAHOO.widget.AutoComplete.prototype.prehighlightClassName = null;
+
+/**
+ * Query delimiter. A single character separator for multiple delimited
+ * selections. Multiple delimiter characteres may be defined as an array of
+ * strings. A null value or empty string indicates that query results cannot
+ * be delimited. This feature is not recommended if you need forceSelection to
+ * be true.
+ *
+ * @property delimChar
+ * @type String | String[]
+ */
+YAHOO.widget.AutoComplete.prototype.delimChar = null;
+
+/**
+ * Whether or not the first item in results container should be automatically highlighted
+ * on expand.
+ *
+ * @property autoHighlight
+ * @type Boolean
+ * @default true
+ */
+YAHOO.widget.AutoComplete.prototype.autoHighlight = true;
+
+/**
+ * Whether or not the input field should be automatically updated
+ * with the first query result as the user types, auto-selecting the substring
+ * that the user has not typed.
+ *
+ * @property typeAhead
+ * @type Boolean
+ * @default false
+ */
+YAHOO.widget.AutoComplete.prototype.typeAhead = false;
+
+/**
+ * Whether or not to animate the expansion/collapse of the results container in the
+ * horizontal direction.
+ *
+ * @property animHoriz
+ * @type Boolean
+ * @default false
+ */
+YAHOO.widget.AutoComplete.prototype.animHoriz = false;
+
+/**
+ * Whether or not to animate the expansion/collapse of the results container in the
+ * vertical direction.
+ *
+ * @property animVert
+ * @type Boolean
+ * @default true
+ */
+YAHOO.widget.AutoComplete.prototype.animVert = true;
+
+/**
+ * Speed of container expand/collapse animation, in seconds..
+ *
+ * @property animSpeed
+ * @type Number
+ * @default 0.3
+ */
+YAHOO.widget.AutoComplete.prototype.animSpeed = 0.3;
+
+/**
+ * Whether or not to force the user's selection to match one of the query
+ * results. Enabling this feature essentially transforms the input field into a
+ * <select> field. This feature is not recommended with delimiter character(s)
+ * defined.
+ *
+ * @property forceSelection
+ * @type Boolean
+ * @default false
+ */
+YAHOO.widget.AutoComplete.prototype.forceSelection = false;
+
+/**
+ * Whether or not to allow browsers to cache user-typed input in the input
+ * field. Disabling this feature will prevent the widget from setting the
+ * autocomplete="off" on the input field. When autocomplete="off"
+ * and users click the back button after form submission, user-typed input can
+ * be prefilled by the browser from its cache. This caching of user input may
+ * not be desired for sensitive data, such as credit card numbers, in which
+ * case, implementers should consider setting allowBrowserAutocomplete to false.
+ *
+ * @property allowBrowserAutocomplete
+ * @type Boolean
+ * @default true
+ */
+YAHOO.widget.AutoComplete.prototype.allowBrowserAutocomplete = true;
+
+/**
+ * Whether or not the results container should always be displayed.
+ * Enabling this feature displays the container when the widget is instantiated
+ * and prevents the toggling of the container to a collapsed state.
+ *
+ * @property alwaysShowContainer
+ * @type Boolean
+ * @default false
+ */
+YAHOO.widget.AutoComplete.prototype.alwaysShowContainer = false;
+
+/**
+ * Whether or not to use an iFrame to layer over Windows form elements in
+ * IE. Set to true only when the results container will be on top of a
+ * <select> field in IE and thus exposed to the IE z-index bug (i.e.,
+ * 5.5 < IE < 7).
+ *
+ * @property useIFrame
+ * @type Boolean
+ * @default false
+ */
+YAHOO.widget.AutoComplete.prototype.useIFrame = false;
+
+/**
+ * Whether or not the results container should have a shadow.
+ *
+ * @property useShadow
+ * @type Boolean
+ * @default false
+ */
+YAHOO.widget.AutoComplete.prototype.useShadow = false;
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Public methods
+//
+/////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Public accessor to the unique name of the AutoComplete instance.
+ *
+ * @method toString
+ * @return {String} Unique name of the AutoComplete instance.
+ */
+YAHOO.widget.AutoComplete.prototype.toString = function() {
+ return "AutoComplete " + this._sName;
+};
+
+ /**
+ * Returns true if container is in an expanded state, false otherwise.
+ *
+ * @method isContainerOpen
+ * @return {Boolean} Returns true if container is in an expanded state, false otherwise.
+ */
+YAHOO.widget.AutoComplete.prototype.isContainerOpen = function() {
+ return this._bContainerOpen;
+};
+
+/**
+ * Public accessor to the internal array of DOM <li> elements that
+ * display query results within the results container.
+ *
+ * @method getListItems
+ * @return {HTMLElement[]} Array of <li> elements within the results container.
+ */
+YAHOO.widget.AutoComplete.prototype.getListItems = function() {
+ return this._aListItems;
+};
+
+/**
+ * Public accessor to the data held in an <li> element of the
+ * results container.
+ *
+ * @method getListItemData
+ * @return {Object | Object[]} Object or array of result data or null
+ */
+YAHOO.widget.AutoComplete.prototype.getListItemData = function(oListItem) {
+ if(oListItem._oResultData) {
+ return oListItem._oResultData;
+ }
+ else {
+ return false;
+ }
+};
+
+/**
+ * Sets HTML markup for the results container header. This markup will be
+ * inserted within a <div> tag with a class of "yui-ac-hd".
+ *
+ * @method setHeader
+ * @param sHeader {String} HTML markup for results container header.
+ */
+YAHOO.widget.AutoComplete.prototype.setHeader = function(sHeader) {
+ if(sHeader) {
+ if(this._oContainer._oContent._oHeader) {
+ this._oContainer._oContent._oHeader.innerHTML = sHeader;
+ this._oContainer._oContent._oHeader.style.display = "block";
+ }
+ }
+ else {
+ this._oContainer._oContent._oHeader.innerHTML = "";
+ this._oContainer._oContent._oHeader.style.display = "none";
+ }
+};
+
+/**
+ * Sets HTML markup for the results container footer. This markup will be
+ * inserted within a <div> tag with a class of "yui-ac-ft".
+ *
+ * @method setFooter
+ * @param sFooter {String} HTML markup for results container footer.
+ */
+YAHOO.widget.AutoComplete.prototype.setFooter = function(sFooter) {
+ if(sFooter) {
+ if(this._oContainer._oContent._oFooter) {
+ this._oContainer._oContent._oFooter.innerHTML = sFooter;
+ this._oContainer._oContent._oFooter.style.display = "block";
+ }
+ }
+ else {
+ this._oContainer._oContent._oFooter.innerHTML = "";
+ this._oContainer._oContent._oFooter.style.display = "none";
+ }
+};
+
+/**
+ * Sets HTML markup for the results container body. This markup will be
+ * inserted within a <div> tag with a class of "yui-ac-bd".
+ *
+ * @method setBody
+ * @param sBody {String} HTML markup for results container body.
+ */
+YAHOO.widget.AutoComplete.prototype.setBody = function(sBody) {
+ if(sBody) {
+ if(this._oContainer._oContent._oBody) {
+ this._oContainer._oContent._oBody.innerHTML = sBody;
+ this._oContainer._oContent._oBody.style.display = "block";
+ this._oContainer._oContent.style.display = "block";
+ }
+ }
+ else {
+ this._oContainer._oContent._oBody.innerHTML = "";
+ this._oContainer._oContent.style.display = "none";
+ }
+ this._maxResultsDisplayed = 0;
+};
+
+/**
+ * Overridable method that converts a result item object into HTML markup
+ * for display. Return data values are accessible via the oResultItem object,
+ * and the key return value will always be oResultItem[0]. Markup will be
+ * displayed within <li> element tags in the container.
+ *
+ * @method formatResult
+ * @param oResultItem {Object} Result item representing one query result. Data is held in an array.
+ * @param sQuery {String} The current query string.
+ * @return {String} HTML markup of formatted result data.
+ */
+YAHOO.widget.AutoComplete.prototype.formatResult = function(oResultItem, sQuery) {
+ var sResult = oResultItem[0];
+ if(sResult) {
+ return sResult;
+ }
+ else {
+ return "";
+ }
+};
+
+/**
+ * Overridable method called before container expands allows implementers to access data
+ * and DOM elements.
+ *
+ * @method doBeforeExpandContainer
+ * @param oTextbox {HTMLElement} The text input box.
+ * @param oContainer {HTMLElement} The container element.
+ * @param sQuery {String} The query string.
+ * @param aResults {Object[]} An array of query results.
+ * @return {Boolean} Return true to continue expanding container, false to cancel the expand.
+ */
+YAHOO.widget.AutoComplete.prototype.doBeforeExpandContainer = function(oTextbox, oContainer, sQuery, aResults) {
+ return true;
+};
+
+/**
+ * Makes query request to the DataSource.
+ *
+ * @method sendQuery
+ * @param sQuery {String} Query string.
+ */
+YAHOO.widget.AutoComplete.prototype.sendQuery = function(sQuery) {
+ this._sendQuery(sQuery);
+};
+
+/**
+ * Overridable method gives implementers access to the query before it gets sent.
+ *
+ * @method doBeforeSendQuery
+ * @param sQuery {String} Query string.
+ * @return {String} Query string.
+ */
+YAHOO.widget.AutoComplete.prototype.doBeforeSendQuery = function(sQuery) {
+ return sQuery;
+};
+
+/**
+ * Nulls out the entire AutoComplete instance and related objects, removes attached
+ * event listeners, and clears out DOM elements inside the container. After
+ * calling this method, the instance reference should be expliclitly nulled by
+ * implementer, as in myDataTable = null. Use with caution!
+ *
+ * @method destroy
+ */
+YAHOO.widget.AutoComplete.prototype.destroy = function() {
+ var instanceName = this.toString();
+ var elInput = this._oTextbox;
+ var elContainer = this._oContainer;
+
+ // Unhook custom events
+ this.textboxFocusEvent.unsubscribe();
+ this.textboxKeyEvent.unsubscribe();
+ this.dataRequestEvent.unsubscribe();
+ this.dataReturnEvent.unsubscribe();
+ this.dataErrorEvent.unsubscribe();
+ this.containerExpandEvent.unsubscribe();
+ this.typeAheadEvent.unsubscribe();
+ this.itemMouseOverEvent.unsubscribe();
+ this.itemMouseOutEvent.unsubscribe();
+ this.itemArrowToEvent.unsubscribe();
+ this.itemArrowFromEvent.unsubscribe();
+ this.itemSelectEvent.unsubscribe();
+ this.unmatchedItemSelectEvent.unsubscribe();
+ this.selectionEnforceEvent.unsubscribe();
+ this.containerCollapseEvent.unsubscribe();
+ this.textboxBlurEvent.unsubscribe();
+
+ // Unhook DOM events
+ YAHOO.util.Event.purgeElement(elInput, true);
+ YAHOO.util.Event.purgeElement(elContainer, true);
+
+ // Remove DOM elements
+ elContainer.innerHTML = "";
+
+ // Null out objects
+ for(var key in this) {
+ if(this.hasOwnProperty(key)) {
+ this[key] = null;
+ }
+ }
+
+};
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Public events
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Fired when the input field receives focus.
+ *
+ * @event textboxFocusEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ */
+YAHOO.widget.AutoComplete.prototype.textboxFocusEvent = null;
+
+/**
+ * Fired when the input field receives key input.
+ *
+ * @event textboxKeyEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ * @param nKeycode {Number} The keycode number.
+ */
+YAHOO.widget.AutoComplete.prototype.textboxKeyEvent = null;
+
+/**
+ * Fired when the AutoComplete instance makes a query to the DataSource.
+ *
+ * @event dataRequestEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ * @param sQuery {String} The query string.
+ */
+YAHOO.widget.AutoComplete.prototype.dataRequestEvent = null;
+
+/**
+ * Fired when the AutoComplete instance receives query results from the data
+ * source.
+ *
+ * @event dataReturnEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ * @param sQuery {String} The query string.
+ * @param aResults {Object[]} Results array.
+ */
+YAHOO.widget.AutoComplete.prototype.dataReturnEvent = null;
+
+/**
+ * Fired when the AutoComplete instance does not receive query results from the
+ * DataSource due to an error.
+ *
+ * @event dataErrorEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ * @param sQuery {String} The query string.
+ */
+YAHOO.widget.AutoComplete.prototype.dataErrorEvent = null;
+
+/**
+ * Fired when the results container is expanded.
+ *
+ * @event containerExpandEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ */
+YAHOO.widget.AutoComplete.prototype.containerExpandEvent = null;
+
+/**
+ * Fired when the input field has been prefilled by the type-ahead
+ * feature.
+ *
+ * @event typeAheadEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ * @param sQuery {String} The query string.
+ * @param sPrefill {String} The prefill string.
+ */
+YAHOO.widget.AutoComplete.prototype.typeAheadEvent = null;
+
+/**
+ * Fired when result item has been moused over.
+ *
+ * @event itemMouseOverEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ * @param elItem {HTMLElement} The <li> element item moused to.
+ */
+YAHOO.widget.AutoComplete.prototype.itemMouseOverEvent = null;
+
+/**
+ * Fired when result item has been moused out.
+ *
+ * @event itemMouseOutEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ * @param elItem {HTMLElement} The <li> element item moused from.
+ */
+YAHOO.widget.AutoComplete.prototype.itemMouseOutEvent = null;
+
+/**
+ * Fired when result item has been arrowed to.
+ *
+ * @event itemArrowToEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ * @param elItem {HTMLElement} The <li> element item arrowed to.
+ */
+YAHOO.widget.AutoComplete.prototype.itemArrowToEvent = null;
+
+/**
+ * Fired when result item has been arrowed away from.
+ *
+ * @event itemArrowFromEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ * @param elItem {HTMLElement} The <li> element item arrowed from.
+ */
+YAHOO.widget.AutoComplete.prototype.itemArrowFromEvent = null;
+
+/**
+ * Fired when an item is selected via mouse click, ENTER key, or TAB key.
+ *
+ * @event itemSelectEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ * @param elItem {HTMLElement} The selected <li> element item.
+ * @param oData {Object} The data returned for the item, either as an object,
+ * or mapped from the schema into an array.
+ */
+YAHOO.widget.AutoComplete.prototype.itemSelectEvent = null;
+
+/**
+ * Fired when a user selection does not match any of the displayed result items.
+ * Note that this event may not behave as expected when delimiter characters
+ * have been defined.
+ *
+ * @event unmatchedItemSelectEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ * @param sQuery {String} The user-typed query string.
+ */
+YAHOO.widget.AutoComplete.prototype.unmatchedItemSelectEvent = null;
+
+/**
+ * Fired if forceSelection is enabled and the user's input has been cleared
+ * because it did not match one of the returned query results.
+ *
+ * @event selectionEnforceEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ */
+YAHOO.widget.AutoComplete.prototype.selectionEnforceEvent = null;
+
+/**
+ * Fired when the results container is collapsed.
+ *
+ * @event containerCollapseEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ */
+YAHOO.widget.AutoComplete.prototype.containerCollapseEvent = null;
+
+/**
+ * Fired when the input field loses focus.
+ *
+ * @event textboxBlurEvent
+ * @param oSelf {YAHOO.widget.AutoComplete} The AutoComplete instance.
+ */
+YAHOO.widget.AutoComplete.prototype.textboxBlurEvent = null;
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Private member variables
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Internal class variable to index multiple AutoComplete instances.
+ *
+ * @property _nIndex
+ * @type Number
+ * @default 0
+ * @private
+ */
+YAHOO.widget.AutoComplete._nIndex = 0;
+
+/**
+ * Name of AutoComplete instance.
+ *
+ * @property _sName
+ * @type String
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._sName = null;
+
+/**
+ * Text input field DOM element.
+ *
+ * @property _oTextbox
+ * @type HTMLElement
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._oTextbox = null;
+
+/**
+ * Whether or not the input field is currently in focus. If query results come back
+ * but the user has already moved on, do not proceed with auto complete behavior.
+ *
+ * @property _bFocused
+ * @type Boolean
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._bFocused = true;
+
+/**
+ * Animation instance for container expand/collapse.
+ *
+ * @property _oAnim
+ * @type Boolean
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._oAnim = null;
+
+/**
+ * Container DOM element.
+ *
+ * @property _oContainer
+ * @type HTMLElement
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._oContainer = null;
+
+/**
+ * Whether or not the results container is currently open.
+ *
+ * @property _bContainerOpen
+ * @type Boolean
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._bContainerOpen = false;
+
+/**
+ * Whether or not the mouse is currently over the results
+ * container. This is necessary in order to prevent clicks on container items
+ * from being text input field blur events.
+ *
+ * @property _bOverContainer
+ * @type Boolean
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._bOverContainer = false;
+
+/**
+ * Array of <li> elements references that contain query results within the
+ * results container.
+ *
+ * @property _aListItems
+ * @type HTMLElement[]
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._aListItems = null;
+
+/**
+ * Number of <li> elements currently displayed in results container.
+ *
+ * @property _nDisplayedItems
+ * @type Number
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._nDisplayedItems = 0;
+
+/**
+ * Internal count of <li> elements displayed and hidden in results container.
+ *
+ * @property _maxResultsDisplayed
+ * @type Number
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._maxResultsDisplayed = 0;
+
+/**
+ * Current query string
+ *
+ * @property _sCurQuery
+ * @type String
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._sCurQuery = null;
+
+/**
+ * Past queries this session (for saving delimited queries).
+ *
+ * @property _sSavedQuery
+ * @type String
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._sSavedQuery = null;
+
+/**
+ * Pointer to the currently highlighted <li> element in the container.
+ *
+ * @property _oCurItem
+ * @type HTMLElement
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._oCurItem = null;
+
+/**
+ * Whether or not an item has been selected since the container was populated
+ * with results. Reset to false by _populateList, and set to true when item is
+ * selected.
+ *
+ * @property _bItemSelected
+ * @type Boolean
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._bItemSelected = false;
+
+/**
+ * Key code of the last key pressed in textbox.
+ *
+ * @property _nKeyCode
+ * @type Number
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._nKeyCode = null;
+
+/**
+ * Delay timeout ID.
+ *
+ * @property _nDelayID
+ * @type Number
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._nDelayID = -1;
+
+/**
+ * Src to iFrame used when useIFrame = true. Supports implementations over SSL
+ * as well.
+ *
+ * @property _iFrameSrc
+ * @type String
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._iFrameSrc = "javascript:false;";
+
+/**
+ * For users typing via certain IMEs, queries must be triggered by intervals,
+ * since key events yet supported across all browsers for all IMEs.
+ *
+ * @property _queryInterval
+ * @type Object
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._queryInterval = null;
+
+/**
+ * Internal tracker to last known textbox value, used to determine whether or not
+ * to trigger a query via interval for certain IME users.
+ *
+ * @event _sLastTextboxValue
+ * @type String
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._sLastTextboxValue = null;
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Private methods
+//
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Updates and validates latest public config properties.
+ *
+ * @method __initProps
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._initProps = function() {
+ // Correct any invalid values
+ var minQueryLength = this.minQueryLength;
+ if(!YAHOO.lang.isNumber(minQueryLength)) {
+ this.minQueryLength = 1;
+ }
+ var maxResultsDisplayed = this.maxResultsDisplayed;
+ if(!YAHOO.lang.isNumber(maxResultsDisplayed) || (maxResultsDisplayed < 1)) {
+ this.maxResultsDisplayed = 10;
+ }
+ var queryDelay = this.queryDelay;
+ if(!YAHOO.lang.isNumber(queryDelay) || (queryDelay < 0)) {
+ this.queryDelay = 0.2;
+ }
+ var delimChar = this.delimChar;
+ if(YAHOO.lang.isString(delimChar)) {
+ this.delimChar = [delimChar];
+ }
+ else if(!YAHOO.lang.isArray(delimChar)) {
+ this.delimChar = null;
+ }
+ var animSpeed = this.animSpeed;
+ if((this.animHoriz || this.animVert) && YAHOO.util.Anim) {
+ if(!YAHOO.lang.isNumber(animSpeed) || (animSpeed < 0)) {
+ this.animSpeed = 0.3;
+ }
+ if(!this._oAnim ) {
+ this._oAnim = new YAHOO.util.Anim(this._oContainer._oContent, {}, this.animSpeed);
+ }
+ else {
+ this._oAnim.duration = this.animSpeed;
+ }
+ }
+ if(this.forceSelection && delimChar) {
+ }
+};
+
+/**
+ * Initializes the results container helpers if they are enabled and do
+ * not exist
+ *
+ * @method _initContainerHelpers
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._initContainerHelpers = function() {
+ if(this.useShadow && !this._oContainer._oShadow) {
+ var oShadow = document.createElement("div");
+ oShadow.className = "yui-ac-shadow";
+ this._oContainer._oShadow = this._oContainer.appendChild(oShadow);
+ }
+ if(this.useIFrame && !this._oContainer._oIFrame) {
+ var oIFrame = document.createElement("iframe");
+ oIFrame.src = this._iFrameSrc;
+ oIFrame.frameBorder = 0;
+ oIFrame.scrolling = "no";
+ oIFrame.style.position = "absolute";
+ oIFrame.style.width = "100%";
+ oIFrame.style.height = "100%";
+ oIFrame.tabIndex = -1;
+ this._oContainer._oIFrame = this._oContainer.appendChild(oIFrame);
+ }
+};
+
+/**
+ * Initializes the results container once at object creation
+ *
+ * @method _initContainer
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._initContainer = function() {
+ YAHOO.util.Dom.addClass(this._oContainer, "yui-ac-container");
+
+ if(!this._oContainer._oContent) {
+ // The oContent div helps size the iframe and shadow properly
+ var oContent = document.createElement("div");
+ oContent.className = "yui-ac-content";
+ oContent.style.display = "none";
+ this._oContainer._oContent = this._oContainer.appendChild(oContent);
+
+ var oHeader = document.createElement("div");
+ oHeader.className = "yui-ac-hd";
+ oHeader.style.display = "none";
+ this._oContainer._oContent._oHeader = this._oContainer._oContent.appendChild(oHeader);
+
+ var oBody = document.createElement("div");
+ oBody.className = "yui-ac-bd";
+ this._oContainer._oContent._oBody = this._oContainer._oContent.appendChild(oBody);
+
+ var oFooter = document.createElement("div");
+ oFooter.className = "yui-ac-ft";
+ oFooter.style.display = "none";
+ this._oContainer._oContent._oFooter = this._oContainer._oContent.appendChild(oFooter);
+ }
+ else {
+ }
+};
+
+/**
+ * Clears out contents of container body and creates up to
+ * YAHOO.widget.AutoComplete#maxResultsDisplayed <li> elements in an
+ * <ul> element.
+ *
+ * @method _initList
+ * @private
+ */
+YAHOO.widget.AutoComplete.prototype._initList = function() {
+ this._aListItems = [];
+ while(this._oContainer._oContent._oBody.hasChildNodes()) {
+ var oldListItems = this.getListItems();
+ if(oldListItems) {
+ for(var oldi = oldListItems.length-1; oldi >= 0; oldi--) {
+ oldListItems[oldi] = null;
+ }
+ }
+ this._oContainer._oContent._oBody.innerHTML = "";
+ }
+
+ var oList = document.createElement("ul");
+ oList = this._oContainer._oContent._oBody.appendChild(oList);
+ for(var i=0; i
vHA{v>|sAOH9VUq1Wx=i{&V_yqRn;}h7QkI%<-7fW_4(VkZ}j!+ z*W3A4Q2+7AAN1S*{zm`&=RfJ^|M;1H`st@nDf0RFlp-IAiA&;HZ{w3b`^>wEDMb|H zcWo7$YKZ}V2ee&$PJPY#JEPa?S+a1^5~E@1g&W3xW#A&D$3t?w{=G7DSsA*r_4-%Y z`s |z_E&o@}NSYWQ_Q@Xg&3d|Ou zW&0J1V(Gn21@Ok$tl7ApUjkd|>Gf{~b_Mkc=*rd-*uVe&`%Pc}`s=SZK(DV?u$Q+4 zw*GP77{rl7z$bi8N=|4%dgnzQK{K@X9=v7^ctch^o|!a57b!8M)R5bd+^DZB*f)2h zXi(pP{pA7dU4m@DUcq}~?BDN ws-MON*}ARvB9}^yYkUl^^T(x&iy;x&pXVP)oMH zG4>x1DYCNm`n+yi_f;$KX~32?&=bL&3KR!`?Ad)hXq&kbL*FF~DPi6Lgmfs6L(D+0 zND0vbo2}RNnI*FeDS>21aabAxyE61@^}W7lp{p^l|7 WGkjF52;svSZcA8su7jAYiZ3LweO3*sn5! zSD>!n=?3mEUmKmhK6d%BxhCszn^Za-*h3D(8SY6kiNgaaXQX90(eY3K$c_zH0Bie! zBHJ2UyB-p(7Bef5YbLIag?2iM6)3&}lecGR>8-$C0bN;o1$K2uG_Y5@@20Q6ep|mt z*;#x0tH7=ryq?1awzczFEUg2jL1spUI~;shFFVJMWT3#Y2!p-uSjIu7V%f`fjs-Ap z^O>)I7X@&(g2WB97X@e*&x;21dj4(4t<5d)+aC4x2CzHGU+%zOzu(5F>;Cn4UV*tj zZ$V@8yjpp$fL&R8ZO0o+m)B>04HkLJJo2A^{CRu7ZvXb}emug@Fk>GfXvJPL@_-^q z01_^oksZ=)$m(QUUX;~R(;!CYEsBZDLoIZEb1~!wBOR^fg#bCy%lortW+6TF_FY@w z &6w46@B~bZ!ZnoFU!@$z>E}`k{^oUMnFB{O&3px81f^O z51QS()3a{J?CEH!+pUC&YHNa5>Eykw2XY`qJYY8f?~*Z#9Wbu!EB)#UYzgYy^IbES z3h4T`vT|kq^`Z574Oq_r_AIDJ0c{44L7>OU1PLbsz#Hu@vK`wj512a*;t0UHoeBa? zk6L-sMY|p{s1$`s7O*7~*BX(c{b~v9`g)Thw{{({Us@kq|5b2nV9S~lBm$l;MJBR- zSsN}46ctdtgi9+$xa(0`+j0;b5@@3(htB#Thl4>@u&CE;xE$KTV_CN$*;#_wksf7p zH>9;!8MRBwYyjM)2y#PKP>c6P+kZ7HSsw`0kDr;x8GBY&%c%cEP&e$K0;+W$c6Ga3 z-f4l|(a#9Tgjm>eL6n{@$mNioz>^;m >!~orEy~tWx3~ zOIPqpfCGkR(H?r%tE(Zjv@CMcj9p#<%|dfa05{ue+I>B?x*{YBs9Lr@sIdpzjet7C z(j3YXHqYbA7G>{gy$rQE(#f8iq4wzn3QC-$G_@y1j3806bjQvT*z2!Z!CPeq$}$Qt zKiFGHksaJG`+EfLE_ox5tq+;W^uV3*u}1^OjK^IhVL9wdjz0DhDyAkTJeq`!B#Pcg zhP)_7_7G+P^?uQ&deV!Q8k_%Iz3u(DO+Od8Pl9$mZo9rWlUfCjjYrK(<6~b$ZT0S1 zS3O;Ux%tu+)a&a-3?R6yAif2U*Sn8h->)oR%UIXfo6X1d@8#Re{rTQ*_p!In<@$MS zi0rd9cH9YgPHQJ6i50k+-%kgas7Aw(CeRhp9m n RTLOFML>EN7Z(g?zUW!g!Hg9HcE;V1EAh^ef|OhyG~Aswq6=s?q4)8Rd863y;SWj zSY7I8OIMc~2-fXFMwH5^wNOOZdAr8m>OGbX*q5qptG2GXdan<;-u3UQn|F kfk?i04PcV*!JYNDjCSSa=A`)xBsyFVf!o zcEj3Z $<&e+vLHr0ak9iY3#aPde)nR zA-C1eupV=%_1K$crutm{Xra05zTK<7Zd!fooV! f@ zjN&Brbhe|X^)|DOW?&^TSXp b-J8|rPncYZ6g>MN)cr9$&910<@S`i zkQl{a Qy;-2thth!_d8NT%MNOLaRo zCuFG|O0c z; &U9i--BvN-HZ`yK2WHwUjV^zvpBugv*x@U8x z({-K67HqPx8;i~;!e{od_khhIU{6k}7 aYNF7KD;K_3|HlOG;X68|WyQts~R_Ey{93p$q#vLD)BBQJf!D rZ2nhQiG*TS=m|!i=sNPks~sAWNwo((+QeE z#&&JLOxr| mJq#Wvwe^U!h^q1|mxGNOYb!WJG9t^^ z7DMZKI!Lj#PN|b)?X9WqMFoppm0nG~SG!I#I0zVZ`DqtCYId&sD^t_0?Sst)+mcAN zUvx@c>}O&7-Gj-m-xe7Z%Keh9Wq_!IMZnTSjO}I%4>EJ4jWgw@5p#Q)LYiP`6fiQd z^$ Ve27n-UX6~u~jBf+k#TtGMMS9dA;lIYZHi)4l30jO7QF&T(%QhouWT^ zynfVSX2S`ZnZPko&ykt#z}4MKa~F_k(OQ~~ 0%y}dDP!_F!}v#hlq(gM^>trIkoxdHpiniE-2)#zvMuOXYZ z@JTauPGdhoUrzx&2nsFzYz-P9uOq1X=#HU5Icmr1+m7`=TI)3+i)$@z)KfDUFrb|e zMgs?s-Dk1u&Z5gh$7`mpZ50a_t)I09Z_Y2;>j48bSd6fih|8uJJL^RwJv?I9MOj1S zdLU4vQqm5rNLt8_pn<5I7Sa+!IUbUAt>rFsNh$%|eUVd;2@Sq8wo0jAKq5vm`3kt3 z=3N~Rsk>_m9Wl3@TSRJXnS>Dm>Ye59TAeks*WX>pFw%@&JCaPu7;eaM#?H163Yxqm ziO1X{Y;1uY1)XquF~r!S{e#6HD+K5cU}@)0U+V%VRlwA-9$@K^y(J@8pB$}LpzaKz z#@63&zauQRhE`R26msV$Z^{OZT{Yqz~|MKp5@rfxL0c0%O-=ufe>>^uw{kcP3JqOoTg z+bjHcEIkgV+Xd`yFtrAF15DLXpaVjTJA-wc7@{Rz2CcE1z_Vdg<2a#%A@fTHRary$ zx^qPy{_{TkOX+1&?B6kR^}s#nLpfB<^+cJ+!)2=@cHV%N&a^i{jchr@!b(pU4J^^d zO17|ADFAxZ`eGm^1_e)qtT@Ery0EFfmaLv(=|+cN8% Rh$tyRdxLGaieqrB*@ zL#S!P*wWof-%8)Qb4e_g_L$k4z#SDq0S(+`G&PH>PTX|r+7{K~Ti3eID{{XsPtw=} z0bxo@dr1_)!UUvA#=f(B>9TP$k}MsN5fF`Yl1{Sda#ZD%T;wD _* zrR(uRL+ilL+JnOY9wm9-+$udbbEn?0(LOj>$r_hai=7gPtL*qQi|ZPXQR$29gp zkeHn=VZ^I8vx(Xr3C^*P2@HVj>St Oa=0bwJ{*liQX>WaLOu?0&v$0Jsj zZGj8Z3^mjKv_3!Zt()Tzw)7pqx@$jMW2a1WYelRebzTA;ZOaH? FNSYztt>#^x{9&}1iB)CrBSv~c-4}XJO4VNu`kuHu8xMR`H+r@WMy3&f@(oD zV-IQUBfyT>ItFjOyTqV&0>y3{a4&QaiC7u|8A%bSB@^2+!6JcGyO0!VK!4kJZHk>1 znNk-3?ybKKaCMsmp|tx{qUMoqf7#pY9YA&lk%ZZwrPU|5J9327sG0NZzDc%Dr%025 zqCIwz@d$Rqv$k9j0=G{sMPM7F99180rOMfIOX{5lSZj~hBGDq+p)@>{DBd>D(nB2@ zz1GjEM9YEo=n!M0*-1Ij*Dw%_*w@JncJ0>&i~KC^eP`rF2U)|W{@J)VA!A!|1Qar* z*jjIw0i|Z_ef0$;GbCeMEUibRZ?mK78J{kaZ6ym=Hg~jl6AQ>=HEZjAwYpG94|(1j zW6N|q-Bz=DfN-cDdxU)xSwru&TsGqlj#fXTK+$96&LKfghSJ8J%i;Ld466Ic7^EwK zuHB7AWfjo^qGj`m7f~6w&sVL%jGPcDOR5_pYDq3q8i?$oc2L&4#06v<$EoipXGE zNe;3`$sh*=YVSyj90(jorckgu1=C1Ik&MB+)?!7BTn*jE=l~4l2WgpM$d0D|LtS8) zRn(GZ-@YY{fb$?L^I#iGmZ(nqu775i>3x}i}tq++VK{F93+C@%G zValBBMTkrmmI0*)W{`~}Bamck?hjo=AIT2H;zr=Ov-i5ctc-b|dO^D>y);olB4TMB zC|XiP28;VR$Cqm(8I)rp >kC5c^snH_B6o0E;X OGDq4mJAaYlw*nfA;o{!0X+V9n+`Q =20nuSDlk0 zdgAwJ=+@w{od%FmxoH4qYj6Ry<_6@487vZ>V-HCY_L4Ek;gHVAHhozyMcV2<0vW}W z{7{x50y!yd{Q&F2azst1q4hSTulr-+Tp`h%A*?MwAJ_+zEJ?ObOB4spn(@)R)X>2& zf=kQ!usm=ppk%N<3>mc_`qq7y4os*fgh3{fE%yV-ALNecQ9724spY3iR-n%;mLmd6 zE873Ja6=*qeEPg1Cyb7X=BtLlGg9Z#Gl5!Z*j^VXfzjv;q(@hx2?D~5Jdw7U_P2|X zXdzX>N67uq%&cTd!^|?#(m5pyv#$2jQbRr?MwiJL*{)@0>KMC94OKA3K#t72B8eGQ zho%5VV9w47VpMu*-A$wNL!A%GiU-iw7fW|M05Jh}mf1p5fbI&HM3Gac! nzy#;7ldzS7-AeZ2l+khNtzzZzBJg{dp zb~=mbyw7Y0_M9bK!fawT={_?Rk|qSC2C5A~wb;**BF6L!W`$oc0wS#Y$YvKE zd17j5OQIOlnsscaN0*DLL$AtD>*~b_1WCz@B5FbwppQe4OMr?AbDH21$qb#P1Oo$h z`Jrt*5Px-YL qh0lp ~&%y-E2w y05*1A5Qnk%V HvfhCyH zC=hpY#SvhGb9KOVK)%&Y^RnGaB(p=7)*V4_v-Gn7YcXoCFRC_(RZ!S6b8Cfqwbxbv zIj6A?%91d9Xt1|+Of }XTw3;!B44<0% znWwtB|2&P4+P1NSp^2?K9QLkzeJ!ClT9SHNtqA8<27~p{mMh}O4{Na_#LV9CBRDWv z0#Jih47q~#9>@&J3tPNlQHDPawRfPgO~%%`y0s}74Z!7ym6a4BPiw1+ygEt-x+0)F zb#IHD5sR_Ifd)sVNL0&_vGT}Xc+?%csAYn*+Q^s#o56yZ9IR5MSuC&vo8!1&Bi#ab zqlFVvgjB+%|65pFi}1B2Pjo6A6*5T>N(ltKX>(B|m0NHnM|lj~Jr~#sRwic ^FJ1-T`NnK`Sm1G_Iw#v}#|WQK-WlSrb8+AYnEC!}nrHzOofG;mq1M+5E# zF0gem)7`OLLSQ3s1BPWaokAx|WSl1)5O_Z6ba(|#QZlH~GNb{nI{^wDZ8k5vpDICX zWG18F@zH)Vr5-va5qopI{6+-mtjzF^x9ma0=x8jY25H&De=xXL-ukp$GA4EJ?nEl- zQ?{Ut(U?OkHjw~DK_d!wv%y_+E+_VTv|H%t Hi!TN=lLhqd)(n^$wIw5XbX}6n z`wVcC!1m9<6ftn4w>5(+5MMNCf!6LiiE4Whw^Y6wQ<}v(+fAE42iREv2fEtS=E3Z# z5uh!P+mj_oiZsVqH6tECPhe|8cAs!~*a#jwEAVkGN;B?DgH ?WY8pQ;Rv=0J-zk#a?d`U!O06|i&_pC0~% zs`H#tCai{-F-YNecP#Zgvjn-m4!WXHOEei+>hHL2yn0o89qjDD)2se_dhnP@n|qag zq7K&uG4;zpyE+``=ZwZifMAQ|GqM9DW;}m7&*Dql$%OnMBsqMELWGhrj(%q3(o|;F zm3fVvSr`q@)mGl(= b*|dsgt6oOp6bR~u`cPXan(Y?Gyewo|O!dC`^>(LOeEI&2wo%g8#w=7X*X=dws6C9Z=RM)P&R>XeWcW*}*S9|dBO z#E4vw6hn)0PQ$k*rB~U=N_Ki%gBgJqonHde7} bdbG3X-bM%8BGh!5%`glVkCChN}~g3BW$M}kFK2>ONsVtFvIPh8-j|! zP`X_o->~@7R1I1~_EP)g01;!(lkT_dq{zVuDO3BhOrBuw4xoebW0 }oV0S%e1I!G@ zS(<=GAhvWhk}V>&+|*Y)b=L0-?9AW-BY4bAxj;rqE3At~Ky}WW0=SHa2iUo(K{h79 zoT?DC0FAa;NfwZ-@hm`!>^_u$QRi@h3rq?mNydOZGE=G{H&Tn{xCKvROl{R@LvI^( zB**_YItF)xh#kMXDH{M%hCO)$0MF=nR&EvuhTU2b Ml1Q!wJQMKqreW=pfU8fHKC=;%8?Wy zW7a2QbPj5(!N Ijz>u$oivp7#D1d0NoTZEjF9$;1MK(IMCCG zx#4mA491Rzkwx{-qhlU*CEw$JHpIZDM;^H+`-KBjpLq;>6vPe?U29I&lkB=&7dC0T zkL-zu_B9&G9&wIzIR`R>lTzb(!J|=U9R-`&X&KV;#bWceXp7Ay_biXxa!>Y)!Gw|^ zRzPXZRpL4+;23w5y4e9ilxJLLJ(vJZZMK~=hmp~M@T|Zw#n?zU2c03D=h^@)oM4jt zsnM*VVdlh?mfl95dUI}PN`okEK2kL0v3MjOvN9u+K?D|Fd;ZM@K@!6VE-4WpdRjVS z`e9&)&ImiJgbn!Hp)7H-n8gVs%>dH{es0F7IG{|}g8|!fpqD#of!gVkQq&1hX3_GZ z_3_C-5c<`Ii&6%I$Pr){sa-I;}6YGm*O`nF^QD6H`R=aiQ0s=u4 zTza2}Kqg;X`>lWu)lt7?a43FWI*YMWGq5^yjaX|lW+X^@MFS{LxLd9dEvFY2K> Mgp?OA;_w>u?86cFL0 zl^u|i`zRqxP>`3><7&bkb1pweD>LXk79h=KJ3K9)2o~)&cO%)= 1g}F zezt&oXXmE4tL+eryM}%D`&caP0o?<5GC3n&x1^uY*oC}O)4&Ny&lDB0x4RvToOh&) zr$x!r&{le0YierKcvjGr`MhwQN2@c|(vL0gU66c2WH&Pd(};GS(b>6m9(kOx2U$5v z@|0jSjm6$YCkMz`nb*JIW&u`KPe-hr@wz*AM^1+0bSRGYKYlwV;Do^T4dXIFHa6J1 z)5oZS&(qlha)lDCehAo6iE0Y?R>0YuTY#ACrKQ=>*;xRyl>~C^y1g2X{k0@ro$W{? zwvM-s$pDz_i65J#OcG%Bz!;8vLC%YD?so%r2z*0VV~N1-I_u_z2H-X|cn0LCMglSM zk_53Mlgw>~!{KUf$8{>kWIt>;QEu|Y(%7QI@3#!A20N#nd+-LJ&->U1fju>|lm~u` zMVtiAa2v1VIPJr2mh ANV{@gxO%YTY*rpumms_z`G6*Nz z@vOfDix06fV0>$B$utX(vNk(w-AuBv%gzxK2aX60xFRL6_ufZ}bZ!6>Cu>P8z_~%E zH<=FdoV||Vfm~{K063R%M}Vz=BS&@=XYTJbm>oS$PO`<-#+Jr*?hJ)Bt`PObM)kSZ8H|g?k<=*@;I^`DUND+wP){p6|2G-|G5*}WiV*^B=KId- z;G@Tf8k-jmEBQtPM31$T_1&(cVlBrp3fd&t$w|wntNq&V-IN5&It_E4@Imd|Kl?!< zfuUTc3zAHObCRvo`Z^&~rld%Ai4Mrxfz*f=J2kV1&iOMk#tIN2lVt AiQla>BXSp(9++bG|?7bP^y}FKW_JBeC3hEjw3ZfkQ=ooGj?Ibj8S#saVB*- z(a3wWYzYE&Mi#gdr2U=EcHfQNhXCmfHXuAzgA3lI-REUKqmoum9Ai$?F!CHEmD0~z zqF9XWl^>uxWn#1olpsOM+>$6(x}0oQrzmhdPl^o4kuac)((nmM5;-1}WlGssZT7om z&`$%t(;*_@Mk^abP-k3?V4{ACM!F=WN2VN=j44B&BSj(rM^iD32B1+VaO%L_i|*Y4 zY*t?&sLqff4*OA7!ZgPku1D6@utwCJ{WyPAO_H%kf$k=8h&W*C==PQYs~c!Wj2#U! zFn*)-F |TRRDm+6Dq3p;2+49V@!K-(?{xPPPoF1to z$x0H=yC}mJp~L3**vIz?8MOj$Qro8;i>#|LTTB(5kG`YF^a7^fmu>C|j@F))CsXdp z2*c0Hi~~%Z-PZrVI2D6HRvrQW5r+0;$An*<4jfMfb~H xr6qu(0VIU{6XB%fdUt+7GKM$ZQ7d z+*G-q^3AhAkG6UGeKe43wk=Z~@{qi^8g4~a7kh3;^x0=fpYx @Ed05BU<|1?Y@y9GNH9cw6|*ydMtC~bu#Ur zW^%V9`U#e%tuqzfu>F?#-9|w@YT*rZk~0~*#cRn2ktxkSAW3Ed%?xnei<~%b>YUvy zQB0iuZb_Yd#{V~D+^D7_lR5mbj7|2~MWfx0!AN^YIy$^?2|Zp0fzVvE@~A_$*cnVA zMw#g)ypC q2vdiFXhx6E0)LqSNUxhI zVW-W=i}3ha-8{;~vjY#ZReptXbC!%U_7TS<<#h*49XcddFz8&+VQpzLHIvtFG}ukc ztmOZTJf~!aiH*rPc@pR;9nD7=JCGdVDq<116>`I1b5xT@tC-Dp5?2cO>`Zi$Oc?Cc z(=Nq~W}cBGvz~W!9>~};tnKM?az?r*E|O;jW_03+7UVQwICongNlHz~C-}PvCLZYw zR_qcowKa`$hP?y!^;8FkQe+UQgE~86O1M%&)(VWI%!rlfG4Y`wbreh^=ZP%0M8brg z;dU5oO-Jt}hg^{e*c8v~l#?WMUt?_~z?@+B^hF06nQ2~sj#!{Wam0bn8hwA4g(GPp zre)|l%aSOn8U*|Kj6IRW895IgvD0rHxqx+pbT_1PaWqhU>-R^eY8dPASqqQe_aWKy zbYM@gdG;KmUU;{SlVCmuz zWeg767aWXd_QWP{P+&9)h)*5Dlk z!wjP}|BcZa_lfvoG{C0G|7UodwcV@AJHfqqjue^oy9ceoY@nI}^Q?wH6;v~TLW`Kr zXy79tKO?#FmJgG$lhVOuZE*2ZjE(?$gwY2R3`cIyVJA+KNNfEK$gcDqZHkf0VMdOe zm>Iv^1hyK8I~oYO9l-Us@yB|M4gsFj<5}h%0z Yagcd)ZD(q&^V&IE^>F~$_5al^!k4%1 zJn&9?;+~W-ewCt^;T`NeM?jwi+M(lXmEnk#nsGM*Cu2AujAFmD9g=3^cRpazg{)2P zn8^slCiA+?NR(!+NuZ1{S-MkuZF%6gv9{}!d*XLgzfH?4N!?6zAmK9F0seNx-8fs1 zXDviP*JZL(3;|+<;j`Pb3|s~}q7~H5vaL6j*f~=voG(jG12zp0Lhxjb*#NQD5r-zc z8>nBrJig*G4d%vKNVex5w1@9RM?K}U<_D?ky?<0-r@?(R`7 p`ot`?H2s#>^rZ4^=FlN|z7;uANn-1>IN}N2Cr3Zk`BP^T*Bp+h=GXQ?n zq4@~xH~`i&0DT(!&VYJ`-9wFiKyS}TnG}0}0I747b%*zVRH^Xn>CK)NJe~mTrv#t_ zcabwXLIlFM0P-}L$AMa=dX$_}d`x3eTZ?RLY=?o8@$7*!k>srvp9PW)jSU?U;_9 z^Qe?K+IdESZGby%u+M1qd$#NeEItDAvw?m9$ftmLQX9{>7*l{gdPf)rHlFHin8at8 zf231nfja`;X`qjQ{TPtT2$-J+=#L;%GCuZM%zFe-GoTq|EqXG0J-rjFA!) YD@D%G+(%jVtWGl1QAU{d2%u&<<*Zbo86f4@e+txxnf@rS&tmU0L4FkI zvtVzu0H1hQIRnIJ0C_feJe#F6_kc5iO=q3M`0__VKFhGvjLYYO_Y5XJ1jJ{7c^Yep zHW&J!52nA`qtNH$?S3o4*oSLk&tTSPNrW?X?<5;9naK^$U}fVxx$Ap| $u2f@bg3gWkxHK%Fp4+8RMf$}i0&g=*$e&(C#qxaD0ZvpHl zv+I 6`Mm!BUmxWOUFGBaUp^pXA9e5eL-vl3cNcl?@AS6vgTG5D@?CMq zTi=Sx2mN33JO2+a9~-FUJu&myl|RHap8Fr(82g7GfB2M~pO3%P F!NeM|wNi2*CXBkqA8W@y2&T>3yVv%g*IK)!uk)}MmImzx& dTmu6$gLVMJ@>Lg{{{wY0c)I$ztaD0e0szdFI V6h6*nD|EKy_G>T#efd;-TcIEZRG=3KG$^eF2Q{FAMV4*KY}f{qh`QSV zerz}DmTbC_Y|fEr#w0P0=t#CW_qsTZ+oCcxM!nmzgkYx0y)F9Qv+IxZJb6w|&dGC5 zaz;Z<^@~z7QUPF5prJObvq_J$hJ`xXo5wcl^n8zhtsmg}%|C)|K3%8x)wT6C_ipWb zw=>oa{%CJkw=1x%v!^@U-5DL&ajAP1z;mggH7#{nz-rJ$faL=7qA45La$rShQh@N! zu%sTCvXE1t{K=eCf%*<$g}~m_lTbYf%#DuhOcn)}oyL+UY!^T)N0SIlg&q}p70^Bg z#v`A*ftBD@6%H#L*N*0!pas#aR&fdVH33r!@M?%V4H}RB^=Q5i+Cy}fVbYD}B4EjH z?P{0x>Acg883%O a|AzJ?B#N%FVmw`fKNY;g=C3&BXB;Z@Kam~dpydSE;!) KoPs+}MGZ#35TL&dgu1t8ZJCOzunMR5)=3wB`6<04>Wd>f EGNt=fUO7S z2UdorB;0d<%}W2%DB8|}rj&7Bd@G600GiIAl}DfA(T~fGjEtPSpMM-+!R dW!t0gq&tGw>1(Ck!bL+QCwfHM zFSfg6C%J1(_sXrL%2?h=Gs#tB`jY5~^n-B4=5yiEV7#4=o|0Az*Nu@}Ih_Oz$pHOG zC`&0{EO(L#V`PcEmKepS-spt1Gd{>irzEF%o|nyJB4uhF{YR*@P1evnVor$^%6<|| znF`WIVUCMc(=ZuIiR8<#kjLU}uWTnbQf7F06KORJtfO9XHgzgQe;2kI(+lL6NRM$K zObK})t}2X5(vY~u9{XN8DGugG$E0mSB^Mi(>V!Gl YlD8E#--~|}G|8JkdYdiH90fD-v+HZu-d;bSJ8}W_+ literal 0 HcmV?d00001 diff --git a/lib/yui/colorpicker/assets/skins/sam/picker_mask.png b/lib/yui/colorpicker/assets/skins/sam/picker_mask.png new file mode 100755 index 0000000000000000000000000000000000000000..f8d91932b376af76ccfac030c12eb9fec7ee4c63 GIT binary patch literal 12174 zcmV;9FLBU`P) _6|b&%Dfjpyv}j54LBIXFZqhE-_t{2r7|?ya1%Cs>h{sswI&ir4|Fh z$jAgxe*E!Arq9Rc<1hC3+xpMn|Ni&&sREphKX2dqw+!rCg4f vHA{v>|sAOH9VUq1Wx=i{&V_yqRn;}h7QkI%<-7fW_4(VkZ}j!+ z*W3A4Q2+7AAN1S*{zm`&=RfJ^|M;1H`st@nDf0RFlp-IAiA&;HZ{w3b`^>wEDMb|H zcWo7$YKZ}V2ee&$PJPY#JEPa?S+a1^5~E@1g&W3xW#A&D$3t?w{=G7DSsA*r_4-%Y z`s |z_E&o@}NSYWQ_Q@Xg&3d|Ou zW&0J1V(Gn21@Ok$tl7ApUjkd|>Gf{~b_Mkc=*rd-*uVe&`%Pc}`s=SZK(DV?u$Q+4 zw*GP77{rl7z$bi8N=|4%dgnzQK{K@X9=v7^ctch^o|!a57b!8M)R5bd+^DZB*f)2h zXi(pP{pA7dU4m@DUcq}~?BDN ws-MON*}ARvB9}^yYkUl^^T(x&iy;x&pXVP)oMH zG4>x1DYCNm`n+yi_f;$KX~32?&=bL&3KR!`?Ad)hXq&kbL*FF~DPi6Lgmfs6L(D+0 zND0vbo2}RNnI*FeDS>21aabAxyE61@^}W7lp{p^l|7 WGkjF52;svSZcA8su7jAYiZ3LweO3*sn5! zSD>!n=?3mEUmKmhK6d%BxhCszn^Za-*h3D(8SY6kiNgaaXQX90(eY3K$c_zH0Bie! zBHJ2UyB-p(7Bef5YbLIag?2iM6)3&}lecGR>8-$C0bN;o1$K2uG_Y5@@20Q6ep|mt z*;#x0tH7=ryq?1awzczFEUg2jL1spUI~;shFFVJMWT3#Y2!p-uSjIu7V%f`fjs-Ap z^O>)I7X@&(g2WB97X@e*&x;21dj4(4t<5d)+aC4x2CzHGU+%zOzu(5F>;Cn4UV*tj zZ$V@8yjpp$fL&R8ZO0o+m)B>04HkLJJo2A^{CRu7ZvXb}emug@Fk>GfXvJPL@_-^q z01_^oksZ=)$m(QUUX;~R(;!CYEsBZDLoIZEb1~!wBOR^fg#bCy%lortW+6TF_FY@w z &6w46@B~bZ!ZnoFU!@$z>E}`k{^oUMnFB{O&3px81f^O z51QS()3a{J?CEH!+pUC&YHNa5>Eykw2XY`qJYY8f?~*Z#9Wbu!EB)#UYzgYy^IbES z3h4T`vT|kq^`Z574Oq_r_AIDJ0c{44L7>OU1PLbsz#Hu@vK`wj512a*;t0UHoeBa? zk6L-sMY|p{s1$`s7O*7~*BX(c{b~v9`g)Thw{{({Us@kq|5b2nV9S~lBm$l;MJBR- zSsN}46ctdtgi9+$xa(0`+j0;b5@@3(htB#Thl4>@u&CE;xE$KTV_CN$*;#_wksf7p zH>9;!8MRBwYyjM)2y#PKP>c6P+kZ7HSsw`0kDr;x8GBY&%c%cEP&e$K0;+W$c6Ga3 z-f4l|(a#9Tgjm>eL6n{@$mNioz>^;m >!~orEy~tWx3~ zOIPqpfCGkR(H?r%tE(Zjv@CMcj9p#<%|dfa05{ue+I>B?x*{YBs9Lr@sIdpzjet7C z(j3YXHqYbA7G>{gy$rQE(#f8iq4wzn3QC-$G_@y1j3806bjQvT*z2!Z!CPeq$}$Qt zKiFGHksaJG`+EfLE_ox5tq+;W^uV3*u}1^OjK^IhVL9wdjz0DhDyAkTJeq`!B#Pcg zhP)_7_7G+P^?uQ&deV!Q8k_%Iz3u(DO+Od8Pl9$mZo9rWlUfCjjYrK(<6~b$ZT0S1 zS3O;Ux%tu+)a&a-3?R6yAif2U*Sn8h->)oR%UIXfo6X1d@8#Re{rTQ*_p!In<@$MS zi0rd9cH9YgPHQJ6i50k+-%kgas7Aw(CeRhp9m n RTLOFML>EN7Z(g?zUW!g!Hg9HcE;V1EAh^ef|OhyG~Aswq6=s?q4)8Rd863y;SWj zSY7I8OIMc~2-fXFMwH5^wNOOZdAr8m>OGbX*q5qptG2GXdan<;-u3UQn|F kfk?i04PcV*!JYNDjCSSa=A`)xBsyFVf!o zcEj3Z $<&e+vLHr0ak9iY3#aPde)nR zA-C1eupV=%_1K$crutm{Xra05zTK<7Zd!fooV! f@ zjN&Brbhe|X^)|DOW?&^TSXp b-J8|rPncYZ6g>MN)cr9$&910<@S`i zkQl{a Qy;-2thth!_d8NT%MNOLaRo zCuFG|O0c z; &U9i--BvN-HZ`yK2WHwUjV^zvpBugv*x@U8x z({-K67HqPx8;i~;!e{od_khhIU{6k}7 aYNF7KD;K_3|HlOG;X68|WyQts~R_Ey{93p$q#vLD)BBQJf!D rZ2nhQiG*TS=m|!i=sNPks~sAWNwo((+QeE z#&&JLOxr| mJq#Wvwe^U!h^q1|mxGNOYb!WJG9t^^ z7DMZKI!Lj#PN|b)?X9WqMFoppm0nG~SG!I#I0zVZ`DqtCYId&sD^t_0?Sst)+mcAN zUvx@c>}O&7-Gj-m-xe7Z%Keh9Wq_!IMZnTSjO}I%4>EJ4jWgw@5p#Q)LYiP`6fiQd z^$ Ve27n-UX6~u~jBf+k#TtGMMS9dA;lIYZHi)4l30jO7QF&T(%QhouWT^ zynfVSX2S`ZnZPko&ykt#z}4MKa~F_k(OQ~~ 0%y}dDP!_F!}v#hlq(gM^>trIkoxdHpiniE-2)#zvMuOXYZ z@JTauPGdhoUrzx&2nsFzYz-P9uOq1X=#HU5Icmr1+m7`=TI)3+i)$@z)KfDUFrb|e zMgs?s-Dk1u&Z5gh$7`mpZ50a_t)I09Z_Y2;>j48bSd6fih|8uJJL^RwJv?I9MOj1S zdLU4vQqm5rNLt8_pn<5I7Sa+!IUbUAt>rFsNh$%|eUVd;2@Sq8wo0jAKq5vm`3kt3 z=3N~Rsk>_m9Wl3@TSRJXnS>Dm>Ye59TAeks*WX>pFw%@&JCaPu7;eaM#?H163Yxqm ziO1X{Y;1uY1)XquF~r!S{e#6HD+K5cU}@)0U+V%VRlwA-9$@K^y(J@8pB$}LpzaKz z#@63&zauQRhE`R26msV$Z^{OZT{Yqz~|MKp5@rfxL0c0%O-=ufe>>^uw{kcP3JqOoTg z+bjHcEIkgV+Xd`yFtrAF15DLXpaVjTJA-wc7@{Rz2CcE1z_Vdg<2a#%A@fTHRary$ zx^qPy{_{TkOX+1&?B6kR^}s#nLpfB<^+cJ+!)2=@cHV%N&a^i{jchr@!b(pU4J^^d zO17|ADFAxZ`eGm^1_e)qtT@Ery0EFfmaLv(=|+cN8% Rh$tyRdxLGaieqrB*@ zL#S!P*wWof-%8)Qb4e_g_L$k4z#SDq0S(+`G&PH>PTX|r+7{K~Ti3eID{{XsPtw=} z0bxo@dr1_)!UUvA#=f(B>9TP$k}MsN5fF`Yl1{Sda#ZD%T;wD _* zrR(uRL+ilL+JnOY9wm9-+$udbbEn?0(LOj>$r_hai=7gPtL*qQi|ZPXQR$29gp zkeHn=VZ^I8vx(Xr3C^*P2@HVj>St Oa=0bwJ{*liQX>WaLOu?0&v$0Jsj zZGj8Z3^mjKv_3!Zt()Tzw)7pqx@$jMW2a1WYelRebzTA;ZOaH? FNSYztt>#^x{9&}1iB)CrBSv~c-4}XJO4VNu`kuHu8xMR`H+r@WMy3&f@(oD zV-IQUBfyT>ItFjOyTqV&0>y3{a4&QaiC7u|8A%bSB@^2+!6JcGyO0!VK!4kJZHk>1 znNk-3?ybKKaCMsmp|tx{qUMoqf7#pY9YA&lk%ZZwrPU|5J9327sG0NZzDc%Dr%025 zqCIwz@d$Rqv$k9j0=G{sMPM7F99180rOMfIOX{5lSZj~hBGDq+p)@>{DBd>D(nB2@ zz1GjEM9YEo=n!M0*-1Ij*Dw%_*w@JncJ0>&i~KC^eP`rF2U)|W{@J)VA!A!|1Qar* z*jjIw0i|Z_ef0$;GbCeMEUibRZ?mK78J{kaZ6ym=Hg~jl6AQ>=HEZjAwYpG94|(1j zW6N|q-Bz=DfN-cDdxU)xSwru&TsGqlj#fXTK+$96&LKfghSJ8J%i;Ld466Ic7^EwK zuHB7AWfjo^qGj`m7f~6w&sVL%jGPcDOR5_pYDq3q8i?$oc2L&4#06v<$EoipXGE zNe;3`$sh*=YVSyj90(jorckgu1=C1Ik&MB+)?!7BTn*jE=l~4l2WgpM$d0D|LtS8) zRn(GZ-@YY{fb$?L^I#iGmZ(nqu775i>3x}i}tq++VK{F93+C@%G zValBBMTkrmmI0*)W{`~}Bamck?hjo=AIT2H;zr=Ov-i5ctc-b|dO^D>y);olB4TMB zC|XiP28;VR$Cqm(8I)rp >kC5c^snH_B6o0E;X OGDq4mJAaYlw*nfA;o{!0X+V9n+`Q =20nuSDlk0 zdgAwJ=+@w{od%FmxoH4qYj6Ry<_6@487vZ>V-HCY_L4Ek;gHVAHhozyMcV2<0vW}W z{7{x50y!yd{Q&F2azst1q4hSTulr-+Tp`h%A*?MwAJ_+zEJ?ObOB4spn(@)R)X>2& zf=kQ!usm=ppk%N<3>mc_`qq7y4os*fgh3{fE%yV-ALNecQ9724spY3iR-n%;mLmd6 zE873Ja6=*qeEPg1Cyb7X=BtLlGg9Z#Gl5!Z*j^VXfzjv;q(@hx2?D~5Jdw7U_P2|X zXdzX>N67uq%&cTd!^|?#(m5pyv#$2jQbRr?MwiJL*{)@0>KMC94OKA3K#t72B8eGQ zho%5VV9w47VpMu*-A$wNL!A%GiU-iw7fW|M05Jh}mf1p5fbI&HM3Gac! nzy#;7ldzS7-AeZ2l+khNtzzZzBJg{dp zb~=mbyw7Y0_M9bK!fawT={_?Rk|qSC2C5A~wb;**BF6L!W`$oc0wS#Y$YvKE zd17j5OQIOlnsscaN0*DLL$AtD>*~b_1WCz@B5FbwppQe4OMr?AbDH21$qb#P1Oo$h z`Jrt*5Px-YL qh0lp ~&%y-E2w y05*1A5Qnk%V HvfhCyH zC=hpY#SvhGb9KOVK)%&Y^RnGaB(p=7)*V4_v-Gn7YcXoCFRC_(RZ!S6b8Cfqwbxbv zIj6A?%91d9Xt1|+Of }XTw3;!B44<0% znWwtB|2&P4+P1NSp^2?K9QLkzeJ!ClT9SHNtqA8<27~p{mMh}O4{Na_#LV9CBRDWv z0#Jih47q~#9>@&J3tPNlQHDPawRfPgO~%%`y0s}74Z!7ym6a4BPiw1+ygEt-x+0)F zb#IHD5sR_Ifd)sVNL0&_vGT}Xc+?%csAYn*+Q^s#o56yZ9IR5MSuC&vo8!1&Bi#ab zqlFVvgjB+%|65pFi}1B2Pjo6A6*5T>N(ltKX>(B|m0NHnM|lj~Jr~#sRwic ^FJ1-T`NnK`Sm1G_Iw#v}#|WQK-WlSrb8+AYnEC!}nrHzOofG;mq1M+5E# zF0gem)7`OLLSQ3s1BPWaokAx|WSl1)5O_Z6ba(|#QZlH~GNb{nI{^wDZ8k5vpDICX zWG18F@zH)Vr5-va5qopI{6+-mtjzF^x9ma0=x8jY25H&De=xXL-ukp$GA4EJ?nEl- zQ?{Ut(U?OkHjw~DK_d!wv%y_+E+_VTv|H%t Hi!TN=lLhqd)(n^$wIw5XbX}6n z`wVcC!1m9<6ftn4w>5(+5MMNCf!6LiiE4Whw^Y6wQ<}v(+fAE42iREv2fEtS=E3Z# z5uh!P+mj_oiZsVqH6tECPhe|8cAs!~*a#jwEAVkGN;B?DgH ?WY8pQ;Rv=0J-zk#a?d`U!O06|i&_pC0~% zs`H#tCai{-F-YNecP#Zgvjn-m4!WXHOEei+>hHL2yn0o89qjDD)2se_dhnP@n|qag zq7K&uG4;zpyE+``=ZwZifMAQ|GqM9DW;}m7&*Dql$%OnMBsqMELWGhrj(%q3(o|;F zm3fVvSr`q@)mGl(= b*|dsgt6oOp6bR~u`cPXan(Y?Gyewo|O!dC`^>(LOeEI&2wo%g8#w=7X*X=dws6C9Z=RM)P&R>XeWcW*}*S9|dBO z#E4vw6hn)0PQ$k*rB~U=N_Ki%gBgJqonHde7} bdbG3X-bM%8BGh!5%`glVkCChN}~g3BW$M}kFK2>ONsVtFvIPh8-j|! zP`X_o->~@7R1I1~_EP)g01;!(lkT_dq{zVuDO3BhOrBuw4xoebW0 }oV0S%e1I!G@ zS(<=GAhvWhk}V>&+|*Y)b=L0-?9AW-BY4bAxj;rqE3At~Ky}WW0=SHa2iUo(K{h79 zoT?DC0FAa;NfwZ-@hm`!>^_u$QRi@h3rq?mNydOZGE=G{H&Tn{xCKvROl{R@LvI^( zB**_YItF)xh#kMXDH{M%hCO)$0MF=nR&EvuhTU2b Ml1Q!wJQMKqreW=pfU8fHKC=;%8?Wy zW7a2QbPj5(!N Ijz>u$oivp7#D1d0NoTZEjF9$;1MK(IMCCG zx#4mA491Rzkwx{-qhlU*CEw$JHpIZDM;^H+`-KBjpLq;>6vPe?U29I&lkB=&7dC0T zkL-zu_B9&G9&wIzIR`R>lTzb(!J|=U9R-`&X&KV;#bWceXp7Ay_biXxa!>Y)!Gw|^ zRzPXZRpL4+;23w5y4e9ilxJLLJ(vJZZMK~=hmp~M@T|Zw#n?zU2c03D=h^@)oM4jt zsnM*VVdlh?mfl95dUI}PN`okEK2kL0v3MjOvN9u+K?D|Fd;ZM@K@!6VE-4WpdRjVS z`e9&)&ImiJgbn!Hp)7H-n8gVs%>dH{es0F7IG{|}g8|!fpqD#of!gVkQq&1hX3_GZ z_3_C-5c<`Ii&6%I$Pr){sa-I;}6YGm*O`nF^QD6H`R=aiQ0s=u4 zTza2}Kqg;X`>lWu)lt7?a43FWI*YMWGq5^yjaX|lW+X^@MFS{LxLd9dEvFY2K> Mgp?OA;_w>u?86cFL0 zl^u|i`zRqxP>`3><7&bkb1pweD>LXk79h=KJ3K9)2o~)&cO%)= 1g}F zezt&oXXmE4tL+eryM}%D`&caP0o?<5GC3n&x1^uY*oC}O)4&Ny&lDB0x4RvToOh&) zr$x!r&{le0YierKcvjGr`MhwQN2@c|(vL0gU66c2WH&Pd(};GS(b>6m9(kOx2U$5v z@|0jSjm6$YCkMz`nb*JIW&u`KPe-hr@wz*AM^1+0bSRGYKYlwV;Do^T4dXIFHa6J1 z)5oZS&(qlha)lDCehAo6iE0Y?R>0YuTY#ACrKQ=>*;xRyl>~C^y1g2X{k0@ro$W{? zwvM-s$pDz_i65J#OcG%Bz!;8vLC%YD?so%r2z*0VV~N1-I_u_z2H-X|cn0LCMglSM zk_53Mlgw>~!{KUf$8{>kWIt>;QEu|Y(%7QI@3#!A20N#nd+-LJ&->U1fju>|lm~u` zMVtiAa2v1VIPJr2mh ANV{@gxO%YTY*rpumms_z`G6*Nz z@vOfDix06fV0>$B$utX(vNk(w-AuBv%gzxK2aX60xFRL6_ufZ}bZ!6>Cu>P8z_~%E zH<=FdoV||Vfm~{K063R%M}Vz=BS&@=XYTJbm>oS$PO`<-#+Jr*?hJ)Bt`PObM)kSZ8H|g?k<=*@;I^`DUND+wP){p6|2G-|G5*}WiV*^B=KId- z;G@Tf8k-jmEBQtPM31$T_1&(cVlBrp3fd&t$w|wntNq&V-IN5&It_E4@Imd|Kl?!< zfuUTc3zAHObCRvo`Z^&~rld%Ai4Mrxfz*f=J2kV1&iOMk#tIN2lVt AiQla>BXSp(9++bG|?7bP^y}FKW_JBeC3hEjw3ZfkQ=ooGj?Ibj8S#saVB*- z(a3wWYzYE&Mi#gdr2U=EcHfQNhXCmfHXuAzgA3lI-REUKqmoum9Ai$?F!CHEmD0~z zqF9XWl^>uxWn#1olpsOM+>$6(x}0oQrzmhdPl^o4kuac)((nmM5;-1}WlGssZT7om z&`$%t(;*_@Mk^abP-k3?V4{ACM!F=WN2VN=j44B&BSj(rM^iD32B1+VaO%L_i|*Y4 zY*t?&sLqff4*OA7!ZgPku1D6@utwCJ{WyPAO_H%kf$k=8h&W*C==PQYs~c!Wj2#U! zFn*)-F |TRRDm+6Dq3p;2+49V@!K-(?{xPPPoF1to z$x0H=yC}mJp~L3**vIz?8MOj$Qro8;i>#|LTTB(5kG`YF^a7^fmu>C|j@F))CsXdp z2*c0Hi~~%Z-PZrVI2D6HRvrQW5r+0;$An*<4jfMfb~H xr6qu(0VIU{6XB%fdUt+7GKM$ZQ7d z+*G-q^3AhAkG6UGeKe43wk=Z~@{qi^8g4~a7kh3;^x0=fpYx @Ed05BU<|1?Y@y9GNH9cw6|*ydMtC~bu#Ur zW^%V9`U#e%tuqzfu>F?#-9|w@YT*rZk~0~*#cRn2ktxkSAW3Ed%?xnei<~%b>YUvy zQB0iuZb_Yd#{V~D+^D7_lR5mbj7|2~MWfx0!AN^YIy$^?2|Zp0fzVvE@~A_$*cnVA zMw#g)ypC q2vdiFXhx6E0)LqSNUxhI zVW-W=i}3ha-8{;~vjY#ZReptXbC!%U_7TS<<#h*49XcddFz8&+VQpzLHIvtFG}ukc ztmOZTJf~!aiH*rPc@pR;9nD7=JCGdVDq<116>`I1b5xT@tC-Dp5?2cO>`Zi$Oc?Cc z(=Nq~W}cBGvz~W!9>~};tnKM?az?r*E|O;jW_03+7UVQwICongNlHz~C-}PvCLZYw zR_qcowKa`$hP?y!^;8FkQe+UQgE~86O1M%&)(VWI%!rlfG4Y`wbreh^=ZP%0M8brg z;dU5oO-Jt}hg^{e*c8v~l#?WMUt?_~z?@+B^hF06nQ2~sj#!{Wam0bn8hwA4g(GPp zre)|l%aSOn8U*|Kj6IRW895IgvD0rHxqx+pbT_1PaWqhU>-R^eY8dPASqqQe_aWKy zbYM@gdG;KmUU;{SlVCmuz zWeg767aWXd_QWP{P+&9)h)*5Dlk z!wjP}|BcZa_lfvoG{C0G|7UodwcV@AJHfqqjue^oy9ceoY@nI}^Q?wH6;v~TLW`Kr zXy79tKO?#FmJgG$lhVOuZE*2ZjE(?$gwY2R3`cIyVJA+KNNfEK$gcDqZHkf0VMdOe zm>Iv^1hyK8I~oYO9l-Us@yB|M4gsFj<5}h%0z Yagcd)ZD(q&^V&IE^>F~$_5al^!k4%1 zJn&9?;+~W-ewCt^;T`NeM?jwi+M(lXmEnk#nsGM*Cu2AujAFmD9g=3^cRpazg{)2P zn8^slCiA+?NR(!+NuZ1{S-MkuZF%6gv9{}!d*XLgzfH?4N!?6zAmK9F0seNx-8fs1 zXDviP*JZL(3;|+<;j`Pb3|s~}q7~H5vaL6j*f~=voG(jG12zp0Lhxjb*#NQD5r-zc z8>nBrJig*G4d%vKNVex5w1@9RM?K}U<_D?ky?<0-r@?(R`7 p`ot`?H2s#>^rZ4^=FlN|z7;uANn-1>IN}N2Cr3Zk`BP^T*Bp+h=GXQ?n zq4@~xH~`i&0DT(!&VYJ`-9wFiKyS}TnG}0}0I747b%*zVRH^Xn>CK)NJe~mTrv#t_ zcabwXLIlFM0P-}L$AMa=dX$_}d`x3eTZ?RLY=?o8@$7*!k>srvp9PW)jSU?U;_9 z^Qe?K+IdESZGby%u+M1qd$#NeEItDAvw?m9$ftmLQX9{>7*l{gdPf)rHlFHin8at8 zf231nfja`;X`qjQ{TPtT2$-J+=#L;%GCuZM%zFe-GoTq|EqXG0J-rjFA!) YD@D%G+(%jVtWGl1QAU{d2%u&<<*Zbo86f4@e+txxnf@rS&tmU0L4FkI zvtVzu0H1hQIRnIJ0C_feJe#F6_kc5iO=q3M`0__VKFhGvjLYYO_Y5XJ1jJ{7c^Yep zHW&J!52nA`qtNH$?S3o4*oSLk&tTSPNrW?X?<5;9naK^$U}fVxx$Ap| $u2f@bg3gWkxHK%Fp4+8RMf$}i0&g=*$e&(C#qxaD0ZvpHl zv+I 6`Mm!BUmxWOUFGBaUp^pXA9e5eL-vl3cNcl?@AS6vgTG5D@?CMq zTi=Sx2mN33JO2+a9~-FUJu&myl|RHap8Fr(82g7GfB2M~pO3%P FFFFFF + * @method rgb2hex + * @param r {int|[int, int, int]} the red value, or an + * array containing all three parameters + * @param g {int} the green value + * @param b {int} the blue value + * @return {string} the hex string + */ + rgb2hex: function(r, g, b) { + if (lang.isArray(r)) { + return this.rgb2hex.call(this, r[0], r[1], r[2]); + } + + var f=this.dec2hex; + return f(r) + f(g) + f(b); + }, + + /** + * Converts an int 0...255 to hex pair 00...FF + * @method dec2hex + * @param n {int} the number to convert + * @return {string} the hex equivalent + */ + dec2hex: function(n) { + n = parseInt(n, 10); + n = (lang.isNumber(n)) ? n : 0; + n = (n > 255 || n < 0) ? 0 : n; + + return HCHARS.charAt((n - n % 16) / 16) + HCHARS.charAt(n % 16); + }, + + /** + * Converts a hex pair 00...FF to an int 0...255 + * @method hex2dec + * @param str {string} the hex pair to convert + * @return {int} the decimal + */ + hex2dec: function(str) { + var f = function(c) { + return HCHARS.indexOf(c.toUpperCase()); + }; + + var s=str.split(''); + + return ((f(s[0]) * 16) + f(s[1])); + }, + + /** + * Converts a hex string to rgb + * @method hex2rgb + * @param str {string} the hex string + * @return {[int, int, int]} an array containing the rgb values + */ + hex2rgb: function(s) { + var f = this.hex2dec; + return [f(s.substr(0, 2)), f(s.substr(2, 2)), f(s.substr(4, 2))]; + }, + + /** + * Returns the closest websafe color to the supplied rgb value. + * @method websafe + * @param r {int|[int, int, int]} the red value, or an + * array containing all three parameters + * @param g {int} the green value + * @param b {int} the blue value + * @return {[int, int, int]} an array containing the closes + * websafe rgb colors. + */ + websafe: function(r, g, b) { + + if (lang.isArray(r)) { + return this.websafe.call(this, r[0], r[1], r[2]); + } + + // returns the closest match [0, 51, 102, 153, 204, 255] + var f = function(v) { + if (lang.isNumber(v)) { + v = Math.min(Math.max(0, v), 255); + var i, next; + for (i=0; i<256; i=i+51) { + next = i+51; + if (v >= i && v <= next) { + return (v-i > 25) ? next : i; + } + } + YAHOO.log("Error calculating the websafe value for " + v, "warn"); + } + + return v; + }; + + return [f(r), f(g), f(b)]; + } + }; +}(); + + +(function() { + + var pickercount = 0; + + /** + * The colorpicker module provides a widget for selecting colors + * @module colorpicker + * @requires yahoo, dom, event, element, slider + */ + + /** + * A widget to select colors + * @namespace YAHOO.widget + * @class YAHOO.widget.ColorPicker + * @extends YAHOO.util.Element + * @constructor + * @param {HTMLElement | String | Object} el(optional) The html + * element that represents the colorpicker, or the attribute object to use. + * An element will be created if none provided. + * @param {Object} attr (optional) A key map of the colorpicker's + * initial attributes. Ignored if first arg is attributes object. + */ + YAHOO.widget.ColorPicker = function(el, attr) { + pickercount = pickercount + 1; + this.logger = new YAHOO.widget.LogWriter("ColorPicker"); + attr = attr || {}; + if (arguments.length === 1 && !YAHOO.lang.isString(el) && !el.nodeName) { + attr = el; // treat first arg as attr object + el = attr.element || null; + } + + if (!el && !attr.element) { // create if we dont have one + this.logger.log("creating host element"); + el = _createHostElement.call(this, attr); + } + + YAHOO.widget.ColorPicker.superclass.constructor.call(this, el, attr); + }; + + YAHOO.extend(YAHOO.widget.ColorPicker, YAHOO.util.Element); + + var proto = YAHOO.widget.ColorPicker.prototype, + Slider=YAHOO.widget.Slider, + Color=YAHOO.util.Color, + Dom = YAHOO.util.Dom, + Event = YAHOO.util.Event, + lang = YAHOO.lang, + sub = lang.substitute; + + + var b = "yui-picker"; + + /** + * The element ids used by this control + * @property ID + * @final + */ + proto.ID = { + + /** + * The id for the "red" form field + * @property ID.R + * @type String + * @final + * @default yui-picker-r + */ + R: b + "-r", + + /** + * The id for the "red" hex pair output + * @property ID.R_HEX + * @type String + * @final + * @default yui-picker-rhex + */ + R_HEX: b + "-rhex", + + /** + * The id for the "green" form field + * @property ID.G + * @type String + * @final + * @default yui-picker-g + */ + G: b + "-g", + + /** + * The id for the "green" hex pair output + * @property ID.G_HEX + * @type String + * @final + * @default yui-picker-ghex + */ + G_HEX: b + "-ghex", + + + /** + * The id for the "blue" form field + * @property ID.B + * @type String + * @final + * @default yui-picker-b + */ + B: b + "-b", + + /** + * The id for the "blue" hex pair output + * @property ID.B_HEX + * @type String + * @final + * @default yui-picker-bhex + */ + B_HEX: b + "-bhex", + + /** + * The id for the "hue" form field + * @property ID.H + * @type String + * @final + * @default yui-picker-h + */ + H: b + "-h", + + /** + * The id for the "saturation" form field + * @property ID.S + * @type String + * @final + * @default yui-picker-s + */ + S: b + "-s", + + /** + * The id for the "value" form field + * @property ID.V + * @type String + * @final + * @default yui-picker-v + */ + V: b + "-v", + + /** + * The id for the picker region slider + * @property ID.PICKER_BG + * @type String + * @final + * @default yui-picker-bg + */ + PICKER_BG: b + "-bg", + + /** + * The id for the picker region thumb + * @property ID.PICKER_THUMB + * @type String + * @final + * @default yui-picker-thumb + */ + PICKER_THUMB: b + "-thumb", + + /** + * The id for the hue slider + * @property ID.HUE_BG + * @type String + * @final + * @default yui-picker-hue-bg + */ + HUE_BG: b + "-hue-bg", + + /** + * The id for the hue thumb + * @property ID.HUE_THUMB + * @type String + * @final + * @default yui-picker-hue-thumb + */ + HUE_THUMB: b + "-hue-thumb", + + /** + * The id for the hex value form field + * @property ID.HEX + * @type String + * @final + * @default yui-picker-hex + */ + HEX: b + "-hex", + + /** + * The id for the color swatch + * @property ID.SWATCH + * @type String + * @final + * @default yui-picker-swatch + */ + SWATCH: b + "-swatch", + + /** + * The id for the websafe color swatch + * @property ID.WEBSAFE_SWATCH + * @type String + * @final + * @default yui-picker-websafe-swatch + */ + WEBSAFE_SWATCH: b + "-websafe-swatch", + + /** + * The id for the control details + * @property ID.CONTROLS + * @final + * @default yui-picker-controls + */ + CONTROLS: b + "-controls", + + /** + * The id for the rgb controls + * @property ID.RGB_CONTROLS + * @final + * @default yui-picker-rgb-controls + */ + RGB_CONTROLS: b + "-rgb-controls", + + /** + * The id for the hsv controls + * @property ID.HSV_CONTROLS + * @final + * @default yui-picker-hsv-controls + */ + HSV_CONTROLS: b + "-hsv-controls", + + /** + * The id for the hsv controls + * @property ID.HEX_CONTROLS + * @final + * @default yui-picker-hex-controls + */ + HEX_CONTROLS: b + "-hex-controls", + + /** + * The id for the hex summary + * @property ID.HEX_SUMMARY + * @final + * @default yui-picker-hex-summary + */ + HEX_SUMMARY: b + "-hex-summary", + + /** + * The id for the controls section header + * @property ID.CONTROLS_LABEL + * @final + * @default yui-picker-controls-label + */ + CONTROLS_LABEL: b + "-controls-label" + }; + + /** + * Constants for any script-generated messages. The values here + * are the default messages. They can be updated by providing + * the complete list to the constructor for the "txt" attribute. + * @property TXT + * @final + */ + proto.TXT = { + ILLEGAL_HEX: "Illegal hex value entered", + SHOW_CONTROLS: "Show color details", + HIDE_CONTROLS: "Hide color details", + CURRENT_COLOR: "Currently selected color: {rgb}", + CLOSEST_WEBSAFE: "Closest websafe color: {rgb}. Click to select.", + R: "R", + G: "G", + B: "B", + H: "H", + S: "S", + V: "V", + HEX: "#", + DEG: "\u00B0", + PERCENT: "%" + }; + + /** + * Constants for the default image locations for img tags that are + * generated by the control. They can be modified by passing the + * complete list to the contructor for the "images" attribute + * @property IMAGE + * @final + */ + proto.IMAGE = { + PICKER_THUMB: "../../build/colorpicker/assets/picker_thumb.png", + HUE_THUMB: "../../build/colorpicker/assets/hue_thumb.png" + }; + + /* + * Constants for the control's custom event names. subscribe + * to the rgbChange event instead. + * @property EVENT + * @final + */ + //proto.EVENT = { + //CHANGE: "change" + //}; + + //proto.CSS = { }; + + /** + * Constants for the control's default default values + * @property DEFAULT + * @final + */ + proto.DEFAULT = { + PICKER_SIZE: 180 + }; + + /** + * Constants for the control's configuration attributes + * @property OPT + * @final + */ + proto.OPT = { + HUE: "hue", + SATURATION: "saturation", + VALUE: "value", + RED: "red", + GREEN: "green", + BLUE: "blue", + HSV: "hsv", + RGB: "rgb", + WEBSAFE: "websafe", + HEX: "hex", + PICKER_SIZE: "pickersize", + SHOW_CONTROLS: "showcontrols", + SHOW_RGB_CONTROLS: "showrgbcontrols", + SHOW_HSV_CONTROLS: "showhsvcontrols", + SHOW_HEX_CONTROLS: "showhexcontrols", + SHOW_HEX_SUMMARY: "showhexsummary", + SHOW_WEBSAFE: "showwebsafe", + //SHOW_SUBMIT: "showsubmit", + CONTAINER: "container", + IDS: "ids", + ELEMENTS: "elements", + TXT: "txt", + IMAGES: "images", + ANIMATE: "animate" + }; + + /** + * Sets the control to the specified rgb value and + * moves the sliders to the proper positions + * @method setValue + * @param rgb {[int, int, int]} the rgb value + * @param silent {boolean} whether or not to fire the change event + */ + proto.setValue = function(rgb, silent) { + silent = (silent) || false; + this.set(this.OPT.RGB, rgb, silent); + _updateSliders.call(this); + }; + + /** + * The hue slider + * @property hueSlider + * @type YAHOO.widget.Slider + */ + proto.hueSlider = null; + + /** + * The picker region + * @property pickerSlider + * @type YAHOO.widget.Slider + */ + proto.pickerSlider = null; + + /** + * Translates the slider value into hue, int[0,359] + * @method _getH + * @private + * @return {int} the hue from 0 to 359 + */ + var _getH = function() { + var size = this.get(this.OPT.PICKER_SIZE), + h = (size - this.hueSlider.getValue()) / size; + h = Math.round(h*360); + return (h === 360) ? 0 : h; + }; + + /** + * Translates the slider value into saturation, int[0,1], left to right + * @method _getS + * @private + * @return {int} the saturation from 0 to 1 + */ + var _getS = function() { + return this.pickerSlider.getXValue() / this.get(this.OPT.PICKER_SIZE); + }; + + /** + * Translates the slider value into value/brightness, int[0,1], top + * to bottom + * @method _getV + * @private + * @return {int} the value from 0 to 1 + */ + var _getV = function() { + var size = this.get(this.OPT.PICKER_SIZE); + return (size - this.pickerSlider.getYValue()) / size; + }; + + /** + * Updates the background of the swatch with the current rbg value. + * Also updates the websafe swatch to the closest websafe color + * @method _updateSwatch + * @private + */ + var _updateSwatch = function() { + var rgb = this.get(this.OPT.RGB), + websafe = this.get(this.OPT.WEBSAFE), + el = this.getElement(this.ID.SWATCH), + color = rgb.join(","), + txt = this.get(this.OPT.TXT); + + Dom.setStyle(el, "background-color", "rgb(" + color + ")"); + el.title = lang.substitute(txt.CURRENT_COLOR, { + "rgb": "#" + this.get(this.OPT.HEX) + }); + + + el = this.getElement(this.ID.WEBSAFE_SWATCH); + color = websafe.join(","); + + Dom.setStyle(el, "background-color", "rgb(" + color + ")"); + el.title = lang.substitute(txt.CLOSEST_WEBSAFE, { + "rgb": "#" + Color.rgb2hex(websafe) + }); + + }; + + /** + * Reads the sliders and converts the values to RGB, updating the + * internal state for all the individual form fields + * @method _getValuesFromSliders + * @private + */ + var _getValuesFromSliders = function() { + var h=_getH.call(this), s=_getS.call(this), v=_getV.call(this); + YAHOO.log("hsv " + [h, s, v]); + + rgb = Color.hsv2rgb(h, s, v); + var websafe = Color.websafe(rgb); + var hex = Color.rgb2hex(rgb[0], rgb[1], rgb[2]); + + this.set(this.OPT.RGB, rgb); + }; + + /** + * Updates the form field controls with the state data contained + * in the control. + * @method _updateFormFields + * @private + */ + var _updateFormFields = function() { + this.getElement(this.ID.H).value = this.get(this.OPT.HUE); + this.getElement(this.ID.S).value = this.get(this.OPT.SATURATION); + this.getElement(this.ID.V).value = this.get(this.OPT.VALUE); + this.getElement(this.ID.R).value = this.get(this.OPT.RED); + this.getElement(this.ID.R_HEX).innerHTML = Color.dec2hex(this.get(this.OPT.RED)); + this.getElement(this.ID.G).value = this.get(this.OPT.GREEN); + this.getElement(this.ID.G_HEX).innerHTML = Color.dec2hex(this.get(this.OPT.GREEN)); + this.getElement(this.ID.B).value = this.get(this.OPT.BLUE); + this.getElement(this.ID.B_HEX).innerHTML = Color.dec2hex(this.get(this.OPT.BLUE)); + this.getElement(this.ID.HEX).value = this.get(this.OPT.HEX); + }; + + /** + * Event handler for the hue slider. + * @method _onHueSliderChange + * @param newOffset {int} pixels from the start position + * @private + */ + var _onHueSliderChange = function(newOffset) { + this.logger.log("hue update: " + newOffset , "warn"); + + var h = _getH.call(this); + this.set(this.OPT.HUE, h, true); + + // set picker background to the hue + var rgb = Color.hsv2rgb(h, 1, 1); + var styleDef = "rgb(" + rgb.join(",") + ")"; + + Dom.setStyle(this.getElement(this.ID.PICKER_BG), "background-color", styleDef); + + if (this.hueSlider.valueChangeSource === this.hueSlider.SOURCE_UI_EVENT) { + _getValuesFromSliders.call(this); + } + + _updateFormFields.call(this); + _updateSwatch.call(this); + }; + + /** + * Event handler for the picker slider, which controls the + * saturation and value/brightness. + * @method _onPickerSliderChange + * @param newOffset {{x: int, y: int}} x/y pixels from the start position + * @private + */ + var _onPickerSliderChange = function(newOffset) { + this.logger.log(sub("picker update [{x}, {y}]", newOffset)); + + var s=_getS.call(this), v=_getV.call(this); + this.set(this.OPT.SATURATION, Math.round(s*100), true); + this.set(this.OPT.VALUE, Math.round(v*100), true); + + if (this.pickerSlider.valueChangeSource === this.pickerSlider.SOURCE_UI_EVENT) { + _getValuesFromSliders.call(this); + } + + _updateFormFields.call(this); + _updateSwatch.call(this); + }; + + + /** + * Key map to well-known commands for txt field input + * @method _getCommand + * @param e {Event} the keypress or keydown event + * @return {int} a command code + * + *
+ * @private + */ + var _getCommand = function(e) { + var c = Event.getCharCode(e); + + //alert(Event.getCharCode(e) + ", " + e.keyCode + ", " + e.charCode); + + // special keys + if (c === 38) { // up arrow + return 3; + } else if (c === 13) { // return + return 6; + } else if (c === 40) { // down array + return 4; + } else if (c >= 48 && c<=57) { // 0-9 + return 1; + } else if (c >= 97 && c<=102) { // a-f + return 2; + } else if (c >= 65 && c<=70) { // A-F + return 2; + //} else if ("8, 9, 13, 27, 37, 39".indexOf(c) > -1 || + // (c >= 112 && c <=123)) { // including F-keys + // tab, delete, return, escape, left, right + } else if ("8, 9, 13, 27, 37, 39".indexOf(c) > -1) { // special chars + return 5; + } else { // something we probably don't want + return 0; + } + }; + + /** + * Handle keypress on one of the rgb or hsv fields. + * @method _rgbFieldKeypress + * @param e {Event} the keypress event + * @param el {HTMLElement} the field + * @param prop {string} the key to the linked property + * @private + */ + var _rgbFieldKeypress = function(e, el, prop) { + var command = _getCommand(e); + var inc = (e.shiftKey) ? 10 : 1; + switch (command) { + case 6: // return, update the value + _useFieldValue.apply(this, arguments); + break; + + case 3: // up arrow, increment + this.set(prop, Math.min(this.get(prop)+inc, 255)); + _updateFormFields.call(this); + //Event.stopEvent(e); + break; + case 4: // down arrow, decrement + this.set(prop, Math.max(this.get(prop)-inc, 0)); + _updateFormFields.call(this); + //Event.stopEvent(e); + break; + + default: + } + + }; + + /** + * Handle keydown on the hex field + * @method _hexFieldKeypress + * @param e {Event} the keypress event + * @param el {HTMLElement} the field + * @param prop {string} the key to the linked property + * @private + */ + var _hexFieldKeypress = function(e, el, prop) { + var command = _getCommand(e); + if (command === 6) { // return, update the value + _useFieldValue.apply(this, arguments); + } + }; + + /** + * Use the value of the text field to update the control + * @method _hexFieldKeypress + * @param e {Event} an event + * @param el {HTMLElement} the field + * @param prop {string} the key to the linked property + * @private + */ + var _useFieldValue = function(e, el, prop) { + var val = el.value; + + if (prop !== this.OPT.HEX) { + val = parseInt(val, 10); + } + + if (val !== this.get(prop)) { + this.set(prop, val); + } + }; + + /** + * Allows numbers and special chars only. Used for the + * rgb and hsv fields keypress handler. + * @method _numbersOnly + * @param e {Event} the event + * @private + * @return {boolean} false if we are canceling the event + */ + var _numbersOnly = function(e) { + return _hexOnly(e, true); + }; + + /** + * Allows numbers and special chars, and by default allows a-f. + * Used for the hex field keypress handler. + * @method _hexOnly + * @param e {Event} the event + * @param numbersOnly omits a-f if set to true + * @private + * @return {boolean} false if we are canceling the event + */ + var _hexOnly = function(e, numbersOnly) { + var command = _getCommand(e); + switch (command) { + case 6: // return + case 5: // special char + case 1: // number + break; + case 2: // hex char (a-f) + if (numbersOnly !== true) { + break; + } + + // fallthrough is intentional + + default: // prevent alpha and punctuation + Event.stopEvent(e); + return false; + } + }; + + /** + * Returns the element reference that is saved. The id can be either + * the element id, or the key for this id in the "id" config attribute. + * For instance, the host element id can be obtained by passing its + * id (default: "yui_picker") or by its key "YUI_PICKER". + * @param id {string} the element id, or key + * @return {HTMLElement} a reference to the element + */ + proto.getElement = function(id) { + return this.get(this.OPT.ELEMENTS)[this.get(this.OPT.IDS)[id]]; + }; + + _createElements = function() { + this.logger.log("Building markup"); + var el, child, img, fld, i, + ids = this.get(this.OPT.IDS), + txt = this.get(this.OPT.TXT), + images = this.get(this.OPT.IMAGES), + Elem = function(type, o) { + var n = document.createElement(type); + if (o) { + lang.augmentObject(n, o, true); + } + return n; + }, + RGBElem = function(type, obj) { + var o = lang.merge({ + //type: "txt", + autocomplete: "off", + value: "0", + size: 3, + maxlength: 3 + }, obj); + + o.name = o.id; + return new Elem(type, o); + }; + + var p = this.get("element"); + + // Picker slider (S and V) --------------------------------------------- + + el = new Elem("div", { + id: ids[this.ID.PICKER_BG], + className: "yui-picker-bg", + tabIndex: -1, + hideFocus: true + }); + + child = new Elem("div", { + id: ids[this.ID.PICKER_THUMB], + className: "yui-picker-thumb" + }); + + img = new Elem("img", { + src: images.PICKER_THUMB + }); + + child.appendChild(img); + el.appendChild(child); + p.appendChild(el); + + // Hue slider --------------------------------------------- + el = new Elem("div", { + id: ids[this.ID.HUE_BG], + className: "yui-picker-hue-bg", + tabIndex: -1, + hideFocus: true + }); + + child = new Elem("div", { + id: ids[this.ID.HUE_THUMB], + className: "yui-picker-hue-thumb" + }); + + img = new Elem("img", { + src: images.HUE_THUMB + }); + + child.appendChild(img); + el.appendChild(child); + p.appendChild(el); + + + // controls --------------------------------------------- + + el = new Elem("div", { + id: ids[this.ID.CONTROLS], + className: "yui-picker-controls" + }); + + p.appendChild(el); + p = el; + + // controls header + el = new Elem("div", { + className: "hd" + }); + + child = new Elem("a", { + id: ids[this.ID.CONTROLS_LABEL], + //className: "yui-picker-controls-label", + href: "#" + }); + el.appendChild(child); + p.appendChild(el); + + // bd + el = new Elem("div", { + className: "bd" + }); + + p.appendChild(el); + p = el; + + // rgb + el = new Elem("ul", { + id: ids[this.ID.RGB_CONTROLS], + className: "yui-picker-rgb-controls" + }); + + child = new Elem("li"); + child.appendChild(document.createTextNode(txt.R + " ")); + + fld = new RGBElem("input", { + id: ids[this.ID.R], + className: "yui-picker-r" + }); + + child.appendChild(fld); + el.appendChild(child); + + child = new Elem("li"); + child.appendChild(document.createTextNode(txt.G + " ")); + + fld = new RGBElem("input", { + id: ids[this.ID.G], + className: "yui-picker-g" + }); + + child.appendChild(fld); + el.appendChild(child); + + child = new Elem("li"); + child.appendChild(document.createTextNode(txt.B + " ")); + + fld = new RGBElem("input", { + id: ids[this.ID.B], + className: "yui-picker-b" + }); + + child.appendChild(fld); + el.appendChild(child); + + p.appendChild(el); + + // hsv + el = new Elem("ul", { + id: ids[this.ID.HSV_CONTROLS], + className: "yui-picker-hsv-controls" + }); + + child = new Elem("li"); + child.appendChild(document.createTextNode(txt.H + " ")); + + fld = new RGBElem("input", { + id: ids[this.ID.H], + className: "yui-picker-h" + }); + + child.appendChild(fld); + child.appendChild(document.createTextNode(" " + txt.DEG)); + + el.appendChild(child); + + child = new Elem("li"); + child.appendChild(document.createTextNode(txt.S + " ")); + + fld = new RGBElem("input", { + id: ids[this.ID.S], + className: "yui-picker-s" + }); + + child.appendChild(fld); + child.appendChild(document.createTextNode(" " + txt.PERCENT)); + + el.appendChild(child); + + child = new Elem("li"); + child.appendChild(document.createTextNode(txt.V + " ")); + + fld = new RGBElem("input", { + id: ids[this.ID.V], + className: "yui-picker-v" + }); + + child.appendChild(fld); + child.appendChild(document.createTextNode(" " + txt.PERCENT)); + + el.appendChild(child); + p.appendChild(el); + + + // hex summary + + el = new Elem("ul", { + id: ids[this.ID.HEX_SUMMARY], + className: "yui-picker-hex_summary" + }); + + child = new Elem("li", { + id: ids[this.ID.R_HEX] + }); + el.appendChild(child); + + child = new Elem("li", { + id: ids[this.ID.G_HEX] + }); + el.appendChild(child); + + child = new Elem("li", { + id: ids[this.ID.B_HEX] + }); + el.appendChild(child); + p.appendChild(el); + + // hex field + el = new Elem("div", { + id: ids[this.ID.HEX_CONTROLS], + className: "yui-picker-hex-controls" + }); + el.appendChild(document.createTextNode(txt.HEX + " ")); + + child = new RGBElem("input", { + id: ids[this.ID.HEX], + className: "yui-picker-hex", + size: 6, + maxlength: 6 + }); + + el.appendChild(child); + p.appendChild(el); + + p = this.get("element"); + + // swatch + el = new Elem("div", { + id: ids[this.ID.SWATCH], + className: "yui-picker-swatch" + }); + + p.appendChild(el); + + // websafe swatch + el = new Elem("div", { + id: ids[this.ID.WEBSAFE_SWATCH], + className: "yui-picker-websafe-swatch" + }); + + p.appendChild(el); + + }; + + /** + * Sets the initial state of the sliders + * @method initPicker + */ + proto.initPicker = function () { + + // bind all of our elements + var o=this.OPT, + ids = this.get(o.IDS), + els = this.get(o.ELEMENTS), + i, el, id; + + // Add the default value as a key for each element for easier lookup + for (i in this.ID) { + if (lang.hasOwnProperty(this.ID, i)) { + ids[this.ID[i]] = ids[i]; + } + } + + // Check for picker element, if not there, create all of them + el = Dom.get(ids[this.ID.PICKER_BG]); + if (!el) { + _createElements.call(this); + } else { + this.logger.log("Using pre-existing markup"); + } + + for (i in ids) { + if (lang.hasOwnProperty(ids, i)) { + // look for element + el = Dom.get(ids[i]); + + // generate an id if the implementer passed in an element reference, + // and the element did not have an id already + id = Dom.generateId(el); + + // update the id in case we generated the id + ids[i] = id; // key is WEBSAFE_SWATCH + ids[ids[i]] = id; // key is websafe_swatch + + // store the dom ref + els[id] = el; + } + } + + // set the initial visibility state of our controls + els = [o.SHOW_CONTROLS, + o.SHOW_RGB_CONTROLS, + o.SHOW_HSV_CONTROLS, + o.SHOW_HEX_CONTROLS, + o.SHOW_HEX_SUMMARY, + o.SHOW_WEBSAFE + ]; + + for (i=0; i- 0 = not a number, letter in range, or special key
+ *- 1 = number
+ *- 2 = a-fA-F
+ *- 3 = increment (up arrow)
+ *- 4 = decrement (down arrow)
+ *- 5 = special key (tab, delete, return, escape, left, right)
+ *- 6 = return
+ *1) { + for (var i in ids) { + if (lang.hasOwnProperty(ids, i)) { + ids[i] = ids[i] + pickercount; + } + } + } + + + /** + * A list of element ids and/or element references used by the + * control. The default is the this.ID list, and can be customized + * by passing a list in the contructor + * @config ids + * @type {referenceid: realid} + * @writeonce + */ + this.setAttributeConfig(this.OPT.IDS, { + value: ids, + writeonce: true + }); + + /** + * A list of txt strings for internationalization. Default + * is this.TXT + * @config txt + * @type {key: txt} + * @writeonce + */ + this.setAttributeConfig(this.OPT.TXT, { + value: attr.txt || this.TXT, + writeonce: true + }); + + /** + * The img src default list + * is this.IMAGES + * @config images + * @type {key: image} + * @writeonce + */ + this.setAttributeConfig(this.OPT.IMAGES, { + value: attr.images || this.IMAGE, + writeonce: true + }); + /** + * The element refs used by this control. Set at initialization + * @config elements + * @type {id: HTMLElement} + * @readonly + */ + this.setAttributeConfig(this.OPT.ELEMENTS, { + value: {}, + readonly: true + }); + + /** + * Returns the cached element reference. If the id is not a string, it + * is assumed that it is an element and this is returned. + * @param id {string|HTMLElement} the element key, id, or ref + * @param on {boolean} hide or show. If true, show + * @private */ + _hideShowEl = function(id, on) { + var el = (lang.isString(id) ? this.getElement(id) : id); + //Dom.setStyle(id, "visibility", (on) ? "" : "hidden"); + Dom.setStyle(el, "display", (on) ? "" : "none"); + }; + + /** + * Hide/show the entire set of controls + * @config showcontrols + * @type boolean + * @default true + */ + this.setAttributeConfig(this.OPT.SHOW_CONTROLS, { + value: (attr.showcontrols) || true, + method: function(on) { + + var el = Dom.getElementsByClassName("bd", "div", + this.getElement(this.ID.CONTROLS))[0]; + + _hideShowEl.call(this, el, on); + + this.getElement(this.ID.CONTROLS_LABEL).innerHTML = + (on) ? this.get(this.OPT.TXT).HIDE_CONTROLS : + this.get(this.OPT.TXT).SHOW_CONTROLS; + + } + }); + + /** + * Hide/show the rgb controls + * @config showrgbcontrols + * @type boolean + * @default true + */ + this.setAttributeConfig(this.OPT.SHOW_RGB_CONTROLS, { + value: (attr.showrgbcontrols) || true, + method: function(on) { + //Dom.setStyle(this.getElement(this.ID.RBG_CONTROLS), "visibility", (on) ? "" : "hidden"); + _hideShowEl.call(this, this.ID.RGB_CONTROLS, on); + } + }); + + /** + * Hide/show the hsv controls + * @config showhsvcontrols + * @type boolean + * @default false + */ + this.setAttributeConfig(this.OPT.SHOW_HSV_CONTROLS, { + value: (attr.showhsvcontrols) || false, + method: function(on) { + //Dom.setStyle(this.getElement(this.ID.HSV_CONTROLS), "visibility", (on) ? "" : "hidden"); + _hideShowEl.call(this, this.ID.HSV_CONTROLS, on); + + // can't show both the hsv controls and the rbg hex summary + if (on && this.get(this.OPT.SHOW_HEX_SUMMARY)) { + this.set(this.OPT.SHOW_HEX_SUMMARY, false); + } + } + }); + + /** + * Hide/show the hex controls + * @config showhexcontrols + * @type boolean + * @default true + */ + this.setAttributeConfig(this.OPT.SHOW_HEX_CONTROLS, { + value: (attr.showhexcontrols) || false, + method: function(on) { + _hideShowEl.call(this, this.ID.HEX_CONTROLS, on); + } + }); + + /** + * Hide/show the websafe swatch + * @config showwebsafe + * @type boolean + * @default true + */ + this.setAttributeConfig(this.OPT.SHOW_WEBSAFE, { + value: (attr.showwebsafe) || true, + method: function(on) { + _hideShowEl.call(this, this.ID.WEBSAFE_SWATCH, on); + } + }); + + /** + * Hide/show the hex summary + * @config showhexsummary + * @type boolean + * @default true + */ + this.setAttributeConfig(this.OPT.SHOW_HEX_SUMMARY, { + value: (attr.showhexsummary) || true, + method: function(on) { + _hideShowEl.call(this, this.ID.HEX_SUMMARY, on); + + // can't show both the hsv controls and the rbg hex summary + if (on && this.get(this.OPT.SHOW_HSV_CONTROLS)) { + this.set(this.OPT.SHOW_HSV_CONTROLS, false); + } + } + }); + this.setAttributeConfig(this.OPT.ANIMATE, { + value: (attr.animate) || true, + method: function(on) { + this.pickerSlider.animate = on; + this.hueSlider.animate = on; + } + }); + + this.on(this.OPT.HUE + "Change", _updateRGBFromHSV, this, true); + this.on(this.OPT.SATURATION + "Change", _updateRGBFromHSV, this, true); + this.on(this.OPT.VALUE + "Change", _updatePickerSlider, this, true); + + this.on(this.OPT.RED + "Change", _updateRGB, this, true); + this.on(this.OPT.GREEN + "Change", _updateRGB, this, true); + this.on(this.OPT.BLUE + "Change", _updateRGB, this, true); + + this.on(this.OPT.HEX + "Change", _updateHex, this, true); + + this.initPicker(); + }; + + /** + * Updates the rgb attribute with the current state of the r,g,b + * fields. This is invoked from change listeners on these + * attributes to facilitate updating these values from the + * individual form fields + * @method _updateRGB + * @private + */ + var _updateRGB = function() { + var rgb = [this.get(this.OPT.RED), + this.get(this.OPT.GREEN), + this.get(this.OPT.BLUE)]; + + this.logger.log("RGB value set to " + rgb); + this.set(this.OPT.RGB, rgb); + + _updateSliders.call(this); + }; + + /** + * Updates the RGB values from the current state of the HSV + * values. Executed when the one of the HSV form fields are + * updated + * _updateRGBFromHSV + * @private + */ + var _updateRGBFromHSV = function() { + var hsv = [this.get(this.OPT.HUE), + this.get(this.OPT.SATURATION)/100, + this.get(this.OPT.VALUE)/100]; + + var rgb = Color.hsv2rgb(hsv); + + this.logger.log("HSV converted to RGB " + hsv + " : " + rgb); + this.set(this.OPT.RGB, rgb); + + _updateSliders.call(this); + }; + + /** + * Parses the hex string to normalize shorthand values, converts + * the hex value to rgb and updates the rgb attribute (which + * updates the state for all of the other values) + * method _updateHex + * @private + */ + var _updateHex = function() { + + var hex = this.get(this.OPT.HEX), l=hex.length; + + // support #369 -> #336699 shorthand + if (l === 3) { + var c = hex.split(""), i; + for (i=0; i 255||n<0)?0:n;return HCHARS.charAt((n-n%16)/16)+HCHARS.charAt(n%16);},hex2dec:function(str){var f=function(c){return HCHARS.indexOf(c.toUpperCase());};var s=str.split('');return((f(s[0])*16)+f(s[1]));},hex2rgb:function(s){var f=this.hex2dec;return[f(s.substr(0,2)),f(s.substr(2,2)),f(s.substr(4,2))];},websafe:function(r,g,b){if(lang.isArray(r)){return this.websafe.call(this,r[0],r[1],r[2]);} +var f=function(v){if(lang.isNumber(v)){v=Math.min(Math.max(0,v),255);var i,next;for(i=0;i<256;i=i+51){next=i+51;if(v>=i&&v<=next){return(v-i>25)?next:i;}}} +return v;};return[f(r),f(g),f(b)];}};}();(function(){var pickercount=0;YAHOO.widget.ColorPicker=function(el,attr){pickercount=pickercount+1;attr=attr||{};if(arguments.length===1&&!YAHOO.lang.isString(el)&&!el.nodeName){attr=el;el=attr.element||null;} +if(!el&&!attr.element){el=_createHostElement.call(this,attr);} +YAHOO.widget.ColorPicker.superclass.constructor.call(this,el,attr);};YAHOO.extend(YAHOO.widget.ColorPicker,YAHOO.util.Element);var proto=YAHOO.widget.ColorPicker.prototype,Slider=YAHOO.widget.Slider,Color=YAHOO.util.Color,Dom=YAHOO.util.Dom,Event=YAHOO.util.Event,lang=YAHOO.lang,sub=lang.substitute;var b="yui-picker";proto.ID={R:b+"-r",R_HEX:b+"-rhex",G:b+"-g",G_HEX:b+"-ghex",B:b+"-b",B_HEX:b+"-bhex",H:b+"-h",S:b+"-s",V:b+"-v",PICKER_BG:b+"-bg",PICKER_THUMB:b+"-thumb",HUE_BG:b+"-hue-bg",HUE_THUMB:b+"-hue-thumb",HEX:b+"-hex",SWATCH:b+"-swatch",WEBSAFE_SWATCH:b+"-websafe-swatch",CONTROLS:b+"-controls",RGB_CONTROLS:b+"-rgb-controls",HSV_CONTROLS:b+"-hsv-controls",HEX_CONTROLS:b+"-hex-controls",HEX_SUMMARY:b+"-hex-summary",CONTROLS_LABEL:b+"-controls-label"};proto.TXT={ILLEGAL_HEX:"Illegal hex value entered",SHOW_CONTROLS:"Show color details",HIDE_CONTROLS:"Hide color details",CURRENT_COLOR:"Currently selected color: {rgb}",CLOSEST_WEBSAFE:"Closest websafe color: {rgb}. Click to select.",R:"R",G:"G",B:"B",H:"H",S:"S",V:"V",HEX:"#",DEG:"\u00B0",PERCENT:"%"};proto.IMAGE={PICKER_THUMB:"../../build/colorpicker/assets/picker_thumb.png",HUE_THUMB:"../../build/colorpicker/assets/hue_thumb.png"};proto.DEFAULT={PICKER_SIZE:180};proto.OPT={HUE:"hue",SATURATION:"saturation",VALUE:"value",RED:"red",GREEN:"green",BLUE:"blue",HSV:"hsv",RGB:"rgb",WEBSAFE:"websafe",HEX:"hex",PICKER_SIZE:"pickersize",SHOW_CONTROLS:"showcontrols",SHOW_RGB_CONTROLS:"showrgbcontrols",SHOW_HSV_CONTROLS:"showhsvcontrols",SHOW_HEX_CONTROLS:"showhexcontrols",SHOW_HEX_SUMMARY:"showhexsummary",SHOW_WEBSAFE:"showwebsafe",CONTAINER:"container",IDS:"ids",ELEMENTS:"elements",TXT:"txt",IMAGES:"images",ANIMATE:"animate"};proto.setValue=function(rgb,silent){silent=(silent)||false;this.set(this.OPT.RGB,rgb,silent);_updateSliders.call(this);};proto.hueSlider=null;proto.pickerSlider=null;var _getH=function(){var size=this.get(this.OPT.PICKER_SIZE),h=(size-this.hueSlider.getValue())/size;h=Math.round(h*360);return(h===360)?0:h;};var _getS=function(){return this.pickerSlider.getXValue()/this.get(this.OPT.PICKER_SIZE);};var _getV=function(){var size=this.get(this.OPT.PICKER_SIZE);return(size-this.pickerSlider.getYValue())/size;};var _updateSwatch=function(){var rgb=this.get(this.OPT.RGB),websafe=this.get(this.OPT.WEBSAFE),el=this.getElement(this.ID.SWATCH),color=rgb.join(","),txt=this.get(this.OPT.TXT);Dom.setStyle(el,"background-color","rgb("+color+")");el.title=lang.substitute(txt.CURRENT_COLOR,{"rgb":"#"+this.get(this.OPT.HEX)});el=this.getElement(this.ID.WEBSAFE_SWATCH);color=websafe.join(",");Dom.setStyle(el,"background-color","rgb("+color+")");el.title=lang.substitute(txt.CLOSEST_WEBSAFE,{"rgb":"#"+Color.rgb2hex(websafe)});};var _getValuesFromSliders=function(){var h=_getH.call(this),s=_getS.call(this),v=_getV.call(this);rgb=Color.hsv2rgb(h,s,v);var websafe=Color.websafe(rgb);var hex=Color.rgb2hex(rgb[0],rgb[1],rgb[2]);this.set(this.OPT.RGB,rgb);};var _updateFormFields=function(){this.getElement(this.ID.H).value=this.get(this.OPT.HUE);this.getElement(this.ID.S).value=this.get(this.OPT.SATURATION);this.getElement(this.ID.V).value=this.get(this.OPT.VALUE);this.getElement(this.ID.R).value=this.get(this.OPT.RED);this.getElement(this.ID.R_HEX).innerHTML=Color.dec2hex(this.get(this.OPT.RED));this.getElement(this.ID.G).value=this.get(this.OPT.GREEN);this.getElement(this.ID.G_HEX).innerHTML=Color.dec2hex(this.get(this.OPT.GREEN));this.getElement(this.ID.B).value=this.get(this.OPT.BLUE);this.getElement(this.ID.B_HEX).innerHTML=Color.dec2hex(this.get(this.OPT.BLUE));this.getElement(this.ID.HEX).value=this.get(this.OPT.HEX);};var _onHueSliderChange=function(newOffset){var h=_getH.call(this);this.set(this.OPT.HUE,h,true);var rgb=Color.hsv2rgb(h,1,1);var styleDef="rgb("+rgb.join(",")+")";Dom.setStyle(this.getElement(this.ID.PICKER_BG),"background-color",styleDef);if(this.hueSlider.valueChangeSource===this.hueSlider.SOURCE_UI_EVENT){_getValuesFromSliders.call(this);} +_updateFormFields.call(this);_updateSwatch.call(this);};var _onPickerSliderChange=function(newOffset){var s=_getS.call(this),v=_getV.call(this);this.set(this.OPT.SATURATION,Math.round(s*100),true);this.set(this.OPT.VALUE,Math.round(v*100),true);if(this.pickerSlider.valueChangeSource===this.pickerSlider.SOURCE_UI_EVENT){_getValuesFromSliders.call(this);} +_updateFormFields.call(this);_updateSwatch.call(this);};var _getCommand=function(e){var c=Event.getCharCode(e);if(c===38){return 3;}else if(c===13){return 6;}else if(c===40){return 4;}else if(c>=48&&c<=57){return 1;}else if(c>=97&&c<=102){return 2;}else if(c>=65&&c<=70){return 2;}else if("8, 9, 13, 27, 37, 39".indexOf(c)>-1){return 5;}else{return 0;}};var _rgbFieldKeypress=function(e,el,prop){var command=_getCommand(e);var inc=(e.shiftKey)?10:1;switch(command){case 6:_useFieldValue.apply(this,arguments);break;case 3:this.set(prop,Math.min(this.get(prop)+inc,255));_updateFormFields.call(this);break;case 4:this.set(prop,Math.max(this.get(prop)-inc,0));_updateFormFields.call(this);break;default:}};var _hexFieldKeypress=function(e,el,prop){var command=_getCommand(e);if(command===6){_useFieldValue.apply(this,arguments);}};var _useFieldValue=function(e,el,prop){var val=el.value;if(prop!==this.OPT.HEX){val=parseInt(val,10);} +if(val!==this.get(prop)){this.set(prop,val);}};var _numbersOnly=function(e){return _hexOnly(e,true);};var _hexOnly=function(e,numbersOnly){var command=_getCommand(e);switch(command){case 6:case 5:case 1:break;case 2:if(numbersOnly!==true){break;} +default:Event.stopEvent(e);return false;}};proto.getElement=function(id){return this.get(this.OPT.ELEMENTS)[this.get(this.OPT.IDS)[id]];};_createElements=function(){var el,child,img,fld,i,ids=this.get(this.OPT.IDS),txt=this.get(this.OPT.TXT),images=this.get(this.OPT.IMAGES),Elem=function(type,o){var n=document.createElement(type);if(o){lang.augmentObject(n,o,true);} +return n;},RGBElem=function(type,obj){var o=lang.merge({autocomplete:"off",value:"0",size:3,maxlength:3},obj);o.name=o.id;return new Elem(type,o);};var p=this.get("element");el=new Elem("div",{id:ids[this.ID.PICKER_BG],className:"yui-picker-bg",tabIndex:-1,hideFocus:true});child=new Elem("div",{id:ids[this.ID.PICKER_THUMB],className:"yui-picker-thumb"});img=new Elem("img",{src:images.PICKER_THUMB});child.appendChild(img);el.appendChild(child);p.appendChild(el);el=new Elem("div",{id:ids[this.ID.HUE_BG],className:"yui-picker-hue-bg",tabIndex:-1,hideFocus:true});child=new Elem("div",{id:ids[this.ID.HUE_THUMB],className:"yui-picker-hue-thumb"});img=new Elem("img",{src:images.HUE_THUMB});child.appendChild(img);el.appendChild(child);p.appendChild(el);el=new Elem("div",{id:ids[this.ID.CONTROLS],className:"yui-picker-controls"});p.appendChild(el);p=el;el=new Elem("div",{className:"hd"});child=new Elem("a",{id:ids[this.ID.CONTROLS_LABEL],href:"#"});el.appendChild(child);p.appendChild(el);el=new Elem("div",{className:"bd"});p.appendChild(el);p=el;el=new Elem("ul",{id:ids[this.ID.RGB_CONTROLS],className:"yui-picker-rgb-controls"});child=new Elem("li");child.appendChild(document.createTextNode(txt.R+" "));fld=new RGBElem("input",{id:ids[this.ID.R],className:"yui-picker-r"});child.appendChild(fld);el.appendChild(child);child=new Elem("li");child.appendChild(document.createTextNode(txt.G+" "));fld=new RGBElem("input",{id:ids[this.ID.G],className:"yui-picker-g"});child.appendChild(fld);el.appendChild(child);child=new Elem("li");child.appendChild(document.createTextNode(txt.B+" "));fld=new RGBElem("input",{id:ids[this.ID.B],className:"yui-picker-b"});child.appendChild(fld);el.appendChild(child);p.appendChild(el);el=new Elem("ul",{id:ids[this.ID.HSV_CONTROLS],className:"yui-picker-hsv-controls"});child=new Elem("li");child.appendChild(document.createTextNode(txt.H+" "));fld=new RGBElem("input",{id:ids[this.ID.H],className:"yui-picker-h"});child.appendChild(fld);child.appendChild(document.createTextNode(" "+txt.DEG));el.appendChild(child);child=new Elem("li");child.appendChild(document.createTextNode(txt.S+" "));fld=new RGBElem("input",{id:ids[this.ID.S],className:"yui-picker-s"});child.appendChild(fld);child.appendChild(document.createTextNode(" "+txt.PERCENT));el.appendChild(child);child=new Elem("li");child.appendChild(document.createTextNode(txt.V+" "));fld=new RGBElem("input",{id:ids[this.ID.V],className:"yui-picker-v"});child.appendChild(fld);child.appendChild(document.createTextNode(" "+txt.PERCENT));el.appendChild(child);p.appendChild(el);el=new Elem("ul",{id:ids[this.ID.HEX_SUMMARY],className:"yui-picker-hex_summary"});child=new Elem("li",{id:ids[this.ID.R_HEX]});el.appendChild(child);child=new Elem("li",{id:ids[this.ID.G_HEX]});el.appendChild(child);child=new Elem("li",{id:ids[this.ID.B_HEX]});el.appendChild(child);p.appendChild(el);el=new Elem("div",{id:ids[this.ID.HEX_CONTROLS],className:"yui-picker-hex-controls"});el.appendChild(document.createTextNode(txt.HEX+" "));child=new RGBElem("input",{id:ids[this.ID.HEX],className:"yui-picker-hex",size:6,maxlength:6});el.appendChild(child);p.appendChild(el);p=this.get("element");el=new Elem("div",{id:ids[this.ID.SWATCH],className:"yui-picker-swatch"});p.appendChild(el);el=new Elem("div",{id:ids[this.ID.WEBSAFE_SWATCH],className:"yui-picker-websafe-swatch"});p.appendChild(el);};proto.initPicker=function(){var o=this.OPT,ids=this.get(o.IDS),els=this.get(o.ELEMENTS),i,el,id;for(i in this.ID){if(lang.hasOwnProperty(this.ID,i)){ids[this.ID[i]]=ids[i];}} +el=Dom.get(ids[this.ID.PICKER_BG]);if(!el){_createElements.call(this);}else{} +for(i in ids){if(lang.hasOwnProperty(ids,i)){el=Dom.get(ids[i]);id=Dom.generateId(el);ids[i]=id;ids[ids[i]]=id;els[id]=el;}} +els=[o.SHOW_CONTROLS,o.SHOW_RGB_CONTROLS,o.SHOW_HSV_CONTROLS,o.SHOW_HEX_CONTROLS,o.SHOW_HEX_SUMMARY,o.SHOW_WEBSAFE];for(i=0;i 1){for(var i in ids){if(lang.hasOwnProperty(ids,i)){ids[i]=ids[i]+pickercount;}}} +this.setAttributeConfig(this.OPT.IDS,{value:ids,writeonce:true});this.setAttributeConfig(this.OPT.TXT,{value:attr.txt||this.TXT,writeonce:true});this.setAttributeConfig(this.OPT.IMAGES,{value:attr.images||this.IMAGE,writeonce:true});this.setAttributeConfig(this.OPT.ELEMENTS,{value:{},readonly:true});_hideShowEl=function(id,on){var el=(lang.isString(id)?this.getElement(id):id);Dom.setStyle(el,"display",(on)?"":"none");};this.setAttributeConfig(this.OPT.SHOW_CONTROLS,{value:(attr.showcontrols)||true,method:function(on){var el=Dom.getElementsByClassName("bd","div",this.getElement(this.ID.CONTROLS))[0];_hideShowEl.call(this,el,on);this.getElement(this.ID.CONTROLS_LABEL).innerHTML=(on)?this.get(this.OPT.TXT).HIDE_CONTROLS:this.get(this.OPT.TXT).SHOW_CONTROLS;}});this.setAttributeConfig(this.OPT.SHOW_RGB_CONTROLS,{value:(attr.showrgbcontrols)||true,method:function(on){_hideShowEl.call(this,this.ID.RGB_CONTROLS,on);}});this.setAttributeConfig(this.OPT.SHOW_HSV_CONTROLS,{value:(attr.showhsvcontrols)||false,method:function(on){_hideShowEl.call(this,this.ID.HSV_CONTROLS,on);if(on&&this.get(this.OPT.SHOW_HEX_SUMMARY)){this.set(this.OPT.SHOW_HEX_SUMMARY,false);}}});this.setAttributeConfig(this.OPT.SHOW_HEX_CONTROLS,{value:(attr.showhexcontrols)||false,method:function(on){_hideShowEl.call(this,this.ID.HEX_CONTROLS,on);}});this.setAttributeConfig(this.OPT.SHOW_WEBSAFE,{value:(attr.showwebsafe)||true,method:function(on){_hideShowEl.call(this,this.ID.WEBSAFE_SWATCH,on);}});this.setAttributeConfig(this.OPT.SHOW_HEX_SUMMARY,{value:(attr.showhexsummary)||true,method:function(on){_hideShowEl.call(this,this.ID.HEX_SUMMARY,on);if(on&&this.get(this.OPT.SHOW_HSV_CONTROLS)){this.set(this.OPT.SHOW_HSV_CONTROLS,false);}}});this.setAttributeConfig(this.OPT.ANIMATE,{value:(attr.animate)||true,method:function(on){this.pickerSlider.animate=on;this.hueSlider.animate=on;}});this.on(this.OPT.HUE+"Change",_updateRGBFromHSV,this,true);this.on(this.OPT.SATURATION+"Change",_updateRGBFromHSV,this,true);this.on(this.OPT.VALUE+"Change",_updatePickerSlider,this,true);this.on(this.OPT.RED+"Change",_updateRGB,this,true);this.on(this.OPT.GREEN+"Change",_updateRGB,this,true);this.on(this.OPT.BLUE+"Change",_updateRGB,this,true);this.on(this.OPT.HEX+"Change",_updateHex,this,true);this.initPicker();};var _updateRGB=function(){var rgb=[this.get(this.OPT.RED),this.get(this.OPT.GREEN),this.get(this.OPT.BLUE)];this.set(this.OPT.RGB,rgb);_updateSliders.call(this);};var _updateRGBFromHSV=function(){var hsv=[this.get(this.OPT.HUE),this.get(this.OPT.SATURATION)/100,this.get(this.OPT.VALUE)/100];var rgb=Color.hsv2rgb(hsv);this.set(this.OPT.RGB,rgb);_updateSliders.call(this);};var _updateHex=function(){var hex=this.get(this.OPT.HEX),l=hex.length;if(l===3){var c=hex.split(""),i;for(i=0;i FFFFFF + * @method rgb2hex + * @param r {int|[int, int, int]} the red value, or an + * array containing all three parameters + * @param g {int} the green value + * @param b {int} the blue value + * @return {string} the hex string + */ + rgb2hex: function(r, g, b) { + if (lang.isArray(r)) { + return this.rgb2hex.call(this, r[0], r[1], r[2]); + } + + var f=this.dec2hex; + return f(r) + f(g) + f(b); + }, + + /** + * Converts an int 0...255 to hex pair 00...FF + * @method dec2hex + * @param n {int} the number to convert + * @return {string} the hex equivalent + */ + dec2hex: function(n) { + n = parseInt(n, 10); + n = (lang.isNumber(n)) ? n : 0; + n = (n > 255 || n < 0) ? 0 : n; + + return HCHARS.charAt((n - n % 16) / 16) + HCHARS.charAt(n % 16); + }, + + /** + * Converts a hex pair 00...FF to an int 0...255 + * @method hex2dec + * @param str {string} the hex pair to convert + * @return {int} the decimal + */ + hex2dec: function(str) { + var f = function(c) { + return HCHARS.indexOf(c.toUpperCase()); + }; + + var s=str.split(''); + + return ((f(s[0]) * 16) + f(s[1])); + }, + + /** + * Converts a hex string to rgb + * @method hex2rgb + * @param str {string} the hex string + * @return {[int, int, int]} an array containing the rgb values + */ + hex2rgb: function(s) { + var f = this.hex2dec; + return [f(s.substr(0, 2)), f(s.substr(2, 2)), f(s.substr(4, 2))]; + }, + + /** + * Returns the closest websafe color to the supplied rgb value. + * @method websafe + * @param r {int|[int, int, int]} the red value, or an + * array containing all three parameters + * @param g {int} the green value + * @param b {int} the blue value + * @return {[int, int, int]} an array containing the closes + * websafe rgb colors. + */ + websafe: function(r, g, b) { + + if (lang.isArray(r)) { + return this.websafe.call(this, r[0], r[1], r[2]); + } + + // returns the closest match [0, 51, 102, 153, 204, 255] + var f = function(v) { + if (lang.isNumber(v)) { + v = Math.min(Math.max(0, v), 255); + var i, next; + for (i=0; i<256; i=i+51) { + next = i+51; + if (v >= i && v <= next) { + return (v-i > 25) ? next : i; + } + } + } + + return v; + }; + + return [f(r), f(g), f(b)]; + } + }; +}(); + + +(function() { + + var pickercount = 0; + + /** + * The colorpicker module provides a widget for selecting colors + * @module colorpicker + * @requires yahoo, dom, event, element, slider + */ + + /** + * A widget to select colors + * @namespace YAHOO.widget + * @class YAHOO.widget.ColorPicker + * @extends YAHOO.util.Element + * @constructor + * @param {HTMLElement | String | Object} el(optional) The html + * element that represents the colorpicker, or the attribute object to use. + * An element will be created if none provided. + * @param {Object} attr (optional) A key map of the colorpicker's + * initial attributes. Ignored if first arg is attributes object. + */ + YAHOO.widget.ColorPicker = function(el, attr) { + pickercount = pickercount + 1; + attr = attr || {}; + if (arguments.length === 1 && !YAHOO.lang.isString(el) && !el.nodeName) { + attr = el; // treat first arg as attr object + el = attr.element || null; + } + + if (!el && !attr.element) { // create if we dont have one + el = _createHostElement.call(this, attr); + } + + YAHOO.widget.ColorPicker.superclass.constructor.call(this, el, attr); + }; + + YAHOO.extend(YAHOO.widget.ColorPicker, YAHOO.util.Element); + + var proto = YAHOO.widget.ColorPicker.prototype, + Slider=YAHOO.widget.Slider, + Color=YAHOO.util.Color, + Dom = YAHOO.util.Dom, + Event = YAHOO.util.Event, + lang = YAHOO.lang, + sub = lang.substitute; + + + var b = "yui-picker"; + + /** + * The element ids used by this control + * @property ID + * @final + */ + proto.ID = { + + /** + * The id for the "red" form field + * @property ID.R + * @type String + * @final + * @default yui-picker-r + */ + R: b + "-r", + + /** + * The id for the "red" hex pair output + * @property ID.R_HEX + * @type String + * @final + * @default yui-picker-rhex + */ + R_HEX: b + "-rhex", + + /** + * The id for the "green" form field + * @property ID.G + * @type String + * @final + * @default yui-picker-g + */ + G: b + "-g", + + /** + * The id for the "green" hex pair output + * @property ID.G_HEX + * @type String + * @final + * @default yui-picker-ghex + */ + G_HEX: b + "-ghex", + + + /** + * The id for the "blue" form field + * @property ID.B + * @type String + * @final + * @default yui-picker-b + */ + B: b + "-b", + + /** + * The id for the "blue" hex pair output + * @property ID.B_HEX + * @type String + * @final + * @default yui-picker-bhex + */ + B_HEX: b + "-bhex", + + /** + * The id for the "hue" form field + * @property ID.H + * @type String + * @final + * @default yui-picker-h + */ + H: b + "-h", + + /** + * The id for the "saturation" form field + * @property ID.S + * @type String + * @final + * @default yui-picker-s + */ + S: b + "-s", + + /** + * The id for the "value" form field + * @property ID.V + * @type String + * @final + * @default yui-picker-v + */ + V: b + "-v", + + /** + * The id for the picker region slider + * @property ID.PICKER_BG + * @type String + * @final + * @default yui-picker-bg + */ + PICKER_BG: b + "-bg", + + /** + * The id for the picker region thumb + * @property ID.PICKER_THUMB + * @type String + * @final + * @default yui-picker-thumb + */ + PICKER_THUMB: b + "-thumb", + + /** + * The id for the hue slider + * @property ID.HUE_BG + * @type String + * @final + * @default yui-picker-hue-bg + */ + HUE_BG: b + "-hue-bg", + + /** + * The id for the hue thumb + * @property ID.HUE_THUMB + * @type String + * @final + * @default yui-picker-hue-thumb + */ + HUE_THUMB: b + "-hue-thumb", + + /** + * The id for the hex value form field + * @property ID.HEX + * @type String + * @final + * @default yui-picker-hex + */ + HEX: b + "-hex", + + /** + * The id for the color swatch + * @property ID.SWATCH + * @type String + * @final + * @default yui-picker-swatch + */ + SWATCH: b + "-swatch", + + /** + * The id for the websafe color swatch + * @property ID.WEBSAFE_SWATCH + * @type String + * @final + * @default yui-picker-websafe-swatch + */ + WEBSAFE_SWATCH: b + "-websafe-swatch", + + /** + * The id for the control details + * @property ID.CONTROLS + * @final + * @default yui-picker-controls + */ + CONTROLS: b + "-controls", + + /** + * The id for the rgb controls + * @property ID.RGB_CONTROLS + * @final + * @default yui-picker-rgb-controls + */ + RGB_CONTROLS: b + "-rgb-controls", + + /** + * The id for the hsv controls + * @property ID.HSV_CONTROLS + * @final + * @default yui-picker-hsv-controls + */ + HSV_CONTROLS: b + "-hsv-controls", + + /** + * The id for the hsv controls + * @property ID.HEX_CONTROLS + * @final + * @default yui-picker-hex-controls + */ + HEX_CONTROLS: b + "-hex-controls", + + /** + * The id for the hex summary + * @property ID.HEX_SUMMARY + * @final + * @default yui-picker-hex-summary + */ + HEX_SUMMARY: b + "-hex-summary", + + /** + * The id for the controls section header + * @property ID.CONTROLS_LABEL + * @final + * @default yui-picker-controls-label + */ + CONTROLS_LABEL: b + "-controls-label" + }; + + /** + * Constants for any script-generated messages. The values here + * are the default messages. They can be updated by providing + * the complete list to the constructor for the "txt" attribute. + * @property TXT + * @final + */ + proto.TXT = { + ILLEGAL_HEX: "Illegal hex value entered", + SHOW_CONTROLS: "Show color details", + HIDE_CONTROLS: "Hide color details", + CURRENT_COLOR: "Currently selected color: {rgb}", + CLOSEST_WEBSAFE: "Closest websafe color: {rgb}. Click to select.", + R: "R", + G: "G", + B: "B", + H: "H", + S: "S", + V: "V", + HEX: "#", + DEG: "\u00B0", + PERCENT: "%" + }; + + /** + * Constants for the default image locations for img tags that are + * generated by the control. They can be modified by passing the + * complete list to the contructor for the "images" attribute + * @property IMAGE + * @final + */ + proto.IMAGE = { + PICKER_THUMB: "../../build/colorpicker/assets/picker_thumb.png", + HUE_THUMB: "../../build/colorpicker/assets/hue_thumb.png" + }; + + /* + * Constants for the control's custom event names. subscribe + * to the rgbChange event instead. + * @property EVENT + * @final + */ + //proto.EVENT = { + //CHANGE: "change" + //}; + + //proto.CSS = { }; + + /** + * Constants for the control's default default values + * @property DEFAULT + * @final + */ + proto.DEFAULT = { + PICKER_SIZE: 180 + }; + + /** + * Constants for the control's configuration attributes + * @property OPT + * @final + */ + proto.OPT = { + HUE: "hue", + SATURATION: "saturation", + VALUE: "value", + RED: "red", + GREEN: "green", + BLUE: "blue", + HSV: "hsv", + RGB: "rgb", + WEBSAFE: "websafe", + HEX: "hex", + PICKER_SIZE: "pickersize", + SHOW_CONTROLS: "showcontrols", + SHOW_RGB_CONTROLS: "showrgbcontrols", + SHOW_HSV_CONTROLS: "showhsvcontrols", + SHOW_HEX_CONTROLS: "showhexcontrols", + SHOW_HEX_SUMMARY: "showhexsummary", + SHOW_WEBSAFE: "showwebsafe", + //SHOW_SUBMIT: "showsubmit", + CONTAINER: "container", + IDS: "ids", + ELEMENTS: "elements", + TXT: "txt", + IMAGES: "images", + ANIMATE: "animate" + }; + + /** + * Sets the control to the specified rgb value and + * moves the sliders to the proper positions + * @method setValue + * @param rgb {[int, int, int]} the rgb value + * @param silent {boolean} whether or not to fire the change event + */ + proto.setValue = function(rgb, silent) { + silent = (silent) || false; + this.set(this.OPT.RGB, rgb, silent); + _updateSliders.call(this); + }; + + /** + * The hue slider + * @property hueSlider + * @type YAHOO.widget.Slider + */ + proto.hueSlider = null; + + /** + * The picker region + * @property pickerSlider + * @type YAHOO.widget.Slider + */ + proto.pickerSlider = null; + + /** + * Translates the slider value into hue, int[0,359] + * @method _getH + * @private + * @return {int} the hue from 0 to 359 + */ + var _getH = function() { + var size = this.get(this.OPT.PICKER_SIZE), + h = (size - this.hueSlider.getValue()) / size; + h = Math.round(h*360); + return (h === 360) ? 0 : h; + }; + + /** + * Translates the slider value into saturation, int[0,1], left to right + * @method _getS + * @private + * @return {int} the saturation from 0 to 1 + */ + var _getS = function() { + return this.pickerSlider.getXValue() / this.get(this.OPT.PICKER_SIZE); + }; + + /** + * Translates the slider value into value/brightness, int[0,1], top + * to bottom + * @method _getV + * @private + * @return {int} the value from 0 to 1 + */ + var _getV = function() { + var size = this.get(this.OPT.PICKER_SIZE); + return (size - this.pickerSlider.getYValue()) / size; + }; + + /** + * Updates the background of the swatch with the current rbg value. + * Also updates the websafe swatch to the closest websafe color + * @method _updateSwatch + * @private + */ + var _updateSwatch = function() { + var rgb = this.get(this.OPT.RGB), + websafe = this.get(this.OPT.WEBSAFE), + el = this.getElement(this.ID.SWATCH), + color = rgb.join(","), + txt = this.get(this.OPT.TXT); + + Dom.setStyle(el, "background-color", "rgb(" + color + ")"); + el.title = lang.substitute(txt.CURRENT_COLOR, { + "rgb": "#" + this.get(this.OPT.HEX) + }); + + + el = this.getElement(this.ID.WEBSAFE_SWATCH); + color = websafe.join(","); + + Dom.setStyle(el, "background-color", "rgb(" + color + ")"); + el.title = lang.substitute(txt.CLOSEST_WEBSAFE, { + "rgb": "#" + Color.rgb2hex(websafe) + }); + + }; + + /** + * Reads the sliders and converts the values to RGB, updating the + * internal state for all the individual form fields + * @method _getValuesFromSliders + * @private + */ + var _getValuesFromSliders = function() { + var h=_getH.call(this), s=_getS.call(this), v=_getV.call(this); + + rgb = Color.hsv2rgb(h, s, v); + var websafe = Color.websafe(rgb); + var hex = Color.rgb2hex(rgb[0], rgb[1], rgb[2]); + + this.set(this.OPT.RGB, rgb); + }; + + /** + * Updates the form field controls with the state data contained + * in the control. + * @method _updateFormFields + * @private + */ + var _updateFormFields = function() { + this.getElement(this.ID.H).value = this.get(this.OPT.HUE); + this.getElement(this.ID.S).value = this.get(this.OPT.SATURATION); + this.getElement(this.ID.V).value = this.get(this.OPT.VALUE); + this.getElement(this.ID.R).value = this.get(this.OPT.RED); + this.getElement(this.ID.R_HEX).innerHTML = Color.dec2hex(this.get(this.OPT.RED)); + this.getElement(this.ID.G).value = this.get(this.OPT.GREEN); + this.getElement(this.ID.G_HEX).innerHTML = Color.dec2hex(this.get(this.OPT.GREEN)); + this.getElement(this.ID.B).value = this.get(this.OPT.BLUE); + this.getElement(this.ID.B_HEX).innerHTML = Color.dec2hex(this.get(this.OPT.BLUE)); + this.getElement(this.ID.HEX).value = this.get(this.OPT.HEX); + }; + + /** + * Event handler for the hue slider. + * @method _onHueSliderChange + * @param newOffset {int} pixels from the start position + * @private + */ + var _onHueSliderChange = function(newOffset) { + + var h = _getH.call(this); + this.set(this.OPT.HUE, h, true); + + // set picker background to the hue + var rgb = Color.hsv2rgb(h, 1, 1); + var styleDef = "rgb(" + rgb.join(",") + ")"; + + Dom.setStyle(this.getElement(this.ID.PICKER_BG), "background-color", styleDef); + + if (this.hueSlider.valueChangeSource === this.hueSlider.SOURCE_UI_EVENT) { + _getValuesFromSliders.call(this); + } + + _updateFormFields.call(this); + _updateSwatch.call(this); + }; + + /** + * Event handler for the picker slider, which controls the + * saturation and value/brightness. + * @method _onPickerSliderChange + * @param newOffset {{x: int, y: int}} x/y pixels from the start position + * @private + */ + var _onPickerSliderChange = function(newOffset) { + + var s=_getS.call(this), v=_getV.call(this); + this.set(this.OPT.SATURATION, Math.round(s*100), true); + this.set(this.OPT.VALUE, Math.round(v*100), true); + + if (this.pickerSlider.valueChangeSource === this.pickerSlider.SOURCE_UI_EVENT) { + _getValuesFromSliders.call(this); + } + + _updateFormFields.call(this); + _updateSwatch.call(this); + }; + + + /** + * Key map to well-known commands for txt field input + * @method _getCommand + * @param e {Event} the keypress or keydown event + * @return {int} a command code + * + *
+ * @private + */ + var _getCommand = function(e) { + var c = Event.getCharCode(e); + + //alert(Event.getCharCode(e) + ", " + e.keyCode + ", " + e.charCode); + + // special keys + if (c === 38) { // up arrow + return 3; + } else if (c === 13) { // return + return 6; + } else if (c === 40) { // down array + return 4; + } else if (c >= 48 && c<=57) { // 0-9 + return 1; + } else if (c >= 97 && c<=102) { // a-f + return 2; + } else if (c >= 65 && c<=70) { // A-F + return 2; + //} else if ("8, 9, 13, 27, 37, 39".indexOf(c) > -1 || + // (c >= 112 && c <=123)) { // including F-keys + // tab, delete, return, escape, left, right + } else if ("8, 9, 13, 27, 37, 39".indexOf(c) > -1) { // special chars + return 5; + } else { // something we probably don't want + return 0; + } + }; + + /** + * Handle keypress on one of the rgb or hsv fields. + * @method _rgbFieldKeypress + * @param e {Event} the keypress event + * @param el {HTMLElement} the field + * @param prop {string} the key to the linked property + * @private + */ + var _rgbFieldKeypress = function(e, el, prop) { + var command = _getCommand(e); + var inc = (e.shiftKey) ? 10 : 1; + switch (command) { + case 6: // return, update the value + _useFieldValue.apply(this, arguments); + break; + + case 3: // up arrow, increment + this.set(prop, Math.min(this.get(prop)+inc, 255)); + _updateFormFields.call(this); + //Event.stopEvent(e); + break; + case 4: // down arrow, decrement + this.set(prop, Math.max(this.get(prop)-inc, 0)); + _updateFormFields.call(this); + //Event.stopEvent(e); + break; + + default: + } + + }; + + /** + * Handle keydown on the hex field + * @method _hexFieldKeypress + * @param e {Event} the keypress event + * @param el {HTMLElement} the field + * @param prop {string} the key to the linked property + * @private + */ + var _hexFieldKeypress = function(e, el, prop) { + var command = _getCommand(e); + if (command === 6) { // return, update the value + _useFieldValue.apply(this, arguments); + } + }; + + /** + * Use the value of the text field to update the control + * @method _hexFieldKeypress + * @param e {Event} an event + * @param el {HTMLElement} the field + * @param prop {string} the key to the linked property + * @private + */ + var _useFieldValue = function(e, el, prop) { + var val = el.value; + + if (prop !== this.OPT.HEX) { + val = parseInt(val, 10); + } + + if (val !== this.get(prop)) { + this.set(prop, val); + } + }; + + /** + * Allows numbers and special chars only. Used for the + * rgb and hsv fields keypress handler. + * @method _numbersOnly + * @param e {Event} the event + * @private + * @return {boolean} false if we are canceling the event + */ + var _numbersOnly = function(e) { + return _hexOnly(e, true); + }; + + /** + * Allows numbers and special chars, and by default allows a-f. + * Used for the hex field keypress handler. + * @method _hexOnly + * @param e {Event} the event + * @param numbersOnly omits a-f if set to true + * @private + * @return {boolean} false if we are canceling the event + */ + var _hexOnly = function(e, numbersOnly) { + var command = _getCommand(e); + switch (command) { + case 6: // return + case 5: // special char + case 1: // number + break; + case 2: // hex char (a-f) + if (numbersOnly !== true) { + break; + } + + // fallthrough is intentional + + default: // prevent alpha and punctuation + Event.stopEvent(e); + return false; + } + }; + + /** + * Returns the element reference that is saved. The id can be either + * the element id, or the key for this id in the "id" config attribute. + * For instance, the host element id can be obtained by passing its + * id (default: "yui_picker") or by its key "YUI_PICKER". + * @param id {string} the element id, or key + * @return {HTMLElement} a reference to the element + */ + proto.getElement = function(id) { + return this.get(this.OPT.ELEMENTS)[this.get(this.OPT.IDS)[id]]; + }; + + _createElements = function() { + var el, child, img, fld, i, + ids = this.get(this.OPT.IDS), + txt = this.get(this.OPT.TXT), + images = this.get(this.OPT.IMAGES), + Elem = function(type, o) { + var n = document.createElement(type); + if (o) { + lang.augmentObject(n, o, true); + } + return n; + }, + RGBElem = function(type, obj) { + var o = lang.merge({ + //type: "txt", + autocomplete: "off", + value: "0", + size: 3, + maxlength: 3 + }, obj); + + o.name = o.id; + return new Elem(type, o); + }; + + var p = this.get("element"); + + // Picker slider (S and V) --------------------------------------------- + + el = new Elem("div", { + id: ids[this.ID.PICKER_BG], + className: "yui-picker-bg", + tabIndex: -1, + hideFocus: true + }); + + child = new Elem("div", { + id: ids[this.ID.PICKER_THUMB], + className: "yui-picker-thumb" + }); + + img = new Elem("img", { + src: images.PICKER_THUMB + }); + + child.appendChild(img); + el.appendChild(child); + p.appendChild(el); + + // Hue slider --------------------------------------------- + el = new Elem("div", { + id: ids[this.ID.HUE_BG], + className: "yui-picker-hue-bg", + tabIndex: -1, + hideFocus: true + }); + + child = new Elem("div", { + id: ids[this.ID.HUE_THUMB], + className: "yui-picker-hue-thumb" + }); + + img = new Elem("img", { + src: images.HUE_THUMB + }); + + child.appendChild(img); + el.appendChild(child); + p.appendChild(el); + + + // controls --------------------------------------------- + + el = new Elem("div", { + id: ids[this.ID.CONTROLS], + className: "yui-picker-controls" + }); + + p.appendChild(el); + p = el; + + // controls header + el = new Elem("div", { + className: "hd" + }); + + child = new Elem("a", { + id: ids[this.ID.CONTROLS_LABEL], + //className: "yui-picker-controls-label", + href: "#" + }); + el.appendChild(child); + p.appendChild(el); + + // bd + el = new Elem("div", { + className: "bd" + }); + + p.appendChild(el); + p = el; + + // rgb + el = new Elem("ul", { + id: ids[this.ID.RGB_CONTROLS], + className: "yui-picker-rgb-controls" + }); + + child = new Elem("li"); + child.appendChild(document.createTextNode(txt.R + " ")); + + fld = new RGBElem("input", { + id: ids[this.ID.R], + className: "yui-picker-r" + }); + + child.appendChild(fld); + el.appendChild(child); + + child = new Elem("li"); + child.appendChild(document.createTextNode(txt.G + " ")); + + fld = new RGBElem("input", { + id: ids[this.ID.G], + className: "yui-picker-g" + }); + + child.appendChild(fld); + el.appendChild(child); + + child = new Elem("li"); + child.appendChild(document.createTextNode(txt.B + " ")); + + fld = new RGBElem("input", { + id: ids[this.ID.B], + className: "yui-picker-b" + }); + + child.appendChild(fld); + el.appendChild(child); + + p.appendChild(el); + + // hsv + el = new Elem("ul", { + id: ids[this.ID.HSV_CONTROLS], + className: "yui-picker-hsv-controls" + }); + + child = new Elem("li"); + child.appendChild(document.createTextNode(txt.H + " ")); + + fld = new RGBElem("input", { + id: ids[this.ID.H], + className: "yui-picker-h" + }); + + child.appendChild(fld); + child.appendChild(document.createTextNode(" " + txt.DEG)); + + el.appendChild(child); + + child = new Elem("li"); + child.appendChild(document.createTextNode(txt.S + " ")); + + fld = new RGBElem("input", { + id: ids[this.ID.S], + className: "yui-picker-s" + }); + + child.appendChild(fld); + child.appendChild(document.createTextNode(" " + txt.PERCENT)); + + el.appendChild(child); + + child = new Elem("li"); + child.appendChild(document.createTextNode(txt.V + " ")); + + fld = new RGBElem("input", { + id: ids[this.ID.V], + className: "yui-picker-v" + }); + + child.appendChild(fld); + child.appendChild(document.createTextNode(" " + txt.PERCENT)); + + el.appendChild(child); + p.appendChild(el); + + + // hex summary + + el = new Elem("ul", { + id: ids[this.ID.HEX_SUMMARY], + className: "yui-picker-hex_summary" + }); + + child = new Elem("li", { + id: ids[this.ID.R_HEX] + }); + el.appendChild(child); + + child = new Elem("li", { + id: ids[this.ID.G_HEX] + }); + el.appendChild(child); + + child = new Elem("li", { + id: ids[this.ID.B_HEX] + }); + el.appendChild(child); + p.appendChild(el); + + // hex field + el = new Elem("div", { + id: ids[this.ID.HEX_CONTROLS], + className: "yui-picker-hex-controls" + }); + el.appendChild(document.createTextNode(txt.HEX + " ")); + + child = new RGBElem("input", { + id: ids[this.ID.HEX], + className: "yui-picker-hex", + size: 6, + maxlength: 6 + }); + + el.appendChild(child); + p.appendChild(el); + + p = this.get("element"); + + // swatch + el = new Elem("div", { + id: ids[this.ID.SWATCH], + className: "yui-picker-swatch" + }); + + p.appendChild(el); + + // websafe swatch + el = new Elem("div", { + id: ids[this.ID.WEBSAFE_SWATCH], + className: "yui-picker-websafe-swatch" + }); + + p.appendChild(el); + + }; + + /** + * Sets the initial state of the sliders + * @method initPicker + */ + proto.initPicker = function () { + + // bind all of our elements + var o=this.OPT, + ids = this.get(o.IDS), + els = this.get(o.ELEMENTS), + i, el, id; + + // Add the default value as a key for each element for easier lookup + for (i in this.ID) { + if (lang.hasOwnProperty(this.ID, i)) { + ids[this.ID[i]] = ids[i]; + } + } + + // Check for picker element, if not there, create all of them + el = Dom.get(ids[this.ID.PICKER_BG]); + if (!el) { + _createElements.call(this); + } else { + } + + for (i in ids) { + if (lang.hasOwnProperty(ids, i)) { + // look for element + el = Dom.get(ids[i]); + + // generate an id if the implementer passed in an element reference, + // and the element did not have an id already + id = Dom.generateId(el); + + // update the id in case we generated the id + ids[i] = id; // key is WEBSAFE_SWATCH + ids[ids[i]] = id; // key is websafe_swatch + + // store the dom ref + els[id] = el; + } + } + + // set the initial visibility state of our controls + els = [o.SHOW_CONTROLS, + o.SHOW_RGB_CONTROLS, + o.SHOW_HSV_CONTROLS, + o.SHOW_HEX_CONTROLS, + o.SHOW_HEX_SUMMARY, + o.SHOW_WEBSAFE + ]; + + for (i=0; i- 0 = not a number, letter in range, or special key
+ *- 1 = number
+ *- 2 = a-fA-F
+ *- 3 = increment (up arrow)
+ *- 4 = decrement (down arrow)
+ *- 5 = special key (tab, delete, return, escape, left, right)
+ *- 6 = return
+ *1) { + for (var i in ids) { + if (lang.hasOwnProperty(ids, i)) { + ids[i] = ids[i] + pickercount; + } + } + } + + + /** + * A list of element ids and/or element references used by the + * control. The default is the this.ID list, and can be customized + * by passing a list in the contructor + * @config ids + * @type {referenceid: realid} + * @writeonce + */ + this.setAttributeConfig(this.OPT.IDS, { + value: ids, + writeonce: true + }); + + /** + * A list of txt strings for internationalization. Default + * is this.TXT + * @config txt + * @type {key: txt} + * @writeonce + */ + this.setAttributeConfig(this.OPT.TXT, { + value: attr.txt || this.TXT, + writeonce: true + }); + + /** + * The img src default list + * is this.IMAGES + * @config images + * @type {key: image} + * @writeonce + */ + this.setAttributeConfig(this.OPT.IMAGES, { + value: attr.images || this.IMAGE, + writeonce: true + }); + /** + * The element refs used by this control. Set at initialization + * @config elements + * @type {id: HTMLElement} + * @readonly + */ + this.setAttributeConfig(this.OPT.ELEMENTS, { + value: {}, + readonly: true + }); + + /** + * Returns the cached element reference. If the id is not a string, it + * is assumed that it is an element and this is returned. + * @param id {string|HTMLElement} the element key, id, or ref + * @param on {boolean} hide or show. If true, show + * @private */ + _hideShowEl = function(id, on) { + var el = (lang.isString(id) ? this.getElement(id) : id); + //Dom.setStyle(id, "visibility", (on) ? "" : "hidden"); + Dom.setStyle(el, "display", (on) ? "" : "none"); + }; + + /** + * Hide/show the entire set of controls + * @config showcontrols + * @type boolean + * @default true + */ + this.setAttributeConfig(this.OPT.SHOW_CONTROLS, { + value: (attr.showcontrols) || true, + method: function(on) { + + var el = Dom.getElementsByClassName("bd", "div", + this.getElement(this.ID.CONTROLS))[0]; + + _hideShowEl.call(this, el, on); + + this.getElement(this.ID.CONTROLS_LABEL).innerHTML = + (on) ? this.get(this.OPT.TXT).HIDE_CONTROLS : + this.get(this.OPT.TXT).SHOW_CONTROLS; + + } + }); + + /** + * Hide/show the rgb controls + * @config showrgbcontrols + * @type boolean + * @default true + */ + this.setAttributeConfig(this.OPT.SHOW_RGB_CONTROLS, { + value: (attr.showrgbcontrols) || true, + method: function(on) { + //Dom.setStyle(this.getElement(this.ID.RBG_CONTROLS), "visibility", (on) ? "" : "hidden"); + _hideShowEl.call(this, this.ID.RGB_CONTROLS, on); + } + }); + + /** + * Hide/show the hsv controls + * @config showhsvcontrols + * @type boolean + * @default false + */ + this.setAttributeConfig(this.OPT.SHOW_HSV_CONTROLS, { + value: (attr.showhsvcontrols) || false, + method: function(on) { + //Dom.setStyle(this.getElement(this.ID.HSV_CONTROLS), "visibility", (on) ? "" : "hidden"); + _hideShowEl.call(this, this.ID.HSV_CONTROLS, on); + + // can't show both the hsv controls and the rbg hex summary + if (on && this.get(this.OPT.SHOW_HEX_SUMMARY)) { + this.set(this.OPT.SHOW_HEX_SUMMARY, false); + } + } + }); + + /** + * Hide/show the hex controls + * @config showhexcontrols + * @type boolean + * @default true + */ + this.setAttributeConfig(this.OPT.SHOW_HEX_CONTROLS, { + value: (attr.showhexcontrols) || false, + method: function(on) { + _hideShowEl.call(this, this.ID.HEX_CONTROLS, on); + } + }); + + /** + * Hide/show the websafe swatch + * @config showwebsafe + * @type boolean + * @default true + */ + this.setAttributeConfig(this.OPT.SHOW_WEBSAFE, { + value: (attr.showwebsafe) || true, + method: function(on) { + _hideShowEl.call(this, this.ID.WEBSAFE_SWATCH, on); + } + }); + + /** + * Hide/show the hex summary + * @config showhexsummary + * @type boolean + * @default true + */ + this.setAttributeConfig(this.OPT.SHOW_HEX_SUMMARY, { + value: (attr.showhexsummary) || true, + method: function(on) { + _hideShowEl.call(this, this.ID.HEX_SUMMARY, on); + + // can't show both the hsv controls and the rbg hex summary + if (on && this.get(this.OPT.SHOW_HSV_CONTROLS)) { + this.set(this.OPT.SHOW_HSV_CONTROLS, false); + } + } + }); + this.setAttributeConfig(this.OPT.ANIMATE, { + value: (attr.animate) || true, + method: function(on) { + this.pickerSlider.animate = on; + this.hueSlider.animate = on; + } + }); + + this.on(this.OPT.HUE + "Change", _updateRGBFromHSV, this, true); + this.on(this.OPT.SATURATION + "Change", _updateRGBFromHSV, this, true); + this.on(this.OPT.VALUE + "Change", _updatePickerSlider, this, true); + + this.on(this.OPT.RED + "Change", _updateRGB, this, true); + this.on(this.OPT.GREEN + "Change", _updateRGB, this, true); + this.on(this.OPT.BLUE + "Change", _updateRGB, this, true); + + this.on(this.OPT.HEX + "Change", _updateHex, this, true); + + this.initPicker(); + }; + + /** + * Updates the rgb attribute with the current state of the r,g,b + * fields. This is invoked from change listeners on these + * attributes to facilitate updating these values from the + * individual form fields + * @method _updateRGB + * @private + */ + var _updateRGB = function() { + var rgb = [this.get(this.OPT.RED), + this.get(this.OPT.GREEN), + this.get(this.OPT.BLUE)]; + + this.set(this.OPT.RGB, rgb); + + _updateSliders.call(this); + }; + + /** + * Updates the RGB values from the current state of the HSV + * values. Executed when the one of the HSV form fields are + * updated + * _updateRGBFromHSV + * @private + */ + var _updateRGBFromHSV = function() { + var hsv = [this.get(this.OPT.HUE), + this.get(this.OPT.SATURATION)/100, + this.get(this.OPT.VALUE)/100]; + + var rgb = Color.hsv2rgb(hsv); + + this.set(this.OPT.RGB, rgb); + + _updateSliders.call(this); + }; + + /** + * Parses the hex string to normalize shorthand values, converts + * the hex value to rgb and updates the rgb attribute (which + * updates the state for all of the other values) + * method _updateHex + * @private + */ + var _updateHex = function() { + + var hex = this.get(this.OPT.HEX), l=hex.length; + + // support #369 -> #336699 shorthand + if (l === 3) { + var c = hex.split(""), i; + for (i=0; i = 200 && httpStatus < 300){ + if(httpStatus >= 200 && httpStatus < 300 || httpStatus === 1223){ responseObject = this.createResponseObject(o, callback.argument); if(callback.success){ if(!callback.scope){ @@ -430,6 +672,14 @@ YAHOO.util.Connect = YAHOO.log('Success callback with scope. HTTP code is ' + httpStatus, 'info', 'Connection'); } } + + // Fire global custom event -- successEvent + this.successEvent.fire(responseObject); + + if(o.successEvent){ + // Fire transaction custom event -- successEvent + o.successEvent.fire(responseObject); + } } else{ switch(httpStatus){ @@ -465,6 +715,15 @@ YAHOO.util.Connect = } } } + + // Fire global custom event -- failureEvent + this.failureEvent.fire(responseObject); + + if(o.failureEvent){ + // Fire transaction custom event -- failureEvent + o.failureEvent.fire(responseObject); + } + } this.releaseObject(o); @@ -501,8 +760,10 @@ YAHOO.util.Connect = catch(e){} obj.tId = o.tId; - obj.status = o.conn.status; - obj.statusText = o.conn.statusText; + // Normalize IE's response to HTTP 204 when Win error 1223. + obj.status = (o.conn.status == 1223)?204:o.conn.status; + // Normalize IE's statusText to "No Content" instead of "Unknown". + obj.statusText = (o.conn.status == 1223)?"No Content":o.conn.statusText; obj.getResponseHeader = headerObj; obj.getAllResponseHeaders = headerStr; obj.responseText = o.conn.responseText; @@ -558,28 +819,38 @@ YAHOO.util.Connect = }, /** - * @description Public method that stores the custom HTTP headers for each transaction. + * @description Method that initializes the custom HTTP headers for the each transaction. * @method initHeader * @public * @static * @param {string} label The HTTP header label * @param {string} value The HTTP header value + * @param {string} isDefault Determines if the specific header is a default header + * automatically sent with each transaction. * @return {void} */ - initHeader:function(label,value) + initHeader:function(label,value,isDefault) { - if(this._http_header[label] === undefined){ - this._http_header[label] = value; + var headerObj = (isDefault)?this._default_headers:this._http_headers; + + if(headerObj[label] === undefined){ + headerObj[label] = value; } else{ // Concatenate multiple values, comma-delimited, // for the same header label, - this._http_header[label] = value + "," + this._http_header[label]; + headerObj[label] = value + "," + headerObj[label]; } - this._has_http_headers = true; + if(isDefault){ + this._has_default_headers = true; + } + else{ + this._has_http_headers = true; + } }, + /** * @description Accessor that sets the HTTP headers for each transaction. * @method setHeader @@ -590,30 +861,53 @@ YAHOO.util.Connect = */ setHeader:function(o) { - for(var prop in this._http_header){ - if(this._http_header.hasOwnProperty(prop)){ - o.conn.setRequestHeader(prop, this._http_header[prop]); - YAHOO.log('HTTP header ' + prop + ' set with value of ' + this._http_header[prop], 'info', 'Connection'); + if(this._has_default_headers){ + for(var prop in this._default_headers){ + if(YAHOO.lang.hasOwnProperty(this._default_headers, prop)){ + o.conn.setRequestHeader(prop, this._default_headers[prop]); + YAHOO.log('Default HTTP header ' + prop + ' set with value of ' + this._default_headers[prop], 'info', 'Connection'); + } } } - delete this._http_header; - this._http_header = {}; - this._has_http_headers = false; + if(this._has_http_headers){ + for(var prop in this._http_headers){ + if(YAHOO.lang.hasOwnProperty(this._http_headers, prop)){ + o.conn.setRequestHeader(prop, this._http_headers[prop]); + YAHOO.log('HTTP header ' + prop + ' set with value of ' + this._http_headers[prop], 'info', 'Connection'); + } + } + delete this._http_headers; + + this._http_headers = {}; + this._has_http_headers = false; + } + }, + + /** + * @description Resets the default HTTP headers object + * @method resetDefaultHeaders + * @public + * @static + * @return {void} + */ + resetDefaultHeaders:function(){ + delete this._default_headers; + this._default_headers = {}; + this._has_default_headers = false; }, /** * @description This method assembles the form label and value pairs and * constructs an encoded string. - * asyncRequest() will automatically initialize the - * transaction with a HTTP header Content-Type of - * application/x-www-form-urlencoded. + * asyncRequest() will automatically initialize the transaction with a + * a HTTP header Content-Type of application/x-www-form-urlencoded. * @method setForm * @public * @static * @param {string || object} form id or name attribute, or form object. - * @param {string} optional boolean to indicate SSL environment. - * @param {string || boolean} optional qualified path of iframe resource for SSL in IE. + * @param {boolean} optional enable file upload. + * @param {boolean} optional enable file upload over SSL in IE only. * @return {string} string of the HTML form field name and value pairs.. */ setForm:function(formId, isUpload, secureUri) @@ -644,14 +938,14 @@ YAHOO.util.Connect = if(isUpload){ // Create iframe in preparation for file upload. - this.createFrame(secureUri?secureUri:null); - + var io = this.createFrame(secureUri?secureUri:null); // Set form reference and file upload properties to true. this._isFormSubmit = true; this._isFileUpload = true; this._formNode = oForm; return; + } var oElement, oName, oValue, oDisabled; @@ -669,7 +963,7 @@ YAHOO.util.Connect = // do not have a name attribute value. if(!oDisabled && oName) { - switch (oElement.type) + switch(oElement.type) { case 'select-one': case 'select-multiple': @@ -681,7 +975,6 @@ YAHOO.util.Connect = else{ this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oElement.options[j].hasAttribute('value')?oElement.options[j].value:oElement.options[j].text) + '&'; } - } } break; @@ -701,14 +994,19 @@ YAHOO.util.Connect = // stub case for input type button elements. break; case 'submit': - if(hasSubmit == false){ - this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&'; + if(hasSubmit === false){ + if(this._hasSubmitListener && this._submitElementValue){ + this._sFormData += this._submitElementValue + '&'; + } + else{ + this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&'; + } + hasSubmit = true; } break; default: this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&'; - break; } } } @@ -751,8 +1049,9 @@ YAHOO.util.Connect = // properties via createElement(). A different iframe creation // pattern is required for IE. var frameId = 'yuiIO' + this._transaction_id; + var io; if(window.ActiveXObject){ - var io = document.createElement(''); + io = document.createElement(''); // IE will throw a security exception in an SSL environment if the // iframe source is undefined. @@ -765,7 +1064,7 @@ YAHOO.util.Connect = } } else{ - var io = document.createElement('iframe'); + io = document.createElement('iframe'); io.id = frameId; io.name = frameId; } @@ -806,72 +1105,125 @@ YAHOO.util.Connect = }, /** - * @description Uploads HTML form, including files/attachments, to the - * iframe created in createFrame. + * @description Uploads HTML form, inclusive of files/attachments, using the + * iframe created in createFrame to facilitate the transaction. * @method uploadFile * @private * @static * @param {int} id The transaction id. - * @param {object} callback - User-defined callback object. + * @param {object} callback User-defined callback object. * @param {string} uri Fully qualified path of resource. + * @param {string} postData POST data to be submitted in addition to HTML form. * @return {void} */ - uploadFile:function(id, callback, uri, postData){ + uploadFile:function(o, callback, uri, postData){ // Each iframe has an id prefix of "yuiIO" followed // by the unique transaction id. - var frameId = 'yuiIO' + id; + var frameId = 'yuiIO' + o.tId; + var uploadEncoding = 'multipart/form-data'; var io = document.getElementById(frameId); + var oConn = this; + + // Track original HTML form attribute values. + var rawFormAttributes = + { + action:this._formNode.getAttribute('action'), + method:this._formNode.getAttribute('method'), + target:this._formNode.getAttribute('target') + }; // Initialize the HTML form properties in case they are // not defined in the HTML form. - this._formNode.action = uri; - this._formNode.method = 'POST'; - this._formNode.target = frameId; + this._formNode.setAttribute('action', uri); + this._formNode.setAttribute('method', 'POST'); + this._formNode.setAttribute('target', frameId); if(this._formNode.encoding){ // IE does not respect property enctype for HTML forms. - // Instead use property encoding. - this._formNode.encoding = 'multipart/form-data'; + // Instead it uses the property - "encoding". + this._formNode.setAttribute('encoding', uploadEncoding); } else{ - this._formNode.enctype = 'multipart/form-data'; + this._formNode.setAttribute('enctype', uploadEncoding); } - if(postData){ var oElements = this.appendPostData(postData); } + // Start file upload. this._formNode.submit(); + // Fire global custom event -- startEvent + this.startEvent.fire(o); + + if(o.startEvent){ + // Fire transaction custom event -- startEvent + o.startEvent.fire(o); + } + + // Start polling if a callback is present and the timeout + // property has been defined. + if(callback && callback.timeout){ + this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout); + } + + // Remove HTML elements created by appendPostData if(oElements && oElements.length > 0){ for(var i=0; i < oElements.length; i++){ this._formNode.removeChild(oElements[i]); } } - // Reset HTML form status properties. + // Restore HTML form attributes to their original + // values prior to file upload. + for(var prop in rawFormAttributes){ + if(YAHOO.lang.hasOwnProperty(rawFormAttributes, prop)){ + if(rawFormAttributes[prop]){ + this._formNode.setAttribute(prop, rawFormAttributes[prop]); + } + else{ + this._formNode.removeAttribute(prop); + } + } + } + + // Reset HTML form state properties. this.resetFormState(); // Create the upload callback handler that fires when the iframe // receives the load event. Subsequently, the event handler is detached // and the iframe removed from the document. - var uploadCallback = function() { + if(callback && callback.timeout){ + window.clearTimeout(oConn._timeOut[o.tId]); + delete oConn._timeOut[o.tId]; + } + + // Fire global custom event -- completeEvent + oConn.completeEvent.fire(o); + + if(o.completeEvent){ + // Fire transaction custom event -- completeEvent + o.completeEvent.fire(o); + } + var obj = {}; - obj.tId = id; + obj.tId = o.tId; obj.argument = callback.argument; try { - obj.responseText = io.contentWindow.document.body?io.contentWindow.document.body.innerHTML:null; + // responseText and responseXML will be populated with the same data from the iframe. + // Since the HTTP headers cannot be read from the iframe + obj.responseText = io.contentWindow.document.body?io.contentWindow.document.body.innerHTML:io.contentWindow.document.documentElement.textContent; obj.responseXML = io.contentWindow.document.XMLDocument?io.contentWindow.document.XMLDocument:io.contentWindow.document; } catch(e){} - if(callback.upload){ + if(callback && callback.upload){ if(!callback.scope){ callback.upload(obj); YAHOO.log('Upload callback.', 'info', 'Connection'); @@ -882,6 +1234,14 @@ YAHOO.util.Connect = } } + // Fire global custom event -- completeEvent + oConn.uploadEvent.fire(obj); + + if(o.uploadEvent){ + // Fire transaction custom event -- completeEvent + o.uploadEvent.fire(obj); + } + if(YAHOO.util.Event){ YAHOO.util.Event.removeListener(io, "load", uploadCallback); } @@ -894,11 +1254,11 @@ YAHOO.util.Connect = setTimeout( function(){ document.body.removeChild(io); + oConn.releaseObject(o); YAHOO.log('File upload iframe destroyed. Id is:' + frameId, 'info', 'Connection'); }, 100); }; - // Bind the onload handler to the iframe to detect the file upload response. if(YAHOO.util.Event){ YAHOO.util.Event.addListener(io, "load", uploadCallback); @@ -918,28 +1278,67 @@ YAHOO.util.Connect = * @static * @param {object} o The connection object returned by asyncRequest. * @param {object} callback User-defined callback object. - * @param {string} isTimeout boolean to indicate if abort was a timeout. + * @param {string} isTimeout boolean to indicate if abort resulted from a callback timeout. * @return {boolean} */ abort:function(o, callback, isTimeout) { - if(this.isCallInProgress(o)){ - o.conn.abort(); - window.clearInterval(this._poll[o.tId]); - delete this._poll[o.tId]; - if(isTimeout){ - delete this._timeOut[o.tId]; + var abortStatus; + + if(o.conn){ + if(this.isCallInProgress(o)){ + // Issue abort request + o.conn.abort(); + + window.clearInterval(this._poll[o.tId]); + delete this._poll[o.tId]; + + if(isTimeout){ + window.clearTimeout(this._timeOut[o.tId]); + delete this._timeOut[o.tId]; + } + + abortStatus = true; + } + } + else if(o.isUpload === true){ + var frameId = 'yuiIO' + o.tId; + var io = document.getElementById(frameId); + + if(io){ + // Destroy the iframe facilitating the transaction. + document.body.removeChild(io); + YAHOO.log('File upload iframe destroyed. Id is:' + frameId, 'info', 'Connection'); + + if(isTimeout){ + window.clearTimeout(this._timeOut[o.tId]); + delete this._timeOut[o.tId]; + } + + abortStatus = true; + } + } + else{ + abortStatus = false; + } + + if(abortStatus === true){ + // Fire global custom event -- abortEvent + this.abortEvent.fire(o); + + if(o.abortEvent){ + // Fire transaction custom event -- abortEvent + o.abortEvent.fire(o); } this.handleTransactionResponse(o, callback, true); YAHOO.log('Transaction ' + o.tId + ' aborted.', 'info', 'Connection'); - - return true; } else{ - YAHOO.log('Transaction ' + o.tId + ' abort call failed.', 'warn', 'Connection'); - return false; + YAHOO.log('Transaction ' + o.tId + ' abort call failed. Connection object no longer exists.', 'warn', 'Connection'); } + + return abortStatus; }, /** @@ -955,11 +1354,14 @@ YAHOO.util.Connect = { // if the XHR object assigned to the transaction has not been dereferenced, // then check its readyState status. Otherwise, return false. - if(o.conn){ - return o.conn.readyState != 4 && o.conn.readyState != 0; + if(o && o.conn){ + return o.conn.readyState !== 4 && o.conn.readyState !== 0; + } + else if(o && o.isUpload === true){ + var frameId = 'yuiIO' + o.tId; + return document.getElementById(frameId)?true:false; } else{ - //The XHR object has been destroyed. return false; } }, @@ -975,9 +1377,13 @@ YAHOO.util.Connect = releaseObject:function(o) { //dereference the XHR instance. - o.conn = null; + if(o.conn){ + o.conn = null; + } YAHOO.log('Connection object for transaction ' + o.tId + ' destroyed.', 'info', 'Connection'); //dereference the connection object. o = null; } -}; \ No newline at end of file +}; + +YAHOO.register("connection", YAHOO.util.Connect, {version: "2.3.0", build: "442"}); diff --git a/lib/yui/connection/connection-min.js b/lib/yui/connection/connection-min.js index 7d44fbac29..1da8cb5d3e 100755 --- a/lib/yui/connection/connection-min.js +++ b/lib/yui/connection/connection-min.js @@ -1,12 +1,16 @@ /* -Copyright (c) 2006, Yahoo! Inc. All rights reserved. +Copyright (c) 2007, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt -version: 0.12.2 +version: 2.3.0 */ -YAHOO.util.Connect={_msxml_progid:['MSXML2.XMLHTTP.3.0','MSXML2.XMLHTTP','Microsoft.XMLHTTP'],_http_header:{},_has_http_headers:false,_use_default_post_header:true,_default_post_header:'application/x-www-form-urlencoded',_isFormSubmit:false,_isFileUpload:false,_formNode:null,_sFormData:null,_poll:{},_timeOut:{},_polling_interval:50,_transaction_id:0,setProgId:function(id) + +YAHOO.util.Connect={_msxml_progid:['MSXML2.XMLHTTP.3.0','MSXML2.XMLHTTP','Microsoft.XMLHTTP'],_http_headers:{},_has_http_headers:false,_use_default_post_header:true,_default_post_header:'application/x-www-form-urlencoded; charset=UTF-8',_use_default_xhr_header:true,_default_xhr_header:'XMLHttpRequest',_has_default_headers:true,_default_headers:{},_isFormSubmit:false,_isFileUpload:false,_formNode:null,_sFormData:null,_poll:{},_timeOut:{},_polling_interval:50,_transaction_id:0,_submitElementValue:null,_hasSubmitListener:(function() +{if(YAHOO.util.Event){YAHOO.util.Event.addListener(document,'click',function(e){var obj=YAHOO.util.Event.getTarget(e);if(obj.type=='submit'){YAHOO.util.Connect._submitElementValue=encodeURIComponent(obj.name)+"="+encodeURIComponent(obj.value);}});return true;} +return false;})(),startEvent:new YAHOO.util.CustomEvent('start'),completeEvent:new YAHOO.util.CustomEvent('complete'),successEvent:new YAHOO.util.CustomEvent('success'),failureEvent:new YAHOO.util.CustomEvent('failure'),uploadEvent:new YAHOO.util.CustomEvent('upload'),abortEvent:new YAHOO.util.CustomEvent('abort'),_customEvents:{onStart:['startEvent','start'],onComplete:['completeEvent','complete'],onSuccess:['successEvent','success'],onFailure:['failureEvent','failure'],onUpload:['uploadEvent','upload'],onAbort:['abortEvent','abort']},setProgId:function(id) {this._msxml_progid.unshift(id);},setDefaultPostHeader:function(b) -{this._use_default_post_header=b;},setPollingInterval:function(i) +{this._use_default_post_header=b;},setDefaultXhrHeader:function(b) +{this._use_default_xhr_header=b;},setPollingInterval:function(i) {if(typeof i=='number'&&isFinite(i)){this._polling_interval=i;}},createXhrObject:function(transactionId) {var obj,http;try {http=new XMLHttpRequest();obj={conn:http,tId:transactionId};} @@ -15,90 +19,112 @@ catch(e) {http=new ActiveXObject(this._msxml_progid[i]);obj={conn:http,tId:transactionId};break;} catch(e){}}} finally -{return obj;}},getConnectionObject:function() +{return obj;}},getConnectionObject:function(isFileUpload) {var o;var tId=this._transaction_id;try -{o=this.createXhrObject(tId);if(o){this._transaction_id++;}} +{if(!isFileUpload){o=this.createXhrObject(tId);} +else{o={};o.tId=tId;o.isUpload=true;} +if(o){this._transaction_id++;}} catch(e){} finally {return o;}},asyncRequest:function(method,uri,callback,postData) -{var o=this.getConnectionObject();if(!o){return null;} -else{if(this._isFormSubmit){if(this._isFileUpload){this.uploadFile(o.tId,callback,uri,postData);this.releaseObject(o);return;} -if(method=='GET'){if(this._sFormData.length!=0){uri+=((uri.indexOf('?')==-1)?'?':'&')+this._sFormData;} +{var o=(this._isFileUpload)?this.getConnectionObject(true):this.getConnectionObject();if(!o){return null;} +else{if(callback&&callback.customevents){this.initCustomEvents(o,callback);} +if(this._isFormSubmit){if(this._isFileUpload){this.uploadFile(o,callback,uri,postData);return o;} +if(method.toUpperCase()=='GET'){if(this._sFormData.length!==0){uri+=((uri.indexOf('?')==-1)?'?':'&')+this._sFormData;} else{uri+="?"+this._sFormData;}} -else if(method=='POST'){postData=postData?this._sFormData+"&"+postData:this._sFormData;}} -o.conn.open(method,uri,true);if(this._isFormSubmit||(postData&&this._use_default_post_header)){this.initHeader('Content-Type',this._default_post_header);if(this._isFormSubmit){this.resetFormState();}} -if(this._has_http_headers){this.setHeader(o);} -this.handleReadyState(o,callback);o.conn.send(postData||null);return o;}},handleReadyState:function(o,callback) +else if(method.toUpperCase()=='POST'){postData=postData?this._sFormData+"&"+postData:this._sFormData;}} +o.conn.open(method,uri,true);if(this._use_default_xhr_header){if(!this._default_headers['X-Requested-With']){this.initHeader('X-Requested-With',this._default_xhr_header,true);}} +if(this._isFormSubmit||(postData&&this._use_default_post_header)){this.initHeader('Content-Type',this._default_post_header);if(this._isFormSubmit){this.resetFormState();}} +if(this._has_default_headers||this._has_http_headers){this.setHeader(o);} +this.handleReadyState(o,callback);o.conn.send(postData||null);this.startEvent.fire(o);if(o.startEvent){o.startEvent.fire(o);} +return o;}},initCustomEvents:function(o,callback) +{for(var prop in callback.customevents){if(this._customEvents[prop][0]){o[this._customEvents[prop][0]]=new YAHOO.util.CustomEvent(this._customEvents[prop][1],(callback.scope)?callback.scope:null);o[this._customEvents[prop][0]].subscribe(callback.customevents[prop]);}}},handleReadyState:function(o,callback) {var oConn=this;if(callback&&callback.timeout){this._timeOut[o.tId]=window.setTimeout(function(){oConn.abort(o,callback,true);},callback.timeout);} -this._poll[o.tId]=window.setInterval(function(){if(o.conn&&o.conn.readyState==4){window.clearInterval(oConn._poll[o.tId]);delete oConn._poll[o.tId];if(callback&&callback.timeout){delete oConn._timeOut[o.tId];} +this._poll[o.tId]=window.setInterval(function(){if(o.conn&&o.conn.readyState===4){window.clearInterval(oConn._poll[o.tId]);delete oConn._poll[o.tId];if(callback&&callback.timeout){window.clearTimeout(oConn._timeOut[o.tId]);delete oConn._timeOut[o.tId];} +oConn.completeEvent.fire(o);if(o.completeEvent){o.completeEvent.fire(o);} oConn.handleTransactionResponse(o,callback);}},this._polling_interval);},handleTransactionResponse:function(o,callback,isAbort) {if(!callback){this.releaseObject(o);return;} var httpStatus,responseObject;try -{if(o.conn.status!==undefined&&o.conn.status!=0){httpStatus=o.conn.status;} +{if(o.conn.status!==undefined&&o.conn.status!==0){httpStatus=o.conn.status;} else{httpStatus=13030;}} catch(e){httpStatus=13030;} -if(httpStatus>=200&&httpStatus<300){try -{responseObject=this.createResponseObject(o,callback.argument);if(callback.success){if(!callback.scope){callback.success(responseObject);} -else{callback.success.apply(callback.scope,[responseObject]);}}} -catch(e){}} -else{try -{switch(httpStatus){case 12002:case 12029:case 12030:case 12031:case 12152:case 13030:responseObject=this.createExceptionObject(o.tId,callback.argument,(isAbort?isAbort:false));if(callback.failure){if(!callback.scope){callback.failure(responseObject);} +if(httpStatus>=200&&httpStatus<300||httpStatus===1223){responseObject=this.createResponseObject(o,callback.argument);if(callback.success){if(!callback.scope){callback.success(responseObject);} +else{callback.success.apply(callback.scope,[responseObject]);}} +this.successEvent.fire(responseObject);if(o.successEvent){o.successEvent.fire(responseObject);}} +else{switch(httpStatus){case 12002:case 12029:case 12030:case 12031:case 12152:case 13030:responseObject=this.createExceptionObject(o.tId,callback.argument,(isAbort?isAbort:false));if(callback.failure){if(!callback.scope){callback.failure(responseObject);} else{callback.failure.apply(callback.scope,[responseObject]);}} break;default:responseObject=this.createResponseObject(o,callback.argument);if(callback.failure){if(!callback.scope){callback.failure(responseObject);} -else{callback.failure.apply(callback.scope,[responseObject]);}}}} -catch(e){}} +else{callback.failure.apply(callback.scope,[responseObject]);}}} +this.failureEvent.fire(responseObject);if(o.failureEvent){o.failureEvent.fire(responseObject);}} this.releaseObject(o);responseObject=null;},createResponseObject:function(o,callbackArg) {var obj={};var headerObj={};try {var headerStr=o.conn.getAllResponseHeaders();var header=headerStr.split('\n');for(var i=0;i ');if(typeof secureUri=='boolean'){io.src='javascript:false';} +break;case'file':case undefined:case'reset':case'button':break;case'submit':if(hasSubmit===false){if(this._hasSubmitListener&&this._submitElementValue){this._sFormData+=this._submitElementValue+'&';} +else{this._sFormData+=encodeURIComponent(oName)+'='+encodeURIComponent(oValue)+'&';} +hasSubmit=true;} +break;default:this._sFormData+=encodeURIComponent(oName)+'='+encodeURIComponent(oValue)+'&';}}} +this._isFormSubmit=true;this._sFormData=this._sFormData.substr(0,this._sFormData.length-1);return this._sFormData;},resetFormState:function(){this._isFormSubmit=false;this._isFileUpload=false;this._formNode=null;this._sFormData="";},createFrame:function(secureUri){var frameId='yuiIO'+this._transaction_id;var io;if(window.ActiveXObject){io=document.createElement('');if(typeof secureUri=='boolean'){io.src='javascript:false';} else if(typeof secureURI=='string'){io.src=secureUri;}} -else{var io=document.createElement('iframe');io.id=frameId;io.name=frameId;} +else{io=document.createElement('iframe');io.id=frameId;io.name=frameId;} io.style.position='absolute';io.style.top='-1000px';io.style.left='-1000px';document.body.appendChild(io);},appendPostData:function(postData) {var formElements=[];var postMessage=postData.split('&');for(var i=0;i 0){try -{for(var i=0;i 0){for(var i=0;i = 200 && httpStatus < 300){ - try - { - responseObject = this.createResponseObject(o, callback.argument); - if(callback.success){ - if(!callback.scope){ - callback.success(responseObject); - } - else{ - // If a scope property is defined, the callback will be fired from - // the context of the object. - callback.success.apply(callback.scope, [responseObject]); - } + if(httpStatus >= 200 && httpStatus < 300 || httpStatus === 1223){ + responseObject = this.createResponseObject(o, callback.argument); + if(callback.success){ + if(!callback.scope){ + callback.success(responseObject); + } + else{ + // If a scope property is defined, the callback will be fired from + // the context of the object. + callback.success.apply(callback.scope, [responseObject]); } } - catch(e){} + + // Fire global custom event -- successEvent + this.successEvent.fire(responseObject); + + if(o.successEvent){ + // Fire transaction custom event -- successEvent + o.successEvent.fire(responseObject); + } } else{ - try - { - switch(httpStatus){ - // The following cases are wininet.dll error codes that may be encountered. - case 12002: // Server timeout - case 12029: // 12029 to 12031 correspond to dropped connections. - case 12030: - case 12031: - case 12152: // Connection closed by server. - case 13030: // See above comments for variable status. - responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort?isAbort:false)); - if(callback.failure){ - if(!callback.scope){ - callback.failure(responseObject); - } - else{ - callback.failure.apply(callback.scope, [responseObject]); - } + switch(httpStatus){ + // The following cases are wininet.dll error codes that may be encountered. + case 12002: // Server timeout + case 12029: // 12029 to 12031 correspond to dropped connections. + case 12030: + case 12031: + case 12152: // Connection closed by server. + case 13030: // See above comments for variable status. + responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort?isAbort:false)); + if(callback.failure){ + if(!callback.scope){ + callback.failure(responseObject); } - break; - default: - responseObject = this.createResponseObject(o, callback.argument); - if(callback.failure){ - if(!callback.scope){ - callback.failure(responseObject); - } - else{ - callback.failure.apply(callback.scope, [responseObject]); - } + else{ + callback.failure.apply(callback.scope, [responseObject]); } - } + } + break; + default: + responseObject = this.createResponseObject(o, callback.argument); + if(callback.failure){ + if(!callback.scope){ + callback.failure(responseObject); + } + else{ + callback.failure.apply(callback.scope, [responseObject]); + } + } } - catch(e){} + + // Fire global custom event -- failureEvent + this.failureEvent.fire(responseObject); + + if(o.failureEvent){ + // Fire transaction custom event -- failureEvent + o.failureEvent.fire(responseObject); + } + } this.releaseObject(o); @@ -494,8 +742,10 @@ YAHOO.util.Connect = catch(e){} obj.tId = o.tId; - obj.status = o.conn.status; - obj.statusText = o.conn.statusText; + // Normalize IE's response to HTTP 204 when Win error 1223. + obj.status = (o.conn.status == 1223)?204:o.conn.status; + // Normalize IE's statusText to "No Content" instead of "Unknown". + obj.statusText = (o.conn.status == 1223)?"No Content":o.conn.statusText; obj.getResponseHeader = headerObj; obj.getAllResponseHeaders = headerStr; obj.responseText = o.conn.responseText; @@ -551,28 +801,38 @@ YAHOO.util.Connect = }, /** - * @description Public method that stores the custom HTTP headers for each transaction. + * @description Method that initializes the custom HTTP headers for the each transaction. * @method initHeader * @public * @static * @param {string} label The HTTP header label * @param {string} value The HTTP header value + * @param {string} isDefault Determines if the specific header is a default header + * automatically sent with each transaction. * @return {void} */ - initHeader:function(label,value) + initHeader:function(label,value,isDefault) { - if(this._http_header[label] === undefined){ - this._http_header[label] = value; + var headerObj = (isDefault)?this._default_headers:this._http_headers; + + if(headerObj[label] === undefined){ + headerObj[label] = value; } else{ // Concatenate multiple values, comma-delimited, // for the same header label, - this._http_header[label] = value + "," + this._http_header[label]; + headerObj[label] = value + "," + headerObj[label]; } - this._has_http_headers = true; + if(isDefault){ + this._has_default_headers = true; + } + else{ + this._has_http_headers = true; + } }, + /** * @description Accessor that sets the HTTP headers for each transaction. * @method setHeader @@ -583,29 +843,51 @@ YAHOO.util.Connect = */ setHeader:function(o) { - for(var prop in this._http_header){ - if(this._http_header.hasOwnProperty(prop)){ - o.conn.setRequestHeader(prop, this._http_header[prop]); + if(this._has_default_headers){ + for(var prop in this._default_headers){ + if(YAHOO.lang.hasOwnProperty(this._default_headers, prop)){ + o.conn.setRequestHeader(prop, this._default_headers[prop]); + } } } - delete this._http_header; - this._http_header = {}; - this._has_http_headers = false; + if(this._has_http_headers){ + for(var prop in this._http_headers){ + if(YAHOO.lang.hasOwnProperty(this._http_headers, prop)){ + o.conn.setRequestHeader(prop, this._http_headers[prop]); + } + } + delete this._http_headers; + + this._http_headers = {}; + this._has_http_headers = false; + } + }, + + /** + * @description Resets the default HTTP headers object + * @method resetDefaultHeaders + * @public + * @static + * @return {void} + */ + resetDefaultHeaders:function(){ + delete this._default_headers; + this._default_headers = {}; + this._has_default_headers = false; }, /** * @description This method assembles the form label and value pairs and * constructs an encoded string. - * asyncRequest() will automatically initialize the - * transaction with a HTTP header Content-Type of - * application/x-www-form-urlencoded. + * asyncRequest() will automatically initialize the transaction with a + * a HTTP header Content-Type of application/x-www-form-urlencoded. * @method setForm * @public * @static * @param {string || object} form id or name attribute, or form object. - * @param {string} optional boolean to indicate SSL environment. - * @param {string || boolean} optional qualified path of iframe resource for SSL in IE. + * @param {boolean} optional enable file upload. + * @param {boolean} optional enable file upload over SSL in IE only. * @return {string} string of the HTML form field name and value pairs.. */ setForm:function(formId, isUpload, secureUri) @@ -614,7 +896,7 @@ YAHOO.util.Connect = var oForm; if(typeof formId == 'string'){ // Determine if the argument is a form id or a form name. - // Note form name usage is deprecated but supported + // Note form name usage is deprecated by supported // here for legacy reasons. oForm = (document.getElementById(formId) || document.forms[formId]); } @@ -635,14 +917,14 @@ YAHOO.util.Connect = if(isUpload){ // Create iframe in preparation for file upload. - this.createFrame(secureUri?secureUri:null); - + var io = this.createFrame(secureUri?secureUri:null); // Set form reference and file upload properties to true. this._isFormSubmit = true; this._isFileUpload = true; this._formNode = oForm; return; + } var oElement, oName, oValue, oDisabled; @@ -660,7 +942,7 @@ YAHOO.util.Connect = // do not have a name attribute value. if(!oDisabled && oName) { - switch (oElement.type) + switch(oElement.type) { case 'select-one': case 'select-multiple': @@ -672,7 +954,6 @@ YAHOO.util.Connect = else{ this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oElement.options[j].hasAttribute('value')?oElement.options[j].value:oElement.options[j].text) + '&'; } - } } break; @@ -692,14 +973,19 @@ YAHOO.util.Connect = // stub case for input type button elements. break; case 'submit': - if(hasSubmit == false){ - this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&'; + if(hasSubmit === false){ + if(this._hasSubmitListener && this._submitElementValue){ + this._sFormData += this._submitElementValue + '&'; + } + else{ + this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&'; + } + hasSubmit = true; } break; default: this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&'; - break; } } } @@ -707,6 +993,7 @@ YAHOO.util.Connect = this._isFormSubmit = true; this._sFormData = this._sFormData.substr(0, this._sFormData.length - 1); + return this._sFormData; }, @@ -731,7 +1018,7 @@ YAHOO.util.Connect = * @method createFrame * @private * @static - * @param {string} secureUri Optional qualified path of iframe resource for SSL in IE. + * @param {string} optional qualified path of iframe resource for SSL in IE. * @return {void} */ createFrame:function(secureUri){ @@ -740,8 +1027,9 @@ YAHOO.util.Connect = // properties via createElement(). A different iframe creation // pattern is required for IE. var frameId = 'yuiIO' + this._transaction_id; + var io; if(window.ActiveXObject){ - var io = document.createElement(''); + io = document.createElement(''); // IE will throw a security exception in an SSL environment if the // iframe source is undefined. @@ -754,7 +1042,7 @@ YAHOO.util.Connect = } } else{ - var io = document.createElement('iframe'); + io = document.createElement('iframe'); io.id = frameId; io.name = frameId; } @@ -794,75 +1082,125 @@ YAHOO.util.Connect = }, /** - * @description Uploads HTML form, including files/attachments, to the - * iframe created in createFrame. + * @description Uploads HTML form, inclusive of files/attachments, using the + * iframe created in createFrame to facilitate the transaction. * @method uploadFile * @private * @static * @param {int} id The transaction id. - * @param {object} callback - User-defined callback object. + * @param {object} callback User-defined callback object. * @param {string} uri Fully qualified path of resource. + * @param {string} postData POST data to be submitted in addition to HTML form. * @return {void} */ - uploadFile:function(id, callback, uri, postData){ + uploadFile:function(o, callback, uri, postData){ // Each iframe has an id prefix of "yuiIO" followed // by the unique transaction id. - var frameId = 'yuiIO' + id; + var frameId = 'yuiIO' + o.tId; + var uploadEncoding = 'multipart/form-data'; var io = document.getElementById(frameId); + var oConn = this; + + // Track original HTML form attribute values. + var rawFormAttributes = + { + action:this._formNode.getAttribute('action'), + method:this._formNode.getAttribute('method'), + target:this._formNode.getAttribute('target') + }; // Initialize the HTML form properties in case they are // not defined in the HTML form. - this._formNode.action = uri; - this._formNode.method = 'POST'; - this._formNode.target = frameId; + this._formNode.setAttribute('action', uri); + this._formNode.setAttribute('method', 'POST'); + this._formNode.setAttribute('target', frameId); if(this._formNode.encoding){ // IE does not respect property enctype for HTML forms. - // Instead use property encoding. - this._formNode.encoding = 'multipart/form-data'; + // Instead it uses the property - "encoding". + this._formNode.setAttribute('encoding', uploadEncoding); } else{ - this._formNode.enctype = 'multipart/form-data'; + this._formNode.setAttribute('enctype', uploadEncoding); } if(postData){ var oElements = this.appendPostData(postData); } + // Start file upload. this._formNode.submit(); + // Fire global custom event -- startEvent + this.startEvent.fire(o); + + if(o.startEvent){ + // Fire transaction custom event -- startEvent + o.startEvent.fire(o); + } + + // Start polling if a callback is present and the timeout + // property has been defined. + if(callback && callback.timeout){ + this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout); + } + + // Remove HTML elements created by appendPostData if(oElements && oElements.length > 0){ - try - { - for(var i=0; i < oElements.length; i++){ - this._formNode.removeChild(oElements[i]); + for(var i=0; i < oElements.length; i++){ + this._formNode.removeChild(oElements[i]); + } + } + + // Restore HTML form attributes to their original + // values prior to file upload. + for(var prop in rawFormAttributes){ + if(YAHOO.lang.hasOwnProperty(rawFormAttributes, prop)){ + if(rawFormAttributes[prop]){ + this._formNode.setAttribute(prop, rawFormAttributes[prop]); + } + else{ + this._formNode.removeAttribute(prop); } } - catch(e){} } - // Reset HTML form status properties. + // Reset HTML form state properties. this.resetFormState(); // Create the upload callback handler that fires when the iframe // receives the load event. Subsequently, the event handler is detached // and the iframe removed from the document. - var uploadCallback = function() { + if(callback && callback.timeout){ + window.clearTimeout(oConn._timeOut[o.tId]); + delete oConn._timeOut[o.tId]; + } + + // Fire global custom event -- completeEvent + oConn.completeEvent.fire(o); + + if(o.completeEvent){ + // Fire transaction custom event -- completeEvent + o.completeEvent.fire(o); + } + var obj = {}; - obj.tId = id; + obj.tId = o.tId; obj.argument = callback.argument; try { - obj.responseText = io.contentWindow.document.body?io.contentWindow.document.body.innerHTML:null; + // responseText and responseXML will be populated with the same data from the iframe. + // Since the HTTP headers cannot be read from the iframe + obj.responseText = io.contentWindow.document.body?io.contentWindow.document.body.innerHTML:io.contentWindow.document.documentElement.textContent; obj.responseXML = io.contentWindow.document.XMLDocument?io.contentWindow.document.XMLDocument:io.contentWindow.document; } catch(e){} - if(callback.upload){ + if(callback && callback.upload){ if(!callback.scope){ callback.upload(obj); } @@ -871,6 +1209,14 @@ YAHOO.util.Connect = } } + // Fire global custom event -- completeEvent + oConn.uploadEvent.fire(obj); + + if(o.uploadEvent){ + // Fire transaction custom event -- completeEvent + o.uploadEvent.fire(obj); + } + if(YAHOO.util.Event){ YAHOO.util.Event.removeListener(io, "load", uploadCallback); } @@ -880,10 +1226,13 @@ YAHOO.util.Connect = else{ io.removeEventListener('load', uploadCallback, false); } - setTimeout(function(){ document.body.removeChild(io); }, 100); + setTimeout( + function(){ + document.body.removeChild(io); + oConn.releaseObject(o); + }, 100); }; - // Bind the onload handler to the iframe to detect the file upload response. if(YAHOO.util.Event){ YAHOO.util.Event.addListener(io, "load", uploadCallback); @@ -903,26 +1252,64 @@ YAHOO.util.Connect = * @static * @param {object} o The connection object returned by asyncRequest. * @param {object} callback User-defined callback object. - * @param {string} isTimeout boolean to indicate if abort was a timeout. + * @param {string} isTimeout boolean to indicate if abort resulted from a callback timeout. * @return {boolean} */ abort:function(o, callback, isTimeout) { - if(this.isCallInProgress(o)){ - o.conn.abort(); - window.clearInterval(this._poll[o.tId]); - delete this._poll[o.tId]; - if(isTimeout){ - delete this._timeOut[o.tId]; + var abortStatus; + + if(o.conn){ + if(this.isCallInProgress(o)){ + // Issue abort request + o.conn.abort(); + + window.clearInterval(this._poll[o.tId]); + delete this._poll[o.tId]; + + if(isTimeout){ + window.clearTimeout(this._timeOut[o.tId]); + delete this._timeOut[o.tId]; + } + + abortStatus = true; } + } + else if(o.isUpload === true){ + var frameId = 'yuiIO' + o.tId; + var io = document.getElementById(frameId); - this.handleTransactionResponse(o, callback, true); + if(io){ + // Destroy the iframe facilitating the transaction. + document.body.removeChild(io); - return true; + if(isTimeout){ + window.clearTimeout(this._timeOut[o.tId]); + delete this._timeOut[o.tId]; + } + + abortStatus = true; + } } else{ - return false; + abortStatus = false; } + + if(abortStatus === true){ + // Fire global custom event -- abortEvent + this.abortEvent.fire(o); + + if(o.abortEvent){ + // Fire transaction custom event -- abortEvent + o.abortEvent.fire(o); + } + + this.handleTransactionResponse(o, callback, true); + } + else{ + } + + return abortStatus; }, /** @@ -938,11 +1325,14 @@ YAHOO.util.Connect = { // if the XHR object assigned to the transaction has not been dereferenced, // then check its readyState status. Otherwise, return false. - if(o.conn){ - return o.conn.readyState != 4 && o.conn.readyState != 0; + if(o && o.conn){ + return o.conn.readyState !== 4 && o.conn.readyState !== 0; + } + else if(o && o.isUpload === true){ + var frameId = 'yuiIO' + o.tId; + return document.getElementById(frameId)?true:false; } else{ - //The XHR object has been destroyed. return false; } }, @@ -958,8 +1348,12 @@ YAHOO.util.Connect = releaseObject:function(o) { //dereference the XHR instance. - o.conn = null; + if(o.conn){ + o.conn = null; + } //dereference the connection object. o = null; } -}; \ No newline at end of file +}; + +YAHOO.register("connection", YAHOO.util.Connect, {version: "2.3.0", build: "442"}); diff --git a/lib/yui/container/README b/lib/yui/container/README index c3801da657..4cf23257e7 100755 --- a/lib/yui/container/README +++ b/lib/yui/container/README @@ -1,74 +1,649 @@ Container Release Notes +*** version 2.3.0 *** + +Fixed the following bugs: +------------------------- + ++ Improved creation of the