RegionStyleProperties = ['top', 'left', 'right', 'width', 'height'] SpaceRegex = /\s+/ module.exports = class HighlightsComponent oldState: null constructor: (@domElementPool) -> @highlightNodesById = {} @regionNodesByHighlightId = {} @domNode = @domElementPool.buildElement("div", "highlights") getDomNode: -> @domNode updateSync: (state) -> newState = state.highlights @oldState ?= {} # remove highlights for id of @oldState unless newState[id]? @domElementPool.freeElementAndDescendants(@highlightNodesById[id]) delete @highlightNodesById[id] delete @regionNodesByHighlightId[id] delete @oldState[id] # add or update highlights for id, highlightState of newState unless @oldState[id]? highlightNode = @domElementPool.buildElement("div", "highlight") @highlightNodesById[id] = highlightNode @regionNodesByHighlightId[id] = {} @domNode.appendChild(highlightNode) @updateHighlightNode(id, highlightState) return updateHighlightNode: (id, newHighlightState) -> highlightNode = @highlightNodesById[id] oldHighlightState = (@oldState[id] ?= {regions: [], flashCount: 0}) # update class if newHighlightState.class isnt oldHighlightState.class if oldHighlightState.class? if SpaceRegex.test(oldHighlightState.class) highlightNode.classList.remove(oldHighlightState.class.split(SpaceRegex)...) else highlightNode.classList.remove(oldHighlightState.class) if SpaceRegex.test(newHighlightState.class) highlightNode.classList.add(newHighlightState.class.split(SpaceRegex)...) else highlightNode.classList.add(newHighlightState.class) oldHighlightState.class = newHighlightState.class @updateHighlightRegions(id, newHighlightState) @flashHighlightNodeIfRequested(id, newHighlightState) updateHighlightRegions: (id, newHighlightState) -> oldHighlightState = @oldState[id] highlightNode = @highlightNodesById[id] # remove regions while oldHighlightState.regions.length > newHighlightState.regions.length oldHighlightState.regions.pop() @domElementPool.freeElementAndDescendants(@regionNodesByHighlightId[id][oldHighlightState.regions.length]) delete @regionNodesByHighlightId[id][oldHighlightState.regions.length] # add or update regions for newRegionState, i in newHighlightState.regions unless oldHighlightState.regions[i]? oldHighlightState.regions[i] = {} regionNode = @domElementPool.buildElement("div", "region") # This prevents highlights at the tiles boundaries to be hidden by the # subsequent tile. When this happens, subpixel anti-aliasing gets # disabled. regionNode.style.boxSizing = "border-box" regionNode.classList.add(newHighlightState.deprecatedRegionClass) if newHighlightState.deprecatedRegionClass? @regionNodesByHighlightId[id][i] = regionNode highlightNode.appendChild(regionNode) oldRegionState = oldHighlightState.regions[i] regionNode = @regionNodesByHighlightId[id][i] for property in RegionStyleProperties if newRegionState[property] isnt oldRegionState[property] oldRegionState[property] = newRegionState[property] if newRegionState[property]? regionNode.style[property] = newRegionState[property] + 'px' else regionNode.style[property] = '' return flashHighlightNodeIfRequested: (id, newHighlightState) -> oldHighlightState = @oldState[id] return unless newHighlightState.flashCount > oldHighlightState.flashCount highlightNode = @highlightNodesById[id] addFlashClass = => highlightNode.classList.add(newHighlightState.flashClass) oldHighlightState.flashClass = newHighlightState.flashClass @flashTimeoutId = setTimeout(removeFlashClass, newHighlightState.flashDuration) removeFlashClass = => highlightNode.classList.remove(oldHighlightState.flashClass) oldHighlightState.flashClass = null clearTimeout(@flashTimeoutId) if oldHighlightState.flashClass? removeFlashClass() requestAnimationFrame(addFlashClass) else addFlashClass() oldHighlightState.flashCount = newHighlightState.flashCount