//////////////////////////////////////////////////////////////////////////////// // Constants //////////////////////////////////////////////////////////////////////////////// var __CFVIEWPORT_ERROR_DIMENSIONS = "viewport dimensions are unavailable"; var __CFVIEWPORT_ERROR_SCROLL_OFFSETS = "viewport scroll offsets are unavailable"; var __CFVIEWPORT_RESIZE_INTERVAL = 200; var __CFVIEWPORT_SCROLL_INTERVAL = 200; var __CFVIEWPORT_SUPPORTS_DIMENSIONS = true; var __CFVIEWPORT_SUPPORTS_SCROLL_OFFSETS = true; var __CFVIEWPORT_WARNING_DIMENSIONS = "viewport dimensions unsupported"; var __CFVIEWPORT_WARNING_SCROLL_OFFSETS = "viewport scroll offsets unsupported"; //////////////////////////////////////////////////////////////////////////////// // Static Variables //////////////////////////////////////////////////////////////////////////////// var __cfViewportLastDimensions = undefined; var __cfViewportLastScrollOffsets = undefined; var __cfViewportOnResizeHandlerSet = new CFEventHandlerSet(); var __cfViewportOnScrollHandlerSet = new CFEventHandlerSet(); var __cfViewportResizeTimer = undefined; var __cfViewportScrollTimer = undefined; //////////////////////////////////////////////////////////////////////////////// // Patches //////////////////////////////////////////////////////////////////////////////// function __cfViewportGetDimensionsClient(ignoreError) { var docBody = document.body; var docElement = document.documentElement; var height = docElement.clientHeight; if (! height) { var bodyHeight = docBody.clientHeight; if (typeof(bodyHeight) == "number") { height = bodyHeight; } } var width = docElement.clientWidth; if (! width) { var bodyWidth = docBody.clientWidth; if (typeof(bodyWidth) == "number") { width = bodyWidth; } } if ((typeof(height) != "number") || (typeof(width) != "number")) { if (ignoreError) { return undefined; } return cfErrorTrigger("__cfViewportGetDimensionsClient: " + __CFVIEWPORT_ERROR_DIMENSIONS); } return {height: height, width: width}; } function __cfViewportGetDimensionsInner(ignoreError) { var height = self.innerHeight; var width = self.innerWidth; if ((typeof(height) != "number") || (typeof(width) != "number")) { if (ignoreError) { return undefined; } return cfErrorTrigger("__cfViewportGetDimensionsInner: " + __CFVIEWPORT_ERROR_DIMENSIONS); } return {height: height, width: width}; } function __cfViewportGetDimensionsUndefined(ignoreError) { if (ignoreError) { return undefined; } return cfErrorTrigger("__cfViewportGetDimensionsUndefined: " + __CFVIEWPORT_ERROR_DIMENSIONS); } function __cfViewportGetScrollOffsets(ignoreError) { var docBody = document.body; var docElement = document.documentElement; var x = docElement.scrollLeft; if (! x) { var bodyX = docBody.scrollLeft; if (typeof(bodyX) == "number") { x = bodyX; } } var y = docElement.scrollTop; if (! y) { var bodyY = docBody.scrollTop; if (typeof(bodyY) == "number") { y = bodyY; } } if ((typeof(x) != "number") || (typeof(y) != "number")) { if (ignoreError) { return undefined; } return cfErrorTrigger("__cfViewportGetScrollOffsets: " + __CFVIEWPORT_ERROR_SCROLL_OFFSETS); } return {x: x, y: y}; } function __cfViewportGetScrollOffsetsPage(ignoreError) { var x = self.pageXOffset; var y = self.pageYOffset; if ((typeof(x) != "number") || (typeof(y) != "number")) { if (ignoreError) { return undefined; } return cfErrorTrigger("__cfViewportGetScrollOffsetsPage: " + __CFVIEWPORT_ERROR_SCROLL_OFFSETS); } return {x: x, y: y}; } function __cfViewportGetScrollOffsetsUndefined(ignoreError) { if (ignoreError) { return undefined; } return cfErrorTrigger("__cfViewportGetScrollOffsetsUndefined: " + __CFVIEWPORT_ERROR_SCROLL_OFFSETS); } function __cfViewportGetScrollOffsetsXY(ignoreError) { var x = self.scrollX; var y = self.scrollY; if ((typeof(x) != "number") || (typeof(y) != "number")) { if (ignoreError) { return undefined; } return cfErrorTrigger("__cfViewportGetScrollOffsetsXY: " + __CFVIEWPORT_ERROR_SCROLL_OFFSETS); } return {x: x, y: y}; } //////////////////////////////////////////////////////////////////////////////// // Event Handlers //////////////////////////////////////////////////////////////////////////////// function __cfViewportEmulateOnResizeEvent() { var oldDimensions = __cfViewportLastDimensions; var dimensions = cfViewportGetDimensions(); if (! ((typeof(oldDimensions) != "undefined") && (oldDimensions.height == dimensions.height) && (oldDimensions.width == dimensions.width))) { __cfViewportLastDimensions = dimensions; __cfViewportHandleOnResizeEvent(); } } function __cfViewportEmulateOnScrollEvent() { var oldScrollOffsets = __cfViewportLastScrollOffsets; var scrollOffsets = cfViewportGetScrollOffsets(); if (! ((typeof(oldScrollOffsets) != "undefined") && (oldScrollOffsets.x == scrollOffsets.x) && (oldScrollOffsets.y == scrollOffsets.y))) { __cfViewportLastScrollOffsets = scrollOffsets; __cfViewportHandleOnScrollEvent(); } } function __cfViewportHandleOnLoadEvent() { // XXX: ?? window.onresize = __cfViewportHandleOnResizeEvent; window.onscroll = __cfViewportHandleOnScrollEvent; if (window.captureEvents) { if (typeof(Event.RESIZE) != "undefined") { window.captureEvents(Event.RESIZE); } if (typeof(Event.SCROLL) != "undefined") { window.captureEvents(Event.SCROLL); } } } function __cfViewportHandleOnResizeEvent(e) { var r; var results = __cfViewportOnResizeHandlerSet.execute(); var truePoll = 0; for (var i = 0; i < results.length; i++) { r = results[i]; if (typeof(r) != "undefined") { truePoll += r ? 1 : -1; } } return (truePoll >= 0); } function __cfViewportHandleOnScrollEvent(e) { var r; var results = __cfViewportOnScrollHandlerSet.execute(); var truePoll = 0; for (var i = 0; i < results.length; i++) { r = results[i]; if (typeof(r) != "undefined") { truePoll += r ? 1 : -1; } } return (truePoll >= 0); } function __cfViewportStopResizeEventEmulation() { window.clearInterval(__cfViewportResizeTimer); } function __cfViewportStopScrollEventEmulation() { window.clearInterval(__cfViewportScrollTimer); } //////////////////////////////////////////////////////////////////////////////// // Public API //////////////////////////////////////////////////////////////////////////////// function cfViewportAddOnResizeCallback() { if (typeof(__cfViewportResizeTimer) == "undefined") { // XXX: We need a reliable way to detect whether or not we can use the // onresize attribute of the window object instead of polling for resize // updates. __cfViewportResizeTimer = window.setInterval(__cfViewportEmulateOnResizeEvent, __CFVIEWPORT_RESIZE_INTERVAL); cfDocumentAddOnUnloadCallback(__cfViewportStopResizeEventEmulation); } __cfViewportOnResizeHandlerSet.add.apply(__cfViewportOnResizeHandlerSet, arguments); } function cfViewportAddOnScrollCallback() { if (typeof(__cfViewportScrollTimer) == "undefined") { // XXX: We need a reliable way to detect whether or not we can use the // onscroll attribute of the window object instead of polling for scroll // updates. __cfViewportScrollTimer = window.setInterval(__cfViewportEmulateOnScrollEvent, __CFVIEWPORT_SCROLL_INTERVAL); cfDocumentAddOnUnloadCallback(__cfViewportStopScrollEventEmulation); } __cfViewportOnScrollHandlerSet.add.apply(__cfViewportOnScrollHandlerSet, arguments); } //////////////////////////////////////////////////////////////////////////////// // Initialization //////////////////////////////////////////////////////////////////////////////// var docBody = document.documentBody; var docElement = document.documentElement; if (self && ((typeof(self.innerHeight) == "number") || (typeof(self.innerWidth) == "number"))) { cfViewportGetDimensions = __cfViewportGetDimensionsInner; } else if ((docBody && ((typeof(docBody.clientWidth) == "number") || (typeof(docBody.clientHeight) == "number"))) || (docElement && ((typeof(docElement.clientWidth) == "number") || (typeof(docElement.clientHeight) == "number")))) { cfViewportGetDimensions = __cfViewportGetDimensionsClient; } else { cfWarningTrigger("[viewport]: " + __CFVIEWPORT_WARNING_DIMENSIONS); __CFVIEWPORT_SUPPORTS_DIMENSIONS = false; cfViewportGetDimensions = __cfViewportGetDimensionsUndefined; } if (self && ((typeof(self.pageXOffset) == "number") || (typeof(self.pageYOffset) == "number"))) { cfViewportGetScrollOffsets = __cfViewportGetScrollOffsetsPage; } else if ((docBody && ((typeof(docBody.scrollLeft) == "number") || (typeof(docBody.scrollTop) == "number"))) || (docElement && ((typeof(docElement.scrollLeft) == "number") || (typeof(docElement.scrollTop) == "number")))) { cfViewportGetScrollOffsets = __cfViewportGetScrollOffsets; } else if (self && ((typeof(self.scrollX) == "number") || (typeof(self.scrollY) == "number"))) { cfViewportGetScrollOffsets = __cfViewportGetScrollOffsetsXY; } else { cfWarningTrigger("[viewport]: " + __CFVIEWPORT_WARNING_SCROLL_OFFSETS); __CFVIEWPORT_SUPPORTS_SCROLL_OFFSETS = false; cfViewportGetScrollOffsets = __cfViewportGetScrollOffsetsUndefined; } // cfDocumentAddOnLoadCallback(__cfViewportHandleOnLoadEvent);