diff --git a/src/animation/rendering.js b/src/animation/rendering.js index 13631e2..b12f98a 100644 --- a/src/animation/rendering.js +++ b/src/animation/rendering.js @@ -1,7 +1,7 @@ goog.module('clulib.animation.rendering'); -const EventTarget = goog.require('goog.events.EventTarget'); const Event = goog.require('goog.events.Event'); +const EventTarget = goog.require('goog.events.EventTarget'); /** * A render loop which runs on requestAnimationFrame. diff --git a/src/cm/cm.js b/src/cm/cm.js index 77635df..4ebcd25 100644 --- a/src/cm/cm.js +++ b/src/cm/cm.js @@ -1,13 +1,13 @@ goog.module('clulib.cm'); -const {removeHoles, asyncForEachRight} = goog.require('clulib.array'); -const Completer = goog.require('clulib.async.Completer'); -const {closest, matches} = goog.require('clulib.dom'); - const {removeDuplicates} = goog.require('goog.array'); -const GoogComponent = goog.require('goog.ui.Component'); const {assert} = goog.require('goog.asserts'); const {getParentElement, isElement} = goog.require('goog.dom'); +const GoogComponent = goog.require('goog.ui.Component'); + +const {removeHoles, asyncForEachRight} = goog.require('clulib.array'); +const Completer = goog.require('clulib.async.Completer'); +const {closest, matches} = goog.require('clulib.dom'); /** * Component metadata. @@ -793,7 +793,7 @@ class NodeTree { while (getParentElement(element) !== rootElement) { const parent = getParentElement(element); if (parent.hasAttribute(this.manager_.getTypeAttribute()) && - parent.hasAttribute(this.manager_.getIdAttribute())) + parent.hasAttribute(this.manager_.getIdAttribute())) highestComponentElement = parent; element = parent; } diff --git a/src/dom/dom.js b/src/dom/dom.js index f1bef99..6a35a8b 100644 --- a/src/dom/dom.js +++ b/src/dom/dom.js @@ -3,6 +3,8 @@ goog.module('clulib.dom'); const {getAncestor, isElement, getViewportSize} = goog.require('goog.dom'); const Rect = goog.require('goog.math.Rect'); +const {rectangleIntersects} = goog.require('clulib.math'); + /** * Returns true if the element would be selected by the specified * selector string, false otherwise. @@ -72,25 +74,7 @@ function isElementVisible (element, factor = null) { */ const elementRect = new Rect(boundingRect.left, boundingRect.top, boundingRect.width, boundingRect.height); - /** - * @type {goog.math.Rect} - */ - const intersectionRect = Rect.intersection(viewportRect, elementRect); - - // If no intersection exists or the area is zero, return false - if (intersectionRect == null || intersectionRect.getSize().area() === 0) - return false; - - // If no percentage calculation is needed, return true - if (factor == null) - return true; - - const elementArea = elementRect.getSize().area(); - const intersectionArea = intersectionRect.getSize().area(); - - const visibleFactor = intersectionArea / elementArea; - - return visibleFactor >= factor; + return rectangleIntersects(viewportRect, elementRect, factor); } exports = {matches, closest, isElementVisible}; diff --git a/src/l10n/resource_bundle.js b/src/l10n/resource_bundle.js index 206ccb7..4947e1a 100644 --- a/src/l10n/resource_bundle.js +++ b/src/l10n/resource_bundle.js @@ -1,8 +1,8 @@ goog.module('clulib.l10n.ResourceBundle'); -const {httpGetJson} = goog.require('clulib.net.http_request'); const {objectToMap} = goog.require('clulib.collections'); const {cacheAsyncValue} = goog.require('clulib.functions'); +const {httpGetJson} = goog.require('clulib.net.http_request'); /** * A resource bundle containing localized messages. diff --git a/src/math/math.js b/src/math/math.js index eacbba0..0afbd10 100644 --- a/src/math/math.js +++ b/src/math/math.js @@ -1,5 +1,7 @@ goog.module('clulib.math'); +const Rect = goog.require('goog.math.Rect'); + /** * Maps a value from one range to another range. * @@ -14,4 +16,33 @@ function mapRange (x, inMin, inMax, outMin, outMax) { return (x - inMin) * (outMax - outMin) / (inMax - inMin) + outMin; } -exports = {mapRange}; +/** + * Returns true if a rectangle intersects another rectangle. + * + * Takes a factor to specify how much of rectangleB needs to intersect rectangleA. + * + * @param {goog.math.Rect} rectangleA + * @param {goog.math.Rect} rectangleB + * @param {?number|null} factor The factor from 0 (not intersecting) to 1 (completely intersecting) + * @returns {boolean} + */ +function rectangleIntersects (rectangleA, rectangleB, factor = null) { + /** + * @type {goog.math.Rect} + */ + const intersectionRect = Rect.intersection(rectangleA, rectangleB); + + // If no intersection exists or the area is zero, return false + if (intersectionRect == null || intersectionRect.getSize().area() === 0) + return false; + + // If no percentage calculation is needed, return true + if (factor == null) + return true; + + const visibleFactor = intersectionRect.getSize().area() / rectangleB.getSize().area(); + + return visibleFactor >= factor; +} + +exports = {mapRange, rectangleIntersects}; diff --git a/src/net/http_request.js b/src/net/http_request.js index 8a42587..ca5d46d 100644 --- a/src/net/http_request.js +++ b/src/net/http_request.js @@ -1,11 +1,12 @@ goog.module('clulib.net.http_request'); -const Completer = goog.require('clulib.async.Completer'); const events = goog.require('goog.events'); -const XhrIo = goog.require('goog.net.XhrIo'); const EventType = goog.require('goog.net.EventType'); +const XhrIo = goog.require('goog.net.XhrIo'); const ResponseType = goog.require('goog.net.XhrIo.ResponseType'); +const Completer = goog.require('clulib.async.Completer'); + /** * @typedef {{ * response: *, diff --git a/src/ui/toggle_button.js b/src/ui/toggle_button.js index 8eda081..fca8975 100644 --- a/src/ui/toggle_button.js +++ b/src/ui/toggle_button.js @@ -1,10 +1,10 @@ goog.module('clulib.ui.ToggleButton'); -const {Component} = goog.require('clulib.cm'); - -const {contains, enable} = goog.require('goog.dom.classlist'); const GoogEvent = goog.require('goog.events.Event'); const GoogEventType = goog.require('goog.events.EventType'); +const {contains, enable} = goog.require('goog.dom.classlist'); + +const {Component} = goog.require('clulib.cm'); /** * @enum {string} diff --git a/test/animation/rendering_spec.js b/test/animation/rendering_spec.js index 34a3e4c..1ab945a 100644 --- a/test/animation/rendering_spec.js +++ b/test/animation/rendering_spec.js @@ -1,9 +1,10 @@ goog.module('test.clulib.animation.rendering'); +const {listen} = goog.require('goog.events'); + const {RenderLoop, RenderLoopEventType} = goog.require('clulib.animation.rendering'); -const {waitFor} = goog.require('testing.async'); -const {listen} = goog.require('goog.events'); +const {waitFor} = goog.require('testing.async'); exports = function () { describe('clulib.animation.rendering', () => { diff --git a/test/array/array_spec.js b/test/array/array_spec.js index f5737d1..847484e 100644 --- a/test/array/array_spec.js +++ b/test/array/array_spec.js @@ -1,8 +1,9 @@ goog.module('test.clulib.array'); -const {removeHoles, asyncForEach, asyncForEachRight} = goog.require('clulib.array'); const {clone} = goog.require('goog.array'); +const {removeHoles, asyncForEach, asyncForEachRight} = goog.require('clulib.array'); + exports = function () { describe('clulib.array', () => { describe('removeHoles', () => { diff --git a/test/async/completer_spec.js b/test/async/completer_spec.js index 17cfded..c959f13 100644 --- a/test/async/completer_spec.js +++ b/test/async/completer_spec.js @@ -1,8 +1,9 @@ goog.module('test.clulib.async.Completer'); -const Completer = goog.require('clulib.async.Completer'); const AssertionError = goog.require('goog.asserts.AssertionError'); +const Completer = goog.require('clulib.async.Completer'); + exports = function () { describe('clulib.async.Completer', () => { it('should resolve its promise', async () => { diff --git a/test/cm/cm_spec.js b/test/cm/cm_spec.js index 456d7fb..806f96e 100644 --- a/test/cm/cm_spec.js +++ b/test/cm/cm_spec.js @@ -1,10 +1,10 @@ goog.module('test.clulib.cm'); -const {ComponentManager, Component} = goog.require('clulib.cm'); - -const {appendChild, removeNode, getFirstElementChild} = goog.require('goog.dom'); const {contains} = goog.require('goog.dom.classlist'); const {has} = goog.require('goog.dom.dataset'); +const {appendChild, removeNode, getFirstElementChild} = goog.require('goog.dom'); + +const {ComponentManager, Component} = goog.require('clulib.cm'); exports = function () { describe('clulib.cm.ComponentManager', () => { diff --git a/test/dom/dom_spec.js b/test/dom/dom_spec.js index 80eccc4..f4bf1ce 100644 --- a/test/dom/dom_spec.js +++ b/test/dom/dom_spec.js @@ -1,9 +1,9 @@ goog.module('test.clulib.dom'); -const {matches, closest, isElementVisible} = goog.require('clulib.dom'); - const {appendChild, removeNode, getViewportSize} = goog.require('goog.dom'); +const {matches, closest, isElementVisible} = goog.require('clulib.dom'); + exports = function () { describe('clulib.dom', () => { describe('matches', () => { diff --git a/test/l10n/resource_bundle_spec.js b/test/l10n/resource_bundle_spec.js index be4d727..c703dd2 100644 --- a/test/l10n/resource_bundle_spec.js +++ b/test/l10n/resource_bundle_spec.js @@ -1,6 +1,7 @@ goog.module('test.clulib.l10n.ResourceBundle'); const ResourceBundle = goog.require('clulib.l10n.ResourceBundle'); + const env = goog.require('testing.environment'); const base = `${env.basePath}/test-assets/l10n`; diff --git a/test/l10n/resource_manager_spec.js b/test/l10n/resource_manager_spec.js index 3e1aabb..b97dfb1 100644 --- a/test/l10n/resource_manager_spec.js +++ b/test/l10n/resource_manager_spec.js @@ -1,6 +1,7 @@ goog.module('test.clulib.l10n.ResourceManager'); const ResourceManager = goog.require('clulib.l10n.ResourceManager'); + const env = goog.require('testing.environment'); const base = `${env.basePath}/test-assets/l10n`; diff --git a/test/math/math_spec.js b/test/math/math_spec.js index 796f9da..3e6d5d1 100644 --- a/test/math/math_spec.js +++ b/test/math/math_spec.js @@ -1,6 +1,9 @@ goog.module('test.clulib.math'); -const {mapRange} = goog.require('clulib.math'); +const Rect = goog.require('goog.math.Rect'); +const Size = goog.require('goog.math.Size'); + +const {mapRange, rectangleIntersects} = goog.require('clulib.math'); exports = function () { describe('clulib.math', () => { @@ -18,5 +21,68 @@ exports = function () { expect(mapRange(-5, -10, 0, -100, 0)).toBe(-50); }); }); + + describe('rectangleIntersects', () => { + /** + * @type {goog.math.Size} + */ + const size = new Size(1024, 768); + + /** + * @type {goog.math.Rect} + */ + const rectA = new Rect(0, 0, size.width, size.height); + + it('should detect fully intersecting rectangles', () => { + /** + * @type {goog.math.Rect} + */ + const fullyVisible = new Rect( + size.width / 4, + size.height / 4, + size.width / 2, + size.height / 2 + ); + + expect(rectangleIntersects(rectA, fullyVisible)).toBe(true); + expect(rectangleIntersects(rectA, fullyVisible, 1)).toBe(true); + }); + + it('should detect partially intersecting rectangles', () => { + const quarterVisible = new Rect( + size.width / 2, + size.height / 2, + size.width, + size.height + ); + + expect(rectangleIntersects(rectA, quarterVisible)).toBe(true); + expect(rectangleIntersects(rectA, quarterVisible, .23)).toBe(true); + expect(rectangleIntersects(rectA, quarterVisible, .25)).toBe(true); + expect(rectangleIntersects(rectA, quarterVisible, .26)).toBe(false); + }); + + it('should not detect non intersecting rectangles', () => { + const notVisible = new Rect( + 0, + size.height * 2, + size.width, + size.height + ); + + expect(rectangleIntersects(rectA, notVisible)).toBe(false); + }); + + it('should not detect non intersecting rectangles on edge', () => { + const notVisibleOnEdge = new Rect( + 0, + size.height, + size.width, + size.height + ); + + expect(rectangleIntersects(rectA, notVisibleOnEdge)).toBe(false); + }); + }); }); }; diff --git a/test/net/http_request_spec.js b/test/net/http_request_spec.js index 2d6c927..cd039e5 100644 --- a/test/net/http_request_spec.js +++ b/test/net/http_request_spec.js @@ -1,9 +1,11 @@ goog.module('test.clulib.net.http_request'); -const env = goog.require('testing.environment'); -const http = goog.require('clulib.net.http_request'); const ErrorType = goog.require('goog.net.EventType'); +const http = goog.require('clulib.net.http_request'); + +const env = goog.require('testing.environment'); + const jsonFile = `${env.basePath}/test-assets/json/dummy.json`; const jsonString = 'Hello, world!'; diff --git a/test/validation/validation_spec.js b/test/validation/validation_spec.js index 9a6c94b..3858451 100644 --- a/test/validation/validation_spec.js +++ b/test/validation/validation_spec.js @@ -2,9 +2,10 @@ goog.module('test.clulib.validation'); const ResponseType = goog.require('goog.net.XhrIo.ResponseType'); +const http = goog.require('clulib.net.http_request'); const validation = goog.require('clulib.validation'); + const {basePath} = goog.require('testing.environment'); -const http = goog.require('clulib.net.http_request'); /** * @returns {Promise}