Core = { _copyFunction: function(f) { return function() { f.apply(this, arguments); }; }, _createFunction: function() { return function() { }; }, extend: function() { var baseClass = arguments.length == 1 ? null : arguments[0]; var definition = arguments.length == 1 ? arguments[0] : arguments[1]; var x, name; if (arguments.length == 2) { if (typeof(baseClass) != "function") { throw new Error("Base class is not a function, cannot derive."); } } if (!definition) { throw new Error("Object definition not provided."); } var constructorClass; if (definition.$construct) { constructorClass = definition.$construct; delete definition.$construct; } else { if (baseClass) { constructorClass = Core._copyFunction(baseClass); } else { constructorClass = Core._createFunction(); } } constructorClass.$virtual = {}; constructorClass.$super = baseClass; if (baseClass) { var prototypeClass = Core._createFunction(); prototypeClass.prototype = baseClass.prototype; constructorClass.prototype = new prototypeClass(); } constructorClass.prototype.constructor = constructorClass; if (definition.$abstract) { constructorClass.$abstract = {}; if (baseClass && baseClass.$abstract) { for (x in baseClass.$abstract) { constructorClass.$abstract[x] = baseClass.$abstract[x]; } } if (definition.$abstract instanceof Object) { for (x in definition.$abstract) { constructorClass.$abstract[x] = true; constructorClass.$virtual[x] = true; } } delete definition.$abstract; } if (baseClass) { for (name in baseClass.$virtual) { constructorClass.$virtual[name] = baseClass.$virtual[name]; } } if (definition.$virtual) { Core._inherit(constructorClass.prototype, definition.$virtual, constructorClass.$virtual); for (name in definition.$virtual) { constructorClass.$virtual[name] = true; } delete definition.$virtual; } if (definition.hasOwnProperty("toString")) { constructorClass.prototype.toString = definition.toString; } if (definition.hasOwnProperty("valueOf")) { constructorClass.prototype.valueOf = definition.valueOf; } delete definition.toString; delete definition.valueOf; if (definition.$include) { var mixins = definition.$include.reverse(); Core._processMixins(constructorClass, mixins); delete definition.$include; } var loadMethod = null; if (definition.$load) { loadMethod = definition.$load; delete definition.$load; } if (definition.$static) { Core._inherit(constructorClass, definition.$static); delete definition.$static; } Core._inherit(constructorClass.prototype, definition, constructorClass.$virtual); if (!constructorClass.$abstract) { this._verifyAbstractImpl(constructorClass); } if (loadMethod) { loadMethod.call(constructorClass); } return constructorClass; }, get: function(object, path) { for (var i = 0; i < path.length; ++i) { object = object[path[i]]; if (!object) { return null; } } return object; }, _isVirtual: function(virtualProperties, propertyName) { switch (propertyName) { case "toString": case "valueOf": return true; } return virtualProperties[propertyName]; }, _inherit: function(destination, source, virtualProperties) { for (var name in source) { if (virtualProperties && destination[name] !== undefined && !this._isVirtual(virtualProperties, name)) { throw new Error("Cannot override non-virtual property \"" + name + "\"."); } else { destination[name] = source[name]; } } }, method: function(instance, method) { return function() { return method.apply(instance, arguments); }; }, _processMixins: function(destination, mixins) { for (var i = 0; i < mixins.length; ++i) { for (var mixinProperty in mixins[i]) { if (destination.prototype[mixinProperty]) { continue; } destination.prototype[mixinProperty] = mixins[i][mixinProperty]; } } }, set: function(object, path, value) { var parentObject = null; for (var i = 0; i < path.length - 1; ++i) { parentObject = object; object = object[path[i]]; if (!object) { object = {}; parentObject[path[i]] = object; } } object[path[path.length - 1]] = value; }, _verifyAbstractImpl: function(constructorClass) { var baseClass = constructorClass.$super; if (!baseClass || !baseClass.$abstract || baseClass.$abstract === true) { return; } for (var x in baseClass.$abstract) { if (constructorClass.prototype[x] == null) { throw new Error("Concrete class does not provide implementation of abstract method \"" + x + "\"."); } } } }; Core.Debug = { consoleElement: null, useAlertDialog: false, consoleWrite: function(text) { if (Core.Debug.consoleElement) { var entryElement = document.createElement("div"); entryElement.appendChild(document.createTextNode(text)); if (Core.Debug.consoleElement.childNodes.length === 0) { Core.Debug.consoleElement.appendChild(entryElement); } else { Core.Debug.consoleElement.insertBefore(entryElement, Core.Debug.consoleElement.firstChild); } } else if (Core.Debug.useAlertDialog) { alert("DEBUG:" + text); } }, toString: function(object) { var s = ""; for (var x in object) { if (typeof object[x] != "function") { s += x + ":" + object[x] + "\n"; } } return s; } }; Core.Arrays = { containsAll: function(array1, array2, unique) { if (unique && array1.length < array2.length) { return false; } if (array2.length === 0) { return true; } var found, item; for (var i = 0; i < array2.length; ++i) { found = false; item = array2[i]; for (var j = 0; j < array1.length; ++j) { if (item == array1[j]) { found = true; break; } } if (!found) { return false; } } return true; }, indexOf: function(array, item) { for (var i = 0; i < array.length; ++i) { if (item == array[i]) { return i; } } return -1; }, remove: function(array, item) { for (var i = 0; i < array.length; ++i) { if (item == array[i]) { array.splice(i, 1); return; } } }, removeDuplicates: function(array) { array.sort(); var removeCount = 0; for (var i = array.length - 1; i > 0; --i) { if (array[i] == array[i - 1]) { array[i] = array[array.length - 1 - removeCount]; ++removeCount; } } if (removeCount > 0) { array.length = array.length - removeCount; } } }; Core.Arrays.LargeMap = Core.extend({ $static: { garbageCollectEnabled: false }, _removeCount: 0, garbageCollectionInterval: 250, map: null, $construct: function() { this.map = {}; }, _garbageCollect: function() { this._removeCount = 0; var newMap = {}; for (var key in this.map) { newMap[key] = this.map[key]; } this.map = newMap; }, remove: function(key) { delete this.map[key]; if (Core.Arrays.LargeMap.garbageCollectEnabled) { ++this._removeCount; if (this._removeCount >= this.garbageCollectionInterval) { this._garbageCollect(); } } }, toString: function() { return Core.Debug.toString(this.map); } }); Core.ListenerList = Core.extend({ _data: null, $construct: function() { this._data = []; }, addListener: function(eventType, eventTarget) { this._data.push(eventType, eventTarget); }, fireEvent: function(event) { if (event.type == null) { throw new Error("Cannot fire event, type property not set."); } var i, returnValue = true, listeners = []; for (i = 0; i < this._data.length; i += 2) { if (this._data[i] == event.type) { listeners.push(this._data[i + 1]); } } for (i = 0; i < listeners.length; ++i) { returnValue = listeners[i](event) && returnValue; } return returnValue; }, getListenerTypes: function() { var types = []; for (var i = 0; i < this._data.length; i += 2) { types.push(this._data[i]); } Core.Arrays.removeDuplicates(types); return types; }, getListeners: function(eventType) { var listeners = []; for (var i = 0; i < this._data.length; i += 2) { if (this._data[i] == eventType) { listeners.push(this._data[i + 1]); } } return listeners; }, getListenerCount: function(eventType) { var count = 0; for (var i = 0; i < this._data.length; i += 2) { if (this._data[i] == eventType) { ++count; } } return count; }, hasListeners: function(eventType) { for (var i = 0; i < this._data.length; i += 2) { if (this._data[i] == eventType) { return true; } } return false; }, isEmpty: function() { return this._data.length === 0; }, removeListener: function(eventType, eventTarget) { for (var i = 0; i < this._data.length; i += 2) { if (this._data[i] == eventType && eventTarget == this._data[i + 1]) { var oldLength = this._data.length; this._data.splice(i, 2); return; } } }, toString: function() { var out = ""; for (var i = 0; i < this._data.length; i += 2) { if (i > 0) { out += ", "; } out += this._data[i] + ":" + this._data[i + 1]; } return out; } }); Core.ResourceBundle = Core.extend({ $static: { getParentLanguageCode: function(languageCode) { if (languageCode.indexOf("-") == -1) { return null; } else { return languageCode.substring(0, languageCode.indexOf("-")); } } }, _sourceMaps: null, _generatedMaps: null, _defaultMap: null, $construct: function(defaultMap) { this._sourceMaps = {}; this._generatedMaps = {}; this._defaultMap = defaultMap; }, get: function(languageCode) { var map = languageCode ? this._generatedMaps[languageCode] : this._defaultMap; if (map) { return map; } map = {}; var x; var sourceMap = this._sourceMaps[languageCode]; if (sourceMap) { for (x in sourceMap) { map[x] = sourceMap[x]; } } var parentLanguageCode = Core.ResourceBundle.getParentLanguageCode(languageCode); if (parentLanguageCode) { sourceMap = this._sourceMaps[parentLanguageCode]; if (sourceMap) { for (x in sourceMap) { if (map[x] === undefined) { map[x] = sourceMap[x]; } } } } for (x in this._defaultMap) { if (map[x] === undefined) { map[x] = this._defaultMap[x]; } } this._generatedMaps[languageCode] = map; return map; }, set: function(languageCode, map) { this._generatedMaps = {}; this._sourceMaps[languageCode] = map; }, toString: function() { var out = "ResourceBundle: "; for (var x in this._sourceMaps) { out += " " + x; } return out; } }); Core.Web = { dragInProgress: false, init: function() { if (Core.Web.initialized) { return; } Core.Web.Env._init(); Core.Web.Measure._calculateExtentSizes(); Core.Web.Measure.Bounds._initMeasureContainer(); if (Core.Web.Env.QUIRK_CSS_POSITIONING_ONE_SIDE_ONLY) { Core.Web.VirtualPosition._init(); } if (Core.Web.Env.ENGINE_MSHTML) { Core.Web.DOM.addEventListener(document, "selectstart", Core.Web._selectStartListener, false); Core.Web.DOM.addEventListener(document, "dragstart", Core.Web._selectStartListener, false); } Core.Web.initialized = true; }, _selectStartListener: function(e) { e = e ? e : window.event; if (Core.Web.dragInProgress) { Core.Web.DOM.preventEventDefault(e); } } }; Core.Web.DOM = { _focusPendingElement: null, _focusRunnable: null, addEventListener: function(eventSource, eventType, eventListener, useCapture) { if (eventSource.addEventListener) { eventSource.addEventListener(eventType, eventListener, useCapture); } else if (eventSource.attachEvent) { eventSource.attachEvent("on" + eventType, eventListener); } }, createDocument: function(namespaceUri, qualifiedName) { if (document.implementation && document.implementation.createDocument) { var dom; if (Core.Web.Env.BROWSER_FIREFOX && Core.Web.Env.BROWSER_VERSION_MAJOR == 3 && Core.Web.Env.BROWSER_VERSION_MINOR === 0) { dom = new DOMParser().parseFromString("<" + qualifiedName + "/>", "application/xml"); } else { dom = document.implementation.createDocument(namespaceUri, qualifiedName, null); try { dom.charset = "utf-8"; } catch(e) {} } if (!dom.documentElement) { dom.appendChild(dom.createElement(qualifiedName)); } return dom; } else if (window.ActiveXObject) { var createdDocument = new ActiveXObject("Microsoft.XMLDOM"); var documentElement = createdDocument.createElement(qualifiedName); createdDocument.appendChild(documentElement); return createdDocument; } else { throw new Error("XML DOM creation not supported by browser environment."); } }, focusElement: function(element) { if (!this._focusRunnable) { this._focusRunnable = new (Core.extend(Core.Web.Scheduler.Runnable, { repeat: true, attempt: 0, timeInterval: 25, run: function() { element = Core.Web.DOM._focusPendingElement; Core.Debug.consoleWrite("Focus:" + element + "/" + element.id + "/" + Core.Web.DOM.isDisplayed(element)); var done = false; if (Core.Web.DOM.isDisplayed(element)) { done = true; try { element.focus(); } catch (ex) { } } done |= this.attempt > 25; ++this.attempt; if (done) { Core.Web.DOM._focusPendingElement = null; Core.Web.Scheduler.remove(this); } } }))(); } if (!(element && element.focus && Core.Web.DOM.isAncestorOf(document.body, element))) { Core.Web.DOM._focusPendingElement = null; Core.Web.Scheduler.remove(this._focusRunnable); return; } this._focusPendingElement = element; this._focusRunnable.attempt = 0; Core.Web.Scheduler.add(this._focusRunnable); }, getChildElementByTagName: function(parentElement, tagName) { var element = parentElement.firstChild; while (element) { if (element.nodeType == 1 && element.nodeName == tagName) { return element; } element = element.nextSibling; } return null; }, getChildElementsByTagName: function(parentElement, tagName) { var elements = []; var element = parentElement.firstChild; while (element) { if (element.nodeType == 1 && element.nodeName == tagName) { elements.push(element); } element = element.nextSibling; } return elements; }, getEventOffset: function(e) { if (typeof e.offsetX == "number") { return { x: e.offsetX, y: e.offsetY }; } else { var bounds = new Core.Web.Measure.Bounds(this.getEventTarget(e)); return { x: e.clientX - bounds.left, y: e.clientY - bounds.top }; } }, getEventTarget: function(e) { return e.target ? e.target : e.srcElement; }, getEventRelatedTarget: function(e) { return e.relatedTarget ? e.relatedTarget : e.toElement; }, isAncestorOf: function(ancestorNode, descendantNode) { var testNode = descendantNode; while (testNode != null) { if (testNode == ancestorNode) { return true; } testNode = testNode.parentNode; } return false; }, isDisplayed: function(node) { while (node != null) { if (node.nodeType == 1) { if (node.style) { if (node.style.visibility == "hidden") { return false; } if (node.style.display == "none") { return false; } } } if (node == document.body) { return true; } node = node.parentNode; } return false; }, preventEventDefault: function(e) { if (e.preventDefault) { e.preventDefault(); } else { e.returnValue = false; } }, removeAllChildren: function(node) { while (node.firstChild) { node.removeChild(node.firstChild); } }, removeEventListener: function(eventSource, eventType, eventListener, useCapture) { if (eventSource.removeEventListener) { eventSource.removeEventListener(eventType, eventListener, useCapture); } else if (eventSource.detachEvent) { eventSource.detachEvent("on" + eventType, eventListener); } }, removeNode: function(node) { var parentNode = node.parentNode; if (!parentNode) { return; } if (Core.Web.Env.QUIRK_PERFORMANCE_LARGE_DOM_REMOVE) { this._removeNodeRecursive(node); } else { parentNode.removeChild(node); } }, _removeNodeRecursive: function(node) { var childNode = node.firstChild; while (childNode) { var nextChildNode = childNode.nextSibling; this._removeNodeRecursive(childNode); childNode = nextChildNode; } node.parentNode.removeChild(node); }, stopEventPropagation: function(e) { if (e.stopPropagation) { e.stopPropagation(); } else { e.cancelBubble = true; } } }; Core.Web.Env = { ENGINE_PRESTO: null, ENGINE_WEBKIT: null, ENGINE_KHTML: null, ENGINE_MSHTML: null, ENGINE_GECKO: null, BROWSER_MOZILLA: null, BROWSER_OPERA: null, BROWSER_KONQUEROR: null, BROWSER_FIREFOX: null, BROWSER_INTERNET_EXPLORER: null, BROWSER_CHROME: null, BROWSER_VERSION_MAJOR: null, BROWSER_VERSION_MINOR: null, ENGINE_VERSION_MAJOR: null, ENGINE_VERSION_MINOR: null, DECEPTIVE_USER_AGENT: null, CSS_FLOAT: "cssFloat", MEASURE_OFFSET_EXCLUDES_BORDER: null, NOT_SUPPORTED_CSS_OPACITY: null, NOT_SUPPORTED_RELATIVE_COLUMN_WIDTHS: null, NOT_SUPPORTED_INPUT_SELECTION: null, NOT_SUPPORTED_RANGE: null, PROPRIETARY_EVENT_MOUSE_ENTER_LEAVE_SUPPORTED: null, PROPRIETARY_EVENT_SELECT_START_SUPPORTED: null, PROPRIETARY_IE_OPACITY_FILTER_REQUIRED: null, PROPRIETARY_IE_PNG_ALPHA_FILTER_REQUIRED: null, PROPRIETARY_IE_RANGE: null, QUIRK_KEY_CODE_IS_CHAR_CODE: null, QUIRK_KEY_PRESS_FIRED_FOR_SPECIAL_KEYS: null, QUIRK_KEY_DOWN_NOT_FIRED_FOR_SPECIAL_KEYS: null, QUIRK_CSS_BORDER_COLLAPSE_INSIDE: null, QUIRK_CSS_POSITIONING_ONE_SIDE_ONLY: null, QUIRK_DELAYED_FOCUS_REQUIRED: null, QUIRK_IE_BLANK_SCREEN: null, QUIRK_IE_HAS_LAYOUT: null, QUIRK_IE_SELECT_LIST_DOM_UPDATE: null, QUIRK_IE_SELECT_PERCENT_WIDTH: null, QUIRK_IE_SELECT_Z_INDEX: null, QUIRK_IE_SECURE_ITEMS: null, QUIRK_IE_TABLE_PERCENT_WIDTH_SCROLLBAR_ERROR: null, QUIRK_MEASURE_OFFSET_HIDDEN_BORDER: null, QUIRK_OPERA_CSS_POSITIONING: null, QUIRK_PERFORMANCE_LARGE_DOM_REMOVE: null, QUIRK_WEBKIT_DOM_TEXT_ESCAPE: null, QUIRK_TABLE_CELL_WIDTH_EXCLUDES_PADDING: null, QUIRK_UNLOADED_IMAGE_HAS_SIZE: null, QUIRK_SERIALIZE_XML_BEFORE_XML_HTTP_REQ: null, _ua: null, _uaAlpha: null, _init: function() { var browserVersion = null, engineVersion = null, engineId = false; this._ua = navigator.userAgent.toLowerCase(); this._uaAlpha = "/" + this._ua.replace(/[^a-z]+/g, "/") + "/"; if (this._testUAString("opera")) { this.BROWSER_OPERA = engineId = this.ENGINE_PRESTO = true; browserVersion = this._parseVersionInfo("opera/"); } else if (this._testUAString("chrome")) { this.BROWSER_CHROME = engineId = this.ENGINE_WEBKIT = true; browserVersion = this._parseVersionInfo("chrome/"); } else if (this._testUAString("safari")) { this.BROWSER_SAFARI = engineId = this.ENGINE_WEBKIT = true; browserVersion = this._parseVersionInfo("version/"); } else if (this._testUAString("konqueror")) { this.BROWSER_KONQUEROR = engineId = this.ENGINE_KHTML = true; browserVersion = this._parseVersionInfo("konqueror/"); } else if (this._testUAString("firefox")) { this.BROWSER_FIREFOX = this.BROWSER_MOZILLA = engineId = this.ENGINE_GECKO = true; browserVersion = this._parseVersionInfo("firefox/"); } else if (this._testUAString("msie")) { this.BROWSER_INTERNET_EXPLORER = engineId = this.ENGINE_MSHTML = true; engineVersion = browserVersion = this._parseVersionInfo("msie "); } if (!engineId) { if (this._testUAString("presto")) { this.ENGINE_PRESTO = true; } else if (this._testUAString("webkit")) { this.ENGINE_WEBKIT = true; } else if (this._testUAString("khtml")) { this.ENGINE_KHTML = true; } else if (this._testUAString("trident")) { this.ENGINE_MSHTML = true; } else if (this._testUAString("gecko")) { this.BROWSER_MOZILLA = this.ENGINE_GECKO = true; } } if (!engineVersion) { if (this.ENGINE_PRESTO) { engineVersion = this._parseVersionInfo("presto/"); } else if (this.ENGINE_WEBKIT) { engineVersion = this._parseVersionInfo("webkit/"); } else if (this.ENGINE_GECKO) { engineVersion = this._parseVersionInfo("rv:"); if (!browserVersion) { browserVersion = engineVersion; } } } if (browserVersion) { this.BROWSER_VERSION_MAJOR = browserVersion.major; this.BROWSER_VERSION_MINOR = browserVersion.minor; } if (engineVersion) { this.ENGINE_VERSION_MAJOR = engineVersion.major; this.ENGINE_VERSION_MINOR = engineVersion.minor; } this.DECEPTIVE_USER_AGENT = this.BROWSER_OPERA || this.BROWSER_SAFARI || this.BROWSER_CHROME || this.BROWSER_KONQUEROR; this.MEASURE_OFFSET_EXCLUDES_BORDER = false; if (this.BROWSER_INTERNET_EXPLORER) { this.CSS_FLOAT = "styleFloat"; this.QUIRK_KEY_CODE_IS_CHAR_CODE = true; this.QUIRK_IE_SECURE_ITEMS = true; this.NOT_SUPPORTED_RANGE = true; this.NOT_SUPPORTED_INPUT_SELECTION = true; this.PROPRIETARY_IE_RANGE = true; this.PROPRIETARY_EVENT_MOUSE_ENTER_LEAVE_SUPPORTED = true; this.PROPRIETARY_EVENT_SELECT_START_SUPPORTED = true; this.QUIRK_DELAYED_FOCUS_REQUIRED = true; this.QUIRK_UNLOADED_IMAGE_HAS_SIZE = true; this.MEASURE_OFFSET_EXCLUDES_BORDER = true; this.QUIRK_IE_HAS_LAYOUT = true; this.NOT_SUPPORTED_CSS_OPACITY = true; this.PROPRIETARY_IE_OPACITY_FILTER_REQUIRED = true; if (this.BROWSER_VERSION_MAJOR < 9) { this.QUIRK_IE_BLANK_SCREEN = true; } if (this.BROWSER_VERSION_MAJOR < 8) { this.QUIRK_TABLE_CELL_WIDTH_EXCLUDES_PADDING = true; this.NOT_SUPPORTED_RELATIVE_COLUMN_WIDTHS = true; this.QUIRK_CSS_BORDER_COLLAPSE_INSIDE = true; this.QUIRK_IE_TABLE_PERCENT_WIDTH_SCROLLBAR_ERROR = true; this.QUIRK_IE_SELECT_PERCENT_WIDTH = true; if (this.BROWSER_VERSION_MAJOR < 7) { this.QUIRK_IE_SELECT_LIST_DOM_UPDATE = true; this.QUIRK_CSS_POSITIONING_ONE_SIDE_ONLY = true; this.PROPRIETARY_IE_PNG_ALPHA_FILTER_REQUIRED = true; this.QUIRK_IE_SELECT_Z_INDEX = true; Core.Arrays.LargeMap.garbageCollectEnabled = true; } } if(this.BROWSER_VERSION_MAJOR == 9) { this.QUIRK_SERIALIZE_XML_BEFORE_XML_HTTP_REQ = true; } } else if (this.ENGINE_GECKO) { this.QUIRK_KEY_PRESS_FIRED_FOR_SPECIAL_KEYS = true; this.MEASURE_OFFSET_EXCLUDES_BORDER = true; this.QUIRK_MEASURE_OFFSET_HIDDEN_BORDER = true; if (this.BROWSER_FIREFOX) { if (this.BROWSER_VERSION_MAJOR < 2) { this.QUIRK_DELAYED_FOCUS_REQUIRED = true; } } else { this.QUIRK_PERFORMANCE_LARGE_DOM_REMOVE = true; this.QUIRK_DELAYED_FOCUS_REQUIRED = true; } } else if (this.ENGINE_PRESTO) { this.QUIRK_KEY_CODE_IS_CHAR_CODE = true; this.QUIRK_TABLE_CELL_WIDTH_EXCLUDES_PADDING = true; if (this.BROWSER_VERSION_MAJOR == 9 && this.BROWSER_VERSION_MINOR >= 50) { this.QUIRK_OPERA_CSS_POSITIONING = true; } this.NOT_SUPPORTED_RELATIVE_COLUMN_WIDTHS = true; } else if (this.ENGINE_WEBKIT) { this.MEASURE_OFFSET_EXCLUDES_BORDER = true; if (this.ENGINE_VERSION_MAJOR < 526 || (this.ENGINE_VERSION_MAJOR == 526 && this.ENGINE_VERSION_MINOR < 8)) { this.QUIRK_WEBKIT_DOM_TEXT_ESCAPE = true; } } }, _parseVersionInfo: function(searchString) { var version = { }; var ix1 = this._ua.indexOf(searchString); if (ix1 == -1) { return; } var ix2 = this._ua.indexOf(".", ix1); var ix3 = this._ua.length; if (ix2 == -1) { ix2 = this._ua.length; } else { for (var i = ix2 + 1; i < this._ua.length; i++) { var c = this._ua.charAt(i); if (isNaN(c)) { ix3 = i; break; } } } version.major = parseInt(this._ua.substring(ix1 + searchString.length, ix2), 10); if (ix2 == this._ua.length) { version.minor = 0; } else { version.minor = parseInt(this._ua.substring(ix2 + 1, ix3), 10); } return version; }, _testUAString: function(browser) { return this._uaAlpha.indexOf("/" + browser + "/") != -1; } }; Core.Web.Event = { Selection: { disable: function(element) { Core.Web.Event.add(element, "mousedown", Core.Web.Event.Selection._disposeEvent, false); if (Core.Web.Env.PROPRIETARY_EVENT_SELECT_START_SUPPORTED) { Core.Web.Event.add(element, "selectstart", Core.Web.Event.Selection._disposeEvent, false); } }, _disposeEvent: function(e) { Core.Web.DOM.preventEventDefault(e); }, enable: function(element) { Core.Web.Event.remove(element, "mousedown", Core.Web.Event.Selection._disposeEvent, false); if (Core.Web.Env.PROPRIETARY_EVENT_SELECT_START_SUPPORTED) { Core.Web.Event.remove(element, "selectstart", Core.Web.Event.Selection._disposeEvent, false); } } }, _nextId: 0, _listenerCount: 0, debugListenerCount: false, _capturingListenerMap: new Core.Arrays.LargeMap(), _bubblingListenerMap: new Core.Arrays.LargeMap(), add: function(element, eventType, eventTarget, capture) { if (!element.__eventProcessorId) { element.__eventProcessorId = ++Core.Web.Event._nextId; } var listenerList; if (element.__eventProcessorId == Core.Web.Event._lastId && capture == Core.Web.Event._lastCapture) { listenerList = Core.Web.Event._lastListenerList; } else { var listenerMap = capture ? Core.Web.Event._capturingListenerMap : Core.Web.Event._bubblingListenerMap; listenerList = listenerMap.map[element.__eventProcessorId]; if (!listenerList) { listenerList = new Core.ListenerList(); listenerMap.map[element.__eventProcessorId] = listenerList; } Core.Web.Event._lastId = element.__eventProcessorId; Core.Web.Event._lastCapture = capture; Core.Web.Event._lastListenerList = listenerList; } if (!listenerList.hasListeners(eventType)) { Core.Web.DOM.addEventListener(element, eventType, Core.Web.Event._processEvent, false); ++Core.Web.Event._listenerCount; } listenerList.addListener(eventType, eventTarget); }, _processEvent: function(e) { if (Core.Web.Event.debugListenerCount) { Core.Debug.consoleWrite("Core.Web.Event listener count: " + Core.Web.Event._listenerCount); } e = e ? e : window.event; if (!e.target && e.srcElement) { e.target = e.srcElement; } var elementAncestry = []; var targetElement = e.target; while (targetElement) { if (targetElement.__eventProcessorId) { elementAncestry.push(targetElement); } targetElement = targetElement.parentNode; } var listenerList, i, propagate = true; for (i = elementAncestry.length - 1; i >= 0; --i) { listenerList = Core.Web.Event._capturingListenerMap.map[elementAncestry[i].__eventProcessorId]; if (listenerList) { e.registeredTarget = elementAncestry[i]; if (!listenerList.fireEvent(e)) { propagate = false; break; } } } if (propagate) { for (i = 0; i < elementAncestry.length; ++i) { listenerList = Core.Web.Event._bubblingListenerMap.map[elementAncestry[i].__eventProcessorId]; if (listenerList) { e.registeredTarget = elementAncestry[i]; if (!listenerList.fireEvent(e)) { break; } } } } Core.Web.DOM.stopEventPropagation(e); }, remove: function(element, eventType, eventTarget, capture) { Core.Web.Event._lastId = null; if (!element.__eventProcessorId) { return; } var listenerMap = capture ? Core.Web.Event._capturingListenerMap : Core.Web.Event._bubblingListenerMap; var listenerList = listenerMap.map[element.__eventProcessorId]; if (listenerList) { listenerList.removeListener(eventType, eventTarget); if (listenerList.isEmpty()) { listenerMap.remove(element.__eventProcessorId); } if (!listenerList.hasListeners(eventType)) { Core.Web.DOM.removeEventListener(element, eventType, Core.Web.Event._processEvent, false); --Core.Web.Event._listenerCount; } } }, removeAll: function(element) { Core.Web.Event._lastId = null; if (!element.__eventProcessorId) { return; } Core.Web.Event._removeAllImpl(element, Core.Web.Event._capturingListenerMap); Core.Web.Event._removeAllImpl(element, Core.Web.Event._bubblingListenerMap); }, _removeAllImpl: function(element, listenerMap) { var listenerList = listenerMap.map[element.__eventProcessorId]; if (!listenerList) { return; } var types = listenerList.getListenerTypes(); for (var i = 0; i < types.length; ++i) { Core.Web.DOM.removeEventListener(element, types[i], Core.Web.Event._processEvent, false); --Core.Web.Event._listenerCount; } listenerMap.remove(element.__eventProcessorId); }, toString: function() { return "Capturing: " + Core.Web.Event._capturingListenerMap + "\n" + "Bubbling: " + Core.Web.Event._bubblingListenerMap; } }; Core.Web.HttpConnection = Core.extend({ _url: null, _contentType: null, _method: null, _messageObject: null, _listenerList: null, _disposed: false, _xmlHttpRequest: null, _requestHeaders: null, $construct: function(url, method, messageObject, contentType) { this._url = url; this._contentType = contentType; this._method = method; if (Core.Web.Env.QUIRK_WEBKIT_DOM_TEXT_ESCAPE && messageObject instanceof Document) { this._preprocessWebkitDOM(messageObject.documentElement); } this._messageObject = messageObject; this._listenerList = new Core.ListenerList(); }, _preprocessWebkitDOM: function(node) { if (node.nodeType == 3) { var value = node.data; value = value.replace(/&/g, "&"); value = value.replace(//g, ">"); node.data = value; } else { var child = node.firstChild; while (child) { this._preprocessWebkitDOM(child); child = child.nextSibling; } } }, addResponseListener: function(l) { this._listenerList.addListener("response", l); }, connect: function() { var usingActiveXObject = false; if (window.XMLHttpRequest) { this._xmlHttpRequest = new XMLHttpRequest(); } else if (window.ActiveXObject) { usingActiveXObject = true; this._xmlHttpRequest = new ActiveXObject("Microsoft.XMLHTTP"); } else { throw "Connect failed: Cannot create XMLHttpRequest."; } var instance = this; this._xmlHttpRequest.onreadystatechange = function() { if (!instance) { return; } try { instance._processReadyStateChange(); } finally { if (instance._disposed) { instance = null; } } }; this._xmlHttpRequest.open(this._method, this._url, true); if (this._requestHeaders && (usingActiveXObject || this._xmlHttpRequest.setRequestHeader)) { for(var h in this._requestHeaders) { try { this._xmlHttpRequest.setRequestHeader(h, this._requestHeaders[h]); } catch (e) { throw new Error("Failed to set header \"" + h + "\""); } } } if (this._contentType && (usingActiveXObject || this._xmlHttpRequest.setRequestHeader)) { this._xmlHttpRequest.setRequestHeader("Content-Type", this._contentType); } if (Core.Web.Env.QUIRK_SERIALIZE_XML_BEFORE_XML_HTTP_REQ) { this._xmlHttpRequest.send(this._messageObject ? new XMLSerializer().serializeToString(this._messageObject) : null); } else { this._xmlHttpRequest.send(this._messageObject ? this._messageObject : null); } }, dispose: function() { this._listenerList = null; this._messageObject = null; this._xmlHttpRequest = null; this._disposed = true; this._requestHeaders = null; }, getResponseHeader: function(header) { return this._xmlHttpRequest ? this._xmlHttpRequest.getResponseHeader(header) : null; }, getAllResponseHeaders: function() { return this._xmlHttpRequest ? this._xmlHttpRequest.getAllResponseHeaders() : null; }, getStatus: function() { return this._xmlHttpRequest ? this._xmlHttpRequest.status : null; }, getResponseText: function() { return this._xmlHttpRequest ? this._xmlHttpRequest.responseText : null; }, getResponseXml: function() { return this._xmlHttpRequest ? this._xmlHttpRequest.responseXML : null; }, _processReadyStateChange: function() { if (this._disposed) { return; } if (this._xmlHttpRequest.readyState == 4) { var responseEvent; try { var valid = !this._xmlHttpRequest.status || (this._xmlHttpRequest.status >= 200 && this._xmlHttpRequest.status <= 299); responseEvent = {type: "response", source: this, valid: valid}; } catch (ex) { responseEvent = {type: "response", source: this, valid: false, exception: ex}; } Core.Web.Scheduler.run(Core.method(this, function() { this._listenerList.fireEvent(responseEvent); this.dispose(); })); } }, removeResponseListener: function(l) { this._listenerList.removeListener("response", l); }, setRequestHeader: function(header, value) { if (!this._requestHeaders) { this._requestHeaders = { }; } this._requestHeaders[header] = value; } }); Core.Web.Image = { _EXPIRE_TIME: 5000, _Monitor: Core.extend({ _processImageLoadRef: null, _runnable: null, _listener: null, _images: null, _count: 0, _expiration: null, _imagesLoadedSinceUpdate: false, $construct: function(element, listener, interval) { this._listener = listener; this._processImageLoadRef = Core.method(this, this._processImageLoad); this._runnable = new Core.Web.Scheduler.MethodRunnable(Core.method(this, this._updateProgress), interval || 250, true); var nodeList = element.getElementsByTagName("img"); this._images = []; for (var i = 0; i < nodeList.length; ++i) { if (!nodeList[i].complete && (Core.Web.Env.QUIRK_UNLOADED_IMAGE_HAS_SIZE || (!nodeList[i].height && !nodeList[i].style.height))) { this._images.push(nodeList[i]); Core.Web.Event.add(nodeList[i], "load", this._processImageLoadRef, false); } } this._count = this._images.length; if (this._count > 0) { this._expiration = new Date().getTime() + Core.Web.Image._EXPIRE_TIME; Core.Web.Scheduler.add(this._runnable); } }, _processImageLoad: function(e) { e = e ? e : window.event; var image = Core.Web.DOM.getEventTarget(e); this._imagesLoadedSinceUpdate = true; Core.Web.Event.remove(image, "load", this._processImageLoadRef, false); Core.Arrays.remove(this._images, image); --this._count; if (this._count === 0) { this._stop(); this._notify(); } }, _notify: function() { Core.Web.Scheduler.run(Core.method(this, function() { this._listener({ source: this, type: "imageLoad", expired: this._expired, complete: this._expired || this._count === 0 }); })); }, _stop: function() { Core.Web.Scheduler.remove(this._runnable); this._runnable = null; for (var i = 0; i < this._images.length; ++i) { Core.Web.Event.remove(this._images[i], "load", this._processImageLoadRef, false); } }, _updateProgress: function() { if (new Date().getTime() > this._expiration) { this._expired = true; this._stop(); this._notify(); return; } if (this._imagesLoadedSinceUpdate) { this._imagesLoadedSinceUpdate = false; this._notify(); } } }), monitor: function(element, l, interval) { var monitor = new Core.Web.Image._Monitor(element, l, interval); return monitor._count > 0; } }; Core.Web.Key = { _KEY_TABLES: { GECKO: { 59: 186, 61: 187, 109: 189 }, MAC_GECKO: { }, PRESTO: { 59: 186, 61: 187, 44: 188, 45: 189, 46: 190, 47: 191, 96: 192, 91: 219, 92: 220, 93: 221, 39: 222 }, WEBKIT: { } }, _keyTable: null, _loadKeyTable: function() { if (Core.Web.Env.ENGINE_GECKO) { this._keyTable = this._KEY_TABLES.GECKO; } else if(Core.Web.Env.ENGINE_PRESTO) { this._keyTable = this._KEY_TABLES.PRESTO; } else { this._keyTable = { }; } }, translateKeyCode: function(keyCode) { if (!this._keyTable) { this._loadKeyTable(); } return this._keyTable[keyCode] || keyCode; } }; Core.Web.Library = { _loadedLibraries: { }, evalLine: null, Group: Core.extend({ _listenerList: null, _libraries: null, _loadedCount: 0, _totalCount: 0, $construct: function() { this._listenerList = new Core.ListenerList(); this._libraries = []; }, add: function(libraryUrl) { if (Core.Web.Library._loadedLibraries[libraryUrl]) { return; } var libraryItem = new Core.Web.Library._Item(this, libraryUrl); this._libraries.push(libraryItem); }, addLoadListener: function(l) { this._listenerList.addListener("load", l); }, hasNewLibraries: function() { return this._libraries.length > 0; }, _install: function() { for (var i = 0; i < this._libraries.length; ++i) { try { this._libraries[i]._install(); } catch (ex) { var e = { type: "load", source: this, success: false, ex: ex, url: this._libraries[i]._url, cancel: false }; try { this._listenerList.fireEvent(e); } finally { if (!e.cancel) { throw new Error("Exception installing library \"" + this._libraries[i]._url + "\"; " + ex); } } } } this._listenerList.fireEvent({type: "load", source: this, success: true}); }, _notifyRetrieved: function() { ++this._loadedCount; if (this._loadedCount == this._totalCount) { this._install(); } }, load: function() { this._totalCount = this._libraries.length; for (var i = 0; i < this._libraries.length; ++i) { this._libraries[i]._retrieve(); } }, removeLoadListener: function(l) { this._listenerList.removeListener("load", l); } }), _Item: Core.extend({ _url: null, _group: null, _content: null, $construct: function(group, url) { this._url = url; this._group = group; }, _retrieveListener: function(e) { if (!e.valid) { throw new Error("Invalid HTTP response retrieving library \"" + this._url + "\", received status: " + e.source.getStatus()); } this._content = e.source.getResponseText(); this._group._notifyRetrieved(); }, _install: function() { if (Core.Web.Library._loadedLibraries[this._url]) { return; } Core.Web.Library._loadedLibraries[this._url] = true; if (this._content == null) { throw new Error("Attempt to install library when no content has been loaded."); } Core.Web.Library.evalLine = new Error().lineNumber + 1; eval(this._content); }, _retrieve: function() { var conn = new Core.Web.HttpConnection(this._url, "GET"); conn.addResponseListener(Core.method(this, this._retrieveListener)); conn.connect(); } }), exec: function(requiredLibraries, f) { var group = null; for (var i = 0; i < requiredLibraries.length; ++i) { if (!Core.Web.Library._loadedLibraries[requiredLibraries[i]]) { if (group == null) { group = new Core.Web.Library.Group(); } group.add(requiredLibraries[i]); } } if (group == null) { Core.Web.Scheduler.run(f); return; } group.addLoadListener(f); group.load(); } }; Core.Web.Measure = { _scrollElements: ["div", "body"], _hInch: 96, _vInch: 96, _hEx: 7, _vEx: 7, _hEm: 13.3333, _vEm: 13.3333, SCROLL_WIDTH: 17, SCROLL_HEIGHT: 17, _PARSER: /^(-?\d+(?:\.\d+)?)(.+)?$/, extentToPixels: function(extent, horizontal) { var parts = this._PARSER.exec(extent); if (!parts) { throw new Error("Invalid Extent: " + extent); } var value = parseFloat(parts[1]); var units = parts[2] ? parts[2] : "px"; if (!units || units == "px") { return value; } var dpi = horizontal ? Core.Web.Measure._hInch : Core.Web.Measure._vInch; switch (units) { case "%": return null; case "in": return value * (horizontal ? Core.Web.Measure._hInch : Core.Web.Measure._vInch); case "cm": return value * (horizontal ? Core.Web.Measure._hInch : Core.Web.Measure._vInch) / 2.54; case "mm": return value * (horizontal ? Core.Web.Measure._hInch : Core.Web.Measure._vInch) / 25.4; case "pt": return value * (horizontal ? Core.Web.Measure._hInch : Core.Web.Measure._vInch) / 72; case "pc": return value * (horizontal ? Core.Web.Measure._hInch : Core.Web.Measure._vInch) / 6; case "em": return value * (horizontal ? Core.Web.Measure._hEm : Core.Web.Measure._vEm); case "ex": return value * (horizontal ? Core.Web.Measure._hEx : Core.Web.Measure._vEx); } }, _calculateExtentSizes: function() { var containerElement = document.getElementsByTagName("body")[0]; var inchDiv4 = document.createElement("div"); inchDiv4.style.width = "4in"; inchDiv4.style.height = "4in"; containerElement.appendChild(inchDiv4); Core.Web.Measure._hInch = inchDiv4.offsetWidth / 4; Core.Web.Measure._vInch = inchDiv4.offsetHeight / 4; containerElement.removeChild(inchDiv4); var emDiv24 = document.createElement("div"); emDiv24.style.width = "24em"; emDiv24.style.height = "24em"; containerElement.appendChild(emDiv24); Core.Web.Measure._hEm = emDiv24.offsetWidth / 24; Core.Web.Measure._vEm = emDiv24.offsetHeight / 24; containerElement.removeChild(emDiv24); var exDiv24 = document.createElement("div"); exDiv24.style.width = "24ex"; exDiv24.style.height = "24ex"; containerElement.appendChild(exDiv24); Core.Web.Measure._hEx = exDiv24.offsetWidth / 24; Core.Web.Measure._vEx = exDiv24.offsetHeight / 24; containerElement.removeChild(exDiv24); var scrollDiv = document.createElement("div"); scrollDiv.style.cssText = "width:500px;height:100px;overflow:auto;"; var largeDiv = document.createElement("div"); largeDiv.style.cssText = "width:100px;height:200px;"; scrollDiv.appendChild(largeDiv); var testDiv = document.createElement("div"); testDiv.style.cssText = "width:100%;height:10px;"; scrollDiv.appendChild(testDiv); containerElement.appendChild(scrollDiv); var measuredWidth = 500 - testDiv.offsetWidth; if (measuredWidth) { Core.Web.Measure.SCROLL_WIDTH = Core.Web.Measure.SCROLL_HEIGHT = measuredWidth; } containerElement.removeChild(scrollDiv); }, _getScrollOffset: function(element) { var valueT = 0, valueL = 0; do { if (element.scrollLeft || element.scrollTop) { valueT += element.scrollTop || 0; valueL += element.scrollLeft || 0; } element = element.offsetParent; } while (element); return { left: valueL, top: valueT }; }, _getCumulativeOffset: function(element) { var valueT = 0, valueL = 0, init = true; do { valueT += element.offsetTop || 0; valueL += element.offsetLeft || 0; if (!init && Core.Web.Env.MEASURE_OFFSET_EXCLUDES_BORDER) { if (element.style.borderLeftWidth && element.style.borderLeftStyle != "none") { var borderLeft = Core.Web.Measure.extentToPixels(element.style.borderLeftWidth, true); valueL += borderLeft; if (Core.Web.Env.QUIRK_MEASURE_OFFSET_HIDDEN_BORDER && element.style.overflow == "hidden") { valueL += borderLeft; } } if (element.style.borderTopWidth && element.style.borderTopStyle != "none") { var borderTop = Core.Web.Measure.extentToPixels(element.style.borderTopWidth, false); valueT += borderTop; if (Core.Web.Env.QUIRK_MEASURE_OFFSET_HIDDEN_BORDER && element.style.overflow == "hidden") { valueT += borderTop; } } } init = false; element = element.offsetParent; } while (element); return { left: valueL, top: valueT }; }, Bounds: Core.extend({ $static: { FLAG_MEASURE_DIMENSION: 0x1, FLAG_MEASURE_POSITION: 0x2, _initMeasureContainer: function() { this._offscreenDiv = document.createElement("div"); this._offscreenDiv.style.cssText = "position: absolute; top: -1300px; left: -1700px; width: 1600px; height: 1200px;"; document.body.appendChild(this._offscreenDiv); } }, width: null, height: null, top: null, left: null, $construct: function(element, constraints) { var flags = (constraints && constraints.flags) || (Core.Web.Measure.Bounds.FLAG_MEASURE_DIMENSION | Core.Web.Measure.Bounds.FLAG_MEASURE_POSITION); if (element === document.body) { return { x: 0, y: 0, height: window.innerHeight || document.documentElement.clientHeight, width: window.innerWidth || document.documentElement.clientWidth }; } var testElement = element; while (testElement && testElement != document) { testElement = testElement.parentNode; } var rendered = testElement == document; var parentNode, nextSibling; if (flags & Core.Web.Measure.Bounds.FLAG_MEASURE_DIMENSION) { if (!rendered) { parentNode = element.parentNode; nextSibling = element.nextSibling; if (parentNode) { parentNode.removeChild(element); } if (constraints) { if (constraints.width) { Core.Web.Measure.Bounds._offscreenDiv.width = constraints.width; } if (constraints.height) { Core.Web.Measure.Bounds._offscreenDiv.height = constraints.height; } } Core.Web.Measure.Bounds._offscreenDiv.appendChild(element); if (constraints) { Core.Web.Measure.Bounds._offscreenDiv.width = "1600px"; Core.Web.Measure.Bounds._offscreenDiv.height = "1200px"; } } this.width = element.offsetWidth; this.height = element.offsetHeight; if (!rendered) { Core.Web.Measure.Bounds._offscreenDiv.removeChild(element); if (parentNode) { parentNode.insertBefore(element, nextSibling); } } } if (rendered && (flags & Core.Web.Measure.Bounds.FLAG_MEASURE_POSITION)) { var cumulativeOffset = Core.Web.Measure._getCumulativeOffset(element); var scrollOffset = Core.Web.Measure._getScrollOffset(element); this.top = cumulativeOffset.top - scrollOffset.top; this.left = cumulativeOffset.left - scrollOffset.left; } }, toString: function() { return (this.left != null ? (this.left + "," + this.top + " : ") : "") + (this.width != null ? ("[" + this.width + "x" + this.height + "]") : ""); } }) }; Core.Web.Scheduler = { _runnables: [], _threadHandle: null, _nextExecution: null, add: function(runnable) { Core.Arrays.remove(Core.Web.Scheduler._runnables, runnable); runnable._nextExecution = new Date().getTime() + (runnable.timeInterval ? runnable.timeInterval : 0); Core.Web.Scheduler._runnables.push(runnable); Core.Web.Scheduler._setTimeout(runnable._nextExecution); }, _execute: function() { Core.Web.Scheduler._threadHandle = null; var currentTime = new Date().getTime(); var nextInterval = Number.MAX_VALUE; var i, runnable; for (i = 0; i < Core.Web.Scheduler._runnables.length; ++i) { runnable = Core.Web.Scheduler._runnables[i]; if (runnable && runnable._nextExecution && runnable._nextExecution <= currentTime) { runnable._nextExecution = null; try { runnable.run(); } catch (ex) { throw(ex); } } } var newRunnables = []; for (i = 0; i < Core.Web.Scheduler._runnables.length; ++i) { runnable = Core.Web.Scheduler._runnables[i]; if (runnable == null) { continue; } if (runnable._nextExecution) { newRunnables.push(runnable); var interval = runnable._nextExecution - currentTime; if (interval < nextInterval) { nextInterval = interval; } continue; } if (runnable.timeInterval != null && runnable.repeat) { runnable._nextExecution = currentTime + runnable.timeInterval; newRunnables.push(runnable); if (runnable.timeInterval < nextInterval) { nextInterval = runnable.timeInterval; } } } Core.Web.Scheduler._runnables = newRunnables; if (nextInterval < Number.MAX_VALUE) { Core.Web.Scheduler._setTimeout(currentTime + nextInterval); } }, remove: function(runnable) { var index = Core.Arrays.indexOf(Core.Web.Scheduler._runnables, runnable); Core.Web.Scheduler._runnables[index] = null; }, run: function(f, timeInterval, repeat) { var runnable = new Core.Web.Scheduler.MethodRunnable(f, timeInterval, repeat); Core.Web.Scheduler.add(runnable); return runnable; }, _setTimeout: function(nextExecution) { if (Core.Web.Scheduler._threadHandle != null && Core.Web.Scheduler._nextExecution < nextExecution) { return; } if (Core.Web.Scheduler._threadHandle != null) { window.clearTimeout(Core.Web.Scheduler._threadHandle); } var currentTime = new Date().getTime(); Core.Web.Scheduler._nextExecution = nextExecution; var timeout = nextExecution - currentTime > 0 ? nextExecution - currentTime : 0; Core.Web.Scheduler._threadHandle = window.setTimeout(Core.Web.Scheduler._execute, timeout); }, update: function(runnable) { if (Core.Arrays.indexOf(Core.Web.Scheduler._runnables, runnable) == -1) { return; } var currentTime = new Date().getTime(); var timeInterval = runnable.timeInterval ? runnable.timeInterval : 0; runnable._nextExecution = currentTime + timeInterval; Core.Web.Scheduler._setTimeout(runnable._nextExecution); } }; Core.Web.Scheduler.Runnable = Core.extend({ _nextExecution: null, $virtual: { timeInterval: null, repeat: false }, $abstract: { run: function() { } } }); Core.Web.Scheduler.MethodRunnable = Core.extend(Core.Web.Scheduler.Runnable, { f: null, $construct: function(f, timeInterval, repeat) { if (!timeInterval && repeat) { throw new Error("Cannot create repeating runnable without time delay:" + f); } this.f = f; this.timeInterval = timeInterval; this.repeat = !!repeat; }, $virtual: { run: function() { this.f(); } } }); Core.Web.VirtualPosition = { _OFFSETS_VERTICAL: ["paddingTop", "paddingBottom", "marginTop", "marginBottom", "borderTopWidth", "borderBottomWidth"], _OFFSETS_HORIZONTAL: ["paddingLeft", "paddingRight", "marginLeft", "marginRight", "borderLeftWidth", "borderRightWidth"], enabled: false, _calculateOffsets: function(offsetNames, style) { var offsets = 0; for (var i = 0; i < offsetNames.length; ++i) { var value = style[offsetNames[i]]; if (value) { if (value.toString().indexOf("px") == -1) { return -1; } offsets += parseInt(value, 10); } } return offsets; }, _init: function() { this.enabled = true; }, redraw: function(element) { if (!this.enabled) { return; } if (!element || !element.parentNode) { return; } var offsets; if (this._verifyPixelValue(element.style.top) && this._verifyPixelValue(element.style.bottom)) { var offsetHeight = element.parentNode.offsetHeight; if (!isNaN(offsetHeight)) { offsets = this._calculateOffsets(this._OFFSETS_VERTICAL, element.style); if (offsets != -1) { var calculatedHeight = offsetHeight - parseInt(element.style.top, 10) - parseInt(element.style.bottom, 10) - offsets; if (calculatedHeight <= 0) { element.style.height = 0; } else { if (element.style.height != calculatedHeight + "px") { element.style.height = calculatedHeight + "px"; } } } } } if (this._verifyPixelValue(element.style.left) && this._verifyPixelValue(element.style.right)) { var offsetWidth = element.parentNode.offsetWidth; if (!isNaN(offsetWidth)) { offsets = this._calculateOffsets(this._OFFSETS_HORIZONTAL, element.style); if (offsets != -1) { var calculatedWidth = offsetWidth - parseInt(element.style.left, 10) - parseInt(element.style.right, 10) - offsets; if (calculatedWidth <= 0) { element.style.width = 0; } else { if (element.style.width != calculatedWidth + "px") { element.style.width = calculatedWidth + "px"; } } } } } }, _verifyPixelValue: function(value) { if (value == null || value === "") { return false; } var valueString = value.toString(); return valueString == "0" || valueString.indexOf("px") != -1; } }; Echo = { }; Echo.Application = Core.extend({ $static: { _nextUid: 1, generateUid: function() { return this._nextUid++; } }, $abstract: true, $virtual: { init: function() { }, dispose: function() { }, isActive: function() { return true; } }, client: null, _idToComponentMap: null, _listenerList: null, _locale: null, _modalComponents: null, _styleSheet: null, _focusedComponent: null, rootComponent: null, updateManager: null, focusManager: null, $construct: function() { this._idToComponentMap = new Core.Arrays.LargeMap(); this._listenerList = new Core.ListenerList(); this.rootComponent = new Echo.Component(); this.rootComponent.componentType = "Root"; this.rootComponent.register(this); this._modalComponents = []; this.updateManager = new Echo.Update.Manager(this); this.focusManager = new Echo.FocusManager(this); }, addListener: function(eventType, eventTarget) { this._listenerList.addListener(eventType, eventTarget); }, doDispose: function() { this.updateManager.dispose(); this.dispose(); }, doInit: function() { this.init(); }, _findModalContextRoot: function(searchComponent) { searchComponent = searchComponent ? searchComponent : this.rootComponent; for (var i = searchComponent.children.length - 1; i >= 0; --i) { var foundComponent = this._findModalContextRoot(searchComponent.children[i]); if (foundComponent) { return foundComponent; } } if (searchComponent.modalSupport && searchComponent.get("modal")) { return searchComponent; } return null; }, fireEvent: function(event) { if (this._listenerList == null) { return; } this._listenerList.fireEvent(event); }, focusNext: function(reverse) { var focusedComponent = this.focusManager.find(null, reverse); if (focusedComponent != null) { this.setFocusedComponent(focusedComponent); } }, getComponentByRenderId: function(renderId) { return this._idToComponentMap.map[renderId]; }, getFocusedComponent: function() { return this._focusedComponent; }, getLayoutDirection: function() { return this._layoutDirection ? this._layoutDirection : Echo.LayoutDirection.LTR; }, getLocale: function() { return this._locale; }, getModalContextRoot: function() { if (this._modalComponents.length === 0) { return null; } else if (this._modalComponents.length == 1) { return this._modalComponents[0]; } return this._findModalContextRoot(); }, getStyleSheet: function() { return this._styleSheet; }, notifyComponentUpdate: function(parent, propertyName, oldValue, newValue, rendered) { if (parent.modalSupport && propertyName == "modal") { this._setModal(parent, newValue); } if (this._listenerList.hasListeners("componentUpdate")) { this._listenerList.fireEvent({type: "componentUpdate", parent: parent, propertyName: propertyName, oldValue: oldValue, newValue: newValue}); } if (!rendered) { this.updateManager._processComponentUpdate(parent, propertyName, oldValue, newValue); } }, _registerComponent: function(component) { if (this._idToComponentMap.map[component.renderId]) { throw new Error("Component already exists with id: " + component.renderId); } this._idToComponentMap.map[component.renderId] = component; if (component.modalSupport && component.get("modal")) { this._setModal(component, true); } }, removeListener: function(eventType, eventTarget) { this._listenerList.removeListener(eventType, eventTarget); }, setFocusedComponent: function(newValue) { var oldValue = this._focusedComponent; while (newValue != null && !newValue.focusable) { newValue = newValue.parent; } if (this._modalComponents.length > 0) { var modalContextRoot = this.getModalContextRoot(); if (!modalContextRoot.isAncestorOf(newValue)) { return; } } if (this._focusedComponent == newValue) { return; } this._focusedComponent = newValue; this._listenerList.fireEvent({type: "focus", source: this, oldValue: oldValue, newValue: newValue }); }, setLayoutDirection: function(newValue) { this._layoutDirection = newValue; this.updateManager._processFullRefresh(); }, setLocale: function(newValue) { this._locale = newValue; this.updateManager._processFullRefresh(); }, _setModal: function(component, modal) { Core.Arrays.remove(this._modalComponents, component); if (modal) { this._modalComponents.push(component); } if (this._modalComponents.length > 0 && this._focusedComponent) { var modalContextRoot = this.getModalContextRoot(); if (!modalContextRoot.isAncestorOf(this._focusedComponent)) { if (modalContextRoot.focusable) { this.setFocusedComponent(modalContextRoot); } else { this.setFocusedComponent(this.focusManager.findInParent(modalContextRoot, false)); } } } this.fireEvent({ source: this, type: "modal", modal: this._modalComponents.length > 0 }); }, setStyleSheet: function(newValue) { this._styleSheet = newValue; this.updateManager._processFullRefresh(); }, _unregisterComponent: function(component) { this._idToComponentMap.remove(component.renderId); if (component.modalSupport) { this._setModal(component, false); } } }); Echo.ComponentFactory = { _typeToConstructorMap: {}, newInstance: function(typeName, renderId) { var typeConstructor = this._typeToConstructorMap[typeName]; if (!typeConstructor) { throw new Error("Type not registered with ComponentFactory: " + typeName); } var component = new typeConstructor(); component.renderId = renderId; return component; }, getConstructor: function(typeName) { return this._typeToConstructorMap[typeName]; }, getSuperType: function(typeName) { var typeConstructor = this._typeToConstructorMap[typeName]; if (!typeConstructor) { return "Component"; } if (typeConstructor.$super) { return typeConstructor.$super.prototype.componentType; } else { return null; } }, registerType: function(typeName, typeConstructor) { if (this._typeToConstructorMap[typeName]) { throw new Error("Type already registered: " + typeName); } this._typeToConstructorMap[typeName] = typeConstructor; } }; Echo.Component = Core.extend({ $static: { _nextRenderId: 0 }, $load: function() { Echo.ComponentFactory.registerType("Component", this); }, $abstract: true, $virtual: { componentType: "Component", focusable: false, getFocusOrder: null, pane: false }, _layoutDirection: null, _locale: null, renderId: null, parent: null, application: null, _listenerList: null, _style: null, _styleName: null, _enabled: true, children: null, focusNextId: null, focusPreviousId: null, _localStyle: null, $construct: function(properties) { this.children = []; this._localStyle = { }; if (properties) { for (var name in properties) { switch (name) { case "style": this._style = properties.style; break; case "styleName": this._styleName = properties.styleName; break; case "renderId": this.renderId = properties.renderId; break; case "children": for (var i = 0; i < properties.children.length; ++i) { this.add(properties.children[i]); } break; case "events": for (var eventType in properties.events) { this.addListener(eventType, properties.events[eventType]); } break; default: this._localStyle[name] = properties[name]; } } } }, add: function(component, index) { if (!(component instanceof Echo.Component)) { throw new Error("Cannot add child: specified component object is not derived from Echo.Component. " + "Parent: " + this + ", Child: " + component); } if (!component.componentType) { throw new Error("Cannot add child: specified component object does not have a componentType property. " + "Parent: " + this + ", Child: " + component); } if (component.parent) { component.parent.remove(component); } component.parent = this; if (index == null || index == this.children.length) { this.children.push(component); } else { this.children.splice(index, 0, component); } if (this.application) { component.register(this.application); this.application.notifyComponentUpdate(this, "children", null, component); } if (component._listenerList && component._listenerList.hasListeners("parent")) { component._listenerList.fireEvent({type: "parent", source: component, oldValue: null, newValue: this}); } if (this._listenerList && this._listenerList.hasListeners("children")) { this._listenerList.fireEvent({type: "children", source: this, add: component, index: index}); } }, addListener: function(eventType, eventTarget) { if (this._listenerList == null) { this._listenerList = new Core.ListenerList(); } this._listenerList.addListener(eventType, eventTarget); if (this.application) { this.application.notifyComponentUpdate(this, "listeners", null, eventType); } }, fireEvent: function(event) { if (this._listenerList == null) { return; } this._listenerList.fireEvent(event); }, get: function(name) { return this._localStyle[name]; }, getComponent: function(index) { return this.children[index]; }, getComponentCount: function() { return this.children.length; }, getIndex: function(name, index) { var valueArray = this._localStyle[name]; return valueArray ? valueArray[index] : null; }, getLayoutDirection: function() { return this._layoutDirection; }, getLocale: function() { return this._locale; }, getLocalStyleData: function() { return this._localStyle; }, getRenderLayoutDirection: function() { var component = this; while (component) { if (component._layoutDirection) { return component._layoutDirection; } component = component.parent; } if (this.application) { return this.application.getLayoutDirection(); } return null; }, getRenderLocale: function() { var component = this; while (component) { if (component._locale) { return component._locale; } component = component.parent; } if (this.application) { return this.application.getLocale(); } return null; }, getStyle: function() { return this._style; }, getStyleName: function() { return this._styleName; }, indexOf: function(component) { for (var i = 0; i < this.children.length; ++i) { if (this.children[i] == component) { return i; } } return -1; }, isActive: function() { if (!this.isRenderEnabled()) { return false; } if (!this.application || !this.application.isActive()) { return false; } var modalContextRoot = this.application.getModalContextRoot(); if (modalContextRoot != null && !modalContextRoot.isAncestorOf(this)) { return false; } return true; }, isAncestorOf: function(c) { while (c != null && c != this) { c = c.parent; } return c == this; }, isEnabled: function() { return this._enabled; }, isRenderEnabled: function() { var component = this; while (component != null) { if (!component._enabled) { return false; } component = component.parent; } return true; }, register: function(application) { if (application && this.application) { throw new Error("Attempt to re-register or change registered application of component."); } var i; if (!application) { if (this.children != null) { for (i = 0; i < this.children.length; ++i) { this.children[i].register(false); } } this.application._unregisterComponent(this); if (this.application._focusedComponent == this) { this.application.setFocusedComponent(this.parent); } if (this._listenerList != null && this._listenerList.hasListeners("dispose")) { this._listenerList.fireEvent({ type: "dispose", source: this }); } } this.application = application; if (application) { if (this.renderId == null) { this.renderId = "CL." + (++Echo.Component._nextRenderId); } this.application._registerComponent(this); if (this._listenerList != null && this._listenerList.hasListeners("init")) { this._listenerList.fireEvent({ type: "init", source: this }); } if (this.children != null) { for (i = 0; i < this.children.length; ++i) { this.children[i].register(application); } } } }, render: function(name, defaultValue) { var value = this._localStyle[name]; if (value == null) { if (this._style != null) { value = this._style[name]; } if (value == null && this.application && this.application._styleSheet) { var style = this.application._styleSheet.getRenderStyle( this._styleName != null ? this._styleName : "", this.componentType); if (style) { value = style[name]; } } } return value == null ? defaultValue : value; }, renderIndex: function(name, index, defaultValue) { var valueArray = this._localStyle[name]; var value = valueArray ? valueArray[index] : null; if (value == null) { if (this._style != null) { valueArray = this._style[name]; value = valueArray ? valueArray[index] : null; } if (value == null && this._styleName && this.application && this.application._styleSheet) { var style = this.application._styleSheet.getRenderStyle( this._styleName != null ? this._styleName : "", this.componentType); if (style) { valueArray = style[name]; value = valueArray ? valueArray[index] : null; } } } return value == null ? defaultValue : value; }, remove: function(componentOrIndex) { var component; var index; if (typeof componentOrIndex == "number") { index = componentOrIndex; component = this.children[index]; if (!component) { throw new Error("Component.remove(): index out of bounds: " + index + ", parent: " + this); } } else { component = componentOrIndex; index = this.indexOf(component); if (index == -1) { return; } } if (this.application) { component.register(null); } this.children.splice(index, 1); component.parent = null; if (this.application) { this.application.notifyComponentUpdate(this, "children", component, null); } if (component._listenerList && component._listenerList.hasListeners("parent")) { component._listenerList.fireEvent({type: "parent", source: component, oldValue: this, newValue: null}); } if (this._listenerList && this._listenerList.hasListeners("children")) { this._listenerList.fireEvent({type: "children", source: this, remove: component, index: index}); } }, removeAll: function() { while (this.children.length > 0) { this.remove(this.children.length - 1); } }, removeListener: function(eventType, eventTarget) { if (this._listenerList == null) { return; } this._listenerList.removeListener(eventType, eventTarget); if (this.application) { this.application.notifyComponentUpdate(this, "listeners", eventType, null); } }, set: function(name, newValue, rendered) { var oldValue = this._localStyle[name]; if (oldValue === newValue) { return; } this._localStyle[name] = newValue; if (this._listenerList && this._listenerList.hasListeners("property")) { this._listenerList.fireEvent({type: "property", source: this, propertyName: name, oldValue: oldValue, newValue: newValue}); } if (this.application) { this.application.notifyComponentUpdate(this, name, oldValue, newValue, rendered); } }, setEnabled: function(newValue) { var oldValue = this._enabled; this._enabled = newValue; if (this.application) { this.application.notifyComponentUpdate(this, "enabled", oldValue, newValue); } }, setIndex: function(name, index, newValue, rendered) { var valueArray = this._localStyle[name]; var oldValue = null; if (valueArray) { oldValue = valueArray[index]; if (oldValue === newValue) { return; } } else { valueArray = []; this._localStyle[name] = valueArray; } valueArray[index] = newValue; if (this.application) { this.application.notifyComponentUpdate(this, name, oldValue, newValue, rendered); } if (this._listenerList && this._listenerList.hasListeners("property")) { this._listenerList.fireEvent({type: "property", source: this, propertyName: name, index: index, oldValue: oldValue, newValue: newValue}); } }, setLayoutDirection: function(newValue) { var oldValue = this._layoutDirection; this._layoutDirection = newValue; if (this.application) { this.application.notifyComponentUpdate(this, "layoutDirection", oldValue, newValue); } }, setLocale: function(newValue) { var oldValue = this._locale; this._locale = newValue; if (this.application) { this.application.notifyComponentUpdate(this, "locale", oldValue, newValue); } }, setStyle: function(newValue) { var oldValue = this._style; this._style = newValue; if (this.application) { this.application.notifyComponentUpdate(this, "style", oldValue, newValue); } }, setStyleName: function(newValue) { var oldValue = this._styleName; this._styleName = newValue; if (this.application) { this.application.notifyComponentUpdate(this, "styleName", oldValue, newValue); } }, toString: function(longFormat) { var out = this.renderId + "/" + this.componentType; if (longFormat) { out += "\n"; var componentCount = this.getComponentCount(); out += this.renderId + "/properties:" + this._localStyle + "\n"; for (var i = 0; i < componentCount; ++i) { var component = this.getComponent(i); out += this.renderId + "/child:" + component.renderId + "\n"; out += component.toString(true); } } return out; } }); Echo.FocusManager = Core.extend({ _application: null, $construct: function(application) { this._application = application; }, find: function(component, reverse) { if (!component) { component = this._application.getFocusedComponent(); if (!component) { component = this._application.rootComponent; } } var setComponentId = reverse ? component.focusPreviousId : component.focusNextId; if (setComponentId) { var setComponent = this._application.getComponentByRenderId(setComponentId); if (setComponent && setComponent.isActive() && setComponent.focusable) { return setComponent; } } var originComponent = component; var visitedComponents = { }; var lastComponent = null; while (true) { var nextComponent = null; if ((reverse && component == originComponent) || (lastComponent && lastComponent.parent == component)) { } else { var componentCount = component.getComponentCount(); if (componentCount > 0) { var focusOrder = this._getFocusOrder(component); if (focusOrder) { nextComponent = component.getComponent(focusOrder[reverse ? componentCount - 1 : 0]); } else { nextComponent = component.getComponent(reverse ? componentCount - 1 : 0); } if (visitedComponents[nextComponent.renderId]) { nextComponent = null; } } } if (nextComponent == null) { if (component.parent) { nextComponent = this._getNextCandidate(component, reverse); } } if (nextComponent == null) { nextComponent = component.parent; } if (nextComponent == null) { return null; } lastComponent = component; component = nextComponent; visitedComponents[component.renderId] = true; if (component != originComponent && component.isActive() && component.focusable) { return component; } } }, findInParent: function(parentComponent, reverse, minimumDistance) { if (!minimumDistance) { minimumDistance = 1; } var visitedIds = {}, focusedComponent = this._application.getFocusedComponent(); if (!focusedComponent) { return null; } visitedIds[focusedComponent.renderId] = true; var focusedIndex = this._getDescendantIndex(parentComponent, focusedComponent); if (focusedIndex == -1) { return null; } var componentIndex = focusedIndex; var component = focusedComponent; do { component = this.find(component, reverse, visitedIds); if (component == null || visitedIds[component.renderId]) { return null; } componentIndex = this._getDescendantIndex(parentComponent, component); visitedIds[component.renderId] = true; } while (Math.abs(componentIndex - focusedIndex) < minimumDistance && component != focusedComponent); if (component == focusedComponent) { return null; } this._application.setFocusedComponent(component); return component; }, _getDescendantIndex: function(parent, descendant) { while (descendant.parent != parent && descendant.parent != null) { descendant = descendant.parent; } if (descendant.parent == null) { return -1; } return parent.indexOf(descendant); }, _getFocusOrder: function(component) { var focusOrder = component.getFocusOrder ? component.getFocusOrder() : null; if (!focusOrder) { return null; } var testOrder = focusOrder.slice().sort(); for (var i = 1; i < testOrder.length; ++i) { if (testOrder[i - 1] >= testOrder[i]) { Core.Debug.consoleWrite("Invalid focus order for component " + component + ": " + focusOrder); return null; } } return focusOrder; }, _getNextCandidate: function(component, reverse) { if (!component.parent) { return null; } var focusOrder = this._getFocusOrder(component.parent); var componentIndex, orderIndex; if (reverse) { componentIndex = component.parent.indexOf(component); if (focusOrder) { orderIndex = Core.Arrays.indexOf(focusOrder, componentIndex); if (orderIndex > 0) { return component.parent.children[focusOrder[orderIndex - 1]]; } } else { if (componentIndex > 0) { return component.parent.getComponent(componentIndex - 1); } } } else { componentIndex = component.parent.indexOf(component); if (focusOrder) { orderIndex = Core.Arrays.indexOf(focusOrder, componentIndex); if (orderIndex < focusOrder.length - 1) { return component.parent.children[focusOrder[orderIndex + 1]]; } } else { if (componentIndex < component.parent.getComponentCount() - 1) { return component.parent.getComponent(componentIndex + 1); } } } } }); Echo.LayoutDirection = Core.extend({ _ltr: false, $construct: function(ltr) { this._ltr = ltr; }, isLeftToRight: function() { return this._ltr; } }); Echo.LayoutDirection.LTR = new Echo.LayoutDirection(true); Echo.LayoutDirection.RTL = new Echo.LayoutDirection(false); Echo.StyleSheet = Core.extend({ _nameToStyleMap: null, _renderCache: null, $construct: function(initialValues) { this._renderCache = { }; this._nameToStyleMap = { }; if (initialValues) { for (var styleName in initialValues) { for (var componentType in initialValues[styleName]) { this.setStyle(styleName, componentType, initialValues[styleName][componentType]); } } } }, getRenderStyle: function(name, componentType) { var typeToStyleMap = this._renderCache[name]; if (!typeToStyleMap) { return null; } var style = typeToStyleMap[componentType]; if (style !== undefined) { return style; } else { return this._loadRenderStyle(name, componentType); } }, _loadRenderStyle: function(name, componentType) { var typeToStyleMap = this._nameToStyleMap[name]; if (typeToStyleMap == null) { this._renderCache[name][componentType] = null; return null; } var style = typeToStyleMap[componentType]; if (style == null) { var testType = componentType; while (style == null) { testType = Echo.ComponentFactory.getSuperType(testType); if (testType == null) { this._renderCache[name][testType] = null; return null; } style = typeToStyleMap[testType]; } } this._renderCache[name][componentType] = style; return style; }, getStyle: function(name, componentType) { var typeToStyleMap = this._nameToStyleMap[name]; if (typeToStyleMap == null) { return null; } return typeToStyleMap[componentType]; }, setStyle: function(name, componentType, style) { this._renderCache[name] = {}; var typeToStyleMap = this._nameToStyleMap[name]; if (typeToStyleMap == null) { typeToStyleMap = {}; this._nameToStyleMap[name] = typeToStyleMap; } typeToStyleMap[componentType] = style; } }); Echo.Update = { }; Echo.Update.ComponentUpdate = Core.extend({ $static: { PropertyUpdate: function(oldValue, newValue) { this.oldValue = oldValue; this.newValue = newValue; } }, _manager: null, parent: null, renderContext: null, _addedChildIds: null, _propertyUpdates: null, _removedChildIds: null, _removedDescendantIds: null, _updatedLayoutDataChildIds: null, _listenerUpdates: null, $construct: function(manager, parent) { this._manager = manager; this.parent = parent; }, _addChild: function(child) { if (!this._addedChildIds) { this._addedChildIds = []; } this._addedChildIds.push(child.renderId); this._manager._idMap[child.renderId] = child; }, _appendRemovedDescendants: function(update) { var i; if (update._removedDescendantIds != null) { if (this._removedDescendantIds == null) { this._removedDescendantIds = []; } for (i = 0; i < update._removedDescendantIds.length; ++i) { this._removedDescendantIds.push(update._removedDescendantIds[i]); } } if (update._removedChildIds != null) { if (this._removedDescendantIds == null) { this._removedDescendantIds = []; } for (i = 0; i < update._removedChildIds.length; ++i) { this._removedDescendantIds.push(update._removedChildIds[i]); } } if (this._removedDescendantIds != null) { Core.Arrays.removeDuplicates(this._removedDescendantIds); } }, getAddedChildren: function() { if (!this._addedChildIds) { return null; } var components = []; for (var i = 0; i < this._addedChildIds.length; ++i) { components[i] = this._manager._idMap[this._addedChildIds[i]]; } return components; }, getRemovedChildren: function() { if (!this._removedChildIds) { return null; } var components = []; for (var i = 0; i < this._removedChildIds.length; ++i) { components[i] = this._manager._removedIdMap[this._removedChildIds[i]]; } return components; }, getRemovedDescendants: function() { if (!this._removedDescendantIds) { return null; } var components = []; for (var i = 0; i < this._removedDescendantIds.length; ++i) { components[i] = this._manager._removedIdMap[this._removedDescendantIds[i]]; } return components; }, getUpdatedLayoutDataChildren: function() { if (!this._updatedLayoutDataChildIds) { return null; } var components = []; for (var i = 0; i < this._updatedLayoutDataChildIds.length; ++i) { components[i] = this._manager._idMap[this._updatedLayoutDataChildIds[i]]; } return components; }, hasAddedChildren: function() { return this._addedChildIds != null; }, hasRemovedChildren: function() { return this._removedChildIds != null; }, hasUpdatedLayoutDataChildren: function() { return this._updatedLayoutDataChildIds != null; }, hasUpdatedProperties: function() { return this._propertyUpdates != null; }, getUpdatedProperty: function(name) { if (this._propertyUpdates == null) { return null; } return this._propertyUpdates[name]; }, isListenerTypeUpdated: function(listenerType) { return this._listenerUpdates == null ? false : this._listenerUpdates[listenerType]; }, getUpdatedPropertyNames: function() { if (this._propertyUpdates == null) { return []; } var updatedPropertyNames = []; for (var i in this._propertyUpdates) { updatedPropertyNames.push(i); } return updatedPropertyNames; }, hasUpdatedPropertyIn: function(updatedPropertySet) { for (var x in this._propertyUpdates) { if (updatedPropertySet[x]) { return true; } } return false; }, isUpdatedPropertySetIn: function(updatedPropertySet) { for (var x in this._propertyUpdates) { if (!updatedPropertySet[x]) { return false; } } return true; }, _removeChild: function(child) { this._manager._removedIdMap[child.renderId] = child; if (this._addedChildIds) { Core.Arrays.remove(this._addedChildIds, child.renderId); } if (this._updatedLayoutDataChildIds) { Core.Arrays.remove(this._updatedLayoutDataChildIds, child.renderId); } if (!this._removedChildIds) { this._removedChildIds = []; } this._removedChildIds.push(child.renderId); for (var i = 0; i < child.children.length; ++i) { this._removeDescendant(child.children[i]); } }, _removeDescendant: function(descendant) { this._manager._removedIdMap[descendant.renderId] = descendant; if (!this._removedDescendantIds) { this._removedDescendantIds = []; } this._removedDescendantIds.push(descendant.renderId); for (var i = 0; i < descendant.children.length; ++i) { this._removeDescendant(descendant.children[i]); } }, toString: function() { var s = "ComponentUpdate\n"; s += "- Parent: " + this.parent + "\n"; s += "- Adds: " + this._addedChildIds + "\n"; s += "- Removes: " + this._removedChildIds + "\n"; s += "- DescendantRemoves: " + this._removedDescendantIds + "\n"; s += "- Properties: " + Core.Debug.toString(this._propertyUpdates) + "\n"; s += "- LayoutDatas: " + this._updatedLayoutDataChildIds + "\n"; return s; }, _updateLayoutData: function(child) { this._manager._idMap[child.renderId] = child; if (this._updatedLayoutDataChildIds == null) { this._updatedLayoutDataChildIds = []; } this._updatedLayoutDataChildIds.push(child.renderId); }, _updateListener: function(listenerType) { if (this._listenerUpdates == null) { this._listenerUpdates = { }; } this._listenerUpdates[listenerType] = true; }, _updateProperty: function(propertyName, oldValue, newValue) { if (this._propertyUpdates == null) { this._propertyUpdates = { }; } var propertyUpdate = new Echo.Update.ComponentUpdate.PropertyUpdate(oldValue, newValue); this._propertyUpdates[propertyName] = propertyUpdate; } }); Echo.Update.Manager = Core.extend({ _componentUpdateMap: null, fullRefreshRequired: false, application: null, _hasUpdates: false, _listenerList: null, _idMap: null, _removedIdMap: null, _lastAncestorTestParentId: null, $construct: function(application) { this._componentUpdateMap = { }; this.application = application; this._listenerList = new Core.ListenerList(); this._idMap = { }; this._removedIdMap = { }; }, addUpdateListener: function(l) { this._listenerList.addListener("update", l); }, _createComponentUpdate: function(parent) { this._hasUpdates = true; var update = this._componentUpdateMap[parent.renderId]; if (!update) { update = new Echo.Update.ComponentUpdate(this, parent); this._componentUpdateMap[parent.renderId] = update; } return update; }, dispose: function() { this.application = null; }, _fireUpdate: function() { if (!this._listenerList.isEmpty()) { this._listenerList.fireEvent({type: "update", source: this}); } }, getUpdates: function() { var updates = []; for (var key in this._componentUpdateMap) { updates.push(this._componentUpdateMap[key]); } return updates; }, hasUpdates: function() { return this._hasUpdates; }, _isAncestorBeingAdded: function(component) { var child = component; var parent = component.parent; var originalParentId = parent ? parent.renderId : null; if (originalParentId && this._lastAncestorTestParentId == originalParentId) { return false; } while (parent) { var update = this._componentUpdateMap[parent.renderId]; if (update && update._addedChildIds) { for (var i = 0; i < update._addedChildIds.length; ++i) { if (update._addedChildIds[i] == child.renderId) { return true; } } } child = parent; parent = parent.parent; } this._lastAncestorTestParentId = originalParentId; return false; }, _processComponentAdd: function(parent, child) { if (this.fullRefreshRequired) { return; } if (this._isAncestorBeingAdded(child)) { return; } var update = this._createComponentUpdate(parent); update._addChild(child); }, _processComponentLayoutDataUpdate: function(updatedComponent) { if (this.fullRefreshRequired) { return; } var parent = updatedComponent.parent; if (parent == null || this._isAncestorBeingAdded(parent)) { return; } var update = this._createComponentUpdate(parent); update._updateLayoutData(updatedComponent); }, _processComponentListenerUpdate: function(parent, listenerType) { if (this.fullRefreshRequired) { return; } if (this._isAncestorBeingAdded(parent)) { return; } var update = this._createComponentUpdate(parent); update._updateListener(listenerType); }, _processComponentRemove: function(parent, child) { if (this.fullRefreshRequired) { return; } if (this._isAncestorBeingAdded(parent)) { return; } var update = this._createComponentUpdate(parent); update._removeChild(child); var disposedIds = null; for (var testParentId in this._componentUpdateMap) { var testUpdate = this._componentUpdateMap[testParentId]; if (child.isAncestorOf(testUpdate.parent)) { update._appendRemovedDescendants(testUpdate); if (disposedIds == null) { disposedIds = []; } disposedIds.push(testParentId); } } if (disposedIds != null) { for (var i = 0; i < disposedIds.length; ++i) { delete this._componentUpdateMap[disposedIds[i]]; } } }, _processComponentPropertyUpdate: function(component, propertyName, oldValue, newValue) { if (this.fullRefreshRequired) { return; } if (this._isAncestorBeingAdded(component)) { return; } var update = this._createComponentUpdate(component); update._updateProperty(propertyName, oldValue, newValue); }, _processFullRefresh: function() { for (var i = 0; i < this.application.rootComponent.children.length; ++i) { this._processComponentRemove(this.application.rootComponent, this.application.rootComponent.children[i]); } this.fullRefreshRequired = true; var update = this._createComponentUpdate(this.application.rootComponent); update.fullRefresh = true; this._fireUpdate(); }, _processComponentUpdate: function(parent, propertyName, oldValue, newValue) { if (propertyName == "children") { if (newValue == null) { this._processComponentRemove(parent, oldValue); } else { this._processComponentAdd(parent, newValue); } } else if (propertyName == "layoutData") { this._processComponentLayoutDataUpdate(parent); } else if (propertyName == "listeners") { this._processComponentListenerUpdate(parent, oldValue || newValue); } else { this._processComponentPropertyUpdate(parent, propertyName, oldValue, newValue); } this._fireUpdate(); }, purge: function() { this.fullRefreshRequired = false; this._componentUpdateMap = { }; this._idMap = { }; this._removedIdMap = { }; this._hasUpdates = false; this._lastAncestorTestParentId = null; }, removeUpdateListener: function(l) { this._listenerList.removeListener("update", l); }, toString: function() { var s = "[ UpdateManager ]\n"; if (this.fullRefreshRequired) { s += "fullRefresh"; } else { for (var key in this._componentUpdateMap) { s += this._componentUpdateMap[key]; } } return s; } }); Echo.AbstractButton = Core.extend(Echo.Component, { $abstract: true, $load: function() { Echo.ComponentFactory.registerType("AbstractButton", this); Echo.ComponentFactory.registerType("AB", this); }, componentType: "AbstractButton", focusable: true, $virtual: { doAction: function() { this.fireEvent({type: "action", source: this, actionCommand: this.get("actionCommand")}); } } }); Echo.Button = Core.extend(Echo.AbstractButton, { $load: function() { Echo.ComponentFactory.registerType("Button", this); Echo.ComponentFactory.registerType("B", this); }, componentType: "Button" }); Echo.ToggleButton = Core.extend(Echo.AbstractButton, { $load: function() { Echo.ComponentFactory.registerType("ToggleButton", this); Echo.ComponentFactory.registerType("TB", this); }, $abstract: true, componentType: "ToggleButton" }); Echo.CheckBox = Core.extend(Echo.ToggleButton, { $load: function() { Echo.ComponentFactory.registerType("CheckBox", this); Echo.ComponentFactory.registerType("CB", this); }, componentType: "CheckBox" }); Echo.RadioButton = Core.extend(Echo.ToggleButton, { $load: function() { Echo.ComponentFactory.registerType("RadioButton", this); Echo.ComponentFactory.registerType("RB", this); }, componentType: "RadioButton" }); Echo.AbstractListComponent = Core.extend(Echo.Component, { $abstract: true, $load: function() { Echo.ComponentFactory.registerType("AbstractListComponent", this); Echo.ComponentFactory.registerType("LC", this); }, componentType: "AbstractListComponent", focusable: true, $virtual: { doAction: function() { this.fireEvent({type: "action", source: this, actionCommand: this.get("actionCommand")}); } } }); Echo.ListBox = Core.extend(Echo.AbstractListComponent, { $static: { SINGLE_SELECTION: 0, MULTIPLE_SELECTION: 2 }, $load: function() { Echo.ComponentFactory.registerType("ListBox", this); Echo.ComponentFactory.registerType("LB", this); }, componentType: "ListBox" }); Echo.SelectField = Core.extend(Echo.AbstractListComponent, { $load: function() { Echo.ComponentFactory.registerType("SelectField", this); Echo.ComponentFactory.registerType("SF", this); }, componentType: "SelectField" }); Echo.Column = Core.extend(Echo.Component, { $load: function() { Echo.ComponentFactory.registerType("Column", this); Echo.ComponentFactory.registerType("C", this); }, componentType: "Column" }); Echo.Composite = Core.extend(Echo.Component, { $abstract: true, $load: function() { Echo.ComponentFactory.registerType("Composite", this); Echo.ComponentFactory.registerType("CM", this); }, componentType: "Composite" }); Echo.Panel = Core.extend(Echo.Composite, { $load: function() { Echo.ComponentFactory.registerType("Panel", this); Echo.ComponentFactory.registerType("P", this); }, componentType: "Panel" }); Echo.ContentPane = Core.extend(Echo.Component, { $static: { OVERFLOW_AUTO: 0, OVERFLOW_HIDDEN: 1, OVERFLOW_SCROLL: 2 }, $load: function() { Echo.ComponentFactory.registerType("ContentPane", this); Echo.ComponentFactory.registerType("CP", this); }, componentType: "ContentPane", pane: true }); Echo.Grid = Core.extend(Echo.Component, { $static: { ORIENTATION_HORIZONTAL: 0, ORIENTATION_VERTICAL: 1, SPAN_FILL: -1 }, $load: function() { Echo.ComponentFactory.registerType("Grid", this); Echo.ComponentFactory.registerType("G", this); }, componentType: "Grid" }); Echo.Label = Core.extend(Echo.Component, { $load: function() { Echo.ComponentFactory.registerType("Label", this); Echo.ComponentFactory.registerType("L", this); }, componentType: "Label" }); Echo.Row = Core.extend(Echo.Component, { $load: function() { Echo.ComponentFactory.registerType("Row", this); Echo.ComponentFactory.registerType("R", this); }, componentType: "Row" }); Echo.SplitPane = Core.extend(Echo.Component, { $static: { ORIENTATION_HORIZONTAL_LEADING_TRAILING: 0, ORIENTATION_HORIZONTAL_TRAILING_LEADING: 1, ORIENTATION_HORIZONTAL_LEFT_RIGHT: 2, ORIENTATION_HORIZONTAL_RIGHT_LEFT: 3, ORIENTATION_VERTICAL_TOP_BOTTOM: 4, ORIENTATION_VERTICAL_BOTTOM_TOP: 5, DEFAULT_SEPARATOR_POSITION: "50%", DEFAULT_SEPARATOR_SIZE_FIXED: 0, DEFAULT_SEPARATOR_SIZE_RESIZABLE: 4, DEFAULT_SEPARATOR_COLOR: "#3f3f4f", OVERFLOW_AUTO: 0, OVERFLOW_HIDDEN: 1, OVERFLOW_SCROLL: 2 }, $load: function() { Echo.ComponentFactory.registerType("SplitPane", this); Echo.ComponentFactory.registerType("SP", this); }, componentType: "SplitPane", pane: true, getFocusOrder: function() { if (this.children.length < 2) { return null; } switch (this.render("orientation")) { case Echo.SplitPane.ORIENTATION_VERTICAL_BOTTOM_TOP: case Echo.SplitPane.ORIENTATION_HORIZONTAL_TRAILING_LEADING: return [1, 0]; case Echo.SplitPane.ORIENTATION_HORIZONTAL_LEFT_RIGHT: return this.getRenderLayoutDirection().isLeftToRight() ? null : [1, 0]; case Echo.SplitPane.ORIENTATION_HORIZONTAL_RIGHT_LEFT: return this.getRenderLayoutDirection().isLeftToRight() ? [1, 0] : null; default: return null; } } }); Echo.TextComponent = Core.extend(Echo.Component, { $abstract: true, $load: function() { Echo.ComponentFactory.registerType("TextComponent", this); Echo.ComponentFactory.registerType("TC", this); }, $virtual: { doAction: function() { this.fireEvent({type: "action", source: this, actionCommand: this.get("actionCommand")}); }, doKeyDown: function(keyCode) { var e = { type: "keyDown", source: this, keyCode: keyCode }; this.fireEvent(e); return !e.veto; }, doKeyPress: function(keyCode, charCode) { var e = { type: "keyPress", source: this, keyCode: keyCode, charCode: charCode }; this.fireEvent(e); return !e.veto; } }, componentType: "TextComponent", focusable: true }); Echo.TextArea = Core.extend(Echo.TextComponent, { $load: function() { Echo.ComponentFactory.registerType("TextArea", this); Echo.ComponentFactory.registerType("TA", this); }, componentType: "TextArea" }); Echo.TextField = Core.extend(Echo.TextComponent, { $load: function() { Echo.ComponentFactory.registerType("TextField", this); Echo.ComponentFactory.registerType("TF", this); }, componentType: "TextField" }); Echo.PasswordField = Core.extend(Echo.TextField, { $load: function() { Echo.ComponentFactory.registerType("PasswordField", this); Echo.ComponentFactory.registerType("PF", this); }, componentType: "PasswordField" }); Echo.WindowPane = Core.extend(Echo.Component, { $load: function() { Echo.ComponentFactory.registerType("WindowPane", this); Echo.ComponentFactory.registerType("WP", this); }, $static: { DEFAULT_RESOURCE_TIMEOUT: 300, DEFAULT_BORDER: { color: "#36537a", borderInsets: 20, contentInsets: 3 }, DEFAULT_BACKGROUND: "#ffffff", DEFAULT_FOREGROUND: "#000000", DEFAULT_CONTROLS_INSETS: 4, DEFAULT_CONTROLS_SPACING: 4, DEFAULT_HEIGHT: "15em", DEFAULT_MINIMUM_WIDTH: 100, DEFAULT_MINIMUM_HEIGHT: 100, DEFAULT_TITLE_BACKGROUND: "#becafe", DEFAULT_TITLE_HEIGHT: 30, DEFAULT_TITLE_INSETS: "5px 10px", DEFAULT_WIDTH: "30em" }, componentType: "WindowPane", modalSupport: true, floatingPane: true, pane: true, focusable: true, _preMaximizedState: null, userClose: function() { this.fireEvent({type: "close", source: this}); }, userMaximize: function() { if (this.render("width") == "100%" && this.render("height") == "100%") { if (this._preMaximizedState) { this.set("width", this._preMaximizedState.width); this.set("height", this._preMaximizedState.height); this.set("positionX", this._preMaximizedState.x); this.set("positionY", this._preMaximizedState.y); } } else { this._preMaximizedState = { x: this.get("positionX"), y: this.get("positionY"), width: this.get("width"), height: this.get("height") }; this.set("width", "100%"); this.set("height", "100%"); } this.fireEvent({type: "maximize", source: this}); }, userMinimize: function() { this.fireEvent({type: "minimize", source: this}); } }); Echo.Render = { _loadedPeerCount: 0, _nextPeerId: 0, _peers: {}, _disposedComponents: null, _componentDepthArraySort: function(a, b) { return Echo.Render._getComponentDepth(a.parent) - Echo.Render._getComponentDepth(b.parent); }, _doRenderDisplay: function(component, includeSelf) { var i, testComponent = component; var testParent = testComponent.parent; while (testParent) { if (testParent.peer.isChildVisible && !testParent.peer.isChildVisible(testComponent)) { return; } testComponent = testParent; testParent = testParent.parent; } if (includeSelf) { Echo.Render._doRenderDisplayImpl(component); } else { if (component.peer.isChildVisible) { for (i = 0; i < component.children.length; ++i) { if (component.peer.isChildVisible(component.children[i])) { Echo.Render._doRenderDisplayImpl(component.children[i]); } } } else { for (i = 0; i < component.children.length; ++i) { Echo.Render._doRenderDisplayImpl(component.children[i]); } } } }, _doRenderDisplayImpl: function(component) { if (!component.peer) { return; } if (component.peer.renderDisplay) { component.peer.renderDisplay(); } component.peer.displayed = true; var i; if (component.peer.isChildVisible) { for (i = 0; i < component.children.length; ++i) { if (component.peer.isChildVisible(component.children[i])) { Echo.Render._doRenderDisplayImpl(component.children[i]); } } } else { for (i = 0; i < component.children.length; ++i) { Echo.Render._doRenderDisplayImpl(component.children[i]); } } }, _getComponentDepth: function(component) { var depth = -1; while (component != null) { component = component.parent; ++depth; } return depth; }, _loadPeer: function(client, component) { if (component.peer) { return; } var peerClass = Echo.Render._peers[component.componentType]; if (!peerClass) { throw new Error("Peer not found for: " + component.componentType); } ++this._loadedPeerCount; component.peer = new peerClass(); component.peer._peerId = this._nextPeerId++; component.peer.component = component; component.peer.client = client; }, notifyResize: function(parent) { Echo.Render._doRenderDisplay(parent, false); }, _processDispose: function(update) { var i, components = update.getRemovedDescendants(); if (components) { for (i = 0; i < components.length; ++i) { Echo.Render._renderComponentDisposeImpl(update, components[i]); } } components = update.getRemovedChildren(); if (components) { for (i = 0; i < components.length; ++i) { Echo.Render._renderComponentDisposeImpl(update, components[i]); } } }, processUpdates: function(client) { var updateManager = client.application.updateManager; if (!updateManager.hasUpdates()) { return; } Echo.Render._disposedComponents = {}; var updates = updateManager.getUpdates(); updates.sort(Echo.Render._componentDepthArraySort); var peer, i, j; for (i = 0; i < updates.length; ++i) { updates[i].renderContext = {}; peer = updates[i].parent.peer; if (peer == null && updates[i].parent.componentType == "Root") { Echo.Render._loadPeer(client, updates[i].parent); } } for (i = updates.length - 1; i >= 0; --i) { if (updates[i] == null) { continue; } peer = updates[i].parent.peer; Echo.Render._processDispose(updates[i]); } if (Echo.Client.profilingTimer) { Echo.Client.profilingTimer.mark("rem"); } for (i = 0; i < updates.length; ++i) { if (updates[i] == null) { continue; } peer = updates[i].parent.peer; var fullRender = peer.renderUpdate(updates[i]); if (fullRender) { for (j = i + 1; j < updates.length; ++j) { if (updates[j] != null && updates[i].parent.isAncestorOf(updates[j].parent)) { updates[j] = null; } } } Echo.Render._setPeerDisposedState(updates[i].parent, false); } if (Echo.Client.profilingTimer) { Echo.Client.profilingTimer.mark("up"); } var displayed = []; for (i = 0; i < updates.length; ++i) { if (updates[i] == null) { continue; } var cancelDisplay = false; for (j = 0; j < displayed.length; ++j) { if (displayed[j].isAncestorOf(updates[i].parent)) { cancelDisplay = true; break; } } if (cancelDisplay) { continue; } if (updates[i].renderContext.displayRequired) { for (j = 0; j < updates[i].renderContext.displayRequired.length; ++j) { displayed.push(updates[i].renderContext.displayRequired[j]); Echo.Render._doRenderDisplay(updates[i].renderContext.displayRequired[j], true); } } else { displayed.push(updates[i].parent); Echo.Render._doRenderDisplay(updates[i].parent, true); } } if (Echo.Client.profilingTimer) { Echo.Client.profilingTimer.mark("disp"); } for (var peerId in Echo.Render._disposedComponents) { var component = Echo.Render._disposedComponents[peerId]; Echo.Render._unloadPeer(component); } Echo.Render._disposedComponents = null; updateManager.purge(); Echo.Render.updateFocus(client); }, registerPeer: function(componentName, peerObject) { if (this._peers[componentName]) { throw new Error("Peer already registered: " + componentName); } this._peers[componentName] = peerObject; }, renderComponentAdd: function(update, component, parentElement) { if (!component.parent || !component.parent.peer || !component.parent.peer.client) { throw new Error("Cannot find reference to the Client with which this component should be associated: " + "cannot load peer. This is due to the component's parent's peer not being associated with a Client. " + "Component = " + component + ", Parent = " + component.parent + ", Parent Peer = " + (component.parent ? component.parent.peer : "N/A") + ", Parent Peer Client = " + ((component.parent && component.parent.peer) ? component.parent.peer.client : "N/A")); } Echo.Render._loadPeer(component.parent.peer.client, component); Echo.Render._setPeerDisposedState(component, false); component.peer.renderAdd(update, parentElement); }, renderComponentDisplay: function(parent) { this._doRenderDisplay(parent, true); }, renderComponentDispose: function(update, component) { this._renderComponentDisposeImpl(update, component); }, _renderComponentDisposeImpl: function(update, component) { if (!component.peer || component.peer.disposed) { return; } Echo.Render._setPeerDisposedState(component, true); component.peer.renderDispose(update); for (var i = 0; i < component.children.length; ++i) { Echo.Render._renderComponentDisposeImpl(update, component.children[i]); } }, renderComponentHide: function(component) { if (!component.peer || component.peer.disposed) { return; } if (component.peer.displayed) { if (component.peer.renderHide) { component.peer.renderHide(); } component.peer.displayed = false; for (var i = 0; i < component.children.length; ++i) { Echo.Render.renderComponentHide(component.children[i]); } } }, _setPeerDisposedState: function(component, disposed) { if (disposed) { component.peer.disposed = true; Echo.Render._disposedComponents[component.peer._peerId] = component; } else { component.peer.disposed = false; delete Echo.Render._disposedComponents[component.peer._peerId]; } }, _unloadPeer: function(component) { component.peer.client = null; component.peer.component = null; component.peer = null; --this._loadedPeerCount; }, updateFocus: function(client) { var focusedComponent = client.application.getFocusedComponent(); if (focusedComponent && focusedComponent.peer) { if (!focusedComponent.peer.renderFocus) { throw new Error("Cannot focus component: " + focusedComponent + ", peer does not provide renderFocus() implementation."); } focusedComponent.peer.renderFocus(); } else { Core.Web.DOM.focusElement(null); } } }; Echo.Render.ComponentSync = Core.extend({ $static: { FOCUS_PERMIT_ARROW_UP: 0x1, FOCUS_PERMIT_ARROW_DOWN: 0x2, FOCUS_PERMIT_ARROW_LEFT: 0x4, FOCUS_PERMIT_ARROW_RIGHT: 0x8, FOCUS_PERMIT_ARROW_ALL: 0xf, SIZE_HEIGHT: 0x1, SIZE_WIDTH: 0x2 }, _peerId: null, client: null, component: null, displayed: false, disposed: false, $construct: function() { }, $abstract: { renderAdd: function(update, parentElement) { }, renderDispose: function(update) { }, renderUpdate: function(update) { } }, $virtual: { clientKeyDown: null, clientKeyPress: null, clientKeyUp: null, getFocusFlags: null, getPreferredSize: null, isChildVisible: null, renderFocus: null, renderHide: null, renderDisplay: null } }); Echo.Render.RootSync = Core.extend(Echo.Render.ComponentSync, { $load: function() { Echo.Render.registerPeer("Root", this); }, renderAdd: function(update, parentElement) { throw new Error("Unsupported operation: renderAdd()."); }, _renderContent: function(update) { Echo.Render.renderComponentDispose(update, update.parent); Core.Web.DOM.removeAllChildren(this.client.domainElement); for (var i = 0; i < update.parent.children.length; ++i) { Echo.Render.renderComponentAdd(update, update.parent.children[i], this.client.domainElement); } }, renderDispose: function(update) { }, renderUpdate: function(update) { var property, fullRender = false; if (update.fullRefresh || update.hasAddedChildren() || update.hasRemovedChildren()) { Echo.Sync.renderComponentDefaults(this.component, this.client.domainElement); var title = this.component.render("title"); if (title) { document.title = title; } this._renderContent(update); fullRender = true; } else { this.client.domainElement.dir = this.client.application.getLayoutDirection().isLeftToRight() ? "ltr" : "rtl"; if (update.hasUpdatedProperties()) { property = update.getUpdatedProperty("title"); if (property) { document.title = property.newValue; } property = update.getUpdatedProperty("background"); if (property) { Echo.Sync.Color.renderClear(property.newValue, this.client.domainElement, "backgroundColor"); } property = update.getUpdatedProperty("foreground"); if (property) { Echo.Sync.Color.renderClear(property.newValue, this.client.domainElement, "foreground"); } property = update.getUpdatedProperty("font"); if (property) { Echo.Sync.Font.renderClear(property.newValue, this.client.domainElement); } Echo.Sync.LayoutDirection.render(this.component.getLayoutDirection(), this.client.domainElement); } } return fullRender; } }); Echo.Sync = { getEffectProperty: function(component, defaultPropertyName, effectPropertyName, effectState, defaultDefaultPropertyValue, effectDefaultPropertyValue) { var property; if (effectState) { property = component.render(effectPropertyName, effectDefaultPropertyValue); } if (!property) { property = component.render(defaultPropertyName, defaultDefaultPropertyValue); } return property; }, renderComponentDefaults: function(component, element) { var color; if ((color = component.render("foreground"))) { color = Echo.Sync.Color.toTransparent(color); element.style.color = color; } if ((color = component.render("background"))) { color = Echo.Sync.Color.toTransparent(color); element.style.backgroundColor = color; } var font = component.render("font"); if (font) { Echo.Sync.Font.render(font, element); } if (component.getLayoutDirection()) { element.dir = component.getLayoutDirection().isLeftToRight() ? "ltr" : "rtl"; } } }; Echo.Sync.Alignment = { _HORIZONTALS: { left: true, center: true, right: true, leading: true, trailing: true }, _VERTICALS: { top: true, middle: true, bottom: true }, getRenderedHorizontal: function(alignment, layoutDirectionProvider) { if (alignment == null) { return null; } var layoutDirection = layoutDirectionProvider ? layoutDirectionProvider.getRenderLayoutDirection() : Echo.LayoutDirection.LTR; var horizontal = typeof(alignment) == "object" ? alignment.horizontal : alignment; switch (horizontal) { case "leading": return layoutDirection.isLeftToRight() ? "left" : "right"; case "trailing": return layoutDirection.isLeftToRight() ? "right" : "left"; default: return horizontal in this._HORIZONTALS ? horizontal : null; } }, getHorizontal: function(alignment) { if (alignment == null) { return null; } if (typeof(alignment == "string")) { return alignment in this._HORIZONTALS ? alignment : null; } else { return alignment.horizontal; } }, getVertical: function(alignment) { if (alignment == null) { return null; } if (typeof(alignment == "string")) { return alignment in this._VERTICALS ? alignment : null; } else { return alignment.vertical; } }, render: function(alignment, element, renderToElement, layoutDirectionProvider) { if (alignment == null) { return; } var horizontal = Echo.Sync.Alignment.getRenderedHorizontal(alignment, layoutDirectionProvider); var vertical = typeof(alignment) == "object" ? alignment.vertical : alignment; var horizontalValue; switch (horizontal) { case "left": horizontalValue = "left"; break; case "center": horizontalValue = "center"; break; case "right": horizontalValue = "right"; break; default: horizontalValue = ""; break; } var verticalValue; switch (vertical) { case "top": verticalValue = "top"; break; case "middle": verticalValue = "middle"; break; case "bottom": verticalValue = "bottom"; break; default: verticalValue = ""; break; } if (renderToElement) { element.align = horizontalValue; element.vAlign = verticalValue; } else { element.style.textAlign = horizontalValue; element.style.verticalAlign = verticalValue; } } }; Echo.Sync.Border = { _PARSER_PX: new RegExp("^(-?\\d+px)?(?:^|$|(?= )) ?(none|hidden|dotted|dashed|solid|" + "double|groove|ridge|inset|outset)?(?:^|$|(?= )) ?(#[0-9a-fA-F]{6})?$"), _PARSER: new RegExp("^(-?\\d+(?:\\.\\d*)?(?:px|pt|pc|cm|mm|in|em|ex))?(?:^|$|(?= )) ?(none|hidden|dotted|dashed|solid|" + "double|groove|ridge|inset|outset)?(?:^|$|(?= )) ?(#[0-9a-fA-F]{6})?$"), _TEST_EXTENT_PX: /^-?\d+px$/, compose: function(size, style, color) { if (typeof size == "number") { size += "px"; } var out = []; if (size) { out.push(size); } if (style) { out.push(style); } if (color) { out.push(color); } return out.join(" "); }, isMultisided: function(border) { return (border && (border.top || border.bottom || border.left || border.right)) ? true : false; }, parse: function(border) { if (!border) { return { }; } if (typeof(border) == "string") { var parts = this._PARSER.exec(border); return { size: parts[1], style: parts[2], color: parts[3] }; } else { return Echo.Sync.Border.parse(border.top || border.right || border.bottom || border.left); } }, render: function(border, element, styleAttribute) { if (!border) { return; } styleAttribute = styleAttribute ? styleAttribute : "border"; if (typeof(border) == "string") { if (this._PARSER_PX.test(border)) { element.style[styleAttribute] = border; } else { var elements = this._PARSER.exec(border); if (elements == null) { throw new Error("Invalid border: \"" + border + "\""); } this.render(Echo.Sync.Extent.toPixels(elements[1]) + "px " + elements[2] + " " + elements[3], element, styleAttribute); } } else { this.render(border.top, element, styleAttribute + "Top"); if (border.right !== null) { this.render(border.right || border.top, element, styleAttribute + "Right"); } if (border.bottom !== null) { this.render(border.bottom || border.top, element, styleAttribute + "Bottom"); } if (border.left !== null) { this.render(border.left || border.right || border.top, element, styleAttribute + "Left"); } } }, renderClear: function(border, element) { if (border) { if (border instanceof Object) { element.style.border = ""; } this.render(border, element); } else { element.style.border = ""; } }, getPixelSize: function(border, sideName) { if (!border) { return 0; } if (typeof(border) == "string") { var extent = this._PARSER.exec(border)[1]; if (extent == null) { return 0; } else if (this._TEST_EXTENT_PX.test(extent)) { return parseInt(extent, 10); } else { return Echo.Sync.Extent.toPixels(extent); } } else if (typeof(border) == "object") { while (true) { var side = this.getPixelSize(border[sideName]); if (side == null) { switch (sideName) { case "left": sideName = "right"; continue; case "right": case "bottom": sideName = "top"; continue; } } return side; } } } }; Echo.Sync.Color = { adjust: function(value, r, g, b) { var colorInt = parseInt(value.substring(1), 16); var red = Math.floor(colorInt / 0x10000) + r; var green = Math.floor(colorInt / 0x100) % 0x100 + g; var blue = colorInt % 0x100 + b; return this.toHex(red, green, blue); }, blend: function(value1, value2, ratio) { ratio = ratio < 0 ? 0 : (ratio > 1 ? 1 : ratio); var colorInt1 = parseInt(value1.substring(1), 16); var colorInt2 = parseInt(value2.substring(1), 16); var red = Math.round(Math.floor(colorInt1 / 0x10000) * (1 - ratio) + Math.floor(colorInt2 / 0x10000) * ratio); var green = Math.round(Math.floor(colorInt1 / 0x100) % 0x100 * (1 - ratio) + Math.floor(colorInt2 / 0x100) % 0x100 * ratio); var blue = Math.round((colorInt1 % 0x100) * (1 - ratio) + (colorInt2 % 0x100) * ratio); return this.toHex(red, green, blue); }, render: function(color, element, styleAttribute) { if (color) { color = this.toTransparent(color); element.style[styleAttribute] = color; } }, renderClear: function(color, element, styleAttribute) { element.style[styleAttribute] = color ? color : ""; }, renderFB: function(component, element) { var color; if ((color = component.render("foreground"))) { color = this.toTransparent(color); element.style.color = color; } if ((color = component.render("background"))) { color = this.toTransparent(color); element.style.backgroundColor = color; } }, toHex: function(red, green, blue) { if (red < 0) { red = 0; } else if (red > 255) { red = 255; } if (green < 0) { green = 0; } else if (green > 255) { green = 255; } if (blue < 0) { blue = 0; } else if (blue > 255) { blue = 255; } return "#" + (red < 16 ? "0" : "") + red.toString(16) + (green < 16 ? "0" : "") + green.toString(16) + (blue < 16 ? "0" : "") + blue.toString(16); }, toTransparent: function(color) { return (color == -1 || color == '#-1' || (color && color.toLowerCase() == '#transparent')) ? 'transparent' : color; } }; Echo.Sync.Extent = { _PARSER: /^(-?\d+(?:\.\d+)?)(.+)?$/, _FORMATTED_INT_PIXEL_TEST: /^(-?\d+px *)$/, _FORMATTED_DECIMAL_PIXEL_TEST: /^(-?\d+(.\d+)?px *)$/, isPercent: function(extent) { if (extent == null || typeof(extent) == "number") { return false; } else { var parts = this._PARSER.exec(extent); if (!parts) { return false; } return parts[2] == "%"; } }, render: function(extent, element, styleAttribute, horizontal, allowPercent) { var cssValue = Echo.Sync.Extent.toCssValue(extent, horizontal, allowPercent); if (cssValue !== "") { element.style[styleAttribute] = cssValue; } }, toCssValue: function(extent, horizontal, allowPercent) { switch(typeof(extent)) { case "number": return Math.round(extent) + "px"; case "string": if (this._FORMATTED_INT_PIXEL_TEST.test(extent)) { return extent; } else if (this._FORMATTED_DECIMAL_PIXEL_TEST.test(extent)) { return Math.round(parseFloat(extent)) + "px"; } else { if (this.isPercent(extent)) { return allowPercent ? extent : ""; } else { var pixels = this.toPixels(extent, horizontal); return pixels == null ? "" : this.toPixels(extent, horizontal) + "px"; } } break; } return ""; }, toPixels: function(extent, horizontal) { if (extent == null) { return 0; } else if (typeof(extent) == "number") { return Math.round(extent); } else { return Math.round(Core.Web.Measure.extentToPixels(extent, horizontal)); } } }; Echo.Sync.FillImage = { _REPEAT_VALUES: { "0": "no-repeat", "x": "repeat-x", "y": "repeat-y", "xy": "repeat", "no-repeat": "no-repeat", "repeat-x": "repeat-x", "repeat-y": "repeat-y", "repeat": "repeat" }, FLAG_ENABLE_IE_PNG_ALPHA_FILTER: 0x1, getPosition: function(fillImage) { if (fillImage.x || fillImage.y) { var x, y; if (Echo.Sync.Extent.isPercent(fillImage.x)) { x = fillImage.x; } else { x = Echo.Sync.Extent.toPixels(fillImage.x, true) + "px"; } if (Echo.Sync.Extent.isPercent(fillImage.y)) { y = fillImage.y; } else { y = Echo.Sync.Extent.toPixels(fillImage.y, false) + "px"; } return x + " " + y; } else { return null; } }, getRepeat: function(fillImage) { if (this._REPEAT_VALUES[fillImage.repeat]) { return this._REPEAT_VALUES[fillImage.repeat]; } else { return null; } }, getUrl: function(fillImage) { if (fillImage == null) { return null; } return typeof(fillImage) == "object" ? fillImage.url : fillImage; }, render: function(fillImage, element, flags) { if (fillImage == null) { return; } var isObject = typeof(fillImage) == "object"; var url = isObject ? fillImage.url : fillImage; if (Core.Web.Env.QUIRK_IE_SECURE_ITEMS && document.location.protocol == "https:") { if (url.substring(0, 5) != "http:" && url.substring(0, 6) != "https:") { url = document.location.protocol + "//" + document.location.hostname + (document.location.port ? (":" + document.location.port) : "") + url; } } if (Core.Web.Env.PROPRIETARY_IE_PNG_ALPHA_FILTER_REQUIRED && flags && (flags & this.FLAG_ENABLE_IE_PNG_ALPHA_FILTER)) { element.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + url + "', sizingMethod='scale')"; } else { element.style.backgroundImage = "url(" + url + ")"; } if (isObject) { var position = Echo.Sync.FillImage.getPosition(fillImage); element.style.backgroundPosition = position ? position : ""; element.style.backgroundRepeat = this._REPEAT_VALUES[fillImage.repeat] ? this._REPEAT_VALUES[fillImage.repeat]: ""; } }, renderClear: function(fillImage, element, flags) { if (fillImage) { this.render(fillImage, element, flags); } else { if (Core.Web.Env.PROPRIETARY_IE_PNG_ALPHA_FILTER_REQUIRED) { element.style.filter = ""; } element.style.backgroundImage = ""; element.style.backgroundPosition = ""; element.style.backgroundRepeat = ""; } } }; Echo.Sync.FillImageBorder = { _NAMES: ["top", "topRight", "right", "bottomRight", "bottom", "bottomLeft", "left", "topLeft"], _MAP: [ [0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0], [1, 1, 1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0], [1, 0, 0, 0, 1, 0, 0, 0], [0, 0, 1, 1, 1, 0, 0, 0], [1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0], [1, 0, 0, 0, 0, 0, 1, 1], [0, 0, 1, 0, 0, 0, 1, 0], [1, 1, 1, 0, 0, 0, 1, 1], [0, 0, 0, 0, 1, 1, 1, 0], [1, 0, 0, 0, 1, 1, 1, 1], [0, 0, 1, 1, 1, 1, 1, 0], [1, 1, 1, 1, 1, 1, 1, 1] ], _PROTOTYPES: [], _createSegment: function(parent, css) { var child = document.createElement("div"); child.style.cssText = "font-size:1px;line-height:0;position:absolute;" + css; parent.appendChild(child); }, _createPrototype: function(key) { var div = document.createElement("div"); if (Core.Web.Env.QUIRK_IE_HAS_LAYOUT) { div.style.zoom = 1; } if (key & 0x1) { this._createSegment(div, "top:0;"); if (key & 0x2) { this._createSegment(div, "top:0;right:0;"); } } if (key & 0x2) { this._createSegment(div, "right:0;"); if (key & 0x4) { this._createSegment(div, "bottom:0;right:0;"); } } if (key & 0x4) { this._createSegment(div, "bottom:0;"); if (key & 0x8) { this._createSegment(div, "bottom:0;left:0;"); } } if (key & 0x8) { this._createSegment(div, "left:0;"); if (key & 0x1) { this._createSegment(div, "top:0;left:0;"); } } return div; }, getBorder: function(containerDiv) { var border = []; var child = containerDiv.firstChild; while (child) { if (child.__FIB_segment != null) { border[child.__FIB_segment] = child; } child = child.nextSibling; } return border; }, getContainerContent: function(containerDiv) { if (!containerDiv.__FIB_hasContent) { return null; } var child = containerDiv.firstChild; while (child) { if (child.__FIB_content) { return child; } child = child.nextSibling; } return null; }, renderContainer: function(fillImageBorder, configuration) { fillImageBorder = fillImageBorder || {}; configuration = configuration || {}; var bi = Echo.Sync.Insets.toPixels(fillImageBorder.borderInsets); var key = (bi.left && 0x8) | (bi.bottom && 0x4) | (bi.right && 0x2) | (bi.top && 0x1); var map = this._MAP[key]; var prototypeDiv = this._PROTOTYPES[key] ? this._PROTOTYPES[key] : this._PROTOTYPES[key] = this._createPrototype(key); var div, child, childClone, firstChild, i, content = null, border = [], insertBefore = null, testChild, insets; if (configuration.update) { div = configuration.update; child = div.firstChild; while (child) { testChild = child; child = child.nextSibling; if (testChild.__FIB_segment != null) { insertBefore = child; div.removeChild(testChild); } if (testChild.__FIB_content) { content = testChild; } } child = prototypeDiv.firstChild; while (child) { childClone = child.cloneNode(true); if (!firstChild) { firstChild = childClone; } if (insertBefore) { div.insertBefore(childClone, insertBefore); } else { div.appendChild(childClone); } child = child.nextSibling; } } else { div = prototypeDiv.cloneNode(true); firstChild = div.firstChild; if (configuration.content || configuration.child) { content = document.createElement("div"); content.__FIB_content = true; if (configuration.child) { content.appendChild(configuration.child); } div.__FIB_hasContent = true; div.appendChild(content); } if (configuration.absolute) { div.__FIB_absolute = true; div.style.position = "absolute"; } else { div.style.position = "relative"; if (content) { content.style.position = "relative"; if (Core.Web.Env.QUIRK_IE_HAS_LAYOUT) { content.style.zoom = 1; } } } } div.__key = key; child = firstChild; for (i = 0; i < 8; ++i) { if (!map[i]) { continue; } child.__FIB_segment = i; border[i] = child; if (fillImageBorder.color) { child.style.backgroundColor = fillImageBorder.color; } if (i === 0 || i === 1 || i === 7) { child.style.height = bi.top + "px"; } else if (i >= 3 && i <= 5) { child.style.height = bi.bottom + "px"; } if (i >= 1 && i <= 3) { child.style.width = bi.right + "px"; } else if (i >= 5) { child.style.width = bi.left + "px"; } Echo.Sync.FillImage.render(fillImageBorder[this._NAMES[i]], child, Echo.Sync.FillImage.FLAG_ENABLE_IE_PNG_ALPHA_FILTER); child = child.nextSibling; } if (bi.top) { border[0].style.left = bi.left + "px"; border[0].style.right = bi.right + "px"; } if (bi.right) { border[2].style.top = bi.top + "px"; border[2].style.bottom = bi.bottom + "px"; } if (bi.bottom) { border[4].style.left = bi.left + "px"; border[4].style.right = bi.right + "px"; } if (bi.left) { border[6].style.top = bi.top + "px"; border[6].style.bottom = bi.bottom + "px"; } if (div.__FIB_absolute) { if (content) { var ci = Echo.Sync.Insets.toPixels(fillImageBorder.contentInsets); content.style.position = "absolute"; content.style.overflow = "auto"; content.style.top = ci.top + "px"; content.style.right = ci.right + "px"; content.style.bottom = ci.bottom + "px"; content.style.left = ci.left + "px"; } } else { if (content) { Echo.Sync.Insets.render(fillImageBorder.contentInsets, content, "padding"); } if (!configuration.update) { div.style.position = "relative"; if (content) { content.style.position = "relative"; } } } return div; }, renderContainerDisplay: function(containerDiv) { var content; if (Core.Web.VirtualPosition.enabled) { if (containerDiv.__FIB_absolute) { Core.Web.VirtualPosition.redraw(containerDiv); if ((content = this.getContainerContent(containerDiv))) { Core.Web.VirtualPosition.redraw(content); } } var border = this.getBorder(containerDiv); for (var i = 0; i < 8; i += 2) { if (border[i]) { Core.Web.VirtualPosition.redraw(border[i]); } } } } }; Echo.Sync.Font = { render: function(font, element) { if (!font) { return; } if (font.typeface) { if (font.typeface instanceof Array) { element.style.fontFamily = font.typeface.join(","); } else { element.style.fontFamily = font.typeface; } } if (font.size) { element.style.fontSize = Echo.Sync.Extent.toCssValue(font.size); } if (font.bold) { element.style.fontWeight = "bold"; } if (font.italic) { element.style.fontStyle = "italic"; } if (font.underline) { element.style.textDecoration = "underline"; } else if (font.overline) { element.style.textDecoration = "overline"; } else if (font.lineThrough) { element.style.textDecoration = "line-through"; } }, renderClear: function(font, element) { if (font) { this.render(font, element); if (!font.typeface) { element.style.fontFamily = ""; } if (!font.underline && !font.overline && !font.lineThrough) { element.style.textDecoration = ""; } if (!font.bold) { element.style.fontWeight = ""; } if (!font.size) { element.style.fontSize = ""; } if (!font.italic) { element.style.fontStyle = ""; } } else { element.style.fontFamily = ""; element.style.fontSize = ""; element.style.fontWeight = ""; element.style.fontStyle = ""; element.style.textDecoration = ""; } } }; Echo.Sync.ImageReference = { getUrl: function(imageReference) { return imageReference ? (typeof(imageReference) == "string" ? imageReference : imageReference.url) : null; }, renderImg: function(imageReference, imgElement) { if (!imageReference) { return; } if (typeof(imageReference) == "string") { imgElement.src = imageReference; } else { imgElement.src = imageReference.url; if (imageReference.width) { imgElement.style.width = Echo.Sync.Extent.toCssValue(imageReference.width, true); } if (imageReference.height) { imgElement.style.height = Echo.Sync.Extent.toCssValue(imageReference.height, false); } } } }; Echo.Sync.Insets = { _FORMATTED_PIXEL_INSETS: /^(-?\d+px *){1,4}$/, _ZERO: { top: 0, right: 0, bottom: 0, left: 0 }, _INDEX_MAPS: { 1: [0, 0, 0, 0], 2: [0, 1, 0, 1], 3: [0, 1, 2, 1], 4: [0, 1, 2, 3] }, render: function(insets, element, styleAttribute) { switch(typeof(insets)) { case "number": element.style[styleAttribute] = Math.round(insets) + "px"; break; case "string": if (this._FORMATTED_PIXEL_INSETS.test(insets)) { element.style[styleAttribute] = insets; } else { var pixelInsets = this.toPixels(insets); element.style[styleAttribute] = pixelInsets.top + "px " + pixelInsets.right + "px " + pixelInsets.bottom + "px " + pixelInsets.left + "px"; } break; } }, renderPosition: function(insets, element) { var insetsPx = this.toPixels(insets); element.style.top = insetsPx.top + "px"; element.style.right = insetsPx.right + "px"; element.style.bottom = insetsPx.bottom + "px"; element.style.left = insetsPx.left + "px"; }, toCssValue: function(insets) { switch(typeof(insets)) { case "number": return insets + "px"; case "string": if (this._FORMATTED_PIXEL_INSETS.test(insets)) { return insets; } else { var pixelInsets = this.toPixels(insets); return pixelInsets.top + "px " + pixelInsets.right + "px " + pixelInsets.bottom + "px " + pixelInsets.left + "px"; } break; } return ""; }, toPixels: function(insets) { if (insets == null) { return this._ZERO; } else if (typeof(insets) == "number") { insets = Math.round(insets); return { top: insets, right: insets, bottom: insets, left: insets }; } insets = insets.split(" "); var map = this._INDEX_MAPS[insets.length]; return { top: Echo.Sync.Extent.toPixels(insets[map[0]], false), right: Echo.Sync.Extent.toPixels(insets[map[1]], true), bottom: Echo.Sync.Extent.toPixels(insets[map[2]], false), left: Echo.Sync.Extent.toPixels(insets[map[3]], true) }; } }; Echo.Sync.LayoutDirection = { render: function(layoutDirection, element) { if (layoutDirection) { element.dir = layoutDirection.isLeftToRight() ? "ltr" : "rtl"; } } }; Echo.Sync.TriCellTable = Core.extend({ $static: { INVERTED: 1, VERTICAL: 2, LEADING_TRAILING: 0, TRAILING_LEADING: 1, TOP_BOTTOM: 2, BOTTOM_TOP: 3, _createTablePrototype: function() { var table = document.createElement("table"); table.style.borderCollapse = "collapse"; table.style.padding = "0"; var tbody = document.createElement("tbody"); table.appendChild(tbody); return table; }, getInvertedOrientation: function(component, propertyName, defaultValue) { return this.getOrientation(component, propertyName, defaultValue) ^ this.INVERTED; }, getOrientation: function(component, propertyName, defaultValue) { var position = component.render(propertyName, defaultValue); var orientation; if (position) { switch (Echo.Sync.Alignment.getRenderedHorizontal(position, component)) { case "left": return this.LEADING_TRAILING; case "right": return this.TRAILING_LEADING; } switch (Echo.Sync.Alignment.getVertical(position, component)) { case "top": return this.TOP_BOTTOM; case "bottom": return this.BOTTOM_TOP; } } return component.getRenderLayoutDirection().isLeftToRight() ? this.TRAILING_LEADING : this.LEADING_TRAILING; } }, $load: function() { this._tablePrototype = this._createTablePrototype(); }, tableElement: null, tbodyElement: null, $construct: function(orientation0_1, margin0_1, orientation01_2, margin01_2) { this.tableElement = Echo.Sync.TriCellTable._tablePrototype.cloneNode(true); this.tbodyElement = this.tableElement.firstChild; if (orientation01_2 == null) { this._configure2(orientation0_1, margin0_1); } else { this._configure3(orientation0_1, margin0_1, orientation01_2, margin01_2); } }, _addColumn: function(tr, td) { if (td != null) { tr.appendChild(td); } }, _addRow: function(td) { if (td == null) { return; } var tr = document.createElement("tr"); tr.appendChild(td); this.tbodyElement.appendChild(tr); }, _addSpacer: function(parentElement, size, vertical) { var divElement = document.createElement("div"); if (vertical) { divElement.style.cssText = "width:1px;height:" + size + "px;font-size:1px;line-height:0;"; } else { divElement.style.cssText = "width:" + size + "px;height:1px;font-size:1px;line-height:0;"; } parentElement.appendChild(divElement); }, _configure2: function(orientation0_1, margin0_1) { this.tdElements = [document.createElement("td"), document.createElement("td")]; this.tdElements[0].style.padding = "0"; this.tdElements[1].style.padding = "0"; this.marginTdElements = []; if (margin0_1) { this.marginTdElements[0] = document.createElement("td"); this.marginTdElements[0].style.padding = "0"; if ((orientation0_1 & Echo.Sync.TriCellTable.VERTICAL) === 0) { this.marginTdElements[0].style.width = margin0_1 + "px"; this._addSpacer(this.marginTdElements[0], margin0_1, false); } else { this.marginTdElements[0].style.height = margin0_1 + "px"; this._addSpacer(this.marginTdElements[0], margin0_1, true); } } if (orientation0_1 & Echo.Sync.TriCellTable.VERTICAL) { if (orientation0_1 & Echo.Sync.TriCellTable.INVERTED) { this._addRow(this.tdElements[1]); this._addRow(this.marginTdElements[0]); this._addRow(this.tdElements[0]); } else { this._addRow(this.tdElements[0]); this._addRow(this.marginTdElements[0]); this._addRow(this.tdElements[1]); } } else { var tr = document.createElement("tr"); if (orientation0_1 & Echo.Sync.TriCellTable.INVERTED) { this._addColumn(tr, this.tdElements[1]); this._addColumn(tr, this.marginTdElements[0]); this._addColumn(tr, this.tdElements[0]); } else { this._addColumn(tr, this.tdElements[0]); this._addColumn(tr, this.marginTdElements[0]); this._addColumn(tr, this.tdElements[1]); } this.tbodyElement.appendChild(tr); } }, _configure3: function(orientation0_1, margin0_1, orientation01_2, margin01_2) { this.tdElements = []; for (var i = 0; i < 3; ++i) { this.tdElements[i] = document.createElement("td"); this.tdElements[i].style.padding = "0"; } this.marginTdElements = []; if (margin0_1 || margin01_2 != null) { if (margin0_1 && margin0_1 > 0) { this.marginTdElements[0] = document.createElement("td"); if (orientation0_1 & Echo.Sync.TriCellTable.VERTICAL) { this.marginTdElements[0].style.height = margin0_1 + "px"; this._addSpacer(this.marginTdElements[0], margin0_1, true); } else { this.marginTdElements[0].style.width = margin0_1 + "px"; this._addSpacer(this.marginTdElements[0], margin0_1, false); } } if (margin01_2 != null && margin01_2 > 0) { this.marginTdElements[1] = document.createElement("td"); if (orientation0_1 & Echo.Sync.TriCellTable.VERTICAL) { this.marginTdElements[1].style.height = margin01_2 + "px"; this._addSpacer(this.marginTdElements[1], margin01_2, true); } else { this.marginTdElements[1].style.width = margin01_2 + "px"; this._addSpacer(this.marginTdElements[1], margin01_2, false); } } } if (orientation0_1 & Echo.Sync.TriCellTable.VERTICAL) { if (orientation01_2 & Echo.Sync.TriCellTable.VERTICAL) { if (orientation01_2 & Echo.Sync.TriCellTable.INVERTED) { this._addRow(this.tdElements[2]); this._addRow(this.marginTdElements[1]); } if (orientation0_1 & Echo.Sync.TriCellTable.INVERTED) { this._addRow(this.tdElements[1]); this._addRow(this.marginTdElements[0]); this._addRow(this.tdElements[0]); } else { this._addRow(this.tdElements[0]); this._addRow(this.marginTdElements[0]); this._addRow(this.tdElements[1]); } if (!(orientation01_2 & Echo.Sync.TriCellTable.INVERTED)) { this._addRow(this.marginTdElements[1]); this._addRow(this.tdElements[2]); } } else { var rows = (margin0_1 && margin0_1 > 0) ? 3 : 2; this.tdElements[2].rowSpan = rows; if (this.marginTdElements[1]) { this.marginTdElements[1].rowSpan = rows; } var tr = document.createElement("tr"); if (orientation01_2 & Echo.Sync.TriCellTable.INVERTED) { this._addColumn(tr, this.tdElements[2]); this._addColumn(tr, this.marginTdElements[1]); if (orientation0_1 & Echo.Sync.TriCellTable.INVERTED) { this._addColumn(tr, this.tdElements[1]); } else { this._addColumn(tr, this.tdElements[0]); } } else { if (orientation0_1 & Echo.Sync.TriCellTable.INVERTED) { this._addColumn(tr, this.tdElements[1]); } else { this._addColumn(tr, this.tdElements[0]); } this._addColumn(tr, this.marginTdElements[1]); this._addColumn(tr, this.tdElements[2]); } this.tbodyElement.appendChild(tr); this._addRow(this.marginTdElements[0]); if (orientation0_1 & Echo.Sync.TriCellTable.INVERTED) { this._addRow(this.tdElements[0]); } else { this._addRow(this.tdElements[1]); } } } else { if (orientation01_2 & Echo.Sync.TriCellTable.VERTICAL) { var columns = margin0_1 ? 3 : 2; this.tdElements[2].setAttribute("colspan", columns); if (this.marginTdElements[1] != null) { this.marginTdElements[1].setAttribute("colspan", columns); } if (orientation01_2 & Echo.Sync.TriCellTable.INVERTED) { this._addRow(this.tdElements[2]); this._addRow(this.marginTdElements[1]); } tr = document.createElement("tr"); if ((orientation0_1 & Echo.Sync.TriCellTable.INVERTED) === 0) { this._addColumn(tr, this.tdElements[0]); this._addColumn(tr, this.marginTdElements[0]); this._addColumn(tr, this.tdElements[1]); } else { this._addColumn(tr, this.tdElements[1]); this._addColumn(tr, this.marginTdElements[0]); this._addColumn(tr, this.tdElements[0]); } this.tbodyElement.appendChild(tr); if (!(orientation01_2 & Echo.Sync.TriCellTable.INVERTED)) { this._addRow(this.marginTdElements[1]); this._addRow(this.tdElements[2]); } } else { tr = document.createElement("tr"); if (orientation01_2 & Echo.Sync.TriCellTable.INVERTED) { this._addColumn(tr, this.tdElements[2]); this._addColumn(tr, this.marginTdElements[1]); } if (orientation0_1 & Echo.Sync.TriCellTable.INVERTED) { this._addColumn(tr, this.tdElements[1]); this._addColumn(tr, this.marginTdElements[0]); this._addColumn(tr, this.tdElements[0]); } else { this._addColumn(tr, this.tdElements[0]); this._addColumn(tr, this.marginTdElements[0]); this._addColumn(tr, this.tdElements[1]); } if (!(orientation01_2 & Echo.Sync.TriCellTable.INVERTED)) { this._addColumn(tr, this.marginTdElements[1]); this._addColumn(tr, this.tdElements[2]); } this.tbodyElement.appendChild(tr); } } } }); Echo.Serial = { _translatorMap: { }, _translatorTypeData: [ ], addPropertyTranslator: function(className, translator) { this._translatorMap[className] = translator; }, addPropertyTranslatorByType: function(type, translator) { this._translatorTypeData.push(type, translator); }, getPropertyTranslator: function(className) { return this._translatorMap[className]; }, getPropertyTranslatorByType: function(type) { for (var i = 0; i < this._translatorTypeData.length; i += 2) { if (this._translatorTypeData[i] == type) { return this._translatorTypeData[i + 1]; } } return null; }, loadComponent: function(client, cElement, propertyMap, styleMap) { if (!cElement.nodeName == "c") { throw new Error("Element is not a component."); } var type = cElement.getAttribute("t"); var id = cElement.getAttribute("i"); var component = Echo.ComponentFactory.newInstance(type, id); var styleData = component.getLocalStyleData(); var element = cElement.firstChild; while (element) { if (element.nodeType == 1) { switch (element.nodeName) { case "c": var childComponent = this.loadComponent(client, element, propertyMap, styleMap); component.add(childComponent); break; case "p": this.loadProperty(client, element, component, styleData, propertyMap); break; case "s": component.setStyleName(element.firstChild ? element.firstChild.nodeValue : null); break; case "sr": component.setStyle(styleMap ? styleMap[element.firstChild.nodeValue] : null); break; case "e": this._loadComponentEvent(client, element, component); break; case "en": component.setEnabled(element.firstChild.nodeValue == "true"); break; case "locale": component.setLocale(element.firstChild ? element.firstChild.nodeValue : null); break; case "dir": component.setLayoutDirection(element.firstChild ? (element.firstChild.nodeValue == "rtl" ? Echo.LayoutDirection.RTL : Echo.LayoutDirection.LTR) : null); break; case "f": if (element.getAttribute("n")) { component.focusNextId = element.getAttribute("n"); } if (element.getAttribute("p")) { component.focusPreviousId = element.getAttribute("p"); } } } element = element.nextSibling; } return component; }, _loadComponentEvent: function(client, eventElement, component) { if (client.addComponentListener) { var eventType = eventElement.getAttribute("t"); client.addComponentListener(component, eventType); } }, loadProperty: function(client, pElement, object, styleData, propertyMap) { var name = pElement.getAttribute("n"); var type = pElement.getAttribute("t"); var index = pElement.getAttribute("x"); var value; if (type) { var translator = Echo.Serial._translatorMap[type]; if (!translator) { throw new Error("Translator not available for property type: " + type); } value = translator.toProperty(client, pElement); } else { if (propertyMap) { var propertyReference = pElement.getAttribute("r"); if (propertyReference) { value = propertyMap[propertyReference]; } else { value = Echo.Serial.String.toProperty(client, pElement); } } else { value = Echo.Serial.String.toProperty(client, pElement); } } if (name) { if (styleData) { if (index == null) { styleData[name] = value; } else { var indexValues = styleData[name]; if (!indexValues) { indexValues = []; styleData[name] = indexValues; } indexValues[index] = value; } } else { if (index == null) { object.set(name, value); } else { object.setIndex(name, index, value); } } } else { var propertyMethod = pElement.getAttribute("m"); if (index == null) { object[propertyMethod](value); } else { object[propertyMethod](index, value); } } }, loadStyleSheet: function(client, ssElement, propertyMap) { var styleSheet = new Echo.StyleSheet(); var ssChild = ssElement.firstChild; while (ssChild) { if (ssChild.nodeType == 1) { if (ssChild.nodeName == "s") { var style = {}; var sChild = ssChild.firstChild; while (sChild) { if (sChild.nodeType == 1) { if (sChild.nodeName == "p") { this.loadProperty(client, sChild, null, style, propertyMap); } } sChild = sChild.nextSibling; } styleSheet.setStyle(ssChild.getAttribute("n") || "", ssChild.getAttribute("t"), style); } } ssChild = ssChild.nextSibling; } return styleSheet; }, storeProperty: function(client, pElement, value) { if (value == null) { } else if (typeof (value) == "object") { var translator = null; if (value.className) { translator = this._translatorMap[value.className]; } else { translator = this.getPropertyTranslatorByType(value.constructor); } if (!translator || !translator.toXml) { return; } translator.toXml(client, pElement, value); } else { pElement.appendChild(pElement.ownerDocument.createTextNode(value.toString())); } } }; Echo.Serial.PropertyTranslator = Core.extend({ $abstract: true, $static: { toProperty: function(client, pElement) { return null; }, toXml: null } }); Echo.Serial.Null = Core.extend(Echo.Serial.PropertyTranslator, { $static: { toProperty: function(client, pElement) { return null; } }, $load: function() { Echo.Serial.addPropertyTranslator("0", this); } }); Echo.Serial.Boolean = Core.extend(Echo.Serial.PropertyTranslator, { $static: { toProperty: function(client, pElement) { return pElement.firstChild.data == "true"; } }, $load: function() { Echo.Serial.addPropertyTranslator("b", this); } }); Echo.Serial.Integer = Core.extend(Echo.Serial.PropertyTranslator, { $static: { toProperty: function(client, pElement) { return parseInt(pElement.firstChild.data, 10); } }, $load: function() { Echo.Serial.addPropertyTranslator("i", this); } }); Echo.Serial.Number = Core.extend(Echo.Serial.PropertyTranslator, { $static: { toProperty: function(client, pElement) { return parseFloat(pElement.firstChild.data); } }, $load: function() { Echo.Serial.addPropertyTranslator("n", this); } }); Echo.Serial.String = Core.extend(Echo.Serial.PropertyTranslator, { $static: { toProperty: function(client, pElement) { var textNode = pElement.firstChild; if (!textNode) { return ""; } var text = textNode.data; while (textNode.nextSibling) { textNode = textNode.nextSibling; text += textNode.data; } return text; } }, $load: function() { Echo.Serial.addPropertyTranslator("s", this); } }); Echo.Serial.Date = Core.extend(Echo.Serial.PropertyTranslator, { $static: { _expr: /(\d{4})\.(\d{2}).(\d{2})/, toProperty: function(client, pElement) { var value = Echo.Serial.String.toProperty(client, pElement); var result = this._expr.exec(value); if (!result) { return null; } return new Date(result[1], parseInt(result[2], 10) - 1, result[3]); }, toXml: function(client, pElement, value) { pElement.appendChild(pElement.ownerDocument.createTextNode( value.getFullYear() + "." + (value.getMonth() + 1) + "." + value.getDate())); } }, $load: function() { Echo.Serial.addPropertyTranslator("d", this); Echo.Serial.addPropertyTranslatorByType(Date, this); } }); Echo.Serial.Map = Core.extend(Echo.Serial.PropertyTranslator, { $static: { toProperty: function(client, pElement) { var mapObject = {}; var element = pElement.firstChild; while (element) { if (element.nodeType != 1) { continue; } Echo.Serial.loadProperty(client, element, null, mapObject, null); element = element.nextSibling; } return mapObject; } }, $load: function() { Echo.Serial.addPropertyTranslator("m", this); } }); Echo.Serial.Alignment = Core.extend(Echo.Serial.PropertyTranslator, { $static: { _HORIZONTAL_MAP: { "leading": "leading", "trailing": "trailing", "left": "left", "center": "center", "right": "right" }, _VERTICAL_MAP: { "top": "top", "center": "middle", "bottom": "bottom" }, toProperty: function(client, pElement) { var element = Core.Web.DOM.getChildElementByTagName(pElement, "a"); var h = this._HORIZONTAL_MAP[element.getAttribute("h")]; var v = this._VERTICAL_MAP[element.getAttribute("v")]; if (h) { if (v) { return { horizontal: h, vertical: v }; } return h; } if (v) { return v; } return null; } }, $load: function() { Echo.Serial.addPropertyTranslator("Alignment", this); Echo.Serial.addPropertyTranslator("AL", this); } }); Echo.Serial.Border = Core.extend(Echo.Serial.PropertyTranslator, { $static: { toProperty: function(client, pElement) { if (pElement.firstChild.nodeType == 3) { return pElement.firstChild.data; } else if (pElement.getAttribute("v")) { return pElement.getAttribute("v"); } else { var element = Core.Web.DOM.getChildElementByTagName(pElement, "b"); var border = {}; var value = element.getAttribute("t"); if (value) { border.top = value; value = element.getAttribute("r"); if (value) { border.right = value; value = element.getAttribute("b"); if (value) { border.bottom = value; value = element.getAttribute("l"); if (value) { border.left = value; } } } } else { throw new Error("Invalid multi-sided border: no sides set."); } return border; } } }, $load: function() { Echo.Serial.addPropertyTranslator("Border", this); Echo.Serial.addPropertyTranslator("BO", this); } }); Echo.Serial.FillImage = Core.extend(Echo.Serial.PropertyTranslator, { $static: { parseElement: function(client, fiElement) { var url = fiElement.getAttribute("u"); if (client.decompressUrl) { url = client.decompressUrl(url); } var repeat = fiElement.getAttribute("r"); var x = fiElement.getAttribute("x"); var y = fiElement.getAttribute("y"); if (repeat || x || y) { return { url: url, repeat: repeat, x: x, y: y }; } else { return url; } }, toProperty: function(client, pElement) { var element = Core.Web.DOM.getChildElementByTagName(pElement, "fi"); return this.parseElement(client, element); } }, $load: function() { Echo.Serial.addPropertyTranslator("FillImage", this); Echo.Serial.addPropertyTranslator("FI", this); } }); Echo.Serial.FillImageBorder = Core.extend(Echo.Serial.PropertyTranslator, { $static: { _NAMES: [ "topLeft", "top", "topRight", "left", "right", "bottomLeft", "bottom", "bottomRight" ], _parseElement: function(client, fibElement) { var fillImageBorder = { contentInsets: fibElement.getAttribute("ci") ? fibElement.getAttribute("ci") : null, borderInsets: fibElement.getAttribute("bi") ? fibElement.getAttribute("bi") : null, color: fibElement.getAttribute("bc") }; var element = fibElement.firstChild; var i = 0; while(element) { if (element.nodeType == 1) { if (element.nodeName == "fi") { fillImageBorder[this._NAMES[i]] = Echo.Serial.FillImage.parseElement(client, element); ++i; } else if (element.nodeName == "null-fi") { ++i; } } element = element.nextSibling; } if (!(i === 0 || i == 8)) { throw new Error("Invalid FillImageBorder image count: " + i); } return fillImageBorder; }, toProperty: function(client, pElement) { var element = Core.Web.DOM.getChildElementByTagName(pElement, "fib"); return Echo.Serial.FillImageBorder._parseElement(client, element); } }, $load: function() { Echo.Serial.addPropertyTranslator("FillImageBorder", this); Echo.Serial.addPropertyTranslator("FIB", this); } }); Echo.Serial.Font = Core.extend(Echo.Serial.PropertyTranslator, { $static: { toProperty: function(client, pElement) { var element = Core.Web.DOM.getChildElementByTagName(pElement, "f"); var tfElements = Core.Web.DOM.getChildElementsByTagName(element, "tf"); var font = { }; if (tfElements.length > 1) { font.typeface = []; for (var i = 0; i < tfElements.length; ++i) { font.typeface[i] = tfElements[i].firstChild.data; } } else if (tfElements.length == 1) { font.typeface = tfElements[0].firstChild.data; } var size = element.getAttribute("sz"); if (size) { font.size = size; } if (element.getAttribute("bo")) { font.bold = true; } if (element.getAttribute("it")) { font.italic = true; } if (element.getAttribute("un")) { font.underline = true; } if (element.getAttribute("ov")) { font.overline = true; } if (element.getAttribute("lt")) { font.lineThrough = true; } return font; } }, $load: function() { Echo.Serial.addPropertyTranslator("Font", this); Echo.Serial.addPropertyTranslator("F", this); } }); Echo.Serial.ImageReference = Core.extend(Echo.Serial.PropertyTranslator, { $static: { toProperty: function(client, pElement) { var url; if (pElement.firstChild.nodeType == 1) { var iElement = pElement.firstChild; url = iElement.firstChild.data; if (client.decompressUrl) { url = client.decompressUrl(url); } var width = iElement.getAttribute("w"); width = width ? width : null; var height = iElement.getAttribute("h"); height = height ? height : null; if (width || height) { return { url: url, width: width, height: height }; } else { return url; } } else { url = pElement.firstChild.data; return client.decompressUrl ? client.decompressUrl(url) : url; } } }, $load: function() { Echo.Serial.addPropertyTranslator("ImageReference", this); Echo.Serial.addPropertyTranslator("I", this); } }); Echo.Serial.LayoutData = Core.extend(Echo.Serial.PropertyTranslator, { $static: { toProperty: function(client, pElement) { var layoutData = {}; var element = pElement.firstChild; while (element) { if (element.nodeType == 1) { if (element.nodeName == "p") { Echo.Serial.loadProperty(client, element, null, layoutData); } } element = element.nextSibling; } return layoutData; } }, $load: function() { Echo.Serial.addPropertyTranslator("LayoutData", this); Echo.Serial.addPropertyTranslator("L", this); } }); Echo.Client = Core.extend({ $static: { DEFAULT_CONFIGURATION: { "StopError.Message": "This application has been stopped due to an error.", "WaitIndicator.Text": "Please wait...", "Action.Continue": "Continue", "Action.Restart": "Restart Application" }, STYLE_CRITICAL: 0, STYLE_MESSAGE: 1, _activeClients: [], _globalWindowResizeListener: function(e) { for (var i = 0; i < Echo.Client._activeClients.length; ++i) { Echo.Client._activeClients[i]._windowResizeListener(e); } }, windowId: null }, $load: function() { Core.Web.DOM.addEventListener(window, "resize", this._globalWindowResizeListener, false); var re = /EchoWindowId=([0-9a-f]*\.[0-9a-f]*);/i; var match = re.exec(window.name || ""); this.windowId = match && match[1]; if (!this.windowId) { this.windowId = new Date().getTime().toString(16) + "." + parseInt(Math.random() * 0x100000000, 10).toString(16); window.name = (window.name || "") + ";EchoWindowId=" + this.windowId + ";"; } }, configuration: null, designMode: false, domainElement: null, application: null, _lastInputRestrictionId: 0, _inputRestrictionCount: 0, _inputRestrictionListeners: null, _inputRescriptionMap: null, _keyFocusedComponentId: null, _lastKeyCode: null, _processKeyRef: null, _waitIndicatorActive: false, _processApplicationFocusRef: null, parent: null, _waitIndicator: null, _preWaitIndicatorDelay: 500, _waitIndicatorRunnable: null, _failed: false, $construct: function() { this.configuration = { }; for (var x in Echo.Client.DEFAULT_CONFIGURATION) { this.configuration[x] = Echo.Client.DEFAULT_CONFIGURATION[x]; } this._inputRestrictionMap = { }; this._processKeyRef = Core.method(this, this._processKey); this._processApplicationFocusRef = Core.method(this, this._processApplicationFocus); this._waitIndicator = new Echo.Client.DefaultWaitIndicator(); this._waitIndicatorRunnable = new Core.Web.Scheduler.MethodRunnable(Core.method(this, this._waitIndicatorActivate), this._preWaitIndicatorDelay, false); }, $abstract: true, $virtual: { getResourceUrl: function(packageName, resourceName) { if (this.parent) { return this.parent.getResourceUrl(packageName, resourceName); } else { return null; } }, verifyInput: function(component) { if (this._inputRestrictionCount !== 0) { return false; } if (component) { return component.isActive(); } else { return this.application.isActive(); } }, dispose: function() { this.configure(null, null); this._setWaitVisible(false); } }, addElement: function(element) { Core.Web.Event.add(element, "keypress", this._processKeyRef, false); Core.Web.Event.add(element, "keydown", this._processKeyRef, false); Core.Web.Event.add(element, "keyup", this._processKeyRef, false); }, configure: function(application, domainElement) { if (this.application) { Core.Arrays.remove(Echo.Client._activeClients, this); this.removeElement(this.domainElement); this.application.removeListener("focus", this._processApplicationFocusRef); this.application.doDispose(); this.application.client = null; } this.application = application; this.domainElement = domainElement; if (this.application) { this.application.client = this; this.application.doInit(); this.application.addListener("focus", this._processApplicationFocusRef); this.addElement(this.domainElement); Echo.Client._activeClients.push(this); } }, createInputRestriction: function() { this._setWaitVisible(true); var id = (++this._lastInputRestrictionId).toString(); ++this._inputRestrictionCount; this._inputRestrictionMap[id] = true; return id; }, displayError: function(parentElement, message, detail, actionText, actionFunction, style) { parentElement = parentElement || document.body; var restriction = this.createInputRestriction(); this._setWaitVisible(false); var blackoutDiv = document.createElement("div"); blackoutDiv.style.cssText = "position:absolute;z-index:32766;width:100%;height:100%;background-color:#000000;opacity:0.75"; if (Core.Web.Env.PROPRIETARY_IE_OPACITY_FILTER_REQUIRED) { blackoutDiv.style.filter = "alpha(opacity=75)"; } parentElement.appendChild(blackoutDiv); var div = document.createElement("div"); div.style.cssText = "position:absolute;z-index:32767;width:100%;height:100%;overflow:hidden;"; parentElement.appendChild(div); var contentDiv = document.createElement("div"); contentDiv.style.cssText = "color:#ffffff;padding:20px 40px 0px;" + (style === Echo.Client.STYLE_MESSAGE ? "border-bottom:4px solid #1f1faf;background-color:#1f1f5f" : "border-bottom:4px solid #af1f1f;background-color:#5f1f1f"); if (message) { var messageDiv = document.createElement("div"); messageDiv.style.cssText = "font-weight: bold; margin-bottom:20px;"; messageDiv.appendChild(document.createTextNode(message)); contentDiv.appendChild(messageDiv); } if (detail) { var detailDiv = document.createElement("div"); detailDiv.style.cssText = "max-height:10em;overflow:auto;margin-bottom:20px;"; detailDiv.appendChild(document.createTextNode(detail)); contentDiv.appendChild(detailDiv); } div.appendChild(contentDiv); if (actionText) { var actionDiv = document.createElement("div"); actionDiv.tabIndex = "0"; actionDiv.style.cssText = "margin-bottom:20px;cursor:pointer;font-weight:bold;padding:2px 10px;" + (style === Echo.Client.STYLE_MESSAGE ? "border: 1px outset #2f2faf;background-color:#2f2faf;" : "border: 1px outset #af2f2f;background-color:#af2f2f;"); actionDiv.appendChild(document.createTextNode(actionText)); contentDiv.appendChild(actionDiv); var listener = Core.method(this, function(e) { if (e.type != "keypress" || e.keyCode == 13) { try { Core.Web.DOM.removeEventListener(actionDiv, "click", listener, false); Core.Web.DOM.removeEventListener(actionDiv, "keypress", listener, false); div.parentNode.removeChild(div); blackoutDiv.parentNode.removeChild(blackoutDiv); this.removeInputRestriction(restriction); } finally { if (actionFunction) { actionFunction(); } } } }); Core.Web.DOM.addEventListener(actionDiv, "click", listener, false); Core.Web.DOM.addEventListener(actionDiv, "keypress", listener, false); Core.Web.DOM.focusElement(actionDiv); } var closeDiv = document.createElement("div"); closeDiv.style.cssText = "position:absolute;top:2px;right:8px;color:#ffffff;font-weight:bold;cursor:pointer;"; closeDiv.appendChild(document.createTextNode("x")); Core.Web.DOM.addEventListener(closeDiv, "click", Core.method(this, function() { blackoutDiv.parentNode.removeChild(blackoutDiv); div.parentNode.removeChild(div); }), false); div.appendChild(closeDiv); }, exec: function(requiredLibraries, f) { var restriction = this.createInputRestriction(); Core.Web.Library.exec(requiredLibraries, Core.method(this, function(e) { if (e && !e.success) { this.fail("Cannot install library: " + e.url + " Exception: " + e.ex); return; } this.removeInputRestriction(restriction); f(); })); }, fail: function(detail) { if (this._failed) { return; } this._failed = true; var element = this.domainElement; try { this.dispose(); } finally { if (this.configuration["StopError.URI"]) { window.location.href = this.configuration["StopError.URI"]; } else { this.displayError(element, this.configuration["StopError.Message"], detail, this.configuration["Action.Restart"], function() { window.location.reload(); }); } } }, forceRedraw: function() { if (this.parent) { this.parent.forceRedraw(); } else if (Core.Web.Env.QUIRK_IE_BLANK_SCREEN) { if (this.domainElement && this.domainElement.offsetHeight === 0) { var displayState = document.documentElement.style.display || ""; document.documentElement.style.display = "none"; document.documentElement.style.display = displayState; } } }, getWaitIndicator: function() { return this._waitIndicator; }, _processApplicationFocus: function(e) { var focusedComponent = this.application.getFocusedComponent(); if (focusedComponent && focusedComponent.peer && focusedComponent.peer.renderFocus) { focusedComponent.peer.renderFocus(); } }, _processKey: function(e) { var up = e.type == "keyup", press = e.type == "keypress", component = this.application.getFocusedComponent(), bubble = true, keyEvent = null, keyCode; if (press) { keyCode = this._lastKeyCode; } else { keyCode = this._lastKeyCode = Core.Web.Key.translateKeyCode(e.keyCode); } if (!up) { if (keyCode == 8) { var nodeName = e.target.nodeName ? e.target.nodeName.toLowerCase() : null; if (nodeName != "input" && nodeName != "textarea") { Core.Web.DOM.preventEventDefault(e); } } else if (!press && keyCode == 9) { this.application.focusNext(e.shiftKey); Core.Web.DOM.preventEventDefault(e); } if (press && Core.Web.Env.QUIRK_KEY_PRESS_FIRED_FOR_SPECIAL_KEYS && !e.charCode) { return true; } } if (!component) { return true; } if (up || press) { if (this._keyFocusedComponentId != component.renderId) { return true; } } else { this._keyFocusedComponentId = component.renderId; } var eventMethod = press ? "clientKeyPress" : (up ? "clientKeyUp" : "clientKeyDown"); while (component && bubble) { if (component.peer && component.peer[eventMethod]) { if (!keyEvent) { keyEvent = { type: e.type, source: this, keyCode: keyCode, domEvent: e }; if (press) { keyEvent.charCode = Core.Web.Env.QUIRK_KEY_CODE_IS_CHAR_CODE ? e.keyCode : e.charCode; } } bubble = component.peer[eventMethod](keyEvent); } component = component.parent; } return true; }, processUpdates: function() { var ir = null; try { ir = this.createInputRestriction(); Echo.Render.processUpdates(this); this.removeInputRestriction(ir); this.forceRedraw(); } catch (ex) { if (ex.lineNumber) { Core.Debug.consoleWrite("Reported Line #: " + ex.lineNumber); Core.Debug.consoleWrite("Evaluated Line #: " + (ex.lineNumber - Core.Web.Library.evalLine) + " (if evaluated script)"); } if (ex.stack) { Core.Debug.consoleWrite("Exception: " + ex + ", Stack Trace: " + ex.stack); } this.fail("Exception during Client.processUpdates(): " + ex.message); throw (ex); } }, registerRestrictionListener: function(component, l) { if (!this._inputRestrictionListeners) { this._inputRestrictionListeners = { }; } this._inputRestrictionListeners[component.renderId] = l; }, removeInputRestriction: function(id) { if (this._inputRestrictionMap[id] === undefined) { return; } delete this._inputRestrictionMap[id]; --this._inputRestrictionCount; if (this._inputRestrictionCount === 0) { this._setWaitVisible(false); if (this._inputRestrictionListeners) { var listeners = this._inputRestrictionListeners; this._inputRestrictionListeners = null; for (var x in listeners) { listeners[x](); } } } }, _setWaitVisible: function(visible) { if (visible) { if (!this._waitIndicatorActive) { this._waitIndicatorActive = true; Core.Web.Scheduler.add(this._waitIndicatorRunnable); } } else { if (this._waitIndicatorActive) { this._waitIndicatorActive = false; Core.Web.Scheduler.remove(this._waitIndicatorRunnable); this._waitIndicator.deactivate(this); this.forceRedraw(); } } }, setWaitIndicator: function(waitIndicator) { if (this._waitIndicator) { this._setWaitVisible(false); if (this._waitIndicator.dispose) { this._waitIndicator.dispose(this); } } this._waitIndicator = waitIndicator; }, removeElement: function(element) { Core.Web.Event.remove(element, "keypress", this._processKeyRef, false); Core.Web.Event.remove(element, "keydown", this._processKeyRef, false); Core.Web.Event.remove(element, "keyup", this._processKeyRef, false); }, _waitIndicatorActivate: function() { this._waitIndicator.activate(this); }, _windowResizeListener: function(e) { if (this.application.rootComponent.peer) { Echo.Render.notifyResize(this.application.rootComponent); } } }); Echo.Client.Timer = Core.extend({ _times: null, _labels: null, $construct: function() { this._times = [new Date().getTime()]; this._labels = ["Start"]; }, mark: function(label) { this._times.push(new Date().getTime()); this._labels.push(label); }, toString: function() { var out = ""; for (var i = 1; i < this._times.length; ++i) { var time = this._times[i] - this._times[i - 1]; out += this._labels[i] + ":" + time + " "; } out += "TOT:" + (this._times[this._times.length - 1] - this._times[0]) + "ms"; return out; } }); Echo.Client.WaitIndicator = Core.extend({ $abstract: { activate: function(client) { }, deactivate: function(client) { } }, $virtual: { dispose: null } }); Echo.Client.DefaultWaitIndicator = Core.extend(Echo.Client.WaitIndicator, { $construct: function() { this._divElement = document.createElement("div"); this._divElement.style.cssText = "display: none;z-index:32000;position:absolute;top:30px;right:30px;" + "width:200px;padding:20px;border:1px outset #abcdef;background-color:#abcdef;color:#000000;text-align:center;"; this._textNode = document.createTextNode(""); this._divElement.appendChild(this._textNode); this._fadeRunnable = new Core.Web.Scheduler.MethodRunnable(Core.method(this, this._tick), 50, true); document.body.appendChild(this._divElement); }, activate: function(client) { if (client.configuration["WaitIndicator.Background"]) { this._divElement.style.backgroundColor = client.configuration["WaitIndicator.Background"]; this._divElement.style.borderColor = client.configuration["WaitIndicator.Background"]; } if (client.configuration["WaitIndicator.Foreground"]) { this._divElement.style.color = client.configuration["WaitIndicator.Foreground"]; } this._textNode.nodeValue = client.configuration["WaitIndicator.Text"]; this._divElement.style.display = "block"; Core.Web.Scheduler.add(this._fadeRunnable); this._opacity = 0; }, deactivate: function(client) { this._divElement.style.display = "none"; Core.Web.Scheduler.remove(this._fadeRunnable); }, dispose: function(client) { if (this._divElement && this._divElement.parentNode) { this._divElement.parentNode.removeChild(this._divElement); } this._divElement = null; this._textNode = null; }, _tick: function() { ++this._opacity; var opacityValue = 1 - (Math.abs((this._opacity % 40) - 20) / 30); if (!Core.Web.Env.PROPRIETARY_IE_OPACITY_FILTER_REQUIRED) { this._divElement.style.opacity = opacityValue; } } }); Echo.RemoteClient = Core.extend(Echo.Client, { $static: { libraryServerUrl: null, initialized: false, DEFAULT_CONFIGURATION: { "NetworkError.Message": "A network error has occurred, please try again.", "SessionExpiration.Message": "Your session has expired.", "Resync.Message": "This window was not synchronized with the server and has been reset. " + "Please try your last request again." }, init: function() { if (Echo.RemoteClient.initialized) { return; } Echo.RemoteClient.initialized = true; Core.Web.init(); if (Core.Web.Env.ENGINE_MSHTML) { document.documentElement.style.overflow = "hidden"; } } }, _uiid: null, _serverUrl: null, _syncRequested: false, _transactionInProgress: false, _inputRestrictionId: null, _processClientUpdateRef: null, _processClientEventRef: null, _urlMappings: null, _commandQueue: null, _clientMessage: null, _asyncManager: null, _initialized: false, _clientFocusedComponent: null, _serverFocusedComponent: null, transactionId: 0, $construct: function(serverUrl, initId) { Echo.RemoteClient.init(); Echo.Client.call(this); for (var x in Echo.RemoteClient.DEFAULT_CONFIGURATION) { this.configuration[x] = Echo.RemoteClient.DEFAULT_CONFIGURATION[x]; } this._serverUrl = serverUrl; this._processClientUpdateRef = Core.method(this, this._processClientUpdate); this._processClientEventRef = Core.method(this, this._processClientEvent); this._commandQueue = null; this._clientMessage = new Echo.RemoteClient.ClientMessage(this, initId); this._asyncManager = new Echo.RemoteClient.AsyncManager(this); }, addComponentListener: function(component, eventType) { component.addListener(eventType, this._processClientEventRef); }, decompressUrl: function(url) { var urlTokens = url.split("!"); if (urlTokens[0]) { return url; } else { if (urlTokens.length != 3) { throw new Error("Invalid encoded URL: " + url); } var replacementValue = this._urlMappings[urlTokens[1]]; if (replacementValue == null) { throw new Error("Invalid URL shorthand key \"" + urlTokens[1] + "\" in URL: " + url); } return replacementValue + urlTokens[2]; } }, _isAsyncEvent: function(eventType) { if(eventType && typeof eventType === "string") { if(eventType.length > 5) { return (eventType.substring(0,5) === 'async_'); } } return false; }, _enqueueCommand: function(commandPeer, commandData) { if (this._commandQueue == null) { this._commandQueue = []; } this._commandQueue.push(commandPeer, commandData); }, _executeCommands: function() { if (this._commandQueue) { for (var i = 0; i < this._commandQueue.length; i += 2) { this._commandQueue[i].execute(this, this._commandQueue[i + 1]); } this._commandQueue = null; } }, _getLibraryServiceUrl: function(serviceId) { if (!Echo.RemoteClient._libraryServerUrl) { Echo.RemoteClient._libraryServerUrl = this._serverUrl; } if (this._uiid == null) { return Echo.RemoteClient._libraryServerUrl + "?sid=" + serviceId; } else { return Echo.RemoteClient._libraryServerUrl + "?sid=" + serviceId + "&uiid=" + this._uiid; } }, getResourceUrl: function(packageName, resourceName) { return this.getServiceUrl("Echo.Resource") + "&pkg=" + packageName + "&res=" + resourceName; }, getServiceUrl: function(serviceId) { if (this._uiid == null) { return this._serverUrl + "?sid=" + serviceId; } else { return this._serverUrl + "?sid=" + serviceId + "&uiid=" + this._uiid; } }, _handleInvalidResponse: function(e) { var detail = null; if (e.exception) { detail = e.exception.toString(); } else if (e.source.getResponseText()) { if (e.source.getResponseText().indexOf("!*! Session Expired") != -1) { this._handleSessionExpiration(); return; } else { detail = e.source.getResponseText(); } } else { this._handleNetworkError(); return; } this.fail(detail); }, _handleSessionExpiration: function() { var element = this.domainElement; try { this.dispose(); } finally { if (this.configuration["SessionExpiration.Restart"]) { window.location.reload(); } else if (this.configuration["SessionExpiration.URI"]) { window.location.href = this.configuration["SessionExpiration.URI"]; } else { this.displayError(element, this.configuration["SessionExpiration.Message"], null, this.configuration["Action.Continue"], function() { window.location.reload(); }, Echo.Client.STYLE_MESSAGE); } } }, _handleNetworkError: function() { var element = this.domainElement; try { this.dispose(); } finally { this.displayError(element, this.configuration["NetworkError.Message"], null, this.configuration["Action.Continue"], function() { window.location.reload(); }, Echo.Client.STYLE_MESSAGE); } }, init: function(initialResponseDocument) { this._uiid = initialResponseDocument.documentElement.getAttribute("u"); if (this._uiid === "") { this._uiid = null; } this._urlMappings = {}; this._urlMappings.I = this._serverUrl + "?" + (this._uiid == null ? "" : "uiid=" + this._uiid + "&") + "sid=Echo.Image&iid="; var domainElementId = initialResponseDocument.documentElement.getAttribute("root"); var domainElement = document.getElementById(domainElementId); if (!domainElement) { throw new Error("Cannot find domain element: " + domainElementId); } var application = new Echo.Application(); application.addListener("componentUpdate", this._processClientUpdateRef); this.configure(application, domainElement); this._initialized = true; }, _processClientEvent: function(e) { if (this._syncRequested || this._transactionInProgress) { return; } this._clientMessage.setEvent(e.source.renderId, e.type, e.data); if (!this._inputRestrictionId && !this._isAsyncEvent(e.type)) { this._inputRestrictionId = this.createInputRestriction(); } this._syncRequested = true; Core.Web.Scheduler.run(Core.method(this, this.sync)); }, _processClientUpdate: function(e) { if (this._transactionInProgress) { return; } var stored = false; if (e.parent.peer.storeProperty) { stored = e.parent.peer.storeProperty(this._clientMessage, e.propertyName); } if (!stored) { this._clientMessage.storeProperty(e.parent.renderId, e.propertyName, e.newValue); } }, _processSyncComplete: function(e) { if (Echo.Client.profilingTimer) { Echo.Client.profilingTimer.mark("ser"); } this.processUpdates(); this._executeCommands(); this.application.addListener("componentUpdate", this._processClientUpdateRef); this._transactionInProgress = false; this.removeInputRestriction(this._inputRestrictionId); this._inputRestrictionId = null; if (this._serverFocusedComponent) { this.application.setFocusedComponent(this._serverFocusedComponent); } if (Echo.Client.profilingTimer) { Core.Debug.consoleWrite(Echo.Client.profilingTimer + " /pc:" + Echo.Render._loadedPeerCount); Echo.Client.profilingTimer = null; } if (e.source.resync) { this.displayError(this.domainElement, this.configuration["Resync.Message"], null, this.configuration["Action.Continue"], null, Echo.Client.STYLE_MESSAGE); } }, _processSyncResponse: function(e) { var responseDocument = e.source.getResponseXml(); if (!e.valid || !responseDocument || !responseDocument.documentElement) { this._handleInvalidResponse(e); return; } if (!this._initialized) { this.init(responseDocument); } Echo.Client.profilingTimer = new Echo.Client.Timer(); this.application.removeListener("componentUpdate", this._processClientUpdateRef); var serverMessage = new Echo.RemoteClient.ServerMessage(this, responseDocument); this.transactionId = serverMessage.transactionId; serverMessage.addCompletionListener(Core.method(this, this._processSyncComplete)); serverMessage.process(); }, removeComponentListener: function(component, eventType) { component.removeListener(eventType, this._processClientEventRef); }, sync: function() { if (this._transactionInProgress) { throw new Error("Attempt to invoke client/server synchronization while another transaction is in progress; " + "event data: componentId=" + this._clientMessage._eventComponentId + " eventType=" + this._clientMessage._eventType + " eventData=" + this._clientMessage._eventData); } this._clientFocusedComponent = this.application ? this.application.getFocusedComponent() : null; this._serverFocusedComponent = null; this._transactionInProgress = true; this._syncRequested = false; if (!this._inputRestrictionId && !this._isAsyncEvent(this._clientMessage._eventType)) { this._inputRestrictionId = this.createInputRestriction(); } this._asyncManager._stop(); this._syncInitTime = new Date().getTime(); var conn = new Core.Web.HttpConnection(this.getServiceUrl("Echo.Sync"), "POST", this._clientMessage._renderXml(), "text/xml;charset=utf-8"); this._clientMessage = new Echo.RemoteClient.ClientMessage(this, null); conn.addResponseListener(Core.method(this, this._processSyncResponse)); conn.connect(); } }); Echo.RemoteClient.DirectiveProcessor = Core.extend({ client: null, $construct: function(client) { this.client = client; }, $abstract: { process: function(dirElement) { } } }); Echo.RemoteClient.AsyncManager = Core.extend({ $static: { MAX_CONNECT_ATTEMPTS: 3 }, _client: null, _failedConnectAttempts: null, _runnable: null, $construct: function(client) { this._client = client; this._runnable = new Core.Web.Scheduler.MethodRunnable(Core.method(this, this._pollServerForUpdates), 1000, false); }, _pollServerForUpdates: function() { var conn = new Core.Web.HttpConnection(this._client.getServiceUrl("Echo.AsyncMonitor"), "GET"); conn.addResponseListener(Core.method(this, this._processPollResponse)); conn.connect(); }, _processPollResponse: function(e) { var responseDocument = e.source.getResponseXml(); if (e.valid && responseDocument && responseDocument.documentElement) { this._failedConnectAttempts = 0; if (responseDocument.documentElement.getAttribute("request-sync") == "true") { if (!this._client._transactionInProgress && !this._client._syncRequested) { this._client.sync(); } return; } } else if (++this._failedConnectAttempts >= Echo.RemoteClient.AsyncManager.MAX_CONNECT_ATTEMPTS) { this._client._handleInvalidResponse(e); this._failedConnectAttempts = 0; return; } Core.Web.Scheduler.add(this._runnable); }, _setInterval: function(interval) { this._runnable.timeInterval = interval; }, _start: function() { this._failedConnectAttempts = 0; Core.Web.Scheduler.add(this._runnable); }, _stop: function() { Core.Web.Scheduler.remove(this._runnable); } }); Echo.RemoteClient.ClientMessage = Core.extend({ $static: { _ClientProperties: Core.extend({ _element: null, _clientMessage: null, $construct: function(clientMessage, map) { this._element = clientMessage._document.createElement("dir"); this._element.setAttribute("proc", "ClientProperties"); clientMessage._document.documentElement.appendChild(this._element); this._clientMessage = clientMessage; if (map) { for (var key in map) { this._add(key, map[key]); } } }, _add: function(key, value) { if (value == null) { return; } var element = this._clientMessage._document.createElement("p"); element.setAttribute("n", key); Echo.Serial.storeProperty(this._clientMessage._client, element, value); this._element.appendChild(element); } }) }, _client: null, _componentIdToPropertyMap: null, _eventComponentId: null, _eventType: null, _eventData: null, _document: null, $construct: function(client, initId) { this._client = client; this._componentIdToPropertyMap = {}; this._document = Core.Web.DOM.createDocument("http://www.nextapp.com/products/echo/svrmsg/clientmessage.3.0", "cmsg"); if (initId != null) { this._document.documentElement.setAttribute("t", "init"); this._document.documentElement.setAttribute("w", Echo.Client.windowId); this._document.documentElement.setAttribute("ii", initId); this._renderClientProperties(); } }, _renderCFocus: function() { if (!this._client.application) { return; } var focusedComponent = this._client.application.getFocusedComponent(); if (focusedComponent && focusedComponent.renderId.substring(0,2) == "C.") { var cFocusElement = this._document.createElement("dir"); cFocusElement.setAttribute("proc", "CFocus"); var focusElement = this._document.createElement("focus"); focusElement.setAttribute("i", focusedComponent.renderId); cFocusElement.appendChild(focusElement); this._document.documentElement.appendChild(cFocusElement); } }, _renderCSync: function() { var cSyncElement = this._document.createElement("dir"); cSyncElement.setAttribute("proc", "CSync"); if (this._eventType) { var eElement = this._document.createElement("e"); eElement.setAttribute("t", this._eventType); eElement.setAttribute("i", this._eventComponentId); if (this._eventData != null) { Echo.Serial.storeProperty(this._client, eElement, this._eventData); } cSyncElement.appendChild(eElement); } for (var componentId in this._componentIdToPropertyMap) { var propertyMap = this._componentIdToPropertyMap[componentId]; var component = this._client.application.getComponentByRenderId(componentId); for (var propertyName in propertyMap) { var propertyValue = propertyMap[propertyName]; var pElement = this._document.createElement("p"); pElement.setAttribute("i", componentId); pElement.setAttribute("n", propertyName); Echo.Serial.storeProperty(this._client, pElement, propertyValue); cSyncElement.appendChild(pElement); } } this._document.documentElement.appendChild(cSyncElement); }, _renderClientProperties: function() { var env = Core.Web.Env; var cp = new Echo.RemoteClient.ClientMessage._ClientProperties(this, { screenWidth: screen.width, screenHeight: screen.height, screenColorDepth: screen.colorDepth, utcOffset: 0 - parseInt(new Date().getTimezoneOffset(), 10), navigatorAppName: window.navigator.appName, navigatorAppVersion: window.navigator.appVersion, navigatorAppCodeName: window.navigator.appCodeName, navigatorCookieEnabled: window.navigator.cookieEnabled, navigatorJavaEnabled: window.navigator.javaEnabled(), navigatorLanguage: window.navigator.language ? window.navigator.language : window.navigator.userLanguage, navigatorPlatform: window.navigator.platform, navigatorUserAgent: window.navigator.userAgent, browserChrome: env.BROWSER_CHROME, browserOpera: env.BROWSER_OPERA, browserSafari: env.BROWSER_SAFARI, browserKonqueror: env.BROWSER_KONQUEROR, browserMozillaFirefox: env.BROWSER_FIREFOX, browserMozilla: env.BROWSER_MOZILLA, browserInternetExplorer: env.BROWSER_INTERNET_EXPLORER, browserVersionMajor: env.BROWSER_VERSION_MAJOR, browserVersionMinor: env.BROWSER_VERSION_MINOR, engineGecko: env.ENGINE_GECKO, engineKHTML: env.ENGINE_KHTML, engineMSHTML: env.ENGINE_MSHTML, enginePresto: env.ENGINE_PRESTO, engineWebKit: env.ENGINE_WEBKIT, engineVersionMajor: env.ENGINE_VERSION_MAJOR, engineVersionMinor: env.ENGINE_VERSION_MINOR }); }, _renderXml: function() { if (!this._rendered) { this._renderCFocus(); this._renderCSync(); this._document.documentElement.setAttribute("i", this._client.transactionId); this._rendered = true; } return this._document; }, setEvent: function(componentId, eventType, eventData) { if (this._eventComponentId) { return; } this._eventComponentId = componentId; this._eventType = eventType; this._eventData = eventData; }, storeProperty: function(componentId, propertyName, propertyValue) { var propertyMap = this._componentIdToPropertyMap[componentId]; if (!propertyMap) { propertyMap = {}; this._componentIdToPropertyMap[componentId] = propertyMap; } propertyMap[propertyName] = propertyValue; } }); Echo.RemoteClient.CommandExec = Core.extend({ $abstract: true, $static: { execute: function(client, commandData) { } } }); Echo.RemoteClient.ServerMessage = Core.extend({ $static: { _processorClasses: { }, addProcessor: function(name, processor) { this._processorClasses[name] = processor; } }, client: null, transactionId: null, document: null, _listenerList: null, _processorInstances: null, resync: false, $construct: function(client, xmlDocument) { this.client = client; this.document = xmlDocument; this._listenerList = new Core.ListenerList(); this._processorInstances = { }; this.transactionId = xmlDocument.documentElement.getAttribute("i"); if (xmlDocument.documentElement.getAttribute("resync")) { this.resync = true; } }, addCompletionListener: function(l) { this._listenerList.addListener("completion", l); }, process: function() { var libsElement = Core.Web.DOM.getChildElementByTagName(this.document.documentElement, "libs"); if (libsElement) { var libraryGroup = new Core.Web.Library.Group(); var element = libsElement.firstChild; while (element) { if (element.nodeType == 1) { if (element.nodeName == "lib") { var url = this.client._getLibraryServiceUrl(element.firstChild.data); libraryGroup.add(url); } } element = element.nextSibling; } if (libraryGroup.hasNewLibraries()) { libraryGroup.addLoadListener(Core.method(this, this._processPostLibraryLoad)); libraryGroup.load(); } else { this._processPostLibraryLoad(); } } else { this._processPostLibraryLoad(); } }, _processPostLibraryLoad: function(e) { try { if (Echo.Client.profilingTimer) { Echo.Client.profilingTimer.mark("lib"); } if (e && !e.success) { this.client.fail("Cannot install library: " + e.url + " Exception: " + e.ex); return; } var groupElements = Core.Web.DOM.getChildElementsByTagName(this.document.documentElement, "group"); for (var i = 0; i < groupElements.length; ++i) { var dirElements = Core.Web.DOM.getChildElementsByTagName(groupElements[i], "dir"); for (var j = 0; j < dirElements.length; ++j) { var procName = dirElements[j].getAttribute("proc"); var processor = this._processorInstances[procName]; if (!processor) { if (!Echo.RemoteClient.ServerMessage._processorClasses[procName]) { throw new Error("Invalid processor specified in ServerMessage: " + procName); } processor = new Echo.RemoteClient.ServerMessage._processorClasses[procName](this.client); this._processorInstances[procName] = processor; } processor.process(dirElements[j]); } } this._listenerList.fireEvent({type: "completion", source: this}); if (this.document.documentElement.getAttribute("async-interval")) { this.client._asyncManager._setInterval(parseInt(this.document.documentElement.getAttribute("async-interval"), 10)); this.client._asyncManager._start(); } } catch (ex) { this.client.fail("Exception: " + ex); } }, removeCompletionListener: function(l) { this._listenerList.removeListener("completion", l); } }); Echo.RemoteClient.ApplicationSyncProcessor = Core.extend(Echo.RemoteClient.DirectiveProcessor, { $load: function() { Echo.RemoteClient.ServerMessage.addProcessor("AppSync", this); }, process: function(dirElement) { var propertyElement = dirElement.firstChild; while (propertyElement) { if (propertyElement.nodeName == "locale") { this.client.application.setLocale(propertyElement.firstChild.nodeValue); } if (propertyElement.nodeName == "dir") { this.client.application.setLayoutDirection(propertyElement.firstChild.nodeValue === "rtl" ? Echo.LayoutDirection.RTL : Echo.LayoutDirection.LTR); } if (propertyElement.nodeName == "config") { var pElement = propertyElement.firstChild; while (pElement) { Echo.Serial.loadProperty(this.client, pElement, null, this.client.configuration, null); pElement = pElement.nextSibling; } } propertyElement = propertyElement.nextSibling; } } }); Echo.RemoteClient.CommandExecProcessor = Core.extend(Echo.RemoteClient.DirectiveProcessor, { $static: { _typeToPeerMap: {}, registerPeer: function(type, commandPeer) { Echo.RemoteClient.CommandExecProcessor._typeToPeerMap[type] = commandPeer; } }, $load: function() { Echo.RemoteClient.ServerMessage.addProcessor("CmdExec", this); }, process: function(dirElement) { var cmdElement = dirElement.firstChild; while (cmdElement) { var type = cmdElement.getAttribute("t"); var commandPeer = Echo.RemoteClient.CommandExecProcessor._typeToPeerMap[type]; if (!commandPeer) { throw new Error("Peer not found for: " + type); } var commandData = {}; var pElement = cmdElement.firstChild; while (pElement) { Echo.Serial.loadProperty(this.client, pElement, null, commandData, null); pElement = pElement.nextSibling; } this.client._enqueueCommand(commandPeer, commandData); cmdElement = cmdElement.nextSibling; } } }); Echo.RemoteClient.ComponentFocusProcessor = Core.extend(Echo.RemoteClient.DirectiveProcessor, { $load: function() { Echo.RemoteClient.ServerMessage.addProcessor("CFocus", this); }, process: function(dirElement) { var element = dirElement.firstChild; while (element) { if (element.nodeType == 1 && element.nodeName == "focus") { var newComponent = this.client.application.getComponentByRenderId(element.getAttribute("i")); if (newComponent != this.client._clientFocusedComponent) { this.client._serverFocusedComponent = newComponent; } } element = element.nextSibling; } } }); Echo.RemoteClient.ComponentSyncInitProcessor = Core.extend(Echo.RemoteClient.DirectiveProcessor, { $load: function() { Echo.RemoteClient.ServerMessage.addProcessor("CSyncIn", this); }, process: function(dirElement) { var element = dirElement.firstChild; while (element) { if (element.nodeType == 1 && element.nodeName == "cl") { this.client.application.rootComponent.removeAll(); } element = element.nextSibling; } } }); Echo.RemoteClient.ComponentSyncRemoveProcessor = Core.extend(Echo.RemoteClient.DirectiveProcessor, { $static: { _numericReverseSort: function(a, b) { return b - a; } }, $load: function() { Echo.RemoteClient.ServerMessage.addProcessor("CSyncRm", this); }, process: function(dirElement) { var rmElement = dirElement.firstChild; while (rmElement) { if (rmElement.nodeType != 1) { continue; } var parentComponent; if (rmElement.getAttribute("r") == "true") { parentComponent = this.client.application.rootComponent; } else { var parentId = rmElement.getAttribute("i"); parentComponent = this.client.application.getComponentByRenderId(parentId); } var childElementIds = rmElement.getAttribute("rm").split(","); this._removeComponents(parentComponent, childElementIds); rmElement = rmElement.nextSibling; } }, _removeComponents: function(parentComponent, childElementIds) { var i; if (childElementIds.length > 5) { var idToIndexMap = {}; for (i = 0; i < parentComponent.children.length; ++i) { idToIndexMap[parentComponent.children[i].renderId] = i; } var indicesToRemove = []; for (i = 0; i < childElementIds.length; ++i) { var index = idToIndexMap[childElementIds[i]]; if (index != null) { indicesToRemove.push(parseInt(index, 10)); } } indicesToRemove.sort(Echo.RemoteClient.ComponentSyncRemoveProcessor._numericReverseSort); for (i = 0; i < indicesToRemove.length; ++i) { parentComponent.remove(indicesToRemove[i]); } } else { for (i = 0; i < childElementIds.length; ++i) { var component = this.client.application.getComponentByRenderId(childElementIds[i]); if (component) { parentComponent.remove(component); } } } } }); Echo.RemoteClient.ComponentSyncUpdateProcessor = Core.extend(Echo.RemoteClient.DirectiveProcessor, { $load: function() { Echo.RemoteClient.ServerMessage.addProcessor("CSyncUp", this); }, _propertyMap : null, _styleMap: null, process: function(dirElement) { var element; element = dirElement.firstChild; while (element) { if (element.nodeType == 1) { switch (element.nodeName) { case "ss": this._processStyleSheet(element); break; case "up": this._processUpdate(element); break; case "rp": this._processReferencedProperties(element); break; case "rs": this._processReferencedStyles(element); break; } } element = element.nextSibling; } }, _processReferencedProperties: function(rpElement) { var propertyElement = rpElement.firstChild; while (propertyElement) { if (propertyElement.nodeName == "p") { var propertyId = propertyElement.getAttribute("i"); var propertyType = propertyElement.getAttribute("t"); var translator = Echo.Serial.getPropertyTranslator(propertyType); if (!translator) { throw new Error("Translator not available for property type: " + propertyType); } var propertyValue = translator.toProperty(this.client, propertyElement); if (!this._propertyMap) { this._propertyMap = {}; } this._propertyMap[propertyId] = propertyValue; } propertyElement = propertyElement.nextSibling; } }, _processReferencedStyles: function(rsElement) { var styleElement = rsElement.firstChild; while (styleElement) { if (styleElement.nodeName == "s") { var styleId = styleElement.getAttribute("i"); var style = { }; var propertyElement = styleElement.firstChild; while (propertyElement) { Echo.Serial.loadProperty(this.client, propertyElement, null, style, this._propertyMap); propertyElement = propertyElement.nextSibling; } if (!this._styleMap) { this._styleMap = {}; } this._styleMap[styleId] = style; } styleElement = styleElement.nextSibling; } }, _processStyleSheet: function(ssElement) { var styleSheet = Echo.Serial.loadStyleSheet(this.client, ssElement); this.client.application.setStyleSheet(styleSheet); }, _processUpdate: function(upElement) { var parentComponent; if (upElement.getAttribute("r") == "true") { parentComponent = this.client.application.rootComponent; } else { var parentId = upElement.getAttribute("i"); parentComponent = this.client.application.getComponentByRenderId(parentId); } var cursorIndex = 0; var element = upElement.firstChild; while (element) { if (element.nodeType == 1) { switch (element.nodeName) { case "c": var component = Echo.Serial.loadComponent(this.client, element, this._propertyMap, this._styleMap); var index = element.getAttribute("x"); if (index == null) { parentComponent.add(component, cursorIndex); ++cursorIndex; } else { index = parseInt(index, 10); parentComponent.add(component, index); cursorIndex = index + 1; } break; case "p": Echo.Serial.loadProperty(this.client, element, parentComponent, null, this._propertyMap); break; case "s": parentComponent.setStyleName(element.firstChild ? element.firstChild.nodeValue : null); break; case "sr": if (element.firstChild) { parentComponent.setStyle(this._styleMap ? this._styleMap[element.firstChild.nodeValue] : null); } else { parentComponent.setStyle(null); } break; case "e": var eventType = element.getAttribute("t"); if (element.getAttribute("v") == "true") { this.client.removeComponentListener(parentComponent, eventType); this.client.addComponentListener(parentComponent, eventType); } else { this.client.removeComponentListener(parentComponent, eventType); } break; case "en": parentComponent.setEnabled(element.firstChild.nodeValue == "true"); break; case "locale": parentComponent.setLocale(element.firstChild ? element.firstChild.nodeValue : null); break; case "dir": parentComponent.setLayoutDirection(element.firstChild ? (element.firstChild.nodeValue == "rtl" ? Echo.LayoutDirection.RTL : Echo.LayoutDirection.LTR) : null); break; } } element = element.nextSibling; } } }); Echo.FreeClient = Core.extend(Echo.Client, { _processUpdateRef: null, _doRenderRef: null, _resourcePaths: null, _renderPending: false, $construct: function(application, domainElement) { Echo.Client.call(this); this._doRenderRef = Core.method(this, this._doRender); this._processUpdateRef = Core.method(this, this._processUpdate); this.configure(application, domainElement); this._processUpdate(); }, addResourcePath: function(packageName, baseUrl) { if (!this._resourcePaths) { this._resourcePaths = { }; } this._resourcePaths[packageName] = baseUrl; }, dispose: function() { this.application.updateManager.removeUpdateListener(this._processUpdateRef); Echo.Render.renderComponentDispose(null, this.application.rootComponent); Echo.Client.prototype.dispose.call(this); }, _doRender: function() { if (this.application) { this.processUpdates(); this._renderPending = false; } }, getResourceUrl: function(packageName, resourceName) { if (this._resourcePaths && this._resourcePaths[packageName]) { return this._resourcePaths[packageName] + resourceName; } else { return Echo.Client.prototype.getResourceUrl.call(this, packageName, resourceName); } }, init: function() { Core.Web.init(); this.application.updateManager.addUpdateListener(this._processUpdateRef); }, loadStyleSheet: function(url) { var conn = new Core.Web.HttpConnection(url, "GET"); conn.addResponseListener(Core.method(this, this._processStyleSheet)); conn.connect(); }, _processStyleSheet: function(e) { if (!e.valid) { throw new Error("Received invalid response from StyleSheet HTTP request."); } var ssElement = e.source.getResponseXml().documentElement; var styleSheet = Echo.Serial.loadStyleSheet(this, ssElement); this.application.setStyleSheet(styleSheet); }, _processUpdate: function(e) { if (!this._renderPending) { this._renderPending = true; Core.Web.Scheduler.run(this._doRenderRef); } } }); Echo.Arc = { }; Echo.Arc.Application = Core.extend(Echo.Application, { arcSync: null, isActive: function() { if (!this.arcSync.component.isActive()) { return false; } else { return Echo.Application.prototype.isActive.call(this); } } }); Echo.Arc.Client = Core.extend(Echo.FreeClient, { arcSync: null, verifyInput: function(component, flags) { if (!this.arcSync.client.verifyInput(this.arcSync.component, flags)) { return false; } return Echo.FreeClient.prototype.verifyInput.call(this, component, flags); } }); Echo.Arc.ComponentSync = Core.extend(Echo.Render.ComponentSync, { arcApplication: null, arcClient: null, baseComponent: null, _defaultDomainElement: null, _applicationFocusRef: null, $abstract: { createComponent: function() { } }, $virtual: { getDomainElement: function() { if (!this._defaultDomainElement) { this._defaultDomainElement = document.createElement("div"); } return this._defaultDomainElement; }, _processApplicationFocus: function(e) { if (e.source == this.component.application) { if (e.newValue != this.component) { this.arcApplication.setFocusedComponent(null); } } else if (e.source == this.arcApplication && e.newValue) { this.component.application.setFocusedComponent(this.component); } }, renderAdd: function(update, parentElement) { var element = this.getDomainElement(); parentElement.appendChild(element); }, renderDisplay: function() { if (this.arcApplication) { if (!this.baseComponent.peer) { return; } Echo.Render.renderComponentDisplay(this.baseComponent); } else { this.arcApplication = new Echo.Arc.Application(); this.arcApplication.arcSync = this; this.arcApplication.setStyleSheet(this.client.application.getStyleSheet()); this.baseComponent = this.createComponent(); if (!this.baseComponent) { throw new Error("Invalid base component: null"); } this.arcApplication.rootComponent.add(this.baseComponent); this.arcClient = new Echo.Arc.Client(this.arcApplication, this.getDomainElement()); this.arcClient.arcSync = this; this.arcClient.parent = this.client; this.arcClient.init(); this._applicationFocusRef = Core.method(this, this._processApplicationFocus); this.arcApplication.addListener("focus", this._applicationFocusRef); this.client.application.addListener("focus", this._applicationFocusRef); } }, renderDispose: function(update) { if (this._applicationFocusRef) { this.arcApplication.removeListener("focus", this._applicationFocusRef); this.client.application.removeListener("focus", this._applicationFocusRef); this._applicationFocusRef = null; } if (this.arcClient) { this.arcClient.dispose(); this.arcClient = null; } if (this.arcApplication) { this.arcApplication.arcSync = null; this.arcApplication = null; this.baseComponent = null; } this._defaultDomainElement = null; }, renderHide: function() { if (this.arcApplication) { if (!this.baseComponent.peer) { return; } Echo.Render.renderComponentHide(this.baseComponent); } }, renderUpdate: function(update) { var domainElement = this.getDomainElement(); var containerElement = domainElement.parentNode; Echo.Render.renderComponentDispose(update, update.parent); containerElement.removeChild(domainElement); this.renderAdd(update, containerElement); } } }); Echo.Arc.ChildContainer = Core.extend(Echo.Component, { $load: function() { Echo.ComponentFactory.registerType("ArcChildContainer", this); }, componentType: "ArcChildContainer" }); Echo.Arc.ChildContainerPeer = Core.extend(Echo.Render.ComponentSync, { $load: function() { Echo.Render.registerPeer("ArcChildContainer", this); }, renderAdd: function(update, parentElement) { this._div = document.createElement("div"); var component = this.component.get("component"); if (component) { if (!component.parent || !component.parent.peer || !component.parent.peer.client) { throw new Error("Invalid component: not part of registered hierarchy."); } Echo.Render.renderComponentAdd(null, component, this._div); } parentElement.appendChild(this._div); }, renderDisplay: function() { var component = this.component.get("component"); if (component) { Echo.Render.renderComponentDisplay(component); } }, renderDispose: function(update) { var component = this.component.get("component"); if (component) { Echo.Render.renderComponentDispose(null, component); } this._div = null; }, renderUpdate: function(update) { } }); Echo.DebugConsole = { _installed: false, _rendered: false, _titleDiv: null, _contentDiv: null, _div: null, _logging: false, _maximized: false, _mouseMoveRef: null, _mouseDownRef: null, _addControl: function(text, method) { var button = document.createElement("span"); button.style.cssText = "padding:0 8px 0 0;cursor:pointer;"; button.appendChild(document.createTextNode("[" + text + "]")); this._controlsDiv.appendChild(button); Core.Web.DOM.addEventListener(button, "click", Core.method(this, method), false); }, _clearListener: function(e) { while (this._contentDiv.firstChild) { this._contentDiv.removeChild(this._contentDiv.firstChild); } }, _closeListener: function(e) { this._div.style.display = "none"; }, _consoleWrite: function(text) { if (!this._logging) { return; } if (!this._rendered) { this._render(); } var lineDiv = document.createElement("div"); lineDiv.appendChild(document.createTextNode(text)); this._contentDiv.appendChild(lineDiv); this._contentDiv.scrollTop = 10000000; }, _keyListener: function(e) { e = e ? e : window.event; if (!(e.keyCode == 67 && e.ctrlKey && e.altKey)) { return; } this._logging = true; this.setVisible(!this.isVisible()); }, install: function() { if (this._installed) { return; } Core.Web.DOM.addEventListener(document, "keydown", Core.method(this, this._keyListener), false); Core.Debug.consoleWrite = function(text) { Echo.DebugConsole._consoleWrite(text); }; if (document.URL.toString().indexOf("?debug") != -1) { this.setVisible(true); this._logging = true; } this._installed = true; }, isVisible: function() { if (!this._rendered) { return false; } return this._div.style.display == "block"; }, _maximizeListener: function(e) { this._maximized = !this._maximized; this._div.style.top = "20px"; this._div.style.right = "20px"; this._div.style.left = ""; this._div.style.bottom = ""; if (this._maximized) { var height = document.height || 600; var width = document.width || 600; this._div.style.width = (width - 50) + "px"; this._div.style.height = (height - 50) + "px"; this._contentDiv.style.width = (width - 72) + "px"; this._contentDiv.style.height = (height - 85) + "px"; } else { this._div.style.width = "300px"; this._div.style.height = "300px"; this._contentDiv.style.width = "278px"; this._contentDiv.style.height = "265px"; } }, _render: function() { var button; this._div = document.createElement("div"); this._div.id = "__DebugConsole__"; this._div.style.cssText = "display:none;position:absolute;top:20px;right:20px;width:300px;height:300px;background-color:#2f2f3f;" + "border:5px solid #3f6fff;overflow:hidden;z-index:32500;"; this._titleDiv = document.createElement("div"); this._titleDiv.style.cssText = "position:relative;" + "margin:1px;height:20px;padding:3px 10px;background-color:#5f5f8f;color:#ffffff;overflow:hidden;cursor:move;"; Core.Web.DOM.addEventListener(this._titleDiv, "mousedown", Core.method(this, this._titleMouseDown), false); Core.Web.Event.Selection.disable(this._titleDiv); this._div.appendChild(this._titleDiv); var titleTextDiv = document.createElement("div"); titleTextDiv.style.cssText = "position:absolute;font-weight:bold;"; titleTextDiv.appendChild(document.createTextNode("Debug Console")); this._titleDiv.appendChild(titleTextDiv); this._controlsDiv = document.createElement("div"); this._controlsDiv.style.cssText = "position:absolute;right:0;background-color:#5f5f8f;"; this._titleDiv.appendChild(this._controlsDiv); this._addControl("C", this._clearListener); this._addControl("^", this._maximizeListener); this._addControl("X", this._closeListener); this._contentDiv = document.createElement("div"); this._contentDiv.style.cssText = "font-family:monospace;font-size:9px;position:absolute;top:28px;left:1px;" + "width:278px;height:265px;padding:3px 10px;background-color:#1f1f2f;overflow:auto;color:#3fff6f;"; this._div.appendChild(this._contentDiv); document.body.appendChild(this._div); this._titleMouseUpRef = Core.method(this, this._titleMouseUp); this._titleMouseMoveRef = Core.method(this, this._titleMouseMove); this._rendered = true; }, _titleMouseDown: function(e) { this._drag = { originX: e.clientX, originY: e.clientY, initialX: this._div.offsetLeft, initialY: this._div.offsetTop }; Core.Web.DOM.preventEventDefault(e); Core.Web.DOM.addEventListener(document.body, "mouseup", this._titleMouseUpRef, false); Core.Web.DOM.addEventListener(document.body, "mousemove", this._titleMouseMoveRef, false); }, _titleMouseMove: function(e) { if (!this._drag) { return; } this._div.style.right = this._div.style.bottom = ""; this._div.style.top = (e.clientY - this._drag.originY + this._drag.initialY) + "px"; this._div.style.left = (e.clientX - this._drag.originX + this._drag.initialX) + "px"; }, _titleMouseUp: function(e) { this._drag = null; Core.Web.DOM.removeEventListener(document.body, "mouseup", this._titleMouseUpRef, false); Core.Web.DOM.removeEventListener(document.body, "mousemove", this._titleMouseMoveRef, false); }, setVisible: function(newValue) { if (!this._rendered) { this._render(); } this._div.style.display = newValue ? "block" : "none"; } }; Echo.Boot = { _initMethods: [], addInitMethod: function(initMethod) { Echo.Boot._initMethods.push(initMethod); }, boot: function(serverBaseUrl, initId, debug) { Core.Web.init(); if (debug && window.Echo.DebugConsole) { Echo.DebugConsole.install(); } var client = new Echo.RemoteClient(serverBaseUrl, initId); for (var i = 0; i < Echo.Boot._initMethods.length; ++i) { Echo.Boot._initMethods[i](client); } client.sync(); } };