//////////////////////////////////////////////////////////////////////////////// // Constants //////////////////////////////////////////////////////////////////////////////// var __CFELEMENT_ERROR_CHILDREN = "element's children are unavailable"; var __CFELEMENT_ERROR_DIMENSIONS = "element dimensions are unavailable"; var __CFELEMENT_ERROR_GET_HTML = "can't get element's inner html"; var __CFELEMENT_ERROR_ID = "invalid element id"; var __CFELEMENT_ERROR_INVALID_CLASS = "invalid class name"; var __CFELEMENT_ERROR_PARENT = "element's parent is unavailable"; var __CFELEMENT_ERROR_POSITION = "element position is unavailable"; var __CFELEMENT_ERROR_SET_ATTRIBUTE = "can't set element's attribute"; var __CFELEMENT_ERROR_SET_HTML = "can't set element's inner html"; var __CFELEMENT_VALID_CSS_CLASS_CHARS = String.ALPHANUMERIC + '-'; var CFELEMENT_NODE_TYPE_ELEMENT = 1; var CFELEMENT_NODE_TYPE_ATTRIBUTE = 2; var CFELEMENT_NODE_TYPE_TEXT = 3; var CFELEMENT_NODE_TYPE_CDATA_SECTION = 4; var CFELEMENT_NODE_TYPE_ENTITY_REFERENCE = 5; var CFELEMENT_NODE_TYPE_ENTITY = 6; var CFELEMENT_NODE_TYPE_PROCESSING_INSTRUCTION = 7; var CFELEMENT_NODE_TYPE_COMMENT = 8; var CFELEMENT_NODE_TYPE_DOCUMENT = 9; var CFELEMENT_NODE_TYPE_DOCUMENT_TYPE = 10; var CFELEMENT_NODE_TYPE_DOCUMENT_FRAGMENT = 11; var CFELEMENT_NODE_TYPE_NOTATION = 12; var CFELEMENT_HIDDEN_CLASS = "cf-hidden-element"; var CFELEMENT_INVISIBLE_CLASS = "cf-invisible-element"; var CFELEMENT_SKELETON_CLASS = "cf-skeleton"; //////////////////////////////////////////////////////////////////////////////// // Private functions //////////////////////////////////////////////////////////////////////////////// function __cfElementGetChildren(element) { return element.children; } function __cfElementGetChildrenChildNodes(element) { return element.childNodes; } function __cfElementGetDimensions(element, ignoreError) { if ((typeof(element.offsetWidth) == "number") || (typeof(element.offsetHeight) == "number")) { __cfElementGetDimensions = __cfElementGetDimensionsOffset; } else { return undefined; } return __cfElementGetDimensions(element, ignoreError); } function __cfElementGetDimensionsOffset(element, ignoreError) { return {height: element.offsetHeight || 0, width: element.offsetWidth || 0}; } function __cfElementGetDimensionsUndefined(element, ignoreError) { if (ignoreError) { return undefined; } return cfErrorTrigger("__cfElementGetDimensionsUndefined: " + __CFELEMENT_ERROR_DIMENSIONS); } function __cfElementGetDocumentPosition(element, ignoreError) { if ((typeof(element.offsetLeft) == "number") || (typeof(element.offsetTop) == "number")) { if ((typeof(element.clientLeft) == "number") || (typeof(element.clientTop) == "number")) { cfElementGetDocumentPosition = __cfElementGetDocumentPositionOffsetClient; } else { cfElementGetDocumentPosition = __cfElementGetDocumentPositionOffset; } } else if ((typeof(element.x) == "number") || (typeof(element.y) == "number")) { cfElementGetDocumentPosition = __cfElementGetDocumentPositionXY; } else { return undefined; } return cfElementGetDocumentPosition(element, ignoreError); } function __cfElementGetDocumentPositionOffset(element, ignoreError) { var left; var top; var x = 0; var y = 0; do { left = element.offsetLeft; top = element.offsetTop; if ((typeof(left) == "undefined") || (typeof(top) == "undefined")) { if (ignoreError) { return undefined; } return cfErrorTrigger("__cfElementGetDocumentPositionOffset: " + __CFELEMENT_ERROR_POSITION); } x += left; y += top; element = element.offsetParent; } while (element); return {x: x, y: y}; } function __cfElementGetDocumentPositionOffsetClient(element, ignoreError) { var left; var leftBorder; var top; var topBorder; var x = 0; var y = 0; do { left = element.offsetLeft; leftBorder = element.clientLeft; top = element.offsetTop; topBorder = element.clientTop; if ((typeof(left) == "undefined") || (typeof(top) == "undefined") || (typeof(leftBorder) == "undefined") || (typeof(topBorder) == "undefined")) { if (ignoreError) { return undefined; } var func = "__cfElementGetDocumentPositionOffsetClient"; return cfErrorTrigger(func + ": " + __CFELEMENT_ERROR_POSITION); } x += left + leftBorder; y += top + topBorder; element = element.offsetParent; } while (element); return {x: x, y: y}; } function __cfElementGetDocumentPositionUndefined(element, ignoreError) { if (ignoreError) { return undefined; } return cfErrorTrigger("__cfElementGetDocumentPositionUndefined: " + __CFELEMENT_ERROR_POSITION); } function __cfElementGetDocumentPositionXY(element, ignoreError) { var x = element.x; var y = element.y; if ((typeof(x) == "undefined") || (typeof(y) == "undefined")) { if (ignoreError) { return undefined; } return cfErrorTrigger("__cfElementGetDocumentPositionXY: " + __CFELEMENT_ERROR_POSITION); } return {x: x, y: y}; } function __cfElementGetInnerHTML(element) { return element.innerHTML; } function __cfElementGetParentNode(element) { return element.parentNode; } function __cfElementIsValidCSSClass(cls) { if (! cls) { return false; } var length = cls.length; if (! length) { return false; } var c = cls.charAt(0); if (! c.isAlphanumeric()) { return false; } return __CFELEMENT_VALID_CSS_CLASS_CHARS.containsAllChars(cls.substring(1)); } function __cfElementLoadDisplay(element) { var style = element.style; var state = { display: style.display, position: style.position, visibility: style.visibility }; style.visibility = "hidden"; style.position = "absolute"; style.display = "block"; return state; } function __cfElementRestoreDisplay(element, state) { var style = element.style; style.display = state.display; style.position = state.position; style.visibility = state.visibility; } function __cfElementSetAttribute(element, name, value) { element.setAttribute(name, value); } function __cfElementSetAttributeCreate(element, name, value) { var node = document.createAttribute(name); // Workaround Safari bug node.nodeValue = value; node.value = value; element.setAttributeNode(node); } function __cfElementSetInnerHTML(element, html) { // Workaround bugs in Netscape 6 and IE 5. element.innerHTML = ''; element.innerHTML = html.toString(); } function __cfElementSetInnerHTMLDocument(element, html) { var doc = element.document; doc.open(); doc.write(html); doc.close(); } //////////////////////////////////////////////////////////////////////////////// // Public API //////////////////////////////////////////////////////////////////////////////// function cfElementAddClass(element, cls) { var classes = cfElementGetClasses(element); classes.push(cls); cfElementSetClasses(element, classes); } function cfElementDiscardClass(element, cls) { var classes = cfElementGetClasses(element); var result = classes.discard(cls); if (result) { cfElementSetClasses(element, classes); } return result; } function cfElementDiscardClasses(element, cls) { while (cfElementDiscardClass(element, cls)); } function cfElementGet(id) { var element = document.getElementById(id); if (! element) { return cfErrorTrigger("cfElementGet: '" + id + "': " + __CFELEMENT_ERROR_ID); } return element; } function cfElementGetChildren(element, ignoreError) { if (typeof(element.childNodes) != "undefined") { cfElementGetChildren = __cfElementGetChildrenChildNodes; } else if (typeof(element.children) != "undefined") { cfElementGetChildren = __cfElementGetChildren; } else if (ignoreError) { return undefined; } else { return cfErrorTrigger("cfElementGetChildren: " + __CFELEMENT_ERROR_CHILDREN); } return cfElementGetChildren(element); } function cfElementGetClasses(element) { var className = element.className; if (! className) { return new Array(); } var classes = className.split(/\s+/); var replace = false; for (var i = classes.length - 1; i >= 0; i--) { var cls = classes[i]; if (! __cfElementIsValidCSSClass(cls)) { classes.splice(i, 1); replace = true; } } if (replace) { cfElementSetClasses(element, classes); } return classes; } function cfElementGetDimensions(element, ignoreError) { var dimensions = __cfElementGetDimensions(element, ignoreError); if ((typeof(dimensions) == "undefined") || (! (dimensions.width || dimensions.height))) { // Perhaps the element is hidden var state = __cfElementLoadDisplay(element); dimensions = __cfElementGetDimensions(element); __cfElementRestoreDisplay(element, state); if (typeof(dimensions) == "undefined") { cfElementGetDimensions = __cfElementGetDimensionsUndefined; return cfElementGetDimensions(element, ignoreError); } } return dimensions; } function cfElementGetDocumentPosition(element, ignoreError) { var position = __cfElementGetDocumentPosition(element, ignoreError); if (typeof(position) == "undefined") { // Perhaps the element is hidden var state = __cfElementLoadDisplay(element); position = __cfElementGetDocumentPosition(element); __cfElementRestoreDisplay(element, state); if (typeof(position) == "undefined") { cfElementGetDocumentPosition = __cfElementGetDocumentPositionUndefined; return cfElementGetDocumentPosition(element, ignoreError); } } return position; } function cfElementGetInnerHTML(element) { if (typeof(element.innerHTML) != "undefined") { cfElementGetInnerHTML = __cfElementGetInnerHTML; cfElementSetInnerHTML = __cfElementSetInnerHTML; } else { return cfErrorTrigger("cfElementGetInnerHTML: " + __CFELEMENT_ERROR_GET_HTML); } return cfElementGetInnerHTML(element); } function cfElementGetParent(element) { if (typeof(element.parentNode) != "undefined") { cfElementGetParent = __cfElementGetParentNode; } else { return cfErrorTrigger("cfElementGetParent: " + __CFELEMENT_ERROR_PARENT); } return cfElementGetParent(element); } function cfElementIntersectsElements(element, elementList) { var results = new Array(); var aPosition = cfElementGetDocumentPosition(element, true); if (typeof(aPosition) != "undefined") { var aDimensions = cfElementGetDimensions(element, true); if (typeof(aDimensions) != "undefined") { var aHeight = aDimensions.height; var aLeft = aPosition.x; var aTop = aPosition.y; var aWidth = aDimensions.width; for (var i = 0; i < elementList.length; i++) { var bElement = elementList[i]; var bPosition = cfElementGetDocumentPosition(bElement, true); if (typeof(bPosition) == "undefined") { continue; } var bDimensions = cfElementGetDimensions(bElement, true); if (typeof(bDimensions) == "undefined") { continue; } var bLeft = bPosition.x; var bTop = bPosition.y; if (! ((aLeft > (bLeft + bDimensions.width)) || (bLeft > (aLeft + aWidth)) || (aTop > (bTop + bDimensions.height)) || (bTop > (aTop + aHeight)))) { results.push(bElement); } } } } return results; } function cfElementIntersectsElement(elementA, elementB) { return !! cfElementIntersectsElements(elementA, [elementB]).length; } function cfElementHasDescendant(element, descendant) { var nodes = cfElementGetChildren(element); for (var i = 0; i < nodes.length; i++) { var node = nodes[i]; if (node === descendant) { return true; } if (cfElementHasDescendant(node, descendant)) { return true; } } return false; } function cfElementRemoveClass(element, cls) { var classes = cfElementGetClasses(element); classes.remove(cls); cfElementSetClasses(element, classes); } function cfElementSetAttribute(element, name, value) { if (typeof(element.setAttribute) != "undefined") { cfElementSetAttribute = __cfElementSetAttribute; } else if ((typeof(document.createAttribute) != "undefined") && (typeof(element.setAttributeNode) != "undefined")) { cfElementSetAttribute = __cfElementSetAttributeCreate; } else { return cfErrorTrigger("cfElementSetAttribute: " + __CFELEMENT_ERROR_SET_ATTRIBUTE); } return cfElementSetAttribute(element, name, value); } function cfElementSetClasses(element, classes) { for (var i = 0; i < classes.length; i++) { var cls = classes[i]; if (! __cfElementIsValidCSSClass(cls)) { return cfErrorTrigger("cfElementSetClasses: '" + cls + "': " + __CFELEMENT_ERROR_INVALID_CLASS); } } element.className = classes.join(' '); } function cfElementSetInnerHTML(element, html) { if (typeof(element.innerHTML) != "undefined") { cfElementGetInnerHTML = __cfElementGetInnerHTML; cfElementSetInnerHTML = __cfElementSetInnerHTML; } else if (element.document) { cfElementSetInnerHTML = __cfElementSetInnerHTMLDocument; } else { return cfErrorTrigger("cfElementSetInnerHTML: " + __CFELEMENT_ERROR_SET_HTML); } return cfElementSetInnerHTML(element, html); } function cfElementSetInnerText(element, text) { cfElementSetInnerHTML(element, text.toString().toHTML()); }