/*
 * This file is generated.  Do not edit it directly.
 * Run 'ant annotate' to regenerate it.  See tools.javascript.JSConstantApf
 */

var DesktopSidebarComponents = {
'SOFTPHONE_CONTAINER_ID' : "softphoneContainer",
'MRU_LIST_CONTAINER_ID' : "mruList"
};

var LookupInputElement = {
'LOOKUP_IFRAME' : "lookupIFrame"
};

var SoftphoneLayoutEditorConstants = {
'RESULT_FIELDS_KEY' : "resultFields",
'CALL_TYPE_PREFIX' : "callType_",
'XSLT_RELATED_OBJS_CSS' : "relatedObjects",
'HIDDEN_IFRAME_ID' : "previewIframe",
'FLIPPY_PREFIX' : "flippy_",
'CALL_TYPE_PREVIEW_PREFIX' : "callTypePreview_",
'FIRST_FLIPPY_CSS' : "firstFlippy",
'FLIPPY_CONTROL_PREFIX' : "control_",
'XSLT_INFO_FIELDS_CSS' : "infoFields",
'LISTING_PREFIX' : "listing_"
};

var ForecastRoleUser = {
'pUSER' : "user",
'FORECAST_SHARE_RADIO' : "forecastSharingRadios",
'pCAN_SHARE' : "allowMgrFctSharing"
};

var ForecastSummaryPage = {
'pLOOKUP_INPUT_ENTERED' : "lookupEntered"
};

var MCXHRParams = {
'pLoadObjId' : "LOI",
'pIsSuccess' : "isSuccess",
'pTestResultRecordCount' : "recordCount",
'pSaveObjParentId' : "SPI",
'pTestResultDataSize' : "dataSize",
'pLoadObjType' : "LOT",
'pTotalsElement' : "totalsElement",
'pTestResultConfigError' : "configError",
'pCollisionParam' : "LMT",
'pData' : "data",
'pSaveObjId' : "SOI",
'pQSTestResults' : "qsTestResults",
'pTestResultQSID' : "id",
'pAction' : "ACT",
'pSaveObjType' : "SOT",
'pFilterItemCount' : "itemCount"
};

var IFrameElement = {
'EmptyRelatedListDoc' : "emptyHtmlDoc.html",
'BLANK_SRC' : "javascript: \'\'"
};

var vaSelectElementConst = {
'UP_CLASS' : "up",
'DOWN_CLASS' : "down"
};

var DetailElement = {
'DEFAULT_DETAIL_ELEMENT_ID' : "ep",
'DEFAULT_ERROR_DIV_ID' : "errorDiv_ep",
'TOP_BUTTON_ROW' : "topButtonRow",
'BOTTOM_BUTTON_ROW' : "bottomButtonRow"
};

var AjaxValidateFormula = {
'RANGE_KEY' : "range",
'VALID_KEY' : "valid"
};

var TaskOwnerLookup = {
'pLOOKUP_UROG_SUFFIX' : "_lkurogid",
'DONE_BUTTON_ID' : "doneButton"
};

var DatePickerIds = {
'DOM_ID' : "datePicker",
'MONTH_PICKER' : "calMonthPicker",
'TABLE_ID' : "datePickerCalendar",
'YEAR_PICKER' : "calYearPicker"
};

var CrtLayoutElement = {
'cFIELD_IN_SECTION' : "#CCCCCC",
'pVALUE' : "val",
'SECTION_NAME' : "name",
'SECTION_CAN_EDIT_LABEL' : "canEditLabel",
'MAX_DISPLAY_FIELD_LENGTH' : 15,
'cFIELD_USED_FONT' : "#B0B0B0",
'CSS_CLASS_LAYOUT_ITEM_SEPARATOR' : "sepCell",
'LAYOUT_NAME' : "name",
'pSAVE_AND_CLOSE' : "saveAndClose",
'ACTIONREF_NAME' : "name",
'COLUMN_NAME' : "columnName",
'LAST_SEC_SEP_DIV' : "LAST_SEC_SEP_DIV",
'SECTION_SORT_ORDER_HORIZONTAL' : "h",
'cSEPARATOR_ON' : "#000000",
'SECTION_NUM_COLUMNS' : "numColumns",
'SECTION_SORT_ORDER' : "sortOrder",
'cFIELD_UNUSED' : "#CCCCAA",
'cFIELD_SELECTED' : "#6699CC",
'SECTION_DIV_SUFFIX' : "availSectionDiv",
'ITEM_CUSTOMLABEL' : "customLabel",
'ACTIONREF' : "actionRef",
'ITEM_LAYOUT_IDS' : "lIds",
'CSS_CLASS_LAYOUT_ITEM' : "itemCell",
'ITEM_BEHAVIOR' : "behavior",
'SECTION_HEADER_ID_PREFIX' : "sec_",
'ITEM_NAME' : "name",
'ITEM_POS_X' : "xPos",
'XML_FORM_NAME' : "submitForm",
'SCROLL_BUFFER_ID' : "scrollBuffer",
'cFIELD_EMPTY' : "#FFFFFF",
'LAYOUT_FIELDS_LIMIT' : 1000,
'SECTION_MASTER_LABEL' : "masterLabel",
'SECTION_TABLE_ID_PREFIX' : "table",
'ITEM_HEIGHT' : "height",
'SECTION_ID' : "sectionId",
'ITEM_WIDTH' : "width",
'COLUMN_ID' : "columnId",
'DEFAULT_NUM_COLS' : "defaultNumCols",
'HOVER_DIV' : "MOUSE_HOVER_DIV",
'SECTION_EDIT_HEADING' : "editHeading",
'cAVAILABLE_HIGHLIGHT' : "#000000",
'MAIN_TABLE_DIV_ID' : "mainTableDiv",
'ITEM' : "item",
'ITEM_ID' : "itemId",
'SECTION_SEP_DIV_PREFIX' : "LayoutSectionSeparator_",
'ACTIONREF_ORDER' : "order",
'NUM_LAYOUT_COLS' : 4,
'FIELD_TYPE_SELECT_NAME' : "availableDropDown",
'SEPARATOR_PREFIX' : "rp_",
'SECTION' : "section",
'CSS_CLASS_LAYOUT_CELL' : "layoutCell",
'cFIELD_USED' : "#EEEEEE",
'SECTION_AVAIL_WRAPPER_ID' : "availableSectionWrapper",
'ITEM_DEFAULT_CHECKED' : "defaultChecked",
'AVAIL_CELL' : "availCell",
'ITEM_SHOWLABEL' : "showLabel",
'ITEM_SHOWSCROLLBARS' : "showScrollbars",
'LEFT_SECTION_ID' : "layoutdndLeft",
'COLUMN' : "column",
'ITEM_TYPE' : "itemType",
'SECTION_DETAIL_HEADING' : "detailHeading",
'ROOT_CONTAINER' : "root"
};

var MailmergeTemplateSelectElementConst = {
'TEMPLATE_DESCRIPTION' : "mmtse_description",
'TEMPLATE_VIEW_BUTTON' : "mmtse_preview",
'TEMPLATE_ID' : "mmtse_id",
'TEMPLATE_TITLE' : "mmtse_title"
};

var EmailAuthorConstants = {
'EMAIL_ADDR_DELIM' : "; "
};

var TaskUi = {
'ASSIGNEE_SEPARATOR' : ",",
'pLOOKUP_BUTTON_MULTI_OWNER_SUFFIX' : "m",
'MAX_TMU_ASSIGNEES' : 100,
'pMAX_ASSIGNEE_TEXT_LENGTH' : 200,
'pLOOKUP_SUMMARY_SUFFIX' : "_sum",
'pLOOKUP_DISPLAY_SUFFIX' : "_dsp"
};

var FilterSelectionElement = {
'pCOLUMN' : "col",
'ON_LOAD_CRITERIA' : "onLoadCriteria",
'pOPERATOR' : "oper",
'pFILTER_VALUE' : "fval"
};

var DynamicContent = {
'pTYME' : "tyme",
'pERROR_TITLE' : "errorTitle",
'pCOOKIE_PARAM' : "cookieParam",
'pERROR_DESC' : "errorDesc"
};

var InlineEditConstants = {
'ENTITY_ID' : "entityId",
'FIELD_VALUE' : "initialValue",
'FIELD_REQUIRED' : "required",
'VALIDATION_ERRORS' : "validationErrors",
'FIELD_STATE' : "state",
'EDITABLE' : "editable",
'FIELD_ID' : "fieldId",
'LAST_MOD' : "sysMod",
'CELL_ID' : "_ilecell",
'FIELD_TYPE' : "fieldType",
'NON_SPECIFIC_ERRORS' : "nonSpecificErrors",
'OVERRIDE_TYPE' : "overrideType",
'SAVE_BUTTON' : "inlineEditSave",
'FIELD_DATA' : "fields",
'SUCCESS' : "success",
'CANCEL_BUTTON' : "inlineEditCancel",
'INNER_ID' : "_ileinner",
'DYNAMIC_DATA' : "dynamicData"
};

var CreateNewList = {
'DHTML_ID' : "newEntityList"
};

var SetupTreeNodeConstants = {
'COOKIE_KEY' : "setupopen"
};

var RoleTreeCookieConstants = {
'COOKIE_KEY' : "roleopen"
};

var HTPortal = {
'pORG_ID' : "orgId",
'pID' : "id",
'pTRACK' : "track",
'pTARGET' : "target",
'pCLASS_NAME' : "cname",
'pSECTION' : "section",
'pCLASS_DAY' : "R_DAY",
'pSELECT_LOCATION' : "sel_loc",
'pBODY' : "body",
'pLOCATION' : "loc",
'pFEATURE' : "feature"
};

var EmailCCBccLookupConstants = {
'CC_NAME_ID' : "cc_name",
'CC_ID' : "cc",
'ADDITIONAL_TO_ID' : "additional_to",
'BCC_ADDR_ID' : "bcc_addr",
'BCC_ID' : "bcc",
'ADDITIONAL_TO_NAME_ID' : "additional_to_name",
'ADDITIONAL_TO_ADDR_ID' : "additional_to_addr",
'BCC_NAME_ID' : "bcc_name",
'REF_ID' : "ref",
'CC_ADDR_ID' : "cc_addr"
};

var CriteriaInputConstants = {
'pOP' : "critop",
'pCOL' : "critfld",
'FILTER_SECTION_ID' : "filterSection",
'pFIELD_VAL' : "critfld_val",
'pVAL' : "pVAL",
'pLOOKUP' : "pLOOKUP",
'SHOW_SUMMARY_FILTER' : "filterControl"
};

var EditPageConstants = {
'pCANCEL' : "cancel",
'pEDIT_PAGE' : "editPage",
'pSAVE_CLOSE' : "save_close",
'pSAVE_ATTACH' : "save_attach",
'pSAVE_NEW_URL' : "save_new_url",
'pSAVE_NEW' : "save_new",
'pSAVE' : "save",
'pQUICK_SAVE' : "quick_save"
};

var EmailAddrEditConstants = {
'pSAVE_CANCEL' : "saveCancel"
};

var MultiSelectList = {
'pIDS' : "selectedIds",
'pFILTER_TYPE' : "filterType",
'availableFrameId' : "available",
'pUNSAVED_IDS' : "unsavedIds",
'pTOTAL_ROW_COUNT_FILTER' : "msl_totalRowCountFilter",
'selectedFrameId' : "selected",
'availableCheckboxPrefix' : "chk_",
'nameCellPrefix' : "name_",
'availableRowPrefix' : "row_",
'pTOTAL_ROW_COUNT' : "msl_totalRowCount",
'selectedCheckboxPrefix' : "uch_",
'listEmptyLabelId' : "listEmptyLabel",
'selectionsTableId' : "selections",
'selectedRowPrefix' : "sel_",
'selectLabelId' : "selectLabel",
'deselectLabelId' : "deselectLabel"
};

var EventPage = {
'CALENDAR_IFRAME_ID' : "calendarIFrame"
};

var AjaxLoadPLAServlet = {
'TYPE' : "type",
'PAGE_SIZE' : "pageSize"
};

var Activity = {
'pNEW_ATTACHMENTS' : "newatt",
'pATT_WARNING' : "attWarning"
};

var CrtConstants = {
'PICKLIST_VALUE_TABLE_FIELD_SEPARATOR' : '.',
'OBJECT_PREFIX' : "o",
'MAX_OBJECTS' : 4,
'PICKLIST_VALUE_ID_SEPARATOR' : '|'
};

var MenuButtonElement = {
'BUTTON' : "Button",
'GO_BUTTON' : "Go",
'MENU' : "Menu",
'SELECT' : "Select"
};

var ColorPickerConstants = {
'HEX_VIEW_ID' : "colorPickerHexView",
'DOM_ID' : "colorPicker",
'COLOR_VIEW_ID' : "colorPickerColorView"
};

var CrtObjectElement = {
'REMOVE_OBJECT_LINK' : "remove",
'ESTABLISHED' : "established",
'INNER_JOIN_SELECT' : "inner_select",
'ELBOW' : "elbow",
'GHOST1' : "ghost1",
'LEVEL' : "level",
'EST_JOIN_LABEL' : "estJoinLabel",
'EST_OBJECT_LABEL' : "estObjLabel",
'JOIN_RADIO' : "radio",
'GHOST_ELBOW' : "ghost_elbow",
'WARNING' : "warning",
'GHOST0' : "ghost0",
'OUTER_JOIN_SELECT' : "outer_select"
};

var MCFilterPaneParams = {
'pORDER_BY_DIV' : "orderBySection",
'pNO_LIMIT' : "noLimit",
'pMAX_RECORD_RADIO' : "maxRecordRadio",
'pSCOPE' : "ofscope",
'pSET_LIMIT' : "setLimit",
'NONE_SCOPE_VALUE' : "-1"
};

var BodyLayout = {
'FOOTER_DIV_ID' : "bodyFooter",
'PAGE_HEADER_ID' : "AppBodyHeader",
'BODY_CELL_ID' : "bodyCell",
'BODY_TABLE_ID' : "bodyTable"
};

var FilterEditPageConstants = {
'pSEARCH_ANCHOR' : "searchAnchor"
};

var JSPDispatcher = {
'STANDARD_PACKAGE' : "ui",
'NONSTANDARD_PACKAGE_PREFIX' : "_ui/",
'PACKAGE_MARKER' : "p/"
};

var InlineHelp = {
'CLASS_NAME' : "helpButton",
'ORB' : "helpOrb",
'DISPLAY_DIV_CLASS' : "helpText",
'CLASS_NAME_HOVER' : "helpButtonOn",
'ID_SUFFIX' : "_help"
};

var ListView = {
'CHECKBOX_ID' : "ids",
'SELECT_ALL_BOX_ID' : "allBox"
};

var AjaxServlet = {
'CSRF_PROTECT' : "while(1);\n",
'ERROR_MSG_KEY' : "errMsg"
};

var FieldTreeConstants = {
'SELECT_ID' : "FieldTreeSelect"
};

var TagConstants = {
'BROWSER_SEARCH_HEADER_CLASS' : "pbTagBrowserSearch",
'BROWSER_LIST_ID' : "browseTags",
'BROWSER_TAG_TABLE_ID' : "browseTagsTable",
'TAG_RESULTS_ID' : "tagResults",
'HIDE_LINK_ID' : "hideLinkId",
'ROLODEX_SEARCH_VALUE' : "-10",
'TAGh_DROP_DOWN_ID' : "tagh_drop_down",
'BROWSER_MODE' : "tagBrowserMode",
'TAG_EDIT_DESCRIPTION_ID' : "tagEditDescription",
'TAG_SEARCH_FIELD' : "tagsSearch",
'TAGh_EDIT_AREA_ID' : "tagh_edit_area",
'TAG_RESULTS_BODY_ID' : "tagListBody",
'ERROR_DIV_ID' : "tagHomeErrorDiv",
'LOOKUP_TAGS_PAGE' : "/ui/tag/LookupTagsPage",
'TAGh_EDIT_ID' : "tagh_edit",
'SELECTED_TAGS_DESCRIPTION_ID' : "selecteTagDescription",
'SAVED_TAG_SEARCH' : "savedTagSearch",
'DELETE_EDIT_CONTROLS_ID' : "deleteEditControls",
'NOTIFY_MSG_ID' : "successNotifyId",
'EDIT_SECTION_ID' : "editSectionId",
'TAG_HIDDEN_TAG_LIST_EMPTY' : "!Unset!",
'CHANGE_TAGS_ID' : "changeTagsId",
'HIDING_SECTION_ID' : "layoutEditSection",
'TAGh_CANCEL_ID' : "tagh_cancel",
'TAG_EDIT_FIELD' : "tagEditField",
'TAG_HIDDEN_TAG_LIST' : "tag_hidden_tag_list",
'TAGh_TAG_DISPLAY_LIST' : "tagh_tag_display_list",
'TAG_SUMMARY_ID' : "tagSummary",
'TAG_ROLODEX_ID' : "tagRolodexId",
'TAGh_SAVE_ID' : "tagh_save",
'TAG_SET_HAS_RECORDS' : "tagSetHasRecords",
'SAVE_TAGS_PAGE' : "/ui/tag/SaveTagsPage",
'TAG_UPDATE_STRING' : "tagUpdate",
'TAGh_EDIT_ERROR_ID' : "tagh_edit_error",
'RENAME_EDIT_CONTROLS_ID' : "renameEditControls",
'TAGh_EDIT_TEXT_ID' : "tagh_edit_text",
'EDIT_TAGS_PAGE' : "/ui/tag/TagsEditPage",
'RENAME_LINK_ID' : "renameLinkId",
'DELETE_LINK_ID' : "deleteLinkId"
};

var ProfileEditConstants = {
'CRUD_DELETE' : "crudDelete",
'CRUD_UPDATE' : "crudUpdate",
'CRUD_CREATE' : "crudCreate",
'CRUD_READ' : "crudRead"
};

var Udd = {
'EMPTY_KEY' : "000000000000000"
};

var RequestInfo = {
'pSID' : "sid"
};

var LookupsUi = {
'LOOKUPS' : "lookups",
'FIELD' : "field",
'PATH' : "path"
};

var EditEventMultiUserCalendarElementConstants = {
'EDIT_PAGE_CALENDAR' : "editEventCalendar"
};

var TagBrowserMode = {
'RENAME' : "RENAME",
'DELETE' : "DELETE",
'BROWSE' : "BROWSE"
};

var StageManager = {
'pWIZARD_RET_URL' : "wizardRetUrl"
};

var CrtLookupConstants = {
'LOOKUP_HEADER' : "lookupInnerHeader",
'CONTROL_ELEM_1' : "controlLinks1",
'LOOKUP_DEPTH_LIMIT' : 4,
'LOOKUP_ELEM' : "lookupBox",
'PATH_ELEM' : "pathBox"
};

var MouseOverElement = {
'DEFAULT_CLASS_INNER' : "mouseOverInfo",
'DEFAULT_CLASS' : "mouseOverInfoOuter"
};

var SearchRelatedList = {
'FILTER_FIELDS_PARAM' : "sFltrFields",
'ALL_STATES_PREFIX' : "allStates_",
'SearchUserLayoutServletName' : "UserSearchListLayout",
'COMBO_BUTTON_ID' : "comboButton",
'SearchFilterInfoServletName' : "SearchFilterInfo",
'COLUMN_SELECTOR_PREFIX' : "selector_",
'SEARCH_ACTION_IDENTIFIER_PARAM' : "aId",
'COLUMN_PARAMETER' : "columns",
'pSEARCH' : "search",
'pSEARCH_STR' : "str",
'ENTITY_PARAMETER' : "entity",
'SEARCH_IDENTIFIER_PARAM' : "searchId",
'PER_ENTITY_VALUE' : "perEntityValue",
'FILTER_FIELDS_SAVE_PREFIX' : "save_filter_",
'pENTITY_ALL' : "0",
'ShouldNotLookUp' : "noLookUp",
'FILTER_FIELD_FORM_PREFIX' : "field_name_form_",
'FILTER_FIELDS_PREFIX' : "field_name_"
};

var ActivityReminderConstants = {
'pTEST' : "test",
'DISMISS_ID' : "dismiss",
'SUMMARY_ID' : "summary",
'SNOOZE_TIME_ID' : "snooze_time",
'DUE_MINUTES_ID' : "minutes",
'ALL_DAY_ATTR' : "all_day",
'DUE_TIME_ATTR' : "due_time",
'DISMISS_ALL_ID' : "dismiss_all",
'SNOOZE_ID' : "snooze",
'pAT' : "at",
'REMINDERS_OK' : "reminders_ready",
'pSNOOZED_AT' : "snoozed_at",
'REMINDER_ID' : "reminder",
'REMINDERS_NONE' : "reminders_none_active"
};

var AjaxGetUsersInGroups = {
'pOWNER_ID_LIST' : "ownerIdList",
'pCLASS_NAME' : "common.ownership.group.AjaxGetUsersInGroups",
'pNUM_USERS' : "numUsers",
'pINVALID_GROUPS_MESSAGE' : "invalidGroups",
'pOWNER_NAME_LIST' : "ownerNameList"
};

var ForecastSettings = {
'pFORECAST_SHARING' : "forecastSharing",
'pALLOW_FM_SHARING' : "allowFMSharing"
};

var ColorInputConstants = {
'COLOR_BOX_CSS' : "colorBox",
'ERROR_COLOR_BOX_CSS' : "errorColorBox"
};

var AdvancedCurrencyEnable = {
'pENABLE' : "enable",
'enableButton' : "enableButton"
};

var AjaxLoadPLAForRecordTypeServlet = {
'RECORD_TYPE_ID' : "rtId"
};

var SidebarConstants = {
'HANDLE_ID' : "handle",
'SIDEBAR_PINNED_COOKIE' : "sidebarPinned",
'pSEARCH_SIDEBAR_STR' : "sbstr",
'PIN2_INDICATOR_ID' : "pinIndicator2",
'PIN_INDICATOR_ID' : "pinIndicator"
};

var ColumnTypeConstants = {
'PERSONNAME_LASTNAME_OFFSET' : 2,
'PERSONNAME_FIRSTNAME_OFFSET' : 1,
'DEFAULT_STATE_LENGTH' : 20,
'ADDRESS_STREET_OFFSET' : 0,
'DEFAULT_CITY_LENGTH' : 40,
'PERSONNAME_SALUTATION_OFFSET' : 0,
'DEFAULT_ZIP_LENGTH' : 20,
'DEFAULT_SALUTATION_LENGTH' : 40,
'ADDRESS_POSTAL_CODE_OFFSET' : 3,
'DEFAULT_LASTNAME_LENGTH' : 80,
'DEFAULT_FIRSTNAME_LENGTH' : 40,
'DEFAULT_COUNTRY_LENGTH' : 40,
'ADDRESS_COUNTRY_OFFSET' : 4,
'ADDRESS_CITY_OFFSET' : 1,
'ADDRESS_STATE_OFFSET' : 2,
'DEFAULT_TEXTNAME_LENGTH' : 255,
'DEFAULT_STREET_LENGTH' : 255
};

var Desktop = {
'AgentConsoleY' : "AgentConsoleY",
'AgentConsoleS' : "AgentConsoleS",
'pGOTO_ID' : "goToId",
'RESIZE_WIDTH' : "resizeWidth",
'pGOTO_URL' : "goToUrl",
'AgentConsoleX' : "AgentConsoleX",
'BROWSER_MAX_URL' : "2048",
'AgentConsoleFE' : "AgentConsoleFE",
'SIDEBAR_NORMAL_WIDTH_STYLE_PX' : "200",
'IS_DESKTOP' : "isdtp"
};

var EmailRelayConstants = {
'EMAIL_RELAY_TLS_SETTING_ID' : "email_relay_tls_setting",
'RESTRICT_TO_DOMAINS_ID' : "restrict_to_domains",
'ACTIVATE_RESTRICT_TO_DOMAINS_ID' : "activate_restrict_to_domains",
'EMAIL_HOST_PORT_ID' : "email_host_port",
'EMAIL_HOST_ID' : "email_host",
'ACTIVATE_EMAIL_RELAY_ID' : "activate_email_relay",
'RESTRICT_TO_DOMAINS_HIDDEN_ID' : "restrict_to_domains_hidden"
};

var EditElement = {
'FIELD_NAME_LAST' : "name_last",
'pOLD_NAME_SUFFIX' : "_lkold",
'SELECTED_ID_SUFFIX' : "_selected",
'FIELD_NAME_ZIP' : "zip",
'pBASE_NAME' : "lknm",
'pTYPE_SUFFIX' : "_lktp",
'FIELD_NAME_SALUTATION' : "name_salutation",
'FIELD_NAME_CITY' : "city",
'FIELD_NAME_COUNTRY' : "country",
'pID_SUFFIX' : "_lkid",
'STREET_NUM_COLS' : 27,
'FIELD_NAME_FIRST' : "name_first",
'FIELD_NAME_STREET' : "street",
'FIELD_NAME_STATE' : "state",
'CHECKBOX_SUFFIX' : "_chkbox",
'STREET_NUM_ROWS' : 2,
'ERROR_CLASS' : "error",
'UNSELECTED_ID_SUFFIX' : "_unselected"
};

var BrowserSettingsWarningElement = {
'NEVER_SHOW_AGAIN_ID' : "browserSettingsWarningGoAway",
'cBrowserSettings' : "numReqs",
'BROWSER_SETTINGS_WARNING_ID' : "browserSettingsWarning",
'MORE_INFO_ID' : "browserSettingsWarningMoreInfo"
};

var ReportConstants = {
'pSubTotalBy0' : "subtotalBy0"
};

var CreateNewElement = {
'DOM_ID' : "createNew"
};

var AjaxLoadPLAForPageServlet = {
'PAGE_NUM' : "pageNum"
};

var CustomMotifDefinitionPageConst = {
'MOTIF_ICON_PARAM' : "file_id",
'COLOR_ELEMENT' : "ce"
};

var DeveloperSettings = {
'LICENSE_MGR_CHOICE_STR' : "licenseMgr"
};

var ReportsFch = {
'FLOATING_HEADER' : "floatingHeader",
'HEADER_ROW' : "headerRow",
'FCH_AREA' : "fchArea"
};

var ForecastSharingPrefPopup = {
'CAN_SHARE_RADIO' : "enableRadio",
'pIS_FCT_SHARE_ENABLED' : "isFctShareEnabled",
'DISABLE_CHECKBOX' : "disableCheckbox"
};

var UiData = {
'pCANCEL_URL' : "cancelURL",
'pRET_URL' : "retURL",
'pSAVE_URL' : "saveURL"
};

var SummaryFieldConstants = {
'OPERATION_CONTAINER_ID' : "operationCtr"
};

var PortalStyleConfigEditorConstants = {
'PARAM_PREFIX' : "p_"
};

var CompactLayoutUiConst = {
'showItemsLeft' : "showItemsLeft",
'showItemsRight' : "showItemsRight",
'switchColumnToLeft' : "switchColumnLeft",
'hideItemsLeft' : "hideItemsLeft",
'hideItemsRight' : "hideItemsRight",
'switchColumnToRight' : "switchColumnRight",
'saveButtonId' : "saveButton"
};

var TaskMassAction = {
'ROW_LIMIT' : 200
};

var AjaxLoadFieldsForEntity = {
'pFIELD_LIST' : "fieldList",
'pENTITY_NAME' : "entity",
'pPARENT_ENTITY_NAME' : "parentEntity"
};

var ManageableInfo = {
'MORE_INFO_CLASS' : "manageableMoreInfo",
'DHTML_ID' : "manageableInfo"
};

var MotifInputElementConst = {
'FIELD_NAME_MOTIF' : "motifName",
'MOTIF_ELEMENT_SUFFIX' : "motifElement",
'FIELD_NAME_ICON' : "motifIcon",
'FIELD_NAME_DESCRIPTION' : "motifClass"
};

var ColumnType = {
'ANYTYPE':{
'inlineEditFieldObject':null,'queryOperators':null,'datatypeLetter':'K','filterQueryOperators':null,'isNumber':false,'needsLookup':false,'inlineEditable':false,'inlineEditExtraData':null,'isDate':false
},'AUTONUMBER':{
'inlineEditFieldObject':'TextField','queryOperators':['e',
'n',
'l',
'g',
'm',
'h',
'c',
'k',
's',
'i'],'datatypeLetter':'V','filterQueryOperators':['e',
'n',
'c',
'k',
's'],'isNumber':false,'needsLookup':false,'inlineEditable':true,'inlineEditExtraData':null,'isDate':false
},'SUMMARY':{
'inlineEditFieldObject':null,'queryOperators':null,'datatypeLetter':'1','filterQueryOperators':null,'isNumber':false,'needsLookup':false,'inlineEditable':false,'inlineEditExtraData':null,'isDate':false
},'PERCENT':{
'inlineEditFieldObject':'NumberField','queryOperators':['e',
'n',
'l',
'g',
'm',
'h'],'datatypeLetter':'P','filterQueryOperators':['e',
'n',
'l',
'g',
'm',
'h'],'isNumber':true,'needsLookup':false,'inlineEditable':true,'inlineEditExtraData':null,'isDate':false
},'BLOB':{
'inlineEditFieldObject':null,'queryOperators':null,'datatypeLetter':'X','filterQueryOperators':null,'isNumber':false,'needsLookup':false,'inlineEditable':false,'inlineEditExtraData':null,'isDate':false
},'FAX':{
'inlineEditFieldObject':'PhoneField','queryOperators':['e',
'n',
'l',
'g',
'm',
'h',
'c',
'k',
's',
'i'],'datatypeLetter':'G','filterQueryOperators':['e',
'n',
'c',
'k',
's'],'isNumber':false,'needsLookup':false,'inlineEditable':true,'inlineEditExtraData':['maxLength',
'formatPhone'],'isDate':false
},'DOUBLE':{
'inlineEditFieldObject':'NumberField','queryOperators':['e',
'n',
'l',
'g',
'm',
'h'],'datatypeLetter':'N','filterQueryOperators':['e',
'n',
'l',
'g',
'm',
'h'],'isNumber':true,'needsLookup':false,'inlineEditable':true,'inlineEditExtraData':null,'isDate':false
},'STATICENUM':{
'inlineEditFieldObject':'StaticEnumField','queryOperators':['e',
'n',
'l',
'g',
'm',
'h',
'c',
'k',
's',
'i'],'datatypeLetter':'L','filterQueryOperators':['e',
'n',
'c',
'k',
's'],'isNumber':false,'needsLookup':true,'inlineEditable':true,'inlineEditExtraData':['picklistData'],'isDate':false
},'CURRENCYCODE':{
'inlineEditFieldObject':null,'queryOperators':['e',
'n',
'l',
'g',
'm',
'h',
'c',
'k',
's',
'i'],'datatypeLetter':'L','filterQueryOperators':['e',
'n',
'c',
'k',
's'],'isNumber':false,'needsLookup':false,'inlineEditable':false,'inlineEditExtraData':null,'isDate':false
},'TEXTENUM':{
'inlineEditFieldObject':'TextField','queryOperators':['e',
'n',
'l',
'g',
'm',
'h',
'c',
'k',
's',
'i'],'datatypeLetter':'L','filterQueryOperators':['e',
'n',
'c',
'k',
's'],'isNumber':false,'needsLookup':false,'inlineEditable':true,'inlineEditExtraData':['maxLength'],'isDate':false
},'ADDRESSCOUNTRY':{
'inlineEditFieldObject':null,'queryOperators':['e',
'n',
'l',
'g',
'm',
'h',
'c',
'k',
's',
'i'],'datatypeLetter':'S','filterQueryOperators':['e',
'n',
'c',
'k',
's'],'isNumber':false,'needsLookup':false,'inlineEditable':false,'inlineEditExtraData':null,'isDate':false
},'EMAIL':{
'inlineEditFieldObject':'TextField','queryOperators':['e',
'n',
'l',
'g',
'm',
'h',
'c',
'k',
's',
'i'],'datatypeLetter':'E','filterQueryOperators':['e',
'n',
'c',
'k',
's'],'isNumber':false,'needsLookup':false,'inlineEditable':true,'inlineEditExtraData':['maxLength'],'isDate':false
},'PHONE':{
'inlineEditFieldObject':'PhoneField','queryOperators':['e',
'n',
'l',
'g',
'm',
'h',
'c',
'k',
's',
'i'],'datatypeLetter':'H','filterQueryOperators':['e',
'n',
'c',
'k',
's'],'isNumber':false,'needsLookup':false,'inlineEditable':true,'inlineEditExtraData':['maxLength',
'formatPhone'],'isDate':false
},'ENTITYID':{
'inlineEditFieldObject':'ForeignKeyField','queryOperators':['e',
'n',
's'],'datatypeLetter':'Y','filterQueryOperators':['e',
'n',
's'],'isNumber':false,'needsLookup':false,'inlineEditable':true,'inlineEditExtraData':['domain'],'isDate':false
},'YEARQUARTER':{
'inlineEditFieldObject':null,'queryOperators':['e',
'n',
'l',
'g',
'm',
'h',
'c',
'k',
's',
'i'],'datatypeLetter':'O','filterQueryOperators':['e',
'n',
'c',
'k',
's'],'isNumber':false,'needsLookup':false,'inlineEditable':false,'inlineEditExtraData':null,'isDate':false
},'ENCRYPTEDTEXT':{
'inlineEditFieldObject':'EncryptedTextField','queryOperators':['e',
'n',
'l',
'g',
'm',
'h',
'c',
'k',
's',
'i'],'datatypeLetter':'6','filterQueryOperators':['e',
'n',
'c',
'k',
's'],'isNumber':false,'needsLookup':false,'inlineEditable':true,'inlineEditExtraData':['maxLength',
'masked'],'isDate':false
},'RECORDTYPE':{
'inlineEditFieldObject':null,'queryOperators':['e',
'n'],'datatypeLetter':'7','filterQueryOperators':['e',
'n'],'isNumber':false,'needsLookup':true,'inlineEditable':false,'inlineEditExtraData':null,'isDate':false
},'HTMLSTRINGPLUSCLOB':{
'inlineEditFieldObject':'MultiLineTextField','queryOperators':['e',
'n',
'l',
'g',
'm',
'h',
'c',
'k',
's',
'i'],'datatypeLetter':'z','filterQueryOperators':['e',
'n',
'c',
'k',
's'],'isNumber':false,'needsLookup':false,'inlineEditable':true,'inlineEditExtraData':['maxLength',
'numCols',
'numRows',
'isHtml'],'isDate':false
},'SWITCHABLE_PERSONNAME':{
'inlineEditFieldObject':null,'queryOperators':['e',
'n',
'l',
'g',
'm',
'h',
'c',
'k',
's',
'i'],'datatypeLetter':'m','filterQueryOperators':['e',
'n',
'c',
'k',
's'],'isNumber':false,'needsLookup':false,'inlineEditable':true,'inlineEditExtraData':null,'isDate':false
},'PERSONNAME':{
'inlineEditFieldObject':'PersonNameField','queryOperators':['e',
'n',
'l',
'g',
'm',
'h',
'c',
'k',
's',
'i'],'datatypeLetter':'M','filterQueryOperators':['e',
'n',
'c',
'k',
's'],'isNumber':false,'needsLookup':false,'inlineEditable':true,'inlineEditExtraData':['hasSalutation',
'reverse',
'picklistId',
'labels'],'isDate':false
},'DUEDATE':{
'inlineEditFieldObject':'DateField','queryOperators':['e',
'n',
'l',
'g',
'm',
'h'],'datatypeLetter':'F','filterQueryOperators':['e',
'n',
'l',
'g',
'm',
'h'],'isNumber':false,'needsLookup':false,'inlineEditable':true,'inlineEditExtraData':['hasTime'],'isDate':true
},'URL':{
'inlineEditFieldObject':'TextField','queryOperators':['e',
'n',
'l',
'g',
'm',
'h',
'c',
'k',
's',
'i'],'datatypeLetter':'U','filterQueryOperators':['e',
'n',
'c',
'k',
's'],'isNumber':false,'needsLookup':false,'inlineEditable':true,'inlineEditExtraData':['maxLength'],'isDate':false
},'CONTENT':{
'inlineEditFieldObject':'TextField','queryOperators':['e',
'n',
'l',
'g',
'm',
'h',
'c',
'k',
's',
'i'],'datatypeLetter':'9','filterQueryOperators':['e',
'n',
'c',
'k',
's'],'isNumber':false,'needsLookup':false,'inlineEditable':true,'inlineEditExtraData':['maxLength'],'isDate':false
},'MULTILINETEXT':{
'inlineEditFieldObject':'MultiLineTextField','queryOperators':['e',
'n',
'l',
'g',
'm',
'h',
'c',
'k',
's',
'i'],'datatypeLetter':'X','filterQueryOperators':['e',
'n',
'c',
'k',
's'],'isNumber':false,'needsLookup':false,'inlineEditable':true,'inlineEditExtraData':['maxLength'],'isDate':false
},'ADDRESSSTATE':{
'inlineEditFieldObject':null,'queryOperators':['e',
'n',
'l',
'g',
'm',
'h',
'c',
'k',
's',
'i'],'datatypeLetter':'S','filterQueryOperators':['e',
'n',
'c',
'k',
's'],'isNumber':false,'needsLookup':false,'inlineEditable':false,'inlineEditExtraData':null,'isDate':false
},'MULTIENUM':{
'inlineEditFieldObject':'MultiEnumField','queryOperators':['e',
'n',
'u',
'x'],'datatypeLetter':'Q','filterQueryOperators':['e',
'n'],'isNumber':false,'needsLookup':true,'inlineEditable':true,'inlineEditExtraData':['picklistId',
'controller',
'controllerLabel',
'height'],'isDate':false
},'STRINGPLUSCLOB':{
'inlineEditFieldObject':'MultiLineTextField','queryOperators':['e',
'n',
'l',
'g',
'm',
'h',
'c',
'k',
's',
'i'],'datatypeLetter':'J','filterQueryOperators':['e',
'n',
'c',
'k',
's'],'isNumber':false,'needsLookup':false,'inlineEditable':true,'inlineEditExtraData':['maxLength'],'isDate':false
},'SFDCENCRYPTEDTEXT':{
'inlineEditFieldObject':null,'queryOperators':['e',
'n',
'l',
'g',
'm',
'h',
'c',
'k',
's',
'i'],'datatypeLetter':'T','filterQueryOperators':['e',
'n',
'c',
'k',
's'],'isNumber':false,'needsLookup':false,'inlineEditable':false,'inlineEditExtraData':null,'isDate':false
},'BIRTHDAY':{
'inlineEditFieldObject':'DateField','queryOperators':['e',
'n',
'l',
'g',
'm',
'h'],'datatypeLetter':'D','filterQueryOperators':['e',
'n',
'l',
'g',
'm',
'h'],'isNumber':false,'needsLookup':false,'inlineEditable':true,'inlineEditExtraData':['hasTime'],'isDate':true
},'INTEGER':{
'inlineEditFieldObject':'NumberField','queryOperators':['e',
'n',
'l',
'g',
'm',
'h'],'datatypeLetter':'N','filterQueryOperators':['e',
'n',
'l',
'g',
'm',
'h'],'isNumber':true,'needsLookup':false,'inlineEditable':true,'inlineEditExtraData':null,'isDate':false
},'BOOLEAN':{
'inlineEditFieldObject':'BooleanField','queryOperators':['e',
'n'],'datatypeLetter':'B','filterQueryOperators':['e',
'n'],'isNumber':false,'needsLookup':true,'inlineEditable':true,'inlineEditExtraData':null,'isDate':false
},'CURRENCY':{
'inlineEditFieldObject':'NumberField','queryOperators':['e',
'n',
'l',
'g',
'm',
'h'],'datatypeLetter':'C','filterQueryOperators':['e',
'n',
'l',
'g',
'm',
'h'],'isNumber':true,'needsLookup':false,'inlineEditable':true,'inlineEditExtraData':null,'isDate':false
},'DATETIME':{
'inlineEditFieldObject':'DateField','queryOperators':['e',
'n',
'l',
'g',
'm',
'h'],'datatypeLetter':'F','filterQueryOperators':['e',
'n',
'l',
'g',
'm',
'h'],'isNumber':false,'needsLookup':false,'inlineEditable':true,'inlineEditExtraData':['hasTime'],'isDate':true
},'DIVISION':{
'inlineEditFieldObject':'StaticEnumField','queryOperators':['e',
'n',
'l',
'g',
'm',
'h',
'c',
'k',
's',
'i'],'datatypeLetter':'I','filterQueryOperators':['e',
'n',
'c',
'k',
's'],'isNumber':false,'needsLookup':true,'inlineEditable':true,'inlineEditExtraData':['picklistData'],'isDate':false
},'INETADDRESS':{
'inlineEditFieldObject':null,'queryOperators':['e',
'n',
'l',
'g',
'm',
'h',
'c',
'k',
's',
'i'],'datatypeLetter':'W','filterQueryOperators':['e',
'n',
'c',
'k',
's'],'isNumber':false,'needsLookup':false,'inlineEditable':false,'inlineEditExtraData':null,'isDate':false
},'TEXT':{
'inlineEditFieldObject':'TextField','queryOperators':['e',
'n',
'l',
'g',

'm',
'h',
'c',
'k',
's',
'i'],'datatypeLetter':'S','filterQueryOperators':['e',
'n',
'c',
'k',
's'],'isNumber':false,'needsLookup':false,'inlineEditable':true,'inlineEditExtraData':['maxLength'],'isDate':false
},'DYNAMICENUM':{
'inlineEditFieldObject':'DynamicEnumField','queryOperators':['e',
'n',
'l',
'g',
'm',
'h',
'c',
'k',
's',
'i'],'datatypeLetter':'L','filterQueryOperators':['e',
'n',
'c',
'k',
's'],'isNumber':false,'needsLookup':true,'inlineEditable':true,'inlineEditExtraData':['picklistId',
'controller',
'controllerLabel'],'isDate':false
},'BITVECTOR':{
'inlineEditFieldObject':null,'queryOperators':['e',
'n',
'l',
'g',
'm',
'h',
'c',
'k',
's',
'i'],'datatypeLetter':'8','filterQueryOperators':['e',
'n',
'c',
'k',
's'],'isNumber':false,'needsLookup':false,'inlineEditable':false,'inlineEditExtraData':null,'isDate':false
},'FORMULA':{
'inlineEditFieldObject':null,'queryOperators':null,'datatypeLetter':'Z','filterQueryOperators':null,'isNumber':false,'needsLookup':false,'inlineEditable':false,'inlineEditExtraData':null,'isDate':false
},'ENUMORID':{
'inlineEditFieldObject':null,'queryOperators':['e',
'n',
'l',
'g',
'm',
'h',
'c',
'k',
's',
'i'],'datatypeLetter':'L','filterQueryOperators':['e',
'n',
'c',
'k',
's'],'isNumber':false,'needsLookup':true,'inlineEditable':false,'inlineEditExtraData':null,'isDate':false
},'DATEONLY':{
'inlineEditFieldObject':'DateField','queryOperators':['e',
'n',
'l',
'g',
'm',
'h'],'datatypeLetter':'D','filterQueryOperators':['e',
'n',
'l',
'g',
'm',
'h'],'isNumber':false,'needsLookup':false,'inlineEditable':true,'inlineEditExtraData':['hasTime'],'isDate':true
},'ADDRESS':{
'inlineEditFieldObject':'AddressField','queryOperators':['e',
'n',
'l',
'g',
'm',
'h',
'c',
'k',
's',
'i'],'datatypeLetter':'A','filterQueryOperators':['e',
'n',
'c',
'k',
's'],'isNumber':false,'needsLookup':false,'inlineEditable':true,'inlineEditExtraData':['showState',
'labels'],'isDate':false
}
}
var InlineEditState = {
'READONLY':{
'display':true,'cssClass':'inlineEditLock'
},'NONE':{
'display':false,'cssClass':''
},'EDIT':{
'display':true,'cssClass':'inlineEditWrite'
}
}
var QueryOperator = {
'NOT_EQUAL':{
'value':'n'
},'EQUALS':{
'value':'e'
},'GREATER_THAN':{
'value':'g'
},'NOT_LIKE':{
'value':'j'
},'EXACT_EQUALS':{
'value':'a'
},'RANGE_EXCLUSIVE':{
'value':'E'
},'EXCLUDES':{
'value':'x'
},'STARTS_WITH':{
'value':'s'
},'NOT_CONTAIN':{
'value':'k'
},'RANGE_INCLUSIVE':{
'value':'I'
},'LESS_THAN':{
'value':'l'
},'RANGE_INCL_RIGHT':{
'value':'R'
},'RANGE_INCL_LEFT':{
'value':'L'
},'LESS_OR_EQUAL':{
'value':'m'
},'CONTAINS':{
'value':'c'
},'NOT_START_WITH':{
'value':'t'
},'INCLUDES':{
'value':'u'
},'LIKE':{
'value':'i'
},'NOT_EXACT_EQUALS':{
'value':'o'
},'GREATER_OR_EQUAL':{
'value':'h'
}
}
/*
* @author mpolcari
* @since 142.wm
*/

function GenericSfdcPage() {
  this.init()

  this.inlineHelpMap = {};
  this.helpFader = null;

  var self = this;

  this.handleHelpHover = function(e){
      var target = getEventTarget(getEvent(e));
      if (!target) return;
      if (target.className == InlineHelp.CLASS_NAME){
          target.className = InlineHelp.CLASS_NAME_HOVER;
      } else if (target.nodeName == 'LABEL' && target.parentNode.className == InlineHelp.CLASS_NAME){
          target.parentNode.className = InlineHelp.CLASS_NAME_HOVER;
      }else if (target.className == InlineHelp.ORB){
          helpSpan = target.parentNode;
          while (!(helpSpan.className == InlineHelp.CLASS_NAME || helpSpan.className == InlineHelp.CLASS_NAME_HOVER)){
              helpSpan = helpSpan.parentNode
          }
          var helpId = helpSpan.id;
          helpSpan.className = InlineHelp.CLASS_NAME_HOVER;
          if (helpId && helpId.length > InlineHelp.ID_SUFFIX.length){
              var helpKey = helpId.substring(0, helpId.length - InlineHelp.ID_SUFFIX.length);
              self.showHelp(helpKey, helpSpan);
          }
      }
  }

  this.handleHelpUnhover = function(e){
      var target = getEventTarget(getEvent(e));
     if (!target) return;
      if ((target.className == InlineHelp.CLASS_NAME_HOVER) && mouseExited(e, target)){
          target.className = InlineHelp.CLASS_NAME;
      } else if ((target.nodeName == 'LABEL' && target.parentNode.className == InlineHelp.CLASS_NAME_HOVER) && mouseExited(e, target.parentNode)) {
          target.parentNode.className = InlineHelp.CLASS_NAME;
      } else if (target.className == InlineHelp.DISPLAY_DIV_CLASS && mouseExited(e, target)){
          var toElement = getEventToElement(getEvent(e));
          if (toElement.className != InlineHelp.ORB && self.helpFader){
              self.helpFader.fadeOut();
          }
      } else if (target.className == InlineHelp.ORB && mouseExited(e, target)){
          var toElement = getEventToElement(getEvent(e));
          var parent = target.parentNode;
          //hack for accessiblity mode, where the img has a link around it.
          if (UserContext.isAccessibleMode){
              parent = parent.parentNode;
          }
          if (toElement.className == InlineHelp.DISPLAY_DIV_CLASS){
              return;
          }
          if (toElement != parent){
              parent.className = InlineHelp.CLASS_NAME;
          }
        if (self.helpFader){
            self.helpFader.fadeOut();
        }
      }
  }

  this.appendToOnloadQueue(function() {addEvent(document, 'mouseover', self.handleHelpHover, false);});
  this.appendToOnloadQueue(function() {addEvent(document, 'mouseout', self.handleHelpUnhover, false);});
}

GenericSfdcPage.prototype.init = function() {
  this.onLoadQueue = [];
  this.onBeforeUnloadQueue = [];
  this.hasRun = false;
  this.relatedLists = [];
  this.relatedListsById = {};
  this.dialogs = {};
  this.messages = {};
  this.appendToOnloadQueue(function() { XBrowser.turnOnBackgroundImageCache(); }, "Turn on CSS background image cache (IE6 only)");
}

GenericSfdcPage.prototype.prependToOnloadQueue = function(fn, description) {
  if (description) fn.desc = description;
  if (this.hasRun) {
    this.execFunctionNoThrow(fn); //if the queue has already run, just execute this function
  } else {
    this.onLoadQueue.unshift(fn);
  }
}

GenericSfdcPage.prototype.appendToOnloadQueue = function(fn, description) {
  if (description) fn.desc = description;
  if (this.hasRun) {
    this.execFunctionNoThrow(fn); //if the queue has already run, just execute this function
  } else {
    this.onLoadQueue.push(fn);
  }
}

GenericSfdcPage.prototype.executeOnloadQueue = function() {
  for(var i = 0; (this.onLoadQueue && (i < this.onLoadQueue.length)); i++) {
    this.execFunctionNoThrow(this.onLoadQueue[i]);
  }
  this.hasRun = true;
  this.onLoadQueue = [];
}

GenericSfdcPage.prototype.prependToOnBeforeUnloadQueue = function(fn, description) {
  if (description) fn.desc = description;
  this.onBeforeUnloadQueue.unshift(fn);
}

GenericSfdcPage.prototype.appendToOnBeforeUnloadQueue = function(fn, description) {
  if (description) fn.desc = description;
  this.onBeforeUnloadQueue.push(fn);
}

GenericSfdcPage.prototype.executeOnBeforeUnloadQueue = function() {
  for(var i = 0; (this.onBeforeUnloadQueue && (i < this.onBeforeUnloadQueue.length)); i++) {
    this.execFunctionNoThrow(this.onBeforeUnloadQueue[i]);
  }
}

GenericSfdcPage.prototype.execFunctionNoThrow = function(fn) {
    try {
      fn();
    } catch (ex) {
      if (fn.desc) ex.sfdcDesc = fn.desc;
      Gack.handleException(ex);
    }
}


GenericSfdcPage.prototype.registerRelatedList = function(listId, visibleRowCount, title, hasMore, refURL, refQS, onlySkipLink) {
  var firstList = (this.relatedLists.length == 0);

  if(!this.relatedListsById[listId]){
      this.relatedListsById[listId] = new RelatedList(listId, visibleRowCount, title, hasMore, refURL, refQS, onlySkipLink);
      this.relatedLists.push(this.relatedListsById[listId]);
  }

  if (this.relatedListPanel) {
    this.relatedListPanel.registerList(this.relatedListsById[listId]);
  }
  ///If this is the first RL on the page, queue up the panel initialization script
   var self = this;
   if (firstList) {
     this.prependToOnloadQueue(
       function () {
         if ((self.relatedListPanel) && (self.relatedListPanel.addListsToPanel)) {
           self.relatedListPanel.addListsToPanel();
         }
      }, "rlHovers: Initializing panel");
    }
}

GenericSfdcPage.prototype.registerDialog = function(dialog) {
    this.appendToOnloadQueue(function() { dialog.createDialog(); }, "Create a dialog");
    this.dialogs[dialog.id] = dialog;
}

GenericSfdcPage.prototype.getDialogById = function(dialogId) {
    return this.dialogs[dialogId];
}

GenericSfdcPage.prototype.getRelatedListById = function(listId) {
  return this.relatedListsById[listId];
}

/**
  Retrieves the current address as an URLencoded retUrl
*/
GenericSfdcPage.prototype.getHrefAsRetURL = function() {
  var href = window.location.href;

  var retUrlArr = href.split("/");
  retUrlArr.splice(0,3);
  return encodeURIComponent("/" + retUrlArr.join("/"));
}

GenericSfdcPage.prototype.includeJavascriptTag = function(scriptUri) {
    return this.embedJSTag(scriptUri, document);
}

GenericSfdcPage.prototype.embedJSTag = function(scriptUri, doc) {
    var html_doc = doc.getElementsByTagName('head').item(0);
    var js = doc.createElement('script');
    js.setAttribute('language', 'javascript');
    js.setAttribute('type', 'text/javascript');
    js.setAttribute('src', scriptUri);
    html_doc.appendChild(js);
    return false;
}

GenericSfdcPage.prototype.embedExternalStyleSheet = function(sheetUrl, doc) {
    var styleNode = doc.createElement("link");
    styleNode.setAttribute("href", sheetUrl);
    styleNode.setAttribute("type", "text/css");
    styleNode.setAttribute("rel", "stylesheet");

    var targetHead = doc.body.parentNode.firstChild;
    targetHead.appendChild(styleNode);
}

GenericSfdcPage.prototype.hideEmbeddingIframe = function(iframeWin) {
    if (iframeWin.name && document.getElementById(iframeWin.name)) {
      document.getElementById(iframeWin.name).style.display = 'none';
      document.getElementById(iframeWin.name).parentNode.style.border ='none';
    }
}

GenericSfdcPage.prototype.setHelp = function(key, text){
    this.inlineHelpMap[key] = text;
}

GenericSfdcPage.prototype.getHelp = function(key){
    return this.inlineHelpMap[key];
}

GenericSfdcPage.prototype.showHelp = function(id, element){
    var help = this.getHelp(id);
    if (help){
        if (!this.displayDiv){
            this.displayDiv = document.createElement("DIV");
            this.displayDiv.className = InlineHelp.DISPLAY_DIV_CLASS;
        }
        document.body.insertBefore(this.displayDiv, document.body.firstChild);
        this.displayDiv.style.top = (getObjY(element) + element.offsetHeight + 8) + 'px';
        if (!this.helpFader){
            this.helpFader = new MouseOverFadeHandler(null, this.displayDiv, false);
        }
        this.displayDiv.innerHTML = help;
        this.helpFader.setPosition();
        var left = (getObjX(element) + element.offsetWidth - this.displayDiv.offsetWidth);
        if (left >= 0){
            this.displayDiv.style.left = left + 'px';
        }
        this.helpFader.fadeIn();
    }
}

/**
 * simple messaging for now. should have type, strength, etc and allow sorting, dismissing...
 */
GenericSfdcPage.prototype.registerMessage = function(id) {
    var message = document.getElementById(id);
    if (message && !this.messages[id]) {
        this.messages[id] = message;
    }
}

GenericSfdcPage.prototype.hideMessage = function(id) {
    var message = this.messages[id];
    if (message) {
        message.style.display = "none";
    }
}

/*
GenericSfdcPage.prototype.createMessage = function(id) {
    no creation yet.
}
*/

/**
  A select element with the up & down arrows next to it.

  @author polcari
  @since 144

*/
function VerticallyArrangableSelectElement(_id) {

  this.id = _id;
  var vaSelectElement = this;
  sfdcPage.appendToOnloadQueue(function() {vaSelectElement.init()});
}

VerticallyArrangableSelectElement.prototype.init = function() {
  var containingNode = document.getElementById(this.id).parentNode.parentNode;
   //find the up & down buttons & attach to them
  var imgs = containingNode.getElementsByTagName("img");

  var _id = this.id;
  for (var i=0; (imgs && (i < imgs.length)); i++) {
    if (hasStyleClass(imgs[i], vaSelectElementConst.UP_CLASS)) {
      addEvent(imgs[i], 'click', function() {moveUp(document.getElementById(_id))}, false);
    } else if (hasStyleClass(imgs[i], vaSelectElementConst.DOWN_CLASS)) {
      addEvent(imgs[i], 'click', function() {moveDown(document.getElementById(_id))}, false);
    }
  }
}

/**
 * Functions for handling Email Relay Activation processing.
 * @author ccopek
 * @since 146
 */

function EmailRelay() {}

/**
 * The Activate Restrict Relay To Domains checkbox has to enable/disable the
 * restrict relay to domains input field based on being checked/un-checked.
 * When Activate is checked, the input field must be enabled.  When Activate
 * is un-checked, the input field must be disabled and blanked out.
 */
EmailRelay.prototype.checkActivateRestrictToDomains = function() {
    var restrictRelayToDomainsCheckbox = document.getElementById(EmailRelayConstants.ACTIVATE_RESTRICT_TO_DOMAINS_ID);
    var restrictRelayToDomains = document.getElementById(EmailRelayConstants.RESTRICT_TO_DOMAINS_ID);
    var restrictRelayToDomainsHidden = document.getElementById(EmailRelayConstants.RESTRICT_TO_DOMAINS_HIDDEN_ID);

    if (restrictRelayToDomainsCheckbox.checked) {
        // Activate Restrict Relay To Domains is checked.  Need to enable the Restrict Relay
        // To Domains input field.
        restrictRelayToDomains.disabled = false;
    } else {
        // Activate Restrict Relay To Domains is un-checked.  Need to disable the Restrict Relay
        // To Domains input field and blank out it's value.
        restrictRelayToDomains.value = '';

        // Blank out the hidden field as well.
        restrictRelayToDomainsHidden.value = '';

        restrictRelayToDomains.disabled = true;
    }
}

/**
 * In the case were the Restrict Relay To Domains field is disabled in either of these scenarios:
 *		- Come into the panel disabled and then is enabled via the activate checkbox being checked
 *	    OR
 *		- Is disabled via the activate checkbox being unchecked.
 * In these scenarios, the value contained in the Restrict Relay to Domains is not being sent
 * when the form is submitted.  A hidden variable is being used to transmit the value to
 * the server.  This function will set the value of the hidden variable to the value of the
 * restrict relay to domains input field when it has changed.
 */
EmailRelay.prototype.changeRestrictToDomains = function() {
    var restrictRelayToDomains = document.getElementById(EmailRelayConstants.RESTRICT_TO_DOMAINS_ID);
    var restrictRelayToDomainsHidden = document.getElementById(EmailRelayConstants.RESTRICT_TO_DOMAINS_HIDDEN_ID);

    restrictRelayToDomainsHidden.value = restrictRelayToDomains.value;
}

/**
 * When the Email Relay TLS Setting is changed to Off or from Off, we need to default
 * the Port as follows:
 * 		Change TLS TO Off - Default Port to 25
 *		Change TLS FROM Off - Default Port to 587
 */
EmailRelay.prototype.handleTlsChange = function() {
    var TLS_OFF_VALUE = 0;
    var DEFAULT_EMAIL_HOST_PORT_INDEX = 0;
    var DEFAULT_TLS_PORT_INDEX = 1;

    var emailRelayTlsSetting = document.getElementById(EmailRelayConstants.EMAIL_RELAY_TLS_SETTING_ID);
    var relayPort = document.getElementById(EmailRelayConstants.EMAIL_HOST_PORT_ID);
    var emailRelayTlsSettingVal = emailRelayTlsSetting.options[emailRelayTlsSetting.selectedIndex].value;
    var relayPortSelectedIndex = relayPort.selectedIndex;

    if (emailRelayTlsSettingVal == TLS_OFF_VALUE && relayPortSelectedIndex == DEFAULT_TLS_PORT_INDEX) {
        // TLS has been changed to Off.  Default Port to non TLS default value.
        // We will only Default the value if it's set to the non-TLS default.  We
        // don't want to change the value if the user has set their port to a custom
        // port.
        relayPort.options[DEFAULT_EMAIL_HOST_PORT_INDEX].selected = true;
    }
    else if (emailRelayTlsSettingVal != TLS_OFF_VALUE && relayPortSelectedIndex == DEFAULT_EMAIL_HOST_PORT_INDEX) {
        // TLS has been turned on.  Default Port to TLS default value.
        relayPort.options[DEFAULT_TLS_PORT_INDEX].selected = true;
    }
}

function ApiUtils() {}

ApiUtils.getApiURL = function(isPartner, version) {
    var url = window.location.href;
    var idx = url.indexOf('/', 10); // well after (http|https)://
    var base = url.substring(0, idx) + "/services/Soap/" + (isPartner ? "u" : "c") + "/" + version;
    return base;
}

ApiUtils.getSessionId = function(){
    var sessionId = getCookie(RequestInfo.pSID);
    return sessionId;
}

// fluff up a 15 char id to return an 18 char id
ApiUtils.to18CharId = function (id) {
 if (id == null || (id.length == 18)) return id;
 id = id.replace(/\"/g, ''); // scrub quotes from this id
 if (id.length != 15) {
  return null;
 }
 var suffix = "";
 for (var i = 0; i < 3; i++) {
  var flags = 0;
  for (var j = 0; j < 5; j++) {
   var c = id.charAt(i * 5 + j);
   if (c >= 'A' && c <= 'Z') {
    flags += 1 << j;
   }
  }
  if (flags <= 25) {
   suffix += "ABCDEFGHIJKLMNOPQRSTUVWXYZ".charAt(flags);
  } else {
   suffix += "012345".charAt(flags-26);
  }
 }
 return id + suffix;
}

ApiUtils.to15CharId = function(id){
    if (!id) return null;
    return id.substring(0, 15);
}

// the id field returned by the API is often returned twice,resulting
// in an array when calling record.get(). this returns a single ID always.
ApiUtils.getId = function(record){
    if (!record) return null;
    var id = record.get("Id");
    // looking for array by checking for "splice()" function
    if (id.splice && id.length && id.length > 0) {
        id = id[0];
    }
    if (id){
        id = ApiUtils.to15CharId(id);
    }
    return id;
}

ApiUtils.soqlEncode = function(str){
    var newStr = str;
    newStr = newStr.replace("\\", "\\\\");
    newStr = newStr.replace("'", "\\'");
    return newStr;
}

/*
* @author mpolcari
* @since 142.ml
*/
function DetailPage() {
    this.relatedListPanel = null;
    this.editMode = false;
    this.inlineEditData = null;
    this.detailButtons = [];
    this.editButtons = [];
    this.errorDiv = null;
    this.saving = false;
}

DetailPage.prototype = new GenericSfdcPage();

DetailPage.prototype.registerRelatedListPanel = function (panelId) {
  this.relatedListPanel = new RelatedListPanel(panelId);
  var self = this;
  this.appendToOnloadQueue(
    function () { //Copy css styleSheets, script includes, & body.className
      var iFrameWindow = self.relatedListPanel.getIFrameNode().contentWindow;
      var iFrameDoc = iFrameWindow.document;
      DomUtil.copyScriptsCssBodyClass(document, iFrameDoc);
      iFrameDoc.body.className += " rlHoverFrame";
      iFrameWindow.sfdcPage = self;
    }, "rlHovers: Importing Scripts and CSS");
}

DetailPage.prototype.getEntityId = function() {
  var href = window.location.href;
  var paths = href.split('?')[0].split('/');
  return paths[paths.length-1];
}

DetailPage.prototype.evalScripts = function (rlElement) {
  var scriptElements = rlElement.getElementsByTagName('script');
  for (var ind = 0; ind<scriptElements.length;ind++){
  	var jsExpression = scriptElements[ind].innerHTML;
  	eval(jsExpression);
  }
}

DetailPage.prototype.hasNoRelatedList = function(doc, listId) {
	var retVal = getElementByIdCSWithDoc(doc, listId);
	if(!retVal){
		return true;		
	}else{
		return false;
	}
}

DetailPage.prototype.desktopAjaxDisplayErrorInline = function() {
	return false;
}

DetailPage.prototype.hook_postRelatedListActionUpdate = function(listUrl) {
	return;
}

DetailPage.prototype.initInlineEdit = function(iled) {
    this.inlineEditData = iled;
    if (iled.isEditable) {
        var self = this;
        this.appendToOnloadQueue(function() { self.inlineEditData.init(); }, "Initialize inline edit");
        this.appendToOnloadQueue(function() { self.initializeButtons(); }, "Setup detail buttons");
    }
}

DetailPage.prototype.getFieldById = function(domId) {
    if (this.inlineEditData) {
        if (domId.search(InlineEditConstants.CELL_ID + "$") != -1) {
            return this.inlineEditData.getField(domId.substring(0, domId.length - InlineEditConstants.CELL_ID.length));
        } else {
            return this.inlineEditData.getField(domId);
        }
    }
}

DetailPage.prototype.initializeButtons = function() {
    var self = this;
    function getButtonsInCell(cellId) {
        var cell = document.getElementById(cellId);
        var buttons = cell.getElementsByTagName("input");
        for (var i = 0; i < buttons.length; i++) {
            var button = buttons[i];
            if (button.type == "button") {
                if (button.name == InlineEditConstants.SAVE_BUTTON || button.name == InlineEditConstants.CANCEL_BUTTON) {
                    self.editButtons.push(button);
                } else {
                    self.detailButtons.push(button);
                }
            }
        }
        var muttons = cell.getElementsByTagName("div");
        for (var i = 0; i < muttons.length; i++) {
            var mutton = muttons[i];
            if (mutton.className == "menuButton") {
                self.detailButtons.push(mutton);
            }
        }
    };
    getButtonsInCell(DetailElement.TOP_BUTTON_ROW);
    getButtonsInCell(DetailElement.BOTTOM_BUTTON_ROW);
}

DetailPage.prototype.focusOnSave = function() {
    this.editButtons[0].focus();
}

DetailPage.prototype.activateInlineEditMode = function() {
    if (!this.errorDiv) {
        this.errorDiv = document.getElementById(DetailElement.DEFAULT_ERROR_DIV_ID);
    }
    this.editMode = true;
    for (var i = 0; i < this.detailButtons.length; i++) {
        this.detailButtons[i].style.display = "none";
    }
    for (var i = 0; i < this.editButtons.length; i++) {
        this.editButtons[i].style.display = "inline";
    }
}

DetailPage.prototype.deactivateInlineEditMode = function() {
    this.editMode = false;
    for (var i = 0; i < this.detailButtons.length; i++) {
        this.detailButtons[i].style.display = "inline";
    }
    for (var i = 0; i < this.editButtons.length; i++) {
        this.editButtons[i].style.display = "none";
    }
    this.clearError();
}

DetailPage.prototype.setError = function(error) {
    if (this.saving) {
        this.saving = false;
        for (var i = 0; i < this.editButtons.length; i++) {
            this.editButtons[i].className = "btn"
            if (this.editButtons[i].name == InlineEditConstants.SAVE_BUTTON) {
                this.editButtons[i].value = LC.getLabel("Buttons", "save");
            } else if (this.editButtons[i].name == InlineEditConstants.CANCEL_BUTTON) {
                this.editButtons[i].value = LC.getLabel("Buttons", "cancel");
            }
        }
    }
    if (error) {
        this.errorDiv.innerHTML = error;
    }
    this.errorDiv.style.display = "block";
}

DetailPage.prototype.clearError = function(error) {
    this.errorDiv.style.display = "none";
}

DetailPage.prototype.refreshDetail = function() {
    //this.deactivateInlineEditMode();
    // TODO: ajax refresh here
    navigateToUrl(window.location);
}

DetailPage.prototype.save = function() {
    if (!this.saving && this.editMode) {
        this.saving = true;
        for (var i = 0; i < this.editButtons.length; i++) {
		    this.editButtons[i].className = "btnDisabled";
		    this.editButtons[i].value = LC.getLabel("Buttons", "saving");
        }
        this.inlineEditData.save();
    }
}

DetailPage.prototype.revert = function() {
    if (!this.saving && this.editMode) {
        this.inlineEditData.revert();
        this.deactivateInlineEditMode();
    }
}

DetailPage.prototype.dblClickField = function(evt, element) {
    var target = getEventTarget(getEvent(evt));
    if (target.nodeName.toLowerCase() != "a") {
        var field = this.getFieldById(element.id);
        if (field && field.state == InlineEditState.EDIT) {
            if (!this.editMode) {
                this.activateInlineEditMode();
            }
            if (!this.inlineEditData.isCurrentField(field)) {
                this.inlineEditData.openField(field);
            }
            eventCancelBubble(evt);
        }
    }
}

DetailPage.prototype.clickField = function(evt, element) {
    var field = this.getFieldById(element.id);
    if (field && this.inlineEditData.isCurrentField(field)) {
        eventCancelBubble(evt);
    }
}

DetailPage.prototype.mouseOverField = function(evt, element) {
    var field = this.getFieldById(element.id);
    if (field) {
        delStyleClass(element, field.getCSSClass());
        addStyleClass(element, field.getCSSHoverClass());
    }
}

DetailPage.prototype.mouseOutField = function(evt, element) {
    var field = this.getFieldById(element.id);
    if (field) {
        delStyleClass(element, field.getCSSHoverClass());
        addStyleClass(element, field.getCSSClass());
    }
}

DetailPage.prototype.dismissILEBanner = function() {
    UserContext.userPreferences.setBoolean("HideInlineEditSplash", true);
    this.hideMessage("ileBanner");
}



var backgroundIFrameTitle;
function EmailTemplateEditorUi(title, isCustomP, formNameP) {
    registerEmptyCellText(LC.getLabel("EmailTemplate", "emptyCellText"));
    this.editAreaIds = null;
    backgroundIFrameTitle = title; //used by EmailEditorJS
    this.isCustom = isCustomP;
    this.formName = formNameP;
}

EmailTemplateEditorUi.prototype.gatherEditableIds = function () {
    var tempIds = new Object(20);
    var elements = document.getElementsByTagName('td');
    var index = 0;
    for (var i = 0; i < elements.length; i++) {
        var element = elements[i];
        if (element.id != null && element.id.length > 0) {
            tempIds[index] = element.id;
            index++;
        }
    }
    editAreaIds = new Object(index);
    for (var i = 0; i < index; i++) {
        editAreaIds[i] = tempIds[i]
    }
}

EmailTemplateEditorUi.prototype.getTopTable = function() {
      var topTable = document.getElementById('topTable');
      var allTables = document.getElementsByTagName("table");
      if ((!topTable) && allTables) {
        topTable = allTables[0];
      }
      return topTable;
    }

EmailTemplateEditorUi.prototype.getDefaultPopupWidth = function (){
  var topTable = getTopTable();
  if (topTable) {
    return Math.min(Math.max(topTable.offsetWidth + 60, 500), 1024);
  } else {
    return 500;
  }
}
EmailTemplateEditorUi.prototype.getDefaultPopupHeight = function (){
  var topTable = getTopTable();
  if (topTable) {
    return Math.min(Math.max(topTable.offsetHeight + 200, 500), 768);
  } else {
    return 500;
  }
}
// Init function called by the onload event handler in the <BODY> tag.
EmailTemplateEditorUi.prototype.init = function () {
    if (top != this) {
    	if(this.isCustom) {
	        if (document.body) {
	 	       parent.setFrameWidth(document.body.scrollWidth);
    		   parent.setFrameHeight(document.body.scrollHeight);
	        }
    	}
    	else {
	        this.gatherEditableIds();
	        setEditableIds(editAreaIds);
	        registerLockingConfirmText(LC.getLabel("EmailTemplate", "SetLockedConfirmText"));
	        setLockedImageSrc("/email/wysiwyg/images/locked.gif");
	        setUnlockedImageSrc("/email/wysiwyg/images/unlocked.gif");
	        // Tells the editor where to load the values from, and where to save them to.
	        registerEditForm(parent.document.forms[this.formName]);
	        initializeEditor();
	        //signal the parent that the edit ctr is finished initializing
	        top.editFinishedLoading=true;
        }
    } else {
        window.resizeTo(this.getDefaultPopupWidth(), this.getDefaultPopupHeight());
    }
}
var SELECT_MENU_SELECT = 1;
var SELECT_MENU_SIDEWAYS = 2;

/**
 * A class that acts more or less exactly like a select box.
 *
 * @param sourceID The DOM id of a UL that will form the elements of the select
 * @param targetID A div target in which to build the SelectMenu
 * @param displayWord A string to display as the initial entry of the menu.
 * @param hasDefaultAction Splits the button in two parts if true: the button area, which when clicked
 *		  loads the first link in the list, and the arrow area, which displays or hides the menu.
 * @author emoses
 * @deprecated don't use this, use MenuButton instead
 */
function SelectMenu(sourceId, targetId, displayWord, selectMenuOuterClass, selectMenuClass, style, groupClass, needScrollbars, hasDefaultAction, isRightAligned){
    this.sourceList = document.getElementById(sourceId)
    this.select = null;
    this.menuDiv = null;
    this.displayWord = displayWord;
    this.targetDiv = document.getElementById(targetId)
    this.divClass = selectMenuOuterClass;
    this.menuClass = selectMenuClass;
    this.menuOpen = false;
    this.hasSetPosition = false;
    this.scrollbars = needScrollbars;
    this.hasDefaultAction = hasDefaultAction;
    this.isCreateNew = false;
    this.isRightAligned = isRightAligned;

    if (style){
        this.style = style;
    } else {
        this.style = SELECT_MENU_SELECT;
    }

    var self = this;

    this.documentHideMenu = function(e){
        var elem = getEventTarget(e);
        if(elem.className == groupClass) { return;}
        if (self.menuOpen){
            self.hideMenu();
        }
    }

    this.handleDocumentKeyDown = function(e){
        var evt = getEvent(e);

        if (self.menuOpen && evt.keyCode == KEY_ESC){
            //ESC key, when not pinned and all the way out
            self.hideMenu();
        }
    };

    this.handleDivClick = function(e) {
        var elem = getEventTarget(e);
        if(elem.className == groupClass) { return;}

        if (self.hasDefaultAction) {
            var nOffsetX=(e.layerX)?(e.layerX):e.offsetX;
            if(nOffsetX<(elem.offsetWidth-17)) {
                window.location = self.sourceList.childNodes[0].href;
                eventCancelBubble(e);
                return;
            }
        }
        if (self.menuOpen){
            self.hideMenu();

        } else {
            self.showMenu();
            self.setPosition();
        }
        eventCancelBubble(e);
    }

    if (!this.sourceList) return;
    if (!this.targetDiv) return;
    this.init();
}

SelectMenu.prototype.showMenu = function(){
    if (!this.isCreateNew) {
        this.targetDiv.style.position = "relative";
    }
    this.menuDiv.setStyle("display", "block");
    this.menuOpen = true;
};

SelectMenu.prototype.hideMenu = function(){
    if (!this.isCreateNew) {
        this.targetDiv.style.position = "static";
    }    
        this.menuDiv.setStyle("display", "none");
        this.menuOpen = false;
};

SelectMenu.prototype._setPositionSelect = function(){
    if (!this.hasSetPosition) {
        this.hasSetPosition = true;

        if (!this.isCreateNew) {
            this.menuDiv.setStyle("top", this.select.offsetHeight + "px");
            if(!this.isRightAligned){
                this.menuDiv.setStyle("left", "0px");
            }else{
                this.menuDiv.setStyle("right","3px");
            }
        }

        // Begin IE fix
        // Can't do this in CSS because the whitespace at the end of
        // the link elements don't function as a link.
        // So, grab the maximum text length of the link elements, make that
        // the width of the container div, and then set all link elements
        // to width 100%.

        var maxWidth = this.sourceList.childNodes[0].scrollWidth;
        var maxOptionWidth = maxWidth;
        for (var i = 1; i < this.sourceList.childNodes.length; i++) {
            if (this.sourceList.childNodes[i].scrollWidth > maxWidth)
                maxWidth = this.sourceList.childNodes[i].scrollWidth;
        }
        // this should not be set for menu with scrollbars
        for (var i = 0; i < this.sourceList.childNodes.length; i++) {
            this.sourceList.childNodes[i].style.width = "100%";
            if(this.sourceList.childNodes[i].offsetWidth > maxOptionWidth){
                maxOptionWidth = this.sourceList.childNodes[i].offsetWidth;
            }
        }
        this.menuDiv.setStyle("width", maxWidth + "px");
        // End IE Fix.

        // if the menu should have scrollbars then proceed with height calculations below
        if(this.scrollbars){
            // show a maximum of 20 items
            var maxHeight = this.sourceList.childNodes[0].offsetHeight*20;
            var actualHeight = this.sourceList.offsetHeight;
            if (actualHeight < maxHeight){
                maxHeight = actualHeight;
            }
            this.menuDiv.setStyle("height", maxHeight + "px");
            this.menuDiv.setStyle("overflowY", "auto");


        }
        if (this.menuDiv.div.offsetWidth < this.select.childNodes[0].offsetWidth){
            this.menuDiv.setStyle("width", this.select.childNodes[0].offsetWidth + "px");
            // firefox fix to ensure that horizontal scrollbar doesn't show up on click
            if(this.scrollbars){
                this.menuDiv.setStyle("width", (maxOptionWidth + 34) + "px");
                this.menuDiv.setStyle("backgroundColor", "#CCC");
                this.menuDiv.div.childNodes[0].style.width = this.menuDiv.div.offsetWidth - 34 + "px";
            }
        }
    }
};

SelectMenu.prototype._setPositionSideways = function() {
    this.menuDiv.setStyle("top", this.select.offsetHeight / 2 + "px");
    this.menuDiv.setStyle("left", this.select.offsetWidth + "px");
}

SelectMenu.prototype.init = function(){

    this.select = document.createElement("div");
    this.select.className = this.divClass;

    this.targetDiv.insertBefore(this.select, this.targetDiv.firstChild);

    var upperDiv = document.createElement("div");
    if (this.hasDefaultAction) {
        upperDiv.className = "selectMenuButton hasDefault";
    } else {
        upperDiv.className = "selectMenuButton";
    }
    this.select.appendChild(upperDiv);
    upperDiv.appendChild(document.createTextNode(this.displayWord));

    this.menuDiv = document.createElement("div");
    this.menuDiv.className = this.menuClass;
    this.select.appendChild(this.menuDiv);

    this.menuDiv.appendChild(this.sourceList);
    switch(this.style){
    case SELECT_MENU_SIDEWAYS:
        this.setPosition = this._setPositionSideways;
//      document.body.appendChild(this.menuDiv);
        break;
    case SELECT_MENU_SELECT:
    default:
        this.setPosition = this._setPositionSelect;
    }

    this.menuDiv = new iframeShim(this.menuDiv);
    this.menuDiv.setStyle("display", "none");
    this.menuDiv.setStyle("position", "absolute");

    addEvent(document, 'click', this.documentHideMenu, false);
    addEvent(document, 'keydown', this.handleDocumentKeyDown, true);
    addEvent(this.targetDiv, 'click', this.handleDivClick, false);
};

    /** @author zzhou
     *  @since 150
     * Used to determine whether we take a user directly to another forecast on inputting a value in the forecast user lookup
     * input box and pressing enter */

function FctSummaryPage() {}

FctSummaryPage.onForecastLookupBoxInput = function(element,oEvent) {
	if ((oEvent.keyCode && oEvent.keyCode==13) || (oEvent.which && oEvent.which==13)) {
		document.getElementById(ForecastSummaryPage.pLOOKUP_INPUT_ENTERED).value = 1;
		element.form.submit();
	}
}

function SearchQueryTool(targetEntityInputId) {
	this.queryTypeWithEntityStorage = new Object();
	this.targetEntityInputId = targetEntityInputId;
	this.entityInput = null;
	this.savedValue = null;
}

SearchQueryTool.prototype.addQueryTypeWithEntity = function (queryType){
	this.queryTypeWithEntityStorage[queryType] = queryType;
}

SearchQueryTool.prototype.processTargetEntity = function (queryType){
	if(!this.entityInput && !this.targetEntityInputId){ return; }
	
	// initialize lazily
	if (!this.entityInput){ 
		this.entityInput = document.getElementById(this.targetEntityInputId);
		this.targetEntityInputId = null;
	}
	
	if (queryType in this.queryTypeWithEntityStorage){
		this.entityInput.disabled = true;
		this.savedValue = this.entityInput.value;
		this.entityInput.value = '';
	} else {
		this.entityInput.disabled = false;
		if(this.savedValue){
			this.entityInput.value = this.savedValue;
		}
	}
}
/**
 * This class handles auto complete functionality on the tag header element.
 * It grabs _all_ the tags for the current user from LookupTagsPage and caches this list.
 * Given the current count limit on private tags (500) this is not a problem, but in the 
 * future we might want to be more selective about what we grab.
 * 
 * @author mpaksoy
 * @since 150
 * based on AutoComplete.js by rchoi
 */

/**
 * @param elem text area element used for auto complete
 * NOTE: this element needs a DOM id
 * @param callback keypress event handler callback. Auto complete needs priviledged access to
 * the keypress event to grab the tab/enter keys, it will pass the event on as necessary.
 */
function TagAutoComplete(elem, callback) {
    this.element = elem;
    this.id = this.element.id;

    this.recordHeight = 18;

    this.selection = false;
    this.keyPressCallback = callback;

    // EVENT HANDLERS
    var self = this;
    addEvent(this.element, 'keyup', function(e) { self.onKeyUp(e); });
    addEvent(this.element, 'keydown', function(e) { self.captureKeyPress(e); });
    addEvent(this.element, 'keypress', function(e) { self.captureKeyPress(e, true); });
    addEvent(this.element, 'blur', function(e) { self.onBlur(e); });
    addEvent(window, 'resize', function(e) { self.resizeLocationUpdater(e); });
    
    window[this.id + TagAutoComplete.MOUSE_OVER_HANDLER] = function(i) { self.updateSelection(i); };
}

// STATIC CONSTANTS
TagAutoComplete.MOUSE_OVER_HANDLER = '_autoCompleteMouseOverHandler';
TagAutoComplete.BOX_ID = '_autoCompleteBoxId';
TagAutoComplete.FRAME_ID = '_autoCompleteFrameId';
TagAutoComplete.ROW_ID = '_autoCompleteRowId';
TagAutoComplete.BOX_CSS_CLASS = 'autoCompleteBox';
TagAutoComplete.ROW_CSS_CLASS = 'autoCompleteRow';
TagAutoComplete.SELECTED_ROW_CSS_CLASS = 'autoCompleteSelectedRow';
TagAutoComplete.MORE_ROW_CSS_CLASS = 'autoCompleteMoreRow';
TagAutoComplete.MIN_LENGTH_THRESHOLD = 1;
TagAutoComplete.MAX_SUGGESTIONS = 15;

// INSTANCE METHODS

/**
 * Set the function to call when text is entered to see if text area needs to
 * be resized.
 */
TagAutoComplete.prototype.setResizer = function(callback) {
    this.resizeCallback = callback;
}

/**
 * Call the resize handler and move auto complete box if necessary.
 */
TagAutoComplete.prototype.resizeTextArea = function() {
    if (this.resizeCallback && this.resizeCallback()) { // if resized
        this.updateBoxLocation();
    } 
}

/**
 * Display the auto-complete box containing the given set of suggestions.
 * @param records array of suggestions
 */
TagAutoComplete.prototype.displayResults = function(records) {
    if (!records || !(records instanceof Array)) return;
    if (records.length == 0) {
        if (this.box) {
            this.doClear();
        }
        return;
    }

    var html = [];
    html.push("<table width='100%' cellpadding='0' cellspacing='0' border='0'>");
    for (var i = 0; i < records.length; i++){
        if (i >= TagAutoComplete.MAX_SUGGESTIONS) {
            html.push("<tr><td>");
            html.push("<div ");
            html.push("class='" + TagAutoComplete.MORE_ROW_CSS_CLASS+"'");
            html.push(">");
            html.push(LC.getLabel('TagHeader','auto_complete_more'));
            html.push("</div>");
            html.push("</td></tr>");
            break;
        }
        
        var record = records[i];
        html.push("<tr><td>");
        html.push("<div ");
        html.push("onmouseover='");
        html.push("window."+this.id+TagAutoComplete.MOUSE_OVER_HANDLER+"("+i+");'");
        html.push("class='"+TagAutoComplete.ROW_CSS_CLASS+"' ");
        html.push("id='"+this.getRowId(i)+"'>");
        html.push("<span>");
        html.push(record);
        html.push("</span>");
        html.push("</div>");
        html.push("</td></tr>");
    }
    html.push("</table>");

    var recordsShown = (records.length > TagAutoComplete.MAX_SUGGESTIONS) ? 
                        TagAutoComplete.MAX_SUGGESTIONS + 1: records.length;
    if (!this.box){
        this.makeBox();
        this.updateBoxLocation();
    }
    this.box.div.innerHTML = html.join('');
    this.showBox(true);
    this.updateSelection(0); // pick the first one by default
}

TagAutoComplete.prototype.resizeLocationUpdater = function() {
    if (this.box) // box already created
        this.updateBoxLocation();
}

TagAutoComplete.prototype.updateBoxLocation = function() {
    this.moveBox(getObjY(this.element) + this.element.offsetHeight, getObjX(this.element));
}

/**
 * Connects to the servlet to grab the list tags for the current user.
 * This list is only grabbed once, and cached.
 */
TagAutoComplete.prototype.doLookup = function() {
    if (this.IS_PROCESSING) return;
    if (this.cache || this.doneCaching) return;
    
    var servlet = TagConstants.LOOKUP_TAGS_PAGE;
    this.IS_PROCESSING = true;
    var self = this;
    
    var requestHandler = function(request) {
        var resp = request.responseText;
        if (resp) {
            self.cache = resp.split(', ');
            self.filterDisplayResults();
        }
        self.doneCaching = true;
        self.IS_PROCESSING = false;
    }
    
    XBrowser.getHttpResponse(servlet, requestHandler);
}

/**
 * Hide the suggestion box and deselection suggestion.
 */
TagAutoComplete.prototype.doClear = function(){
    this.selection = false;
    this.results = false;
    this.lastSearched = false;
    this.showBox(false);
}

/**
 * Update the selected suggestion. Updates highlighing on suggestion rows accordingly.
 */
TagAutoComplete.prototype.updateSelection = function(index) {
    if (this.getRow(this.selection)) { // if something previously selected, unselect
        this.getRow(this.selection).className = TagAutoComplete.ROW_CSS_CLASS;
    }
    var row = this.getRow(index);
    if (!row) return;
    this.selection = index;
    row.className = TagAutoComplete.SELECTED_ROW_CSS_CLASS;
}

/**
 * Return the last word in the comma seperated list of tags from the text area.
 * Trims the whitespace around this element.
 */
TagAutoComplete.prototype.getCurrentWord = function() {
    if (!this.element || !this.element.value) return;
    var tags = this.element.value.split(",");
    return tags[tags.length-1].replace(/^\s+|\s+$/g,""); // trim spaces before/after word
}

/**
 * Search the cache for tags starting with the current word (see getCurrentWord)
 * If the new search string matches beginning of the last search string, it will
 * do an incremental search on the results of the last search
 * If not, it will do a brute force search.
 *
 * With 500 tags, this is not a very big issue, but might become an issue in the future.
 * Might want to use a data structure like a trie to  easily narrow down results and
 * backtrack as necessary.
 */
TagAutoComplete.prototype.filterDisplayResults = function() {
    if (!this.cache) {
        this.results = false;
        return; // need stuff in the cache
    }
    var val = this.getCurrentWord();
    if (this.results && this.lastSearched && TagAutoComplete.isMatch(this.lastSearched, val)) {
        var newresults = [];
        for (var i = 0; i < this.results.length; i++) {
            var string = this.results[i];
            if (TagAutoComplete.isMatch(val, string)) {
                newresults.push(this.results[i]);
            }
        }
        this.results = newresults;
    } else {
        this.results = [];
        for (var i = 0; i < this.cache.length; i++) {
            var string = this.cache[i];
            if (TagAutoComplete.isMatch(val, string)) {
                this.results.push(string);
            }
        }
        this.results.sort(function(a,b) { // case insensitive compare
                var aNorm = a.toLowerCase();
                var bNorm = b.toLowerCase();
                if (aNorm == bNorm) return 0;
                return (aNorm > bNorm) ? 1 : -1;
            });
    }
    
    this.lastSearched = val;
    this.displayResults(this.results);
}

/**
 * Get the id of the suggestion row with index
 */
TagAutoComplete.prototype.getRowId = function(index) { 
    return this.id + TagAutoComplete.ROW_ID + index;
}

/**
 * Get the DOM node for the suggestion row
 */
TagAutoComplete.prototype.getRow = function(index) {
    return document.getElementById(this.getRowId(index));
}

// EVENTS (called from within the context of this class)

/**
 * Called by the (yes, you guessed it) key up listener.
 * Calls the enter/tab/esc keys accordingly, or updates the suggestions for the
 * new string.
 */
TagAutoComplete.prototype.onKeyUp = function(event){
    if (!event || !event.keyCode) return;

    this.captureKeyPress(event);

    var val = this.element.value;
    if (this.IS_PROCESSING) {
        return;
    } else if (TagAutoComplete.isComplete(event)) {
        this.complete();
        return;
    } else if (TagAutoComplete.isNavigation(event)) {
        this.handleNav(event);
        return;
    } else if (TagAutoComplete.isEscape(event) || !this.checkCursorAtEnd()) {
        this.doClear();
        return;
    } else if (TagAutoComplete.isIgnore(event)) {
        return;
    }

    if (!this.cache) {
        this.doLookup();
    }

    this.resizeTextArea();
    
    if (val != null && val.length >= TagAutoComplete.MIN_LENGTH_THRESHOLD){
        this.filterDisplayResults();
    } else if (val == null || val.length < TagAutoComplete.prototype.MIN_LENGTH_THRESHOLD){
        this.doClear();
    }
}

/**
 * Auto-complete with the current suggestion: replace the partially entered
 * suggestion with the text of the chosen suggestion and close suggestion box.
 */
TagAutoComplete.prototype.complete = function() {
    if (this.selection === null || this.selection === false) return;
    if (this.selection < 0 || this.selection >= this.results.length) return;
    
    var suggestion = this.results[this.selection];
    this.completeText(suggestion);
    this.doClear();
    this.element.focus();

    if (XBrowser.userAgent.isSafari) {
        this.element.selectionStart = this.element.value.length;
        this.element.selectionEnd = this.element.value.length;
    }
}

/**
 * Handles the string substitution for completing the partially typed
 * tag with the chosen suggestion.
 */
TagAutoComplete.prototype.completeText = function(text) {
    var values = this.element.value.split(',');
    if (values.length <= 1) {
        values = [text];
    } else {
        values[values.length-1] = ' '+text;
    }
    this.element.value = values.join(',')+', ';
    this.resizeTextArea();
}

/**
 * Called when focus moves away from our text area. Closes the suggestion box.
 * There's a delay associated with this to allow clicks to be processed on the
 * suggestion box, otherwised it's closed before the event can be caught.
 */
TagAutoComplete.prototype.onBlur = function() {
    var self = this;
    var handler = function() { self.doClear(); }
    setTimeout(handler, 200);
}

/**
 * Handle the up and down navigation keys. Wraps around the suggestion box (going up at the top
 * will go back to the bottom).
 */
TagAutoComplete.prototype.handleNav = function(event) {
    if (!event || !event.keyCode) return;
    if (!this.results || (this.results.length == 0)) return;	
    
    var lastIndex = (this.results.length > TagAutoComplete.MAX_SUGGESTIONS) ?
                        TagAutoComplete.MAX_SUGGESTIONS - 1: this.results.length - 1;

    var keyCode = event.keyCode;
    var newSelect = this.selection;
    if (keyCode == KEY_ARROW_U) {
        if (newSelect === false) { // nothing selected yet
            newSelect = lastIndex;
        } else {
            newSelect--;
        }
    } else if (keyCode == KEY_ARROW_D) {
        if (newSelect === false) {
            newSelect = 0;
        } else {
            newSelect++;
        }
    }

    if (newSelect > lastIndex) {
        newSelect = 0;
    } else if (newSelect < 0) {
        newSelect = lastIndex;
    }

    this.updateSelection(newSelect);
}

// BOXES
TagAutoComplete.prototype.makeBox = function() {
    // div for the contents
    this.box = document.createElement("div");
    this.box.id = this.id+TagAutoComplete.BOX_ID;
    this.box.className = TagAutoComplete.BOX_CSS_CLASS;
    document.body.appendChild(this.box);
    // add event handler
    var self = this;
    addEvent(this.box, 'click', function() { self.complete(); });
    this.box = new iframeShim(this.box);
    return this.box;
}

TagAutoComplete.prototype.moveBox = function(top, left){
    if (this.box){
        this.box.setStyle('top', top + "px");
        this.box.setStyle('left', left + "px");
    }
}

TagAutoComplete.prototype.showBox = function(isVisible){
    var disp = isVisible ? "block" : "none";

    if (this.box){
        this.box.setStyle('display', disp);
    }
}

/**
 * Prevent the navigation and tab/enter keys from being handled
 * @param event event object for the key/up/down/press
 * @param boolean set to true, if you want to execute keyPressCallback as necessary
 */
TagAutoComplete.prototype.captureKeyPress = function(event, isKeyPress) {
    if (!event || (!event.charCode && !event.keyCode)) {
        return;
    }
    if (this.selection === false) {
        if (isKeyPress && this.keyPressCallback) this.keyPressCallback(event);
        return;
    }

    var code = event.charCode ? event.charCode : event.keyCode;
    if (code == KEY_ENTER || code == KEY_TAB || code == KEY_ARROW_U || code == KEY_ARROW_D || code == KEY_ESC) {
        TagAutoComplete.stopBubble(event);
    }
}

/**
 * Return true if there is no text after cursor in input element (excluding spaces).
 * If 1 or more characters are selected, returns false.
 * Note: Behavior is undefined if the element does not have focus, so make sure it does.
 */
TagAutoComplete.prototype.checkCursorAtEnd = function() {
    if (!this.element) return false;
    var empty = /^\s*$/;
    var afterCaret;
    if (XBrowser.userAgent.isIE) {
        var selRange = document.selection.createRange().duplicate();
        if (selRange.text && (selRange.text.length > 0)) return false; // text selected
        var textRange = selRange.duplicate();
        textRange.moveToElementText(this.element);
        try { // setEndPoint is known throw exceptions on bad days
            selRange.setEndPoint("EndToEnd", textRange);
            afterCaret = selRange.text;
        } catch (e) {
            return true;
        }
    } else { // life's so much better with Firefox
        if (this.element.selectionStart != this.element.selectionEnd) return false; // text selected
        afterCaret = this.element.value.substring(this.element.selectionStart);
    }
    return empty.exec(afterCaret);
}

// STATIC METHODS
TagAutoComplete.isNavigation = function(event){
    var code = event.keyCode;
    return (code == KEY_ARROW_U || code == KEY_ARROW_D);
}

// 9 is TAB; 16 is SHIFT-TAB
TagAutoComplete.isIgnore = function(event){
    var code = event.keyCode;
    return (code == 16 || (code >= 33 && code <= 46) || (code >= 112 && code <= 123));
}

TagAutoComplete.isEscape = function(event){
    var code = event.keyCode;
    return code == KEY_ESC;
}

/**
 * Tab/enter keys execute auto complete.
 * Checks if the current keystroke is tab or enter.
 */
TagAutoComplete.isComplete = function(event){
    var code = event.keyCode;
    return (code == KEY_ENTER || code == KEY_TAB);
}

/**
 * Return the normalized version of the given tag.
 */
TagAutoComplete.normalize = function(tag) {
    return tag.replace(/\s|-|_/g,"").toLowerCase()
}

/**
 * Return true if substring matches the beginning of string. Case insensitive.
 * Ex. if substring='abc' and string='AbCdef' then this will return true.
 */
TagAutoComplete.isMatch = function(substring, string) {
    if (!substring || !string) return false;
    substring = TagAutoComplete.normalize(substring);
    string = TagAutoComplete.normalize(string);
    if (substring.length > string.length) return false;
    return (string.indexOf(substring) === 0);
}

/**
 * Stops bubbling on the event and prevents default browser behavior at the same time.
 */
TagAutoComplete.stopBubble = function(event) {
    if (XBrowser.userAgent.isIE) {
        event.returnValue = false;
        event.cancelBubble = false;
    } else {
        event.preventDefault();
        event.stopPropagation();
    }
}

/*
 * History Storage for ajax based states
 */
/** 
   Material Copyrighted  2005, Brad Neuberg, bkn3@columbia.edu  
   Modified by salesforce.com 2007
   --------------------------------STARTS HERE-----------------------------------------

   Copyright (c) 2005, Brad Neuberg, bkn3@columbia.edu
   http://codinginparadise.org
   
   Permission is hereby granted, free of charge, to any person obtaining 
   a copy of this software and associated documentation files (the "Software"), 
   to deal in the Software without restriction, including without limitation 
   the rights to use, copy, modify, merge, publish, distribute, sublicense, 
   and/or sell copies of the Software, and to permit persons to whom the 
   Software is furnished to do so, subject to the following conditions:
   
   The above copyright notice and this permission notice shall be 
   included in all copies or substantial portions of the Software.
*/ 
DhtmlHistory.POLLING_FREQUENCY = 300;
DhtmlHistory.WAIT_FREQUENCY = 500;
 function DhtmlHistory(storageMap, listenerFunction, parentObject) {
   this.locationOfBlankPage = "/back_blank.html?";
	/** Our current hash location, without the "#" symbol. */
   this.currentLocation = null;
   this.listener = listenerFunction;
   /** A hidden IFrame we use in Internet Explorer to detect history
       changes. */
   this.iframe = null;
   /** Indicates to the browser whether to ignore location changes. */
   this.ignoreLocationChange = false;
   /** The amount of time in milliseconds an add request has to wait in line before being
       run on a window.setTimeout. */
   this.currentWaitTime = 0;
   this.historyStorage = storageMap;
   // initialization
   this.parentObject = parentObject;
   this.create();
}

DhtmlHistory.prototype.add = function(newLocation, historyData) {	
	// most browsers require that we wait a certain amount of time before changing the
	// location, such as 200 milliseconds; thus we internally handle this
	// detail by using a 'currentWaitTime' variable and have requests wait in line
	var self = this;
	var addImpl = function() {
	   // indicate that the current wait time is now less
	   if (self.currentWaitTime > 0) {
	      self.currentWaitTime = self.currentWaitTime - DhtmlHistory.WAIT_FREQUENCY;
	   }
	   // remove any leading hash symbols on newLocation
	   newLocation = self.removeHash(newLocation);
	   var idCheck = document.getElementById(newLocation);
	   if (idCheck) {
	      Gack.sendGack("History locations can not have the same value as any id's that might be in the document");
	      return; 
	   }
	   self.historyStorage[newLocation] = historyData;
	   // indicate to the browser to ignore this upcomming 
	   // location change
	   self.ignoreLocationChange = true;
	   // save this as our current location
	   self.currentLocation = newLocation;
	   // change the browser location
	   window.location.hash = newLocation;
	   // change the hidden iframe's location if on IE
	   if (XBrowser.userAgent.isIE){
	   		// write out a hidden iframe for IE and
	   		if(!self.iframe) {
		      	var b = document.getElementsByTagName('body')[0];
		      	if(!b){ return; }
		        var divElem = document.createElement('div');
		       	b.appendChild(divElem);
		       	divElem.style.display = 'none';
		        divElem.innerHTML = "<iframe class='dhtmlHistoryFrame' name='DhtmlHistoryFrame' id='DhtmlHistoryFrame' "
		                               + "src='"+ self.locationOfBlankPage + newLocation + "'></iframe>";
		         self.iframe = document.getElementById("DhtmlHistoryFrame");
	   	  } else {	
	      	self.iframe.src = self.locationOfBlankPage + newLocation;
	   	  }
	   }
	};
	// now execute this add request after waiting a certain amount of time, so as to
	// queue up requests
	 window.setTimeout(addImpl, this.currentWaitTime);
	 // indicate that the next request will have to wait for awhile
	 this.currentWaitTime = this.currentWaitTime + DhtmlHistory.WAIT_FREQUENCY;
}

/** Gets the current hash value that is in the browser's
       location bar, removing leading # symbols if they are present. */
DhtmlHistory.prototype.getCurrentLocation = function() {
	return this.removeHash(window.location.hash);
}
   
DhtmlHistory.prototype.create = function() {

      var initialHash = this.getCurrentLocation();
      this.currentLocation = initialHash;
      this.ignoreLocationChange = true;
      if(!XBrowser.userAgent.isIE){
      	var self = this;
      	var locationHandler = function() {
       	 	self.checkLocation();
      	};
      	setInterval(locationHandler, DhtmlHistory.POLLING_FREQUENCY);
      }
}
   
DhtmlHistory.prototype.fireHistoryEvent = function(oldHash, newHash) {
    this.listener.call(this.parentObject, oldHash, newHash);
}

DhtmlHistory.prototype.shouldProceedWithHistoryEvent = function () {
	if (this.ignoreLocationChange) {
       this.ignoreLocationChange = false;
       return false;
    } else {
    	return true;
    }
}

DhtmlHistory.prototype.checkLocation = function() {
   	if (XBrowser.userAgent.isIE || !this.shouldProceedWithHistoryEvent()){ return; }
    var hash = this.getCurrentLocation();
    if (hash == this.currentLocation) { return; }
    // save this new location
    var oldHash = this.currentLocation;
    this.currentLocation = hash;
    this.fireHistoryEvent(oldHash, hash);
}  
   
DhtmlHistory.prototype.removeHash = function(hashValue) {
    if (!hashValue){return null;}
    else if (hashValue == "" ||  (hashValue.length == 1 && hashValue.charAt(0) == "#")) { return ""; } 
    else if (hashValue.length > 1 && hashValue.charAt(0) == "#") { return hashValue.substring(1);} 
    else { return hashValue; }     
}          
   
DhtmlHistory.prototype.iframeLoaded = function(newLocation) {
    if (!XBrowser.userAgent.isIE || !this.shouldProceedWithHistoryEvent()){ return; }
    // get the new location
    var hash = new String(newLocation.search);
    if (!hash || (hash.length == 1 && hash.charAt(0) == "?")) {
       hash = "";
    } else if (hash.length >= 2 && hash.charAt(0) == "?") {
       hash = hash.substring(1);
    }
    
   	var oldHash = this.removeHash(window.location.hash);
    window.location.hash = hash;
    this.fireHistoryEvent(oldHash, hash);
}
/**
 * Material Copyrighted  2005, Brad Neuberg, bkn3@columbia.edu 
 * Modified by salesforce.com 2007
 * 
 * --------------------------------ENDS HERE-----------------------------------------
 */

/*
    This controls the greying out of certain checkboxes on the user edit page according to
    the license type that's picked by the user.

    @param picklistID. 	String.  The ID of the picklist that controls the user's license types.
    @param dataMap.		Map.     The control data.  It should be a map from picklist values to
                                 an array of the dom ids of checkboxes that should be disabled if that
                                 picklist values is checked.  For instance, if the 'mktUser'
                                 and 'offline' should be disabled when the option in picklistID with the
                                 value '0' is selected, the checkbox 'other' should be disabled when
                                 '1' is selected, and nothing should be greyed when '2' is selected,
                                 then the map should look like this:

                                {
                                  '0' : ['mktUser', 'offline'],
                                  '1' : ['other'],
                                  '2' : []
                                }
    @author emoses
    @since 144
*/
function UserEdit(picklistId, dataMap){
    var self = this;

    self.picklist = document.getElementById(picklistId);
    self.controlMap = dataMap;

    if (!(self.picklist && self.controlMap)) return;
    self.allCheckboxes = null;

    function initAllChecks(){
        self.allCheckboxes = {};
        for (var val in self.controlMap){
            for (var i = 0; i < self.controlMap[val].length; i++){
                var controlId = self.controlMap[val][i];
                var control = (document.getElementById(controlId));
                if (control){
                    self.allCheckboxes[controlId] = true;
                }
            }
        }
    }

    this.handleSelectChange = function(e){
        var curr = self.picklist.options[self.picklist.selectedIndex];
        if (curr.value && curr.value.length > 0){
            self.setChecksTo(curr.value);
        } else {
            self.resetChecks();
        }
    }

    initAllChecks();
    addEvent(self.picklist, "change", this.handleSelectChange, false);
    self.handleSelectChange(null);
}

UserEdit.prototype.resetChecks = function(){
    for (var id in this.allCheckboxes){
        document.getElementById(id).disabled = false;
    }
}

UserEdit.prototype.setChecksTo = function(setTo){
    this.resetChecks();
    if (!(setTo in this.controlMap)) return;
    var idsToDisable = this.controlMap[setTo];
    for (var i = 0; i < idsToDisable.length; i++){
        var control = document.getElementById(idsToDisable[i]);
        if (control) {
            control.disabled = true;
            if (control.checked){
                control.checked = false;
            }
        }
    }
}
/**
* @author: mpolcari
* @since: 144
*/
function DeveloperNameInputElement() {}

DeveloperNameInputElement.setName = function(labelElement, developerNameElement, defaultName) {
  var ov = labelElement.value;
  var v =""
  var hasFirstChar = false;
  var lastCharIsUnderscore = false;
  if (developerNameElement.value.length == 0 && ov.length > 0) {
    for (i = 0; i < ov.length; i++) {
      var ch = ov.charAt(i);
      if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')) {
          //                copy char if alphanumeric
        if (!hasFirstChar && (ch >= '0' && ch <= '9')) {
          //                  first char must be letter
          v += 'X';
        }
        v += ch;
        hasFirstChar = true;
        lastCharIsUnderscore = false;
      } else if (hasFirstChar && !lastCharIsUnderscore) {
          //                convert non-alphanumeric char to underscore, except that first char must not be underscore, and no consecutive underscores
        v += '_';
        lastCharIsUnderscore = true;
      }
    }
    if (!hasFirstChar) {
          //              if there is no alphanumeric chars, use the default
      developerNameElement.value = defaultName ;
    } else if (lastCharIsUnderscore) {
          //              make sure the last char is not an underscore
      developerNameElement.value = v.substring(0, v.length-1);
    } else {
      developerNameElement.value = v;
    }
  }
  return true;
}


SfdcElement = function() {};

SfdcElement.prototype.getElements = function(ids) {
    var elements = [];
    for (var n = 0; n < ids.length; n++) {
         elements.push(document.getElementById(ids[n]))
    }
    return elements;
}

SfdcElement.prototype.setVisibleElementsById = function(ids, show) {
    SfdcElement.prototype.setVisibleElements(SfdcElement.prototype.getElements(ids), show);
}

SfdcElement.prototype.setVisibleElements = function(elements, show) {
    for (var n = 0; n < elements.length; n++) {
        elements[n].style.visibility = (show) ? 'visible' : 'hidden'
    }
}

SfdcElement.prototype.setDisplayElementsById = function(ids, display) {
    SfdcElement.prototype.setDisplayElements(SfdcElement.prototype.getElements(ids), display);
}

SfdcElement.prototype.setDisplayElements = function(elements, display) {
    for (var n = 0; n < elements.length; n++) {
        elements[n].style.display = (display) ? 'block' : 'none'
    }
}

SfdcElement.prototype.setDisabledById = function(ids, disabled){
    SfdcElement.prototype.setDisabled(SfdcElement.prototype.getElements(ids), disabled);
}

SfdcElement.prototype.setDisabled = function(elements, disabled){
    for (var n = 0; n < elements.length; n++) {
        elements[n].disabled = disabled;
    }
}


/**
 * JS for Page Layout Asssignment Page
 */

/**
 * @param pagePLAUrl paginated Page Layout assignment URL
 * @param rtPLAUrl jump to a specific record type
 */
function LayoutMapping(pagePLAUrl, rtPLAUrl, pageLayoutType, pageLayoutDetailUrl, viewProfileId, isEdit, pageNum,
	recordTypeFieldsUrl, sysAdminQS) {
	this.pagePLAUrl = pagePLAUrl;
	this.rtPLAUrl = rtPLAUrl;
	this.pageLayoutType = pageLayoutType;
	this.pageNum = pageNum;
	this.isEdit = isEdit;
	this.pageSize = Cookies.prototype.GetCookie(LayoutMapping.PAGE_SIZE_COOKIE_NAME);
	if (!this.pageSize) {
		this.pageSize = LayoutMapping.DEFAULT_PAGE_SIZE;
	}
	this.profiles = null;
	this.recordTypes = null;
	this.pageLayouts = null;
	this.pageLayoutsMap = new Object();
	this.mapping = new Object();
	this.beginIndex = 0; // inclusive on recordtypes
	this.endIndex = 0; // exclusive on recordtypes
	this.pageLayoutQS = new QueryString(pageLayoutDetailUrl.substring(pageLayoutDetailUrl.indexOf('?') + 1));
	this.pageLayoutDetailUrl = pageLayoutDetailUrl.substring(0, pageLayoutDetailUrl.indexOf('?'));
	this.recordTypeFieldsQS = new QueryString(recordTypeFieldsUrl.substring(recordTypeFieldsUrl.indexOf('?') + 1));
	this.recordTypeFieldsUrl = recordTypeFieldsUrl.substring(0, recordTypeFieldsUrl.indexOf('?'));
	this.viewProfileId = viewProfileId; // profile and all its pla cells to highlight in view mode
	this.sysAdminQS = new QueryString(sysAdminQS.substring(sysAdminQS.indexOf('?') + 1));

	// mouse move cell selection
	this.cellMouseDown = false;
	this.origCell = null;
	this.curTarget = null;
	// click/ctrl+click/shift+click cell selection
	this.curCell = null;
	// select rows, click/ctrl+click/shift+click
	this.curRow = null;
	this.selectedRows = new Map();
	// select cols, click/ctrl+click/shift+click
	this.curCol = null;
	this.selectedCols = new Map();
	// selected cells and changed cells
	this.selected = new Map();
	this.profileRecordTypeSelected = new Map();
	this.recordTypeProfileSelected = new Map();
	this.changed = new Map();
}

LayoutMapping.CELLS_LIMIT = 2000;

LayoutMapping.DEFAULT_PAGE_SIZE = 4;

LayoutMapping.PAGE_SIZE_COOKIE_NAME = 'plaPageSize';

LayoutMapping.CELL_EVENT_HANDLER_HTML = " onmousedown=\"if(layoutMapping){layoutMapping.resetPageLayoutSelector(); layoutMapping.cellHandleMouseDown(event,this);}\" "
	+ "onmousemove=\"if(layoutMapping){layoutMapping.cellHandleMouseMove(event,this);}\" "
	+ "onmouseup=\"if(layoutMapping){layoutMapping.cellHandleMouseUp(event, this);}\" ";

LayoutMapping.PROFILE_HEADER_EVENT_HANDLER_HTML = " onclick=\"if(layoutMapping){layoutMapping.resetPageLayoutSelector(); layoutMapping.handleClickProfileHeader(event, this);}\" ";

LayoutMapping.RT_HEADER_EVENT_HANDLER_HTML = " onclick=\"if(layoutMapping){layoutMapping.resetPageLayoutSelector(); layoutMapping.handleClickRTHeader(event, this);}\" ";

LayoutMapping.EMPTY_KEY = "000000000000000";

LayoutMapping.PROFILE_HEADER_BASE_CLASS_NAME = " profileHeader ";

LayoutMapping.RT_HEADER_BASE_CLASS_NAME = " rtHeader ";

LayoutMapping.MASTER_RT_HEADER_BASE_CLASS_NAME = " pHeader rtHeader ";

LayoutMapping.prototype.getPageLayoutAssignment = function (profileId, recordTypeId) {
	if (!this.mapping) {
		return null;
	}
	var key = profileId + "_" + recordTypeId;
	var mappingVal = this.mapping[key];
	var changedMappingVal = this.changed.map[key];
	if (changedMappingVal) {
		mappingVal = changedMappingVal;
	}
	if (!mappingVal || !this.pageLayoutsMap) {
		return null;
	}
	return this.pageLayoutsMap[mappingVal.plId];
}

LayoutMapping.prototype.fetchMappingPage = function (showLoadingOverlay) {
	var qs = new QueryString("");
	qs.add('type', this.pageLayoutType);
	qs.add('pageNum', this.pageNum);
	qs.add('pageSize', this.pageSize);

	if (showLoadingOverlay) {
		var plaContainerEl = document.getElementById('plaContainer');
		this.drawLoadingOverlay(plaContainerEl);
	}

	var lm = this;
	XBrowser.getHttpResponse(this.getUrl(this.pagePLAUrl, qs), function(req) {lm.processMappingData(req);});
}

LayoutMapping.prototype.getChunk = function (direction) {
	this.resetSelectionStartPoints();
	if (direction < 0) { // previous
		if (this.pageNum == 0)
			return;
		this.pageNum = this.pageNum - 1;
	} else { // next
		this.pageNum =  this.pageNum + 1;
	}
	this.fetchMappingPage(true);
}

LayoutMapping.prototype.processMappingData = function (request) {
	// json
	var response = Util.evalAjaxServletOutput(request.responseText);

	// page number
	this.pageNum = response.pageNum;

	// profile
	this.profiles = response.profiles;

	// record type
	this.recordTypes = response.recordTypes;
	this.beginIndex = this.pageNum * this.pageSize;
	this.endIndex = this.beginIndex + this.pageSize;
	if (this.endIndex > this.recordTypes.length) {
		this.endIndex = this.recordTypes.length;
	}

	// page layout
	this.pageLayouts = response.pageLayouts;
	for (var i = 0; i < this.pageLayouts.length; i++) {
		this.pageLayoutsMap[this.pageLayouts[i].id] = this.pageLayouts[i];
	}

	// mapping
	for (var i = 0; i < response.mapping.length; i++) {
		var key = response.mapping[i].pId + '_' + response.mapping[i].rtId;
		this.mapping[key] = response.mapping[i];
	}

	// hide loading indicator if needed
	var loadingDivEl = document.getElementById('loadingDiv');
	if (loadingDivEl) {
		if (loadingDivEl.style.display != 'none') {
			loadingDivEl.style.display = 'none';
		}
	}

	// rendering
	if (this.isEdit) {
		var pageLayoutSelectorSpan = document.getElementById('pageLayoutSelectorSpan');
		pageLayoutSelectorSpan.innerHTML = this.getPageLayoutSelectorHTML();
	}
	this.selectInitProfileRow();
	this.drawTable();
	this.setHeadersWidth();
	var lm = this;
	window.onresize = function(event) { lm.setHeadersWidth(); };
}

LayoutMapping.prototype.switchToEditMode = function () {
	var qs = new QueryString("");
	qs.add("type", this.pageLayoutType);
	qs.add("e", 1);
	qs.add("pageNum", this.pageNum);
	if (this.viewProfileId) {
		qs.add("pid", this.viewProfileId);
	}

	var url = location.href.substring(0, location.href.indexOf('?'));
	location.href = url + qs.toString();
}

//---------------------------------//
// Rendering                       //
//---------------------------------//
LayoutMapping.prototype.drawTable = function () {
	var w = [];

	w.push("<div id=\"plaHeaderDiv\" class=\"plaHeader\">");
	w.push("<table id=\"plaHeaderTable\" cellpadding=\"0\" cellspacing=\"0\">");
	this.drawTableHeader(w);
	w.push("</table></div>");
	w.push("<div id=\"plaBodyDiv\" class=\"plaBody\">");
	w.push("<table id=\"plaBodyTable\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">");
	w.push("<tbody>");
	for (var i = 0; i < this.profiles.length; i++) {
		w.push("<tr>");
		w.push(this.getProfileHTML(this.profiles[i]));
		for (var j = this.beginIndex; j < this.endIndex; j++) {
			var pageLayout = this.getPageLayoutAssignment(this.profiles[i].id, this.recordTypes[j].id);
			var highLight = (this.profiles[i].id == this.viewProfileId);
			if (!highLight) {
				highLight = (LayoutMappingHelper.getCellId(this.profiles[i].id, this.recordTypes[j].id) in this.selected.map);
			}
			this.drawPageLayoutCell(w, this.profiles[i].id, this.recordTypes[j].id, pageLayout, highLight);
		}
		w.push("</tr>");
	}
	w.push("</tbody>");
	w.push("</table>");
	w.push("</div>");
	var containerElem = document.getElementById("plaContainer");
	containerElem.innerHTML = w.join('');
}

LayoutMapping.prototype.drawLoadingOverlay = function (el) {
	if (!el) {
		return;
	}

	var loadingOpacityEl = this.createLoadingDivEl(el);
	loadingOpacityEl.className = "loadingOverlayDiv loadingOverlayDivOpacity";

	var loadingClearEl = this.createLoadingDivEl(el);
	loadingClearEl.className = "loadingOverlayDiv";

	var description = LC.getLabel("Global", "loading");

	var loadingHolder = document.createElement('span');
	loadingClearEl.appendChild(loadingHolder);
	loadingHolder.className = 'loadingHolder';

	var loadingImage = document.createElement('img');
	loadingHolder.appendChild(loadingImage);
	loadingImage.src = '/img/loading.gif';
	loadingImage.className  = 'loadingImage';

	var loadingDescription = document.createElement('span');
	loadingHolder.appendChild(loadingDescription);
	loadingDescription.innerHTML =  description;
	loadingDescription.className  = 'loadingDescription';
}

LayoutMapping.prototype.createLoadingDivEl = function (el) {
	var loadingDivEl = document.createElement('div');
	el.appendChild(loadingDivEl);
	var height = el.offsetHeight;
	if (XBrowser.userAgent.isIE) {
		height = el.offsetHeight - 15;
	}
	loadingDivEl.style.width = el.offsetWidth + "px";
	loadingDivEl.style.height = height + "px";

	return loadingDivEl;
}

LayoutMapping.prototype.drawPageLayoutCell = function (w, profileId, recordTypeId, pageLayout, highLight) {
	var isChanged = this.changed.map[profileId + "_" + recordTypeId];
	w.push("<td id=\"" + LayoutMappingHelper.getCellId(profileId, recordTypeId) + "\" class=\""
		+ (isChanged ? " changedCell ": "") + (highLight ? " selectedCell " : "") + "\" "
		+ (this.isEdit ? LayoutMapping.CELL_EVENT_HANDLER_HTML : "") + ">");
	if (pageLayout == undefined) {
		w.push(LC.getLabel("LayoutMap", "NotAssigned"));
	} else {
		if (!this.isEdit) {
			this.pageLayoutQS.add("type", this.pageLayoutType);
			this.pageLayoutQS.add("lid", pageLayout.id);
			w.push("<a href=\"" +  (this.pageLayoutDetailUrl + this.pageLayoutQS.toString()) + "\">"
				+ pageLayout.name + "</a>");
		} else {
			w.push(pageLayout.name);
		}
	}
	w.push("</td>");
}

LayoutMapping.prototype.drawTableHeader = function (w) {
	if (this.recordTypes.length > 1) { // has record types other than just master
		w.push("<thead><tr><th id=\"emptyHeader\" class=\"prtHeader\"><div/></th>");
		this.drawRecordTypeHeader(w);
		w.push("</tr>");
		w.push("<tr><th class=\"prtHeader\"><div>" + LC.getLabel("LayoutMap", "Profiles") + "</div></th>");
		for (var i = this.beginIndex; i < this.endIndex; i++) {
			w.push(this.getRecordTypeHTML(this.recordTypes[i]));
		}
		w.push("</tr></thead>");
	} else {
		w.push("<thead><tr><th class=\"\prtHeader\"><div>" + LC.getLabel("LayoutMap", "Profiles")
			+ "</div></th>");
		this.drawRecordTypeHeader(w);
		w.push("</tr></thead>");
	}
}

LayoutMapping.prototype.getRecordTypeHTML = function (recordType) {
	var className = this.getRecordTypeHeaderClassName(recordType.id);
	var rtHTML = [];
	rtHTML.push('<th' + (this.isEdit ? LayoutMapping.RT_HEADER_EVENT_HANDLER_HTML : "")
		+ " id=\"" + recordType.id + "\" class=\"rtHeader " + className + "\"><div title=\"" + recordType.name + "\">");
	if (!this.isEdit && recordType.id != LayoutMapping.EMPTY_KEY) {
		this.recordTypeFieldsQS.add('id', recordType.id);
		this.recordTypeFieldsQS.add('type', this.pageLayoutType);
		rtHTML.push('<a href=\"' + this.recordTypeFieldsUrl + this.recordTypeFieldsQS.toString() + '\">'
			+ recordType.name + '</a>');
	} else {
		rtHTML.push(recordType.name);
	}
	rtHTML.push('</div></th>');
	return rtHTML.join('');
}

LayoutMapping.prototype.getProfileHTML = function (profile) {
	var className = this.getProfileHeaderClassName(profile.id, this.viewProfileId);
	var profileHTML = [];
	profileHTML.push("<td class=\"profileHeader " + className + "\""
			+ (this.isEdit ? LayoutMapping.PROFILE_HEADER_EVENT_HANDLER_HTML : "")
			+ " id=\"" + profile.id + "\">");
	if (!this.isEdit) {
		profileHTML.push("<a href=\"" + this.getUrl('/' + profile.id, this.sysAdminQS) + "\">" + profile.name + "</a>");
	} else {
		profileHTML.push(profile.name);
	}
	profileHTML.push("</td>");
	return profileHTML.join('');
}

LayoutMapping.prototype.drawRecordTypeHeader = function(w) {
	if (this.recordTypes.length > 1) {
		w.push("<th id=\"recordTypesHeader\" colspan=\"" + (this.endIndex - this.beginIndex)
			+ "\"><table width=\"100%\" id=\"recordTypeHeader\"><thead><tr><th class=\"prtHeader alignLeft\">"
			+ LC.getLabel("LayoutMap", "RecordTypes")
			+ "</th><th class=\"navigationHeaderNormal\">");
		this.drawPrevNext(w);
		w.push("</th></tr></thead></table></th>");
	} else {
		var className = this.getRecordTypeHeaderClassName(this.recordTypes[0].id);
		w.push("<th class=\"pHeader rtHeader " + className + "\""
			+ (this.isEdit ? LayoutMapping.RT_HEADER_EVENT_HANDLER_HTML : "")
			+ " id=\"" + this.recordTypes[0].id + "\"><div>" + LC.getLabel("FLSLayout", "Layout") + "</div></th>");
	}
}

LayoutMapping.prototype.drawPrevNext = function(w) {
	if (this.pageNum > 0) {
		w.push("<a href=\"javascript: layoutMapping.getChunk(-1);\" id=\"plaPrevLink\">&lt;Prev</a>");
	} else {
		w.push("");
	}
	w.push(" (" + (this.beginIndex + 1) + "-" + this.endIndex + " of " + this.recordTypes.length + ") ");
	if (this.endIndex >= this.recordTypes.length) {
		w.push("");
	} else {
		w.push("<a href=\"javascript: layoutMapping.getChunk(1);\" id=\"plaNextLink\">Next&gt;</a>");
	}
}

LayoutMapping.prototype.getPageLayoutSelectorHTML = function () {
	var pageLayoutSelectorHTML = "<select id=\"pageLayoutSelector\" name=\"pageLayoutSelector\" onchange=\"layoutMapping.onChangePLA(this);\">";
	pageLayoutSelectorHTML += "<option selected=\"selected\" value=\"000000000000000\">"
		+ LC.getLabel("LayoutMap", "SelectOne") + "</option>";
	for (var i = 0; i < this.pageLayouts.length; i++) {
		pageLayoutSelectorHTML += "<option value=\"" + this.pageLayouts[i].id + "\">" + this.pageLayouts[i].name
			+ "</option>";
	}
	pageLayoutSelectorHTML += "</select>";
	return pageLayoutSelectorHTML;
}

//------------------------------------------//
// Fixed column header, Scrollable body     //
//------------------------------------------//
LayoutMapping.prototype.setHeadersWidth = function () {
	var plaBodyTableEl = document.getElementById('plaBodyTable');
	if (!plaBodyTableEl) {
		return;
	}
	var plaBodyDivEl = document.getElementById('plaBodyDiv');
	if (plaBodyDivEl) {
		if (plaBodyTableEl.clientHeight < 450) {
			plaBodyDivEl.style.height = plaBodyTableEl.clientHeight + "px";
		} else {
			plaBodyDivEl.style.height = "450px";
		}
	}

	var plaBodyTableBodyEl = plaBodyTableEl.getElementsByTagName('tbody')[0];
	var plaBodyTableFirstRowEl = plaBodyTableBodyEl.firstChild;
	var firstRowCells = plaBodyTableFirstRowEl.childNodes;
	var profileHeaderCell = firstRowCells[0];

	var plaHeaderTableEl = document.getElementById('plaHeaderTable');
	plaHeaderTableEl.style.display = "none";

	var plaHeaderTableHeaderEl = plaHeaderTableEl.getElementsByTagName('thead')[0];
	if (this.recordTypes.length > 1) {
		var emptyHeaderEl = document.getElementById('emptyHeader');
		emptyHeaderEl.firstChild.style.width = profileHeaderCell.clientWidth + "px";
		var recordTypeHeaderRowEl = plaHeaderTableHeaderEl.childNodes[1];
		var recordTypeCells = recordTypeHeaderRowEl.childNodes;
		for (var i = 0; i < firstRowCells.length; i++) {
			recordTypeCells[i].firstChild.style.width = firstRowCells[i].clientWidth + "px";
		}
	} else {
		var pageLayoutHeaderRowEl = plaHeaderTableHeaderEl.childNodes[0];
		var pageLayoutHeaderCells = pageLayoutHeaderRowEl.childNodes;
		for (var i = 0; i < firstRowCells.length; i++) {
			pageLayoutHeaderCells[i].firstChild.style.width = firstRowCells[i].clientWidth + "px";
		}
	}

	plaHeaderTableEl.style.display="";
}

//---------------------------------//
// Selection                       //
//---------------------------------//
// cell onmousedown
LayoutMapping.prototype.cellHandleMouseDown = function (ev, el) {
	this.clearTextSelection();
	this.curRow = null;
	this.curCol = null;

	this.cellMouseDown = true;
	this.curTarget = el;
	if (ev.shiftKey && this.curCell) { // shift+click following a click
		this.clearSelection();
		this.selectRange(el, this.curCell);
	} else {
		if (ev.ctrlKey) { // toggle selection, ctrl+click
			this.curCell = null;
			this.origCell = el; // start point for holding mouse down and move
			this.toggleCell(el.id);
		} else { // click or shift+click not following a click
			this.clearSelection();
			this.curCell = el; // for shift+click later
			this.origCell = el; // for cellHandleMouseMove
			this.selectCell(el.id);
		}
	}
	this.updateNumSelected();
}

// cell onmousemove
LayoutMapping.prototype.cellHandleMouseMove = function(ev,el) {
	if(this.cellMouseDown && el != this.curTarget) {
		this.curTarget = el;
		this.clearTextSelection();
		if( !ev.ctrlKey )
			this.clearSelection();
		this.selectRange(el, this.origCell);
		this.updateNumSelected();
	}
}

// cell onmouseup
LayoutMapping.prototype.cellHandleMouseUp = function () {
	this.cellMouseDown = false;
	this.origCell = null;
	this.curTarget = null;
	this.clearTextSelection();
	this.setStylesOfAllHeaders();
}

LayoutMapping.prototype.handleClickProfileHeader = function (ev, el) {
	this.clearTextSelection();
	this.curCell = null;
	this.curCol = null;

	if (ev.shiftKey && this.curRow) {
		this.clearSelection();
		this.selectRows(el.id, this.curRow.id);
	} else {
		if (ev.ctrlKey) {
			this.curRow = null;
			this.toggleRow(el.id);
		} else {
			this.clearSelection();
			this.curRow = el;
			this.selectRow(el.id);
		}
	}
	this.setStylesOfAllHeaders();
	this.updateNumSelected();
}

LayoutMapping.prototype.handleClickRTHeader = function (ev, el) {
	this.clearTextSelection();
	this.curCell = null;
	this.curRow = null;

	if (ev.shiftKey && this.curCol) {
		this.clearSelection();
		this.selectColumns(el.id, this.curCol.id);
	} else {
		if (ev.ctrlKey) {
			this.curCol = null;
			this.toggleColumn(el.id);
		} else {
			this.clearSelection();
			this.curCol = el;
			this.selectColumn(el.id);
		}
	}
	this.setStylesOfAllHeaders();
	this.updateNumSelected();
}

LayoutMapping.prototype.selectRange = function (elFrom, elTo) {
	if (!elFrom || !elTo) {
		return;
	}

	var fromRecordTypeId = LayoutMappingHelper.getCellRecordTypeId(elFrom);
	var fromProfileId = LayoutMappingHelper.getCellProfileId(elFrom);
	var toRecordTypeId = LayoutMappingHelper.getCellRecordTypeId(elTo);
	var toProfileId = LayoutMappingHelper.getCellProfileId(elTo);

	var rtIndex1 = LayoutMappingHelper.getIndex(this.recordTypes, fromRecordTypeId);
	var rtIndex2 = LayoutMappingHelper.getIndex(this.recordTypes, toRecordTypeId);
	var pIndex1 = LayoutMappingHelper.getIndex(this.profiles, fromProfileId);
	var pIndex2 = LayoutMappingHelper.getIndex(this.profiles, toProfileId);

	var beginRTIndex = Math.min(rtIndex1, rtIndex2);
	var endRTIndex = Math.max(rtIndex1, rtIndex2) + 1;
	var beginPIndex = Math.min(pIndex1, pIndex2);
	var endPIndex = Math.max(pIndex1, pIndex2) + 1;

	for (var i = beginRTIndex; i < endRTIndex; i++) {
		for (var j = beginPIndex; j < endPIndex; j++) {
			this.selectCell(LayoutMappingHelper.getCellId(this.profiles[j].id, this.recordTypes[i].id));
		}
	}
}

LayoutMapping.prototype.selectColumn = function (recordTypeId) {
	this.selectedCols.put(recordTypeId, recordTypeId);
	for (var i = 0; i < this.profiles.length; i++) {
		this.selectCell(LayoutMappingHelper.getCellId(this.profiles[i].id, recordTypeId));
	}
}

LayoutMapping.prototype.selectColumns = function (fromRecordTypeId, toRecordTypeId) {
	var rtIndex1 = LayoutMappingHelper.getIndex(this.recordTypes, fromRecordTypeId);
	var rtIndex2 = LayoutMappingHelper.getIndex(this.recordTypes, toRecordTypeId);
	var beginRTIndex = Math.min(rtIndex1, rtIndex2);
	var endRTIndex = Math.max(rtIndex1, rtIndex2) + 1;
	for (var i = beginRTIndex; i < endRTIndex; i++) {
		this.selectColumn(this.recordTypes[i].id);
	}
}

LayoutMapping.prototype.selectInitProfileRow = function () {
	if (!this.viewProfileId) {
		return;
	}

	this.selectedRows.put(this.viewProfileId, this.viewProfileId);
	for (var i = 0; i < this.recordTypes.length; i++) {
		this.populateDataForSelectedCell(this.viewProfileId, this.recordTypes[i].id);
	}

	if (this.isEdit) {
		this.updateNumSelected();
	}
}

LayoutMapping.prototype.selectRow = function (profileId) {
	this.selectedRows.put(profileId, profileId);
	for (var i = 0; i < this.recordTypes.length; i++) {
		this.selectCell(LayoutMappingHelper.getCellId(profileId, this.recordTypes[i].id));
	}
}

LayoutMapping.prototype.selectRows = function (fromProfileId, toProfileId) {
	var pIndex1 = LayoutMappingHelper.getIndex(this.profiles, fromProfileId);
	var pIndex2 = LayoutMappingHelper.getIndex(this.profiles, toProfileId);
	var beginPIndex = Math.min(pIndex1, pIndex2);
	var endPIndex = Math.max(pIndex1, pIndex2) + 1;
	for (var i = beginPIndex; i < endPIndex; i++) {
		this.selectRow(this.profiles[i].id);
	}
}

LayoutMapping.prototype.selectCell = function (id) {
	LayoutMappingHelper.selectCellById(id);
	var recordTypeId = id.substring(id.indexOf("_") + 1);
	var profileId = id.substring(0, id.indexOf("_"));

	this.populateDataForSelectedCell(profileId, recordTypeId);
}

LayoutMapping.prototype.populateDataForSelectedCell = function (profileId, recordTypeId) {
	var id = profileId + "_" + recordTypeId;
	var val = this.mapping[id];
	if (!val) {
		val = new Object();
		val.rtId = recordTypeId;
		val.pId = profileId;
	}
	this.selected.put(id, val);

	var recordTypeSelected = this.profileRecordTypeSelected.map[profileId];
	if (!recordTypeSelected) {
		recordTypeSelected = new Map();
		this.profileRecordTypeSelected.put(profileId, recordTypeSelected);
	}
	recordTypeSelected.put(recordTypeId, val);

	var profileSelected = this.recordTypeProfileSelected.map[recordTypeId];
	if (!profileSelected) {
		profileSelected = new Map();
		this.recordTypeProfileSelected.put(recordTypeId, profileSelected);
	}
	profileSelected.put(profileId, val);
}

LayoutMapping.prototype.deselectColumn = function(recordTypeId) {
	for (var i = 0; i < this.profiles.length; i++) {
		this.deselectCell(LayoutMappingHelper.getCellId(this.profiles[i].id, recordTypeId));
	}
	this.selectedCols.remove(recordTypeId);
}

LayoutMapping.prototype.deselectRow = function (profileId) {
	for (var i = 0; i < this.recordTypes.length; i++) {
		this.deselectCell(LayoutMappingHelper.getCellId(profileId, this.recordTypes[i].id));
	}
	this.selectedRows.remove(profileId);
}

LayoutMapping.prototype.deselectCell = function (id) {
	LayoutMappingHelper.deselectCellById(id);
	this.selected.remove(id);

	var profileId = id.substring(0, id.indexOf("_"));
	var recordTypeId = id.substring(id.indexOf("_") + 1);
	this.selectedRows.remove(profileId);
	this.selectedCols.remove(recordTypeId);

	var recordTypeSelected = this.profileRecordTypeSelected.map[profileId];
	if (recordTypeSelected) {
		recordTypeSelected.remove(recordTypeId);
		if (recordTypeSelected.size == 0) {
			this.profileRecordTypeSelected.remove(profileId);
		}
	}

	var profileSelected = this.recordTypeProfileSelected.map[recordTypeId];
	if (profileSelected) {
		profileSelected.remove(profileId);
		if (profileSelected.size == 0) {
			this.recordTypeProfileSelected.remove(recordTypeId);
		}
	}
}

LayoutMapping.prototype.toggleRow = function (id) {
	if (id in this.selectedRows.map) {
		this.deselectRow(id);
	} else {
		this.selectRow(id);
	}
}

LayoutMapping.prototype.toggleColumn = function (id) {
	if (id in this.selectedCols.map) {
		this.deselectColumn(id);
	} else {
		this.selectColumn(id);
	}
}

LayoutMapping.prototype.toggleCell = function (id) {
	if (id in this.selected.map) {
		this.deselectCell(id);
	} else {
		this.selectCell(id);
	}
}

LayoutMapping.prototype.resetSelectionStartPoints = function () {
	this.curRow = null;
	this.curCol = null;
	this.cellMouseDown = false;
	this.origCell = null;
	this.curTarget = null;
	this.curCell = null;
}

LayoutMapping.prototype.clearSelection = function () {
	for (var key in this.selected.map) {
		this.deselectCell(key);
	}
	this.selectedRows = new Map();
	this.selectedCols = new Map();
	this.selected = new Map();
	this.profileRecordTypeSelected = new Map();
	this.recordTypeProfileSelected = new Map();
}

LayoutMapping.prototype.clearTextSelection = function (){
    if (document.selection && document.selection.empty) {
        document.selection.empty();
    } else {
		if (window.getSelection().removeAllRanges)
	    	window.getSelection().removeAllRanges();
	}
}

LayoutMapping.prototype.updateNumSelected = function () {
	var numSelectedEl = document.getElementById("selectedCellsSpan");
	if (numSelectedEl) {
		numSelectedEl.innerHTML = this.selected.size + " " + LC.getLabel("LayoutMap", "Selected");
	}
}

LayoutMapping.prototype.updateNumChosen = function () {
	var numChangedEl = document.getElementById("changedCellsSpan");
	if (numChangedEl) {
		numChangedEl.innerHTML = this.changed.size + " " + LC.getLabel("LayoutMap", "Changed");
	}
}

//-----------------------//
// Change                //
//-----------------------//
LayoutMapping.prototype.onChangePLA = function (plaSelectorEl) {
	var newPageLayoutId = plaSelectorEl.options[plaSelectorEl.selectedIndex].value;
	if (newPageLayoutId == LayoutMapping.EMPTY_KEY || this.selected.size == 0) {
		return;
	}

	if (this.isChangedOverLimit()) {
		alert(LC.getLabel("LayoutMap", "PLAChangesAboveLimit", LayoutMapping.CELLS_LIMIT));
		return;
	}

	for (var profileRTId in this.selected.map) {
		var origPLA = this.mapping[profileRTId];
		var newPLA = new Object();
		if (!origPLA) {
			newPLA.pId = profileRTId.substring(0, profileRTId.indexOf("_"));
			newPLA.rtId = profileRTId.substring(profileRTId.indexOf("_") + 1);
			newPLA.plId = newPageLayoutId;
		} else {
			var newPLA = new Object();
			newPLA.id = origPLA.id;
			newPLA.pId = origPLA.pId;
			newPLA.rtId = origPLA.rtId;
			newPLA.plId = newPageLayoutId;
		}
		this.changed.put(profileRTId, newPLA);
		LayoutMappingHelper.changeCellById(profileRTId);
		var cellEl = getElementByIdCS(profileRTId);
		if (cellEl) {
			cellEl.innerHTML = this.pageLayoutsMap[newPageLayoutId].name;
		}
	}

	this.updateNumChosen();
	this.setHeadersWidth();
}

LayoutMapping.prototype.resetPageLayoutSelector = function () {
	var pageLayoutSelectorEl = document.getElementById('pageLayoutSelector');
	if (pageLayoutSelectorEl) {
		pageLayoutSelectorEl.selectedIndex = 0;
	}
}

LayoutMapping.prototype.isChangedOverLimit = function () {
	if (this.selected.size == 0 || (this.selected.size + this.changed.size) <= LayoutMapping.CELLS_LIMIT) {
		return false;
	}

	var totalToBeChanged = this.changed.size;
	for (var profileRTId in this.selected.map) {
		if (!(profileRTId in this.changed.map)) {
			totalToBeChanged++;
		}
	}
	if (totalToBeChanged > LayoutMapping.CELLS_LIMIT) {
		return true;
	}
	return false;
}

//-----------------------//
// Header Style          //
//-----------------------//
LayoutMapping.prototype.setStylesOfAllHeaders = function () {
	for (var i = 0; i < this.profiles.length; i++) {
		var recordTypeSelected = this.profileRecordTypeSelected.map[this.profiles[i].id];
		var profileHeaderEl = getElementByIdCS(this.profiles[i].id);
		this.setHeaderStyle(LayoutMapping.PROFILE_HEADER_BASE_CLASS_NAME, profileHeaderEl, recordTypeSelected,
			 this.selectedRows);
	}

	var rtHeaderBaseClassName = (this.recordTypes.length > 1) ? LayoutMapping.RT_HEADER_BASE_CLASS_NAME :
		LayoutMapping.MASTER_RT_HEADER_BASE_CLASS_NAME;

	for (var j = this.beginIndex; j < this.endIndex; j++) {
		var profileSelected = this.recordTypeProfileSelected.map[this.recordTypes[j].id];
		var recordTypeHeaderEl = getElementByIdCS(this.recordTypes[j].id);
		this.setHeaderStyle(rtHeaderBaseClassName, recordTypeHeaderEl, profileSelected, this.selectedCols);
	}
}

LayoutMapping.prototype.setHeaderStyle = function (baseClassName, el, selectedMap, selectedHeaders) {
	if (!el) {
		return;
	}
	if (!selectedMap || selectedMap.size == 0) {
		if (el.className.indexOf('selectedHeader') > 0 || el.className.indexOf('highlightedHeader') > 0) {
			el.className = baseClassName;
		}
		// el.className = baseClassName;
	} else {
		if (el.id in selectedHeaders.map) {
			if (el.className.indexOf('selectedHeader') < 0) {
				el.className =	baseClassName + ' selectedHeader';
				// el.style.backgroundColor = '#8E9DBE';
				// el.style.color = '#FFFFFF';
			}
		} else {
			if (el.className.indexOf('highlightedHeader') < 0) {
				el.className =	baseClassName + ' highlightedHeader';
				// el.style.backgroundColor = '#D0D0FF';
			}
		}
	}
}

LayoutMapping.prototype.getProfileHeaderClassName = function (headerProfileId, profileId) {
	var isSelectedRow = (this.viewProfileId == headerProfileId);
	if (this.isEdit && !isSelectedRow) {
		isSelectedRow = (headerProfileId in this.selectedRows.map);
	}
	if (isSelectedRow) {
		return " selectedHeader ";
	}
	var recordTypeSelected = this.profileRecordTypeSelected.map[headerProfileId];
	if (recordTypeSelected && recordTypeSelected.size > 0) {
		return " highlightedHeader ";
	}
	return " ";
}

LayoutMapping.prototype.getRecordTypeHeaderClassName = function (recordTypeId) {
	if (this.isEdit && (recordTypeId in this.selectedCols.map)) {
		return " selectedHeader ";
	}
	var profileSelected = this.recordTypeProfileSelected.map[recordTypeId];
	if (profileSelected && profileSelected.size > 0) {
		return " highlightedHeader ";
	}
	return " ";
}

//---------------------------------//
// Save                            //
//---------------------------------//
LayoutMapping.prototype.save = function() {
	var changedPLAsEl = document.getElementById('changedPLAs');
	if (!changedPLAsEl) {
		return;
	}
	changedPLAsEl.value = this.toXML();
}

LayoutMapping.prototype.toXML = function () {
	var xml = [];
	xml.push('<changedPLAs>');
	for (var profileRTId in this.changed.map) {
		var val = this.changed.map[profileRTId];
		xml.push('<item ');
		xml.push('id="' + (val.id ? val.id : LayoutMapping.EMPTY_KEY) + '" ');
		xml.push('pId="' + val.pId + '" ');
		xml.push('rtId="' + val.rtId + '" ');
		xml.push('plId="' + val.plId + '" ');
		xml.push('/>');
	}
	xml.push('</changedPLAs>');
	return xml.join('');
}

LayoutMapping.prototype.cancel = function () {
	var qs = new QueryString("");
	qs.add("type", this.pageLayoutType);
	qs.add("pageNum", this.pageNum);
	if (this.viewProfileId) {
		qs.add("pid", this.viewProfileId);
	}
	var url = location.href.substring(0, location.href.indexOf('?'));
	location.href = url + qs.toString();
}

LayoutMapping.prototype.getUrl = function (url, qs) {
	if (!url || !qs) {
		return "";
	}

	// query string is empty
	var qsStr = qs.toString();
	if (qsStr.length == 0) {
		return url;
	}

	// question mark in url
	if (url.indexOf('?') >= 0) {
		if (url.indexOf('?') == (url.length - 1)) {
			// no params after question mark
			return url + qs.toString().substring(1);
		} else {
			// params after question mark
			return url + '&' + qs.toString().substring(1);
		}
	}

	// no question mark in url
	return url + qs.toString();
}



/**
 * multiforce!
 */
function AppPicker(appExchangeUrl, appStoreUrl, pick, btn) {
    this.appExchangeUrl = appExchangeUrl;
    this.appStoreUrl = appStoreUrl;
    this.picker = pick;
    this.originalIndex = pick.selectedIndex;

    var self = this;
    if (btn) {
        addEvent(btn, 'click', function() { self.handleChange(); }, false);
        addEvent(pick, 'change', function() { self.changeAlt(); }, false);
    } else {
        addEvent(pick, 'change', function() { self.handleChange(); }, false);
    }
    this.changeAlt();
}

AppPicker.prototype.changeAlt = function() {
    this.picker.title = this.picker[this.picker.selectedIndex].text;
}

AppPicker.prototype.handleChange = function() {
    if (this.picker.options[this.picker.selectedIndex].value == "AppExchange") {
        var newWindow = window.open(this.appExchangeUrl, "AppExchangePopup");
        this.picker.selectedIndex = this.originalIndex;
        newWindow.focus();
    } else if(this.picker.options[this.picker.selectedIndex].value == "AppStore") {
        var newWindow = window.open(this.appStoreUrl, "AppStorePopup");
        this.picker.selectedIndex = this.originalIndex;
        newWindow.focus();
    } else {
        this.picker.form.submit();
    }
}

/**
  CustomMotifDefinition is instantiated on (you guessed it) CustomMotifDefinitionPage.java

  @author polcari
  @since 144


  @parentMotifId  This id for the parent's motifElement.  It is passed as a QS var to CustomMotifDefinitionPage

*/

function CustomMotifDefinition(parentMotifId) {

  this.parentMotifInputObject = window.opener.document.getElementById(parentMotifId).motifInputElement;
  this.motifObject = document.getElementById(CustomMotifDefinitionPageConst.COLOR_ELEMENT).motifInputElement;
  this.iconObject = document.getElementById(CustomMotifDefinitionPageConst.MOTIF_ICON_PARAM).imageSelectElement;
}


//this has dependancies on a number of global JS vars
//see CustomMotifDefinitionPage.getVariablesForJS()
CustomMotifDefinition.prototype.returnSelections = function() {
  if (this.motifObject.isNull()) {
    alert(noMotifErrorMsg);  //It would be nice to load these from LC
    return false;
  } else if (this.iconObject.isNull()) {
    alert(noIconErrorMsg);
    return false;
  } else {
   //copy Motif key
    this.parentMotifInputObject.motifElement.className = this.motifObject.motifElement.className.replace("motifColorElement","customDefinedMotif");
    this.parentMotifInputObject.setMotifKey(this.motifObject.motifKeyInput.value);
   //copy text
    this.parentMotifInputObject.setDescription(userDefinedDescription);
   //copy image
    this.parentMotifInputObject.setIconSrc(this.iconObject.image.src);
    this.parentMotifInputObject.setIconValue(this.iconObject.inputElement.value);

    window.blur();
    window.close();
    return true;
  }
}


//helper function
CustomMotifDefinition.prototype.copyInputVal = function(fromID, toID) {
  window.opener.document.getElementById(toID).value = document.getElementById(fromID).value;
}
//helper function
CustomMotifDefinition.prototype.copyInput = function(fromInput, toInput) {
  toInput.value = fromInput.value;
}



/*
 * @author pburstein
 * @since 150
 * Lookup input element
 *
 */

function LookupElement() {
	this.closeLookup;
	this.clientX;
	this.clientY;
	this.oldOffsetLeft;
	this.oldOffsetTop;
	this.commonDiv;
	this.lookupHeader;
}


LookupElement.prototype.openLookup = function(baseURL,width,modified,searchParam) {
    if (modified == '1') baseURL = baseURL + searchParam;
    this.openOverlay(baseURL);
}

function setBodySize(element) {
	element.style.height = document.body.offsetHeight + "px";
	element.style.width = document.body.offsetWidth + "px";
}


function setOnResize(onresize) {
	if (XBrowser.userAgent.isIE) {
		document.body.onresize = onresize;
	} else {
		window.onresize = onresize;
	}
}

LookupElement.prototype.openOverlay = function(baseURL) {
	var body = XBrowser.userAgent.isIE ? document.body : document.getElementsByTagName("body")[0];

	var overlay = document.createElement("iframe");
    overlay.src = IFrameElement.BLANK_SRC;
	overlay.className = 'pageMask pageMaskWithOpacity';
	setBodySize(overlay);
	var onresize = function() {
		setBodySize(overlay);
	}
	setOnResize(onresize);
	body.appendChild(overlay);

	this.commonDiv = this.createAndAddDiv(body);
	this.commonDiv.className = 'pageMask pageMaskWithPosition';
	var spanWithIFrame = this.createMessage(this.commonDiv, baseURL);

	var self = this;
	var cancelButton = document.createElement('input');
	cancelButton.title = LC.getLabel("Global","cancel");
	cancelButton.className = 'pageMaskCancel';
	cancelButton.type = "button";
	spanWithIFrame.appendChild(cancelButton);
	this.closeLookup = function() {
		body.removeChild(self.commonDiv);
		body.removeChild(overlay);
		self.closeLookup = null;
		setOnResize(null);
	}
	cancelButton.onclick = this.closeLookup;

	cancelButton = null;
}


LookupElement.prototype.createAndAddDiv = function (dataElement){
	var loadingElement = document.createElement('div');
	dataElement.appendChild(loadingElement);
	return loadingElement;
}

LookupElement.prototype.createMessage = function (element, baseURL) {
	
	this.lookupHeader = document.createElement('div');
	this.lookupHeader.className = "pageMaskHeader";
	this.commonDiv.appendChild(this.lookupHeader);
	
	var self = this;
	var onmousemove = function(event) {
		event = getEvent(event);
		
		var dX = self.clientX - event.clientX;
		var dY = self.clientY - event.clientY;
		self.commonDiv.style.left = (self.oldOffsetLeft - dX) + "px";
		self.commonDiv.style.top = (self.oldOffsetTop - dY) + "px";		 
	}
	
	var onmouseup = function(event) {
		event = getEvent(event);
		
		var body = XBrowser.userAgent.isIE ? document.body : window;
		removeEvent(body, "mouseup", onmouseup);
		removeEvent(body, "mousemove", onmousemove);
	}

	this.lookupHeader.onmousedown = function(event) {
		event = getEvent(event);
		
		self.clientX = event.clientX;
		self.clientY = event.clientY;
		self.oldOffsetLeft = self.commonDiv.offsetLeft;
		self.oldOffsetTop = self.commonDiv.offsetTop;

		var body = XBrowser.userAgent.isIE ? document.body : window;
		addEvent(body, "mouseup", onmouseup);
		addEvent(body, "mousemove", onmousemove);
	}
	

	var newWaitingHolder = document.createElement('div');
	this.commonDiv.appendChild(newWaitingHolder);
	
	var lookupIFrame = document.createElement('iframe');
	lookupIFrame.src = baseURL;
	lookupIFrame.className = "pageMaskIFrame";
	lookupIFrame.scrolling = "no";
	lookupIFrame.name = LookupInputElement.LOOKUP_IFRAME;
	
	this.commonDiv.appendChild(lookupIFrame);
	
	return this.commonDiv;
}



LookupElement.prototype.lookupPick = function (formName, parentIdElementName, parentEditElementName, relatedFieldName, id, display, relatedFieldValue, extraNameElementName) {
	this.closeLookup();
	lookupPick(formName, parentIdElementName, parentEditElementName, relatedFieldName, id, display, relatedFieldValue, extraNameElementName);
}

LookupElement.prototype.lookupPick2 = function(formName, parentIdElementName, parentEditElementName, id, display, extraNameElementName, extraName, extraIdElementName, extraId, allowOverwrite) {
	this.closeLookup();
	lookupPick2(formName, parentIdElementName, parentEditElementName, id, display, extraNameElementName, extraName, extraIdElementName, extraId, allowOverwrite)
}

LookupElement.prototype.lookupPhonePick = function(parentId, newValue) {
	this.closeLookup();
	lookupPhonePick(parentId, newValue)
}




/**
 * Informational dialogs are for alerts or warnings or simple confirmations.
 * 
 * @author jmooney
 * @since 150
 */
function InformationalDialog(id, isModal, title) {
    this.id = id;
    this.isModal = isModal;
    this.type = OverlayDialogType.INFORMATIONAL;
    this.extraClass = "informationalDialog";
    this.width = OverlayDialog.MAX_WIDTH;
    this.title = title;
    this.iconSrc = "s.gif";
    this.iconClass = "";
    this.setupDefaultButtons();
    var self = this;
    addEvent(window, "resize", function() { self.resizeEvent(); }, false);
    addEvent(document, "keydown", function(e) { self.handleKeyPress(e); }, false);
    if (XBrowser.userAgent.isIE6) {
        addEvent(window, "scroll", function() { self.scrollEvent(); }, false);
    }
}

InformationalDialog.prototype = new OverlayDialog();

// TODO: fix the icon.
InformationalDialog.prototype.createContent = function() {
    var content = document.getElementById(this.id + "Content");
    var html = [];
    html.push("<h2>");
    html.push(this.header);
    html.push("</h2>");
    if (this.info) {
        html.push("<p>");
        html.push(this.info);
        html.push("</p>");
    }
    this.createButtons(html);
    content.innerHTML = html.join("");
}

/**
 * set the src of the icon image
 */
InformationalDialog.prototype.setIconSrc = function() {
}

/**
 * sets the class of the icon image
 */
InformationalDialog.prototype.setIconClass = function() {
}


/*
 * @author ldelascurain
 * @since 150
 *
 */

function FieldTreeNode( key, isLeaf, children, labelName, showLabel) {
    this.key = key;
    this.isLeaf = isLeaf;
    this.children = children;
    this.labelName = labelName;
    this.showLabel = showLabel;
    if (this.children){
        for (var i=0;i<children.length;i++){
            children[i].parent = this;
        }
    }
}

FieldTreeNode.prototype.getLabelToInsert = function(){
    if (!this.parent || !this.parent.showLabel)
        return this.key;
    return this.parent.getLabelToInsert() + "." + this.key;
}
/**
 * @author zzhou
 * since 148
 * CrtLayout javascript code; also see AvailableSection.js, AvailableField.js, Lookups.js
 */

     CrtLayoutElement.layoutSections = {};
     CrtLayoutElement.layoutSecPos = [];
     MoveableItem.selectedBucket = [];
     LayoutSection.currentSelectedObj = null;
     MoveableItem.currentSelectedObj = null;
     CrtLayoutElement.mouseDown = false;
     CrtLayoutElement.dragMove = false;
     CrtLayoutElement.CSS_CLASS_SELECTED_SECTION = 'sectionSelected';
     CrtLayoutElement.CSS_CLASS_LAYOUT_SECTION = 'layoutSecHeaderLeft';
     CrtLayoutElement.CSS_CLASS_SELECTED_ITEM = 'itemSelected';
     CrtLayoutElement.CSS_CLASS_LAYOUT_ITEM_SEPARATOR_HIGHLIGHT = 'sepCellHighlight';
     CrtLayoutElement.CSS_CLASS_LAYOUT_SECTION_SEPARATOR_HIGHLIGHT = 'sepSectionHighlight';
     CrtLayoutElement.CSS_CLASS_LAYOUT_SECTION_SEPARATOR = 'sepSection';
     LayoutItemSeparator.highlightSep = null;
     LayoutSection.NEW_SECTION_ID_PREFIX = "newSectionId";
     LayoutSection.NEW_SECTION_SEP_ID_PREFIX = "newSectionSepId";
     LayoutSection.sectionIdInterator = 0;
     CrtLayoutElement.FIELD_SEP = '$';
     CrtLayoutElement.COL_SEP = '|';
     CrtLayoutElement.DIV_SEP = '_';
     CrtLayoutElement.sectionNameIdMap = {};
     CrtLayoutElement.initialStateColIdMap = null;
     CrtLayoutElement.lookupItemPosMap = {};
     CrtLayoutElement.currentDisplayedSec = null;
     CrtLayoutElement.NUM_ROWS_PER_AVAILABLE_SECTION = 8;
     CrtLayoutElement.disableButtons = false;
     CrtLayoutElement.HOVER_TIME_OUT = 1000;
     CrtLayoutElement.availableSectionPosInited = false;
     CrtLayoutElement.availableSectionInitPosY = 0;
     CrtLayoutElement.sectionsToReformat = {};

     LayoutItem.prototype.createNewCellId = function() {
        return this.sectionId + 'r'+this.rowPos+'c'+(this.colPos+1);
     }

     LayoutItemSeparator.prototype.createNewCellId = function() {
        return 'rp_'+this.sectionId + 'r'+this.rowPos+'c'+(this.colPos+1);
     }

     CrtLayoutElement.init = function() {
         document.onmousemove = function(evt) {CrtLayoutElement.handleMouseMove(evt)};
         document.onmouseup = function(evt) {CrtLayoutElement.handleMouseUp(evt)};
         window.onscroll = function(evt) {CrtLayoutElement.scrollAvailableSection(evt)};
         sfdcPage.appendToOnloadQueue(CrtLayoutElement.onLoad,"onLoad Scripts for the CrtLayout Page");
     }

     CrtLayoutElement.onLoad = function() {
        setTimeout('CrtLayoutElement.initLoad()',10);
     }

     CrtLayoutElement.handleMouseOver = function() {

     }

    CrtLayoutElement.openPropertiesEdit = function(evt) {
        if (!MoveableItem.currentSelectedObj) {
            alert(LC.getLabel("CrtLayout","mustSelectField"));
            return;
        }
        if (MoveableItem.currentSelectedObj.isSection) {
            CrtLayoutElement.openSectionPopup(evt,MoveableItem.currentSelectedObj.fieldObj.sectionId);
            return;
        }

        evt = getEvent(evt);
        setLastMousePosition(evt);


        var url = MoveableItem.currentSelectedObj.inLayout ? CrtLayoutElement.CRT_FIELD_EDIT_URL : "";
        var h = (MoveableItem.selectedBucket.length > 2) ? 400 : 200;
        openPopup(url, 'sectionEdit', 450, h, 'width=450,height='+h+',scrollbars=yes,toolbar=no,status=no,directories=no,menubar=no,resizable=yes', true);
        return false;

    }

     CrtLayoutElement.swapAvailableType = function(sel,fromPageNum,toPageNum) {
        if (CrtLayoutElement.currentDisplayedSec) {
            CrtLayoutElement.currentDisplayedSec.swapFromPage(fromPageNum);
        }
        var secId = sel.options[sel.selectedIndex].value;
        CrtLayoutElement.availableSections[secId].swapToPage(toPageNum);
     }

    CrtLayoutElement.getPrimObjMetaMap = function() {
        return CrtLookups.primaryObjects;
    }

    CrtLayoutElement.getLookupIdFromPrimObjId = function(primaryObjId) {
        return primaryObjId+'_'+LookupsUi.LOOKUPS;
    }

    CrtLayoutElement.setLookupItemPostion = function(fieldValue, fieldObj) {
        if (CrtLayoutElement.lookupItemPosMap == null) {
            CrtLayoutElement.lookupItemPosMap = {};
        }
        CrtLayoutElement.lookupItemPosMap[fieldValue] = fieldObj;
    }

    CrtLayoutElement.clearTextSelection = function(){
        if (isIE) {
            document.selection.empty();
        } else {
          if (window.getSelection().removeAllRanges) {
              window.getSelection().removeAllRanges();
          }
        }
    }

    CrtLayoutElement.deleteSection = function(sectionId) {
        var section = CrtLayoutElement.layoutSections[sectionId];
        if (!section.isEmpty && !window.confirm(LC.getLabel("LayoutDND", "confirmDeleteSectionPrompt") + '\n\n' + LC.getLabel("Global", "are_you_sure"))) {
            return;
        }

        section.remove();

        CrtLayoutElement.layoutSections[sectionId].numFields = 0;
        CrtLayoutElement.checkDisableSave();
        //delete
        delete CrtLayoutElement.layoutSections[sectionId];
        //this name no longer exists for lookup fields
        delete CrtLayoutElement.sectionNameIdMap[section.labelName];

    }

    CrtLayoutElement.createNewSection = function(sectionName, evt) {
        if (sectionName) {
            var sec = initSectionTable(null,null, null, 'H', sectionName);
            sec.reformatSection();
            sec.attachItemEvents();
            return sec;
        }
    }

    CrtLayoutElement.openSectionPopup = function(evt,sectionId) {
        var url = CrtLayoutElement.CRT_SECTION_EDIT_URL;
        if (sectionId) {
          url += '?sectionId=' + sectionId;
        }
        evt = getEvent(evt);
        setLastMousePosition(evt);
        openPopup(url, 'sectionEdit', 450, 245, 'width=450,height=245,scrollbars=yes,toolbar=no,status=no,directories=no,menubar=no,resizable=yes', true);
    }

    CrtLayoutElement.handleMouseUp = function(evt) {
        if (CrtLayoutElement.dragMove) {
            document.getElementById('dragDummy').style.visibility = 'hidden';
             //Shouldn't be handling mouseup here but on the element itself but that seems broken (broken on FIREFOX) - for now...so handle it here
            CrtLayoutElement.handleSelectedItems();
            MoveableItem.clearSelectedItems();
            CrtLayoutElement.clearHighlights();
            CrtLayoutElement.dragMove = false;
        }
        CrtLayoutElement.mouseDown = false;
     }

     CrtLayoutElement.clearHighlights = function() {
        if (CrtLayoutElement.highlightAvailSection) {
            CrtLayoutElement.highlightAvailSection.handleMouseUp(null);

        }
        if (CrtLayoutElement.highlightSec) {
            CrtLayoutElement.highlightSec.setHighlighted(false);
        }
        if (LayoutItemSeparator.highlightSep) {
            LayoutItemSeparator.highlightSep.setHighlighted(false);
        }
     }

     CrtLayoutElement.handleMouseMove = function(evt) {
        var evt = getEvent(evt);
        if (MoveableItem.currentSelectedObj && CrtLayoutElement.mouseDown) {
            CrtLayoutElement.dragMove = true;
            CrtLayoutElement.clearTextSelection();
            var scrollX = getScrollX();
            var scrollY = getScrollY();
            var dragDummy = document.getElementById('dragDummy');
            var dragDummyValue = document.getElementById('dragDummyValue');
            dragDummy.style.visibility = 'visible';
            if (MoveableItem.currentSelectedObj.isSection) {
                dragDummyValue.innerHTML =  MoveableItem.selectedBucket.length > 1 ? LC.getLabel("CrtLayout","dragMultiSelect") : MoveableItem.currentSelectedObj.fieldName;
            } else {
                dragDummyValue.innerHTML = MoveableItem.selectedBucket.length > 1 ? LC.getLabel("CrtLayout","dragMultiSelect") : MoveableItem.currentSelectedObj.fieldName;
            }

            var parentX = 0;
            var parentY = 0;
            if (dragDummy.offsetParent) {
               var dummyParent = dragDummy.offsetParent;
               parentX = getObjX(dummyParent);
               parentY = getObjY(dummyParent);
            }
            dragDummy.style.left = (getMouseX(evt) - parentX) + "px";
            dragDummy.style.top = (getMouseY(evt) - parentY) + "px";
            var currentX = getObjX(dragDummy) - scrollX;
            var theWidth = 500;
            if (document.documentElement && document.documentElement.clientWidth)
                theWidth = document.documentElement.clientWidth;
            else if (document.body) {
                theWidth = document.body.clientWidth;
                if (currentX > theWidth) {
                    if (isIE) document.body.scrollLeft = document.body.scrollLeft + 10;
                    //else window.scroll(10, 0);
                } else if (currentX < 0) {
                    if (isIE) document.body.scrollLeft = document.body.scrollLeft - 10;
                    //else window.scroll(-10, 0);
                }
            }
            else {

            }
            var currentY = getObjY(dragDummy) - scrollY;
            var theHeight = 500;
            if (document.documentElement && document.documentElement.clientHeight)
                theHeight = document.documentElement.clientHeight;
            else if (document.body) {}
                theHeight = document.body.clientHeight;
                if (currentY > theHeight - 50) {
                    //if (isIE) document.documentElement.scrollTop = document.body.scrollTop + 50;
                    window.scrollBy(0, 50);
                } else if (currentY < 50) {
                    // if (isIE) document.documentElement.scrollTop = document.body.scrollTop - 50;
                    window.scrollBy(0, -50);
                }
            }
            else {
            }
     }

     function initSectionTable(sectionId,sectionHeaderId, sectionTableId, sortOrder, masterLabel) {
         var section = new LayoutSection(sectionId,sectionHeaderId, sectionTableId, sortOrder,masterLabel);
         CrtLayoutElement.layoutSections[section.sectionId] = section;
         //position map to keep track of the positions of the layout sections
         CrtLayoutElement.layoutSecPos.push(section.sectionId);

        //some section name handling
        //for now handle section names so that we place the lookups in the appropriate buckets
        CrtLayoutElement.sectionNameIdMap[masterLabel] = section;
        return section;
     }

     //init the innerHTML on constructing a new table;
     LayoutSection.prototype.initSectionInnerHtml = function() {
        var secDiv = document.createElement("DIV");
        var innerHTML = '';
        secDiv.id = this.sectionId;
        secDiv.className = "layoutSection";
        innerHTML += '<div class="layoutSecHeader"><table  border="0" cellpadding="0" cellspacing="0"><tr><td class="layoutSecHeaderLeft" ';
        innerHTML += 'id="' + this.sectionHeaderId  +'">' + this.labelName + '</td><td class="layoutSecHeaderLink">';
        innerHTML += '<span class="sectionHeadLink" onclick="CrtLayoutElement.openSectionPopup(event,\'' + this.sectionId + '\');">'+LC.getLabel("CrtLayout","sectionEdit")+'</span>';
        innerHTML += '&nbsp;|&nbsp;<span class="sectionHeadLink" onclick="CrtLayoutElement.deleteSection(\'' + this.sectionId + '\');">'+LC.getLabel("CrtLayout","sectionDelete")+'</span></td></table></div>';
        innerHTML += '<div><table id="' + this.sectionTableId + '"class="layoutSectionTable">';
        innerHTML += '</table></div>';
        secDiv.innerHTML = innerHTML;
        return secDiv;
     }

     //init the innerHTML for a new table separator
     LayoutSection.prototype.initSectionSepInnerHtml = function() {
        var sepDiv = document.createElement("DIV");
        sepDiv.id = this.getSectionSepId();
        sepDiv.className = "sepSection";
        return sepDiv;
     }

     LayoutSection.prototype.createSectionId = function() {
        return LayoutSection.NEW_SECTION_ID_PREFIX + '_' + LayoutSection.sectionIdInterator++;
     }

     LayoutSection.prototype.getSectionTableId = function() {
        return this.sectionId + "_layoutItemSection";
     }

     LayoutSection.prototype.getSectionHeaderId = function() {
         return "sec_" + this.sectionId;
     }

     LayoutSection.prototype.getLastSectionSepId = function() {
        return CrtLayoutElement.SECTION_SEP_DIV_PREFIX +CrtLayoutElement.LAST_SEC_SEP_DIV;
     }


     function LayoutSection(sectionId,sectionHeaderId, sectionTableId, sortOrder, masterLabel) {
         this.labelName = masterLabel;
         if (sectionId == null) {
             this.sectionId = this.createSectionId();
             this.sectionHeaderId = this.getSectionHeaderId();
             this.sectionTableId = this.getSectionTableId();
             var parentNode = document.getElementById("mainTableDiv");
             var lastSecSep = document.getElementById(this.getLastSectionSepId());
             var sepDiv = this.initSectionSepInnerHtml();
             parentNode.insertBefore(sepDiv,lastSecSep);
             parentNode.insertBefore(this.initSectionInnerHtml(),lastSecSep);
             new LayoutSectionSeparator(sepDiv.id, this.sectionId);
             this.sectionValue = "";
             this.attachEvents();
         } else {
             this.sectionId = sectionId;
             this.sectionHeaderId = sectionHeaderId;
             this.sectionTableId = sectionTableId;
             this.sectionValue = sectionId;
         }

         this.sortOrder = sortOrder;
         this.isSelected = false;
         this.numCols = CrtLayoutElement.NUM_LAYOUT_COLS;
         this.numRows = 0;
         this.layoutItems = [];
         //only here to keep the separator objects alive
         this.layoutSeparators = [];
         this.fieldsMap = {};
         this.isEmpty = true;
     }

    //move cell -- or more precisely move contents of fromField to the contents of toField
    LayoutSection.prototype.moveCell = function(toField,fromField) {
            toField.sectionId = this.sectionId;
            toField.customName = fromField.customName;
            toField.displayName = fromField.displayName;
            toField.fieldName = fromField.fieldName;
            toField.fieldId = fromField.fieldId;
            toField.isEmpty = fromField.isEmpty;
            toField.type = fromField.type;
            toField.isLookup = fromField.isLookup;
            toField.defaultChecked = fromField.defaultChecked;
            fromField.isEmpty = true;
    }

    LayoutSection.prototype.insertCell = function(toField,moveableItem) {
        toField.fieldName = moveableItem.fieldName;
        toField.displayName = moveableItem.displayName;
        toField.customName = moveableItem.customName;
        toField.type = moveableItem.type;
        toField.fieldId = moveableItem.itemId;
        toField.isLookup = moveableItem.isLookup;
        toField.defaultChecked = moveableItem.defaultChecked;
        toField.isEmpty = false;
    }

    LayoutItem.prototype.setCellToEmpty = function() {
        this.itemElem.innerHTML = '';
    }


     //insert a table row in the html for this layout section
     //update internal object references
     LayoutSection.prototype.insertSectionRow = function() {
        var tableElem = document.getElementById(this.sectionTableId);
        var row = tableElem.insertRow(-1);
        var numRows = this.numRows;
        for (var i=0; i<this.numCols; i++) {
            setSeparatorAttributes(this.sectionId,null,numRows,i,row);
            setEmptyFieldAttributes(this.sectionId,null,numRows,i,row);
        }
     }

    //delete an extra empty row in a section
    LayoutSection.prototype.deleteSectionRow = function() {
        var tableElem = document.getElementById(this.sectionTableId);
        tableElem.deleteRow(-1);
        this.layoutItems.splice(this.numRows-1,1);
        this.layoutSeparators.splice(this.numRows-1,1);
        this.numRows--;
    }

    //reformat section after putting in cells from selected bucket
    //alignment is from left to right; cells automatically align as far left on their row as possible
    //@ param isLoad specifies whether we are calling this on loading the page or not -- if we are loading, we don't do most of the formatting for speed
    LayoutSection.prototype.reformatSection = function(isLoad) {
        var columnSize = this.numCols;
        var pos =0;
        var lastEmpty= -1; //assume non cells Empty at start
        var row, col;
        for (var lastEmpty = -1, pos = 0; Math.floor(pos / columnSize) < this.numRows; pos++) {
            row = Math.floor(pos / columnSize);
            col = (pos % columnSize);
            //if we see a nonEmptyCell, copy it over
            if (!this.layoutItems[row][col].isEmpty) {
                 if (lastEmpty == -1) {
                     continue;
                 }
                 this.moveCell(this.layoutItems[Math.floor(lastEmpty/columnSize)][lastEmpty%columnSize],
                             this.layoutItems[row][col]);

                 //move lastEmptyPosition
                 lastEmpty++;
            }
            else {
                if (lastEmpty == -1) {
                    lastEmpty = pos;
                }
            }
        }

        var lastNonEmpty = lastEmpty-1;
        this.isEmpty = true;

        if (lastEmpty >= 0 && (pos-columnSize) > lastNonEmpty) {
            var emptyRows = Math.floor((pos-1-lastNonEmpty)/columnSize);
            //if we have more than one empty row, delete the extras
            while (emptyRows > 1) {
               this.deleteSectionRow();
               emptyRows--;
            }
        }
        else {
            //else we have no empty rows, so add one here
            this.insertSectionRow();
        }
        this.numFields = 0;

        //set cell style
        for (var j = 0; j < this.numRows; j++) {
            for (var k = 0; k < this.numCols; k++) {
                var field = this.layoutItems[j][k];
                this.fieldsMap[field.itemId] = field;
                var sep = this.layoutSeparators[j][k];
                if (!isLoad) {
                    sep.setHighlighted(false);
                }
                if (!isLoad || field.isEmpty) {
                    field.formatField();
                }
                if (!field.isEmpty) {
                    this.isEmpty = false;
                    this.numFields++;
                    //set lookup positioning
                    if (field.isLookup) {
                        CrtLayoutElement.setLookupItemPostion(field.fieldId,field);
                    }
                }

            }
        }

        //attach the the event handlers that would handle the events on the LayoutItems
        if (isLoad) {
            this.attachItemEvents();
        }
    }

    //theorically it will make more sense to attach event handlers to the individual field element themselves
    //however setting all the event handlers on each of the fields would take too long on a page with many fields
    //thus we attach events to the parent element and then delegate to the appropriate event handler during events
    LayoutSection.prototype.attachItemEvents = function() {
         var self = this;
         var tableElem = document.getElementById(self.sectionTableId);
         addEvent(tableElem, 'click', function(evt) {self.handleItemMouseClick(evt);}, false);
         addEvent(tableElem, 'mousedown', function(evt) {self.handleItemMouseDown(evt);}, false);
         addEvent(tableElem, 'mouseover', function(evt) {self.handleItemMouseOver(evt);}, false);
         addEvent(tableElem, 'dblclick', function(evt) {self.handleItemMouseDblClick(evt);}, false);
         addEvent(tableElem, 'mouseout', function(evt) {self.handleItemMouseOut(evt);}, false);
    }

    LayoutSection.prototype.handleItemMouseClick = function(evt) {
        var elem = getEventTarget(evt);
        var field = this.fieldsMap[elem.id];
        if (field) {
            if (!field.elem) {
                field.itemElem = elem;
            }
            field.handleMouseClick(evt);
        }
    }

    LayoutSection.prototype.handleItemMouseDown = function(evt) {
        var elem = getEventTarget(evt);
        var field = this.fieldsMap[elem.id];
        if (field) {
            if (!field.elem) {
                field.itemElem = elem;
            }
            field.handleMouseDown(evt);
        }
    }

    LayoutSection.prototype.handleItemMouseOver = function(evt) {
        var elem = getEventTarget(evt);
        var field = this.fieldsMap[elem.id];
        if (field) {
            if (!field.elem) {
                field.itemElem = elem;
            }
            field.handleMouseOver(evt);
        }
    }

    LayoutSection.prototype.handleItemMouseDblClick = function(evt) {
        var elem = getEventTarget(evt);
        var field = this.fieldsMap[elem.id];
        if (field) {
            if (!field.elem) {
                field.itemElem = elem;
            }
            field.handleMouseDblClick(evt);
        }
    }

    LayoutSection.prototype.handleItemMouseOut = function(evt) {
        var elem = getEventTarget(evt);
        var field = this.fieldsMap[elem.id];
        if (field) {
            if (!field.elem) {
                field.itemElem = elem;
            }
            field.handleMouseOut(evt);
        }
    }

    CrtLayoutElement.disableLayoutButtons = function(disableButton) {
        if (disableButton) {
            //if there are empty sections, disable both save and preview layout buttons
            for (var i=0; i<2; i++) {
                if (document.getElementsByName("saveAndCloseButton")[i]) {
                    document.getElementsByName("saveAndCloseButton")[i].className = 'btnDisabled';
                    document.getElementsByName("saveAndCloseButton")[i].disabled = true;
                    document.getElementsByName("previewButton")[i].className = 'btnDisabled';
                    document.getElementsByName("previewButton")[i].disabled = true;
                    CrtLayoutElement.disableButtons = true;
                }
            }
        }
        else {
        //else, enable buttons
           for (var i=0; i<2; i++) {
              if (document.getElementsByName("saveAndCloseButton")[i]) {
                  document.getElementsByName("saveAndCloseButton")[i].className = 'btn';
                  document.getElementsByName("saveAndCloseButton")[i].disabled = false;
                  document.getElementsByName("previewButton")[i].className = 'btn';
                  document.getElementsByName("previewButton")[i].disabled = false;
                  CrtLayoutElement.disableButtons = false;
              }
          }
        }
    }

    LayoutItem.prototype.handleMoved = function() {
        this.isEmpty = true;
        //reformat any sections as needed
        CrtLayoutElement.sectionsToReformat[this.sectionId] = CrtLayoutElement.getLayoutSection(this.sectionId);
    }

    /*move the cells around the layout
     *param rowPos, colPos specifies position to move these cells to
     */
     LayoutSection.prototype.moveCells = function(rowPos, colPos) {
         //first set the old positions to empty for all the cells being moved
         for (var i=0; i < MoveableItem.selectedBucket.length; i++) {
              MoveableItem.selectedBucket[i].fieldObj.handleMoved();
         }

        var shiftSize = MoveableItem.selectedBucket.length;
        var columnSize = this.numCols;
        //leftmost cell that needs to be moved
        var start = rowPos * columnSize + colPos;

        //rightmost element
        var lastElement = this.numRows * columnSize - 1;
        var lastNonEmptyPos = lastElement;
        if (!this.isEmpty) {
            while (lastNonEmptyPos >= 0 && this.layoutItems[Math.floor(lastNonEmptyPos/columnSize)][lastNonEmptyPos%columnSize].isEmpty) {
                lastNonEmptyPos--;
            }
        } else {
            lastNonEmptyPos = -1;
        }

        //farthest element that can be shifted but still be kept on screen
        var end = lastElement - shiftSize;

        //if we are moving to the very end, then we should just move it right after the lastNonEmptyPos if possible
        start = Math.min(start,lastNonEmptyPos+1);

       //if there are elements that we can't keep on the screen, we might have to increase the row size
       if (lastNonEmptyPos > end) {
            var moreRows = Math.ceil((lastNonEmptyPos + shiftSize - lastElement)/columnSize);
            while (moreRows > 0) {
                this.insertSectionRow();
                moreRows--;
            }
       }

       //shift all elements to the right one by one, starting from the rightmost until we reach the leftmost cell to be moved
       while (lastNonEmptyPos >= start) {
               this.moveCell(this.layoutItems[Math.floor((lastNonEmptyPos+shiftSize)/columnSize)][(lastNonEmptyPos+shiftSize)%columnSize],
                          this.layoutItems[Math.floor(lastNonEmptyPos/columnSize)][lastNonEmptyPos%columnSize]);
              lastNonEmptyPos--;
       }

       //now move all selected cells into vacated spot
       //starting from leftmost moving right
       for (var i =0 ; i < MoveableItem.selectedBucket.length; i++) {
              var selItem = MoveableItem.selectedBucket[i];
              this.insertCell(this.layoutItems[Math.floor((start+i)/columnSize)][(start+i)%columnSize],
                        selItem);
       }
        CrtLayoutElement.sectionsToReformat[this.sectionId] = this;
        CrtLayoutElement.reformatSections();
    }

    CrtLayoutElement.reformatSections = function() {
        for (var sId in CrtLayoutElement.sectionsToReformat) {
            CrtLayoutElement.sectionsToReformat[sId].reformatSection();
        }
        CrtLayoutElement.sectionsToReformat = {};

        CrtLayoutElement.checkDisableSave();

    }

    CrtLayoutElement.checkDisableSave = function() {

        var totalFields = 0;
        for (var sId in CrtLayoutElement.layoutSections) {
            totalFields += CrtLayoutElement.layoutSections[sId].numFields;
        }

        //check for fields limits
        var counter = document.getElementById('fieldsCounterInner');
        var counter2 = document.getElementById('fieldsCounterInner2');
        counter.innerHTML = totalFields;
        counter2.innerHTML = totalFields;
        if (totalFields > CrtLayoutElement.LAYOUT_FIELDS_LIMIT) {
            counter.style.color = '#CC0000';
            counter2.style.color = '#CC0000';
            if (document.getElementById('fieldsLimitError').style.display == 'none') {
                alert(LC.getLabel("CrtLayout","fieldsLimitErrorMsg",CrtLayoutElement.LAYOUT_FIELDS_LIMIT));

                //there's something weird with the order these statements are being run -- hide the dragDummy again because this alert clears the execution of the prior hiding of this dragDummy
                document.getElementById('dragDummy').style.visibility = 'hidden';
            }
            document.getElementById('fieldsLimitError').style.display = 'block';
            document.getElementById('fieldsLimitError2').style.display = 'block';
            CrtLayoutElement.disableLayoutButtons(true);
        } else {
            counter.style.color = 'green';
            counter2.style.color = 'green';
            document.getElementById('fieldsLimitError').style.display = 'none';
            document.getElementById('fieldsLimitError2').style.display = 'none';

            //if all fields are empty then we also disable the layout buttons
            if (totalFields > 0) {
                CrtLayoutElement.disableLayoutButtons(false);
            } else {
                CrtLayoutElement.disableLayoutButtons(true);
            }
        }
    }

    CrtLayoutElement.handleSelectedItems = function() {
        if (CrtLayoutElement.highlightSec) {
            CrtLayoutElement.moveSections();
        }
        else if (CrtLayoutElement.highlightAvailSection) {
            CrtLayoutElement.highlightAvailSection.moveCells();
        }
        else if (LayoutItemSeparator.highlightSep) {
            var itemSep = LayoutItemSeparator.highlightSep;
            CrtLayoutElement.getLayoutSection(itemSep.sectionId).moveCells(itemSep.rowPos, itemSep.colPos);
        } else {
        }
   }

   //move layout sections around in drag and drop
   CrtLayoutElement.moveSections = function() {
        //find the highlighted sec separator and insert all selected sections after this separator
        //find the of the section position we are inserting before
        var moveSecPos;
        var highlightSec = CrtLayoutElement.highlightSec;

        var moveToSep = document.getElementById(highlightSec.divId);
        var parentNode = document.getElementById(highlightSec.divId).parentNode;
        for (var i=0; i < MoveableItem.selectedBucket.length; i++) {
            if (MoveableItem.selectedBucket[i].isSection) {
                var sectionObj = MoveableItem.selectedBucket[i].fieldObj;
                var secDiv = document.getElementById(sectionObj.sectionId);
                var sepDivId = sectionObj.getSectionSepId();
                var sepDiv = document.getElementById(sepDivId);
                //insert both the section Div and the sep div that separates it from the next section
                parentNode.insertBefore(sepDiv,moveToSep);
                parentNode.insertBefore(secDiv,moveToSep);

                //remove the section
                for (var k=0; k<CrtLayoutElement.layoutSecPos.length;k++) {
                    if (sectionObj.sectionId == CrtLayoutElement.layoutSecPos[k]) {
                        CrtLayoutElement.layoutSecPos.splice(k,1);
                        break;
                    }
                }
            }
        }

        if (highlightSec.sectionId != CrtLayoutElement.LAST_SEC_SEP_DIV) {
            for (moveSecPos=0; moveSecPos<CrtLayoutElement.layoutSecPos.length; moveSecPos++) {
                if (highlightSec.sectionId == CrtLayoutElement.layoutSecPos[moveSecPos]) {
                    break;
                }
            }
        } else {
            //if we are inserting before the last section separator, we are inserting at the end
            moveSecPos = -1;
        }

        //insert the section into its new position
        for (var l=0; l<MoveableItem.selectedBucket.length; l++) {
            if (MoveableItem.selectedBucket[l].isSection) {
                //insert at the end
                if (moveSecPos < 0) {
                   CrtLayoutElement.layoutSecPos.push(MoveableItem.selectedBucket[l].fieldObj.sectionId);
                } else {
                   CrtLayoutElement.layoutSecPos.splice(moveSecPos,0,MoveableItem.selectedBucket[l].fieldObj.sectionId);
                }
            }
        }

   }


    LayoutSection.prototype.getSectionSepId = function() {
        return CrtLayoutElement.SECTION_SEP_DIV_PREFIX + this.sectionId;
    }


     LayoutSection.prototype.insertSepItemRow = function(itemObj){
        this.layoutSeparators[itemObj.rowPos] = [];
        if (itemObj.rowPos == this.numRows) {
            this.numRows++;
        }
     }

     LayoutSection.prototype.insertSeparatorCell = function(itemObj) {
         if (!this.layoutSeparators[itemObj.rowPos]) {
             this.insertSepItemRow(itemObj);
         }
         this.layoutSeparators[itemObj.rowPos][itemObj.colPos] = itemObj;
     }

     LayoutSection.prototype.insertItemRow = function(itemObj){
        this.layoutItems[itemObj.rowPos] = [];
        if (itemObj.rowPos == this.numRows) {
            this.numRows++;
        }
     }

     LayoutSection.prototype.insertField = function(itemObj) {
         if (!this.layoutItems[itemObj.rowPos]) {
             this.insertItemRow(itemObj);
         }
         this.layoutItems[itemObj.rowPos][itemObj.colPos] = itemObj;
     }

     LayoutSection.prototype.attachEvents = function() {
         var self = this;
         var tableElem = document.getElementById(self.sectionHeaderId);
         addEvent(tableElem, 'click', function(evt) {self.handleMouseClick(evt);}, false);
         addEvent(tableElem, 'mousedown', function(evt) {self.handleMouseDown(evt);}, false);
         addEvent(tableElem, 'mouseover', function(evt) {self.handleMouseOver(evt);}, false);
         addEvent(tableElem, 'dblclick', function(evt) {self.handleMouseDblClick(evt);}, false);
         addEvent(tableElem, 'mouseout', function(evt) {self.handleMouseOut(evt);}, false);
     }

     LayoutSection.prototype.handleMouseClick = function(evt) {
        var evt= getEvent(evt);
        if (evt.ctrlKey) {
            this.toggleSelected(true);
        } else {

        }
     }

     LayoutSection.prototype.handleMouseDblClick = function(evt) {
         CrtLayoutElement.openPropertiesEdit(evt);
     }

    LayoutSection.getSection = function(rowNum,colNum) {
        return LayoutSection.sections[rowNum][colNum];
    }

     LayoutSection.prototype.handleMouseUp = function(evt) {


     }

     LayoutSection.prototype.handleMouseDown = function(evt) {
       var evt = getEvent(evt);
       if (!evt.shiftKey && !evt.ctrlKey && !this.isSelected) {
            this.toggleSelected(false);
       }
       CrtLayoutElement.mouseDown = true;
    }

    LayoutSection.prototype.handleMouseOut = function(evt) {
        document.getElementById(CrtLayoutElement.HOVER_DIV).style.display = 'none';
        clearTimeout(this.timeOut);
        this.mousingOver = false;
    }


    LayoutSection.prototype.handleMouseOver = function(evt) {
        var evt = getEvent(evt);
        this.posX = getMouseX(evt);
        this.posY = getMouseY(evt);
        if (!CrtLayoutElement.dragMove && !this.mousingOver) {
            var self = this;
            this.timeOut = setTimeout(function() {self.setupMouseOverDiv();},CrtLayoutElement.HOVER_TIME_OUT)
            this.mousingOver = true;
        }

    }

    LayoutSection.prototype.setupMouseOverDiv = function(evt) {
        var moElem = document.getElementById(CrtLayoutElement.HOVER_DIV);
        var html = '';
        var posx = this.posX+10;
        var posy = this.posY-60;
        html += '<div class="mouseOverHeader">'+ this.labelName + '</div>';
        html += '<div class="mouseOverBody">' + LC.getLabel("CrtLayout","layoutSecHover");
        html += '</div>';
        moElem.innerHTML = html;

        moElem.style.left = posx+"px";
        moElem.style.top = posy+"px";
        moElem.style.display = 'block';
        return moElem;
    }

LayoutSection.prototype.toggleSelected = function(isCtrlSelect) {
        if (this.isSelected) {
            this.removeSelection(false);
        }
        else {
            if (!isCtrlSelect) {
                MoveableItem.clearSelectedItems();
            }
            this.addSelection();
        }
}

LayoutSection.prototype.createMoveableItem = function() {
    return new MoveableItem(this.sectionId, this.labelName, this.labelName, this.labelName, 'SECTION', this, true, true, false, false);
}

LayoutSection.prototype.removeSelection = function(noBucketRemove) {
    if (!noBucketRemove && this.moveableItem) {
        this.moveableItem.removeSelection();
    }
    this.moveableItem = null;
    this.removeSelectionFormatting();
}

LayoutSection.prototype.remove = function() {
    this.removeSelection();

    for (var j = 0; j < this.numRows; j++) {
        for (var k = 0; k < this.numCols; k++) {
            var field = this.layoutItems[j][k];
                if (!field.isEmpty) {
                    field.addSelection();
                }
        }
    }

    //move all the fields back
    if (CrtLayoutElement.currentDisplayedSec) {
        CrtLayoutElement.currentDisplayedSec.moveCells();
    }

    MoveableItem.clearSelectedItems();
    this.isEmpty = true;

    //reorder sections
    for (var i=0; i<CrtLayoutElement.layoutSecPos.length;i++) {
        if (this.sectionId == CrtLayoutElement.layoutSecPos[i]) {
            CrtLayoutElement.layoutSecPos.splice(i,1);
            break;
        }
    }

    //remove the element
    var parentNode = document.getElementById(this.sectionId).parentNode;
    parentNode.removeChild(document.getElementById(this.sectionId));
    //remove the section separator
    parentNode.removeChild(document.getElementById(this.getSectionSepId()));

}


LayoutSection.prototype.addSelection = function() {
    var addSelection;
    this.moveableItem = this.createMoveableItem();
    addSelection = this.moveableItem.addSelection();
    //are we mixing the selection Bucket?
    if (addSelection) {
        this.addSelectionFormatting();
    }
}

LayoutSection.prototype.removeSelectionFormatting = function() {
    this.isSelected = false;
    this.render();

}

LayoutSection.prototype.addSelectionFormatting = function() {
    this.isSelected = true;
    this.render();
}

LayoutSection.prototype.render = function() {
    var headerElem = document.getElementById(this.sectionHeaderId);
    headerElem.innerHTML = this.labelName;
    headerElem.className = CrtLayoutElement.CSS_CLASS_LAYOUT_SECTION;
    if (this.isSelected) {
        headerElem.className += ' '+CrtLayoutElement.CSS_CLASS_SELECTED_SECTION;
    }
}



     function setFieldAttributes(sectionId, elementId, fieldId, fieldName, displayName, customName, type, rowPos, colPos, inLayout, isLookup, defaultChecked) {
        var item = new LayoutItem(sectionId, elementId, fieldId, fieldName, displayName, customName, type, rowPos, colPos, inLayout, isLookup, false, null, defaultChecked);
        item.insertField();
     }

     function setEmptyFieldAttributes(sectionId,elementId,rowPos,colPos,rowObj) {
        new LayoutItem(sectionId,elementId,null,null,null,null,null,rowPos,colPos,true,false, true, rowObj,false).insertField();
     }


    function LayoutItem(sectionId, elementId, fieldId, fieldName, displayName, customName, type, rowPos, colPos, inLayout, isLookup, isEmpty, rowObj, defaultChecked) {
         this.sectionId = sectionId;
         this.rowPos = rowPos;
         this.colPos = colPos;
         if (elementId == null && fieldId == null) {
             this.itemElem  = rowObj.insertCell(rowObj.cells.length);
             this.itemId = this.createNewCellId();
             this.itemElem.id = this.itemId;
         }
         else {
             this.itemId = elementId;
         }

         this.isEmpty = isEmpty;
         this.fieldId = fieldId;
         this.customName = customName;
         this.fieldName = escapeHTML(fieldName);

         this.isSelected = false;

         this.type = type;
         this.inLayout = inLayout;
         this.isLookup = isLookup;
         this.defaultChecked = defaultChecked;

         //this takes too long to set for lookup fields -- set it when we actually hover
         this.displayName = this.isLookup ? null : displayName;
    }


    LayoutItem.prototype.setupMouseOverDiv = function(evt) {
        var moElem = document.getElementById(CrtLayoutElement.HOVER_DIV);
        var html = '';
        var posx = this.posX+10;
        var posy = this.posY-60;
        html += '<div class="mouseOverHeader">'+ this.fieldName + (this.isLookup ? ' ' + LC.getLabel("CRTLookupLayer","viaLookupParen") : '') + '</div>';
        if (!this.displayName) {
            this.displayName = CrtLayoutElement.getDisplayPath(this.fieldId,true);
        }
        html += '<div class="mouseOverBody">' + LC.getLabel("CrtLayout","CustomLabel") + ': '+ (this.customName ? this.customName : this.displayName) + '<br>';
        html += LC.getLabel("CrtLayout","defaultState") +' ';
        if (this.defaultChecked) {
              html += LC.getLabel("CrtLayout","checked");
        } else {
            html += LC.getLabel("CrtLayout","unchecked");
        }
        html += ' <br> ';
        html += LC.getLabel("CrtLayout","sourceObject") + ' ';
        //get source object
        var primObjId = this.isLookup ? CrtLayoutElement.getPrimObjId(this.fieldId,'.') : CrtLayoutElement.getPrimObjId(this.fieldId,CrtLayoutElement.FIELD_SEP);
        var sourceObject = CrtLayoutElement.getPrimObjMetaMap()[primObjId].label;
        if (this.isLookup) {
            html += CrtLayoutElement.getLookupFieldFromPath(CrtLayoutElement.getPath(this.fieldId)).lookupLabel + '<br>(' +LC.getLabel("CrtLayout","associatedWith")+' ' + sourceObject +')';
        } else {
            html += sourceObject;
        }
        html += '<br>';
        if (this.isLookup) {
            html += '<br>';
            html += LC.getLabel("CrtLayout","lookupPath") + ' ' + CrtLayoutElement.getDisplayPath(this.fieldId,false);
            html += '<br>';
        }
        moElem.innerHTML = html;

        moElem.style.left = posx+"px";
        moElem.style.top = posy+"px";
        moElem.style.display = 'block';
        return moElem;
    }

    function MoveableItem(itemId, fieldName, displayName, customName, type, fieldObj, inLayout, isSection, isLookup, defaultChecked) {
        this.itemId = itemId;
        this.fieldName = fieldName;
        this.displayName = displayName;
        this.customName = customName;
        this.type = type;
        this.fieldObj = fieldObj;
        this.inLayout = inLayout;
        this.isSection = isSection;
        this.isLookup = isLookup;
        this.defaultChecked = defaultChecked;
    }

    //insert a field into the Layout Section Object
    LayoutItem.prototype.insertField = function() {
        CrtLayoutElement.layoutSections[this.sectionId].insertField(this);
    }

    //format a field - set background color
    LayoutItem.prototype.formatField = function() {
        if (!this.itemElem) {
            this.itemElem = document.getElementById(this.itemId);
        }
        var className = CrtLayoutElement.CSS_CLASS_LAYOUT_CELL;
        if (!this.isEmpty) {
             className += ' ' + CrtLayoutElement.CSS_CLASS_LAYOUT_ITEM;
             if (this.isSelected) {
                className += ' ' + CrtLayoutElement.CSS_CLASS_SELECTED_ITEM;
             }
             var innerHTML = '';
             var formattedName = this.formatName(this.fieldName);
             if (this.defaultChecked) {
                 innerHTML += CrtLayoutElement.DFLT_CHECKED_ICON_URL;
             }
             if (this.isLookup) {
                innerHTML += CrtLayoutElement.LOOKUP_ICON_URL;
            }
            if (innerHTML != '') {
                innerHTML += ('&nbsp;' + formattedName);
            } else {
                innerHTML = formattedName;
            }
            this.itemElem.innerHTML = innerHTML;
        } else {
            this.setCellToEmpty();
        }
        this.itemElem.className = className;
    }

    LayoutItem.prototype.formatName = function(fieldName) {
        if (!fieldName) {
            return '';
        }
        //truncate more characters if we have the other icons on the tiles also
        var maxLength = CrtLayoutElement.MAX_DISPLAY_FIELD_LENGTH - (this.defaultChecked ? 3 : 0) - (this.isLookup ? 2 : 0);

        if (fieldName.length > maxLength) {
            return fieldName.substring(0,maxLength-2)+"...";
        }
        else {
            return fieldName;
        }
    }

    LayoutItem.prototype.handleMouseOut = function() {
        if (this.isEmpty) {
            return;
        }
        document.getElementById(CrtLayoutElement.HOVER_DIV).style.display = 'none';
        clearTimeout(this.timeOut);
        this.mousingOver = false;
    }

    LayoutItem.prototype.handleMouseDown = function(evt) {
       if (this.isEmpty) {
           return;
       }
       var evt = getEvent(evt);
       if (!evt.shiftKey && !evt.ctrlKey && !this.isSelected) {
            this.toggleSelected(false);
       }
       CrtLayoutElement.mouseDown = true;
    }

    LayoutItem.prototype.handleMouseOver = function(evt) {
        var evt = getEvent(evt);
        this.posX = getMouseX(evt);
        this.posY = getMouseY(evt);
        if (CrtLayoutElement.mouseDown && CrtLayoutElement.dragMove && !MoveableItem.currentSelectedObj.isSection) {
           var sectionObj = CrtLayoutElement.getLayoutSection(this.sectionId);
           CrtLayoutElement.clearHighlights();
           var sepObj = sectionObj.layoutSeparators[this.rowPos][this.colPos];
           sepObj.setHighlighted(true);
        } else {
            if (!this.isEmpty && !CrtLayoutElement.dragMove && !this.mousingOver) {
                 var self = this;
                 this.timeOut = setTimeout(function() {self.setupMouseOverDiv();},CrtLayoutElement.HOVER_TIME_OUT)
                 this.mousingOver = true;
            }
        }
    }

    LayoutSection.prototype.getSepCell = function(rowPos,colPos) {
        return this.layoutSeparators[rowPos][colPos];
    }

    CrtLayoutElement.getLayoutSection = function(sectionId) {
        return CrtLayoutElement.layoutSections[sectionId];
    }

    LayoutItem.prototype.handleMouseDblClick = function(evt) {
        if (this.isEmpty) {
            return;
        }
        CrtLayoutElement.openPropertiesEdit(evt);
    }


    LayoutItem.prototype.handleMouseClick = function(evt) {
        if (this.isEmpty) {
            return;
        }
        var evt = getEvent(evt);
        if (evt.shiftKey && MoveableItem.currentSelectedObj) {
            LayoutItem.multiSelect(this);
        } else if (evt.ctrlKey) {
            this.toggleSelected(true);
        } else {

        }
     }

     LayoutItem.prototype.toggleSelected = function(isCtrlSelect) {
        if (this.isSelected) {
            this.removeSelection(false);
        }
        else {
            if (!isCtrlSelect) {
                MoveableItem.clearSelectedItems();
            }
            this.addSelection();
        }
     }

    //remove all selected elements
    MoveableItem.clearSelectedItems = function() {
        var selB = MoveableItem.selectedBucket;
        for (var i = 0; i < MoveableItem.selectedBucket.length; i++) {
            MoveableItem.selectedBucket[i].fieldObj.removeSelection(true);
        }
        MoveableItem.selectedBucket = [];
        MoveableItem.currentSelectedObj = null;
    }

    LayoutItem.prototype.removeSelectionFormatting = function() {
        this.isSelected = false;
        this.formatField();
    }

    LayoutItem.prototype.createMoveableItem = function() {
        return new MoveableItem(this.fieldId, this.fieldName, this.displayName, this.customName, this.type, this, true, false, this.isLookup, this.defaultChecked)
    }

    LayoutItem.prototype.removeSelection = function(noBucketRemove) {
        if (!noBucketRemove && this.moveableItem) {
            this.moveableItem.removeSelection();
        }
        this.moveableItem = null;
        this.removeSelectionFormatting();
    }

    LayoutItem.prototype.addSelectionFormatting = function() {
        this.isSelected = true;
        this.formatField();
    }

    LayoutItem.prototype.addSelection = function() {
        var addSelection;
        if (this.isEmpty) {
            return;
        }
        this.moveableItem = this.createMoveableItem();
        addSelection = this.moveableItem.addSelection();
        //are we mixing the selection Bucket?
        if (addSelection) {
            this.addSelectionFormatting();
        }
    }

    MoveableItem.prototype.addSelection = function() {
        //make sure we are not mixing sections and items
        if (MoveableItem.currentSelectedObj && (this.isSection != MoveableItem.currentSelectedObj.isSection)) {
            return false;
        }
        if (isIE5) {
            MoveableItem.selectedBucket = MoveableItem.selectedBucket.concat(this);
        } else {
            MoveableItem.selectedBucket.push(this);
        }

        MoveableItem.currentSelectedObj = this;
        return true;
    }

    MoveableItem.prototype.removeSelection = function() {
         for (var i = 0; i < MoveableItem.selectedBucket.length; i++) {
            if (MoveableItem.selectedBucket[i].itemId == this.itemId) {
                MoveableItem.selectedBucket.splice(i, 1);
            }
        }
        if (MoveableItem.currentSelectedObj == this) {
            MoveableItem.currentSelectedObj = null;
            if (MoveableItem.selectedBucket.length > 0) {
                MoveableItem.currentSelectedObj = MoveableItem.selectedBucket[MoveableItem.selectedBucket.length-1];
            }
        }
    }


    //handleMultiSelect with shift key
    LayoutItem.multiSelect = function(itemObj) {
        //if in different sections, return
        if (MoveableItem.currentSelectedObj.fieldObj.sectionId != itemObj.sectionId) {
            return;
        }
        var previousSelectedItem = MoveableItem.currentSelectedObj;
        var rowNum1 = previousSelectedItem.fieldObj.rowPos;
        var rowNum2 = itemObj.rowPos;
        var colNum1 = previousSelectedItem.fieldObj.colPos;
        var colNum2 = itemObj.colPos;
        var startRow = Math.min(rowNum1, rowNum2);
        var endRow =  Math.max(rowNum1, rowNum2);
        var startCol =  Math.min(colNum1, colNum2);
        var endCol =  Math.max(colNum1, colNum2);
        if (startRow == endRow && startCol == endCol) {
            itemObj.removeSelection(false);
            return;
        }
        var moving = MoveableItem.selectedBucket;
        MoveableItem.clearSelectedItems();
        CrtLayoutElement.clearTextSelection();
        for (var i = startRow; i <= endRow; i++ ) {
            for (var j = startCol; j <= endCol; j++) {
                 CrtLayoutElement.layoutSections[itemObj.sectionId].getItem(i,j).addSelection();
            }
        }

        MoveableItem.currentSelectedObj = previousSelectedItem;
    }

   CrtLayoutElement.save = function() {
        var xml = CrtLayoutElement.toXML();
        var submitForm = document.forms[CrtLayoutElement.XML_FORM_NAME];
        submitForm.val.value = xml;
        submitForm.submit();
   }

   CrtLayoutElement.toXML = function() {
        var xml = '<' + CrtLayoutElement.ROOT_CONTAINER + '>\n';

       for (var i=0; i<CrtLayoutElement.layoutSecPos.length; i++) {
            xml += CrtLayoutElement.layoutSections[CrtLayoutElement.layoutSecPos[i]].toXml();
        }

        xml += '</' + CrtLayoutElement.ROOT_CONTAINER + '>\n';
        return xml;
    }


    CrtLayoutElement.escapeXML = function(v) {
        v = v.replace(/&/g, '\&amp;');
        v = v.replace(/"/g, '\&quot;');
        v = v.replace(/'/g, "\'");
        v = v.replace(/</g, '&lt;');
        v = v.replace(/>/g, '&gt;');

        return v;
    }

   LayoutSection.prototype.toXml = function() {
        var xml = '<' + CrtLayoutElement.SECTION + ' ';
        //add sectionId
        xml += CrtLayoutElement.SECTION_ID + '=' + '"' + CrtLayoutElement.escapeXML(this.sectionValue) + '" ';
       //add sectionName
        xml += CrtLayoutElement.SECTION_NAME + '=' + '"' + CrtLayoutElement.escapeXML(this.labelName) + '" >\n';
        //iterate over all layout items and add them to xml
        for (var i=0; i < this.numRows; i++) {
            for (var j=0; j< this.numCols; j++) {
                if (!this.getItem(i,j).isEmpty) {
                    xml += '\t';
                    xml += this.getItem(i,j).toXml();
                }
            }
        }

        xml += '</' + CrtLayoutElement.SECTION + '>\n';
        return xml;
   }

   LayoutItem.prototype.toXml = function() {
        var xml = '<' + CrtLayoutElement.ITEM + ' ';
        //add itemId
        xml += CrtLayoutElement.ITEM_ID + '=' + '"' + CrtLayoutElement.escapeXML(this.fieldId) + '" ';
        //add itemName
        xml += CrtLayoutElement.ITEM_NAME + '=' + '"' + CrtLayoutElement.escapeXML(this.fieldName) + '" ';
        //add default selected
        xml += CrtLayoutElement.ITEM_DEFAULT_CHECKED + '=' + '"' + this.defaultChecked + '" ';
        //add custom label
        if (this.customName != null) {
            xml += CrtLayoutElement.ITEM_CUSTOMLABEL + '=' + '"' + CrtLayoutElement.escapeXML(this.customName) + '" ';
        }
        //add itemType
        xml += CrtLayoutElement.ITEM_TYPE + '=' + '"' + this.type + '" >';

        xml += '</' + CrtLayoutElement.ITEM + '>\n';
        return xml;
   }

   LayoutSection.prototype.getItem = function(rowNum,colNum) {
        return this.layoutItems[rowNum][colNum];
    }

    CrtLayoutElement.isMouseDown = function() {
        return CrtLayoutElement.mouseDown;
    }

     function LayoutSectionSeparator(divId, sectionId) {
         this.divId = divId;
         this.sectionId = sectionId;
         this.attachEvents();
         this.setHighlighted(false);
     }

     LayoutSectionSeparator.prototype.setHighlighted = function(isHighlighted) {
           this.isHighlighted = isHighlighted;
           this.formatSeparator();
           if (isHighlighted) {
                   CrtLayoutElement.highlightSec = this;
           } else {
                   CrtLayoutElement.highlightSec = null;
           }
     }

    LayoutSectionSeparator.prototype.attachEvents = function() {
         var self = this;
         addEvent(document.getElementById(self.divId), 'mouseover', function(evt) {self.handleMouseOver(evt);}, false);
    }

    LayoutSectionSeparator.prototype.handleMouseOver = function() {
        if (CrtLayoutElement.mouseDown && CrtLayoutElement.dragMove && MoveableItem.currentSelectedObj.isSection) {
            if (CrtLayoutElement.highlightSec) {
                CrtLayoutElement.highlightSec.setHighlighted(false);
            }
            this.setHighlighted(true);
        }
    }

    //format a section separator
    LayoutSectionSeparator.prototype.formatSeparator = function() {
        var itemElem = document.getElementById(this.divId);
        var className = CrtLayoutElement.CSS_CLASS_LAYOUT_SECTION_SEPARATOR;
        if (this.isHighlighted) {
            className += ' ' + CrtLayoutElement.CSS_CLASS_LAYOUT_SECTION_SEPARATOR_HIGHLIGHT;
        }
        itemElem.className = className;
    }

     function setSeparatorAttributes(sectionId,elementId, rowPos, colPos, rowObj) {
        new LayoutItemSeparator(sectionId,elementId, rowPos,colPos, rowObj).insertField();
     }


     function LayoutItemSeparator(sectionId, elementId, rowPos, colPos, rowObj) {
         this.sectionId = sectionId;
         this.rowPos = rowPos;
         this.colPos = colPos;
         if (elementId == null) {
             this.itemElem  = rowObj.insertCell(rowObj.cells.length);
             this.id = this.createNewCellId();
             this.itemElem.id = this.id;
         }
         else {
            this.id = elementId;
         }
    }

    //insert a field into the Layout Section Object
    LayoutItemSeparator.prototype.insertField = function() {
        CrtLayoutElement.layoutSections[this.sectionId].insertSeparatorCell(this);
    }

    LayoutItemSeparator.prototype.handleMouseUp = function(evt) {
        if (CrtLayoutElement.mouseDown && CrtLayoutElement.dragMove) {
            var evt = getEvent(evt);
            CrtLayoutElement.layoutSections[this.sectionId].moveCells(this.rowPos,this.colPos);
            this.setHighlighted(false);
        }
    }

    LayoutItemSeparator.prototype.handleMouseOver = function() {
        if (CrtLayoutElement.mouseDown && CrtLayoutElement.dragMove && !MoveableItem.currentSelectedObj.isSection) {
            if (LayoutItemSeparator.highlightSep) {
                LayoutItemSeparator.highlightSep.setHighlighted(false);
            }
            this.setHighlighted(true);
        }
      }

       LayoutItemSeparator.prototype.setHighlighted = function(isHighlighted) {
           this.isHighlighted = isHighlighted;
           this.formatSeparator();
           if (isHighlighted) {
                   LayoutItemSeparator.highlightSep = this;
           } else {
                   LayoutItemSeparator.highlightSep = null;
           }
       }


    //format a separator
    LayoutItemSeparator.prototype.formatSeparator = function() {
        var className = CrtLayoutElement.CSS_CLASS_LAYOUT_ITEM_SEPARATOR;
        if (this.isHighlighted) {
            className += ' ' + CrtLayoutElement.CSS_CLASS_LAYOUT_ITEM_SEPARATOR_HIGHLIGHT;
        }
        if (!this.itemElem) {
            this.itemElem = document.getElementById(this.id);
        }
        this.itemElem.className = className;
    }

    function escapeQuotes(v) {
        if (v && v.replace){
            v = v.replace(/'/g, "\'");
            v = v.replace(/"/g, '&quot;');
        }
        return v;
    }

    function doNothing(evt) {
        evt = getEvent(evt);
        eventCancelBubble(evt);
    }

   CrtLayoutElement.preview = function(url) {
        document.submitForm.val.value = CrtLayoutElement.toXML();
        openPopupFocus('', 'crtlayoutpreview', 700, 500,'location=no,dependent=no,resizable=yes,toolbar=no,status=no,directories=no,menubar=no,scrollbars=yes', true, false, false);
        borrowForm('submitForm', url, 'crtlayoutpreview');
   }

   CrtLayoutElement.getLookupFieldFromPath = function(path) {
    var primObjMData = CrtLookups.primaryObjects;
    var fieldsMData = CrtLookups.fields;

    var iFieldSep = path.lastIndexOf('.');
    var iSep = path.indexOf('.');
    var table = primObjMData[path.substring(0,iSep)].table;
    var lookup;
    while (iSep != iFieldSep) {
        lookup = path.substring(iSep+1,path.indexOf('.',iSep+1));
        table = fieldsMData[table][lookup].lookup;
        iSep = path.indexOf('.',iSep+1);
    }
    lookup = path.substring(iSep+1);
    return fieldsMData[table][lookup];
}



    CrtLayoutElement.getPath  = function(value) {
        return value.substring(0,value.indexOf(CrtLayoutElement.FIELD_SEP));
    }

    //if isDisplayName, we use ":" separator notation, ">>" otherwise
    CrtLayoutElement.getDisplayPath = function(value, isDisplayName) {
        var path = '';
        path += CrtLayoutElement.getPrimObjMetaMap()[CrtLayoutElement.getPrimObjId(value,'.')].label + (isDisplayName ? ': ' : ' >> ');
        var table = CrtLayoutElement.getPrimObjMetaMap()[CrtLayoutElement.getPrimObjId(value,'.')].table;
        value = value.substring(value.indexOf('.')+1);
        var separator = isDisplayName ? ': ' : ' > ';
        var pathLabel;
        while (value.indexOf('.') > -1) {
            table = CrtLookups.fields[table];
            path += table[value.substring(0,value.indexOf('.'))].label + separator;
            table = table[value.substring(0,value.indexOf('.'))].lookup;
            value = value.substring(value.indexOf('.')+1);
        }
        table = CrtLookups.fields[table];
        path += table[value.substring(0,value.indexOf(CrtLayoutElement.FIELD_SEP))].label + separator;
        table = table[value.substring(0,value.indexOf(CrtLayoutElement.FIELD_SEP))].lookup;

        path += CrtLookups.fields[table][value.substring(value.indexOf(CrtLayoutElement.FIELD_SEP)+1,value.indexOf(CrtLayoutElement.COL_SEP))].label;
        return path;
    }

    //return the AvailabaleSection based on the fieldValue we are given
CrtLayoutElement.getSectionFromFieldValue = function(fieldValue) {
    //kind of HACK way of knowing whether something is a lookup item for not...looking for path separator
    if (fieldValue.indexOf('.') > -1) {
        var primId = CrtLayoutElement.getPrimObjId(fieldValue,'.');
        return CrtLayoutElement.availableSections[CrtLookupSection.getSectionIdFromPrim(primId)];
    } else {
        var primId = CrtLayoutElement.getPrimObjId(fieldValue,CrtLayoutElement.FIELD_SEP);
        return CrtLayoutElement.availableSections[primId];
    }
}

CrtLayoutElement.getPrimObjId = function(path,separator) {
    if (!path) {
        return;
    }
    var iSep= path.indexOf(separator);
    if (iSep > 0) {
        path = path.substring(0,iSep);
        return path;
    } else {
        return null;
    }
}

   CrtLayoutElement.scrollAvailableSection = function(evt){
        var cru = document.getElementById(CrtLayoutElement.SECTION_AVAIL_WRAPPER_ID);
        if (cru) {
            var topValue;
             if(!CrtLayoutElement.availableSectionPosInited) {
                CrtLayoutElement.availableSectionInitPosY = getObjY(cru);
                cru.style.top = '0px';
                CrtLayoutElement.availableSectionPosInited = true;
                return;
            }
            var initLeftHeight = document.getElementById(CrtLayoutElement.LEFT_SECTION_ID).offsetHeight;
            var initRightHeight = document.getElementById(CrtLayoutElement.SECTION_AVAIL_WRAPPER_ID).offsetHeight;
            maxTopValue = initLeftHeight;
            //don't scroll it till it goes offscreen
            if(CrtLayoutElement.availableSectionInitPosY+5 < getScrollY()){
                topValue = getScrollY();
                if (topValue + initRightHeight - 295 < initLeftHeight) {
                   cru.style.top = topValue - 295 + "px";
                //if we are at the bottom of the layout sections, then no more scrolling
                } else {
                    cru.style.top = initLeftHeight-initRightHeight + "px";
                }
            } else {
                cru.style.top = "0px";
            }
        }

    }


/**
  Used for delete confirmations on related lists
  Implemented primarily as a demo of the js/localization binding

  @author polcari
  @since 144
*/

function confirmDelete(msg) {
  if (!msg) {
      msg = LC.getLabel("Global", "are_you_sure");
  }
  return Modal.confirm(msg);
}
// Display the div the corresponds to the selectd value of the SelectElement.  Hide the divs for
// all unselected values.
// The divIds should be provided in the same order that the SelectElement values are generated.
function SelectElementDependency(selectElementId, /* array */ divIds) {
	this.divIds = divIds;
	this.selectElementId = selectElementId;

    var self = this;

	this.changeVisibleDiv = function() {
		var selectedIndex = document.getElementById(self.selectElementId).selectedIndex;

		for (var i = 0; i < divIds.length; i++) {
			var displayValue;
			if (i == selectedIndex)
				displayValue = 'block';
			else
				displayValue = 'none';

			document.getElementById(self.divIds[i]).style.display = displayValue;
		}
	}

	// initialize so the correct the element is shown on the page
    this.changeVisibleDiv();

    addEvent(document.getElementById(this.selectElementId), 'change', this.changeVisibleDiv, false);
}

/**
 * CRT Object selection UI
 *
 * @author tkim
 * @since 148
 */
 
/**
 * This JS class manages the object selection picklists
 */
CrtObjects.DOT_SEPARATOR = CrtConstants.PICKLIST_VALUE_TABLE_FIELD_SEPARATOR;
CrtObjects.ID_SEPARATOR = CrtConstants.PICKLIST_VALUE_ID_SEPARATOR;

CrtObjects.init = function(relationships) {
    var picks = new Array();
    for(var iPicklist = 0; iPicklist < CrtConstants.MAX_OBJECTS; iPicklist++) {
        var pick = document.getElementById(CrtConstants.OBJECT_PREFIX + iPicklist);
        picks[iPicklist] = pick;
    }
    return new CrtObjects(relationships, picks);
}

/**
  we require a map, given as JSON:

  Map<String, Array of Relationship> map from Object to Array of Relationship
  Relationship = tuple (name/value, label)

    var relationships = {
        "Account" : [
            { value : "Contact.Account", label: "Contacts" },
            { value : "Opportunity.Account", label: "Opportunities } ],
        "Contact": [
        ]
    };

For a given relationship value, for example, "Contact.Account", the substring
preceding the dot is necessarily another element in the map.

*/
function CrtObjects(relationships, picks) {
    this.relationships = relationships;
    this.picks = picks;
    this.originalValues = CrtObjects.getSelectedValues(picks);

// for debugging:
/*
    var self = this;
    for(var iPicklist = 0; iPicklist < picks.length; iPicklist++) {
        addEvent(this.picks[iPicklist], 'change', this.getUpdatePicklistsClosure(iPicklist), false);
    } */
}

// public
CrtObjects.prototype.getPicklist = function(iPicklist) {
    return this.picks[iPicklist];
}

// public
CrtObjects.prototype.getValue = function(iPicklist) {
    return CrtObjects.getSelectedValue(this.picks[iPicklist]);
}

/** this is necessary to make iPicklist effectively like a final variable
    by taking a snapshot of the stack frame */
CrtObjects.prototype.getUpdatePicklistsClosure = function(iPicklist) {
    var self = this;
    return function() { self.updatePicklists(iPicklist) };
}

/**
 if picklist i changes, then:
     if this is not the last picklist:
         - set all picklists j > i to null value (or maybe to a previous value?)
         - set the options for the next picklist i+1 to the relationship values
 */
CrtObjects.prototype.updatePicklists = function(iPicklist) {
    // clear all picklists j > i
    for(var j = iPicklist + 1; j < CrtConstants.MAX_OBJECTS; j++) {
       this.picks[j].options.length = 1; // leave the --None-- option
    }

    if (iPicklist < (CrtConstants.MAX_OBJECTS - 1)) {
        // set the options for the next picklist i+1 to the relationships
        var value = CrtObjects.getSelectedValue(this.picks[iPicklist]);
        if (value != '') {
	        var object = this.parseObject(CrtObjects.getSelectedValue(this.picks[iPicklist]));
	        var rels = this.relationships[object];
            for(var iRel = 0; iRel < rels.length; iRel++) {
                var rel = rels[iRel];
                var relValue = this.swapOriginalValue(iPicklist + 1, rel.value);
                var options = this.picks[iPicklist + 1].options;
                options[options.length] = new Option(rel.label, relValue);
            }
        }
    }
}

/* restore the original value that includes the ID, if this is the same as the original value */
CrtObjects.prototype.swapOriginalValue = function(iChildPicklist, relationshipValue) {
    var ret = relationshipValue;
    // is the parent set to the original value?  only do the swap if the parent
    // is also set to an original value.  if the parent set to a new value then
    // the child never corresponds to the original value.
    var parentPicklistValue = CrtObjects.getSelectedValue(this.picks[iChildPicklist - 1]);
    if (parentPicklistValue.indexOf(CrtObjects.ID_SEPARATOR) >= 0) {
        // if the parent has an ID, then it must be an original value
        // so test this child relationship for swap
        var originalChildValue = this.originalValues[iChildPicklist];
        originalChildValue = originalChildValue.substring(0, originalChildValue.indexOf(CrtObjects.ID_SEPARATOR)); // strip the ID for the test
        if (relationshipValue == originalChildValue) {
            ret = this.originalValues[iChildPicklist];
        }
    }
    return ret;
}

// private
// get the selected value of a select element
CrtObjects.getSelectedValue = function(selectElement) {
    return selectElement.selectedIndex < 0 ? null : selectElement.options[selectElement.selectedIndex].value;
}

// private
CrtObjects.getSelectedValues = function(picks) {
    var originalValues = new Array();
    for(var iPick = 0; iPick < picks.length; iPick++) {
        originalValues[iPick] = CrtObjects.getSelectedValue(picks[iPick]);
    }
    return originalValues;
}

// private
// grab the object from a string like "Contact.Account" or "Contact.Account|071xxxxxx"
CrtObjects.prototype.parseObject = function(picklistValue) {
    var value;
    var iDot = picklistValue.indexOf(CrtObjects.DOT_SEPARATOR);
    var iPipe = picklistValue.indexOf(CrtObjects.ID_SEPARATOR);
    if (iDot >= 0) {
        value = picklistValue.substring(0, iDot);
    } else if (iPipe >= 0) {
        // No dot (first picklist of root objects) so just take the value, minus the pipe-ID part
        value = picklistValue.substring(0, iPipe);
    } else {
        // Not even a pipe, so take the whole thing
        value = picklistValue;
    }
    return value;
}
    /** @author zzhou
     *  @since 150
     * Used for ForecastRoleUserPage to enable/disable the Forecast Share options for a FM */

	function ForecastRoleUser() {};
    ForecastRoleUser.checkDisplayAllowSharing = function() {
        var selectedElement = document.getElementById(ForecastRoleUser.pUSER)
        //if no user is selected, we hide the Forecast Sharing div and disable the checkboxes
        if (selectedElement.options[selectedElement.selectedIndex].value == '') {
            document.getElementsByName(ForecastRoleUser.pCAN_SHARE)[0].disabled=true;
            document.getElementsByName(ForecastRoleUser.pCAN_SHARE)[1].disabled=true;
            document.getElementById(ForecastRoleUser.FORECAST_SHARE_RADIO).style.visibility = 'hidden';
        } else {
            document.getElementsByName(ForecastRoleUser.pCAN_SHARE)[0].disabled=false;
            document.getElementsByName(ForecastRoleUser.pCAN_SHARE)[1].disabled=false;
            document.getElementById(ForecastRoleUser.FORECAST_SHARE_RADIO).style.visibility = 'visible';
        }
    }

    //confirm fct share removal
    ForecastRoleUser.confirmRemoveShares = function(hadMgr) {
        var selectedElement = document.getElementById(ForecastRoleUser.pUSER);
        return selectedElement.options[selectedElement.selectedIndex].value != '' || !hadMgr || window.confirm(LC.getLabel("ForecastSharingPref","confirmAssignNoMgr"));
    }
/** 
 * 
 * Auto-Complete Lookup functionality using script injection technique.
 * To use this script, create a layout section header name with the value:
 * 
 *     <script src="/appex/autocomplete/ac.js"></script>AutoComplete
 *     
 * Presuming, of course, that the file exists in the specified directory.
 * This script also relies on the following scripts to be available:
 * 
 *     /soap/ajax/8.0/connection.js
 *
 * These scripts are included dynamically. (See the code below for more 
 * information.
 *
 */

//////////////////////////////////////////
// AutoComplete object handles query for Entities
// matching lookup type and partial string.
//////////////////////////////////////////

AutoComplete = function(el){

    this.baseName = this.getBaseName(el);
    this.type = this.getLookupType(el);
    this.lookupInput = this.getLookupInput(el);

    this.lookupInput.onkeyup = AutoComplete.prototype.onKeyUp;
    this.lookupInput.onfocus = AutoComplete.prototype.onFocus;
    this.lookupInput.onblur = AutoComplete.prototype.onBlur;
    this.hasFocus = false;

    this.valQueried = null;
    this.records;
    this.selected = -1;
    
    this.entityName;
    this.entityNameFields;
    
    if (this.type == '001'){
        this.entityName = 'Account';
        this.entityNameFields = ['Name'];
    } else if (this.type == '003') {
        this.entityName = 'Contact';
        this.entityNameFields = ['FirstName', 'LastName'];
    } else if (this.type == '005') {
        this.entityName = 'User';
        this.entityNameFields = ['FirstName', 'LastName', 'Alias'];
    }
    
}

// static variables
AutoComplete.prototype.RECORD_HEIGHT = 17;
AutoComplete.prototype.MIN_LENGTH_THRESHOLD = 3;
AutoComplete.prototype.TYPING_THRESHOLD_MILLIS = 200;
AutoComplete.prototype.HIGHLIGHT_COLOR = "#AACCFF";
AutoComplete.prototype.IS_IE = document.all;

// class variables
AutoComplete.prototype.AUTOCOMPLETE_MAP = [];
AutoComplete.prototype.IS_PROCESSING = false;
AutoComplete.prototype.FORM_NAME = 'editPage';

AutoComplete.prototype.getBaseName = function(el){
    if (!el) return this.baseName;
    var name = el.name;
    var idx = name.indexOf('_');
    if (idx > -1){
        return name.substring(0, idx);
    } else {
        return name;
    }
}

AutoComplete.prototype.isLookup = function(el){
    return el.name.indexOf('_lkid') > 0;
}

AutoComplete.prototype.getLookupInput = function(el){
    if (!el) return this.lookupInput;
    var base = this.getBaseName(el);
    return document.getElementById(base);
}

AutoComplete.prototype.getLookupType = function(el){
    if (!el) return this.type;
    var type;
    var base = this.getBaseName(el);
    var name = base +  "_lktp";
    var el = document.getElementById(name);
    if (el){
        type = el.value;
    }    
    return type;
}

AutoComplete.prototype.doLookup = function(val){

    if (!this.hasFocus || AutoComplete.prototype.IS_PROCESSING) return;

    // validate name parts before doing query
    var nameParts = this.parseName(val);
    if (!nameParts || nameParts.length == 0){
        return;
    }

    // mark as processing so we don't run multiple queries at a time
    AutoComplete.prototype.IS_PROCESSING = true;

    // keep last queried so we can do client-side subfiltering
    this.valQueried = val;
    
    var soql = "SELECT Id";

    for (var i = 0; i < this.entityNameFields.length; i++){
        soql += ", " + this.entityNameFields[i];
    }

    soql += " FROM " + this.entityName + " WHERE ";

    // for single-name entities, query single field as complete string
    if (this.entityNameFields.length == 1){
    
        soql += this.entityNameFields[0] + " like '" + val + "%' ";
    
    // for person-name entities, query for variations on the name
    } else {
        for (var i = 0; i < this.entityNameFields.length; i++){
            for (var j = 0; j < nameParts.length; j++){
                var part = nameParts[j];
                if (part){
                    soql += this.entityNameFields[i] + " like '" + part + "%' ";
                    if (i != this.entityNameFields.length - 1 || j != nameParts.length - 1){
                        soql += " OR "
                    }
                }
            }
        }
    }

    this.records = AutoComplete.prototype.query(soql).records;
    this.displayResults(this.records, val);
    
    AutoComplete.prototype.IS_PROCESSING = false;
    
}

AutoComplete.prototype.parseName = function(val){
    var partsTemp;
    if (val){
        partsTemp = val.split(' ');
    } else {
        partsTemp = [];
    }
    
    var parts = [];
    for (var i = 0; i < partsTemp.length; i++){
        var part = partsTemp[i];
        part = part.replace(' ', '');
        if (part){
            parts.push(part);
        }
    }
    return parts;
}

AutoComplete.prototype.displayResults = function(records, value){

    if (records && records.length > 0){

        var baseName = this.getBaseName();

        var html = [];
        html.push("<table width='100%' cellpadding='0' cellspacing='0' border='0'>");
        for (var i = 0; i < records.length; i++){
            var record = records[i];
            var id = ApiUtils.getId(record);
            html.push("<tr><td style='cursor: pointer;' onmouseover='AutoComplete.prototype.setSelected(\""+baseName+"\", "+i+");this.style.backgroundColor=\""+AutoComplete.prototype.HIGHLIGHT_COLOR+"\";' onmouseout='this.style.backgroundColor=\"\";'>");
            html.push("<div style='height:"+AutoComplete.prototype.RECORD_HEIGHT+"px;' id='"+id+"'>");
            var name = this.getName(record);
            html.push(this.highlightMatch(name, value));
            html.push("</div>");
            html.push("</td></tr>");
        }
        html.push("</table>");

        var boxName = this.getBaseName();
        var box = AutoComplete.prototype.getBox(boxName);
        if (!box){
            box = AutoComplete.prototype.makeBox(boxName, records.length * AutoComplete.prototype.RECORD_HEIGHT, 200);
    	    var inputEl = this.getLookupInput();
    	    var top = getObjY(inputEl) + 20;
    	    var left = getObjX(inputEl);
    	    AutoComplete.prototype.moveBox(boxName, top, left);
        } else {
            AutoComplete.prototype.resizeBox(baseName, records.length * AutoComplete.prototype.RECORD_HEIGHT, 200);
        }
        box.innerHTML = html.join('');    
        this.displaySuggestions(true);
    }    

}

AutoComplete.prototype.getName = function(record){
    var name = "";
    for (var i = 0; i < this.entityNameFields.length; i++){
        var value = record.get(this.entityNameFields[i]);
        if (value){
            name += value + " ";
        }
    }
    return name;
}

AutoComplete.prototype.highlightMatch = function(name, value){
    if (!name || !value) return "";
    
    var nameLower = name.toLowerCase();
    var valueLower = value.toLowerCase();
    
    var idx = nameLower.indexOf(valueLower);
    if (idx == -1){
        return name;
    } else {
        var before = name.substring(0, idx);
        var middle = name.substring(idx, idx + value.length);
        var after = name.substring(idx + value.length);
        var match = before + "<b>" + middle + "</b>" + after;
        return match;
    }
}

AutoComplete.prototype.doFilter = function(val){

    var filteredRecords = [];
    for (var i = 0; i < this.records.length; i++){
        var record = this.records[i];
        if (this.isMatch(record, val)){
            filteredRecords.push(record);
        }
    }
    
    if (filteredRecords && filteredRecords.length > 0){
        this.displayResults(filteredRecords, val);
    } else {
        this.doClear();
    }
	        
}

// similar to doLookup() code
AutoComplete.prototype.isMatch = function(record, val){

    var value = null;

    // for single-name entities, match single field as complete string
    if (this.entityNameFields.length == 1){
        value = record.get(this.entityNameFields[0]);
    // for person-name entities, query for variations on the name
    } else {
        value = this.getName(record);
    }
    
    return (value && value.toLowerCase().indexOf(val.toLowerCase()) > -1);
    
}

AutoComplete.prototype.doClear = function(){
    this.displaySuggestions(false);
    this.selected = -1;
}

AutoComplete.prototype.displaySuggestions = function(isVisible){
    AutoComplete.prototype.showBox(this.getBaseName(), isVisible);    
}

AutoComplete.prototype.selectItem = function(selectedIndex){

    var baseName = this.getBaseName();
    var id = null;
    var name = null;
            
    var box = AutoComplete.prototype.getBox(baseName);
    if (box && selectedIndex >= 0){
         var content = box.childNodes[0].childNodes[0];
     	 if (content && content.rows && selectedIndex < content.rows.length){
            var row = content.rows[selectedIndex];
			if (row){
			    var cell = row.cells[0];
                var div = cell.getElementsByTagName("div")[0];
                if (div){
                    id = div.id;
                }
			}
    	}
    }
    
    if (id){
        for (var i = 0; i < this.records.length; i++){
            var rec = this.records[i];
            if (id == ApiUtils.getId(rec)){
                name = this.getName(rec);
                break;
            } 
        }
    }

    // found an ID and a name; load the picklist
    if (id && name){
        lookupPick(AutoComplete.prototype.FORM_NAME, baseName+'_lkid', baseName, '', id, name, '', '');
    }    

    // item selected; clear the suggestion box
    var autoComplete = AutoComplete.prototype.AUTOCOMPLETE_MAP[baseName];
    if (autoComplete){
        autoComplete.doClear();
    }
    
}

//////////////////////////////
// Event hooks on lookup input element.
//////////////////////////////

AutoComplete.prototype.onKeyUp = function(event){

    if (!event){
        event = window.event;
    }

    var autoComplete = AutoComplete.prototype.AUTOCOMPLETE_MAP[this.name];
    if (!autoComplete){
        return;
    }

    var val = this.value;
    
    var isKeyPress = (event && event.keyCode);
    if (AutoComplete.prototype.IS_PROCESSING) {
        return;
    } else if (isKeyPress && AutoComplete.prototype.isEnter(event)) {
        // BUGBUG: how do we avoid submitting form on [ENTER] before
        // executing the onBlur()?
        return;
    } else if (isKeyPress && AutoComplete.prototype.isNavigation(event)) {
        AutoComplete.prototype.handleNav(this.name, event);
        return;
    } else if (isKeyPress && AutoComplete.prototype.isTypingFast(this.name, event)){
        return;
    } else if (isKeyPress && AutoComplete.prototype.isIgnore(event)) {
    	return;
    } else if (isKeyPress && AutoComplete.prototype.isEscape(event)){
        autoComplete.doClear();
    	return;
    }

    if (val != null && val.length >= AutoComplete.prototype.MIN_LENGTH_THRESHOLD){
    
        // initial query
        if (autoComplete.valQueried == null || val.toLowerCase().indexOf(autoComplete.valQueried.toLowerCase()) == -1){
            autoComplete.doLookup(val);
        // subquery; filter results
        } else {
            autoComplete.doFilter(val);
        }
            
   } else if (val == null || val.length <= AutoComplete.prototype.MIN_LENGTH_THRESHOLD){
       autoComplete.valQueried = null;
       autoComplete.doClear();
   }

}

AutoComplete.prototype.onFocus = function(event){

    var autoComplete = AutoComplete.prototype.AUTOCOMPLETE_MAP[this.name];
    if (!autoComplete){
        return;
    }
    autoComplete.hasFocus = true;

}

AutoComplete.prototype.onBlur = function(event){

    if (!event){
        event = window.event;
    }

    var autoComplete = AutoComplete.prototype.AUTOCOMPLETE_MAP[this.name];
    if (!autoComplete){
        return;
    }

    autoComplete.hasFocus = false;

    if (AutoComplete.prototype.TIMEOUT_ID){    
        clearTimeout(AutoComplete.prototype.TIMEOUT_ID);
    }
    
    // leaving input and is NOT ignore and NOT escape: select the highlighted item
    if (event && !AutoComplete.prototype.isIgnore(event) && !AutoComplete.prototype.isEscape(event) && autoComplete.selected >= 0){
        autoComplete.selectItem(autoComplete.selected);
        
    // otherwise: clear the complete suggestion w/ slight delay to allow
    // possible onclick to pass through to the suggestion div
    } else {
        setTimeout("AutoComplete.prototype.AUTOCOMPLETE_MAP['"+this.name+"'].doClear()", 200);
    }
    
}

//////////////////////////////
// Interaction with suggestion box.
//////////////////////////////

// sets selected index of a given auto-complete object. 
AutoComplete.prototype.setSelected = function(baseName, idx){
    var autoComplete = AutoComplete.prototype.AUTOCOMPLETE_MAP[baseName];
    if (!autoComplete){
        return;
    } else {
        autoComplete.selected = idx;
    }
    
}

// handle up/down for auto-complete
AutoComplete.prototype.handleNav = function(name, event){

    var autoComplete = AutoComplete.prototype.AUTOCOMPLETE_MAP[name];
    if (!autoComplete){
        return;
    }

    var code = event.keyCode;
    var isDown = (code == 40);
    
    var newSelected = autoComplete.selected;

      var box = AutoComplete.prototype.getBox(autoComplete.getBaseName());
      if (box){
      
          var content = box.childNodes[0].childNodes[0];
          if (content && content.rows){

              if (isDown){
                  if (autoComplete.selected < content.rows.length - 1){
                      newSelected++;
                  }
              } else {
                  if (autoComplete.selected > 0){
                      newSelected--;
                  }
              }
              
              if (autoComplete.selected >= 0){
                  var oldRow = content.rows[autoComplete.selected];
                  if (oldRow && oldRow.childNodes[0]){
                      oldRow.childNodes[0].onmouseout();
                  }
              }

			  if (newSelected >= 0){
                  var newRow = content.rows[newSelected];
                  if (newRow && newRow.childNodes[0]){
                      newRow.childNodes[0].onmouseover();
                  }
                  autoComplete.selected = newSelected;
		      }                  

		}           
     
    }
    
}

//////////////////////////
// Typing helpers.
//////////////////////////

// fast typing gets delayed complete
AutoComplete.prototype.LAST_DATE = null;
AutoComplete.prototype.TIMEOUT_ID = null;

// if typing fast, will delay onKeyUp() until after typing is slowed
AutoComplete.prototype.isTypingFast = function(name, event){
    
    // clear existing timeout to avoid flood of delayed onKeyUp calls
    if (AutoComplete.prototype.TIMEOUT_ID){    
        clearTimeout(AutoComplete.prototype.TIMEOUT_ID);
    }
    
	var d = new Date();
	if (AutoComplete.prototype.LAST_DATE == null){
	    AutoComplete.prototype.LAST_DATE = d;
	    return true;
	} else {
	    var diff = d.getTime() - AutoComplete.prototype.LAST_DATE.getTime();
	    AutoComplete.prototype.LAST_DATE = d;
	    if (diff < AutoComplete.prototype.TYPING_THRESHOLD_MILLIS){
	        var delayedExec = "document.getElementById('"+name+"').onkeyup();";
	        AutoComplete.prototype.TIMEOUT_ID = setTimeout(delayedExec, AutoComplete.prototype.TYPING_THRESHOLD_MILLIS);
	        return true;
	    } else {
	        return false;
	    }
	}
}

AutoComplete.prototype.isNavigation = function(event){
    var code = event.keyCode;
    return (code == 38 || code == 40);
}

// 9 is TAB; 16 is SHIFT-TAB
AutoComplete.prototype.isIgnore = function(event){
    var code = event.keyCode;
    return (code == 9 || code == 16 || (code >= 33 && code <= 46) || (code >= 112 && code <= 123));
}

AutoComplete.prototype.isEscape = function(event){
    var code = event.keyCode;
    return code == 27;
}

AutoComplete.prototype.isEnter = function(event){
    var code = event.keyCode;
    return code == 13;
}

/////////////////////////
// Query Helpers
/////////////////////////

AutoComplete.prototype.query = function(soql){

    try {
        var qr = sforce.connection.query(soql);
        var results = new Object();
        results.size = qr.size;
        results.records = qr.getArray("records");
        return results;
    } catch (fault){
        alert("error: " + fault);
    }
}

//////////////////////////
// Box helpers.
//////////////////////////

AutoComplete.prototype.makeBox = function(name, height, width) {

    // iframe shim b/c IE has input elements show throw plain DIVs
    if (AutoComplete.prototype.IS_IE){
        var iframe = document.createElement("iframe");
        iframe.id = name+"I";
        iframe.style.display = "block";
        iframe.style.position = "absolute";
        iframe.style.padding = "3px";
        iframe.style.height = height + "px";
        iframe.style.width = width + "px";
        iframe.style.backgroundColor = "#ffffCC";
        document.body.appendChild(iframe);

    }

    // div for the contents
    var div = document.createElement("div");
    div.id = name+"D";
    div.style.border = "1px solid #666";
    div.style.display = "block";
    div.style.position = "absolute";
    div.style.padding = "2px";
    div.style.height = height + "px";
    div.style.width = width + "px";
    div.style.backgroundColor = "#ffffCC";
    document.body.appendChild(div);
    
    return div;

}

AutoComplete.prototype.getBox = function(name){
    return document.getElementById(name+'D');

}

AutoComplete.prototype.resizeBox = function(name, height, width) {

    var iframe = document.getElementById(name+'I');
    if (iframe){
        iframe.style.height = height + "px";
        iframe.style.width = width + "px";
    }

    var box = document.getElementById(name+'D');
    if (box){
        box.style.height = height + "px";
        box.style.width = width + "px";
    }
}

AutoComplete.prototype.moveBox = function(name, top, left){

    var iframe = document.getElementById(name+'I');
    if (iframe){
        iframe.style.top = top + "px";
        iframe.style.left = left + "px";
        iframe.style.zIndex = 10;
    }

    var box = document.getElementById(name+'D');
    if (box){
        box.style.top = top + "px";
        box.style.left = left + "px";
        box.style.zIndex = 20;
    }

}

AutoComplete.prototype.showBox = function(name, isVisible){
    var disp = isVisible ? "block" : "none";
    var iframe = document.getElementById(name+'I');
    if (iframe){
        iframe.style.display = disp;
    }

    var box = document.getElementById(name+'D');
    if (box){
        box.style.display = disp;
    }
}

//////////////////////////
// Initialization helpers.
//////////////////////////

AutoComplete.prototype.loadAutoComplete = function(formName){

    if (!formName){
        AutoComplete.prototype.FORM_NAME = "editPage";
    }
    
    // initialize sforce connection
    var apiSession = getCookie("sid");
    var apiURL = ApiUtils.getApiURL(true, "7.0");
    sforce.connection.init(apiSession, apiURL, true);

    var els = document.getElementsByTagName("input");
    for (var i = 0; i < els.length; i++){
        var el = els[i];
        if (AutoComplete.prototype.isLookup(el)){
            var autoComplete = new AutoComplete(el);
            AutoComplete.prototype.AUTOCOMPLETE_MAP[autoComplete.getBaseName()] = autoComplete;
        }
    }
}






/*
* @author axing
* @since 148
*/
function ApexPage() {}

ApexPage.prototype = new GenericSfdcPage();

/**
 * A simple overlay screen for displaying a loading/saving method during ajax calls.
 * Currently used in check syntax links for S-Controls and Validation rules, as
 * well as for tag headers on detail pages during saving.
 *
 * @author agusev, emoses, jbergan 
 */
function LoadingScreen(theDiv, waitingText){
    this.div = theDiv;
    this.text = waitingText;
}

LoadingScreen.prototype = {
    show : function() {
        if (!this.transparantElement){
            //Lazily create the div.
            this.createElements();
        }
        this.transparantElement.style.display = 'block';
        this.opaqueElement.style.display = 'block';
    },

    hide : function() {
        if (this.transparantElement) {
            this.transparantElement.style.display = 'none';
            this.opaqueElement.style.display = 'none';
        }
    },

    createElements : function() {
        // During construction in Safari, this is undefined, so it has
        // to be checked when show() is called.
        if (XBrowser.getCurrentStyle(this.div, "position") == "static"){
            this.div.style.position = 'relative';
        }

        // We have one overlay that is partially transparant, and one
        // that is not, so that the description will be opaque.
        this.transparantElement = this.createLoadingElement(this.div);
        this.transparantElement.className = 'waitingSearchDiv waitingSearchDivOpacity';
        this.opaqueElement = this.createLoadingElement(this.div);
        this.opaqueElement.className = 'waitingSearchDiv';

        // The loading description must be on a non-transparant div
        // because otherwise it will have the same opacity as the overlay,
        // and it doesn't look right to have semi-transparant text over text.
        this.addLoadingDescription(this.text, this.opaqueElement);
    },

    createLoadingElement : function(element) {
        var loading = document.createElement('div');

        // 100% is ideal, if it works, since it handles resizing correctly
        // and excludes the border of the element.
        loading.style.width = '100%';
        loading.style.height = '100%';
        if (XBrowser.userAgent.isIE) {
            // IE defaults to left being further left (-5 or so)
            loading.style.left = 0;
            // IE doesn't properly draw '100%' for height on the first try.
            loading.style.height = this.div.clientHeight + "px";
        }

        // Appending the overlay last helps work out the sizing done above
        element.appendChild(loading);
        return loading;
    },

    addLoadingDescription : function(description, element) {
        // On IE, a span ends up showing as two separate sections
        // (img and text) in some contexts.
        var newWaitingHolder = document.createElement('div');
        element.appendChild(newWaitingHolder);
        newWaitingHolder.className = 'waitingHolder';
        var limitedOffset = (element.offsetHeight)/5;
        if (limitedOffset > 100) {
            limitedOffset = 100;
        }
        newWaitingHolder.style.top =  limitedOffset + "px";

        var newWaitingImage = document.createElement('img');
        newWaitingHolder.appendChild(newWaitingImage);
        newWaitingImage.src = '/img/loading.gif';
        newWaitingImage.className  = 'waitingImage';

        var newWaitingDescription = document.createElement('span');
        newWaitingHolder.appendChild(newWaitingDescription);
        newWaitingDescription.innerHTML =  description;
        newWaitingDescription.className  = 'waitingDescription';

        // This is a bit kludgy, but given the variety of widths this is used in,
        // a straight percentage (as in the css) doesn't work.  Also, the extra 20
        // is for safari, which otherwise ends up with too small a value for some reason.
        newWaitingHolder.style.width = (newWaitingImage.offsetWidth + newWaitingDescription.offsetWidth + 20) + "px";
        return newWaitingHolder;
    }
}


SearchHistory.LIST_URL = 'listUrl';
SearchHistory.ACTION_URL = 'actionUrl';
SearchHistory.LIST_QS = 'listQS';
SearchHistory.LIST_ID = 'listId';

function SearchHistory() {
	
	this.actionUrl = null;
	this.listUrl = null;
	this.listQs = null;
	this.listId = null;
}
SearchHistory.prototype.readFromQs = function (queryString){
	this.actionUrl = SearchPage.prototype.decodeForSearch(queryString.get(SearchHistory.ACTION_URL));
	this.listUrl = SearchPage.prototype.decodeForSearch(queryString.get(SearchHistory.LIST_URL));
	this.listQs = SearchPage.prototype.decodeForSearch(queryString.get(SearchHistory.LIST_QS));
	this.listId = SearchPage.prototype.decodeForSearch(queryString.get(SearchHistory.LIST_ID));
}
	
//setters
SearchHistory.prototype.setActionUrl = function (newActionUrl) {
	this.actionUrl = newActionUrl;
}

SearchHistory.prototype.setListUrl  = function (newListUrl) {
	this.listUrl = newListUrl;
}

SearchHistory.prototype.setListQs = function (newListQs) {
	this.listQs = newListQs;
}

SearchHistory.prototype.setListId =  function (newListId) {
	this.listId = newListId;
}

//getters
SearchHistory.prototype.getActionUrl = function () {
	return this.actionUrl;
}

SearchHistory.prototype.getListUrl = function () {
	return this.listUrl;
}

SearchHistory.prototype.getListQs =  function () {
	return this.listQs;
}

SearchHistory.prototype.getListId =  function () {
	return this.listId;
}

SearchHistory.prototype.toString = function () {
	
	var qs = new QueryString("");
	if(this.actionUrl && this.actionUrl.length > 0){
		qs.add(SearchHistory.ACTION_URL, SearchPage.prototype.encodeForSearch(this.actionUrl));
	}
	if(this.listUrl && this.listUrl.length > 0) {
		qs.add(SearchHistory.LIST_URL, SearchPage.prototype.encodeForSearch(this.listUrl));
	}
	if(this.listQs && this.listQs.length > 0) {
		qs.add(SearchHistory.LIST_QS, SearchPage.prototype.encodeForSearch(this.listQs));
	}
	if(this.listId && this.listId.length > 0){
		qs.add(SearchHistory.LIST_ID, SearchPage.prototype.encodeForSearch(this.listId));
	}
	var historyDescriptor = qs.toString();
	return historyDescriptor.substring(1, historyDescriptor.length);
	
}
/**
 * Simplified version of hash map
 * undefined value is not allowed, null or undefined key is not allowed
 * feel free to enhance it
 */
function Map() {
	this.size = 0;
	this.map = new Object();
}

Map.prototype.put = function (key, val) {
	// key not null or undefined, value is not undefined
	if (!key || typeof(val) == 'undefined' ) {
		return;
	}
	if (!(key in this.map)) {
		this.size++;
	}
	this.map[key] = val;
}

Map.prototype.remove = function (key) {
	if (key in this.map) {
		delete(this.map[key]);
		this.size--;
	}
}
function Scontrol() {}

Scontrol.prototype.getSelectedRecordIdsFromForm = function(form, keyPrefix) {
	var selected = [];

	// Get the values directly from the form
	if (form) {
		// Find selected rows with matching key prefix
	    for (i = 0; i < form.elements.length; i++) {
	    	var element = form.elements[i];
	        if ((element.name == "ids") && element.checked) {
	        	var value = element.value;
	        	if (value.substr(0, 3) == keyPrefix) {
					selected.push(value);
				}
	        }
	    }		
	} else {
		alert('Unable to find a form for this button');
	}
			
	return selected;
}
var FilterLookupPage = function(){}

FilterLookupPage.prototype.checked = new Array();

FilterLookupPage.prototype.checkAll = function(val)
{
  for(var i = 0; ; i++)
  {
    var chkbox = document.getElementById("chkbox"+i);
    if (!chkbox) break;
    chkbox.checked = val;
    FilterLookupPage.prototype.checked[i] = (val ? chkbox.value : null);
  }
}

//For boolean fields, we replace the filter value. For enums, we append 
FilterLookupPage.prototype.transfer = function(isBooleanField)
{
    var valueElem = opener.document.getElementById(opener.filterLookupValueElem);
    var string = valueElem.value;
    //Thanks chops!
    if (isBooleanField){
    	if (document.getElementById('chkbox0').checked){
    		string = document.getElementById('chkbox0').value;
    	} else if (document.getElementById('chkbox1').checked){ 
    		string = document.getElementById('chkbox1').value;
    	}
	} else {
        for(var i in FilterLookupPage.prototype.checked) {
            var value = FilterLookupPage.prototype.checked[i];
            if (value != null) {
    		    if (string.length > 0) {
    		        string = string + ", ";
    		    }
    		    if (value.indexOf(",") >= 0) {
    		        value = "\"" + value + "\"";
    		    }
    		    string = string + value;
    		}
    	}
    	if (string.length > valueElem.maxLength){
    		string = string.substring(0,valueElem.maxLength);
    	}
	}
    valueElem.value = string;
	self.close();
	return false;
}

FilterLookupPage.prototype.doCheckbox = function(i) {
    var chkbox = document.getElementById("chkbox"+i);
    if (chkbox.checked) {
        FilterLookupPage.prototype.checked[i] = chkbox.value;
    } else {
        FilterLookupPage.prototype.checked[i] = null;
    }
}

FilterLookupPage.prototype.initBoolean = function() {
	for (i = 0; i < 2; i++){
    	var chkbox = document.getElementById("chkbox"+i);
		chkbox.checked = chkbox.value == opener.document.getElementById(opener.filterLookupValueElem).value;
	}
}
    
function EncryptedTextInputElement(id, maxLength, isMasked){
	if (!isMasked) return;
	this.maxLength = maxLength;
	this.element = getElementByIdCS(id);
	if (!this.element) return;
	
	this.wasCleared = false;
	this.origValue = this.element.value;
	
	var self = this;
	
	this.handleOnFocus = function(e){
		if (self.wasCleared){
			return;
		}
		self.element.select();
	}
	
	this.handleKey = function(e){
		if (!self.wasCleared){
			var code = getEvent(e).keyCode;
			if (!(code === KEY_ENTER || code === KEY_TAB || code === KEY_PAGEUP || code === KEY_PAGEDOWN)){
				self.element.value = "";
				self.element.maxLength = self.maxLength;
				self.wasCleared = true;
			}
		}
	}
	
	this.handleBlur = function(e){
		if (self.element.value === self.origValue){
			self.wasCleared = false;
		}
	}
	
	addEvent(this.element, 'focus', this.handleOnFocus, true);
	addEvent(this.element, 'blur', this.handleBlur, true)
	addEvent(this.element, 'keypress', this.handleKey, true);
}
/**
  This is used to retrieve labels

  @author polcari
  @since 144
*/

function LC() {}

/* This function name is referenced in the dynamicJSLibrary. */
LC.getLabel = function() {
  var retVal = '';
  var args = this.getLabel.arguments;
  if (args[0] && args[1]) {
    retVal = LC.labels[args[0]][args[1]];
  }
  for (i = 2; i < args.length; i++) {  
    //substitutions are done client-side.
    var regexp = new RegExp('\\{'+(i - 2)+'\\}', 'g');
    retVal = retVal.replace(regexp, args[i]);
  }
  return retVal;
}

/*
 * maybe pull a list of  ISO 639 and ISO 3166 codes here?
 */
LC.isEnglishLanguage = function() {
    return UserContext.initialized && UserContext.language == "en_US";
}

LC.isUSLocale = function() {
    return UserContext.initialized && UserContext.locale == "en_US";
}

LC.isEnglishUS = function() {
    return LC.isEnglishLanguage() && LC.isUSLocale();
}

/**
 * XBrowser is a singleton object for all cross-browser utility functions.
 *
 * @author mooney
 * @since 146
 */
var XBrowser = {
    /* simple user agent parsing, use object detection in most cases */
    userAgent : {
        isIE : navigator.userAgent.indexOf("MSIE") != -1,
        isIE6 : navigator.userAgent.indexOf("MSIE 6") != -1,
        isIE7 : navigator.userAgent.indexOf("MSIE 7") != -1,
        isOpera : navigator.userAgent.indexOf("Opera") != -1,
        isSafari : navigator.userAgent.indexOf("AppleWebKit") != -1,
        isFirefox : navigator.userAgent.indexOf("Firefox/") != -1,
        isNetscape : navigator.userAgent.indexOf("Netscape/") != -1
    },

    /**
     * transforms CSS property names from IE format to Mozilla/Opera format
     * i.e. XBrowser.dashify("fontColor") == "font-color"
     */
    dashify : function(str){
        return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
    },

    /**
     * Retrieves the current state for a style property
     */
    getCurrentStyle : function(element, styleProp) {
        if (element.currentStyle) {
            //IE
            return element.currentStyle[styleProp];
        } else if (document.defaultView && document.defaultView.getComputedStyle) {
            //Mozilla & Opera
            return document.defaultView.getComputedStyle(element, null).getPropertyValue(XBrowser.dashify(styleProp));
        }
        // Safari :(
        return null;
    },
    
    /**
     * Retreive the CSS style as set on the element with the 'style' HTML attribute.
     * NOTE: This is different from computed style, which usually is not all defined on the
     * element and is inherited from css classes etc.
     * See getCurrentStyle for getting computed style.
     */
    getElementStyle : function(element, styleProp) {
        if (element.currentStyle) { // IE
            return element.currentStyle[styleProp];
        } else if (element.style.getPropertyValue) { // DOM
            return element.style.getPropertyValue(styleProp);
        }
        return null;
    },

    /**
     * Get the actual height of the element in pixels. Uses offsetHeight and corrects for the padding.
     * NOTE: if the padding is not set in pixels will just return offsetHeight.
     * @param useScrollHeight if set to true, use scrollHeight instead of offsetHeight for height calculation
     */
    getActualHeight : function(/*DOMElement*/ element, /* boolean */ useScrollHeight) {
        var regex = /.*px$/;
        var height = useScrollHeight ? element.scrollHeight : element.offsetHeight;
        var paddingTop = XBrowser.getCurrentStyle(element, 'paddingTop');
        var paddingBottom = XBrowser.getCurrentStyle(element, 'paddingBottom');

        if ((!regex.exec(paddingTop)) || (!regex.exec(paddingBottom))) { // can only handle px padding
            return height;
        }
        paddingTop = paddingTop.substring(0, paddingTop.length - 2);
        paddingBottom = paddingBottom.substring(0, paddingBottom.length - 2);
        return height - paddingTop - paddingBottom;
    },

    /**
     * Creats a new XMLHttpRequest and returns it.
     */
    createHttpRequest : function() {
        if (window.XMLHttpRequest) {
            return new XMLHttpRequest();
        } else if (window.ActiveXObject) {
            try {
                return new ActiveXObject("Msxml2.XMLHTTP");
            } catch (e) {
                try {
                    return new ActiveXObject("Microsoft.XMLHTTP");
                } catch (e) {
                }
            }
        }
        return null;
    },

    /**
     * Convenience method which opens an XMLHttpRequest to the specified page, then passes the
     * result to the specified function, or to the specified error handler if an HTTP error occurs
     * 
     * @param url URL to make the request to [String]
     * @param handler Callback to execute after the request returns success [function(request)]
     * @param errorHandler Callback to execute if the request return failure [optional, function(request)]
     * @param method HTTP method to call (GET, POST, PUT, DELETE) [optional, String, default: "GET"]
     * @param body The body of the request [optional, String]
     */
    getHttpResponse : function(url, handler, /* optional */ errorHandler, /*optional*/ method, /*optional*/ body) {
        var request = XBrowser.createHttpRequest();
        var theMethod = method || "GET";
        request.open(theMethod, url, true);
        request.onreadystatechange = function() {
            if (request.readyState == 4) {
                if (request.status == 200) {
                    handler(request);
                } else if (errorHandler) {
                    errorHandler(request);
                }
            }
        };
        if (body && method == "POST"){
            request.setRequestHeader('Content-Type','application/x-www-form-urlencoded; charset=ISO-8859-13');
        }
        request.send(body);
    },

    /**
     * Convenience method to execute a POST XmlHttpRequest
     * 
     * @param url URL to make the request to [String]
     * @param handler Callback to execute after the request returns success [function(request)]
     * @param postBody The body of the request [optional, String]
     * @param errorHandler Callback to execute if the request return failure [optional, function(request)]
     */    
    postHttpResponse : function(url, handler, postBody, /*optional*/ errorHandler) {
        XBrowser.getHttpResponse(url, handler, errorHandler, "POST", postBody);
    },
    
    /**
     * Build a post-style request from a JS Object.  ex: { a: "b", c: "2" } -> "a=b&c=2"
     * @throws URIError from ecodeURIComponent
     */
    buildPost : function(map){
        var arr = [];
        var first = true;
        for (var key in map){
            if (!first){
                arr.push("&");
            }
            first = false;
            if (map[key] instanceof Array) {
                var valueArray = map[key]; 
                if (valueArray.length == 0) {
                    arr.push(key);
                    arr.push("=");
                } else {
                    for (var i = 0; i < valueArray.length; i++) {
                        if (i > 0) {
                            arr.push("&");
                        }
                        arr.push(key);
                        arr.push("=");
                        arr.push(encodeURIComponent(valueArray[i]));
                    }
                }
            } else {
                arr.push(key);
                arr.push("=");
                arr.push(encodeURIComponent(map[key]));
            }
        }
        return arr.join("");
    },

    /**
     * Convenience method to retrieve a script at url and execute it, calling handler after the script has been run.
     * 
     * @param url the url of the script to run
     * @param handler function reference to be called once the script has been loaded
     */
    createDynamicScript : function(url, handler) {
        var script = document.createElement("script");
        if (handler) {
            if (window.ActiveXObject) {
                /* IE uses onreadystatechanged, when loading from the server it fires twice with
                 * readyState values of "loading" and then "loaded", but when loading from the
                 * cache it only fires once with a readyState of "complete"
                 */
                script.onreadystatechange = function() {
                    if (this.readyState == "complete" || this.readyState == "loaded") {
                        handler();
                    }
                };
            } else {
                script.onload = handler;
            }
        }
        script.type = "text/javascript"
        script.src = url;
        document.body.appendChild(script);
    },

    // fix for IE6 CSS background image cache bug (#98786)
    turnOnBackgroundImageCache : function() {
        try {
            document.execCommand("BackgroundImageCache", false, true);
        } catch (error) {
            // not in IE6
        }
    }

};

/**
 * add the Node interface to IE
 */
if (!window.Node) {
    var Node = {
		ELEMENT_NODE : 1,
		ATTRIBUTE_NODE : 2,
		TEXT_NODE : 3,
		CDATA_SECTION_NODE : 4,
		ENTITY_REFERENCE_NODE : 5,
		ENTITY_NODE : 6,
		PROCESSING_INSTRUCTION_NODE : 7,
		COMMENT_NODE : 8,
		DOCUMENT_NODE : 9,
		DOCUMENT_TYPE_NODE : 10,
		DOCUMENT_FRAGMENT_NODE : 11,
		NOTATION_NODE : 12
    };
}

/**
 *  EventHover.js, extends Hover class, and is used specifically for Events
 *
 *  @author eli
 *  @since 146 (rewrite of 144 code in functions.js)
 */

EventHover.prototype = new Hover();

function EventHover() {
}

EventHover.getHover = function(elementId) {
    var hover = Hover.hoverMap[elementId];
    if (typeof hover == 'undefined') {
		return Hover.createHover(new EventHover(), elementId);
    }
    return hover;
}

EventHover.prototype.loadHook_afterLoad = function() {
    var contentObj = getElementByIdCS(this.eid + "_content");
    var pbSubsectionObj = DomUtil.findDescendantWithClassName(contentObj, 'pbSubsection');
    if (pbSubsectionObj) {
        var detailListObj = DomUtil.findDescendantWithClassName(pbSubsectionObj, 'detailList');

        // Increase width of hover if the contents are too big to fit inside
        if (Hover.getElementWidth(detailListObj) > Hover.getElementWidth(pbSubsectionObj)) {
            var diff = Hover.getElementWidth(detailListObj) - Hover.getElementWidth(pbSubsectionObj);
            contentObj.style.width = (Hover.getElementWidth(contentObj) + diff) + 'px';
            pbSubsectionObj.style.width = Hover.getElementWidth(detailListObj) + 'px';
        }
    }
}

EventHover.prototype.setXPos = function(x) {
	var minX = this.getMinX();
	var maxX = this.getMaxX();

    /** Implements a 75px wide 'safezone' where a hover will never appear so that it doesn't cover up the element
     *  If maxX is below this safezone position, then the maxX is adjusted to the left of the element,
     *  and if x is below the safezone line, then we move that as our mouse position. Then if x>maxX, we move x back down
     *  to the max so the hover doesn't go too far right or cover up our safezone.
     *  Also implements an expanding hover that will expand in case the contents do not fit inside the box. This will happen in
     *  the case of long character strings that dont break up into mulitple lines.
     *  This is used by Events.
     */
    var srcEleWidth = this.xObjRight-this.xObjLeft;
    var offset = (srcEleWidth < 75) ? srcEleWidth : 75;

    var maximum = Math.max(this.xObjLeft - minX, maxX + Hover.getElementWidth(this.ele) - this.xObjLeft - offset);
    if (maximum < 200) maximum = 200;

    if (Hover.getElementWidth(this.ele) > maximum) {
        var contentObj = getElementByIdCS(this.eid + "_content");
        var pbSubsectionObj = DomUtil.findDescendantWithClassName(contentObj, 'pbSubsection');
        // Skip over this code if there is no detail layout hover
        if (pbSubsectionObj) {
            var contentSubsectionDiff = Hover.getElementWidth(contentObj) - Hover.getElementWidth(pbSubsectionObj);
            var hoverContentDiff = Hover.getElementWidth(this.ele)-Hover.getElementWidth(contentObj);
            contentObj.style.width = (maximum - hoverContentDiff) + 'px';
            pbSubsectionObj.style.width = (Hover.getElementWidth(contentObj) - contentSubsectionDiff) + 'px';
            /* This prevents bad behavior on extremely small screens. If the hover did not get reduced to the correct width
             * it'll expand the contents to the size of the hover.
             */
            if (Hover.getElementWidth(this.ele) > maximum) {
                contentObj.style.width = (Hover.getElementWidth(this.ele)-hoverContentDiff) + 'px';
                pbSubsectionObj.style.width = (Hover.getElementWidth(contentObj) - contentSubsectionDiff) + 'px';
            }
            // recalculate maxX since width changed
            maxX = assureInt((Hover.documentBody && Hover.documentBody.clientWidth)? Hover.documentBody.clientWidth : window.innerWidth) +
                assureInt(window.pageXOffset || (Hover.documentBody? Hover.documentBody.scrollLeft : 0) || 0) - Hover.getElementWidth(this.ele);
        }
    }
    if (maxX < this.xObjLeft+offset) maxX = this.xObjLeft - Hover.getElementWidth(this.ele);
    if (x < this.xObjLeft+offset) x = this.xObjLeft+offset;

    if (x > maxX) x = Math.max(minX, maxX);
    this.xCoord = x;
}

/*
 * @author ldelascurain
 * @since 150
 *
 */

function FieldTreeController( container, rootNodeList, elementName, insertCurlyBangDelims, afterInsertCallback){

    this.tree = new FieldTree( rootNodeList );
    this.selects = [document.getElementById(FieldTreeConstants.SELECT_ID + "0")];
    this.elementName = elementName;
    this.insertCurlyBangDelims = insertCurlyBangDelims;
    this.container = container;
    this.afterInsertCallback = afterInsertCallback;

    var depthRE = /(\d+)$/;

    var self = this;

    this.handleSelectClick = function(e){
        var target = getEventTarget(getEvent(e));
        var match = depthRE.exec(target.name);
        if (!match){
            return;
        }
        var selectDepth = parseInt(match[1]);
        var selectedKey = target.options[target.selectedIndex].value;

        self.moveSelectionTo(selectDepth, selectedKey);
    }

    addEvent(this.container.firstChild.firstChild, 'change', this.handleSelectClick, false);
    this.hideSelects();
}

FieldTreeController.prototype.moveSelectionTo = function(selectDepth, selectedKey){
    //TODO:  fix for change to "none"
    if (!selectedKey){
        return;
    }

    if (selectDepth < this.tree.currentDepth){
        this.tree.ascend(selectDepth);
    }

    var selectedNode = this.tree.getNodeFromCurrentList(selectedKey);

    if (!(selectedNode.isLeaf)){
        this.buildSelect(this.tree.getChildren(selectedNode), selectDepth + 1);
        this.tree.descend(selectedNode.key);
        this.scrollRight();
    }
    else {
        // Add the Insert code
        var insertString = selectedNode.getLabelToInsert();
        this.buildInsert(insertString, selectDepth + 1);
        this.scrollRight();
    }
}

FieldTreeController.prototype.focusTop = function(){
    var topSelect = this.selects[0];
    if(topSelect){
        topSelect.focus();
    }
}

FieldTreeController.prototype.reset = function(){
    this.tree.ascend(0);
    this.selects = [document.getElementById(FieldTreeConstants.SELECT_ID + "0")];

    var topSelect = this.selects[0];
    if (topSelect.options.length > 0){
        topSelect.selectedIndex = 0;
        this.moveSelectionTo(0, topSelect.options[0].value);
    }
}

FieldTreeController.prototype.buildSelect = function( nodeList, index ){

    var map = [];
    for(var i=0;i<nodeList.length;i++){
        var nodeLabel = nodeList[i].isLeaf ? nodeList[i].labelName : (nodeList[i].labelName + " &gt;");
        map[i] = [nodeLabel, nodeList[i].key];
    }

    this.eraseSelects(index);

    var attributeMap = {"size":"9", "name":FieldTreeConstants.SELECT_ID + this.selects.length, "class":"fieldTreeSelect"};
    var selectHTML = Util.createDynamicSelect(attributeMap, map, false);

    var wrapper = document.createElement("TD");
    wrapper.id = "selectWrapper" + this.selects.length;
    wrapper.className = "selectWrapper";
    this.container.appendChild(wrapper);
    wrapper.innerHTML = selectHTML;
    wrapper.isDynamicSelect = true;
    this.selects.push(wrapper.firstChild);


    addEvent(this.selects[index], 'change', this.handleSelectClick, false);
}

FieldTreeController.prototype.buildInsert = function( insertString, index ){
    var insertInfoHTML = LC.getLabel("NewFormulaEditor", "SelectedInfo");
    var insertText = LC.getLabel("Buttons", "ins");
    var insertStringHTML = insertString;
    var insertButtonHTML = "<input type='button' id='fieldInsertButton' name='insertButton' class='btn' value='" + insertText + "' />";
    var self = this;

    if (this.selects[index]) {
        this.eraseSelects(index);
    }
    var insertBox = document.createElement("TD");
    this.container.appendChild(insertBox);
    insertBox.id = "insertBox";

    var wrapper = document.createElement("SPAN");
    wrapper.id = "insertWrapperInfo" + this.selects.length;
    insertBox.appendChild(wrapper);
    wrapper.innerHTML = insertInfoHTML;
    wrapper.isDynamicSelect = false;
    wrapper.className = "insertWrapperInfo";

    wrapper = document.createElement("SPAN");
    wrapper.id = "insertWrapperString" + this.selects.length;
    insertBox.appendChild(wrapper);
    wrapper.innerHTML = insertStringHTML;
    wrapper.isDynamicSelect = true;
    wrapper.className = "insertWrapperString";

    this.selects.push(wrapper.firstChild);

    wrapper = document.createElement("SPAN");
    wrapper.id = "insertWrapperButton" + this.selects.lenght;
    insertBox.appendChild(wrapper);
    wrapper.innerHTML = insertButtonHTML;
    wrapper.isDynamicSelect = false;
    wrapper.className = "insertWrapperButton";

    addEvent(document.getElementById("fieldInsertButton"), "click", function(){self.insertCode(self.elementName, insertString, self.insertCurlyBangDelims);}, false);

}

FieldTreeController.prototype.eraseSelects = function(index){
    var i = this.selects.length;
    while (i > index ){
        i--;
        var element = this.selects[i].parentNode;
        while (element.nodeName != "TD"){
            element = element.parentNode;
        }
        this.container.removeChild(element);
        this.selects.pop();
    }
}

FieldTreeController.prototype.insertCode = function(elementName, insertString, insertCurlyBangDelims){
    FormulaEditor.insertCode( elementName, insertString, insertCurlyBangDelims);
    if (this.afterInsertCallback){
        this.afterInsertCallback();
    }
}

FieldTreeController.prototype.scrollRight = function(){
    this.container.scrollLeft = this.container.scrollWidth;
}

FieldTreeController.prototype.showSelects = function(){
    for (var i=0; i< this.selects.length; i++ ){
        this.selects[i].style.display = "inline";
    }
}

FieldTreeController.prototype.hideSelects = function(){
    for (var i=0; i< this.selects.length; i++ ){
        var element = this.selects[i];
        if (element!=null && element.nodeName == "SELECT"){
            element.style.display = "none";
        }
    }
}

/**
 * Javascript object and controls for the inline date picker
 * @author jmooney
 * @since 148
 */
function DatePicker() {
    this.today = DateUtil.getDateTimeFromUserLocale(UserContext.today);
    this.calendarDiv = document.getElementById(DatePickerIds.DOM_ID);
    this.shim = new iframeShim(this.calendarDiv);
    this.table = document.getElementById(DatePickerIds.TABLE_ID);
    this.monthPicker = document.getElementById(DatePickerIds.MONTH_PICKER);
    this.yearPicker = document.getElementById(DatePickerIds.YEAR_PICKER);
    this.callOnChange = false;
    this.myField = null;
    this.currentDate = null;
    this.hasTime = false;
    this.addedFields = {};
    var self = this;
    addEvent(document, "click", function() { self.hide(); }, false);
    addEvent(this.calendarDiv, "click", function(e) { self.cancelHide(e); }, false);
    addEvent(this.monthPicker, "change", function() { self.generateMonth(); }, false);
    addEvent(this.yearPicker, "change", function() { self.generateMonth(); }, false);
}

/**
 * sets the month and year to the passed in date. If the year is not in the dropdown already
 * it will add it in the correct place.
 */
DatePicker.prototype.setMyDate = function(date) {
    var found = false;
    for (var i = 0; i < this.yearPicker.options.length; i++) {
        var year = parseInt(this.yearPicker[i].value);
        if (year > date.getFullYear()) {
            // need to insert the year
            Util.insertOption(this.yearPicker, new Option(date.getFullYear(), date.getFullYear(), false, true), i);
            found = true;
            break;
        } else if (parseInt(this.yearPicker[i].value) == date.getFullYear()) {
            // found it
            this.yearPicker.selectedIndex = i;
            found = true;
            break;
        }
    }
    if (!found) {
        // need to insert the year at the end
        this.yearPicker.options[this.yearPicker.options.length] = new Option(date.getFullYear(), date.getFullYear(), false, true);
    }

    this.monthPicker.selectedIndex = date.getMonth();
}

/**
 * view the previous month
 */
DatePicker.prototype.prevMonth = function() {
    if (this.monthPicker.selectedIndex == 0) {
        var prevYear = parseInt(this.yearPicker[this.yearPicker.selectedIndex].value) - 1;
        if (this.yearPicker.selectedIndex == 0 || prevYear != this.yearPicker[this.yearPicker.selectedIndex - 1].value) {
            var d = new Date();
            d.setFullYear(prevYear);
            d.setMonth(11);
            this.setMyDate(d);
        } else {
            this.yearPicker.selectedIndex--;
            this.monthPicker.selectedIndex = 11;
        }
    } else {
        this.monthPicker.selectedIndex--;
    }
    this.generateMonth();
}

/**
 * view the next month
 */
DatePicker.prototype.nextMonth = function() {
    if (this.monthPicker.selectedIndex == 11) {
        var nextYear = parseInt(this.yearPicker[this.yearPicker.selectedIndex].value) + 1;
        if (this.yearPicker.selectedIndex == this.yearPicker.length - 1 || nextYear != this.yearPicker[this.yearPicker.selectedIndex + 1].value) {
            var d = new Date();
            d.setFullYear(nextYear);
            d.setMonth(0);
            this.setMyDate(d);
        } else {
             this.yearPicker.selectedIndex++;
             this.monthPicker.selectedIndex = 0;
        }
    } else {
        this.monthPicker.selectedIndex++;
    }
    this.generateMonth();
}

/**
 * generates the days for the current selected month.
 */
DatePicker.prototype.generateMonth = function() {
    var month = parseInt(this.monthPicker[this.monthPicker.selectedIndex].value);
    var d = new Date();
    d.setDate(1);
    var year = parseInt(this.yearPicker[this.yearPicker.selectedIndex].value);
    d.setFullYear(year);
    d.setMonth(month);
    // java days are indexed from 1-7, javascript 0-6
    var startPoint = UserContext.startOfWeek - d.getDay();
    if (startPoint > 1) startPoint -= 7;
    d.setDate(startPoint);
    document.getElementById("calRow5").style.display = "";
    document.getElementById("calRow6").style.display = "";
    var dayCells = this.table.getElementsByTagName("td");
    for (var i = 0; i < dayCells.length; i++) {
        var dayOfWeek = d.getDay();
        var week = i / 7;
        var clazz;
        if (dayOfWeek == 0 || dayOfWeek == 6) {
            clazz = "weekend";
        } else {
            clazz = "weekday";
        }
        if (d.getMonth() == month - 1 || d.getFullYear() == year - 1) {
            clazz += " prevMonth"
        } else if (d.getMonth() == month + 1 || d.getFullYear() == year + 1) {
            if (i % 7 == 0) {
                // done, hide the remaining rows.
                document.getElementById("calRow6").style.display = "none";
                if (i / 7 == 4) {
                     document.getElementById("calRow5").style.display = "none";
                }
                break;
            }
            clazz += " nextMonth"
        }
        if (DateUtil.equals(d, this.today)) {
            clazz += " todayDate";
        }
        if (DateUtil.equals(d, this.currentDate)) {
            clazz += " selectedDate"
        }
        dayCells[i].className = clazz;
        dayCells[i].innerHTML = d.getDate();
        d.setDate(d.getDate() + 1);
    }
}

DatePicker.prototype.selectDate = function(value) {
    var selectedDate;
    if ("today" == value) {
        this.currentDate = this.today;
        selectedDate = this.hasTime ? DateUtil.getDateTimeStringFromUserLocale(this.today) : DateUtil.getDateStringFromUserLocale(this.today);
        this.setMyDate(this.today);
        this.generateMonth();
    } else {
        var d = new Date(this.today.getTime());
        // set the date to the 1st to avoid month wrapping
        d.setDate(1);
        d.setFullYear(parseInt(this.yearPicker[this.yearPicker.selectedIndex].value));
        var month = parseInt(this.monthPicker[this.monthPicker.selectedIndex].value);
        if (value.className.indexOf("nextMonth") != -1) {
            month++;
        } else if (value.className.indexOf("prevMonth") != -1) {
            month--;
        } 
        d.setMonth(month);
        d.setDate(value.innerHTML);
        selectedDate = this.hasTime ? DateUtil.getDateTimeStringFromUserLocale(d) : DateUtil.getDateStringFromUserLocale(d);
        this.hide();
    }
    DatePicker.insertDate(selectedDate, this.myField.id, this.callOnChange);
}

DatePicker.prototype.position = function() {
    // have to position the calendar relative to its relatively positioned parent (the body div)
    var x = 0;
    var y = 0;
    var element = this.myField;
    while (element != null && element != this.calendarDiv.offsetParent) {
        x += element.offsetLeft;
        y += element.offsetTop;
        element = element.offsetParent;
    }
    // to check the picker isn't off the bottom of the page we need to calculate the position relative to the top of the page
    if (getObjY(this.myField) + this.calendarDiv.offsetHeight > getScrollY() + getWindowHeight()) {
        y -= this.calendarDiv.offsetHeight;
    } else {
        y += this.myField.offsetHeight;
    }
    this.shim.setStyle("left", x + "px");
    this.shim.setStyle("top", y + "px");
}

DatePicker.prototype.show = function(callOnChange, myField, hasTime) {
    this.callOnChange = callOnChange;
    this.hasTime = hasTime;
    this.myField = getElementByIdCS(myField);
    var self = this;
    if (this.myField.type != "hidden") {
        if (this.addedFields[myField] !== this.myField) {
            // gotta detect if field was deleted and replaced by a new one but still has the same dom id
            // remove the reference to the deleted field and re-add the events.
            this.addedFields[myField] = null;
        }
        if (!this.addedFields[myField]) {
            this.addedFields[myField] = this.myField;
            addEvent(this.myField, "click", function(e) { self.cancelHide(e); }, false);
            addEvent(this.myField, "keydown", function(e) { self.handleKeyPress(e); }, false);
            if (!hasTime && LC.isEnglishUS()) {
                addEvent(this.myField, "blur", function() { DateUtil.evaluateShortcut(self.myField, self.callOnChange); }, false);
            }
        }
    }

    // try to parse the date in the field, with or without time
    var time = hasTime ? DateUtil.getDateFromFormat(this.myField.value, UserContext.dateTimeFormat) :
                         DateUtil.getDateFromFormat(this.myField.value, UserContext.dateFormat);
    if (time != 0) {
        // parse successful
        this.currentDate = new Date(time);
        this.setMyDate(this.currentDate);
    } else {
        // couldn't parse, use today's date
        this.setMyDate(this.today);
    }
    this.generateMonth();

    this.shim.setStyle("display", "block");
    this.position();
}

DatePicker.prototype.cancelHide = function(e) {
    eventCancelBubble(e);
    return false;
}

DatePicker.prototype.handleKeyPress = function(e) {
    var key = getEvent(e).keyCode;
    if (key == KEY_ESC || key == KEY_TAB) {
        this.hide();
    } else if (key == KEY_ENTER && !this.hasTime && LC.isEnglishUS()) {
        DateUtil.evaluateShortcut(this.myField);
    }
}

DatePicker.prototype.hide = function() {
    this.shim.setStyle("display", "none");
}
// static date picker object, lazily create 1 per page
DatePicker.datePicker = null;

DatePicker.pickDate = function(callOnChange, field, hasTime) {
    if (!DatePicker.datePicker) {
    	DatePicker.datePicker = new DatePicker();
    }
    DatePicker.datePicker.show(callOnChange, field, hasTime);
}

DatePicker.insertDate = function(value, field, callOnChange) {
    var element = getElementByIdCS(field);
    if (!element.disabled && element.value != value) {
        element.value = value;
        if (callOnChange && element.onchange) {
            element.onchange();
        }
    }
}

function ActionOverrideUi() {}

ActionOverrideUi.prototype.visiblePicklist = null;

ActionOverrideUi.prototype.setVisiblePicklist = function(toDisplay) {
    if (ActionOverrideUi.prototype.visiblePicklist != null) {
        document.getElementById(ActionOverrideUi.prototype.visiblePicklist).className = 'hidden';
    }
    document.getElementById(toDisplay).className = null;
    ActionOverrideUi.prototype.visiblePicklist = toDisplay;
}


function ManageableInfoElement(dhtml_id){
	if (!dhtml_id) return;
	
	var outer = document.getElementById(dhtml_id);
	if (!outer) return;

	var elements = getElementsByClassName(ManageableInfo.MORE_INFO_CLASS, outer, "div");

	if (!elements || elements.length < 1) return;
	var moreInfo = elements[0];
	
	var self = this;
	this.isHidden = true;
	
	this.onLinkClickHandler = function(){
		if (self.isHidden){
			moreInfo.style.display = 'block';
			self.link.innerHTML = LC.getLabel("ManageableInfo", "LessInfo");
			self.isHidden = false;
		} else {
			moreInfo.style.display = 'none';
			self.link.innerHTML = LC.getLabel("ManageableInfo", "MoreInfo");
			self.isHidden = true;
		}
	}

	
	var linkArr = outer.getElementsByTagName("a");
	if (linkArr && linkArr.length > 0){
		this.link = linkArr[0]
		addEvent(this.link, 'click', this.onLinkClickHandler, false);
	}
}
/*
 * Copyright, 1999-2006, SALESFORCE.com
 * All Rights Reserved
 * Company Confidential
 *
 * JavaScript Utilities. 
 * REQUIRES: DOJO-0.4.1.
 * WILL BE REMOVED - WILL TRY TO UTILIZE EXISTING FUNCTIONS IF AVAILABLE
 * @author dkothule
 * @since 148
 */

/**
 * Modify date object to parse ISO8601 format:
 */
Date.prototype.setISO8601 = function (string) {
    var regexp = "([0-9]{4})(-([0-9]{2})(-([0-9]{2})" +
        "(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?" +
        "(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?";
    var d = string.match(new RegExp(regexp));

    var offset = 0;
    var date = new Date(d[1], 0, 1);

    if (d[3]) { date.setMonth(d[3] - 1); }
    if (d[5]) { date.setDate(d[5]); }
    if (d[7]) { date.setHours(d[7]); }
    if (d[8]) { date.setMinutes(d[8]); }
    if (d[10]) { date.setSeconds(d[10]); }
    if (d[12]) { date.setMilliseconds(Number("0." + d[12]) * 1000); }
    if (d[14]) {
        offset = (Number(d[16]) * 60) + Number(d[17]);
        offset *= ((d[15] == '-') ? 1 : -1);
    }

    offset -= date.getTimezoneOffset();
    time = (Number(date) + (offset * 60 * 1000));
    this.setTime(Number(time));
}
Date.prototype.toISO8601String = function (format, offset) {
    /* accepted values for the format [1-6]:
     1 Year:
       YYYY (eg 1997)
     2 Year and month:
       YYYY-MM (eg 1997-07)
     3 Complete date:
       YYYY-MM-DD (eg 1997-07-16)
     4 Complete date plus hours and minutes:
       YYYY-MM-DDThh:mmTZD (eg 1997-07-16T19:20+01:00)
     5 Complete date plus hours, minutes and seconds:
       YYYY-MM-DDThh:mm:ssTZD (eg 1997-07-16T19:20:30+01:00)
     6 Complete date plus hours, minutes, seconds and a decimal
       fraction of a second
       YYYY-MM-DDThh:mm:ss.sTZD (eg 1997-07-16T19:20:30.45+01:00)
       
       offset indicates the timezone which in the form +HH:MM or -HH:MM
    */
    if (!format) { var format = 6; }
    if (!offset) {
        var offset = 'Z';
        var date = this;
    } else {
        var d = offset.match(/([-+])([0-9]{2}):([0-9]{2})/);
        var offsetnum = (Number(d[2]) * 60) + Number(d[3]);
        offsetnum *= ((d[1] == '-') ? -1 : 1);
        var date = new Date(Number(Number(this) + (offsetnum * 60000)));
    }

    var zeropad = function (num) { return ((num < 10) ? '0' : '') + num; }

    var str = "";
    str += date.getUTCFullYear();
    if (format > 1) { str += "-" + zeropad(date.getUTCMonth() + 1); }
    if (format > 2) { str += "-" + zeropad(date.getUTCDate()); }
    if (format > 3) {
        str += "T" + zeropad(date.getUTCHours()) +
               ":" + zeropad(date.getUTCMinutes());
    }
    if (format > 5) {
        var secs = Number(date.getUTCSeconds() + "." +
                   ((date.getUTCMilliseconds() < 100) ? '0' : '') +
                   zeropad(date.getUTCMilliseconds()));
        str += ":" + zeropad(secs);
    } else if (format > 4) { str += ":" + zeropad(date.getUTCSeconds()); }

    if (format > 3) { str += offset; }
    return str;
}
/**
 * JSUtils: Utility functions.
 */
var JSUtils = {
		/** 
		 * getTiomeZoneString - returns the current timezone string:
		 */
		getTimeZoneString: function(){
			var str = new Date().toString();
			var pos = str.lastIndexOf("(");
			if(pos != -1){
				str = str.substring(pos + 1);
				pos = str.indexOf(")");
				str = pos != -1? str.substring(0, pos) : str;
			}else{
				var words = str.split(' ');
				if(words != null && words.length == 6){
					str = words[words.length - 2];
				}else{
					str = "Local Timezone";
				}
			}
			return str;
		},
		
		/**
		 * Converts the ISO8601 string into a javascript date object.
		 */
		iso8601StringToDate: function(iso8601String){
			var date = new Date(); 
			date.setISO8601(iso8601String);
			return date;
		},
		
		/**
		 * Formats the timestamp in javascript date object to displayable string.
		 */
		formatTimestamp: function(timestamp) {
			if(timestamp == null){
				return "";
			}
			var strTimestamp = "";
			num = timestamp.getMonth() + 1;
			strTimestamp += " " + (num >= 10? "" : "0") + num;
			num = timestamp.getDate();
			strTimestamp += "/" + (num>= 10? "" : "0") + num;
			num = timestamp.getFullYear();
			strTimestamp += "/" + num;
			num = timestamp.getHours();
			strTimestamp += " " + (num>= 10? "" : "0") + num;
			num = timestamp.getMinutes();
			strTimestamp += ":" + (num>= 10? "" : "0") + num;
			num = timestamp.getSeconds();
			strTimestamp += "." + (num>= 10? "" : "0") + num;
			return strTimestamp;
		},
		
		/**
		 * Format numbers to add comma separator.
		 * @param {int} numbers
		 */
		formatNumbers: function (numbers){
			var nStr =  numbers + '';
			x = nStr.split('.');
			x1 = x[0];
			x2 = x.length > 1 ? '.' + x[1] : '';
			var rgx = /(\d+)(\d{3})/;
			while (rgx.test(x1)) {
				x1 = x1.replace(rgx, '$1' + ',' + '$2');
			}
			return x1 + x2;	
		}, /*::Method:formatNumbers*/
		
		/**
		 * Compare two strings.
		 * @param {string} str1 First string
		 * @param {string} str2 Second string.
		 * @type {int}
		 */
		strCompare: function(str1, str2){
			if(str1 > str2){
				return 1;
			}else if(str1 < str2){
				return -1;
			}else{
				return 0;
			}
		},/*::Method::strCompare*/
		
		/**
		 * Trim whitespace from 'str'. If 'wh' > 0,
		 * only trim from start, if 'wh' < 0, only trim
		 * from end, otherwise trim both ends
		 */
		trim: function(str, wh){
			if(!str.replace){ return str; }
			if(!str.length){ return str; }
			var re = (wh > 0) ? (/^\s+/) : (wh < 0) ? (/\s+$/) : (/^\s+|\s+$/g);
			return str.replace(re, "");
		}, /*::Method::trim*/
		
		/**
		 * Trim whitespaces at begining.
		 */
		ltrim: function(str){
			return JSUtils.trim(str, 1);
		}, /*::Method::ltrim*/
		
		/**
		 * Trim whitespace at the end.
		 */
		rtrim: function(str){
			return JSUtils.trim(str, -1);
		},/*::Method::rtrim*/
		
		unescapeChar: function(str, c) {
			var cur = new String;
			var inEscape = false;
			
			for (var i = 0; i < str.length; i++) {
				var ch = str.charAt(i);
				if (inEscape) { // escaped character
					cur = cur + ch;
					inEscape = false;
				} else if (ch == c) { // escaping
					inEscape = true;
				} else {
					cur = cur + ch;
				}
			}
			if (inEscape) return null;
			return cur;
		},/*::Method::unescapeChar*/
		
		
		splitWithQuoteAndEsc: function(str, split) {
	        var result = [];
	        var cur = new String;
	        var inQuote = false;
	        var inEscape = false;
	
	        for (var i = 0; i < str.length; i++) {
	            var ch = str.charAt(i);
	            if (inEscape) {  // escaped character
	                switch (ch) {
	                case 'n':
	                    cur = cur + '\n'; break;
	                case 'r':
	                    cur = cur + '\r'; break;
	                case 't':
	                    cur = cur + '\t'; break;
	                default:
	                    cur = cur + ch;
	                }
	                inEscape = false;
	            } else if (ch == '\\') {  // Escaping
	                inEscape = true;
	            } else if (ch == '\"') {  // Quoting
	                inQuote = !inQuote;
	            } else if (!inQuote && ch == split) {  // A delimiter
	                result.push(cur);
	                cur = new String;
	            } else {  // normal
	                cur = cur + ch;
	            }
	        }
	        if (inEscape || inQuote) {
	            return null;
	        }
	        result.push(cur);
	        return result;
		},/*::Method::splitWithQuoteAndEsc*/
		
		KEY_DEL 		: 46,
		KEY_ESC			: 27,
		KEY_BACKSPACE	: 8,
		KEY_ENTER		: 13, //0x0D
		/**
		 * Returns the event key code
		 */
		getEventKeyCode: function(e){
			var Esc = (window.event) ? 27 : e.DOM_VK_ESCAPE // MSIE : Firefox
			var keyChar = (window.event)? event.keyCode : e.keyCode;
			return (keyChar);
		},		
		
		/**
		 * Checks if the given event is an ESC key event or not.
		 * @param {event} e Event to be checked for.
		 */
		isESCKeyEvent: function (e){
			var Esc = (window.event) ? 27 : e.DOM_VK_ESCAPE // MSIE : Firefox
			var keyChar = (window.event)? event.keyCode : e.keyCode;
			return (keyChar == Esc);
		}, /*::Method::isESCKeyEvent*/
		
		/**
		 * Checks if the given event key contains a valid ASCII character or not.
		 * @param {event} e Event to be checked for.
		 */
		isASCIIKeyEvent: function(e){
			var Esc = (window.event) ? 27 : e.DOM_VK_ESCAPE // MSIE : Firefox
			var keyChar = (window.event)? event.keyCode : e.keyCode;
			return (keyChar >= 32 && keyChar <= 126);
		},
		
		/**
		 * Return the element from document with given id.
		 * @param {string} or {object} elementId to be searched for
		 * @return element if the argument is of type string with id of element;
		 *   		otherwise returns the argument itself;
		 * @type {object}
		 */
		getById: function(elementId){
			if(elementId && (typeof elementId == "string" || elementId instanceof String)){
				return document.getElementById(elementId);
			}else{
				return elementId;
			}
		}, /*::Method::getById*/
		
		/**
		 * Return the first matching element from document where the name 
		 * matches the elementName.
		 * @param {string} elementName Name of the element to search.
		 * @return element object if found; otherwise null;
		 * @type {object} 
		 */
		getByName: function(elementName){
			if(elementName == null) { return null;}
			if(typeof(elementName) == 'string'){
				var el = document.getElementsByName(elementName);
				if(el != null && el.length > 0){
					return el[0];
				}else{
					return null;
				}
			}else{
				return elementName;
			}
		}, /*::Method::getByName*/
		
		/**
		 * clearList
		 * Clears each list box that was passed to this method.
		 * Variable arguments can be passed with either list element or 
		 * an id of the list element (SELECT).
		 * Example: clearList(firstListObj, 'secondListId', thirdListObj).
		 * @param {...} args list1, list2...listn
		 */
		clearList: function() {
			for (var i = 0; i < arguments.length; i++) {
		    	var element = JSUtils.getById(arguments[i]);
		        if (element && element.options) {
		            element.options.length = 0;
		            element.selectedIndex = -1;
		        }
		    }
		}, /*::Method::clearList*/

		/**
		 * Clears the list from given index.
		 * @param list List Object
		 * @param fromIndex index to start removing items from (inclusive)
		 */
		clearListFrom: function(list, fromIndex){
			if(fromIndex == null || fromIndex <= 0){
				JSUtils.clearList(list);
				return;
			}
	    	var element = JSUtils.getById(list);
	        if (element && element.options && element.options.length > fromIndex && fromIndex > 0) {
	        	element.options.length = fromIndex;
	            element.selectedIndex = fromIndex - 1;
	        }
		},
		
		/**
		 * Populate the list box with provided data. 
		 * @param {array} data The multi-dimensional array [][2], 
		 *  with first item of an array item as label and second as value. 
		 *  Example: [['California', 'CA'],['Pittsburgh', 'PA']]
		 *	or		 [{label:'California', value:'CA'}, {label:'Pittsburgh', value:'PA'}]
		 *  or 		 ['California', 'Pittsburgh', 'New York']
		 *  or		 [10, 20, 30, 40, 50]
		 */
		setListData: function(listId, data){
			var list = JSUtils.getById(listId);
			if(list && data && data.length && data.length > 0){
				var text, value, item;
				for(var i=0; i < data.length; i++){
					item = data[i];
					if(item){
						//name, label pair
						//name, value pair
						//label, value pair
						//name list
						//number list
						//multi dimentional
						if(item.name != null && item.label != null){
							text = item.label;
							value = item.name;
						}else if(item.name != null && item.value != null){
							text = item.name;
							value = item.value;
						}else if(item.label != null && item.value != null){
							text = item.label;
							value = item.value;
						}else if(typeof(item) == 'string' || typeof(item) == 'number'){
							text = item.toString();
							value = text;
						}else{
							text = item.length > 0? item[0] : item.toString();
							value = item.length > 1? item[1] : text;
						}
						list.options[list.length] 
								= JSUtils.newOption(text, value);
					}
				}
			}
		}, /*::Method::setListData*/
		
		/**
		 * Removes the selected option(s) from the list box. 
		 * An optional boolean flag removeAll can be passed with value "true"
		 * to remove all selected options if it is multi-select list;
		 * otherwise by default it removes only one element.
		 * @param {object} list The List Element
		 * @param {boolean} removeAll Indicates if multiple option removal.
		 */
		removeSelectedOption: function(list, removeAll){
			list = JSUtils.getById(list);
			var idx = list? list.selectedIndex : -1;
			while(idx != -1){
				list.options[idx] = null;
				if(removeAll != null && removeAll == true){
					idx = list.selectedIndex;
				}else{
					return;
				}
			}
		}, /*::Method::removeSelectedOption*/
		
		/**
		 * Select the given option in given list.
		 * @param {object} list The list to select
		 * @param {object} option Option to select in in (text:, value:} format.
		 * @return Selects and returns the index of selected option if found;
		 *	  		otherwise -1;
		 * @type {int}
		 */
		selectOption: function(list, option){
			list = JSUtils.getById(list);
			if(list){
				var idx = JSUtils.indexOfOption(list, option);
				if(idx != -1){
					list.options[idx].selected = true;
					return idx;
				}
			}
			return -1;
		}, /*::Method::selectOption*/
		
		/**
		 * Unselect the given option in given list.
		 * @param {object} list The list object
		 * @param {object} option Option to unselect in in (text:, value:} format.
		 * @return Unselects and returns the index of option if found;
		 *	  		otherwise -1;
		 * @type {int}
		 */
		unselectOption: function(list, option){
			list = JSUtils.getById(list);
			if(list){
				var idx = JSUtils.indexOfOption(list, option);
				if(idx != -1){
					list.options[idx].selected = false;
					return idx;
				}
			}
			return -1;
		}, /*::Method::unselectOption*/
		
		/**
		 * Selects all the option in given list.
		 * @param {object} list The list object.
		 */
		selectAll: function(list){
			list = JSUtils.getById(list);
			if(list && list.options && list.options.length > 0){
				for(var i=0; i < list.options.length; i++){
					list.options[i].selected = true;
				}
			}
		}, /*::Method::selectAll*/
		
		/**
		 * Unselects all the option in given list.
		 * @param {object} list The list object.
		 */
		unselectAll: function(list){
			list = JSUtils.getById(list);
			if(list && list.options && list.options.length > 0){
				for(var i=0; i < list.options.length; i++){
					list.options[i].selected = false;
				}
			}
		}, /*::Method::unselectAll*/
		
		/**
		 * appendOption()
		 * Adds a new option to the list.
		 * @param {object} list The list object
		 * @param {string} optText The display text for the option.
		 * @param {string} optValue The actual value of the option.
		 * @return Returns the newly created option.
		 * @type {object}
		 */
		appendOption: function (list, optText, optValue){
			list = JSUtils.getById(list);
			if(!list){ return null;}
			var newOpt = JSUtils.newOption(optText, optValue);	
			list.options[list.length] = newOpt;
			return list.options[list.length-1];
		}, /*::Method::appendOption*/
		
		/**
		 * newOption()
		 * Creates a new option with given text and value.
		 * @param {string} optText The display text.
		 * @param {string} optValue The value of the option.
		 * @return Returns newly created option.
		 * @type {object}
		 */
		newOption: function(optText, optValue) {
			var newOpt = document.createElement("OPTION");
			newOpt.text = optText;
			newOpt.value = optValue;
			return newOpt;
		}, /*::Method::newOption*/
		
		/**
		 * setSelectedIndex
		 * @param {object} list The SELECT object
		 * @param {int} index Index of new selection.
		 */
		setSelectedIndex: function(list, index){
			list = JSUtils.getById(list);
			if(list && list.options && list.options.length > index){
				list.options[index].selected = true;
			}
		}, /*::Method::setSelectedIndex*/
		
		/**
		 * Returns the index of selected option in the list.
		 * @return selected index if selected; otherwise -1
		 * @type {int}
		 */
		getSelectedIndex: function(list){
			list = JSUtils.getById(list);
			if(list){
				return list.selectedIndex;
			}
			return -1;
		},  /*::Method::getSelectedIndex*/
		
		/**
		 * Returns the selected option in the list.
		 * @return selected option if selected; otherwise null.
		 * @type {object}
		 */
		getSelectedOption: function(list){
			list = JSUtils.getById(list);
			if(list && list.options && list.selectedIndex >=0 ){
				return list.options[list.selectedIndex];
			}
			return null;
		}, /*::Method::getSelectedOption*/
		
		/**
		 * Returns the selected option value in the list.
		 * @return selected option value if selected; otherwise null.
		 * @type {string}
		 */
		getSelectedValue: function(list){
			var opt = JSUtils.getSelectedOption(list);
			return opt == null? null : opt.value;
		}, /*::Method::getSelectedValue*/
		
		/**
		 * Returns the selected option text in the list.
		 * @return selected option text if selected; otherwise null.
		 * @type {string}
		 */
		getSelectedText: function(list){
			var opt = JSUtils.getSelectedOption(list);
			return opt == null? null : opt.text;
		}, /*::Method::getSelectedValue*/
		
		/**
		 * Returns the index of given option in the list.
		 * Example: JSUtils.indexOfOption(list, {value:"USA"})
		 * @return index of option if found; otherwise -1;
		 * @type {int}
		 */
		indexOfOption: function(list, option){
			if(option == null) {return -1; }
			var check;
			if(option.text == null && option.value != null){
				check = 0; //value only
			}else if(option.text != null && option.value == null){
				check = 1; //text only
			}else if(option.text != null && option.value != null){
				check = 2; //text and value
			}else {
				return -1; //invalid
			}
			list = JSUtils.getById(list);
			if(list && list.options && list.options.length > 0){
				for(var i=0; i < list.options.length; i++){
					switch(check){
						case 0:
							if(list.options[i].value == option.value){
								return i;
							}
							break;
						case 1:
							if(list.options[i].text == option.text){
								return i;
							}
							break;
						case 2:
							if(	list.options[i].value == option.value &&
								list.options[i].text == option.text){
								return i;
							}
							break;
					}
				}
			}
			return -1;
		},/*::Method::indexOfOption*/

		/**
		 * Set the innerHTML for the give object ID.
		 */
		setInnerHTML: function(objectId, value){
			var obj = JSUtils.getById(objectId);
			if(obj != null){
				obj.innerHTML = value;
			}
		},

		/**
		 * Gets the innerHTML for the give object ID.
		 */
		getInnerHTML: function(objectId){
			var obj = JSUtils.getById(objectId);
			return obj == null? null : obj.innerHTML;
		},
		
		/**
		 * Set the "value" attribute of the given element with given text.
		 * @param {object} input The input element.
		 * @value {string} value The text value.
		 */
		setTextValue: function(input, value){
			input = JSUtils.getById(input);
			if(input){
				try{input.value = value == null? "" : value;}catch(e){}
			}
		}, /*::Method::setTextValue*/
		
		/**
		 * Get the text value of "value" attribute from given object.
		 * @param {object} input The input element.
		 * @return Returns the text value of "value" attribute; otherwise null.
		 * @type {string}
		 */
		getTextValue: function(input){
			input = JSUtils.getById(input);
			return input? input.value : null;
		}, /*::Method::getTextValue*/
		
		/**
		 * Set the "style" attribute with given text. 
		 * Example: JSUtils.setStyleText("nodeId", "background-color:#FFFFFF;padding:5px");
		 */
		setStyleText: function(node, styleText){
			node = JSUtils.getById(node);
			try {
	 			node.style.cssText = styleText;
			} catch (e) {
				node.setAttribute("style", styleText);
			}
		},
		
		/**
		 * Set the value of checkbox.
		 * @param {object} checkbox The checkbox element.
		 * @param {boolean} check The new value of checkbox [true | false]
		 */
		setCheckbox: function(checkbox, check){
			checkbox = JSUtils.getById(checkbox);
			if(checkbox){
				checkbox.checked = check;
			}
		}, /*::Method::setCheckbox*/
		
		/**
		 * Determines if the checkbox is checked or not.
		 * @param {object} checkbox The checkbox element
		 * @return Returns true if the checkbox is checked; false otherwise.
		 * @type {boolean}
		 */
		isCheckboxChecked: function(checkbox){
			checkbox = JSUtils.getById(checkbox);
			return (checkbox && checkbox.checked);
		}, /*::Method::isCheckboxChecked*/
		
		/**
		 * Sets the focus on given element.
		 * @param {object} element The element to focus on.
		 */
		setFocus: function(element){
			element = JSUtils.getById(element);
			if(element){
				try{ element.focus(); }catch(e){}
			}
		}, /*::Method::setFocus*/
		
		/**
		 * Show (unhide) the given element.
		 * @param {object} element The element to make visible.
		 */
		show: function(){
			for (var i = 0; i < arguments.length; i++) {
				try{ dojo.style.show(arguments[i]); }catch(e){}
			}
		}, /*::Method::showObject*/
		
		/**
		 * Extended version of Show (unhide) the given element.
		 * This is used where dojo version (show) is not useful.
		 * @param {object} element The element to make visible.
		 */
		showEx: function(){
			var obj;
			for (var i = 0; i < arguments.length; i++) {
				obj = JSUtils.getById(arguments[i]);
				if(obj){
					try{
						obj.style.display = "";
						obj.style.visibility ="visible";
					}catch(e){}
				}
			}
		}, /*::Method::showObject*/
		
		/**
		 * Hides an element.
		 * @param {object} element An element to hide.
		 */
		hide: function(){
			for (var i = 0; i < arguments.length; i++) {
				try{ dojo.style.hide(arguments[i]); }catch(e){ dojo.debug(e);}
			}
		}, /*::Method::hide*/
		
		/**
		 * Hides an element.
		 * This is used where dojo version (hide) is not useful.
		 * @param {object} element An element to hide.
		 */
		hideEx: function(){
			var obj;
			for (var i = 0; i < arguments.length; i++) {
				obj = JSUtils.getById(arguments[i]);
				if(obj){
					try{
						obj.style.display = "none";
						obj.style.visibility = "hidden";
					}catch(e){}
				}
			}
		},/*::Method::hide*/
		
		/**
		 * Set the tab index for given elements in specified order.
		 * @param elements is an array of {name: "xxx", tabindex: xx}
		 */
		setTabIndex: function(elements){
			if(elements && elements.length && elements.length > 0){
				var el;
				for(var i=0; i < elements.length; i++){
					try{
						el = JSUtils.getById(elements[i].name);
						if(el) el.setAttribute("tabindex", elements[i].tabindex);
					}catch(e){
						dojo.debug(e);
					}
				}
			}
		},/*::Method:setTabIndex*/
		
		reloadPage: function(){
			try{
				window.location.reload();
			}catch(e){
				window.location.href=window.location.href
			}
		},/*::reloadPage():*/
		
		/**
		 * @param arg = {id, label, title, visible, onclick}
		 */
		setButtonProperties: function(){
			var btnVal, btnTitle;
			for (var i = 0; i < arguments.length; i++) {
				var btnProp = arguments[i];
				if(btnProp != null){
					if(btnProp.id){
						var btnObj = JSUtils.getById(btnProp.id);
						if(btnObj){
							btnVal = btnProp.label;
							btnTitle = btnProp.title? btnProp.title : btnVal;
							if(btnVal){
								btnObj.value = btnVal;
							}
							if(btnTitle){
								btnObj.title = btnTitle;
							}
							if(btnProp.onclick){
								btnObj.onclick = btnProp.onclick;
							}
							if(btnProp.visible){
								JSUtils.show(btnObj);
							}else{
								JSUtils.hide(btnObj);
							}
						}
					}
				}
			}
		}/*::setButtonProperties():*/
};/*::Class::JSUtils*/	

var PortalStyleConfigEditor = {
    themes: {},
    themeLoader: null,
    previewUrls: {},
    inputs: []
};

PortalStyleConfigEditor.updateAllPreviews = function () {
    for (key in this.previewUrls) {
        this.updatePreview(this.previewUrls[key], key);
    }
};


PortalStyleConfigEditor.ThemeLoader = function (id) {
    this.id = id;
    this.select = document.getElementById(id);

    var self = this;
    this.handleChange = function (e) {
        self.loadTheme();
    }

    this.init();
};

PortalStyleConfigEditor.ThemeLoader.prototype.loadTheme = function () {
    var newTheme = this.select.value;
    var styleMap = PortalStyleConfigEditor.themes[newTheme];
    for (key in styleMap) {
        var field = document.getElementById(key);
        field.value = styleMap[key];
        if (field.updateColor) {
            field.updateColor();
        }
    }
    this.select.value = newTheme;
    PortalStyleConfigEditor.updateAllPreviews();
};

PortalStyleConfigEditor.ThemeLoader.prototype.reset = function () {
    this.select.value = "";
};

PortalStyleConfigEditor.ThemeLoader.prototype.init = function () {
    addEvent(this.select, 'change', this.handleChange, false);
    PortalStyleConfigEditor.themeLoader = this;
};

PortalStyleConfigEditor.FontSizeInputElement = function (id) {
    this.id = id;
    this.input = document.getElementById(id);

    var self = this;
    this.handleChange = function (e) {
        self.formatInput();
    };

    this.init();
};

PortalStyleConfigEditor.FontSizeInputElement.prototype.formatInput = function () {
    var fontSize = this.input.value;
    if ("%" != fontSize.charAt(fontSize.length - 1)) {
        this.input.value = fontSize + "%";
    }
};

PortalStyleConfigEditor.FontSizeInputElement.prototype.init = function () {
    addEvent(this.input, "change", this.handleChange, false);
};

PortalStyleConfigEditor.CssWidthInputElement = function (id) {
    this.id = id;
    this.select = document.getElementById(id);
};

PortalStyleConfigEditor.FontFamilyInputElement = function (id) {
    this.id = id;
    this.select = document.getElementById(id);
};


PortalStyleConfigEditor.BorderStyleInputElement = function (id) {
    this.id = id;
    this.input = document.getElementById(id);
};

PortalStyleConfigEditor.getPostBody = function () {
    var params = {};
    for (var i = 0; i < this.inputs.length; i++) {
        var input = this.inputs[i];
        params[input.id] = input.value;
    }

    return XBrowser.buildPost(params);
};

PortalStyleConfigEditor.updatePreview = function (url, target) {
    var handler = function (r) {
        var iframe = document.getElementById(target);
        var doc = iframe.contentWindow || iframe.contentDocument;
        if (doc.document) { doc = doc.document;}
        doc.body.innerHTML = "";
        doc.write(r.responseText);
        doc.close();
        iframe.style.height = doc.body.offsetHeight + "px";
    };
    var postBody = this.getPostBody();
    XBrowser.postHttpResponse(url, handler, postBody);
};

PortalStyleConfigEditor.init = function () {
    var tmp = document.getElementById(EditPageConstants.pEDIT_PAGE).elements;
    var handler = function (e) {
        PortalStyleConfigEditor.themeLoader.reset();
        PortalStyleConfigEditor.updateAllPreviews();
    };
    for (var i = 0; i < tmp.length; i++) {
        var field = tmp[i];
        if (0 == field.id.indexOf(PortalStyleConfigEditorConstants.PARAM_PREFIX)
            && field.id != this.themeLoader.id) {
            addEvent(field, "change", handler);
            this.inputs.push(field);
        }
    }
};

/**
 * Javascript object and controls for the inline color picker
 * @author rchen
 * @since 148
 */
 function ColorPicker() {
     this.pickerDiv = document.getElementById(ColorPickerConstants.DOM_ID);
     this.colorView = document.getElementById(ColorPickerConstants.COLOR_VIEW_ID);
     this.hexView = document.getElementById(ColorPickerConstants.HEX_VIEW_ID);
     this.shim = new iframeShim(this.pickerDiv);
     this.field = null;
     this.addedFields = {};
     var self = this;
     addEvent(document, "click", function () { self.hide(); }, false);
     addEvent(this.pickerDiv, "click", function (e) { ColorPicker.cancelHide(e); }, false);
}

ColorPicker.prototype.position = function () {
    // Get field's position relative to the offsetParent
    var x = 0;
    var y = 0;
    var elem = this.field;
    // TODO: RPC: Can this be done by getting absolute offset of the field and my element?
    while (elem != null && elem != this.pickerDiv.offsetParent) {
        x += elem.offsetLeft;
        y += elem.offsetTop;
        elem = elem.offsetParent;
    }

    // X - The default is to have the left match the field's left. If that won't work,
    // align the right sides.
    // Y - The default is to be flush with the bottom of the field. If that won't work,
    // place it flush with the top
    var pickerHeight = this.pickerDiv.offsetHeight;
    var pickerWidth = this.pickerDiv.offsetWidth;
    if (getObjX(this.field) + pickerWidth > getScrollX() + getWindowWidth()) {
        x -= pickerWidth - this.field.offsetWidth;
    }
    if (getObjY(this.field) + pickerHeight > getScrollY() + getWindowHeight()) {
        y -= pickerHeight;
    } else {
        y += this.field.offsetHeight;
    }

    this.shim.setStyle("left", x + "px");
    this.shim.setStyle("top", y + "px");
}

ColorPicker.prototype.hide = function () {
    this.shim.setStyle("display", "none");
}

ColorPicker.prototype.show = function (fieldId) {
    this.field = document.getElementById(fieldId);
    this.updateView(this.field.value);
    this.shim.setStyle("display", "block");
    if (!this.addedFields[fieldId]) {
        this.addedFields[fieldId] = true;
        var self = this;
        addEvent(this.field, "keydown", function (e) { self.handleKeyPress(e); }, false);
    }
    this.position();
}

ColorPicker.prototype.handleKeyPress = function(e) {
    var key = getEvent(e).keyCode;
    if (key == KEY_ESC) {
        this.hide();
    }
}

ColorPicker.prototype.selectBasic = function (value) {
    if (!this.field.disabled && this.field.value != value) {
        // Assign new value
        this.field.value = ColorPicker.formatHex(value);

        // Fire events
        if (this.field.fireEvent) {
            this.field.fireEvent('onchange');
        } else if (this.field.dispatchEvent) {
            var e = document.createEvent("HTMLEvents");
            e.initEvent('change', true, true);
            this.field.dispatchEvent(e);
        }
    }
    this.hide();
}

ColorPicker.prototype.updateView = function (hex) {
    var fHex = ColorPicker.formatHex(hex);
    this.colorView.style.backgroundColor = fHex;
    this.hexView.value = fHex;
}

// Lazily instantiated singleton
ColorPicker.singleton = null;

ColorPicker.cancelHide = function (e) {
    eventCancelBubble(e);
    return false;
}

ColorPicker.pick = function (fieldId, event) {
    if (!ColorPicker.singleton) {
        ColorPicker.singleton = new ColorPicker();
    }
    ColorPicker.singleton.show(fieldId);
    if (event) {
        ColorPicker.cancelHide(event);
    }
}

ColorPicker.formatHex = function (hex) {
    var newHex = ('#' == hex.charAt(0)) ? hex : '#' + hex;
    // Attempt to expand 3 hex to 6 hex
    if (newHex.match(/^#[0-9a-f]{3}$/i)) {
        var r = newHex.charAt(1);
        var g = newHex.charAt(2);
        var b = newHex.charAt(3);
        newHex = '#' + r + r + g + g + b + b;
    } else if (!newHex.match(/^#[0-9a-f]{6}$/i)) {
        newHex = '#FFFFFF';
    }
    return newHex;
}

ColorPicker.hiOn = function (elem, boxColor) {
    hiOn(elem);
    elem.style.backgroundColor = '#000000';
    ColorPicker.singleton.updateView(boxColor);
}

ColorPicker.hiOff = function (elem, boxColor) {
    hiOff(elem);
    elem.style.backgroundColor = ColorPicker.formatHex(boxColor);
}

/**
 * Represents a field in a detail element. Handles opening the field for editing, undo, retrieving value for save...
 * Fields are created onload so they can use the DOM.
 * 
 * @author jmooney
 * @since 150
 */
function InlineEditField() {
    this.id = null;
    this.tableCell = null;
    this.readDiv = null;
    this.created = false;
    this.editDiv = null;
    this.state = InlineEditState.NONE;
    this.required = false;
    this.initialValue = null;
    this.initialHTML = null;
    this.currentValue = null;
    this.changed = false;
    this.undoButton = null;
    this.error = null;
    this.errorDiv = null;
    this.compound = false;
    this.waitForLoad = false;
    this.controllerId = null;
}

// overlay for compound fields and dependencies
InlineEditField.overlay = null;

/**
 * common initializer for all fields
 */
InlineEditField.prototype.init = function(id, state, required, initialValue) {
    this.id = id;
    this.state = state;
    this.required = required;
    if (initialValue === undefined || initialValue === null) {
        this.initialValue = "";
    } else {
        this.initialValue = initialValue;
    }
    this.currentValue = this.initialValue;
    this.tableCell = getElementByIdCS(id + InlineEditConstants.CELL_ID);
    this.readDiv = getElementByIdCS(id + InlineEditConstants.INNER_ID);
    if (!this.tableCell || !this.readDiv) {
        this.state = InlineEditState.NONE;
        return;
    }
    this.initialHTML = this.readDiv.innerHTML;
}

/* abstract methods that need to be overridden */

InlineEditField.prototype.isDifferentValue = function(newValue) { }

InlineEditField.prototype.openField = function() { }

InlineEditField.prototype.showEdit = function() { }

InlineEditField.prototype.hideEdit = function() { }

InlineEditField.prototype.closeField = function() { }

InlineEditField.prototype.createEditElements = function() { }

InlineEditField.prototype.getValueFromEdit = function() { return null; }

InlineEditField.prototype.updateReadElement = function() { }

InlineEditField.prototype.updateEditElement = function() { }

InlineEditField.prototype.addSaveData = function(saveData) { }

InlineEditField.prototype.createDummy = function() { }

InlineEditField.prototype.load = function() { }

/**
 * resets this field back to its initial value
 */
InlineEditField.prototype.reset = function() { }

/**
 * strip out < > and replace newlines with <br>
 */
InlineEditField.prototype.cleanValue = function(value) {
    if (value && value.replace) {
        return value.replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\n/g, '<br>').replace(/"/, "&quot;");
    }
    return value;
}

/**
 * strip < > out of a string
 */
InlineEditField.prototype.cleanValueNoBR = function(value) {
    if (value && value.replace) {
        return value.replace(/</g, '&lt;').replace(/>/g, '&gt;');
    }
    return value;
}

/**
 * creates the undo button
 */
InlineEditField.prototype.createUndoButton = function() {
    this.undoButton = document.createElement("a");
    this.undoButton.className = "inlineEditUndoLink";
    this.undoButton.title = LC.getLabel("Icons", "inlineEditUndo");
    this.undoButton.href = "javascript:sfdcPage.inlineEditData.resetFieldById('" + this.id + "');";
    this.undoButton.innerHTML = "<img width='16px' height='16px' src='s.gif' alt='" + LC.getLabel("Icons", "inlineEditUndo") + "' class='inlineEditUndo'>";
}

InlineEditField.prototype.getFieldLabel = function() {
    var labelNode = this.tableCell.previousSibling;
    while (labelNode && labelNode.nodeType != Node.TEXT_NODE) {
        labelNode = labelNode.firstChild;
    }
    return labelNode ? labelNode.nodeValue : "";
}

/**
 * sets a validation error on this field
 */
InlineEditField.prototype.setError = function(message) {
    this.error = message;
    if (!this.errorDiv) {
        this.errorDiv = document.createElement("div");
        this.errorDiv.className = "errorMsg";
        this.errorDiv.innerHTML = this.error;
        this.tableCell.appendChild(this.errorDiv);
    } else {
        this.errorDiv.innerHTML = this.error;
        this.errorDiv.style.display = "block";
    }
}

/**
 * clears any validation errors on this field
 */
InlineEditField.prototype.clearError = function() {
    if (this.errorDiv) {
        this.errorDiv.style.display = "none";
    }
    this.error = null;
}

/**
 * get the css class for this field's state
 */
InlineEditField.prototype.getCSSClass = function() {
    return this.state.cssClass;
}

/**
 * get the mouseover css class for this state
 */
InlineEditField.prototype.getCSSHoverClass = function() {
    return this.state.cssClass + "On";
}


/*  -----   STATIC FUNCTIONS   -----  */

/**
 * create a field from the field data struct
 */
InlineEditField.createField = function(fieldData) {
    var fieldType = InlineEditField.resolveSwitchableField(fieldData);
    var fieldId = fieldData[InlineEditConstants.FIELD_ID];
    var state = InlineEditState[fieldData[InlineEditConstants.FIELD_STATE]];
    var required = fieldData[InlineEditConstants.FIELD_REQUIRED];
    var initialValue = fieldData[InlineEditConstants.FIELD_VALUE];
    var extraData = {};
    if (fieldType && fieldType.inlineEditExtraData) {
        for (var i = 0; i <  fieldType.inlineEditExtraData.length; i++) {
            extraData[fieldType.inlineEditExtraData[i]] = fieldData[fieldType.inlineEditExtraData[i]];
        }
    }
    try {
        if (fieldType && fieldType.inlineEditFieldObject && fieldId && state) {
            return eval("new " + fieldType.inlineEditFieldObject + "(fieldId, state, required, initialValue, extraData)");
        }
    } catch(e) {
        // squash js error, field won't be editable
    }
    return null;
}

/**
 * resolves switchable personname fields for Account name
 */
InlineEditField.resolveSwitchableField = function(fieldData) {
    var type = ColumnType[fieldData[InlineEditConstants.FIELD_TYPE]];
    if (type == ColumnType.SWITCHABLE_PERSONNAME) {
        return ColumnType[fieldData[InlineEditConstants.OVERRIDE_TYPE]];
    }
    return type;
}

var Util = {
    makeOptionString : function(labelText, value, array){
        if (!array){
            return "<option value='" + value + "'>" + labelText + "</option>";
        } else {
            array.push('<option value="')
            array.push(value);
            array.push('">');
            array.push(labelText);
            array.push('</option>');
        }
    },

    evalScriptsUnderElement : function(element){
        scriptTags = element.getElementsByTagName("script");
        for (var i = 0; i < scriptTags.length; i++){
            eval(scriptTags[i].innerHTML);
        }
    },

    evalAjaxServletOutput : function(jsonStr){
        if (!jsonStr){
            return null;
        } else if (jsonStr.substring(0, AjaxServlet.CSRF_PROTECT.length) !== AjaxServlet.CSRF_PROTECT){
            //If it doesn't start with CSRF_PROTECT, it didn't come from AjaxServlet...something's wrong
            throw "CSRF protect string not added to servlet response.";
        } else  {
            return eval('('+jsonStr.substring(AjaxServlet.CSRF_PROTECT.length, jsonStr.length) + ')');
        }
    },

    refreshDynamicSelect : function(/*SelectElement*/selectElement, /*list of [label, value]*/options, /*boolean*/ showNone, /*str*/valToMatch){
        var selectedIndex = 0;
        var optionsArr = ["<select name='", selectElement.name, "' id='", selectElement.id, "' class='", selectElement.className, "' title='", selectElement.title, "'"];
        // otherAtributes contains optional attributes for select elements, they are retained if they are set for the original element
        var otherAttributes = ["size","multiple"];
        for (var i = 0; i < otherAttributes.length; i++){
            var param=otherAttributes[i];
            if (selectElement[param]) {
                optionsArr.push(" ");
                optionsArr.push(param);
                optionsArr.push("='");
                optionsArr.push(selectElement[param]);
                optionsArr.push("'");
            }
        }
        optionsArr.push(">");

        var indexNum = 0;
        if (showNone){
            Util.makeOptionString(LC.getLabel("SelectElement", "Required"), '', optionsArr);
            indexNum++;
        }
        for (var i = 0; i < options.length; i++){
            var newVal = options[i][1];
            Util.makeOptionString(options[i][0], newVal, optionsArr);
            if (valToMatch && (newVal === valToMatch)){
                selectedIndex = indexNum;
            }
            indexNum++;
        }
        optionsArr.push("</select>");
        var selParent = selectElement.parentNode;
        if (!selParent.isDynamicSelect){
            var wrapperSpan = document.createElement("SPAN");
            wrapperSpan.isDynamicSelect = true;
            selParent.insertBefore(wrapperSpan, selectElement);
            selParent.removeChild(selectElement);
            selParent = wrapperSpan;
        }
        selParent.innerHTML = optionsArr.join("");
        var newSel = selParent.firstChild;
        if (valToMatch){
            newSel.selectedIndex = selectedIndex;
        }
        return newSel;
     },
 /**
  * Will return a string that can be used as the innerHTML of another element, which will
  * create a select element with the desired attributes and options
  */
    createDynamicSelect : function(/*attribute map*/attributeMap, /*list of [label, value]*/options, /*boolean*/ showNone) {
        var optionsArr = ["<select"];
        for (var key in attributeMap){
                optionsArr.push(" ");
                optionsArr.push(key);
                optionsArr.push("='");
                optionsArr.push(attributeMap[key]);
                optionsArr.push("'");
        }
        optionsArr.push(">");

        if (showNone){
            Util.makeOptionString(LC.getLabel("SelectElement", "Required"), '', optionsArr);
        }
        for (var i = 0; i < options.length; i++){
            var newVal = options[i][1];
            Util.makeOptionString(options[i][0], newVal, optionsArr);
        }
        optionsArr.push("</select>");

        return optionsArr.join("");
    },

    /**
     * convenience method to add Options to SelectElements
     */
    insertOption : function(/*Select Element*/ selectElement, /* Option Element */ optionElement, /* int */index) {
        if (selectElement.currentStyle) {
            selectElement.add(optionElement, index);
        } else {
            selectElement.add(optionElement, selectElement.options[index]);
        }
    },

    /**
     * converts a list of options in [label, value] format into the format used by the picklist/MSP code
     */
    convertOptionsForPicklistData : function(/* list of [label, value] */ options) {
        var ret = [];
        for (var i = 0; i < options.length; i++) {
            ret.push(options[i][1]);
            ret.push(options[i][0]);
        }
        return ret;
    },

	/**
	 * Very useful function for subclassing.
	 * Makes the parent's prototype available to the child non-destructively.
	 * Supports *multiple* inheritance.
	 */
    bequeath : function (parentClass, childClass, /* optional,debug */ keepLocalCopyOfParent) {
    var parentConstructor = parentClass.toString();
    var parentName = parentConstructor.match( /\s*function (.*)\(/ );
    if (parentName && keepLocalCopyOfParent) { childClass.prototype[parentName[1]] = parentClass; }
    for (var m in parentClass.prototype) {
	// non-destructively inherit the parent properties
	if (!childClass.prototype[m]) childClass.prototype[m] = parentClass.prototype[m];
    	}
    },

	/**
	 * This method allows inheritance with constructor chaining.
	 * The parent constructor may be called like :
	 * 		function childClass() {
	 * 			parentClass.call(this);
	 * 			//do other stuff..
	 * 		}
	 */
    inherit : function (childClass, parentClass) {
		childClass.prototype = new parentClass ;
		childClass.prototype.constructor = childClass ;
    }
}

function MaskTypeSelector(targetDivId, typeSelectId, charSelectId, maskData){
	this.targetDiv = document.getElementById(targetDivId);
	this.typeSelect = document.getElementById(typeSelectId);
	this.charSelect = document.getElementById(charSelectId);
	this.maskData = maskData;
	
	var self = this;
	
	this.handleSelectChange = function(e){
		var typeValue = self.typeSelect.options[self.typeSelect.selectedIndex].value;
		var charValue = self.charSelect.options[self.charSelect.selectedIndex].value;
		
		if (!(typeValue && charValue)){
			self.targetDiv.innerHTML = "";
			return;
		}
		
		var example = self.maskData[typeValue];
		example = example.replace(/X/g, charValue);
		self.targetDiv.innerHTML = example;
	};

	addEvent(this.typeSelect, 'change', this.handleSelectChange, false);
	addEvent(this.charSelect, 'change', this.handleSelectChange, false);
	
	this.handleSelectChange();
}
////////////////////////////////
// Dynamically load CSS Skin
////////////////////////////////

function DynamicCss (){
	this.cssLinks = [];
}

DynamicCss.prototype.addCssUrl = function(url, mediaType) {
	this.cssLinks.push(new DynamicCss.CssLink(url, mediaType));
}

DynamicCss.prototype.writeCss = function(apiVersion, doc) {
	for (var i = 0; i < this.cssLinks.length; i++) {
		doc.write(this.cssLinks[i].getLink(apiVersion));
	}
	doc.close();
}

DynamicCss.addCssUrl = function(url, mediaType) {
	DynamicCss.instance.addCssUrl(url, mediaType);
}

DynamicCss.writeCss = function(apiVersion, doc) {
	if (!doc) alert('You must specify a document when calling DynamicCss.writeCss');
	if (!apiVersion) alert('You must specify an API version when calling DynamicCss.writeCss');
	DynamicCss.instance.writeCss(apiVersion, doc);
}


// load one CSS file
DynamicCss.loadCSS = function(headElem, cssUri) {
	var commonCSSLink = document.createElement("link");
	commonCSSLink.setAttribute("type", "text/css");
	commonCSSLink.setAttribute("href", cssUri);
	commonCSSLink.setAttribute("rel", "stylesheet");

	headElem.appendChild(commonCSSLink);
}

// load a set of CSS style sheets for a theme
DynamicCss.loadSkin = function(getUserInfoResult, version) {
	var headElem = DynamicCss.getHead();  
	if (headElem) {
		var uiSkin = "Theme2";
		if (getUserInfoResult && getUserInfoResult.userUiSkin) {
			uiSkin = getUserInfoResult.userUiSkin;
		}
		var base;
		if (version){
		    base = "/sCSS/" + version;
		} else {
            base = "/dCSS";
		}
		base = base + "/" + uiSkin + "/default";
		DynamicCss.loadCSS(headElem, base + "/common.css");
		DynamicCss.loadCSS(headElem, base + "/custom.css");
	}
}

DynamicCss.getHead = function(){
	var headElems = document.getElementsByTagName("head");
	var headElem = (headElems && headElems.length == 1) ? headElems[0] : null;
    return headElem;
}

DynamicCss.CssLink = function(url, mediaType) {
	this.url = url;
	if (mediaType) {
		this.mediaType = mediaType;
	} else {
		this.mediaType = null;
	}
}

DynamicCss.CssLink.prototype.getUrl = function() { 
	return this.url;
}

DynamicCss.CssLink.prototype.getMediaType = function() { 
	return this.mediaType;
}

DynamicCss.CssLink.prototype.getLink = function(apiVersion) {
	if (!DynamicCss.CssLink.URL_VERSION_REGEX) DynamicCss.CssLink.URL_VERSION_REGEX = new RegExp("sCSS/[^/]*");
	var strBuf = [];
	var repStr = "sCSS/" + apiVersion;
	
	strBuf.push("<link type='text/css' rel='stylesheet' href='");
	strBuf.push(this.getUrl().replace(DynamicCss.CssLink.URL_VERSION_REGEX, repStr));
	strBuf.push("'");
	if (this.getMediaType()) {
		strBuf.push(" media='");
		strBuf.push(this.getMediaType());
		strBuf.push("'");
	}
	strBuf.push(">");
	return strBuf.join('');
}


DynamicCss.instance = new DynamicCss();  //singleton

/**
 * MRU hover detail object.
 * @author mooney
 * @since 146
 */
function MRUHoverDetail(id) {
    this.id = id;
    this.mruItem = document.getElementById("mru" + id);
    this.hover = document.createElement("div");
    this.hover.id = id + "Hover";
    this.hover.className = "mruHoverDetail";
    this.hover.innerHTML = "<div class=\"bPageBlock secondaryPalette\"><div class=\"pbBody\">" + LC.getLabel("Global", "loading") + "</div></div>";
    this.mruItem.appendChild(this.hover);
    this.originalClass = this.mruItem.className;
    var width = this.mruItem.offsetWidth;
    // IE6 calculates the width incorrectly, add in 30 pixels for the mruIcon and its margin
    if (this.mruItem.currentStyle && XBrowser.userAgent.isIE6) {
       width -= 30;
    }
    this.hover.style.left = width + "px";
    this.hover = new iframeShim(this.hover);
    this.fadingOut = null;
    this.fadingIn = null;
    this.loaded = false;
}

MRUHoverDetail.SHOW_DELAY = 800;
MRUHoverDetail.HIDE_DELAY = 400;
// the URL for bulk loading all the MRU hovers
MRUHoverDetail.loaderURL = null;
// boolean for if the request has been sent already
MRUHoverDetail.sentRequest = false;
// response from the bulk loader servlet
MRUHoverDetail.response = null;
// map from id to hover object
MRUHoverDetail.hovers = {};
// id of the currently open hover
MRUHoverDetail.openHover = null;

// static function to retrieve a hover detail using a 15 char ID
MRUHoverDetail.getHover = function(id) {
    if (MRUHoverDetail.hovers[id]) {
        return MRUHoverDetail.hovers[id];
    }
    var hover = new MRUHoverDetail(id);
    MRUHoverDetail.hovers[id] = hover;
    return hover;
}

// static function to load the hover details
MRUHoverDetail.bulkLoad = function() {
    if (!MRUHoverDetail.sentRequest && MRUHoverDetail.loaderURL != null) {
        MRUHoverDetail.sentRequest = true;
        XBrowser.getHttpResponse(MRUHoverDetail.loaderURL,
            function(request) {
                MRUHoverDetail.response = request.responseText;
                // if a hover detail is already open then load it now
                if (MRUHoverDetail.openHover != null) {
                	MRUHoverDetail.hovers[MRUHoverDetail.openHover].load();
                }
            },
            function(request) {
                MRUHoverDetail.response = request.responseText;
                if (MRUHoverDetail.openHover != null) {
                	MRUHoverDetail.hovers[MRUHoverDetail.openHover].load();
                }
            }
        );
    }
}
// show the hover detail
MRUHoverDetail.prototype.show = function() {
    if (this.fadingOut) {
        clearTimeout(this.fadingOut);
        this.fadingOut = null;
	} else {
        var self = this;
	    this.fadingIn = setTimeout( function() { self.showNow(); }, MRUHoverDetail.SHOW_DELAY);
    }
}

MRUHoverDetail.prototype.showNow = function() {
    if (!MRUHoverDetail.sentRequest) {
    	if (MRUHoverDetail.loaderURL != null) {
            MRUHoverDetail.bulkLoad();
    	} else {
    		// we haven't loaded, and we have no URL to load from, so don't display anything.
    		// onunload should null out the URL, so this is mostly to prevent sending a request after
    		// the user navigates away from this page
    		return;
    	}
    }
	MRUHoverDetail.openHover = this.id;
	if (!this.loaded && MRUHoverDetail.response != null) {
		this.load();
	}
    this.hover.setStyle("display", "block");
    if (this.mruItem.currentStyle && XBrowser.userAgent.isIE6) {
        // something wrong with the offsetLeft calc for this in IE6, override it manually for now
        this.hover.iframe.style.left = this.hover.div.style.left;
    }
    this.mruItem.className = this.originalClass + " secondaryPalette";
    this.fadingIn = null;
}

// hide the hover detail
MRUHoverDetail.prototype.hide = function(id) {
    if (this.fadingIn) {
        clearTimeout(this.fadingIn);
        this.fadingIn = null;
    } else {
        var self = this;
        this.fadingOut = setTimeout(function() { self.hideNow(); }, MRUHoverDetail.HIDE_DELAY);
    }
}

MRUHoverDetail.prototype.hideNow = function() {
	MRUHoverDetail.openHover = null;
    this.hover.setStyle("display", "none");
    this.mruItem.className = this.originalClass;
    this.fadingOut = null;
}

// loads the mini detail from the responseText
MRUHoverDetail.prototype.load = function() {
    var startTag = "<" + this.id + ">";
    var endTag = "</" + this.id + ">";
    var start = MRUHoverDetail.response.indexOf(startTag);
    var end = MRUHoverDetail.response.indexOf(endTag);
    if (start != -1 && end != -1) {
    	this.hover.div.innerHTML = MRUHoverDetail.response.slice(start + startTag.length, end);
    	Util.evalScriptsUnderElement(this.hover.div);
    	this.loaded = true;
    }
}

var MOUSE_OVER_FADE_MAP = [];

/* This should be called from onMouseOver */
function addMouseOver(div, /*optional*/hotSpot){
  for (var i = 0; i < MOUSE_OVER_FADE_MAP.length; i++){
    if (MOUSE_OVER_FADE_MAP[i] === div){
      return;
    }
  }

  var inner;
  for (var i = 0; i < div.childNodes.length; i++){
    if (div.childNodes[i].className == MouseOverElement.DEFAULT_CLASS_INNER) {
      inner = div.childNodes[i];
      break;
    }
  }
  if (!inner) return;
  var foo;
  if (hotSpot){
    foo = new MouseOverFade(hotSpot, inner);
  } else {
    foo = new MouseOverFade(div, inner);
  }
  foo.handleMouseOver();
  MOUSE_OVER_FADE_MAP.push(div);
}

function MouseOverFade(hotSpot, divToFade){
  	this.mover = new MouseOverFadeHandler(hotSpot, divToFade, true);
  	
  	var self = this;
  	
  this.handleMouseOver = function(e){
    self.mover.fadeIn();
  };

  this.handleMouseOut = function(e){
    self.mover.fadeOut();
  };

  this.init();
}

MouseOverFade.prototype.init = function(){
    addEvent(this.mover.controller, 'mouseover', this.handleMouseOver, false);
    addEvent(this.mover.controller, 'mouseout', this.handleMouseOut, false);
    addEvent(this.mover.div, 'mouseover', this.handleMouseOver, false);
    addEvent(this.mover.div, 'mouseout', this.handleMouseOut, false);
};

function MouseOverFadeHandler(hotSpot, divToFade, saveOrigPos){
  this.controller = hotSpot
  this.div = divToFade;
  this.shim = new iframeShim(divToFade);
  this.saveOrigPos = saveOrigPos;

  var self = this;
  
  this.setPosition = function(object) {
  		if (!object){
  			object = self.shim;
  		}
		object.setStyle('display', 'block');
		if (self.saveOrigPos){
		     if (!self.origL){
		          self.origL = self.div.style.left;
		      } else {
		          object.setStyle('left', self.origL);
		      }
		}

        var calloutLeft = getOffsetLeft(self.div);
        var calloutRight = calloutLeft + self.div.offsetWidth;
        var windowRight = getScrollX() + getWindowWidth() - 15;

        if (calloutLeft < 0) {
            object.setStyle('left', "0");
        } else if (calloutRight > windowRight){
            object.setStyle('left', (self.div.offsetLeft - (calloutRight - windowRight) - 20) + "px");
        } 
        
        //Now the bottom
        if (self.saveOrigPos){
	        if (!self.origTop){
	        	self.origTop = self.div.style.top;
	        } else {
	        	object.setStyle('top', self.origTop);
	        }
        }
        
        var calloutTop = getObjY(self.div);
        var calloutBottom = calloutTop + self.div.offsetHeight;
        var windowBottom = getScrollY() + getWindowHeight() - 15; //15 for scrollbars
        
        if (calloutTop < 0){
        	object.setStyle('top', '0');
        } else if (calloutBottom > windowBottom ){
        	object.setStyle('top', (self.div.offsetTop - (calloutBottom - windowBottom) - 15) + 'px');
        } 
    }
    
  this.mover = new Fader(
    this.shim,
    function(object, currVal) { return object.getOpacity() <= 0; },
    function(object, currVal) {	 return object.getOpacity() >= 0.99; },
    30,
    function(currVal, sign) { return currVal += sign*0.2; },
    function(object, nextVal) { object.setOpacity(nextVal); },
    0,
    function(object) {object.setOpacity(0); object.setStyle('display', 'none');},
    function(object) {object.setOpacity(0.99); object.setStyle('display', 'block');},
	this.setPosition
  ); 
  
  this.fadeIn = function(){
  	self.mover.fadeIn();
  }
  
  this.fadeOut = function(){
  	self.mover.fadeOut();
  }
}
function Attachments() {}

var hasPendingAttachments = false;

function addPendingAttachment(attId) {
   document.getElementById(Activity.pNEW_ATTACHMENTS).value=document.getElementById(Activity.pNEW_ATTACHMENTS).value + attId + ',';
   hasPendingAttachments = true;
   displayAttachmentWarning(true);
}

function removePendingAttachment(attId, listDomId) {
   var att = document.getElementById(Activity.pNEW_ATTACHMENTS);
   if(att) {
       var attStr = new String(att.value);
       att.value = attStr.replace(attId +',', '');
       hasPendingAttachments = (att.value.length >= 15);
       if(!hasPendingAttachments) {
           displayAttachmentWarning(false);
       }
       var extraParamNames = null;
       var attIds = document.getElementById(Activity.pNEW_ATTACHMENTS);
       if (attIds) {
           extraParamNames = new Array(attIds.id);
       }
       var rl = sfdcPage.getRelatedListById(listDomId);
       if(rl) {
           rl.refresh(extraParamNames, attIds ? new Array(attIds.value) : null);
       }
   }
}

function displayAttachmentWarning(showWarning){
    var aw = document.getElementById(Activity.pATT_WARNING);
    if (aw) {
        if (showWarning){
            aw.style.display='block';
        } else {
            aw.style.display='none';
        }
    }

}

/**
 * attachments is a comma-separated list of ids. (stupid, yes.)
 * returns object based on PromoteTransientAttachments.formatResults().
 */
Attachments.prototype.promoteAttachments = function(sessionId, parentId, attachments, checkSize){

    var promotionResult = null;

    if (sessionId && parentId && (attachments || checkSize)){

        var qs = new QueryString("");
        qs.add("sid", sessionId);
        qs.add("pid", parentId);
        qs.add("att", attachments);
        if (checkSize){
            qs.add("check", "1");
        }

        var promoteURL = "/servlet/promoteAtt" + qs.toString();
        var response = DesktopAjax.prototype.doGet(promoteURL);
        if (response){
            promotionResult = eval("(" + response + ")");
        }
        
    }
    
    return promotionResult;
}

//  Javascript functionality for the tagging setup.
//

var TagSettingsPage = function(){}

// Static function used by the tagging setup page
TagSettingsPage.toggleTags = function(item) {
  var section = document.getElementById(TagConstants.HIDING_SECTION_ID);
  if (item.checked) {
    section.style.display='block';
  }
  else {
    section.style.display='none';
  }
}

ColorInput = function (id) {
    this.id = id;
    this.box = document.createElement("div");
    this.box.innerHTML = "<span></span>"; // Get IE to respect the height set for the DIV
    this.input = document.getElementById(id);

    var self = this;
    this.handleChange = function (e) {
        self.formatInput();
        self.updateColor();
    };

    this.init();
};

ColorInput.prototype.updateColor = function () {
    var color = this.input.value;
    if (color.match(/^(#[0-9a-f]{6})$/i)) {
        delStyleClass(this.box, ColorInputConstants.ERROR_COLOR_BOX_CSS);
        this.box.style.backgroundColor = color;
    } else {
        this.box.style.backgroundColor = "#FFFFFF";
        addStyleClass(this.box, ColorInputConstants.ERROR_COLOR_BOX_CSS);
    }
};

ColorInput.prototype.formatInput = function () {
    // Correct missing #
    var color = this.input.value;
    if ("#" != color.charAt(0) && 6 == color.length) {
        color = "#" + color;
    }

    // Uppercase
    color = color.toUpperCase();

    // Convert 3 hex to 6 hex
    if (color.match(/^#[0-9a-f]{3}$/i)) {
        var r = color.charAt(1);
        var g = color.charAt(2);
        var b = color.charAt(3);
        color = '#' + r + r + g + g + b + b;
    }

    this.input.value = color;
};

ColorInput.prototype.init = function () {
    // Prep box
    addStyleClass(this.box, ColorInputConstants.COLOR_BOX_CSS);
    this.updateColor();
    this.input.parentNode.insertBefore(this.box, this.input);

    // Add handler
    var self = this;
    addEvent(this.input, "change", this.handleChange, false);
    var handlePick = function (e) {
        ColorPicker.pick(self.id, e);
    };
    addEvent(this.box, "click", handlePick, false);
    addEvent(this.input, "focus", handlePick, false);
    addEvent(this.input, "click", ColorPicker.cancelHide, false);

    // Add a reference to functions
    this.input.updateColor = function() {self.updateColor(); };
};

/**
 *  Javascript object for BannerElement.
 *  @author mooney
 *  @since 144
 *  @param id The id of the banner div
 */ 
function Banner(id) {
    this.bannerDiv = document.getElementById(id);
    this.bannerY = Banner.BANNER_END;
    this.fadingIn = null;
    this.fadingOut = null;
    this.clicked = false;
    var self = this;
    if (getCookie("sawBanner") != id) {
        this.shim = new iframeShim(this.bannerDiv);
        addEvent(this.bannerDiv, 'click', function() { self.click(); }, false);
        //addEvent(this.bannerDiv, 'mouseover', function() { self.interrupt(); }, false);
        //addEvent(this.bannerDiv, 'mouseout', function() { self.goAway(); }, false);
        this.shim.setStyle('display', 'block');
        this.fadeIn();
        //this.goAway();
    }
}

Banner.BANNER_END = -50;
Banner.BANNER_START = 0;
Banner.BANNER_STEP = -5;
Banner.BANNER_STEP_DELAY = 40;
Banner.BANNER_DELAY = 4000;
Banner.COOKIE_LIFE = 90;

// the user clicked on the banner, so make it go away right now and not come back for 90 days
Banner.prototype.click = function() {
    if (this.fadingIn) {
        clearTimeout(this.fadingIn);
        this.fadingIn = null;
    }
    this.clicked = true;
    var expires = new Date();
    expires.setDate(expires.getDate() + Banner.COOKIE_LIFE);
    setCookie("sawBanner", this.bannerDiv.id, expires);
    this.fadeOut();
}

// sets the delay for the banner to go away
Banner.prototype.goAway = function() {
    if (!this.clicked) {
        var self = this;
        this.fadingOut = setTimeout(function() { self.fadeOut(); }, Banner.BANNER_DELAY);
    }
}

// called when the user hovers on the banner, interrupts the call to hide it and fades in
Banner.prototype.interrupt = function() {
    if (!this.clicked && this.fadingOut) {
        var self = this;
        clearTimeout(this.fadingOut);
        this.fadingOut = null;
        this.fadingIn = setTimeout(function() { self.fadeIn(); }, 0);
    }
}

// slides the banner off the screen
Banner.prototype.fadeOut = function() {
    if (this.fadingOut) {
        clearTimeout(this.fadingOut);
        this.fadingOut = null;
    }
    if (this.bannerY > Banner.BANNER_END) {
        var self = this;
        this.bannerY += Banner.BANNER_STEP;
        this.shim.setStyle('top', this.bannerY + 'px');
        this.fadingOut = setTimeout(function() { self.fadeOut(); }, Banner.BANNER_STEP_DELAY);
    }
}

// slides the banner onto the screen
Banner.prototype.fadeIn = function() {
    if (this.fadingIn) {
        clearTimeout(this.fadingIn);
        this.fadingIn = null;
    }
    if (this.bannerY < Banner.BANNER_START) {
        var self = this;
        this.bannerY -= Banner.BANNER_STEP;
        this.shim.setStyle('top', this.bannerY + 'px');
        this.fadingIn = setTimeout(function() { self.fadeIn(); }, Banner.BANNER_STEP_DELAY);
    }
}

/**
	@param object		required. object, The object to be faded
	@param testMin		required. function(object, currVal), should return true if the object is completely faded out
	@param testMax  	required. function(object, currVal), should return true if the object is completely faded in
	@param timestep 	required. number, The interval between steps, in milliseconds
	@param nextStep		required. function(currVal, sign), given the current value and a direction, return the next value.
								  sign will be positive for fadeIn, negative for fadeOut.  Any return value will be saved 
								  to currVal.
	@param increment	requried. function(object, nextVal) the increment function.  Should change the property
							      of the object to nextVal (which is the value returned by nextStep).  
	@param initVal		optional. number, an initial value for currVal.
	@param finalMin		optional. function(object),  Will be called at the end of fadeOut
	@param finalMax		optional. function(object), Will be called at the end of fadeIn
    @param startIn      optional. function(object), Wtill be called before a jumpIn or fadeIn
*/
function Fader(object, testMin, testMax, timestep, nextStep, increment, initVal, finalMin, finalMax, startIn) {	
	var self = this;
	
	if (!(object && testMin && testMax && timestep && nextStep && increment)){
		//required arguments
		return null;
	}
	
	this.object = object;
	this.testMin = testMin;
	this.testMax = testMax;
	this.timestep = timestep;
	this.nextStep = nextStep;
	this.increment = increment;
	this.currVal = initVal;
	this.finalMin = finalMin;
	this.finalMax = finalMax;
	this.startIn = startIn;
	
	this.inId = -1;
	this.outId = -1;
	
	this.position = 'out';
	
	this.fadeIn = function(){
		if (self.startIn) self.startIn(this.object);
		if (self.outId >= 0){
			clearInterval(self.outId);
			self.outId = -1;
		}
		if (this.inId < 0){
			self.inId = setInterval(fadeInCaller, self.timestep);
			self.position = 'moving_in';
 		} 
	}
	
	this.fadeOut = function() {
		if (self.inId >= 0){
			clearInterval(self.inId);
			self.inId = -1;
		}
		if (self.outId < 0){
			self.outId = setInterval(fadeOutCaller, self.timestep);
			self.position = 'moving_out';
 		} 
	}
	
	function fadeInCaller(){
		self.fadeInHelp();
	}
	
	function fadeOutCaller(){
		self.fadeOutHelp();
	}
	
	this.isMoving = function() {
		return (this.inId >= 0 || this.outId >= 0);
	}
	
	/*
	return one of 'in', 'out', 'moving_in', 'moving_out', or 'stopped'
	*/
	this.getPosition = function(){
		return self.position;
	}
}

Fader.prototype.fadeInHelp = function() {
	var nextVal = this.nextStep(this.currVal, 1);
	if (this.testMax(this.object, nextVal)){
		var finalVal = this.finalMax && this.finalMax(this.object);
		if (typeof finalVal == "number") { this.currVal = finalVal; }
		clearInterval(this.inId);
		this.inId = -1;
		this.position = 'in';
		return;
	} 
	this.increment(this.object, nextVal);
	this.currVal = nextVal;
};

Fader.prototype.fadeOutHelp = function() {
	var nextVal = this.nextStep(this.currVal, -1);
	if (this.testMin(this.object, nextVal)){
		var finalVal = this.finalMin && this.finalMin(this.object);
		if (typeof finalVal == "number") { this.currVal = finalVal; }
		clearInterval(this.outId);
		this.outId = -1;
		this.position = 'out';
		return;
	}
	this.increment(this.object, nextVal);
	this.currVal = nextVal;
};

Fader.prototype.stopFade = function() {
	if (this.inId >= 0){
		clearInterval(this.inId);
		this.inId = -1;
	}
	if (this.outId >= 0){
		clearInterval(this.outId);
		this.outId = -1;
	}
	this.position = 'stopped';
};

Fader.prototype.jumpIn = function() {
	if (this.startIn) this.startIn(this.object);
	var newCur = null;
	if (this.finalMax) {newCur = this.finalMax(this.object); }
	if (newCur !== null) { this.currVal = newCur;}
	this.position = 'in';
};

Fader.prototype.jumpOut = function() {
	var newCur = null;
	if (this.finalMin) {newCur = this.finalMin(this.object);}
	if (newCur !== null) { this.currVal = newCur; }
	this.position = 'out'
};
	
function LMACheckboxesManager() {
	var self = this;
	
	this.radios = document.getElementsByName(DeveloperSettings.LICENSE_MGR_CHOICE_STR);
	
	this.divNames = [];
	for (var i = 0; i < arguments.length; i++){
		this.divNames[i] = arguments[i];
	}
	
	this.onClickHandler = function(e){
		self.setVisibility(getEventTarget(getEvent(e)).value);
	}
	
	for (var i = 0; i < this.radios.length; i++){
		addEvent(this.radios[i], 'click', this.onClickHandler, false);
		if (this.radios[i].checked){
			this.setVisibility(this.radios[i].value);
		}
	}
}

LMACheckboxesManager.prototype.setVisibility = function(radioName){
	for (var i = 0; i < this.divNames.length; i++){
		document.getElementById(this.divNames[i] + DeveloperSettings.DIV_SUFFIX).style.display = 
			(this.divNames[i] === radioName ? 'block' : 'none');
	}
}
/**
 * MenuButton (mutton) basically mimics a SelectElement. In accessible mode it is a SelectElement.
 *
 * @author jmooney
 * @since 148
 */
function MenuButton(id, hasDefaultAction, /* Optional */ customCssButtonWidth) {
    if (UserContext.isAccessibleMode) {
        var go = document.getElementById(id + MenuButtonElement.GO_BUTTON);
        addEvent(go, 'click', function() {
            var select = document.getElementById(id + MenuButtonElement.SELECT);
            var where = select.options[select.selectedIndex].value;
            if (where != "")
                navigateToUrl(where);
        }, false);
        return;
    }
    this.hasDefaultAction = hasDefaultAction;
    if (customCssButtonWidth) this.customCssButtonWidth = customCssButtonWidth;
    this.parentDiv = document.getElementById(id);
    this.buttonDiv = document.getElementById(id + MenuButtonElement.BUTTON);
    this.menuDiv = document.getElementById(id + MenuButtonElement.MENU);
    this.shim = new iframeShim(this.menuDiv);
    var self = this;
    addEvent(document, 'click', function(e) {self.hideOthers(e);}, true);
    addEvent(document, 'keydown', function(e) { self.handleKeyPress(e); }, true);
    addEvent(this.parentDiv, 'click', function(e) { self.handleDivClick(e); }, false);
}

// checks for a default action and opens or closes the menu
MenuButton.prototype.handleDivClick = function(e) {
    if (this.hasDefaultAction) {
        // determine if the click was on the arrow or the text
        var x = 0;
        if (e.offsetX) { // IE
            x = e.offsetX - e.srcElement.offsetLeft;
        } else if (e.layerX) { // FF, calc the target coords manually
            var elem = e.target;
            x = e.pageX - getObjX(e.target);
        }
        // 17 is the width of the default arrow
        var buttonWidth = 17;
        if (this.customCssButtonWidth != null && this.customCssButtonWidth > 0) {
        	buttonWidth = this.customCssButtonWidth;
        }


        if (x < this.parentDiv.offsetWidth - buttonWidth) {
            // clicked the text, do the default
            navigateToUrl(this.menuDiv.childNodes[0].href);
            eventCancelBubble(e);
            return;
        }
    }
    // check the default action stuff
    if (XBrowser.getCurrentStyle(this.menuDiv, "display") == "none") {
        this.show();
    } else {
        this.hide();
    }
    eventCancelBubble(e);
}

// show the menu
MenuButton.prototype.show = function() {
    this.shim.setStyle("display", "block");
    this.position();
}

// positions the menu correctly
MenuButton.prototype.position = function() {
    // first we set the width so that nothing wraps
    var width = this.buttonDiv.offsetWidth - 2; // subtract 2 for the borders
    for (var i = 0; i < this.menuDiv.childNodes.length; i++) {
        var node = this.menuDiv.childNodes[i];
        if (node.nodeType == Node.ELEMENT_NODE) {
            if (node.offsetWidth > width) {
                width = node.offsetWidth;
            }
        }
    }
     this.shim.setStyle("width", width + "px");
    // if the parent div is inline, position the menu below the button
    if (XBrowser.getCurrentStyle(this.parentDiv, "display") == "inline") {
        this.parentDiv.style.position = "relative";
        var y = this.buttonDiv.offsetHeight;

        if (this.parentDiv.currentStyle) {
            for (var i = 0; i < this.menuDiv.childNodes.length; i++) {
                var node = this.menuDiv.childNodes[i];
                if (node.nodeType == Node.ELEMENT_NODE) {
                    node.style.width = width + "px";
                }
            }
        }
        this.shim.setStyle("top", y + "px");
    }
}

// hide the menu
MenuButton.prototype.hide = function() {
    if (XBrowser.getCurrentStyle(this.menuDiv, "display") != "none") {
        this.shim.setStyle("display", "none");
        // back to static in case we are in IE
        this.parentDiv.style.position = "";
    }
}

// hide all other menus
MenuButton.prototype.hideOthers = function (e) {
	var event = getEvent(e);
	var target = getEventTarget(event);
	if (target != this.buttonDiv ) {
		this.hide();
	}
}

// esc closes the menu
MenuButton.prototype.handleKeyPress = function(e) {
    var key = getEvent(e).keyCode;
    if (key == KEY_ESC) {
        this.hide();
    }
}

// clear all the options from a mutton
MenuButton.clearOptions = function(muttonId) {
    // can't do this in accessible mode
    if (!UserContext.isAccessibleMode) {
        var menuDiv = document.getElementById(muttonId + MenuButtonElement.MENU);
        menuDiv.innerHTML = "";
    }
}

// add an options to a mutton
MenuButton.addOption = function(muttonId, disp, href, css) {
    // can't do this in accessible mode
    if (!UserContext.isAccessibleMode) {
        var menuDiv = document.getElementById(muttonId + MenuButtonElement.MENU);
        var link = document.createElement("a");
        if (css) link.className = css;
        link.href = href;
        link.innerHTML = disp;
        
        menuDiv.appendChild(link);
    }
}
/**
 *  ActivityReminder, the main class for the popup timer & window logic.
 *
 *  @author beidson
 *  @since 144
 */
 
/**
 * There's only 1 reminder per page, so ActivityReminder is
 * all static methods.
 */
var ActivityReminder = {};

// Used for primitive inter-window communication (from popup to any other main sfdc window):
ActivityReminder._REMINDER_COOKIE = 'reminderCookie';
ActivityReminder._REMINDER_INTERVAL = 5000;

// Used for primitive inter-window communication (to prevent dup popups from multiple windows)
ActivityReminder._LOCK_COOKIE = 'reminderLockCookie';

/**
 * Initialize the popup timer.<b>
 * If isPopup is set, it is for the reminder popup itself,
 * which does some extra setup. 
 */
ActivityReminder.initialize = function(popupSchedule, isPopup, isRefresh) {
    // If isPopup, we're the popup window itself:
    ActivityReminder._isPopup = isPopup;
    
    ActivityReminder._launched = new Date().getTime();
    ActivityReminder._currentSchedule = popupSchedule;
    ActivityReminder._checkScheduleUpdates();
    
    if (ActivityReminder._isPopup) {
        if (!isRefresh) {
            ActivityReminder._isBlur = false;        
            addEvent(window, 'focus', ActivityReminder._wasFocused, false);
            addEvent(window, 'blur', ActivityReminder._wasBlured, false);
            setTimeout('ActivityReminder._pseudoFlash()', 500);
        }

        // Start the countdown:
        ActivityReminder._popupCountdown();
        
        ActivityReminder._setupMeetingSummary();

        ActivityReminder._setupSnoozeDismiss();

        ActivityReminder._updateSchedule();

        ActivityReminder._closeIfEmpty();

        // IE/CSS limitation:
        window.onresize = ActivityReminder._resizeToFitHorizontalChange;
        ActivityReminder._resizeToFitHorizontal();
        
        // Can get out of sync:
        updateToggleAllBox(document.forms.reminder, 'ids');
    }
}

ActivityReminder._checkScheduleUpdates = function() {
    // Check if another window, including the popup itself, refreshed the schedule cookie
    // (This is particularly important after a 'snooze' where the user may not actually
    // refresh the main window, but the user does close the reminder window)
    var rc = getCookie(ActivityReminder._REMINDER_COOKIE);
    // Parse it:
    if (rc) {
        ActivityReminder._currentSchedule = rc.split(',');
        // we (someone) got it, so delete just after the interval so other windows can get it:
        setTimeout("deleteCookie(ActivityReminder._REMINDER_COOKIE)", ActivityReminder._REMINDER_INTERVAL);
    }
    // See if any reminders went off:
    var now = new Date().getTime();
    var anyScheduled = false;
    var kept = new Array();
    for (var i in ActivityReminder._currentSchedule) {
        if (Number(ActivityReminder._currentSchedule[i]) <= (now + ActivityReminder._REMINDER_INTERVAL * 2)) {
            // Now that the meeting is < the polling interval away, go ahead & schedule it directly:
            if (!anyScheduled) {
                ActivityReminder._schedule(ActivityReminder._currentSchedule[i]);
                anyScheduled = true;
            }
        } else {
            kept.push(ActivityReminder._currentSchedule[i]);
        }
    }
    // And clean up:
    ActivityReminder._currentSchedule = kept;
    
    setTimeout("ActivityReminder._checkScheduleUpdates()", ActivityReminder._REMINDER_INTERVAL);
}


ActivityReminder._schedule = function(time) {
    var milliseconds = Number(time) - new Date().getTime();
    // Don't schedule negative reminders, schedule them for now:
    if (milliseconds < 0) { 
        milliseconds = 0;
    }
    if (ActivityReminder._isPopup) {
        milliseconds += 10000; // The popup can refresh itself, but if there's a main window open,
                               // give it a 10 second head start to actually do the refresh.
    }
    var timeSinceStart = Number(time) - ActivityReminder._launched;
    if (!ActivityReminder._isPopup && timeSinceStart <= (-2 * 60 * 60 * 1000)) {
        // If the reminder is more than two hours in the past, there's no point in re-checking the event before showing the popup.
        // More importantly, for users with popup blocking (who will ALWAYS have lots of past reminders),
        // we don't want to hit the refresh page... instead it'll just attempt to launch the popup,
        // which will get blocked.
        
        // We use two hours buffer because of pages views the following scenario:
        // 1) See reminder popup
        // 2) Dismiss all (or snooze, etc.)
        // 3) Hit back button to a previously viewed SFDC page
        // 4) Popup window opens/closes (because the previous page had the old reminder schedule)
        // The doesn't totally solve the issue, but it mitigates it --- the previous page view must have been > 2 hrs. ago.
        setTimeout("ActivityReminder._showPopup(false)", milliseconds);
    } else {
        setTimeout("ActivityReminder._showPopupIfRequired()", milliseconds);
    }
}

ActivityReminder._updateSchedule = function() {
    var scheduleCookieVal = ActivityReminder._currentSchedule.join(',');
    // Send it via cookie (all SFDC windows should get this):
    var dieDate = new Date(new Date().getTime() + ActivityReminder._REMINDER_INTERVAL * 2);
    setCookie(ActivityReminder._REMINDER_COOKIE, scheduleCookieVal, dieDate);
}

// Shows the popup (if necesary; may have been cancelled, etc.)
ActivityReminder._showPopupIfRequired = function() {
    var fn = function(responseStr) {
        // Check that we needed to popup:
        var txt = responseStr.indexOf(ActivityReminderConstants.REMINDERS_OK);
        if (txt < 0) {
            // Nope, it didn't give the signal.
            txt = responseStr.indexOf(ActivityReminderConstants.REMINDERS_NONE);
            if (txt < 0) {
                // Didn't get the signal at all... something wrong on the server, most likely a session timeout.
                // Just show the popup which will show the login page:
                ActivityReminder._showPopup();
            }
            // In we got the none signal, there are no events now; it was cancelled, just do nothing.
        } else {
            // Everything normal, show the popup
            ActivityReminder._showPopup();
        }
    };
    // Do a quick check to see if we still really need to popup:
    // (ideally this could be combined with the call to
    // display the page)
    var at = new Date().getTime();
    makeAjaxRequest('/ui/core/calendar/ActivityReminderRefreshPage?at=' + at, fn);
}

ActivityReminder._showPopup = function(isTest) {
    // Crude (semi reliable) lock to reduce chance of 2 browser windows opening 2 popups
    // (open with a name can still create dup windows if called simultaneously)
    var rc = getCookie(ActivityReminder._LOCK_COOKIE);
    if (rc) {
        // Already being shown.
        return;
    }
    var dieDate = new Date(new Date().getTime() + 5000); // expire quickly.
    setCookie(ActivityReminder._LOCK_COOKIE, 'shown', dieDate);

    // Ok, open the popup (or refresh the already-open one):    
    var locval = isFirefox ? 'yes' : 'no'; // firefox changes the title if we don't show a location bar.
    var at = new Date().getTime();
    var teststr = isTest==true ? '&test=1' : '';
    var useScrollbars = (isNetscape && !isIE) ? 'yes' : 'no';
    var height = 320;
    if (isNetscape && isIE) height += 40; // Netscape/IE popup windows come up shorter for whatever reason.
    var parentWin = window.parent; // for desktop pages.
    var win = parentWin.open('/ui/core/calendar/ActivityReminderPage?at=' + at + teststr,
        'Reminder',
        'width=450,height=' + height + ',status=no,location=' + locval + ',dependent=no,resizable=yes,toolbar=no,directories=no,menubar=no,scrollbars=' + useScrollbars,
        false);
    var opened = win && !win.closed;
    // Note: firefox ignores status=no for security reasons...
    if (isTest && !opened) {
        // popup blocking.
        alert(LC.getLabel("Page_ActivityReminder", "popup_blocked"));
        return;
    }
    if (opened) {
        // for re-opens, need to pop to the front. Would like to flash, but doesn't seem to be a way.
        win.focus();
    }
}

ActivityReminder._popupCountdown = function() {
    // Update the remaining/overdue time display:
    var now = new Date().getTime();
    for (var i=0;;i++) {
        var el = document.getElementById(ActivityReminderConstants.DUE_MINUTES_ID + i);
        if (!el) {
            break;
        }
        var time = el.getAttribute(ActivityReminderConstants.DUE_TIME_ATTR);
        if (time != 0) {
            var away = time - now;
            ActivityReminder._updateTime(el, away);
        }
    }
    
    // Snooze options may have changed:
    ActivityReminder._enableSnoozeOptions();    
    
    // refresh the countdown every 30 seconds.
    setTimeout("ActivityReminder._popupCountdown()", 30000);
}

// Updates 1 reminder due in field with the proper
// text for the current due in value.
ActivityReminder._updateTime = function(el, awayMillis) {
    var awayMins = Math.round(awayMillis / (1000*60));
    var textNode = el.firstChild;
    var overDue;
    if (awayMins < 0) {
       overDue = true;
       awayMins = -awayMins;
    } else {
       overDue = false;
    }
    
    var msg = ActivityReminder._formatDuration(awayMins, overDue);
    if (overDue) {
        msg = LC.getLabel("Page_ReminderSettings", "overdue", msg);
    }
    textNode.nodeValue = msg;
}

ActivityReminder._formatDuration = function(awayMins, overDue) {
    if (awayMins >= 60) {
        var awayHours = overDue ? Math.floor(awayMins / 60) : Math.ceil(awayMins / 60);
        if (awayHours >= 24) {
            var awayDays = Math.floor(awayHours / 24);
            if (awayDays == 1) {
                return LC.getLabel("Page_ReminderSettings", "day", awayDays);
            } else {
                return LC.getLabel("Page_ReminderSettings", "days", awayDays);
            }
        } else {
            if (awayHours == 1) {
                return LC.getLabel("Page_ReminderSettings", "hour", awayHours); 
            } else {
                return LC.getLabel("Page_ReminderSettings", "hours", awayHours);
            }
        }
    } else {
        if (awayMins == 1) {
            return LC.getLabel("Page_ReminderSettings", "minute", awayMins);
        } else {
            return LC.getLabel("Page_ReminderSettings", "minutes", awayMins);
        }
    }
}

// Shows an activity link in the opener (if it still exists)
// or a new window.
ActivityReminder.showLink = function(url) {
    ActivityReminder._stopFlash = true; // Just kill flashing if user has clicked here.
    var openerClosed = true;
    try {
        // IE throws when accessing closed when opener gone.
        openerClosed = !window.opener || window.opener.closed || window.opener==window;
    } catch (e) {
    }
    if (openerClosed) {
        window.open(url, null, '', false);
    } else {
        // See if we're on a desktop.
        var mainFrame = window.opener.document.getElementById('mainFrame');
        if (mainFrame && mainFrame.tagName == 'IFRAME') {
            // Not available: showLoading('mainLoader', mainFrame);
            mainFrame.src = url + '?' + Desktop.IS_DESKTOP + '=mn';
        } else {
            window.opener.location = url;
        }
    }
}

// If there are no more reminders left after snooze/dismiss,
// then close the window.
ActivityReminder._closeIfEmpty = function() {
    var ct = 0;
    for (var i=0;;i++) {
        var item = document.getElementById("ids" + i);
        if (item) {
            ct++;
        } else {
            break;
        }
    }
    if (!ct) {
        // no more remain (dismiss 10ms later, o.w. form won't submit)
	    setTimeout("window.close()", 10);
    }
}

ActivityReminder._setupMeetingSummary = function() {
    // Mouse over any row selects the meeting summary:
    var r = getElementsByClassName('dataRow');
    for (rownum in r) {
        var row = r[rownum];
        var makeHandler = function(rn) {
            return function(event) {
                // The global handler, below, will
                // default the selection (when the
                // mouse isn't over a row).
                eventCancelBubble(event); // stop it.
                ActivityReminder._displayMeeting(rn);
            }
        };
        addEvent(row, 'mouseover', makeHandler(rownum));
    }
    var reminderForm = document.getElementById(ActivityReminderConstants.REMINDER_ID);
    var defaultSelector = function(event) {
        ActivityReminder._displayMeeting(); // no args.
    }
    // When the mouse isn't over one of the rows, have
    // it select the default (checked) one:
    addEvent(reminderForm, 'mouseover', defaultSelector);
    
    // Do initial selection:
    ActivityReminder._displayMeeting();
}

ActivityReminder._displayMeeting = function(rownum) {
    // If rownum not specified, use the first selected checkbox
    if (typeof rownum == 'undefined') {
        for (var i=0; ;i++) {
            var sel = document.getElementById("ids" + i);
            if (!sel) {
                rownum = 0; // just pick the first one, consistent w/ the initial display.
                break;
            }
            if (sel.checked) {
                rownum = i;
                break;
            }
        }
    }
    for (var i=0; ;i++) {
        var summary = document.getElementById(ActivityReminderConstants.SUMMARY_ID + i);
        if (!summary) {
            break;
        }
        summary.style.display = i==rownum ? 'block' : 'none';
    }
}

ActivityReminder._setupSnoozeDismiss = function() {
    for (var i=0; ;i++) {
        var sel = document.getElementById("ids" + i);
        if (!sel) {
            break;
        }
        addEvent(sel, 'click', ActivityReminder._reenableSnoozeDismiss);
    }
    var allBox = document.getElementById('allBox');
    if (allBox) { // If there are no items, can be empty.
        addEvent(allBox, 'click', ActivityReminder._reenableSnoozeDismiss);
    }
    ActivityReminder._reenableSnoozeDismiss();
}

ActivityReminder.updateBrowserTime = function() {
    // Sends the browsers time down for snooze command:
    document.getElementById(ActivityReminderConstants.pSNOOZED_AT).value = new Date().getTime();
}

ActivityReminder._reenableSnoozeDismiss = function() {
    var anyChecked = false;
    for (var i=0; ;i++) {
        var sel = document.getElementById("ids" + i);
        if (!sel) {
            break;
        }
        if (sel.checked) {
            anyChecked = true;
            break;
        }
    }
    var btnCls = anyChecked ? 'btn' : 'btnDisabled';
    document.getElementById(ActivityReminderConstants.SNOOZE_ID).disabled = !anyChecked;
    document.getElementById(ActivityReminderConstants.SNOOZE_ID).className = btnCls;
    document.getElementById(ActivityReminderConstants.SNOOZE_TIME_ID).disabled = !anyChecked;
    document.getElementById(ActivityReminderConstants.DISMISS_ID).disabled = !anyChecked;
    document.getElementById(ActivityReminderConstants.DISMISS_ID).className = btnCls;
    ActivityReminder._enableSnoozeOptions();
}

ActivityReminder._enableSnoozeOptions = function() {
    // Figure out nearest selected activity:
    var now = new Date().getTime();
    var minTimeAway;
    for (var i=0;;i++) {
        var el = document.getElementById(ActivityReminderConstants.DUE_MINUTES_ID + i);
        if (!el) {
            break;
        }
        var sel = document.getElementById("ids" + i);
        if (!sel.checked) {
            continue;
        }
        var time = el.getAttribute(ActivityReminderConstants.DUE_TIME_ATTR);
        var isAllDay = el.getAttribute(ActivityReminderConstants.ALL_DAY_ATTR);        
        var away = time - now;
        if (!minTimeAway || minTimeAway > away) {
            minTimeAway = away;
        }
        if (isAllDay == "true") {
            minTimeAway = 0; // All day events/tasks can't be snoozed for 15 minutes before.
        }
    }

    // Figure out nearest selected activity:
    var snooze = document.getElementById(ActivityReminderConstants.SNOOZE_TIME_ID);
    var options = snooze.childNodes;

    var removedCurrentSelected = false;    
    // Add/remove all the appropriate 'before' times:
    var leadOptions = [0, 5, 10, 15];
    for (var i=0; i < leadOptions.length; i++) {
        var val = leadOptions[i];
        var options = snooze.childNodes;
        
        // Find the existing one, if any:
        var existingOption = null;
        for (var li=options.length-1; li >= 0; li--) {
            var option = options[li];
            if (option.text && option.value == -val) {
                existingOption = option;
                break;
            }
        }
        if ((val + 1.5) * 60 * 1000 <= minTimeAway) { // 1.5 extra minutes since < 1 minute will reshow immediately.
            if (!existingOption) {
                var option = document.createElement('option');
                var label;
                if (val==0) {
                    label = LC.getLabel('Page_ReminderSettings', 'hours_before', val);
                } else {
                    label = LC.getLabel('Page_ReminderSettings', 'minutes_before', val);
                }
                var opttxt = document.createTextNode(label);
                option.appendChild(opttxt);
                option.value = -val; // negative indicates lead time.
                snooze.insertBefore(option, snooze.firstChild);
                removedCurrentSelected = true;
            }
        } else {
            // Should not be here:
            if (existingOption) {
                if (existingOption.selected == true) {
                    removedCurrentSelected = true;
                }
                snooze.removeChild(existingOption);
            }
        }
    }
    if (removedCurrentSelected == true) {
        // reselect the first one:
        var options = snooze.childNodes;
        for (var i=0; i< options.length; i++) {
            var option = options[i];
            if (option.text) {
                option.selected = 'true';
                break;
            }
        }
    }
}

ActivityReminder.selectAll = function() {
    for (var i=0; ;i++) {
        var sel = document.getElementById("ids" + i);
        if (!sel) {
            break;
        }
        sel.checked = true;
    }
}

ActivityReminder._wasFocused = function() {
    ActivityReminder._isBlur = false;
}

ActivityReminder._wasBlured = function() {
    ActivityReminder._isBlur = true;
}

ActivityReminder._pseudoFlash = function() {
    if (!ActivityReminder._originalTitle) {
        ActivityReminder._originalTitle = document.title;
    }
    var flashCount = ActivityReminder._flashCount;
    if (!flashCount) {
        flashCount = 1;
    } else {	
        flashCount++;
    }
    ActivityReminder._flashCount = flashCount;
    var flashLimit = isNetscape ? 6 : 12; // Netscape grabs focus on setTitle, so flash shorter.
    // Netscape blur/focus events don't work well...
    if ((ActivityReminder._isBlur != true || ActivityReminder._stopFlash == true || isNetscape) && ActivityReminder._flashCount > flashLimit) {
        // Ok, the user has seen it, restore title & finish.
        document.title = ActivityReminder._originalTitle;
        return;
    }
    // Keep flashing:
    if (flashCount % 2 == 1) {
        document.title = LC.getLabel("Page_ActivityReminder", "new_flash");
    } else {
        document.title = ActivityReminder._originalTitle;
    }
    setTimeout('ActivityReminder._pseudoFlash()', 500);
}

ActivityReminder.testPopup = function() {
    // Note, 1500 below is not just a random #, Firefox won't consider it a popup unless the
    // # is > 1000 (it seems)
    setTimeout('ActivityReminder._showPopup(true)', 1500);
}

ActivityReminder._resizeToFitHorizontalChange = function() {
    if (!isIE) return;
    if (document.documentElement.clientWidth == ActivityReminder._oldWidth) {
        // need to prune for real changes.
        return;
    }
    ActivityReminder._oldWidth = document.documentElement.clientWidth;
    var els = getElementsByClassName('maxHorizontal');
    var clearedAny = false;
    for (var elnum in els) {
        var el = els[elnum];
        if (el.style.width && el.style.width != null) {
            el.style.width = null;
            clearedAny = true;
        }
    }
    if (clearedAny) {
        // Because we have already altered the document (by setting stylesheet),
        // we clear the style change (above), allow the browser (IE) to recompute the
        // size... then (later) we re-compute the max size fix.
        // This breaks badly if we don't do it later in a timeout -- the layout computations
        // seem to be in a partially complete state otherwise.
        window.setTimeout('ActivityReminder._resizeToFitHorizontal()', 100);
    }
}

ActivityReminder._resizeToFitHorizontal = function() {
    if (isNetscape && !isIE) { // Netscape not in IE mode:
        // Update stylesheet for netscape.
        var els = getElementsByClassName('maxHorizontal');
        var maxFoundWidth = 0;
        for (var elnum in els) {
            var el = els[elnum];
            var curWidth = el.offsetWidth;
            el.style.width = '200px'; // Table layout didn't work.
            el.style.overflow = 'hidden';
        }
        if (els[0]) {
            // Netscape doesn't support scrolling tbody.
            var tbody = els[0].parentNode.parentNode.parentNode;
            tbody.style.height = 'auto';
        }
        return;
    }
    if (!isIE) return;
    // With IE tables, it ignores whether overflow-x is set to hidden for any div inside a td, and just
    // uses as much horz. space as required.  Firefox will, if overflow-x is set to hidden, take up as much
    // horz. space as available & then hide the rest.
    // CSS doesn't seem to specify what to do here, but Firefox's behavior
    // is obviously much nicer.
    
    // Use the width of the related list el as the guide for how much space the table is taking up:
    var relatedListEl = getElementsByClassName('bRelatedList')[0];
    var relatedListTableEl = getElementsByClassName('list')[0];
    // In IE6, the relatedListEl has the full used width, but in IE7 only the table has it.
    // Because of other differences in sizing, use offset width (which will be greater in IE6).
    var tableBasedOffsetWidth = relatedListTableEl.offsetWidth + 4; // extra pixels for borders, etc.
    var offsetWidth;
    if (relatedListEl.offsetWidth < tableBasedOffsetWidth) {
        // oversize IE7
        offsetWidth = tableBasedOffsetWidth;
    } else {
        // not oversized or IE6.
        offsetWidth = relatedListEl.offsetWidth;
    }
    
    var extraWidth = document.documentElement.clientWidth - offsetWidth;
    var minWidth = 100;
    if (extraWidth != 0) {
        var newWidth;
        var els = getElementsByClassName('maxHorizontal');
        var maxFoundWidth = 0;
        for (var elnum in els) {
            var el = els[elnum];
            var curWidth = el.offsetWidth;
            maxFoundWidth = Math.max(maxFoundWidth, curWidth);
        }
        newWidth = Math.max(minWidth, maxFoundWidth + extraWidth);
        for (var elnum in els) {
            var el = els[elnum];
            var curWidth = el.offsetWidth;
            el.style.width = newWidth + 'px';
        }
    }
}

/**
* Contains general utility function for traversing & manipulating the DOM
*
* @author mpolcari
* @since 142.ml
*/
function DomUtil() {}

/**
* applies a function (funk) to node & all of its children
* this results in a depth-first search which
* ends when funk returns a true value
*
* It is a half-assed X-path
*/
DomUtil.walkDomTreeApplyingFunction = function(node, funk) {
  if (!node) return null;
  var ans = funk(node);
  if (ans) {
     return ans;
  } else if (node.firstChild) {
    for (var i = 0; i < node.childNodes.length; i++) {
      ans = DomUtil.walkDomTreeApplyingFunction(node.childNodes[i], funk);
      if (ans) {
        return ans;
      }
    }
  }
  return null;
}

/* stops at the first one */
DomUtil.findDescendantWithProperty = function(startNode, property, value, caseInSensitive) {
  if (caseInSensitive) {
    var lowerVal = value.toLowerCase();
    return DomUtil.walkDomTreeApplyingFunction(startNode, function(node) {
      if (node[property] && node[property].toLowerCase && (node[property].toLowerCase() == lowerVal)) {
        return node;
      } else {
        return null;
      }
    });
  } else {
    return DomUtil.walkDomTreeApplyingFunction(startNode, function(node) {
      if ((node[property] == value)) {
        return node;
      } else {
        return null;
      }
    });
  }
}

/* similar to getElementsByTagName, but just returns the first one */
DomUtil.findDescendantWithTag = function(startNode, tagType) {
  return DomUtil.findDescendantWithProperty(startNode, 'tagName', tagType, true);
}

/* returns only the first one we encounter in a depth-first search */
DomUtil.findDescendantWithClassName = function(startNode, className) {
  if (!className) return;
  var spacedClass = ' ' + className + ' ';
  return DomUtil.walkDomTreeApplyingFunction(startNode, function(node) {
      if (node.className && ((' ' + node.className + ' ').indexOf(spacedClass) > -1)) {
        return node;
      } else {
        return null;
      }
    });
}

/* imports a shallow copy of the node.  If you want a deep copy, copy the innerHTML */
DomUtil.importNode = function(node, targetDocument) {
  if (!node) { Gack.sendGack("invalid Node"); return; }
  if (targetDocument.importNode) { //every reasonable browser on the planet
    return targetDocument.importNode(node, false);
  } else { //ie6
    var copy = targetDocument.createElement(node.tagName);
    for (var at = 0; at < node.attributes.length; at++) {
       if (node.attributes[at].specified) {
           copy.setAttribute(node.attributes[at].name, node.attributes[at].value);
        }
    }
    return copy;
  }
}

DomUtil.copyScripts = function (srcDoc, targetDoc) {
      var targetHead = targetDoc.body.parentNode.firstChild;
      var scripts = srcDoc.getElementsByTagName("SCRIPT");
      var scriptElement;
      for (var i = 0, script; i < scripts.length; i++){
        script = scripts[i];
        if (script.src) { //just copy in functions.js and its ilk
          scriptElement = targetDoc.createElement("script");
          scriptElement.src = script.src
          targetHead.appendChild(scriptElement);
       //   iFrameHead.appendChild(DomUtil.importNode(script, iFrameDoc));  this wasn't working in ffox ?
        }
      }
}

DomUtil.copyCSS = function (srcDoc, targetDoc, apiVersion) {
      var targetHead = targetDoc.body.parentNode.firstChild;
      for (var i = 0, sheet; i < srcDoc.styleSheets.length; i++){
        sheet = srcDoc.styleSheets[i];
        var newNode;
        if (sheet.ownerNode) {
          newNode = DomUtil.importNode(sheet.ownerNode, targetDoc);
        } else {
          newNode = DomUtil.importNode(sheet.owningElement, targetDoc);
        }
        if (apiVersion && newNode.href && newNode.href.replace) {
          newNode.href = newNode.href.replace('/sCSS/', '/sCSS' + apiVersion + '/');
        }
        targetHead.appendChild(newNode);
      }
}

DomUtil.copyScriptsCssBodyClass = function (srcDoc, targetDoc) { //Copy css styleSheets, script includes, & body.className
  DomUtil.copyCSS(srcDoc, targetDoc);
  DomUtil.copyScripts(srcDoc, targetDoc);
  targetDoc.body.className = srcDoc.body.className;
}

DomUtil.setHelpLink = function (doc, url, helpTarget, helpSection) {
  if (helpTarget && helpSection){
     url = "/help/doc/user_ed.jsp?loc=help&target="+helpTarget+"&section="+helpSection;
  }
  var link = DomUtil.findDescendantWithClassName(doc, 'helpLink');
  if (link){
    var par = link.parentNode;
    if (par && par.href && par.href.indexOf("openPopupFocusEscapePounds") > 0){
        par.href = par.href.replace(/'.*?'/, "'"+url+"'");
    }
  }
}


/**
 * TreeNodeElement.js works like a class with lots of static methods.
 */

var TreeNodeElement = function(){}

// static definitions of images used in TreeNodeElement
TreeNodeElement.prototype.collapsedWidget = new Image(20, 16);
TreeNodeElement.prototype.collapsedWidget.src = "/img/tree/plus.gif";
TreeNodeElement.prototype.collapsedWidgetStart = new Image(20, 16);
TreeNodeElement.prototype.collapsedWidgetStart.src = "/img/tree/plusStart.gif";
TreeNodeElement.prototype.collapsedWidgetEnd = new Image(20, 16);
TreeNodeElement.prototype.collapsedWidgetEnd.src = "/img/tree/plusEnd.gif";
TreeNodeElement.prototype.expandedWidget = new Image(20, 16);
TreeNodeElement.prototype.expandedWidget.src = "/img/tree/minus.gif";
TreeNodeElement.prototype.expandedWidgetStart = new Image(20, 16);
TreeNodeElement.prototype.expandedWidgetStart.src = "/img/tree/minusStart.gif";
TreeNodeElement.prototype.expandedWidgetEnd = new Image(20, 16);
TreeNodeElement.prototype.expandedWidgetEnd.src = "/img/tree/minusEnd.gif";

// retrieve matching version of 'minus' images
TreeNodeElement.prototype.getExpandedWidgetState = function(imgURL) {
    if (imgURL.indexOf("Start") != -1) {
        return TreeNodeElement.prototype.expandedWidgetStart.src;
    }
    if (imgURL.indexOf("End") != -1) {
        return TreeNodeElement.prototype.expandedWidgetEnd.src;
    }
    return TreeNodeElement.prototype.expandedWidget.src;
}

// retrieve matching version of 'plus' images
TreeNodeElement.prototype.getCollapsedWidgetState = function(imgURL) {
    if (imgURL.indexOf("Start") != -1) {
        return TreeNodeElement.prototype.collapsedWidgetStart.src;
    }
    if (imgURL.indexOf("End") != -1) {
        return TreeNodeElement.prototype.collapsedWidgetEnd.src;
    }
    return TreeNodeElement.prototype.collapsedWidget.src;
}

TreeNodeElement.prototype.toggle = function(img, blockNum) {
   var obj = document.getElementById(blockNum);
   if (obj != null) {
       visible=(obj.style.display != "none")
       if (visible) {  //Toggle to invisible
         obj.style.display="none";
         img.src = TreeNodeElement.prototype.getCollapsedWidgetState(img.src);
       }
       else {
          obj.style.display="block";
          img.src =  TreeNodeElement.prototype.getExpandedWidgetState(img.src);
       }
   }
}

function LayoutMappingHelper() {
}

LayoutMappingHelper.getCellId = function (profileId, recordTypeId) {
	return profileId + "_" + recordTypeId;
}

LayoutMappingHelper.getCellProfileId = function (cell) {
	var id = cell.id;
	if (!id) {
		return null;
	}
	return id.substring(0, id.indexOf("_"));
}

LayoutMappingHelper.getCellRecordTypeId = function (cell) {
	var id = cell.id;
	if (!id) {
		return null;
	}
	return id.substring(id.indexOf("_") + 1);
}

LayoutMappingHelper.selectCellById = function (id) {
	var el = getElementByIdCS(id);
	if (el) {
		if (el.className.indexOf("selectedCell") < 0) {
			el.className += " selectedCell";
			// el.style.backgroundColor = '#D0D0FF';
		}
	}
}

LayoutMappingHelper.deselectCellById = function (id) {
	var el = getElementByIdCS(id);
	if (el) {
		el.className = el.className.replace(/selectedCell/,"");
	}
}


LayoutMappingHelper.changeCellById = function (id) {
	var el = getElementByIdCS(id);
	if (el) {
		if (el.className.indexOf("changedCell") < 0) {
			el.className += " changedCell";
		}
	}
}


LayoutMappingHelper.resetCellById = function (id) {
	var el = getElementByIdCS(this.getCellId(profileId, recordTypeId));
	if (el) {
		el.className = el.className.replace(/selectedCell/,"");
		el.className = el.className.replace(/changedCell/,"");
	}
}

LayoutMappingHelper.getIndex = function (idNameArr, id) {
	if (!idNameArr || !idNameArr.length || idNameArr.length == 0) {
		return -1;
	}
	for (var i = 0; i < idNameArr.length; i++) {
		if (idNameArr[i].id == id) {
			return i;
		}
	}
	return -1;
}

LayoutMappingHelper.isProfileHeader = function (id) {
	if (!id) {
		return false;
	}
	if (id.indexOf('00e') == 0) {
		return true;
	}
	return false;
}
/**
 * RoleTreeNodeElement handles loading/caching of roles in tree. 
 */

var RoleTreeNodeElement = function(){
    this.openRoleList = new Array(); 
    this.downloadedRoles = new Array();
    this.downloadedAll = false;
    this.treeFormName = null;
}

// called from various java files, typically in a Java-defined
// javascript call, "initializeRoles()"
RoleTreeNodeElement.prototype.init = function(isDownloadedAll, openRoleNodes) {

    this.downloadedAll = isDownloadedAll;
    if (openRoleNodes != null){
        this.openRoleList = openRoleNodes.split(":");
        this.downloadedRoles = openRoleNodes.split(":");
    }
}

RoleTreeNodeElement.prototype.addToOpenRoles = function(item) {
    for (var i = 0; i < this.openRoleList.length; i++) {
        if (this.openRoleList[i] == null || this.openRoleList[i] == '') {
            this.openRoleList[i] = item;    
            this.updateCookiesRole();
            return;     
        }
    }
    this.openRoleList[this.openRoleList.length] = item;
    this.updateCookiesRole();
}

RoleTreeNodeElement.prototype.isInDownloadedRoles = function(item) {
    if (item == '000000000000000') {
        return true;
    }
    
    for (var i = 0; i < this.downloadedRoles.length; i++) {
        if (this.downloadedRoles[i] == item) {
            return true;
        }
    }
    return false;
}    

RoleTreeNodeElement.prototype.removeFromOpenRoles = function(item) {   
    for (var i = 0; i < this.openRoleList.length; i++) {
        if (this.openRoleList[i] == item) {
            this.openRoleList[i] = null;            
        }
    }
    this.updateCookiesRole();
}

RoleTreeNodeElement.prototype.updateCookiesRole = function() {
    var stringlist = "";
    for (var i = 0; i < this.openRoleList.length; i++) {
        if (this.openRoleList[i] != null && this.openRoleList[i] != '') {
            stringlist = stringlist + this.openRoleList[i] + ":";
        }
    }
    Cookies.prototype.SetCookie(RoleTreeCookieConstants.COOKIE_KEY, stringlist, null, "/");     
}

RoleTreeNodeElement.prototype.toggleRoles = function(img, blockNum, roleId) {
   var obj = document.getElementById(blockNum);
   if (obj != null) {
       visible=(obj.style.display != "none")
       if (visible) {  //Toggle to invisible
         obj.style.display = "none";
         img.src = TreeNodeElement.prototype.getCollapsedWidgetState(img.src);
         this.removeFromOpenRoles (roleId);
       }
       else {
          this.addToOpenRoles (roleId);  
          if (this.downloadedAll || this.isInDownloadedRoles (roleId)) {
              obj.style.display = "block";
              img.src = TreeNodeElement.prototype.getExpandedWidgetState(img.src);
          } else {
              this.refreshTree();
          }
       }
   }
}

RoleTreeNodeElement.prototype.collapseAllRoles = function() {
    Cookies.prototype.SetCookie(RoleTreeCookieConstants.COOKIE_KEY, "", null, "/");     
    this.refreshTree();
}

RoleTreeNodeElement.prototype.expandAllRoles = function() {
    Cookies.prototype.SetCookie(RoleTreeCookieConstants.COOKIE_KEY, "EXPANDALL", null, "/");     
    this.refreshTree();
} 

// This is called staticall from TerritoryTreeMultiSelect.java 
// and SelectTargetTerritoryStage.java
RoleTreeNodeElement.prototype.setTreeFormName = function(name) {
	this.treeFormName = name;
}

RoleTreeNodeElement.prototype.refreshTree = function() {
	if (this.treeFormName == null) {
    	window.location.replace(window.location.href);
	} else {
		window.document.forms[this.treeFormName].submit();
	}
}

/**
 * @author zzhou
 * @since 150
 *
 * the javascript for the Forecast Sharing enabling/disabling dialog boxes on the Forecast Settings page
 **/

   function FctSettingsPage() {}
   function FctShareDialog() {}

   FctSettingsPage.fctShareFormSubmit = false;
   FctShareDialog.allowMgrShare = '0';

   //check to see if we submit the form or not
   FctSettingsPage.submitCheck = function(url,isFctShareEnabled) {
        if (validateForm()) {
            FctSettingsPage.fctShareFormSubmit = (isFctShareEnabled == document.getElementById(ForecastSettings.pFORECAST_SHARING).checked);
            if (FctSettingsPage.fctShareFormSubmit) {
                return true;
            } else {
                openPopupFocus(url, 'fctSharingDialog', 300, 500,'height=350,width=600,location=no,dependent=no,resizable=no,toolbar=no,status=yes,directories=no,menubar=no,scrollbars=no', true, true, true);
                return false;
            }
        } else {
            return false;
        }
   }

   FctShareDialog.checkSaveButton = function(checkCheckBox) {
        var checked;
        if (checkCheckBox) {
            checked = document.getElementById(ForecastSharingPrefPopup.DISABLE_CHECKBOX).checked;
        } else {
            checked = document.getElementsByName(ForecastSharingPrefPopup.CAN_SHARE_RADIO)[0].checked || document.getElementsByName(ForecastSharingPrefPopup.CAN_SHARE_RADIO)[1].checked;
        }

        //check if any of the input values are checked so that we allow the user to save by enabling the save button
        if (checked) {
            document.getElementsByName("save")[0].className = 'btn';
            document.getElementsByName("save")[0].disabled = false;
            document.getElementsByName("save")[0].onclick = function() {FctShareDialog.saveResult(true)};
            if (!checkCheckBox) {
                FctShareDialog.allowMgrShare = document.getElementsByName(ForecastSharingPrefPopup.CAN_SHARE_RADIO)[1].checked ? '1' : '0';
            }
        } else {
            document.getElementsByName("save")[0].className = 'btnDisabled';
            document.getElementsByName("save")[0].disabled = true;
        }
   }

   FctShareDialog.saveResult = function(doSave) {
        if (doSave) {
            window.opener.FctSettingsPage.submit(FctShareDialog.allowMgrShare);
        }
        window.close();
   }

   FctSettingsPage.submit = function(allowMgrShare) {
        //simluate clicking on the button to save
        var qs = new QueryString("");
        document.getElementById(ForecastSettings.pALLOW_FM_SHARING).value = allowMgrShare;
        qs.add(EditPageConstants.pSAVE, "Save");
        document.forms[EditPageConstants.pEDIT_PAGE].action += qs.toString();
        document.forms[EditPageConstants.pEDIT_PAGE].submit();
   }




function BrowserSettingsWarning() {
	var warning = this;
	if (window.sfdcPage) {
		window.sfdcPage.appendToOnloadQueue(function() {warning.init();});
	}
}
		
BrowserSettingsWarning.DO_NOT_ASK_AGAIN_EXPIRY = 60; // 2 months

BrowserSettingsWarning.prototype.init = function() {
	var browserSettingsWarning = this;
	var neverAgain = document.getElementById(BrowserSettingsWarningElement.NEVER_SHOW_AGAIN_ID);
	addEvent(neverAgain, "click" , function(e) {
		 browserSettingsWarning.neverShowAgain(); 
		 return false;
		 }, false);
	var moreInfo =  document.getElementById(BrowserSettingsWarningElement.MORE_INFO_ID);
	addEvent(moreInfo, "click" , function(e) {
		 Cookies.prototype.SetCookie(BrowserSettingsWarningElement.cBrowserSettings, "0", new Date(), "/");
		 browserSettingsWarning.hideElement(); 
		 }, false);
}

BrowserSettingsWarning.prototype.neverShowAgain = function() {
	var expiryDate = new Date();
    expiryDate.setDate(expiryDate.getDate() + BrowserSettingsWarning.DO_NOT_ASK_AGAIN_EXPIRY);
						//	name, value, expires, path, domain
	Cookies.prototype.DeleteCookie(BrowserSettingsWarningElement.cBrowserSettings);
	Cookies.prototype.SetCookie(BrowserSettingsWarningElement.cBrowserSettings, "-1", expiryDate, "/");
	this.hideElement();
}

BrowserSettingsWarning.prototype.hideElement = function() {
	document.getElementById(BrowserSettingsWarningElement.BROWSER_SETTINGS_WARNING_ID).style.display = 'none';
}
/**
 * Utility to save open/closed state of setup in browser cookies. 
 * The starting state of the setup tree is initialized in 
 * SetupPageHeader.java, which generates the value for openListSetup below.
 */

var SetupTreeNode = function(){}

// this variable is also referenced in SetupPageHeader.java
SetupTreeNode.prototype.openListSetup = new Array();

SetupTreeNode.prototype.addToOpenSetup = function(item) {
    for (var i = 0; i < SetupTreeNode.prototype.openListSetup.length; i++) {
        if (SetupTreeNode.prototype.openListSetup[i] == null) {
            SetupTreeNode.prototype.openListSetup[i] = item;    
            SetupTreeNode.prototype.updateCookiesSetup();
            return;     
        }
    }
    SetupTreeNode.prototype.openListSetup[SetupTreeNode.prototype.openListSetup.length] = item;
    SetupTreeNode.prototype.updateCookiesSetup();
}

SetupTreeNode.prototype.removeFromOpenSetup = function(item) {   
    for (var i = 0; i < SetupTreeNode.prototype.openListSetup.length; i++) {
        if (SetupTreeNode.prototype.openListSetup[i] == item) {
            SetupTreeNode.prototype.openListSetup[i] = null;            
        }
    }
    SetupTreeNode.prototype.updateCookiesSetup();
}

SetupTreeNode.prototype.updateCookiesSetup = function() {
    var stringlist = "";
    for (var i = 0; i < SetupTreeNode.prototype.openListSetup.length; i++) {
        if (SetupTreeNode.prototype.openListSetup[i] != null) {
            stringlist = stringlist + SetupTreeNode.prototype.openListSetup[i] + ":";
        }
    }
            
    Cookies.prototype.SetCookie(SetupTreeNodeConstants.COOKIE_KEY, stringlist, null, "/");     
}
/**
 * JS code to handle filtering/querying in a SelectFilterElement.
 */
var SelectFilterElement = function(ctlName, ctlOption, target, targetOption, existing, max, useJs){

    this.controllerName = ctlName;
    this.controllerOption = ctlOption;
    this.targetName = target;
    this.targetOption = targetOption;
    this.existingName = existing;

    // array of selected Option objects
    this.selectedOptions = new Array();

    // array of array of all Option objects
    this.allOptions = new Array();
    this.maxValues = max;
    this.searchQueueCount = 0;
    this.useJsSearch = useJs;
    this.searchStringChanged = false;
    this.itemAdded = false;
}

SelectFilterElement.prototype.getSearchElementId = function(){
    return 'searchValue_' + this.controllerName;
}

SelectFilterElement.prototype.addOption = function(type, label, key, searchValue, existingValues) {

    var opt = new Option(label, key);
    opt.searchValue = searchValue;
    var optionsArray = this.allOptions[type];
    if (!optionsArray){
        optionsArray = new Array();
        this.allOptions[type] = optionsArray;
    }

    optionsArray[optionsArray.length] = opt;

    if (existingValues && this.selectedOptions != null && this.selectedOptions[key]) {
        existingValues.options[existingValues.options.length] = new Option(label, key);
    }

}

// doing queueSearch only applies to js searching. otherwise,
// sever-side search results in LOTS of posts to server.
SelectFilterElement.prototype.queueSearch = function() {
    this.searchQueueCount++;
    var callback = this.getJavascriptVarName() + '.doSearch()';
    setTimeout(callback, 300);
}

SelectFilterElement.prototype.doSearch = function() {

    this.searchQueueCount--;
    if (this.searchQueueCount > 0) return;
    // Reset the queue when we do a search
    this.searchQueueCount = 0;

    if (this.useJsSearch){
        this.doSearchJS();
    } else {
        this.doSearchServer();
    }
}

// does a "search" by filtering the values in the select element
SelectFilterElement.prototype.doSearchJS = function() {

    var controller = document.getElementById(this.controllerName);
    var targetField = document.getElementById(this.targetName);
    var existing = document.getElementById(this.existingName);

    var searchEl = document.getElementById(this.getSearchElementId());
    var searchValue = searchEl.value ? searchEl.value.toLowerCase() : "";
    if (searchValue.length == 0) {
        // Just run the whole filter if the input length is 0
        this.filter();
        return;
    }

    targetField.options.length = 0;

    var numMatched = 0;
    var numAdded = 0;
    var optionsOfType = this.allOptions[controller.value];
    if (optionsOfType){
        for (var i = 0; i < optionsOfType.length && numAdded < this.maxValues+1; i++) {
            var option = optionsOfType[i];

            if (existing && this.selectedOptions[option.value]){
                numMatched++;
                continue;
            }

            var match =
                !option.searchValue
                || option.searchValue.length == 0
                || option.searchValue.indexOf(searchValue) == 0
                || option.searchValue.indexOf(' ' + searchValue) > -1;

            if (match){
                if (numAdded < this.maxValues){
                    targetField.options[numAdded] = option;
                }
                numMatched++;
                numAdded++;
            }
        }
    }

    var tooManyValues = numMatched > this.maxValues;
    this.showErrorMessage(tooManyValues);

    if (targetField.options.length == 0) {
        targetField.options[0] = new Option(LC.getLabel("SelectElement", "Required"), '000000000000000');
    }

}

// does a "search" by posting back to the server.
SelectFilterElement.prototype.doSearchServer = function() {
    var form = document.forms[EditPageConstants.pEDIT_PAGE];

    // DuelingListBoxesElement may set javascript to be executed
    // on the form.onsubmit(), so we call it before we submit.
    if (form.onsubmit){
        form.onsubmit();
    }

    form.submit();
}

SelectFilterElement.prototype.handleKeyDown = function(evt) {
    evt = getEvent(evt);
    // Enter key
    if (evt.keyCode == 13 || evt.which == 13) {
        this.queueSearch();
        return false;
    } else {
        this.searchStringChanged = true;
    }
    return true;
}

SelectFilterElement.prototype.handlePropertyChange = function(){
    if (this.useJsSearch){
        this.queueSearch();
    }
}

SelectFilterElement.prototype.showErrorMessage = function(showMsg, errorMessage) {
    var searchEl = document.getElementById(this.getSearchElementId());
    var err = document.getElementById('error_' + this.targetName);
    if (searchEl && err) {
        if (showMsg) {
            err.style.visibility = 'visible';
            err.innerHTML = errorMessage ? errorMessage : LC.getLabel("Search_Filter", "too_many", this.maxValues, this.maxValues);
        } else {
            err.style.visibility = 'hidden';
        }
    }
}

SelectFilterElement.prototype.filter = function() {

// alert(this.targetName + " " + this.existingName);

    var controller = document.getElementById(this.controllerName);
    var targetField = document.getElementById(this.targetName);
    var searchEl = document.getElementById(this.getSearchElementId());
    if (this.useJsSearch && searchEl && searchEl.value.length != 0) {
        searchEl.value = '';
    }

    targetField.options.length = 0;

    // this value is initialized in selectfilterelement.jsp
    if (SelectFilterElement.prototype.NONE_LABEL) {
        targetField.options[0] = new Option(SelectFilterElement.prototype.NONE_LABEL, '000000000000000');
    }

    var counter = 0;
    var numAdded = 0;
    var optionsOfType = this.allOptions[controller.value];
    if (optionsOfType){

        // if there's a set of already-selected options, then we test
        // against that set before adding them to the target.
        var testForAlreadySelected = this.existingName;

        if (testForAlreadySelected){
            this.initSelectedOptionsFromUi();
        }

        // populating target field based on the options loaded with addOption().
        for (var i = 0; i < optionsOfType.length && (!searchEl || numAdded < this.maxValues+1); i++) {

            var option = optionsOfType[i];

            // If there existing values are there, check against the map before adding
            if (testForAlreadySelected && this.selectedOptions[option.value]) {
                continue;
            }

            counter++;

            // If search exists, then they only get up to maxValues
            if (!searchEl || numAdded < this.maxValues) {
                targetField.options[targetField.options.length] = option;
                numAdded++;
            }
        }

    }

    var tooManyValues = counter > this.maxValues;
    this.showErrorMessage(tooManyValues);

    if (targetField.options.length == 0) {
        targetField.options[0] = new Option(LC.getLabel("SelectElement", "Required"), '000000000000000');
    }
}

// After every change, we re-initialize the map so we can have fast lookup on it
SelectFilterElement.prototype.initSelectedOptionsFromUi = function() {

    if (this.existingName){

        this.selectedOptions = new Array();

        var existing = document.getElementById(this.existingName);
        if (existing){
            for (var i = 0; i < existing.options.length; i++) {
                if (existing.options[i].value.length > 0) {
                    this.selectedOptions[existing.options[i].value] = true;
                }
            }
        }
    }

}

// called from SelectFilterElement.java, when setting bodyOnLoad()
SelectFilterElement.prototype.init = function() {

    // The filter needs to be run at lease once.
    this.filter();

    // If existing is there, make sure we set the onchange event so we can keep track
    // of what is selected.
    if (this.existingName){
        var existing = document.getElementById(this.existingName);
        if (existing){
            existing.selectFilterEl = this;
            existing.onchange = function(){
                this.selectFilterEl.initSelectedOptionsFromUi();
            };
        }
    }

    // Initialize the default controller option
    if (this.controllerOption && this.controllerName){
        var controller = document.getElementById(this.controllerName);
        if (controller.options != null && controller.options != undefined) {
            for (var i = 0; i < controller.options.length; i++) {
                if (controller.options[i].value == this.controllerOption) {
                    controller.options[i].selected = true;
                }
            }
        }
    }

    // Initialize the default target option
    if (this.targetOption){
        var targetField = document.getElementById(this.targetName);
        for (var i = 0; i < targetField.options.length; i++) {
            if (targetField.options[i].value == this.targetOption) {
                targetField.options[i].selected = true;
            }
        }
    }

}

// name as defined in SelectFilterElement.getJavascriptVarName().
SelectFilterElement.prototype.getJavascriptVarName = function(){
    return this.targetName + 'Var';
}

// filtering the drop-down based on the controller is really just showing
// the right corresponding drop-down and hiding the others.
SelectFilterElement.prototype.filterDropdown = function(name, currTypeSelect) {
    var selected = currTypeSelect.selectedIndex;
    for (var i = 0; i < currTypeSelect.options.length; i++){
        var option = currTypeSelect.options[i].value;
        var optionList = name + '_' + option;
        var currSelect = document.getElementById(optionList);
        if (i == selected){
            currSelect.style.display = 'inline';
        } else {
            currSelect.style.display = 'none';
        }
    }
}

SelectFilterElement.prototype.moveLeft = function() {
    this.filter();
}

SelectFilterElement.prototype.beforeMoveRight = function() {
    this.itemAdded = true;
}


/**
 * Preference bit vectors in js, stores index, name, and current value. Setting a preference
 * to a different value will automatically make an asynchronous request to save the value to the
 * db. Since only a few preferences are accessible/settable through javascript there's no need to
 * compress them into bit vectors. This only handles boolean preferences right now.
 * 
 * @author jmooney
 * @since 150
 */
function PreferenceBits(/* array of {index, name, value} */ preferences) {
    this.bitsByName = {};
    for (var i = 0; i < preferences.length; i++) {
        var pref = preferences[i];
        this.bitsByName[pref.name] = {index: pref.index, val: pref.value };
    }
}

PreferenceBits.prototype.getBoolean = function(/* string */ name) {
    return this.bitsByName[name].val;
}

PreferenceBits.prototype.getIndexByName = function(/* string */ name) {
    return this.bitsByName[name].index;
}

PreferenceBits.prototype.setBoolean = function(/* string */ name, /* boolean */ val) {
    if (typeof val == "boolean") {
        var current = this.bitsByName[name];
        if (current && current.val != val) {
            this.bitsByName[name].val = val;
            this.save(name);
        }
    }
}

PreferenceBits.prototype.save = function(name) {
    var pref = this.bitsByName[name];
    if (pref) {
        // don't care about the response
        XBrowser.postHttpResponse("/_ui/common/request/servlet/PreferenceServlet",
                                  function(response) {},
                                  XBrowser.buildPost({val: pref.val, bit: pref.index}));
    }
}

/**
 * Lookup schema

/**

  we require a map, give as JSON:

  CrtLookups.fields = {
      "Account": {
          "Name" : { label: "Account Name", lookup : null },
          "Owner" : { label: "Account Owner", lookup : "User", lookupLabel: "User"} },
      "User": {
      }
  };

  CrtLookups.primaryObjects = {
      "070..." : { table: "Account", label: "Accounts" },
      "070..." : { table: "Contact", label: "Contacts", detailField: "Account" }
  }

  detail field is used to filter out the lookup that simply points back to
  the parent object

  CrtLookups.initialState = [
      { path: "070abc.Owner.Role", field: "", colId: "073x..."}
  ];


*/

// selected state, which flattens the CrtLookupNode data.  used as input and output
// objectPath = "070abc000001.Owner.Role.Parent"
function CrtField(objectPath, field) {
    this.objectPath = objectPath;
    this.field = field;
}

function CrtFieldCollection() {
    this.fieldsMap = {};
}

CrtFieldCollection.prototype.addField = function(path, fieldName) {
    var field = new CrtField(path, fieldName);
    this.fieldsMap[field.objectPath + '|' + field.field] = field;
}

CrtFieldCollection.prototype.removeField = function(path, fieldName) {
    delete this.fieldsMap[path + '|' + fieldName];
}

CrtFieldCollection.prototype.hasField = function(path, fieldName) {
    var field = this.fieldsMap[path + '|' + fieldName];
    if (field && field != null) {
        return true;
    } else {
        return false;
    }
}

CrtFieldCollection.prototype.clone = function() {
    var copy = new CrtFieldCollection();
    for(var key in this.fieldsMap) {
        copy.fieldsMap[key] = this.fieldsMap[key];
    }
    return copy;
}

// do we really need table and label?  should we just have a pointer to the static fields record?  yes/maybe
function CrtLookupNode(crtObjectId, fieldFromParent, table, label) {
    this.crtObjectId = crtObjectId;
    this.table = table;
    this.label = label;
    this.fieldFromParent = fieldFromParent;
}

CrtLookupsUi.init = function(fields, primaryObjects) {
    var ui = new CrtLookupsUi(fields, primaryObjects);
    return ui;
}

function CrtLookupsUi(fields, primaryObjects) {
    this.fields = fields; // metadata
    this.primaryObjects = primaryObjects;

    this.lookupElem = document.getElementById('lookupBox');
    this.pathElem = document.getElementById('pathBox');

    this.initialSelectedFields = new CrtFieldCollection();
    this.selectedFields = this.initialSelectedFields.clone();
}

CrtLookupsUi.prototype.openOverlayWithPrimary = function() {
    //remove all selection in the layout
    MoveableItem.clearSelectedItems();

    // Parse the picklist value to determine the primaryObject
    var object = CrtObjects.getSelectedValue(document.getElementById(CrtLayoutElement.FIELD_TYPE_SELECT_NAME));
    var iUnderscore = object.indexOf('_');
    if (iUnderscore > -1) {
        object = object.substring(0, iUnderscore);
    }

    // Get the appropriate metadata for the primary object
    this.primaryObjId = object;
    var primaryObject = this.primaryObjects[object];

    // Bring up the overlay
    if (!CrtLookupsUi.lookup) {
        CrtLookupsUi.lookup = document.getElementById('lookupLayer1');
        CrtLookupsUi.lookup = new iframeShim(CrtLookupsUi.lookup);
    }

    //set the lookup header labels
    var headerElem = document.getElementById(CrtLookupConstants.LOOKUP_HEADER);
    var viaLookup = LC.getLabel("CRTLookupLayer","viaLookup");
    var html = '';
    html += '<h3>'+LC.getLabel("CRTLookupLayer","layerHeader")+ ' <span id="headerPrimObj">' + primaryObject.label + '</span> '+ viaLookup +'</h3>';
    html += '<p>' + LC.getLabel("CRTLookupLayer","layerDesc") + ' "' + primaryObject.label + '".' + '</p>';
    headerElem.innerHTML = html;

    this.showGrayout(true);

    //reposition the lookup layer to the center of the window when we open the overlay
    this.repositionLookup();

    CrtLookupsUi.lookup.setStyle('visibility', 'visible');
    //mac needs to set display as well or else scrollbar problems
    CrtLookupsUi.lookup.setStyle('display', 'block');

    //set initial state path to colId Map
    if (CrtLayoutElement.initialStateColIdMap == null) {
        CrtLayoutElement.initialStateColIdMap = {};
        for (var i=0; i< CrtLookups.initialState.length; i++) {
            CrtLayoutElement.initialStateColIdMap[CrtLookups.initialState[i].path+CrtLayoutElement.FIELD_SEP+CrtLookups.initialState[i].field] = CrtLookups.initialState[i].colId;
        }
    }

    //set the initial selected fields for this primary object
    if (CrtLayoutElement.availableSections[CrtLayoutElement.getLookupSectionId(this.primaryObjId)]) {
        this.initialSelectedFields = CrtLayoutElement.availableSections[CrtLayoutElement.getLookupSectionId(this.primaryObjId)].getFieldCollection();
        this.selectedFields = this.initialSelectedFields.clone();
    } else {
        this.initialSelectedFields = new CrtFieldCollection();
        this.selectedFields = this.initialSelectedFields.clone();
    }

    // Initialize our state
    this.path = [];
    this.path.push(new CrtLookupNode(object, null, primaryObject.table, primaryObject.label));
    this.currentTable = primaryObject.table;

    // Render
    this.renderPrimary();

}

CrtLookupsUi.GRAYOUT = 'grayout';

CrtLookupsUi.prototype.repositionLookup = function() {
    var scrollPos = getScrollY()+ getWindowHeight() / 600 * 200;
    CrtLookupsUi.lookup.setStyle('top',scrollPos+'px');
}

CrtLookupsUi.prototype.showGrayout = function(show) {
    var grayout = document.getElementById('overlayLayer1');
    if (!grayout) {
        var body = document.getElementsByTagName("body")[0];
        var node = document.createElement('div');
        node.className = 'overlayLayer';
        node.id = CrtLookupsUi.GRAYOUT;
        //body.appendChild(node);
        body.insertBefore(node, body.firstChild);
        grayout = document.getElementById(CrtLookupsUi.GRAYOUT);
    }
    if (show) {
        var pageDims = this.getPageSizeWithScroll();
        var left = -1 * getObjX(grayout);
        var top = -1 * getObjY(grayout);
        grayout.style.left = left + 'px';
        grayout.style.top = top + 'px';
        grayout.style.width = pageDims[0] + 'px';
        grayout.style.height = pageDims[1] + 'px';
        grayout.style.visibility = 'visible';
    } else {
        grayout.style.visibility = 'hidden';
    }
}

CrtLookupsUi.prototype.getPageSizeWithScroll = function() {
    if (window.innerHeight && window.scrollMaxY) {// Firefox
        yWithScroll = window.innerHeight + window.scrollMaxY;
        xWithScroll = window.innerWidth + window.scrollMaxX;
    } else if (document.body.scrollHeight > document.body.offsetHeight){ // all but Explorer Mac
        yWithScroll = document.body.scrollHeight;
        xWithScroll = document.body.scrollWidth;
    } else { // works in Explorer 6 Strict, Mozilla (not FF) and Safari
        yWithScroll = document.body.offsetHeight;
        xWithScroll = document.body.offsetWidth;
    }
    var arrayPageSizeWithScroll = [xWithScroll,yWithScroll];
    return arrayPageSizeWithScroll;
}

CrtLookupsUi.prototype.getInitialFieldCollection = function() {
   var lookups = new CrtFieldCollection();
   for (var i=0; i< CrtLookups.initialState.length; i++) {
       lookups.addField(CrtLookups.initialState[i].path,CrtLookups.initialState[i].field);
       CrtLayoutElement.initialStateColIdMap[CrtLookups.initialState[i].path+CrtLayoutElement.FIELD_SEP+CrtLookups.initialState[i].field] = CrtLookups.initialState[i].columnId;
   }

   return lookups;
}

CrtFieldCollection.prototype.getAllFieldKeys = function() {
    var fieldKeys = [];
    for (var fieldKey in this.fieldsMap) {
        fieldKeys.push(fieldKey);
    }
    return fieldKeys;
}

CrtFieldCollection.prototype.size = function() {
    var size=0;
    for (var i in this.fieldsMap) {
        if (this.fieldsMap[i]) {
            size++;
        }
    }
    return size;

}
CrtFieldCollection.prototype.isEmpty = function() {
    var isEmpty = true;
    for (var i in this.fieldsMap) {
        if (this.fieldsMap[i]) {
            isEmpty = false;
        }
    }
    return isEmpty;
}



//subtracts the input fields from this collection
//return a new collection
CrtFieldCollection.prototype.subtract = function(fieldsToSubtract) {
    var remainingFields = this.clone();
    var fieldKeys = fieldsToSubtract.getAllFieldKeys();
    for (var i=0; i<fieldKeys.length; i++) {
        if (remainingFields.fieldsMap[fieldKeys[i]]) {
            delete remainingFields.fieldsMap[fieldKeys[i]];
        }
    }
    return remainingFields;
}

CrtFieldCollection.prototype.getField = function(fieldKey) {
    return this.fieldsMap[fieldKey];
}

CrtLookupsUi.prototype.saveOverlay = function() {
    var removedFields= this.initialSelectedFields.subtract(this.selectedFields);
    if (!removedFields.isEmpty()) {
        this.removeFields(removedFields);
    }
    var newlyAddedFields = this.selectedFields.subtract(this.initialSelectedFields);
    if (!newlyAddedFields.isEmpty()) {
        this.addFields(newlyAddedFields);
    }

    //render the lookup Section
    var lookupSection = CrtLayoutElement.availableSections[CrtLayoutElement.getLookupSectionId(this.primaryObjId)];
    if (lookupSection) {
        lookupSection.renderFields();
        lookupSection.swapToSectionDiv();
    }
    this.closeOverlay();

}

CrtLookupsUi.prototype.closeOverlay = function() {
    this.showGrayout(false);
    CrtLookupsUi.lookup.setStyle('visibility', 'hidden');
    CrtLookupsUi.lookup.setStyle('display', 'none');
}

CrtLookupsUi.prototype.removeFields = function(removedFields) {
    //get the lookupSection object
    var lookupSection = CrtLayoutElement.availableSections[CrtLayoutElement.getLookupSectionId(this.primaryObjId)];

    //if we have no selected any fields, remove the entire section
    if (lookupSection) {
        if (this.selectedFields.isEmpty()) {
            //remove the section element and object and options picklist
            lookupSection.remove();
            delete CrtLayoutElement.availableSections[CrtLayoutElement.getLookupSectionId(this.primaryObjId)];
            return;
        }
        //else just remove the fields
        else {
            lookupSection.removeFieldCollection(removedFields);
        }
    }

}

CrtLookupsUi.prototype.addFields = function(addedFields) {
     //get the lookupSection object
     var lookupSection = CrtLayoutElement.availableSections[CrtLayoutElement.getLookupSectionId(this.primaryObjId)];

      //if we don't have a lookup section, we have to make a new picklist option and construct the section
      if(!lookupSection) {
        var selectedElement = document.getElementById(CrtLayoutElement.FIELD_TYPE_SELECT_NAME);
        var primObjDivId = selectedElement.options[selectedElement.selectedIndex].value;
        //header title
        //HACK getting it from the parent select element value
        var headerTitle = selectedElement.options[selectedElement.selectedIndex].text+' ' +LC.getLabel("CRTLookupLayer","viaLookupParen");
        var lookupSectionId = CrtLayoutElement.getLookupSectionId(this.primaryObjId);
        var lookupDivId = CrtLayoutElement.getLookupDivId(lookupSectionId);

        lookupSection = new CrtLookupSection(lookupSectionId, lookupDivId, headerTitle);
        lookupSection.addPicklist(primObjDivId);
      }
      lookupSection.insertFieldCollection(addedFields,this.primaryObjects,this.fields);
}

CrtLayoutElement.getLookupSectionId = function(primObjId) {
    return primObjId + "_" + LookupsUi.LOOKUPS;
}

CrtLayoutElement.getLookupDivId = function(sectionId) {
    return sectionId+CrtLayoutElement.SECTION_DIV_SUFFIX+LookupsUi.LOOKUPS;
}
CrtLookupsUi.prototype.renderPrimary = function() {
    this.setLinksDisabled(true);
    var html = '';

    var primaryObject = this.primaryObjects[this.primaryObjId];

    // Presume that this.currentTable is a primary table
    var fields = this.getFields(this.currentTable);
    for(var fieldName in fields) {
        var field = fields[fieldName];
        if (field.lookup && fieldName != primaryObject.detailField) {
            html += '<br><a href="javascript: CrtLookups.ui.navigateAhead(\'' + fieldName + '\');"> ' + field.label + ' &#187; </a>';
        }
    }
    this.lookupElem.innerHTML = html;
    this.renderPath();
}

/**
 * Given the current path, navigate through a field to go deeper.
 */
CrtLookupsUi.prototype.navigateAhead = function(fieldName) {
    var field = this.getField(this.currentTable, fieldName);
    this.path.push(new CrtLookupNode(null /* todo; how to find id's? */, fieldName, field.lookup, field.label));
    this.currentTable = field.lookup;
    this.renderLookups();
}

/**
 * Navigate to a specific point on the current path.  Since the current
 * path is a stack, any point on the path is navigable by popping the stack.
 */
CrtLookupsUi.prototype.navigateBreadcrumb = function(iBreadcrumb) {
    this.path.length = iBreadcrumb + 1; // pop everything up to the breadcrumb
    if (this.path.length == 1) {
        var node = this.path[0];
        this.currentTable = node.table;
        this.renderPrimary();
    } else {
        var node = this.path[iBreadcrumb];
        var previousNode = this.path[iBreadcrumb - 1];
        this.currentTable = this.getField(previousNode.table, node.fieldFromParent).lookup;
        this.renderLookups();
    }
}

// renders this.currentTable
CrtLookupsUi.prototype.renderLookups = function() {
    this.renderCheckBoxes();
    this.setLinksDisabled(false);
    this.renderPath();
}

CrtLookupsUi.prototype.renderCheckBoxes = function() {
    var html = '';
    var fields = this.getFields(this.currentTable);
    for(var fieldName in fields) {
        var field = fields[fieldName];
        var checked = (this.isSelected(fieldName) ? 'checked' : '');
        if (field.lookup) {
            html += '<input type=checkbox ' + checked + ' onclick="CrtLookups.ui.toggleField(this, \'' + fieldName + '\');"> ' + field.label;
            if (this.path.length <= CrtLookupConstants.LOOKUP_DEPTH_LIMIT) {
                 html += ' &nbsp; <a href="javascript: CrtLookups.ui.navigateAhead(\'' + fieldName + '\');"> ' + LC.getLabel("CRTLookupLayer","viewRelatedFields") + '</a>';
            }
            html += '<br>';
        } else {
            html += '<input type=checkbox ' + checked + ' onclick="CrtLookups.ui.toggleField(this, \'' + fieldName + '\');"> ' + field.label + '<br>';
        }
    }
    this.lookupElem.innerHTML = html;
}

CrtLookupsUi.prototype.setLinksDisabled = function(disabled) {
    var controlElem = document.getElementById(CrtLookupConstants.CONTROL_ELEM_1);
    var innerHTML = '';

    //this label has to be called separately or else we get undefine for some weird reason
    var clearAll = LC.getLabel("CRTLookupLayer","ClearAll");
    if (disabled) {
        controlElem.className = 'crtDisabledLink';

        innerHTML = LC.getLabel("Report","SelectAll") + ' | ' + clearAll;
    } else {
        controlElem.className = '';
        innerHTML = '<a href="javascript: CrtLookups.ui.selectAll();">'+LC.getLabel("Report","SelectAll")+'</a> | <a href="javascript: CrtLookups.ui.clearAll();">'+clearAll+'</a>';
    }
    controlElem.innerHTML = innerHTML;
}

CrtLookupsUi.prototype.selectAll = function() {
    var fields = this.getFields(this.currentTable);
    for (var fieldName in fields) {
        if (!this.selectedFields.hasField(this.getFlatPath(), fieldName)) {
            this.selectedFields.addField(this.getFlatPath(), fieldName);
        }
    }
    this.renderCheckBoxes();
}

CrtLookupsUi.prototype.clearAll = function() {
    var fields = this.getFields(this.currentTable);
    for (var fieldName in fields) {
        if (this.selectedFields.hasField(this.getFlatPath(), fieldName)) {
            this.selectedFields.removeField(this.getFlatPath(), fieldName);
        }
    }

    this.renderCheckBoxes();
}

CrtLookupsUi.prototype.renderPath = function() {
    var html = '<b>'+LC.getLabel("CRTLookupLayer","Path")+ ': </b>&nbsp;';

    var previousTable;
    for(var i = 0; i < this.path.length; i++) {
        var node = this.path[i];

        // Get the labels of the path elements
        var nodeText;
        if (node.fieldFromParent == null) {
            nodeText = this.primaryObjects[node.crtObjectId].label;
            previousTable = node.table;
        } else {
            var field = this.getField(previousTable, node.fieldFromParent);
            nodeText = field.label;
            previousTable = field.lookup;
        }

        var delimiter = '';
        if (i == 1) delimiter = '&#187;';
        if (i > 1) delimiter = '>';
        html += ' ' + delimiter + ' <a href="javascript: CrtLookups.ui.navigateBreadcrumb(' + i + ');">' + nodeText + '</a>';
    }
    if (this.path.length > CrtLookupConstants.LOOKUP_DEPTH_LIMIT) {
        html += '<div class="maxLimit">'+LC.getLabel("CRTLookupLayer","maxDepthLimit") + "</div>";
    }

    this.pathElem.innerHTML = html;
}

CrtLookupsUi.prototype.toggleField = function(checkbox, fieldName) {
    if (checkbox.checked) {
        this.selectedFields.addField(this.getFlatPath(), fieldName);
    } else {
        this.selectedFields.removeField(this.getFlatPath(), fieldName);
    }
}

CrtLookupsUi.prototype.isSelected = function(fieldName) {
    return this.selectedFields.hasField(this.getFlatPath(), fieldName);
}

CrtLookupsUi.prototype.getFlatPath = function() {
    var flatPath;
    for(var i = 0; i < this.path.length; i++) {
        var node = this.path[i];
        if (node.fieldFromParent == null) {
            flatPath = node.crtObjectId;
        } else {
            flatPath += '.' + node.fieldFromParent;
        }
    }
    return flatPath;
}

CrtLookupsUi.prototype.getFields = function(table) {
    return this.fields[table];
}

CrtLookupsUi.prototype.getField = function(tableName, fieldName) {
    return this.fields[tableName][fieldName];
}

/**
 * Holds data about fields on a detail page for inline editing.
 * 
 * @author jmooney
 * @since 150
 */
function InlineEditData(json) {
    this.isEditable = json[InlineEditConstants.EDITABLE];
    this.sysMod = json[InlineEditConstants.LAST_MOD];
    this.id = json[InlineEditConstants.ENTITY_ID];
    this.fields = {};
    this.dependencyGroups = [];
    this.hasCompoundFields = false;
    this.allFieldData = json[InlineEditConstants.FIELD_DATA];
    this.dynamicDataUrl = json[InlineEditConstants.DYNAMIC_DATA];
    this.loadedDynamicData = false;
    this.sentRequest = false;
    this.currentField = null;
    sfdcPage.initInlineEdit(this);
}

InlineEditData.prototype.init = function() {
    this.createFields();
    if (this.hasCompoundFields) {
        InlineEditField.overlay = new InlineEditDialog();
        InlineEditField.overlay.register();
    }
    var self = this;
    addEvent(document, "click", function() { self.closeCurrentField(); }, false);
}

InlineEditData.prototype.createFields = function() {
    if (this.allFieldData) {
        for (var i = 0; i < this.allFieldData.length; i++) {
            var fieldData = this.allFieldData[i];
            var field = InlineEditField.createField(fieldData);
            this.fields[fieldData.fieldId] = field;
            if (field) {
                if (field.compound) {
                    this.hasCompoundFields = true;
                }
            }
        }
        // create dependencies after all fields are loaded
        for (var i in this.fields) {
            var f = this.fields[i];
            if (f && f.controllerId) {
                this.createDependency(f);
                this.hasCompoundFields = true;
            }
        }
    }
}

InlineEditData.prototype.createDependency = function(field) {
    if (field.group) {
        // already in a group, nothing to do
        return;
    }
    // try to find existing group of a controller, and add to that group
    var f = this.getField(field.controllerId);
    var existingGroup = null;
    while (f != null) {
        existingGroup = f.group;
        if (existingGroup) {
            break;
        }
        f = this.getField(f.controllerId);
    }
    if (existingGroup) {
        // found a group, add fields until we get back to f
        var temp = field;
        var start = existingGroup.length;
        while (temp != f) {
            existingGroup.splice(start, 0, temp.id);
            temp.group = existingGroup;
            if (temp instanceof BooleanField) {
                // tell boolean fields to wait for the rest of the dependency to load
                temp.waitForLoad = true;
            }
            temp = this.getField(temp.controllerId);
        }
    } else {
        // have to create a new group
        var newGroup = [];
        var temp = field;
        while (temp != null) {
            newGroup.unshift(temp.id);
            temp.group = newGroup;
            if (temp instanceof BooleanField) {
                // tell boolean fields to wait for the rest of the dependency to load
                temp.waitForLoad = true;
            }
            temp = this.getField(temp.controllerId);
        }
        this.dependencyGroups.push(newGroup);
    }
}

InlineEditData.prototype.openField = function(field) {
    if (field.group) {
        field = this.fields[field.group[0]];
    } else if (!this.isCurrentField(field)) {
        this.closeCurrentField();
    }
    this.currentField = field;
    if (field.waitForLoad && !this.loadedDynamicData) {
        this.loadDynamicData();
        return;
    } else if (field.group) {
        this.openGroup(field.group);
    } else {
        field.openField();
    }
}

InlineEditData.prototype.openGroup = function(group) {
    var first = this.fields[group[0]];
    if (!first.created) {
        var div = document.createElement("div");
        div.className = "inlineEditDiv";
        document.body.appendChild(div);
        // create a table for the edit elements in the group
        var html = [];
        html.push("<table border=0>");
        for (var i = 0; i < group.length; i++) {
            var f = this.fields[group[i]];
            html.push("<tr><td class='labelCol'>");
            html.push(f.getFieldLabel());
            html.push("</td><td></td></tr>");
            if (f.state == InlineEditState.EDIT) {
                f.createEditDiv();
            } else {
                f.createDummy();
            }
        }
        html.push("</table>");
        div.innerHTML = html.join('');
        // after creating all select elements we can load the dependencies and insert into the table
        for (var i = 0; i < group.length; i++) {
            var f = this.fields[group[i]];
            var node;
            if (f.state == InlineEditState.EDIT) {
                f.load();
                div.firstChild.rows[i].lastChild.appendChild(f.editDiv);
            } else {
                div.firstChild.rows[i].lastChild.innerHTML = f.initialHTML;
            }
        }
        InlineEditField.overlay.addField(first.id, div, LC.getLabel("DependentElement", "dependentFields"));
    }
    InlineEditField.overlay.setActiveField(first.id);
    InlineEditField.overlay.setMaxWidth(400);
    InlineEditField.overlay.show();
}

InlineEditData.prototype.isCurrentField = function(field) {
    return this.currentField && this.currentField == field && !this.currentField.group;
}

InlineEditData.prototype.closeCurrentField = function() {
    if (this.currentField) {
        if (this.currentField.group) {
            this.closeGroup(this.currentField.group);
            this.currentField = null;
        } else {
            this.currentField.closeField();
            this.currentField = null;
        }
    }
}

InlineEditData.prototype.closeGroup = function(group) {
    for (var i = 0; i < group.length; i++) {
        var f = this.fields[group[i]];
        if (f.state == InlineEditState.EDIT) {
            var newValue = f.getValueFromEdit();
            if (f.isDifferentValue(newValue)) {
                if (!f.changed) {
                    f.changed = true;
                    addStyleClass(f.readDiv, "inlineEditModified");
                }
                f.currentValue = newValue;
                f.updateReadElement();
                if (f.changed) {
                    f.readDiv.appendChild(f.undoButton);
                    f.undoButton.style.display = "inline";
                }
            } else if (f.changed) {
                f.changed = false;
                f.currentValue = newValue;
                delStyleClass(f.readDiv, "inlineEditModified");
                f.updateReadElement();
                f.undoButton.style.display = "none";
            }
        }
    }
    InlineEditField.overlay.hide();
}

InlineEditData.prototype.resetCurrentField = function() {
    this.resetField(this.currentField);
    this.currentField = null;
}

InlineEditData.prototype.resetFieldById = function(id) {
    this.resetField(this.fields[id]);
}

InlineEditData.prototype.resetField = function(field) {
    if (field) {
        if (field.group) {
            this.resetGroup(field.group);
        } else {
            field.reset();
        }
    }
}

InlineEditData.prototype.resetGroup = function(group) {
    for (var i = 0; i < group.length; i++) {
        var f = this.fields[group[i]];
        if (f.state == InlineEditState.EDIT) {
            f.reset();
        }
    }
    InlineEditField.overlay.hide();
}

InlineEditData.prototype.getField = function(id) {
    if (id) {
        return this.fields[id];
    }
    return null;
}

InlineEditData.prototype.save = function() {
    if (this.isEditable) {
        this.closeCurrentField();
        var saveData = {};
        saveData[InlineEditConstants.ENTITY_ID] = this.id;
        saveData[InlineEditConstants.LAST_MOD] = this.sysMod;
        saveData[EditPageConstants.pSAVE] = "1"
        for (var id in this.fields) {
            var field = this.fields[id];
            if (field) {
                field.clearError();
                field.addSaveData(saveData);
            }
        }
        var self = this;
        XBrowser.postHttpResponse("/ui/common/InlineEditEntitySave",
                                  function(response) { self.handleResponse(response.responseText); },
                                  XBrowser.buildPost(saveData),
                                  // check the errorMsg??
                                  function(response) {
                                      var url = escape(window.location.pathname+window.location.search);
                                      window.location.replace("/ex/errorduringprocessing.jsp?retURL=" + url);
                                  }
                                  );
    }
}

InlineEditData.prototype.handleResponse = function(responseText) {
    var response = Util.evalAjaxServletOutput(responseText);
    if (response[InlineEditConstants.SUCCESS]) {
        sfdcPage.refreshDetail();
    } else {
        this.handleErrors(response);
    }
}

InlineEditData.prototype.handleErrors = function(response) {
    sfdcPage.setError(response[InlineEditConstants.NON_SPECIFIC_ERRORS]);
    var specific = response[InlineEditConstants.VALIDATION_ERRORS];
    var first;
    for (var id in specific) {
        var f = this.getField(id);
        // error could be on a field that isn't on the page if somebody changed metadata after page 
        if (f) {
            // try to find the first simple field with an error and open it
            if (!f.overlay && !f.group) {
                // all fields have same offsetParent so use local y coord
                if (!first || first.tableCell.offsetTop > f.tableCell.offsetTop) {
                    first = f;
                }
            }
            f.setError(specific[id]);
        }
    }
    if (first) {
        this.openField(first);
    }
}

InlineEditData.prototype.revert = function() {
    if (this.isEditable) {
        this.closeCurrentField();
        var resetGroups = {};
        for (var id in this.fields) {
            var field = this.fields[id];
            if (field) {
                if (field.changed) {
                    // groups need to be reset separately
                    if (field.group) {
                        resetGroups[field.group[0]] = true;
                    } else {
                        field.reset();
                        field.updateReadElement();
                    }
                } else {
                    field.clearError();
                }
            }
        }
        for (var id in resetGroups) {
            this.resetGroup(this.fields[id].group);
        }
    }    
}

InlineEditData.prototype.loadDynamicData = function() {
    if (!this.sentRequest) {
        this.sentRequest = true;
        var self = this;
        XBrowser.createDynamicScript(this.dynamicDataUrl, function() { self.dynamicDataLoaded(); });
    }
}

InlineEditData.prototype.dynamicDataLoaded = function() {
    if (!this.loadedDynamicData) {
        this.loadedDynamicData = true;
        if (this.currentField.waitForLoad) {
            this.openField(this.currentField);
        }
    }
}


/**
  A MotifElement just tracks the relevent peices of the MotifElement

  @author polcari
  @since 144

  @paramName  This is the parameter name which prepends in the inputElements
*/

function MotifElement(id, descCellId, iconId, _motifKey) {
  if (arguments.length > 0) {
    this.init(id, descCellId, iconId, _motifKey);
  }
}

MotifElement.prototype.init	= function(id, descCellId, iconId, _motifKey) {
    this.motifElement = document.getElementById(id);
    this.motifDescCell = document.getElementById(descCellId);
    this.motifIcon = document.getElementById(iconId);
    this.motifKey = _motifKey;

    this.motifElement.motifElement = this;
}

MotifElement.prototype.getDescription = function() {
  return this.motifDescCell.firstChild.nodeValue;
}
/**
 * Functional Dialog with a scrollable content area and a summary.
 * TODO: some might not want any summary??
 * 
 * @author jmooney
 * @since 150
 */

function FunctionalDialog(id, isModal, title) {
    this.id = id;
    this.isModal = isModal;
    this.extraClass = "functionalDialog";
    this.type = OverlayDialogType.FUNCTIONAL;
    this.width = OverlayDialog.MAX_WIDTH;
    this.maxHeight = OverlayDialog.MAX_HEIGHT;
    this.setupDefaultButtons();
    this.title = title;
    var self = this;
    addEvent(window, "resize", function() { self.resizeEvent(); }, false);
    addEvent(document, "keydown", function(e) { self.handleKeyPress(e); }, false);
    if (XBrowser.userAgent.isIE6) {
        addEvent(window, "scroll", function() { self.scrollEvent(); }, false);
    }
}

FunctionalDialog.prototype = new OverlayDialog();

FunctionalDialog.prototype.setInnerHeight = function() {
    // gotta add a bit for the scrollbars
    var inner = this.getContentElement();
    var height = inner.offsetHeight + (Math.min(this.maxHeight, getWindowHeight() - 40) - this.dialog.offsetHeight);
    inner.style.height = Math.max(OverlayDialog.MIN_HEIGHT, height) + "px";
}

// should split this up into sub-classes for each type
FunctionalDialog.prototype.createContent = function() {
    var content = document.getElementById(this.id + "Content");
    var html = [];
    html.push("<h2 id='");
    html.push(this.id);
    html.push("Header'>");
    html.push(this.header);
    html.push("</h2>");
    if (this.info) {
        html.push("<p id='");
        html.push(this.id);
        html.push("Info'>");
        html.push(this.info);
        html.push("</p>");
    }
    html.push("<div class='scrollableArea'  id='");
    html.push(this.id);
    html.push("Inner'></div><div class='split'><img src='/img/overlaypointer.gif' class='pointer'></div><div class='summaryArea' id='");
    html.push(this.id);
    html.push("Summary'></div>");

    this.createButtons(html);
    content.innerHTML = html.join("");
}

/**
 * convenience method to set the innerHTML of the scrollable content area
 */
FunctionalDialog.prototype.setContentInnerHTML = function(html) {
    this.getContentElement().innerHTML = html;
}

/**
 * convenience method to import another node into the scrollable content area
 */
FunctionalDialog.prototype.importContentNode = function(element) {
    this.setContentInnerHTML("");
    this.getContentElement().appendChild(element);
}

/**
 * returns the content element
 */
FunctionalDialog.prototype.getContentElement = function() {
    return document.getElementById(this.id + "Inner");
}

/**
 * convenience method to set the innerHTML of the summary
 */
FunctionalDialog.prototype.setSummaryInnerHTML = function(html) {
    this.getSummaryElement().innerHTML = html;
}

/**
 * convenience method to import nodes into the summary area
 */
FunctionalDialog.prototype.importSummaryNode = function(element) {
    this.setSummaryInnerHTML("");
    this.getSummaryElement().appendChild(element);
}

/**
 * returns the summary element
 */
FunctionalDialog.prototype.getSummaryElement = function() {
    return document.getElementById(this.id + "Summary");
}


/**
  A select element with details displayed on the right

  @author eli
  @since 148

*/
function MailmergeTemplateSelectElement(_id, _items, _fileDownloadUrl) {
  this.id = _id;
  this.items = _items;
  this.fileDownloadUrl = _fileDownloadUrl;
  var element = this;
  sfdcPage.appendToOnloadQueue(function() {element.init()});
}

MailmergeTemplateSelectElement.prototype.init = function() {
  var selectEle = document.getElementById(this.id);
  var mmtseEle = this;
  var previewEle = document.getElementById(MailmergeTemplateSelectElementConst.TEMPLATE_VIEW_BUTTON + '_' + this.id);
  if (previewEle) {
	  addEvent(previewEle, 'mouseup', function() {MailmergeTemplateSelectElement.showPreview(mmtseEle.fileDownloadUrl, selectEle);}, false);
  }
  addEvent(selectEle, 'mousemove', function() {MailmergeTemplateSelectElement.showDetails(mmtseEle.id, selectEle, mmtseEle.items);}, false);
  addEvent(selectEle, 'change', function() {MailmergeTemplateSelectElement.showDetails(mmtseEle.id, selectEle, mmtseEle.items);}, false);
  MailmergeTemplateSelectElement.showDetails(mmtseEle.id, selectEle, mmtseEle.items);
}

MailmergeTemplateSelectElement.showDetails = function(_id, selectEle, items) {
  if (selectEle.selectedIndex < 0)
  	return;
  var selectedOption = selectEle.options[selectEle.selectedIndex];
  var templateItem = (selectedOption) ? items[selectedOption.value] : null;
  if (templateItem == null)
  	return;
  var tName = templateItem[MailmergeTemplateSelectElementConst.TEMPLATE_TITLE];
  var description = templateItem[MailmergeTemplateSelectElementConst.TEMPLATE_DESCRIPTION];
  document.getElementById(MailmergeTemplateSelectElementConst.TEMPLATE_TITLE + '_' + _id).innerHTML = tName;
  document.getElementById(MailmergeTemplateSelectElementConst.TEMPLATE_DESCRIPTION + '_' + _id).innerHTML = description;
}

MailmergeTemplateSelectElement.showPreview = function(url, selectEle) {
  if (selectEle.selectedIndex < 0)
  	return;
  window.open(url + "?file=" + selectEle.options[selectEle.selectedIndex].value, '_blank');
}
/**
 * @author zzhou
 * since 148
 * AvailableSection fields javascript code; also see AvailableSection.js, CrtLayout.js, Lookups.js
 */

CrtLayoutElement.initAvailField = function(sectionId, fieldValue, fieldLabel, inLayout, isLookup) {
    var availField = new AvailableField(sectionId,fieldValue,fieldLabel, inLayout, isLookup);
    CrtLayoutElement.availableSections[sectionId].addField(availField);
}

function AvailableField(sectionId,fieldValue,fieldLabel, inLayout, isLookup) {
    this.id = fieldValue;
    this.sectionId = sectionId;
    this.fieldValue = fieldValue;
    this.fieldLabel = escapeHTML(fieldLabel);
    this.isSelected = false;
    this.pageNum = 1;
    this.inLayout = inLayout;
    this.isLookup = isLookup;
    //this takes too long to set for lookup fields -- set it when we actually hover
    this.displayName = this.isLookup ? null : this.fieldLabel;

    //TODO type
    this.type = 's';
    this.mousingOver = false;

}

AvailableField.prototype.getFieldValue = function() {
    return this.fieldValue;
}

AvailableField.prototype.render = function() {
    var className = 'availField';
    if (this.inLayout) {
        className += ' usedAvailField';
    } else {
        className += ' unusedAvailField';
    }
    if (this.isSelected) {
        className += ' ' +CrtLayoutElement.CSS_CLASS_SELECTED_ITEM;
    }
    if (!this.elem) {
        this.elem = document.getElementById(this.id);
    }
    if (this.elem) {
        this.elem.className = className;
    }

}

AvailableField.prototype.formatDName = function() {
    if (this.fieldLabel.length > CrtLayoutElement.MAX_DISPLAY_FIELD_LENGTH) {
        return this.fieldLabel.substring(0,CrtLayoutElement.MAX_DISPLAY_FIELD_LENGTH-2)+"...";
    }
    else {
        return this.fieldLabel;
    }
}

AvailableField.prototype.setToUsed = function() {
    var self = this;
    self.inLayout = true;
    if (this.mouseOverDiv) {
        this.mouseOverDiv.innerHTML = '';
        this.mouseOverDiv.style.display = 'none';
        this.mouseOverDiv = null;
        this.mousingOver = false;
    }
}

AvailableField.prototype.setToAvailable = function() {
    this.inLayout = false;
}

AvailableField.prototype.handleMouseOver = function(evt) {
    var evt = getEvent(evt);
    this.posX = getMouseX(evt);
    this.posY = getMouseY(evt);
    if (!this.inLayout && !CrtLayoutElement.dragMove && !this.mousingOver) {
        var self = this;
        this.timeOut = setTimeout(function() {self.setupMouseOverDiv();},CrtLayoutElement.HOVER_TIME_OUT)
        this.mousingOver = true;
    }
}

AvailableField.prototype.handleMouseOut = function (evt) {
    if (this.inLayout) {
        return;
    }
    document.getElementById(CrtLayoutElement.HOVER_DIV).style.display = 'none';
    clearTimeout(this.timeOut);
    this.mousingOver = false;
}

AvailableField.prototype.handleMouseDown = function(evt) {
      if (this.inLayout) {
          CrtLayoutElement.clearTextSelection();
          return;
      }
      var evt = getEvent(evt);
      if (!evt.shiftKey && !evt.ctrlKey && !this.isSelected) {
          this.toggleSelected(false);
      }
      CrtLayoutElement.mouseDown = true;
}

AvailableField.prototype.handleMouseClick = function(evt) {
    if (this.inLayout) {
        CrtLayoutElement.clearTextSelection();
        return;
    }
    var evt = getEvent(evt);
    if (evt.ctrlKey) {
        this.toggleSelected(true);
    } else {

    }
}

AvailableField.prototype.toggleSelected = function(isCtrlSelect) {
        if (this.isSelected) {
            this.removeSelection(false);
        }
        else {
            if (!isCtrlSelect) {
                MoveableItem.clearSelectedItems();
            }
            this.addSelection();
        }
}

//remove this field from the layout; used currently by the lookup sections
AvailableField.prototype.removeFromLayout = function() {
    if (this.inLayout) {
        var itemObj = CrtLayoutElement.lookupItemPosMap[this.fieldValue];
        //set this cell to empty and return the id of the table to reformat
        itemObj.handleMoved();
    }
}

AvailableField.prototype.removeSelection = function(noBucketRemove) {
    if (!noBucketRemove) {
        this.moveableItem.removeSelection();
    }
    this.moveableItem = null;
    this.removeSelectionFormatting();
}

AvailableField.prototype.removeSelectionFormatting = function() {
    this.isSelected = false;
    this.render();
}

AvailableField.prototype.handleMoved = function() {
    this.setToUsed();
    this.removeSelectionFormatting();
}

AvailableField.prototype.addSelection = function() {
    var addSelection;
    this.moveableItem = this.createMoveableItem();
    addSelection = this.moveableItem.addSelection();
    //are we mixing the selection Bucket?
    if (addSelection) {
        this.addSelectionFormatting();
    }
}

AvailableField.prototype.createMoveableItem = function() {
    return new MoveableItem(this.fieldValue,this.fieldLabel,this.displayName, null, this.type, this, false, false, this.isLookup, false);
}

AvailableField.prototype.addSelectionFormatting = function() {
    this.isSelected = true;
    this.render();
}

AvailableField.prototype.handleMoveTo = function() {
    this.setToAvailable();
    this.render();
}

AvailableField.prototype.setupMouseOverDiv = function(evt) {
    var moElem = document.getElementById(CrtLayoutElement.HOVER_DIV);
    var html = '';
    var posx = this.posX-210;
    var posy = this.posY-80;
    html += '<div class="mouseOverHeader">'+ this.fieldLabel + (this.isLookup ? ' ' + LC.getLabel("CRTLookupLayer","viaLookupParen") : '') + '</div>';
    if (!this.displayName) {
        this.displayName = CrtLayoutElement.getDisplayPath(this.fieldValue,true);
    }
    html += '<div class="mouseOverBody">'+LC.getLabel("CrtLayout","CustomLabel")+': '+ this.displayName + '<br>';
    if (this.isLookup) {
        html += '<br>';
        html += LC.getLabel("CrtLayout","lookupPath") + ' ' + CrtLayoutElement.getDisplayPath(this.fieldValue,false);
        html += '<br>';
    }
    html += '<br>' + LC.getLabel("CrtLayout","availFieldHover");
    moElem.innerHTML = html;
    moElem.style.display = 'block';
    moElem.style.left = posx+"px";
    moElem.style.top = posy+"px";
    return moElem;
}


function ImageSelectElement (hiddenElementId, imageId, partialImgURL) {
 var self = this;
 this.inputElement = document.getElementById(hiddenElementId);
 this.image = document.getElementById(imageId);
 this.partialImageURL = partialImgURL;  //org-specific

//hook this onto the hidden input box, as that's the main id for ImageInputElements
 this.inputElement.imageSelectElement = this;

 this.inputElement.onchange =
 function() {
  //this should set the visible image
  self.image.src = self.partialImageURL + self.inputElement.value;
  }
}

ImageSelectElement.prototype.isNull = function() {
  return (!((this.inputElement) && (this.inputElement.value) && (this.inputElement.value.length > 0)));
}


/*
* @author mpolcari
* @since 144
*/
function ListPage() {}

ListPage.prototype = new GenericSfdcPage();

/**
 *  javascript object for report wizard
 */
function Reports() {}

Reports.prototype = new GenericSfdcPage();

Reports.prototype.registerTopNEvents = function() {
    var topnSelect = document.getElementById('topn');
    addEvent(topnSelect, 'change', this.showTopNSections, false);
}

Reports.prototype.showTopNSections = function() {
    var topnselect = document.getElementById('topn');
    var sortrow = document.getElementById('sortbyrow');
    var orderrow = document.getElementById('orderrow');
    var box = document.getElementById('topn_custom');
    var section = document.getElementById('top_custom_section');

    if (topnselect.value == 'all') {
        sortrow.style.display = 'none';
        orderrow.style.display = 'none';
        section.style.display = 'none';
        sortrow.style.display = 'none';
    } else if (topnselect.value == 'custom') {
        sortrow.style.display = '';
        orderrow.style.display = '';
        section.style.display = 'inline';
        box.focus();
    } else {
        sortrow.style.display = '';
        orderrow.style.display = '';
        section.style.display = 'none';
    }
}

/** functions for floating column headers */
Reports.prototype.initFch = function() {
    // Grab some key elements
    this.headerMarker = document.getElementById(ReportsFch.FCH_AREA);
    this.headerMarkerY = getObjY(this.headerMarker) + 20;

    // The offset here produces an "overhang" effect.
    // It needs to equal Reports.css.floatingHeader.padding-left/right
    // -1 is for the border
    this.headerX = getObjX(this.headerMarker) - 1 - 3;
    this.floatingHeader = document.getElementById(ReportsFch.FLOATING_HEADER);

    // Init the floating header to invisible
    this.isVisible = false;
    this.floatingHeader.style.display = 'none';
    if (XBrowser.userAgent.isIE6) {
        this.floatingHeader.style.position = 'absolute';
    }
    this.matchHeaderSize();

    // Attach events
    var self = this;
    addEvent(window, 'scroll', function() { self.handleFchScroll() }, false);
    addEvent(window, 'resize', function() { self.matchHeaderSize() }, false);
}

/**
 * Match the look-and-feel of the real header
 */
Reports.prototype.matchHeaderSize = function() {
    var floatingRow = this.floatingHeader.childNodes[0].rows[0]; // this.floatingHeader is a table
    var floatingCells = floatingRow.cells;
    var row = document.getElementById(ReportsFch.HEADER_ROW);
    this.floatingHeader.style.width = row.offsetWidth + 'px';
    for(var i = 0; i < row.cells.length; i++) {
        var cell = row.cells[i];
        // -7 is for the existing padding
        floatingCells[i].style.width = (cell.offsetWidth - 7) + 'px';
    }

    var left = XBrowser.userAgent.isIE6 ? this.headerX : this.headerX - getScrollX();
    this.floatingHeader.style.left = left + 'px';
    this.headerBottomY = this.headerMarkerY + this.headerMarker.offsetHeight - 70 - this.floatingHeader.offsetHeight;
}

Reports.prototype.handleFchScroll = function() {
    var scrollY = getScrollY();
    var inRange = scrollY >= this.headerMarkerY && scrollY < this.headerBottomY;
    if (!this.isVisible) {
        if (inRange) {
            // Moved below the top (the regular header), so display the floating header
            this.matchHeaderSize();
            this.floatingHeader.style.display = 'block';
            // this.floatingHeader.offsetHeight equals 0 until the floating header is actually visible
            this.headerBottomY = this.headerMarkerY + this.headerMarker.offsetHeight - 70 - this.floatingHeader.offsetHeight;
            this.isVisible = true;
        }
    } else /* isVisible */ {
        if (!inRange) {
            // Moved above the top (the regular header), so hide the floating header
            this.floatingHeader.style.display = 'none';
            this.isVisible = false;
        } else /* inRange */ {
            if (XBrowser.userAgent.isIE6) {
                // For IE, re-position manually while visible
                this.floatingHeader.style.top = (getScrollY() + 20) + 'px';
            }

            // Horizontal fixity
            // The conditional here is meant to be true whenever we are using position: fixed-- for everything except IE6
            // IE6 uses position: absolute so horizontal fixity is already established
            if (!XBrowser.userAgent.isIE6) {
                this.floatingHeader.style.left = (this.headerX - getScrollX()) + 'px';
            }
        }
    }
}

Reports.oldSummaryValue = null;

Reports.saveOldSummaryValueIfExists = function() {
    if(document.getElementById(ReportConstants.pSubTotalBy0) != null) {
        Reports.oldSummaryValue = document.getElementById(ReportConstants.pSubTotalBy0).value;
    }
}

Reports.runReportPageOnSubmit = function(formName, format, numBreaks) {
    if(Reports.oldSummaryValue != document.getElementById(ReportConstants.pSubTotalBy0).value) {
        if (document.forms[formName][format].value == 't' && document.getElementById(ReportConstants.pSubTotalBy0).value == '') {
            document.forms[formName][format].value = 'tt';

            //we also need to clear all the breaks
            for (var i = 0; i < numBreaks; i++) {
                document.forms[formName]['break' + i].value = '';
            }
        } else if (document.forms[formName][format].value == 'tt' && document.getElementById(ReportConstants.pSubTotalBy0).value != '') {
            document.forms[formName][format].value = 't';
        }
    }
}

Reports.formatPageOnSubmit = function(formName, numBreaks) {
    if (document.getElementById('format_tabular').checked == true) {
        for (var i = 0; i < numBreaks; i++) {
            document.forms[formName]['break' + i].value = '';
        }
    }
}




/**
 * Class contains JavaScript methods for manipulating dueling lists.
 * TODO there are several static methods here that should eventually be refactored into
 * instance methods.
 * 
 * @author mpaksoy
 * @since 150
 * 
 */

/**
 * Constructor
 * 
 * @param ids Array of selection element ids. These are used to read and restore the selection list
 * contents.
 */
function DuelingListBoxesElement(selectionIds, warnId) {
    this.sListIds=selectionIds;
    this.warnDiv=warnId;
}

/**
 * Sets a flag to make sure the state is stored only once.
 * Stores state using storeState()
 */
DuelingListBoxesElement.prototype.storeStateOnce = function() {
    if (this.isStored) {
        return;
    }
    this.isStored=true;
    this.storeState();
}

/**
 * Note: use storeStateOnce() to prevent overriding the initial state.
 * Stores the ordering, values and labels of option elements inside all selection lists.
 */
DuelingListBoxesElement.prototype.storeState = function() {
    this.selectContents=new Array();
    for (var i = 0; i < this.sListIds.length; i++) {
        var curSelect = document.getElementById(this.sListIds[i]);
        if (!curSelect) {
            continue;
        }
        this.selectContents[i]=new Array();
        for (var j = 0; j < curSelect.options.length; j++) {
            this.selectContents[i][j]=[curSelect.options[j].text, curSelect.options[j].value];
        }
    }
}

/**
 * Restore selection list contents from stored state.
 */
DuelingListBoxesElement.prototype.resetSelection = function() {
    if (!this.isStored)
        return;

    for (var i=0; i < this.sListIds.length; i++) {
        var curSelect = document.getElementById(this.sListIds[i]);
        if (!curSelect) {
            continue;
        }
        
        Util.refreshDynamicSelect(curSelect, this.selectContents[i]);
    }
}

/**
 * Displays a warning message using the warnDiv field (should be set using constructor)
 */
DuelingListBoxesElement.prototype.quickWarn = function(message) {
    DuelingListBoxesElement.warning(this.warnDiv, message);
}

/**
 * Removes the warning message using the warnDiv field (should be set using constructor)
 */
DuelingListBoxesElement.prototype.quickUnwarn = function() {
    DuelingListBoxesElement.removeWarning(this.warnDiv);
} 

// instMove* methods are wrappers around the corresponding static move* methods that store selection box state on first
// modification
DuelingListBoxesElement.prototype.instMoveDown = function(sourceSelect, topSourceValue, radicalValue, unmovableAlertMessage, 
        warnDivId) {
    this.storeStateOnce();
    DuelingListBoxesElement.moveDown(sourceSelect, topSourceValue, radicalValue, unmovableAlertMessage, warnDivId);
}

DuelingListBoxesElement.prototype.instMoveUp = function(sourceSelect, topSourceValue, radicalValue, unmovableAlertMessage,
        warnDivId) {
    this.storeStateOnce();
    DuelingListBoxesElement.moveUp(sourceSelect, topSourceValue, radicalValue, unmovableAlertMessage,
            warnDivId);
}

DuelingListBoxesElement.prototype.instMoveBottom = function(sourceSelect, warnDivId) {
    this.storeStateOnce();
    DuelingListBoxesElement.moveBottom(sourceSelect, warnDivId);
}

DuelingListBoxesElement.prototype.instMoveTop = function(sourceSelect, warnDivId) {
    this.storeStateOnce();
    DuelingListBoxesElement.moveTop(sourceSelect, warnDivId);
}


DuelingListBoxesElement.prototype.instMoveOption = function(sourceSelect, targetSelect, keepSourceLabel, unmovableSourceValues,
        unmovableAlertMessages, keepTargetLabel, cannotBeEmpty, cannotBeEmptyMessage, warnDivId) {
    this.storeStateOnce();
    DuelingListBoxesElement.moveOption(sourceSelect, targetSelect, keepSourceLabel, unmovableSourceValues,
        unmovableAlertMessages, keepTargetLabel, cannotBeEmpty, cannotBeEmptyMessage, warnDivId);
}

DuelingListBoxesElement.prototype.instSaveAllSelected = function(fromSelectArray, toArray, delim, escape, emptyLabel) {
    DuelingListBoxesElement.saveAllSelected(fromSelectArray, toArray, delim, escape, emptyLabel);
}

/**
 * Displays the given warning message in the given warning box.
 * 
 * @param warnId warning box div id
 * @param message the inner html of the warn text div is replaced with this
 */
DuelingListBoxesElement.warning = function(warnId, message) {
    if (!warnId) {
        return;
    }
    var warnDiv = document.getElementById(warnId);
    if (warnDiv) {
        warnDiv.innerHTML = LC.getLabel("Global", "colonSeparatedWords", LC.getLabel("Global", "error"), message);
        warnDiv.style.display = "block";
    }
}

/**
 * Removes the warning message from the given warning box div.
 * 
 * @param warnId warning box div id
 */
DuelingListBoxesElement.removeWarning = function (warnId) {
    if (!warnId) {
        return;
    }
    var warnDiv = document.getElementById(warnId);
    if (warnDiv) {
        warnDiv.innerHTML = ""
        warnDiv.style.display = "none";
    }
}

DuelingListBoxesElement.moveTop = function(sourceSelect, warnDivId) {

    if (sourceSelect.length > 1) {
        var options = sourceSelect.options;

        // find which ones are selected...
        var selectedIds = new Array ();
        var index = 0;

        for (var i = 0; i < sourceSelect.length; i++) {
            if (options[i].selected) {
                selectedIds[index] = i;
                index++;
            }
        }

        // Move each selected option up to the topmost available
        // position.  The first one in the selected list gets position 0,
        // second one gets position 1, and so on.
        var selId;
        for (var i = 0; i < selectedIds.length; i++) {
            selId = selectedIds[i];
            // delta is how many positions up to move the selected item
            // to get it into the target position, which is position "i"
            delta = selId-i;
            for (var j = 0 ; j < delta; j++) {
                DuelingListBoxesElement.privateMoveUp (options, selId-j);
                options[selId-j].selected = false;
                options[(selId-j)-1].selected = true;
                }
        }

        sourceSelect.focus ();

        // invoke if the Slect Element has local function
        if (sourceSelect["onLocalMoveTop"])
            sourceSelect.onLocalMoveTop();
    }
    DuelingListBoxesElement.removeWarning(warnDivId);
}

DuelingListBoxesElement.moveBottom = function(sourceSelect, warnDivId) {

    if (sourceSelect.length > 1) {
        var options = sourceSelect.options;

        // find which ones are selected...
        var selectedIds = new Array ();
        var index = 0;

        for (var i = 0; i < sourceSelect.length; i++) {
            if (options[i].selected) {
                selectedIds[index] = i;
                index++;
            }
        }

        // move each selected option down - starting from the end
        // of the selected items array, we'll move each item down to
        // the next lowest position (i.e., last one in the array ends up at
        // the very bottom, nth one in the array ends up (array length - n) from
        // the bottom
        // targetPos is position the element is moving to
        var targetPos = sourceSelect.length-1;
        var selId;
        for (var i = selectedIds.length-1; i >= 0 ; i--) {
            selId = selectedIds[i];
            // delta is how much to move down from the current position to get to the target position
            var delta = targetPos-selId;
            for (var j = 0 ; j < delta; j++) {
                DuelingListBoxesElement.privateMoveDown (options, selId+j);
                options[selId+j].selected = false;
                options[(selId+j)+1].selected = true;
                }
            targetPos--;
        }

        sourceSelect.focus ();

        // invoke if the Slect Element has local function
        if (sourceSelect["onLocalMoveBottom"])
            sourceSelect.onLocalMoveBottom();
    }
    DuelingListBoxesElement.removeWarning(warnDivId);
}

/*
 * Do not call this function directly.
 * As it does NO bounds checking.
 * Please use the moveUp or moveTop calls.
 */
DuelingListBoxesElement.privateMoveUp = function(options, index) {
    var newOption = new Option (options[index-1].text, options[index-1].value);
    options[index-1].text = options[index].text;
    options[index-1].value = options[index].value;
    options[index].text = newOption.text;
    options[index].value = newOption.value;
}

/*
 * Do not call this function directly.
 * As it does NO bounds checking.
 * Please use the moveDown or moveBottom calls.
 */
DuelingListBoxesElement.privateMoveDown = function(options, index) {
    var newOption = new Option (options[index+1].text, options[index+1].value);
    options[index+1].text = options[index].text;
    options[index+1].value = options[index].value;
    options[index].text = newOption.text;
    options[index].value = newOption.value;
}

DuelingListBoxesElement.moveDown = function(sourceSelect, topSourceValue, radicalValue, unmovableAlertMessage, 
        warnDivId) {
    var seenWarn=false;
    if (sourceSelect.length > 1) {
        var options = sourceSelect.options;

        // find which ones are selected
        var selectedIds = new Array ();
        var index = 0;
        if (topSourceValue != null) {
            // make sure we don't move the top value down, unless the second value is the radical value and it's not being moved
            if (topSourceValue == options[0].value && options[0].selected && (options[1].value != radicalValue || options[1].selected)) {
                options[0].selected = false;
                if (unmovableAlertMessage != null) {
                    DuelingListBoxesElement.warning(warnDivId, unmovableAlertMessage);
                    seenWarn=true;
                }
            }
            // when a radical value is specified, also check the second value:
            // second value can't be moved down if it's the topValue
            if (radicalValue && options[1].value == topSourceValue && options[1].selected) {
                options[1].selected = false;
                if (unmovableAlertMessage != null) {
                    DuelingListBoxesElement.warning(warnDivId, nmovableAlertMessage);
                    seenWarn=true;
                }
            }
        }

        for (var i = sourceSelect.length-2; i >= 0; i--) {
            if (sourceSelect.options[i].selected) {
                // add any remaining selected elements to our array of elements to move
                selectedIds[index] = i;
                index++;
            }
        }

        // move each selected element down
        var selId;
        for (var i = 0; i < selectedIds.length; i++) {
            selId = selectedIds[i];
            DuelingListBoxesElement.privateMoveDown (options, selId);
            options[selId].selected = false;
            options[selId+1].selected = true;
        }

        sourceSelect.focus ();

        // invoke if the Slect Element has local function
        if (sourceSelect["onLocalMoveDown"])
            sourceSelect.onLocalMoveDown();
    }
    if (!seenWarn) {
        DuelingListBoxesElement.removeWarning(warnDivId);
    }
}

DuelingListBoxesElement.moveUp = function(sourceSelect, topSourceValue, radicalValue, unmovableAlertMessage,
        warnDivId) {
    if (sourceSelect.length > 1) {
        var options = sourceSelect.options;

        // find which ones are selected...
        var selectedIds = new Array ();
        var index = 0;
        if (topSourceValue != null) {
            // second value can't be moved up if first value is topValue, unless second value is the radical value
            if (options[0].value == topSourceValue && options[1].selected && options[1].value != radicalValue) {
                options[1].selected = false;
                if (unmovableAlertMessage != null) {
                    DuelingListBoxesElement.warning(warnDivId, unmovableAlertMessage);
                    return;
                }
            }
            // when a radical value is specified, apply the same rule to the third value:
            // third value can't be moved up if second value is topValue OR if second value is going to be moved up
            if (radicalValue && options[2].selected && (options[1].value == topSourceValue || options[1].selected)) {
                options[2].selected = false;
                if (unmovableAlertMessage != null) {
                    DuelingListBoxesElement.warning(warnDivId, unmovableAlertMessage);
                    return;
                }
            }
            // notice the return statements:  if any one option cannot be moved up, then
            // do not move up any of the options
        }
        for (var i = 1; i < sourceSelect.length; i++) {
            if (options[i].selected) {
                selectedIds[index] = i;
                index++;
            }
        }

        // move each selected option up
        var selId;
        for (var i = 0; i < selectedIds.length; i++) {
            selId = selectedIds[i];
            DuelingListBoxesElement.privateMoveUp (options, selId);
            options[selId].selected = false;
            options[selId-1].selected = true;
        }

        sourceSelect.focus ();

        // invoke if the Slect Element has local function
        if (sourceSelect["onLocalMoveUp"])
            sourceSelect.onLocalMoveUp();
    }
    DuelingListBoxesElement.removeWarning(warnDivId);
}


DuelingListBoxesElement.moveOption = function(sourceSelect, targetSelect, keepSourceLabel, unmovableSourceValues,
                        unmovableAlertMessages, keepTargetLabel, cannotBeEmpty, cannotBeEmptyMessage, warnDivId) {
    var seenWarn=false;
    var sourceOptions = sourceSelect.options;

    var canMove;
    var option;

    // find which ones are selected...
    var selectedIds = new Array ();
    var index = 0;
    if (sourceSelect.cannotBeEmpty || cannotBeEmpty) {
        var numSelected = 0;
        for (var i = 0; i < sourceSelect.length; i++) {
            if (sourceSelect.options[i].selected) numSelected++;
        }
        if (numSelected == sourceSelect.options.length) {
            if (sourceSelect.handleEmptyList) {
                sourceSelect.handleEmptyList();
            }
            if (cannotBeEmptyMessage) {
                DuelingListBoxesElement.warning(warnDivId, cannotBeEmptyMessage);
            }
            return;
        }
    }

    for (var i = 0; i < sourceSelect.length; i++) {
        option = sourceOptions[i];
        if (option.selected) {
            canMove = (option.text != keepSourceLabel);
            if (canMove && unmovableSourceValues != null) {
                // make sure we don't move any options defined as unmovable
                for (var j = 0; j < unmovableSourceValues.length; j++) {
                    if (unmovableSourceValues[j] == option.value) {
                        canMove = false;
                        if (unmovableAlertMessages[j] != null) {
                            DuelingListBoxesElement.warning(warnDivId, unmovableAlertMessages[j]);
                            seenWarn=true;
                        }
                        break;
                    }
                }
            }

            // if this option can be moved we add it to our array of elements to move
            if (canMove) {
                selectedIds[index] = i;
                index++;
            } else {
                // if we can't move this option, then unselect it
                option.selected = false;
            }
        }
    }

    // move them over one by one
    var targetOptions = targetSelect.options;
    if (selectedIds.length > 0) {
        targetSelect.selectedIndex = -1;
        for (var i = 0; i < selectedIds.length; i++) {
            option = new Option (sourceOptions[selectedIds[i]].text, sourceOptions[selectedIds[i]].value);
            option.title = sourceOptions[selectedIds[i]].title;

            // replace the target value if its the last one
            if (targetOptions.length == 1 && targetOptions[0].text == keepTargetLabel) {
                targetOptions[0] = option;
                targetOptions[0].selected = true;
            } else {
                targetOptions[targetOptions.length] = option;
                targetOptions[targetOptions.length-1].selected = true;
            }
        }
    }

    // notify the Select Elements that their contents have changed
    if (targetSelect["onchange"]) {
        targetSelect.onchange();
    }
    if (sourceSelect["onchange"]) {
        sourceSelect.onchange();
    }

    // remove selected values from the source, starting with the last one selected
    for (var i = selectedIds.length - 1; i > -1; i--) {
        sourceSelect.remove(selectedIds[i]);
    }

    // Workaround here for a bug in IE:
    // If you have a select element with many values, and you've scrolled to
    // the bottom and move an option from the top-most element you can now see,
    // IE would not refresh the select element, leaving a hole in the list.
    // By forcing the select element disabled and back, it seems to refresh the
    // element properly.
    sourceSelect.disabled = true;
    sourceSelect.disabled = false;

    // make sure we don't get an empty list
    if (sourceOptions.length == 0) {
        sourceOptions[0] = new Option (keepSourceLabel, keepSourceLabel);
    }

    // if we moved anything, put the focus on the target list box
    if (selectedIds.length > 0) targetSelect.focus ();

    // invoke if the Slect Element has local function
    if (targetSelect["onLocalMoveOptions"])
        targetSelect.onLocalMoveOptions();
    if (sourceSelect["onLocalMoveOptions"])
        sourceSelect.onLocalMoveOptions();
    if (! seenWarn) {
        DuelingListBoxesElement.removeWarning(warnDivId);
    }
}

/**
 * Used when submitting a dueling list boxes element.
 * Stores all the values into hidden form parameters so we can get them out
 */
DuelingListBoxesElement.saveAllSelected = function(fromSelectArray, toArray, delim, escape, emptyLabel) {
    var i,j,escapedValue;
    // loop through all the select elements
    for (i = 0; i < fromSelectArray.length; i++) {
        toArray[i].value = ''; // clear out the value to start
        // now loop through all the values in the select element
        for (j = 0; j < fromSelectArray[i].length; j++) {
            // copy over the value as long as it is not the emptyLabel
            if (!(fromSelectArray[i].length == 1 && fromSelectArray[i].options[0].value == emptyLabel)) {
                var val = fromSelectArray[i].options[j].value.replace(new RegExp(escape+escape,"g"), escape+escape);
                toArray[i].value += val.replace(new RegExp(delim,"g"), escape+delim);
            }

            // add the delimiter (except after the last one)
            if (j + 1 < fromSelectArray[i].length) {
                toArray[i].value += delim;
            }
        }
    }
}

/**
 * Lookup hover detail object.
 * @author jmooney
 * @since 148
 */
function LookupHoverDetail(id, url) {
    this.lookup = document.getElementById(id);
    this.width = 302;
    this.bubbleOffset = XBrowser.userAgent.isIE6 ? 5 : 14;
    this.height = 262;
    this.hover = document.createElement("div");
    this.hover.id = id + "Hover";
    this.hover.className = "individualPalette lookupHoverDetail";
    this.hover.innerHTML = "<div class=\"topLeft\">" + LC.getLabel("Global", "loading") + "</div>";
    document.body.appendChild(this.hover);
    var self = this;
    addEvent(this.hover, "mouseover", function() { self.show(); } , true);
    addEvent(this.hover, "mouseout", function() { self.hide(); }, true);
    this.hover = new iframeShim(this.hover);
    this.originalClass = "";
    this.fadingOut = null;
    this.fadingIn = null;
    this.loaderURL = url;
    this.loaded = false;
}

LookupHoverDetail.SHOW_DELAY = 800;
LookupHoverDetail.HIDE_DELAY = 250;
LookupHoverDetail.stopLoading = false;
// map from id to hover object
LookupHoverDetail.hovers = {};

// static function to retrieve a hover detail using a 15 char ID
LookupHoverDetail.getHover = function(id, url) {
    if (LookupHoverDetail.hovers[id]) {
        return LookupHoverDetail.hovers[id];
    }
    var hover = new LookupHoverDetail(id, url);
    LookupHoverDetail.hovers[id] = hover;
    return hover;
}

// show the hover detail
LookupHoverDetail.prototype.show = function() {
    if (this.fadingOut) {
        clearTimeout(this.fadingOut);
        this.fadingOut = null;
	} else {
        var self = this;
	    this.fadingIn = setTimeout( function() { self.showNow(); }, LookupHoverDetail.SHOW_DELAY);
    }
}

LookupHoverDetail.prototype.showNow = function() {
    if (!this.loaded) {
    	if (this.loaderURL != null) {
    		var self = this;
    		XBrowser.getHttpResponse(this.loaderURL, function(response) { self.load(response.responseText); }, function(response) { self.load(response.responseText); });
    	} else {
    		// we haven't loaded, and we have no URL to load from, so don't do anything.
    		return;
    	}
    }
    this.position();
    this.hover.setStyle("display", "block");
    this.fadingIn = null;
}

// hide the hover detail
LookupHoverDetail.prototype.hide = function() {
    if (this.fadingIn) {
        clearTimeout(this.fadingIn);
        this.fadingIn = null;
    } else {
        var self = this;
        this.fadingOut = setTimeout(function() { self.hideNow(); }, LookupHoverDetail.HIDE_DELAY);
    }
}

LookupHoverDetail.prototype.hideNow = function() {
    this.hover.setStyle("display", "none");
    this.fadingOut = null;
}

// loads the mini detail from the responseText
LookupHoverDetail.prototype.load = function(responseText) {
	this.hover.div.innerHTML = responseText;
	Util.evalScriptsUnderElement(this.hover.div);
	this.originalClass = this.hover.div.firstChild.className;
	this.position();
	this.loaded = true;
}

LookupHoverDetail.prototype.position = function() {
    var lookupX = getObjX(this.lookup);
    var lookupY = getObjY(this.lookup);
    var lookupW = this.lookup.offsetWidth;
    var lookupH = this.lookup.offsetHeight;
	var winX = getScrollX();
	var winY = getScrollY();
	var winWidth = getWindowWidth();
	var winHeight = getWindowHeight();
    var bubbleClass = this.originalClass + " ";
    var hoverX, hoverY;
    if (lookupY + lookupH + this.height < winY + winHeight) {
    	bubbleClass += "top";
    	hoverY = lookupY + lookupH;
    } else {
    	bubbleClass += "bottom";
    	hoverY = lookupY - this.height;
    }
    if (lookupX + lookupW - this.bubbleOffset + this.width < winX + winWidth) {
    	bubbleClass += "Left";
    	hoverX = lookupX + lookupW / 2 - this.bubbleOffset;
    } else {
    	bubbleClass += "Right";
    	hoverX = lookupX + lookupW / 2 - this.width; 
    }
    
    this.hover.setStyle("left", hoverX + "px");
    this.hover.setStyle("top", hoverY + "px");
    this.hover.div.firstChild.className = bubbleClass;;
}
/**
 * Implementation of QueryString object in JS.
 * It's not guaranteed to handle UTF8 encoding properly;
 * feel free to add/test it.
 */
var QueryString = function(qs, allowMultipleValuesForParam) {

	this.allowMultipleValuesForParam = allowMultipleValuesForParam;
    this.params = new Object();

    if (qs == null)
        qs = location.search.substring(1, location.search.length);

    if (qs.length == 0) return;

    qs = qs.replace(/\+/g, ' ');
    var args = qs.split('&');

    for (var i = 0 ; i < args.length ; i++) {
        var index = args[i].indexOf('=');
        if (index > 0) {
            var name = decodeURIComponent(args[i].substr(0, index));
            var value = decodeURIComponent(args[i].substr(index+1));
			this.add(name, value);
        }
    }
}

QueryString.prototype.get = function (name, defaultVal) {
    var value = this.params[name];
    if (value) {
        return value;
    }
    else
        if (defaultVal){
            return defaultVal;
        } else {
            return null;
        }
}

QueryString.prototype.add = function(name, value) {
	if(!this.allowMultipleValuesForParam){
    	this.params[name] = value;
	} else {
		var valueArray =  this.params[name];
		if(!valueArray) {
        	valueArray = new Array();
        	valueArray[0] = value;
        } else {
        	valueArray[valueArray.length] = value;
        }
        this.params[name] = valueArray;
	}
}

QueryString.prototype.remove = function(name) {
    this.params[name] = null;
}

QueryString.prototype.toString = function() {
    var str = "?";
    for (var name in this.params) {
        var value = this.params[name];
        if (value != undefined) {
        	if(!this.allowMultipleValuesForParam){
            	str = str + name + "=" + encodeURIComponent(value) + "&";
        	} else {
        		for (var ind = 0; ind < value.length; ind++){
        			if(value[ind]) {
        				str = str + name + "=" + encodeURIComponent(value[ind]) + "&";
        			}
        		}
        	}
        }
    }
    return str;
}



/**
*     Manages the skiplinks and associated hovering lists
*     at the top of the detail page
*     see RelatedListPanelElement
* @author mpolcari
* @since 142.ml
*/
function RelatedListPanel(rlPanelId) {
  this.panelId = rlPanelId;
  this.currentListId = null;
  this.delayingHide = null;
  this.delayingShow = null;
  this.numLists = 0;
  this.linksHtml = [];
  this.isConsole = location.search.indexOf(Desktop['IS_DESKTOP']) > -1;
}

RelatedListPanel.prototype.getPanelNode = function() {
  return document.getElementById(this.panelId);
}

RelatedListPanel.prototype.getPanelShadowNode = function() {
  return (this.getPanelNode()) ? this.getPanelNode().parentNode : null;
}

RelatedListPanel.prototype.getIFrameNode = function() {
  return DomUtil.findDescendantWithTag(this.getPanelNode(), 'iframe');
}

RelatedListPanel.prototype.registerList = function(relatedList) {
  this.linksHtml.push(this.getHoverableLinkHTML(relatedList));
}

/**
* Adds the hoverLinks to the panel
*      must be run after onLoad()
*/
RelatedListPanel.prototype.addListsToPanel = function() {
  var linksNode = this.getHoverableLinksNode();
  if (linksNode.innerHTML.indexOf("linklet") < 0) {
    linksNode.innerHTML = this.linksHtml.join('<span class="pipe"> | </span>');
  }
  linksNode.style.visibility = 'visible';
}

RelatedListPanel.prototype.getHoverableLinkHTML = function (relatedList) {
  var buf = [];
  buf.push('<a class="linklet" href="#');
  buf.push(relatedList.getLinkTarget());
  buf.push('" id="');
  buf.push(this.getLinkId(relatedList.listId));
  buf.push('"');
  if(!relatedList.isOnlySkipLink){
	  buf.push(' onmouseover="sfdcPage.relatedListPanel.showRLDelayed(\'');
	  buf.push(relatedList.listId);
	  buf.push('\')" onmouseout="sfdcPage.relatedListPanel.hideRLDelayed(\'');
	  buf.push(relatedList.listId);
	  buf.push('\')" onclick="sfdcPage.relatedListPanel.hideRL(\'');
	  buf.push(relatedList.listId);
  }
  buf.push('\')"><span class="listTitle">');
  buf.push(relatedList.getTitle());
  buf.push(this.getCountIndicator(relatedList));
  buf.push('</span></a>');
  return  buf.join('');
}

RelatedListPanel.prototype.getCountIndicator = function (relatedList) {
  var html = [];
  html.push('<span class="count">[');
  html.push(relatedList.getNumberVisibleRows());
  if (relatedList.hasMore()) {
    html.push('<span class="plus">+</span>');
  }
  html.push(']</span>');
  return html.join('');
}

RelatedListPanel.prototype.getHoverableLinksNode = function() {
  return this.getPanelShadowNode().nextSibling;
}

RelatedListPanel.prototype.showRLDelayed = function(rlId) {
  this.clearhidemenu();
  if (this.currentListId && (this.currentListId != rlId)) {
    this.hideRL(this.currentListId);
    this.populateRL(rlId);
    this.showRL(rlId);
  } else {
    //clearshowmenu();
    var self = this;
    this.delayingShow = setTimeout(function() {self.showRL(rlId);}, 500);
    this.populateRL(rlId); // This way the time it takes to populate the list is not part of the timing delay
  }
}

RelatedListPanel.prototype.hideRLDelayed = function(rlId, delay) {
  this.clearshowmenu();
  if (!delay) delay = 50;
  if (!rlId) rlId = this.currentListId;
  var self = this;
  this.delayingHide = setTimeout(function() { self.hideRL(rlId); }, delay);
}

RelatedListPanel.prototype.getStyleSheetHtml = function() {
  var sheetHtml = []
  for (var i = 0; i< document.styleSheets.length; i++){
    if (document.styleSheets[i].owningElement && document.styleSheets[i].owningElement.outerHTML) {
      sheetHtml.push(document.styleSheets[i].owningElement.outerHTML);
    }
  }
  return sheetHtml.join('');
}

RelatedListPanel.prototype.populateRL = function(rlId) {
  if (!rlId) rlId = this.currentListId;

  if (this.currentListId != rlId) {
    var panelFrameNode = this.getIFrameNode();
    if (!panelFrameNode) return;
    var relatedListNode = document.getElementById(rlId);
    var iFrameDoc =  panelFrameNode.contentWindow.document;

    if (!(relatedListNode && iFrameDoc)) return;
    this.currentListId = rlId;
    if (iFrameDoc.importNode) { //ffox
      iFrameDoc.body.innerHTML = '';
      iFrameDoc.body.appendChild(iFrameDoc.importNode(relatedListNode, false));
      iFrameDoc.body.firstChild.innerHTML = relatedListNode.innerHTML;
    } else if (relatedListNode.outerHTML) {
      iFrameDoc.body.innerHTML = relatedListNode.outerHTML; // ie
    } else {
      return false;
    }
    var self = this;
    setTimeout(function() { self.fixContent(); }, 1);  //fire this asynchronously
    panelFrameNode.contentWindow.navigateToUrl = function(url) {
    	// window here actually refers to contentWindow.parent because of the context it's executed in, strange huh?
        window.navigateToUrl(url);
      };
  }
}

//forceResize: without forceResize, we will not resize the iframe
//             if the rl in question is already displayed
RelatedListPanel.prototype.showRL = function(rlId, forceResize) {
    this.clearhidemenu();

    if (rlId != this.currentListId) return;  //if the id's don't match, the panel has been cleared since the last hover.  It should not display
    var panelShadowNode = this.getPanelShadowNode();
    if (!panelShadowNode) return;
    var panelFrameNode = this.getIFrameNode();
    var linkletNode = document.getElementById(this.getLinkId(rlId));
    var relatedListNode = DomUtil.findDescendantWithClassName(document.getElementById(rlId), 'bPageBlock');
    if (!( panelFrameNode && linkletNode)) return;
    if (linkletNode.className.indexOf('linkletOn') > 0 && (!forceResize)) return;
    panelShadowNode.style.top = (linkletNode.offsetTop + linkletNode.offsetHeight) + "px";
    if (this.isConsole) {
    	//console has no margins, so this needs to be narrower
        panelShadowNode.style.left = (getObjX(this.getHoverableLinksNode()) - 4) + "px";
        panelShadowNode.style.width = (relatedListNode.offsetWidth - 3) + "px";
    } else {
        panelShadowNode.style.left = (getObjX(this.getHoverableLinksNode()) - 14) + "px";
        panelShadowNode.style.width = (relatedListNode.offsetWidth + 13) + "px"; //5 for each margin + 2 for shadow + 1 for border
    }
    panelFrameNode.style.height = (relatedListNode.offsetHeight + 5) + "px";
    panelShadowNode.style.display = 'block';
    linkletNode.className += ' linkletOn';
}


RelatedListPanel.prototype.hideRL = function(rlId) {
  if (!(Modal.isBlocked())) {
    this.clearshowmenu();
    if (!rlId) rlId = this.currentListId;
    this.currentListId = null;
    var panelShadowNode = this.getPanelShadowNode();
    var linklet = document.getElementById(this.getLinkId(rlId));
    if (linklet) linklet.className = 'linklet';
    if (panelShadowNode) panelShadowNode.style.display = 'none';
  }
}

RelatedListPanel.prototype.clearhidemenu = function(){
  if (this.delayingHide) {
    clearTimeout(this.delayingHide);
  }
}

RelatedListPanel.prototype.clearshowmenu = function(){
  if (this.delayingShow) {
    clearTimeout(this.delayingShow);
  }
}

RelatedListPanel.prototype.getLinkId = function (relatedListId) {
  return relatedListId + '_link';
}

RelatedListPanel.prototype.fixContent = function() {
  var targetableTags = ['a', 'form'];

    var iframeWin = this.getIFrameNode().contentWindow;

  for (var i = 0, tag; tag = targetableTags[i]; i++) {
    var tags = iframeWin.document.body.getElementsByTagName(tag);
    for (var k = 0, node; node = tags[k]; k++) {
      if (!node.target) { node.target = '_parent'; }
    }
  }
    var scripts = iframeWin.document.body.getElementsByTagName("script");
    for (var i = 0; i < scripts.length; i++) {
        iframeWin.eval(scripts[i].innerHTML);
    }
}


var MultiSelectPicklist = {};

MultiSelectPicklist.loadMSP = function(id) {
    var sElem = getElementByIdCS(id + '_selected');
    var uElem = getElementByIdCS(id + '_unselected');
    
    //this is a stupid hack to keep the optgroup labels from disappearing
    if (isSafari) {
	    for(i=0; i < sElem.childNodes.length; i++) {
        	var ch = sElem.childNodes[i]
	        if (ch.nodeName == "OPTGROUP") {
	        	ch.appendChild(document.createElement("p"));
	        }
	    }
	    
   	    for(i=0; i < uElem.childNodes.length; i++) {
        	var ch = uElem.childNodes[i]
	        if (ch.nodeName == "OPTGROUP") {
	        	ch.appendChild(document.createElement("p"));
	        }
	    }
    }
    
    MultiSelectPicklist.resizeMSP(sElem, uElem);
}

MultiSelectPicklist.resizeMSP = function(sElem, uElem) {
    if (!sElem || !uElem) return;
    if (!sElem.style.width) {
        var selW = (sElem.scrollWidth > uElem.scrollWidth) ? sElem.scrollWidth : uElem.scrollWidth;
        selW = selW + 35;
        sElem.style.width = selW + "px";
        uElem.style.width = selW + "px";

    }
}

MultiSelectPicklist.handleMSPChange = function(sel) {
    var sElem = getElementByIdCS(sel.id + '_selected');
    var uElem = getElementByIdCS(sel.id + '_unselected');

    //Safari handles optgroups differently and requires explicit DOM manipulation
    if (isSafari) {
    	var sDepth;
    	var uDepth;
    	
	    for(i=0; i < sElem.childNodes.length; i++) {
        	var ch = sElem.childNodes[i]
	        if (ch.nodeName == "OPTGROUP") {
	        	sDepth = i;
				var chlen = ch.childNodes.length
        	    for(j=0; j<chlen; j++) {
            	    ch.removeChild(ch.childNodes[0]);
            	}
            	ch.appendChild(document.createElement("p"));
        	}
    	}
    	
    	for(i=0; i < uElem.childNodes.length; i++) {
        	var ch = uElem.childNodes[i]
	        if (ch.nodeName == "OPTGROUP") {
				uDepth = i;
				var chlen = ch.childNodes.length
        	    for(j=0; j<chlen; j++) {
            	    ch.removeChild(ch.childNodes[0]);
            	}
            	ch.appendChild(document.createElement("p"));
        	}
    	}
    	
    	for (i=0; i < sel.options.length; i++) {
    		if (sel.options[i].value != picklistNAMarker) {
			    var o = document.createElement("option");
			    o.text = sel.options[i].text;
			    o.value = i;
    			
    			if (sel.options[i].selected) {
    				sElem.childNodes[sDepth].appendChild(o);
    			} else {
    				uElem.childNodes[uDepth].appendChild(o);
    			}
    		}
    	}
    } else {
	    var sI = 0;
	    var uI = 0;

    	sElem.length = 0;
	    uElem.length = 0;
	    
	    for (var i = 0; i < sel.options.length; i++) {
	        if (sel.options[i].value != picklistNAMarker) {
	           if (sel.options[i].selected) {
	                sElem.options[sI] = new Option(sel.options[i].text, i);
	                sI++;
	            } else {
	                uElem.options[uI] = new Option(sel.options[i].text, i);
	                uI++;
	            }
	        }
	    }
    }

    MultiSelectPicklist.resizeMSP(sElem, uElem);
}

MultiSelectPicklist.handleMSPSelect = function(selId) {
    var mainElem = getElementByIdCS(selId);
    var uElem = getElementByIdCS(selId + '_unselected');
    for (var i = 0; i < uElem.options.length; i++) {
        if (uElem.options[i].selected) {
            mainElem.options[parseInt(uElem.options[i].value)].selected = true;
        }
    }
    MultiSelectPicklist.handleMSPChange(mainElem);
}

MultiSelectPicklist.handleMSPUnSelect = function(selId) {
    var mainElem = getElementByIdCS(selId);
    var sElem = getElementByIdCS(selId + '_selected');
    for (var i = 0; i < sElem.options.length; i++) {
        if (sElem.options[i].selected) {
            mainElem.options[parseInt(sElem.options[i].value)].selected = false;
        }
    }
    MultiSelectPicklist.handleMSPChange(mainElem);
}
   function AdvCurrencyEnable() {}

   AdvCurrencyEnable.checkSaveButton = function(checkCheckBox) {
		var checked;
		if (checkCheckBox) {
			checked = document.getElementById(AdvancedCurrencyEnable.pENABLE).checked;
		} else {
			checked = ! document.getElementById(AdvancedCurrencyEnable.pENABLE).checked;
		}
 		//check if any of the input values are checked so that we allow the user to save by enabling the save button
   		if (checked) {
   			document.getElementsByName(AdvancedCurrencyEnable.enableButton)[0].className = 'btn';
            document.getElementsByName(AdvancedCurrencyEnable.enableButton)[0].disabled = false;
   		} else {
        	document.getElementsByName(AdvancedCurrencyEnable.enableButton)[0].className = 'btnDisabled';
            document.getElementsByName(AdvancedCurrencyEnable.enableButton)[0].disabled = true;
   		}
   }

/**
 * This class basically has a bunch of static methods.  It's a class for namespace protection.
 * 
 *  @author emoses
 *  @since 144
 */
function ProfileEditClass(warningmsg) {
	this.crudDeleteWarningMsg = warningmsg;
	this.isPropagating = false;
};

//A global to deal with namespacing issues with the "this" keyword.  Must be instancated on the profile edit page.
var ProfileEdit;

ProfileEditClass.prototype.changeAndPropagate = function(element, newState){
	if (!element) return;
	
	var wasFlipped = element.checked != newState;
	
	element.checked = newState;
	if (element.id.indexOf("crud") === 0){
		ProfileEdit.handleCrudDependencyClick(element, wasFlipped);
	} else {
		ProfileEdit.handlePermDependencyClick(element);
	}
}

ProfileEditClass.prototype.doDependencies = function(id, map, newState){
	if (id in map){
		var dependents = map[id];
		for (var i = 0; i < dependents.length; i++){
			var dependent = document.getElementById(dependents[i]);
			if (dependent){
				ProfileEdit.changeAndPropagate(dependent, newState);
			}
		}
	}
}

ProfileEditClass.prototype.handlePermDependencyClick = function(checkbox){
	if (checkbox.checked){
		//This was just checked
		ProfileEdit.doDependencies(checkbox.id, permDependencies, true);
	} else {
		//This was just unchecked
		ProfileEdit.doDependencies(checkbox.id, permAntecedents, false);
	}
}

ProfileEditClass.prototype.handleCrudDependencyClick = function(checkbox, wasFlippedIn){
	//If the call is coming from the checkbox element itself, wasFlippedIn will be undefined, and we can
	//be sure that the checkbox was actually flipped.  Otherwise, use the value that was passed in
	var wasFlipped = arguments.length > 1 ? wasFlippedIn : true;

	if (checkbox.checked){
		ProfileEdit.doDependencies(checkbox.id, permDependencies, true);
		if (checkbox.id.indexOf(ProfileEditConstants.CRUD_CREATE) === 0){
			var crudid = checkbox.id.substring(ProfileEditConstants.CRUD_CREATE.length);
			ProfileEdit.changeAndPropagate(document.getElementById(ProfileEditConstants.CRUD_READ + crudid), true);
		} else if (checkbox.id.indexOf(ProfileEditConstants.CRUD_UPDATE) === 0){
			var crudid = checkbox.id.substring(ProfileEditConstants.CRUD_UPDATE.length);
			ProfileEdit.changeAndPropagate(document.getElementById(ProfileEditConstants.CRUD_READ + crudid), true);
		} else if (checkbox.id.indexOf(ProfileEditConstants.CRUD_DELETE) === 0){
			var crudid = checkbox.id.substring(ProfileEditConstants.CRUD_DELETE.length);
			ProfileEdit.changeAndPropagate(document.getElementById(ProfileEditConstants.CRUD_UPDATE + crudid), true);
			ProfileEdit.changeAndPropagate(document.getElementById(ProfileEditConstants.CRUD_READ + crudid), true);
		}
	} else {
		if (checkbox.id.indexOf(ProfileEditConstants.CRUD_READ) === 0){
			var crudid = checkbox.id.substring(ProfileEditConstants.CRUD_READ.length);
			ProfileEdit.changeAndPropagate(document.getElementById(ProfileEditConstants.CRUD_CREATE + crudid), false);
			ProfileEdit.changeAndPropagate(document.getElementById(ProfileEditConstants.CRUD_UPDATE + crudid), false);
			ProfileEdit.changeAndPropagate(document.getElementById(ProfileEditConstants.CRUD_DELETE + crudid), false);
		} else if (checkbox.id.indexOf(ProfileEditConstants.CRUD_UPDATE) === 0){
			var crudid = checkbox.id.substring(ProfileEditConstants.CRUD_UPDATE.length);
			ProfileEdit.changeAndPropagate(document.getElementById(ProfileEditConstants.CRUD_DELETE + crudid), false);
		} else if (checkbox.id.indexOf(ProfileEditConstants.CRUD_DELETE) === 0){
			var crudid = checkbox.id.substring(ProfileEditConstants.CRUD_DELETE.length);
			if (crudid in childrenToParents && wasFlipped && !this.isPropagating){
				if (childrenToParents[crudid].length > 0){
						alert(this.crudDeleteWarningMsg);
				}
			}
		}
		if (!this.isPropagating){
			this.isPropagating = true;
			ProfileEdit.doDependencies(checkbox.id, permAntecedents, false);
			this.isPropagating = false;
		} else {
			ProfileEdit.doDependencies(checkbox.id, permAntecedents, false);
		}
	}
		
}

/**
 *  This is a singleton class. Its one instance will live in TabSetLimiter.prototype.theTabSetLimiter.
 *  To create a new one, simply make a call to "new TabSetLimiter(n)", where n is the number of allowable tabsets
 *
 *  The public funcitons are registerControl(TabSetControl) and handleChange(TabSetControl).
 *  @author emoses
 */
function TabSetLimiter(newLimit){ 
	//singleton
	if(TabSetLimiter.prototype.theTabSetLimiter){ return TabSetLimiter.prototype.theTabSetLimiter; }
	TabSetLimiter.prototype.theTabSetLimiter = this;
	
	var limit = newLimit;
	var limitedControls = [];
	var checkedControls = []; //queue
	var self = this;
	
	function checkLimits(){
		while (checkedControls.length > limit){
			if (checkedControls[0].defaultC.checked){
				//Move the second item to the front of the queue
				var temp = checkedControls[1];
				checkedControls[1] = checkedControls[0];
				checkedControls[0] = temp;
			}
			var currControl = checkedControls.shift();
			if (!currControl){
				alert("Error: Inconsistent state.");
			}
			currControl.visibleC.checked = false;
		}
	}
	
	this.registerControl = function(control){
		if (!control) return;
		if (limit == 1){
			control.visibleC.disabled = true;
		}
		limitedControls.push(control);
		if (control.visibleC.checked){
			checkedControls.push(control);
		}
		checkLimits();
	}
	
	this.handleChange = function(control){;
		var found = false;
		for (var i = 0; i < limitedControls.length; i++){
			if (limitedControls[i] === control){
				found = true;
				break;
			}
		}
		if (!found) return;
		
		if (control.visibleC.checked){
			//If it's already in checkedControls, we don't have to do anything.
			//This might happen if a checked control gets switched to default.
			for (var i = 0; i < checkedControls.length; i++){
				if (checkedControls[i] === control){
					return;
				}
			}
			checkedControls.push(control);
			checkLimits();
		} else {
			for (var i = 0; i < checkedControls.length; i++){
				if (checkedControls[i] === control){
					checkedControls.splice(i, 1);
					break;
				}
			}
		}
	}
}
	
/**
 * @author emoses
 * 
 * This represents a "visible" checkbox paired with a "default" radio button.  
 * Make sure that any time a change is made, if there's a TabSetLimiter instanciated,
 * that its handleChange method gets called
 */
function TabSetControl(visibleid, defaultid, isCustom){
	var self = this;
	this.visibleC = document.getElementById(visibleid);
	this.defaultC = document.getElementById(defaultid);
	
	if (this.visibleC == null || this.defaultC == null){
		return false;
	}
	
	this.handleDefaultChange = function(){
		if (self.defaultC.checked){
			self.visibleC.checked = true;
			self.visibleC.disabled = true;
			otherDefaults = document.getElementsByName(self.defaultC.name);
			if (!(TabSetLimiter.prototype.theTabSetLimiter) || TabSetLimiter.prototype.theTabSetLimiter.limit > 1){
				for (var i = 0; i < otherDefaults.length; i++){
					if (otherDefaults[i] != self.defaultC){
						otherDefaults[i].assocCheckbox.disabled = false;
					}
				}
			}
			if (TabSetLimiter.prototype.theTabSetLimiter){
				TabSetLimiter.prototype.theTabSetLimiter.handleChange(self);
			}
		}
	}
	
	this.handleVisibleChange = function(e){
		var target = getEventTarget(getEvent(e));
		TabSetLimiter.prototype.theTabSetLimiter.handleChange(self);
	}
	
	//init code
	addEvent(self.defaultC, 'click', self.handleDefaultChange);
	self.defaultC.assocCheckbox = self.visibleC;
	if (self.defaultC.checked) { self.visibleC.disabled = true; }
	if (TabSetLimiter.prototype.theTabSetLimiter != null && isCustom){
		TabSetLimiter.prototype.theTabSetLimiter.registerControl(self);
		addEvent(self.visibleC, 'click', self.handleVisibleChange, false);
	}
}
	
	
function WebLinkUi(){}

// Update the options for openType according to the displaytype
WebLinkUi.prototype.updateOpenType = function(openType, newWinVal, allow) {
    var typeSelect = document.getElementById(openType);
    var opts = typeSelect.options;
    if (typeSelect._saveOpts == null) {
        typeSelect._saveOpts = new Array(opts.length);
        for (var i = 0; i < opts.length; ++i) {
           typeSelect._saveOpts[i] = opts[i];
        }
    }
    var selectedVal = opts[typeSelect.selectedIndex].value;
    if (selectedVal == newWinVal && !allow) {
        selectedVal = opts[0].value;
    }
    opts.length = 0;
    for (var i = 0; i < typeSelect._saveOpts.length; ++i) {
        var opt = typeSelect._saveOpts[i];
        opt.selected = (opt.value == selectedVal);
        if (allow || opt.value != newWinVal) {
            opts[opts.length] = opt;
        }
    }
}

WebLinkUi.prototype.setContentType = function(id, isJs, jsValue, defValue) {
    var elem = document.getElementById(id);
    if (elem==null) return;
    if (isJs) {
       elem.value = jsValue;
    } else if (elem.value == jsValue) {
        var opts = elem.options;
        if (opts != null) {
            for (var i = 0; i < opts.length; ++i) {
               if (opts[i].value == defValue) {
                   opts[i].selected = true;
                   return;
                }
            }
            opts[0].selected = true;
        }
    }
}

WebLinkUi.prototype.currentDisplayedDiv = null;
WebLinkUi.prototype.displayDiv = function(div) {
    if (WebLinkUi.prototype.currentDisplayedDiv) {
        WebLinkUi.prototype.currentDisplayedDiv.style.display = 'none';
    }

    var d = document.getElementById(div);
    d.style.display = 'block';
    d.style.zIndex = 0;
    WebLinkUi.prototype.currentDisplayedDiv = d;
}


WebLinkUi.prototype.setDisplay = function(id, vis) {
    var d = document.getElementById(id);
    if (d) {
        if (vis) {
            d.style.display = 'block';
        } else {
            d.style.display = 'none';
        }
     }
}

/*
 * keyCode constants (note: NOT the same as ASCII, as returned by charCode)
 * These are returned only by onkeydown and onkeyup events.
 * Onkeypress returns either an ASCII value or (in Firefox) 0.
 * Note that some of these differ by OS and keyboard layout.
 * These values are for a standard English keyboard on Windows XP.
 * Some function keys do not trigger onkey* events in Safari.
 * Most keycodes are the same across platforms, with the only significant
 * differences being with function and modifier keys.  When in doubt, test.
 */

/* common keys */
var KEY_BACKSPACE = 8;
var KEY_TAB = 9;
var KEY_ENTER = 13;
var KEY_ESC = 27;
var KEY_SPACE = 32;

/* directions */
var KEY_PAGEDOWN = 33;
var KEY_PAGEUP = 34;
var KEY_ARROW_L = 37;
var KEY_ARROW_U = 38;
var KEY_ARROW_R = 39;
var KEY_ARROW_D = 40;

/* modifiers */
var KEY_SHIFT = 16;
var KEY_CTRL = 17;
var KEY_ALT = 18;
var KEY_CAPSLOCK = 20;

/* numbers (above the letters) */
var KEY_0 = 48;
var KEY_1 = 49;
var KEY_2 = 50;
var KEY_3 = 51;
var KEY_4 = 52;
var KEY_5 = 53;
var KEY_6 = 54;
var KEY_7 = 55;
var KEY_8 = 56;
var KEY_9 = 57;

/* letters */
var KEY_A = 65;
var KEY_B = 66;
var KEY_C = 67;
var KEY_D = 68;
var KEY_E = 69;
var KEY_F = 70;
var KEY_G = 71;
var KEY_H = 72;
var KEY_I = 73;
var KEY_J = 74;
var KEY_K = 75;
var KEY_L = 76;
var KEY_M = 77;
var KEY_N = 78;
var KEY_O = 79;
var KEY_P = 80;
var KEY_Q = 81;
var KEY_R = 82;
var KEY_S = 83;
var KEY_T = 84;
var KEY_U = 85;
var KEY_V = 86;
var KEY_W = 87;
var KEY_X = 88;
var KEY_Y = 89;
var KEY_Z = 90;

/* special Windows-only keys */
var KEY_WINDOWS_L = 91;
var KEY_WINDOWS_R = 92;
var KEY_SELECT = 93;

/* numbers (numpad) */
var KEY_NUMPAD_0 = 96;
var KEY_NUMPAD_1 = 97;
var KEY_NUMPAD_2 = 98;
var KEY_NUMPAD_3 = 99;
var KEY_NUMPAD_4 = 100;
var KEY_NUMPAD_5 = 101;
var KEY_NUMPAD_6 = 102;
var KEY_NUMPAD_7 = 103;
var KEY_NUMPAD_8 = 104;
var KEY_NUMPAD_9 = 105;
var KEY_NUMPAD_MULTIPLY = 106;
var KEY_NUMPAD_PLUS = 107;
var KEY_NUMPAD_MINUS = 109;
var KEY_NUMPAD_DECIMAL = 110;
var KEY_NUMPAD_DIVIDE = 111;

/* function keys */
var KEY_F1 = 112;
var KEY_F2 = 113;
var KEY_F3 = 114;
var KEY_F4 = 115;
var KEY_F5 = 116;
var KEY_F6 = 117;
var KEY_F7 = 118;
var KEY_F8 = 119;
var KEY_F9 = 120;
var KEY_F10 = 121;
var KEY_F11 = 122;
var KEY_F12 = 123;

/* everything else */
var KEY_PAUSE = 19;
var KEY_END = 35;
var KEY_HOME = 36;
var KEY_INSERT = 45;
var KEY_DELETE = 46;
var KEY_NUMLOCK = 144;
var KEY_SCROLLLOCK = 145;
var KEY_SEMICOLON = 186;
var KEY_EQUALS = 61;
var KEY_COMMA = 188;
var KEY_DASH = 109;
var KEY_PERIOD = 190;
var KEY_FORWARDSLASH = 191;
var KEY_GRAVE = 192;
var KEY_OPENBRACKET = 219;
var KEY_BACKSLASH = 220;
var KEY_CLOSEBRACKET = 221;
var KEY_APOSTROPHE = 222;

/**
  A MotifInputElement consists of the MotifElement, and hidden inputs for the motifkey, Icon & description

  @author polcari
  @since 144

  @param paramName - The parameter name which prepends in the inputElements
*/
function MotifInputElement(paramName) {

  this.paramName = paramName;

  this.motifKeyInput = document.getElementById(paramName + MotifInputElementConst.FIELD_NAME_MOTIF);
  this.motifElement = document.getElementById(paramName + MotifInputElementConst.MOTIF_ELEMENT_SUFFIX);
  this.motifElementObj = this.motifElement.motifElement;

  this.motifDescInput = document.getElementById(paramName + MotifInputElementConst.FIELD_NAME_DESCRIPTION);
  this.motifDescCell = this.motifElementObj.motifDescCell;

  this.motifIconInput = document.getElementById(paramName + MotifInputElementConst.FIELD_NAME_ICON);
  this.motifIcon = this.motifElementObj.motifIcon;

  //leave a hooks so we can find this later
  this.motifElement.motifInputElement = this;  // this is the actual box with the color & the icon.
  document.getElementById(paramName).motifInputElement = this; // this is the surrounding box
}

// methods to set icon
MotifInputElement.prototype.setIconSrc = function (newSrc) {
  this.motifIcon.src = newSrc;
}
MotifInputElement.prototype.setIconValue = function (newVal) {
  this.motifIconInput.value = newVal;
}
// method to set motifKey
MotifInputElement.prototype.setMotifKey = function (newKey) {
  this.motifKeyInput.value = newKey;
}

// method to set cellname
MotifInputElement.prototype.setDescription = function(desc) {
  if (this.motifDescCell.firstChild) {
    this.motifDescCell.firstChild.nodeValue = desc;
  } else {
    this.motifDescCell.appendChild(document.createTextNode(desc));
  }
}

  //function for opening window
MotifInputElement.prototype.openWindow = function (url, width, height) {
  openPopup(url, this.paramName + 'MotifPickerWindow', width, height, 'width=' + width + ',height=' + height + ',dependent=yes,resizable=yes,toolbar=no,status=no,directories=no,menubar=no,scrollbars=1', true);
}

// Test to see if this element is null
MotifInputElement.prototype.isNull = function() {
  return (!((this.motifKeyInput.value) && (this.motifKeyInput.value.length > 0)));
}



var FilterEdit = function(){}

FilterEdit.prototype.disableDiv = function(divId,disableIt) {
    var obj = document.getElementById(divId)
    if (obj!=null) {
      obj.disabled = disableIt;
      obj.selectedIndex = obj.options.length - 1;
    }
}

// after server-side search, we set focus on the search element.
FilterEdit.prototype.setFocusOnSearch = function() {
    var els = document.getElementsByName(FilterEditPageConstants.pSEARCH_ANCHOR);
    if (els && els.length == 1){
        var anchor = els[0];
        anchor.focus();
    }
}
/**
 * Functions for handling the defaulting of Additional To, CC and BCC fields on the Email Author
 * page.  A lookup popup window is opened where additional values can be selected and populated
 * back on the Email Author page.
 * @author ccopek
 * @since 150
 */

function EmailCCBccLookup() {}

EmailCCBccLookup.prototype.storeAddrs = function(select, names, addrs, skipValue) {
	names.value = "";
  	addrs.value = "";
  	if (select != null) {
    	var isFirst = true;
    	for (var i=0; i<select.length; i++) {
      		if (select.options[i] != null && select.options[i].value != '' && select.options[i].value != skipValue) {
        		if (isFirst) {
	            	isFirst = false;
        		}
        		else {
          			names.value += EmailAuthorConstants.EMAIL_ADDR_DELIM;
          			addrs.value += EmailAuthorConstants.EMAIL_ADDR_DELIM;
        		}

        		names.value += select.options[i].text;
        		addrs.value += select.options[i].value;
      		}
    	}
	}
}

EmailCCBccLookup.prototype.storeAllAddresses = function(skipValue) {
    EmailCCBccLookup.prototype.storeAddrs(document.getElementById(EmailCCBccLookupConstants.ADDITIONAL_TO_ID),
    		   document.getElementById(EmailCCBccLookupConstants.ADDITIONAL_TO_NAME_ID),
    		   document.getElementById(EmailCCBccLookupConstants.ADDITIONAL_TO_ADDR_ID),
    		   skipValue);
	EmailCCBccLookup.prototype.storeAddrs(document.getElementById(EmailCCBccLookupConstants.CC_ID),
    		   document.getElementById(EmailCCBccLookupConstants.CC_NAME_ID),
    		   document.getElementById(EmailCCBccLookupConstants.CC_ADDR_ID),
    		   skipValue);
    EmailCCBccLookup.prototype.storeAddrs(document.getElementById(EmailCCBccLookupConstants.BCC_ID),
    		   document.getElementById(EmailCCBccLookupConstants.BCC_NAME_ID),
    		   document.getElementById(EmailCCBccLookupConstants.BCC_ADDR_ID),
    		   skipValue);
}

EmailCCBccLookup.prototype.changeContactType = function(skipValue) {
	EmailCCBccLookup.prototype.storeAllAddresses(skipValue);
	var form = document.forms[EditPageConstants.pEDIT_PAGE];
    form.submit();
    return true;
}

function CriteriaInput(oppLabels, numFilters, prevEntityData) {
	this.oppLabels = oppLabels;
	this.numFilters = numFilters;
	
	this.currentEntityData = prevEntityData;
	this.entityCache = {};
	if (prevEntityData){
		this.entityCache[prevEntityData.entityName] = prevEntityData;
	}
	
	var self = this;
	
	this.handleFieldChange = function(e){
		var field = getEventTarget(getEvent(e));
		self.updateOperator(field, self.getOp(field));
	}
	
	this.init();
}

CriteriaInput.prototype.init = function(){
	for (var i = 1; i <= this.numFilters; i++){
		var field = document.getElementById(CriteriaInputConstants.pCOL + i);
		//initialize any fields that might already be selected
		if (field.selectedIndex > 0){
			this.updateOperator(field, this.getOp(field));
		}
		addEvent(field, 'change', this.handleFieldChange, false);	
	}
}

CriteriaInput.prototype.setEntity = function(entityData, checkPrevVals){
	if (entityData !== this.currentEntityData){
		this.currentEntityData = entityData;
		this.refreshElementsFromData(checkPrevVals);
	}
}

CriteriaInput.prototype.refreshElementsFromData = function(checkPrevVals){
	for (var i = 1; i <= this.numFilters; i++){
		
		var newSel = Util.refreshDynamicSelect(
			document.getElementById(CriteriaInputConstants.pCOL + i),
			this.currentEntityData.getFieldOptions(),
			true);
		addEvent(newSel, 'change', this.handleFieldChange, false);
		document.getElementById(CriteriaInputConstants.pOP + i).selectedIndex = 0;
		document.getElementById(CriteriaInputConstants.pVAL + i).value = "";
		document.getElementById(CriteriaInputConstants.pLOOKUP + i).style.display = 'none';
	}
} 

CriteriaInput.prototype.updateOperator = function(fieldSel, opSel){
	var oldWidth;
	//IE doesn't have a width when called from init()
	if (opSel.offsetWidth > 0)
		oldWidth = opSel.offsetWidth + 'px';
	
	var opList = null;
	var fieldVal = fieldSel.options[fieldSel.selectedIndex].value;
	if (fieldVal){
		opList = this.currentEntityData.getOperators(fieldVal);
	} else {
		opList = null;
	}
	
	var prevSelectedVal = opSel.options[opSel.selectedIndex].value;
	var optionsList = []
	if (opList){
		for (var i = 0; i < opList.length; i++){
			optionsList.push([this.oppLabels[opList[i]], opList[i]]);
		}
	}
	opSel = Util.refreshDynamicSelect(opSel, optionsList, true, prevSelectedVal);
	if (oldWidth){
		opSel.style.width = oldWidth;
	} else {
		opSel.style.width = "98%";
	}
	
	var lookupEl = this.getLookup(fieldSel);
	if (this.currentEntityData.needsLookup(fieldVal)){
		lookupEl.style.display = 'block';
	} else {
		lookupEl.style.display = 'none';
	}
}

CriteriaInput.prototype.getLookup = function(fieldEl){
	var num = fieldEl.id.substring(CriteriaInputConstants.pCOL.length, fieldEl.id.length);
	return document.getElementById(CriteriaInputConstants.pLOOKUP + num);
}

CriteriaInput.prototype.getOp = function(fieldEl){
	var num = fieldEl.id.substring(CriteriaInputConstants.pLOOKUP.length, fieldEl.id.length);
	return document.getElementById(CriteriaInputConstants.pOP + num);
}

function CriteriaEntityData(entityName, fields){
	this.entityName = entityName;
	this.fields = fields;
}

CriteriaEntityData.prototype.getFieldOptions = function(){
	if (this.fieldOptions){
		return this.fieldOptions;
	}
	this.fieldOptions = []
	for (var i = 0; i < this.fields.length; i++){
		this.fieldOptions.push([this.fields[i].label, this.entityName + '.' + this.fields[i].name]);
	}
	return this.fieldOptions;
}

CriteriaEntityData.prototype.getOperators = function(fieldName){
	var colType = ColumnType[this.getFieldByName(fieldName).columnType];
	if (colType.filterQueryOperators){
		return colType.filterQueryOperators;
	} else {
		return colType.queryOperators;
	}
}

CriteriaEntityData.prototype.getFieldByName = function(entityFieldName){
	var field;
	var fieldName = entityFieldName.split('.')[1];
	for (var i = 0; i < this.fields.length; i++){
		if (this.fields[i].name === fieldName){
			field = this.fields[i];
			break;
		}
	}
	return field
}

CriteriaEntityData.prototype.needsLookup = function(fieldName){
	if (!fieldName){
		return false;
	}
	//HACK.  See FilterView
	if (fieldName == 'MEMBER_STATUS' || fieldName == 'CAMPAGN_MEMBER.STATUS' || fieldName == 'Lead.CampaignMemberStatus'){
		return false;
	}

	return ColumnType[this.getFieldByName(fieldName).columnType].needsLookup;
}
	
var HTMLTreeNode = function(){}

HTMLTreeNode.prototype.toggleHTMLTree = function(topic) {
    var informationDiv=document.getElementById('treeInformation');
    if (informationDiv==null){
        return;
    }

    var obj=document.getElementById(topic +informationDiv.getAttribute('child'));
    if (obj != null) {
        visible=(obj.style.display!='none');
        var key=document.getElementById(topic + informationDiv.getAttribute('icon'));
        var currentTitle = key.getElementsByTagName('img')[0].title;

        if (visible) {
            obj.style.display='none';
            var image=key.getElementsByTagName('img')[0];
            image.src=informationDiv.getAttribute('plusSrc');
            image.title = HTMLTreeNode.prototype.changePreTitle(currentTitle,informationDiv.getAttribute('expand'));
            image.alt = HTMLTreeNode.prototype.changePreTitle(currentTitle,informationDiv.getAttribute('expand'));
            SetupTreeNode.prototype.removeFromOpenSetup(topic);
        } else {
            obj.style.display='block';
            var image=key.getElementsByTagName('img')[0];
            image.src=informationDiv.getAttribute('minusSrc');
            image.title = HTMLTreeNode.prototype.changePreTitle(currentTitle,informationDiv.getAttribute('collapse'));
            image.alt = HTMLTreeNode.prototype.changePreTitle(currentTitle,informationDiv.getAttribute('collapse'));
            SetupTreeNode.prototype.addToOpenSetup(topic);
        }
    }
}

HTMLTreeNode.prototype.changePreTitle = function(currentTitle, preTitle){
    return preTitle+" "+currentTitle.substr(currentTitle.indexOf('-'));
}

// if section is null or invalid there is a fall-back mechanism that will 
// attempt to open up needed section by the topic given.
// HTMLTreeNode.prototype.openHTMLTree() is referenced from functions.js and help.js
HTMLTreeNode.prototype.openHTMLTree = function(sectionEsc,topicEsc) {
    var section = (sectionEsc) ? unescapeJsInHtml(sectionEsc) : null;
    var topic = (topicEsc) ? unescapeJsInHtml(topicEsc) : null;
    var informationDiv = document.getElementById('treeInformation');
    if (informationDiv==null){
        return;
    }
    // highlight the needed topic
    if (topic!=null){
         HTMLTreeNode.prototype.unHighlightAll();

         var leaf = document.getElementById(topic +informationDiv.getAttribute('leaf'));
         if (leaf && leaf.className){
             leaf.className="setupHighlightLeaf"	;
             informationDiv.setAttribute('lastHighlight',topic +informationDiv.getAttribute('leaf'));
         }
     }
     // get the secton to expand
     var obj=document.getElementById(section +informationDiv.getAttribute('child'));
     if (obj==null && topic!=null){

         /**
         * Try to open up a secton by going to the parent
         */
         var leaf = document.getElementById(topic +informationDiv.getAttribute('leaf'));
         if (leaf!=null){
             var divParent = leaf.parentNode;
            if (divParent!=null && divParent.id.indexOf(informationDiv.getAttribute('child')) >0){
                section = divParent.id.substr(0,divParent.id.indexOf(informationDiv.getAttribute('child')));
                obj=divParent;
            }
         }
     }
    if (obj != null) {
        var key=document.getElementById(section +informationDiv.getAttribute('icon'));
        var currentTitle = key.getElementsByTagName('img')[0].title;
        obj.style.display='block';
        var image=key.getElementsByTagName('img')[0];
        image.src=informationDiv.getAttribute('minusSrc');
        image.title = HTMLTreeNode.prototype.changePreTitle(currentTitle,informationDiv.getAttribute('collapse'));
        image.alt = HTMLTreeNode.prototype.changePreTitle(currentTitle,informationDiv.getAttribute('collapse'));
        var divParent = obj.parentNode;
        if (divParent!=null){
            divParent = divParent.parentNode;
            if (divParent!=null && divParent.id.indexOf(informationDiv.getAttribute('child')) >0){
                var section = divParent.id.substr(0,divParent.id.indexOf(informationDiv.getAttribute('child')));
                HTMLTreeNode.prototype.openHTMLTree(section,null);
            }
      }
      if (topic!=null){
            window.scrollTo(0,key.offsetTop)
        }
    }else if (topic!=null){
        // if this topic section was not part of any section
        var leaf = document.getElementById(topic + informationDiv.getAttribute('leaf'));
        if (leaf!=null){
            window.scrollTo(0,leaf.offsetTop)
        }
    }
}

HTMLTreeNode.prototype.unHighlightAll = function(){
         var informationDiv = document.getElementById('treeInformation');
         if (informationDiv==null){
            return;
         }
        var lastHighlight = informationDiv.getAttribute('lastHighlight');
        if (lastHighlight==null){
            return;
        }
        var elementH =  document.getElementById(lastHighlight);
        if (elementH!=null && elementH.className==informationDiv.getAttribute('highlightClass')){
            elementH.className =informationDiv.getAttribute('leafClass');
        }
}


HTMLTreeNode.prototype.populateTheTreeInformation = function(font,child,icon,leaf,expand,collapse,plusSrc,minusSrc,highlightClass,leafClass){

    var informationDiv = document.getElementById('treeInformation');
    if (informationDiv==null){
        return;
    }
    informationDiv.setAttribute('font',font);
    informationDiv.setAttribute('child',child);
    informationDiv.setAttribute('icon',icon);
    informationDiv.setAttribute('leaf',leaf);
    informationDiv.setAttribute('expand',expand);
    informationDiv.setAttribute('collapse',collapse);
    informationDiv.setAttribute('plusSrc',plusSrc);
    informationDiv.setAttribute('minusSrc',minusSrc);
    informationDiv.setAttribute('highlightClass',highlightClass);
    informationDiv.setAttribute('leafClass',leafClass);
    informationDiv.setAttribute('lastHighlight','none');
}






/**
 * Animation related functions and constants
 * @author mpaksoy
 * @since 150
 */
var Animation = {
    // This function is created under window when animations are in progress.
    ANIMATION_SLAVE : '_animationEventHandler',
    
    RESIZE_HEIGHT_STEPS : 8,
    RESIZE_HEIGHT_DELAY : 20, // milliseconds
    
    animations : {},
    
     /**
     * Animates the given element resizing it from from the starting height to final height.
     * @param elem DOMElement to manipulate
     * @param heightBefore height of the object at the beginning (the object is initialized according to this)
     * @param heightAfter height of the object at the end.
     *                    if set to -1, objects final normal height is used. 
     * @param finalize function called once the resize animation is complete
     */
    animateResizeHeight : function(/*DOMElem*/ elem, /*number*/ heightBefore, /*number*/ heightAfter, /*function*/ finalize) {
        if (!elem) return;

        var savedState;
        if (Animation.animations[elem]) { // stop old animation
            savedState = Animation.animations[elem];
            savedState.clean();
        } else {
            savedState = {};
            savedState.height = XBrowser.getElementStyle(elem, 'height');
            savedState.overflow = elem.style.overflow;
        }

        Animation.animations[elem] = savedState;
        elem.style.overflow = 'hidden';

        if (heightAfter === -1) {
            elem.style.height = heightBefore + 'px';
            elem.style.display = 'block'
            heightAfter = XBrowser.getActualHeight(elem, true);
        }

        var self = this;
        var series = Animation._getLinearSeries(heightBefore, heightAfter, Animation.RESIZE_HEIGHT_STEPS);
        var i = 0;

        savedState.clean = function() {
            elem.style.height = savedState.height;
            elem.style.overflow = savedState.overflow;
            clearInterval(savedState.intervalHandle);
            Animation.animations[elem] = null;
            if (finalize) finalize();
        };

        savedState.stepper = function() {
            if (i < Animation.RESIZE_HEIGHT_STEPS) {
                var curHeight = series[i];
                elem.style.height = curHeight + 'px';
                i++;
            } else {
                savedState.clean();
            }
        };
        savedState.intervalHandle = setInterval(savedState.stepper, Animation.RESIZE_HEIGHT_DELAY);
        savedState.stepper();
    },
    
    /** Animate element coming into view. Wrapper around animateResizeHeight. */
    rollIn : function(element, finalize) {
        Animation.animateResizeHeight(element, 1,-1, finalize);
    },
    
    /** Animate element going out of view. Wrapper around animateResizeHeight. */
    rollOut: function(element, finalize) {
        element.style.display = 'block';
        Animation.animateResizeHeight(element, XBrowser.getActualHeight(element), 1, function() {
                element.style.display = 'none';
                if (finalize) finalize();
            });
    },
    
    /**
     * Bring element into view, wait delay milliseconds, and bring element out of view.
     * Nice for notifications.
     */
    rollInRollOut : function(element, delay, finalize) {
        Animation.rollIn(element, function() {
                setTimeout(function() {Animation.rollOut(element, finalize)}, delay)
            });
    },
    
    /**
     * Fade in/out the element from the starting opacity to the final opacity.
     * @param elem DOMElement to manipulate
     * @param opacityBefore percent opacity at the beginning
     * @param opacityBefore percent opacity at the end
     * @param finalize function called once the resize animation is complete
     */
    animateOpacity : function(/*DOMElem*/ elem, /*number*/ opacityBefore, /*number*/ opacityAfter, /*function*/ finalize) {
        if (!elem) return;
        
        if (window[Animation.ANIMATION_SLAVE]) { // animation in progress, stop
            return;
        }
    
        // clean up parameters
        if (opacityBefore < 0) {
            opacityBefore = 0;
        } else if (opacityBefore > 100) {
            opacityBefore = 100;
        }
        if (opacityAfter < 0) {
            opacityAfter = 0;
        } else if (opacityAfter > 100) {
            opacityAfter = 100;
        }
    
        // TODO relative step sizes! (duration/smoothness based animation)
        var STEP = 5; // percent opacity
        var DELAY = 20; // milliseconds
        var self = this;

        var isIncreasing = (opacityBefore < opacityAfter);
        currentOpacity = Animation.setOpacity(elem, opacityBefore);
        if (opacityBefore == opacityAfter) {
            return
        }
        window[Animation.ANIMATION_SLAVE] = function() {
            if ((isIncreasing && (currentOpacity > opacityAfter)) ||
                ((!isIncreasing) && (currentOpacity < opacityAfter))) {
                window[Animation.ANIMATION_SLAVE] = false;
                Animation.setOpacity(elem, opacityAfter);
                if (finalize) finalize();
                return;
            }
    
            if (isIncreasing) {
                currentOpacity = Animation.setOpacity(elem, currentOpacity+STEP);
            } else {
                currentOpacity = Animation.setOpacity(elem, currentOpacity-STEP);
            }
            setTimeout('window.'+Animation.ANIMATION_SLAVE+'();', DELAY);
        }
    
        window[Animation.ANIMATION_SLAVE]();
    },
    
    // wrapper around animateOpacity
    fadeOut : function(/*DOMElem*/ elem, /*function*/ finalize) {
        Animation.animateOpacity(elem, 100, 0, finalize);
    },
    
    // wrapper around animateOpacity
    fadeIn : function(/*DOMElem*/ elem, /*function*/ finalize) {
        Animation.animateOpacity(elem, 0, 100, finalize);
    },
    
    // HELPER FUNCTIONS
    /**
     * Cross browser compatible opacity setter
     * @param element DOMElement to modify
     * @param opacity percent opacity to set (70 means 70%)
     * @return new opacity value
     */
    setOpacity : function(/*DOMElement*/ element, /*number*/ opacity) {
        if (XBrowser.userAgent.isIE) {
            element.style.filter = 'alpha(opacity='+opacity+')';
        } else {
            element.style.opacity = opacity/100;
        }
        return opacity;
    },
    
    /**
     * Crows browser compatible, opacity clear.
     * Makes element fully opaque/visible.
     */
    clearOpacity : function(/*DOMElement*/ element) {
        if (XBrowser.userAgent.isIE) {
            element.style.filter = 'alpha(opacity=100)';
        } else {
            element.style.opacity = 1;
        }
    },

    // PRIVATE HELPERS
    /**
     * Get an array of numbers that reprepsents a linear progression from the start point to end point.
     * The series always includes start and end points.
     * @param start starting point for series
     * @param end ending point for series
     * @param steps number of elements in the retured array
     * @return array of floating point numbers
     */
    _getLinearSeries : function(/* int */ start, /* int */ end, /* int */ steps) {
        ret = [];
        steps -= 1; // count the beginning point as a step
        ret.push(start)
        step = (end - start) / steps;
        var current = start;
        for (var i = 0; i < (steps - 1); i++) {
            current += step;
            ret.push(current);
        }
        ret.push(end);
        return ret;
    }
}

/**
  PickableMotifElement mirrors the java class of the same name
  It is a subclass of MotifElement & is used on MotifPicker.java

  @author polcari
  @since 144


  @param motifElementId - The motifElement that this is attached to
  @param motifInputElementId - the parent's param
*/
function PickableMotifElement(id, descCellId, iconId, _motifKey, motifInputElementId) {
  this.init(id, descCellId, iconId, _motifKey); //superclass c'tr
  var self = this;
  if (window.opener) {
    this.parentMotifInputElementObj = window.opener.document.getElementById(motifInputElementId).motifInputElement;
  }
  this.motifElement.onclick = function () {
    //copy class
    self.parentMotifInputElementObj.motifElement.className = self.motifElement.className;
    //copy cell text
    self.parentMotifInputElementObj.setDescription(self.getDescription());
    //copy icon
    if (self.motifIcon && self.motifIcon.src) {
      self.parentMotifInputElementObj.setIconSrc(self.motifIcon.src);
      self.parentMotifInputElementObj.setIconValue('');
    }
    //copy input box
    self.parentMotifInputElementObj.setMotifKey(self.motifKey);
    window.blur();
    window.close();
    return false;
  };
}

//subclass of MotifElement
PickableMotifElement.prototype = new MotifElement;

//static function used on the motifpicker
PickableMotifElement.toggleUsedMotifVisibility = function(showThem) {
  var allMotifs = document.getElementsByTagName("a");

  for (var i = 0; i < allMotifs.length; i++) {
    if ((showThem) && (allMotifs[i].style.display == 'none') && (allMotifs[i].className.indexOf('motifElement') > -1)) {
      allMotifs[i].style.display = 'block';
    } else if ((!showThem) && (allMotifs[i].className) && (allMotifs[i].className.indexOf('usedMotif') > -1)) {
      allMotifs[i].style.display = 'none';
    }
  }
}

PickableMotifElement.hideUsedStyle = function() {
  document.getElementById("hideUsedStyle").style.display = 'none';
  document.getElementById("showUsedStyle").style.display = 'block';
  PickableMotifElement.toggleUsedMotifVisibility(false);
  return false;
}

PickableMotifElement.showUsedStyle = function() {
  document.getElementById("showUsedStyle").style.display = 'none';
  document.getElementById("hideUsedStyle").style.display = 'block';
  PickableMotifElement.toggleUsedMotifVisibility(true);
  return false;
}

/* Alerts the user with a message, and blocks the form from submiting.
 * This hack is needed because firefox will still submit a form even if an alert is shown. */
function alertBlockSubmit (msg) {
  window.ffInAlert = true;
  alert(msg);
  window.ffInAlert = false;
}

function Modal(){}

Modal.confirm = function(msg) {
  top.ffInAlert = true;
  var ans = window.confirm(msg);
  top.ffInAlert = false;
  return ans;
}

Modal.isBlocked = function () {
  return top.ffInAlert;
}

/**
 * Introducing a brand new flavor of UserContext, this time in javascript! Variables passed down from
 * java MUST be declared here or they will not be set.
 * 
 * @author jmooney
 * @since 148
 */
var UserContext = {
	
    initialized : false,
    locale : "",
    language: "",
    startOfWeek : 0,
    dateFormat : "",
    dateTimeFormat : "",
    ampm: null,
    today : "",
    isAccessibleMode : false,
    userPreferences : null,

    initialize : function(values) {
    	if (!values){
    		UserContext.initializeFromServlet();
    		return;
    	}
    	UserContext.processValues(values);
    },
    
    initializeFromServlet : function(){
		var url = "/_ui/system/context/UserContextServlet";
		XBrowser.getHttpResponse(url, function(request) {
		    var values = Util.evalAjaxServletOutput(request.responseText);
    	    UserContext.processValues(values);
		} );
    },
    
    processValues : function (values) {
        for (var key in values) {
            // force strict variable declaration
            if (typeof UserContext[key] != "undefined") {
                if (key == "userPreferences" || key == "orgPreferences") {
                    UserContext[key] = new PreferenceBits(values[key]);
                } else {
                    UserContext[key] = values[key];
                }
            }
        }
        UserContext.initialized = true;
    }
    
};

function FormulaEditor(){};

FormulaEditor.init = function( editorElementId, functionSelectionElementId, formId, validationStatusNotValidated ) {
    FormulaEditor.editorElementId = editorElementId;
    FormulaEditor.functionSelectionElementId = functionSelectionElementId;
    FormulaEditor.formId = formId;
    FormulaEditor.validationStatusNotValidated = validationStatusNotValidated;
    FormulaEditor.setFunctionInfo(FormulaEditor.getSelectedFunction());

}

FormulaEditor.setFunctionInfo = function( functionName ) {
    if (!functionName)
        return;
    document.getElementById('funcFormat').innerHTML = functionNameToPrototypeMap[functionName];
    document.getElementById('funcExplain').innerHTML = functionNameToDescriptionMap[functionName];
}

FormulaEditor.getSelectedFunction = function() {
    functionSelectElement = document.getElementById(FormulaEditor.functionSelectionElementId);
    if (!functionSelectElement)
        return null;

    return functionSelectElement.options[functionSelectElement.selectedIndex].value;
}

FormulaEditor.setListToCategory = function( category, categoryMap, targetId, defaultCategory, preserveFirstOption) {
    var targetSelectElement = document.getElementById(targetId);
    var list = categoryMap[(category != '') ? category : defaultCategory];
    var offset = (preserveFirstOption) ? 1 : 0;

    targetSelectElement.options.length = offset;
    if (!list) return;
    for (var i = 0; i < list.length; i++) {
        targetSelectElement.options[i + offset] = list[i];
    }
}

FormulaEditor.switchMode = function( mode ) {
    if (document.getElementById('editorMode').value == mode)
        return;
    document.getElementById('editorMode').value = mode;
    document.getElementById('changeEditorMode').value = "1";
    document.getElementById(FormulaEditor.formId).submit();
}

FormulaEditor.formulaKeypressEventListener = function() {
    var validationStatus = document.getElementById('validationStatus');
    if(validationStatus) {
        validationStatus.innerHTML = FormulaEditor.validationStatusNotValidated;
    }
}

FormulaEditor.registerFormulaEventListeners = function( name ) {
    // Wire up event listener to change validation status
    setCurrentParamName(name);
    var editorDoc = getDoc();
    if (editorDoc.addEventListener)
        editorDoc.addEventListener("keypress", FormulaEditor.formulaKeypressEventListener, false);
    else
        editorDoc.onkeypress = FormulaEditor.formulaKeypressEventListener;
}

FormulaEditor.insertFieldReference = function( name, fieldSelector, insertCurlyBangDelims ) {
    var value = fieldSelector.options[fieldSelector.selectedIndex].value;
    if (value) {
        FormulaEditor.insertCode(name, value, insertCurlyBangDelims);
        fieldSelector.selectedIndex = 0;
    }
}

FormulaEditor.insertCode = function( name, value, insertCurlyBangDelims ) {
    var insert;
    restoreSelection(document.getElementById(name));
    insert = (insertCurlyBangDelims && !findDelimiters(name, '{!', '}')) ? '{!' + value + '}' : ' ' + value + ' ';
    insertTextAtSelectionInEditor(name, insert);
}

var SIDEBAR_DIV_WIDTH = 216;
var SIDEBAR_DIV_EDGE = 19; //width of handle (b/w right edge and window edge when collapsed)
var SIDEBAR_DIV_SPACE = 0; //Between left edge and content when pinned
var SIDEBAR_RIGHT_INC = 20;
var SIDEBAR_TIMESTEP = 8;
var SIDEBAR_OUT_DELAY = 500;
var SIDEBAR_IN_DELAY = 100;
var SIDEBAR_BORDER_WIDTH = 1;	// used to align the handle with the sidebarDiv


/**
 *  Sidebar.  This is the controller object for the collapsible sidebar.
 *
 *  @author emoses
 *  @since 144
 *
 *  @param sidebarDiv      object. A reference to the sidebar div DOM object.
 *  @param enableCollapse  boolean.  True to enable the collapsible sidebar
 *  @param isPinnned	   boolean.  Whether the sidebar is initialized pinned
 *
 *  Note that isPinned is not required if enabledCollapse is false.
 */
function Sidebar(sidebarDiv, enableCollapse, isPinned) {
  this.div = sidebarDiv;
  this.inOutBox = document.getElementById(SidebarConstants.HANDLE_ID);
  // there are 2 handles, one at the bottom, one at the top
  this.handle = document.getElementById(SidebarConstants.PIN_INDICATOR_ID);
  this.handle2 = document.getElementById(SidebarConstants.PIN2_INDICATOR_ID);
  this.pinned = isPinned;

  this.openHandleRE = /\bopen\b/;

  if (!enableCollapse){
    /* Sidebar not enabled.  Kill the cookie if it exists, resize the sidebar to the
     * size of the body, and exit without instanciating this object
     */
    deleteCookie(SidebarConstants.SIDEBAR_PINNED_COOKIE);
  }

  //this is the reportOverview page and has no sidebar
  if ((enableCollapse) && (!this.pinned) && (!document.getElementById(BodyLayout.BODY_TABLE_ID)) && (document.body)) {
    document.body.className = document.body.className + ' hiddenSidebar';
  }

  if ((!enableCollapse) || (!document.getElementById(BodyLayout.BODY_TABLE_ID))) {
    this.handle.style.display = "none";
    this.handle2.style.display = "none";
    this.sizeBodyToSidebar();
    this.sizeToBody();

    return null;
  }


  //Some styles are specific to the collapsible sidebar
  this.div.className = "collapsible";

  /*private members*/
  var self = this;
  var outDelay = -1;
  var locked = false;

  /* instantiate the controller object for the moveIn/moveOut behavior.  This is all callbacks */
  this.mover = new Fader(
    this.div, //object
    function(object, currVal) { return currVal <= -1* (SIDEBAR_DIV_WIDTH-SIDEBAR_DIV_EDGE); },  //textMin
    function(object, currVal) { return currVal >= 0; }, //testMax
    SIDEBAR_TIMESTEP, //timestep
    function(currVal, sign){ return currVal += sign*SIDEBAR_RIGHT_INC; }, // nextStep
    function(object, nextVal){ object.setStyle('left', nextVal + "px"); }, //increment
    0, //startVal
    function(object) { object.style.left = (-1*(SIDEBAR_DIV_WIDTH-SIDEBAR_DIV_EDGE))+ "px"; return -1*(SIDEBAR_DIV_WIDTH-SIDEBAR_DIV_EDGE) }, //finalMin
    function(object) { object.style.left = "0px"; return 0; } //finalMax
  );

  this.handleInOutClick = function(e) {
    if (!locked){
      self.pin();
    }
  };

  this.handleDocumentKeyDown = function(e){
    var evt = getEvent(e);
    var pos = self.mover.getPosition();
    if (evt.altKey && String.fromCharCode(evt.keyCode) === "S"){
      //ALT-s
      //Highlight the text in the sidebar search area
      var searchBox = document.getElementById(SidebarConstants.pSEARCH_SIDEBAR_STR);
      if (searchBox){
        searchBox.focus();
        searchBox.select();
      }
      //reverse
      if (!locked){
        self.pin();
      }
    }
  };

  this.lock = function() {
    locked = true;
  };

  this.unlock = function() {
    locked = false;
  };

  this.init();
}

Sidebar.prototype.sizeToBody = function(){
  var content = document.getElementById(BodyLayout.BODY_TABLE_ID);
  if (content) {
    this.div.style.height = content.offsetHeight + "px";
    var newHeight = content.offsetHeight - SIDEBAR_BORDER_WIDTH;
    if(newHeight >= 0){
    	this.inOutBox.style.height = newHeight + "px";
    }
  }
};

Sidebar.prototype.sizeBodyToSidebar = function(){
  var content = document.getElementById(BodyLayout.BODY_TABLE_ID);
  if ((content) && (this.div.offsetHeight > content.offsetHeight)) {
    content.style.height = (this.div.offsetHeight - SIDEBAR_BORDER_WIDTH) + "px";
  }
};

Sidebar.prototype.sizeBodyToSidebarNoCheck = function(){
  var content = document.getElementById(BodyLayout.BODY_TABLE_ID);
  var inner = this.div.lastChild
  content.style.height = (inner.offsetHeight - SIDEBAR_BORDER_WIDTH) + "px";
};

/* public functions */
Sidebar.prototype.setSidebarCookie = function(val){
  var expires = new Date();
  expires.setTime(expires.getTime() + (3650*24*60*60*1000));
  setCookie(SidebarConstants.SIDEBAR_PINNED_COOKIE, val, expires);
};

Sidebar.prototype.getSidebarCookie = function(){
  return getCookie(SidebarConstants.SIDEBAR_PINNED_COOKIE);
};

Sidebar.prototype.init = function() {
  addEvent(document, 'keydown', this.handleDocumentKeyDown, false);
  addEvent(this.inOutBox, 'click', this.handleInOutClick, true);
  addEvent(this.handle, 'click', this.handleInOutClick, true);
  addEvent(this.handle2, 'click', this.handleInOutClick, true);

  this.sizeBodyToSidebar();
  this.div.display = "none";
  if (!this.pinned){
    this.pinned = true //Reverse it for the benefit of this.pin()
    this.pin(true);
  } else {
    document.getElementById(BodyLayout.BODY_CELL_ID).style.paddingLeft = SIDEBAR_DIV_WIDTH + "px";
  }
  this.setTitleAndClass();

  //Check for errors in the sidebar, and jump it out if need be

  if (getElementsByClassName(EditElement.ERROR_CLASS, this.div).length > 0){
    this.mover.jumpIn();
  }
};

Sidebar.prototype.pin = function(noCookie) {
  var content = document.getElementById(BodyLayout.BODY_CELL_ID);
  if (this.pinned){
    this.pinned = false;
    content.style.paddingLeft = (SIDEBAR_DIV_EDGE + SIDEBAR_DIV_SPACE) + "px";
    this.sizeToBody();
    this.mover.jumpOut();
    if (!noCookie) { this.setSidebarCookie("0"); }
  } else {
    this.pinned = true;
    content.style.paddingLeft = (SIDEBAR_DIV_WIDTH + SIDEBAR_DIV_SPACE) + "px";
    this.sizeToBody();
    this.mover.jumpIn();
    if (!noCookie) { this.setSidebarCookie("1"); }
  }
  this.setTitleAndClass();
};

Sidebar.prototype.setTitleAndClass = function() {
  if (this.pinned) {
    this.handle.title = LC.getLabel("Sidebar", "collapse");
    this.handle2.title = LC.getLabel("Sidebar", "collapse");
    this.inOutBox.title = LC.getLabel("Sidebar", "collapse");
    if (!this.handle.className.match(this.openHandleRE)) {
      this.handle.className += " open";
      this.handle2.className += " open";
    }
    if (!this.inOutBox.className.match(this.openHandleRE)) {
      this.inOutBox.className += " open";
    }
  } else {
    this.handle.title = LC.getLabel("Sidebar", "expand");
    this.handle2.title = LC.getLabel("Sidebar", "expand");
    this.inOutBox.title = LC.getLabel("Sidebar", "expand");
    this.handle.className = this.handle.className.replace(this.openHandleRE, "");
    this.handle2.className = this.handle.className.replace(this.openHandleRE, "");
    this.inOutBox.className = this.inOutBox.className.replace(this.openHandleRE, "");
  }
};

/**
 *  Hover.js, the main class for a hover in the app
 *
 *  @author eli
 *  @since 146 (rewrite of 144 code in functions.js)
 */

function Hover() {
    this.loaded = false;
	this.xCoord = 0;
	this.xObjLeft = 0;
	this.xObjRight = 0;
	this.yCoord = 0;
	this.yObjTop = 0;
	this.yObjBottom = 0;
	this.showing = false;
	this.showTimer = false;
	this.hideTimer = false;
}

Hover.hoverMap = new Object();
Hover.hoversEnabled = true;

Hover.disableHovers = function() {
    Hover.hoversEnabled = false;
}

Hover.getHover = function(elementId) {
    var hover = Hover.hoverMap[elementId];
    if (typeof hover 
