diff options
author | Stephane Desneux <stephane.desneux@iot.bzh> | 2016-05-31 18:16:48 +0200 |
---|---|---|
committer | Stephane Desneux <stephane.desneux@iot.bzh> | 2016-05-31 18:16:48 +0200 |
commit | 5b1e6cc132f44262a873fa8296a2a3e1017b0278 (patch) | |
tree | 43b2cd54e2e300b399ff3f2af4458a2c4ed8a144 /afb-client/bower_components/tether/coffee | |
parent | f7d2f9ac4168ee5064580c666d508667a73cefc0 (diff) | |
parent | 85ace9c1ce9a98e9b8a22f045c7dd752b38d9129 (diff) |
Merge afb-client
Diffstat (limited to 'afb-client/bower_components/tether/coffee')
6 files changed, 1141 insertions, 0 deletions
diff --git a/afb-client/bower_components/tether/coffee/abutment.coffee b/afb-client/bower_components/tether/coffee/abutment.coffee new file mode 100644 index 0000000..5f331e4 --- /dev/null +++ b/afb-client/bower_components/tether/coffee/abutment.coffee @@ -0,0 +1,40 @@ +{getBounds, updateClasses, defer} = @Tether.Utils + +@Tether.modules.push + position: ({top, left}) -> + {height, width} = @cache 'element-bounds', => getBounds @element + + targetPos = @getTargetBounds() + + bottom = top + height + right = left + width + + abutted = [] + if top <= targetPos.bottom and bottom >= targetPos.top + for side in ['left', 'right'] + if targetPos[side] in [left, right] + abutted.push side + + if left <= targetPos.right and right >= targetPos.left + for side in ['top', 'bottom'] + if targetPos[side] in [top, bottom] + abutted.push side + + allClasses = [] + addClasses = [] + + sides = ['left', 'top', 'right', 'bottom'] + allClasses.push @getClass('abutted') + for side in sides + allClasses.push "#{ @getClass('abutted') }-#{ side }" + + if abutted.length + addClasses.push @getClass('abutted') + for side in abutted + addClasses.push "#{ @getClass('abutted') }-#{ side }" + + defer => + updateClasses @target, addClasses, allClasses + updateClasses @element, addClasses, allClasses + + true diff --git a/afb-client/bower_components/tether/coffee/constraint.coffee b/afb-client/bower_components/tether/coffee/constraint.coffee new file mode 100644 index 0000000..d27c1c9 --- /dev/null +++ b/afb-client/bower_components/tether/coffee/constraint.coffee @@ -0,0 +1,260 @@ +{getOuterSize, getBounds, getSize, extend, updateClasses, defer} = @Tether.Utils + +MIRROR_ATTACH = + left: 'right' + right: 'left' + top: 'bottom' + bottom: 'top' + middle: 'middle' + +BOUNDS_FORMAT = ['left', 'top', 'right', 'bottom'] + +getBoundingRect = (tether, to) -> + if to is 'scrollParent' + to = tether.scrollParent + else if to is 'window' + to = [pageXOffset, pageYOffset, innerWidth + pageXOffset, innerHeight + pageYOffset] + + if to is document + to = to.documentElement + + if to.nodeType? + pos = size = getBounds to + style = getComputedStyle to + + to = [pos.left, pos.top, size.width + pos.left, size.height + pos.top] + + for side, i in BOUNDS_FORMAT + side = side[0].toUpperCase() + side.substr(1) + if side in ['Top', 'Left'] + to[i] += parseFloat style["border#{ side }Width"] + else + to[i] -= parseFloat style["border#{ side }Width"] + + to + +@Tether.modules.push + position: ({top, left, targetAttachment}) -> + return true unless @options.constraints + + removeClass = (prefix) => + @removeClass prefix + for side in BOUNDS_FORMAT + @removeClass "#{ prefix }-#{ side }" + + {height, width} = @cache 'element-bounds', => getBounds @element + + if width is 0 and height is 0 and @lastSize? + # Handle the item getting hidden as a result of our positioning without glitching + # the classes in and out + {width, height} = @lastSize + + targetSize = @cache 'target-bounds', => @getTargetBounds() + targetHeight = targetSize.height + targetWidth = targetSize.width + + tAttachment = {} + eAttachment = {} + + allClasses = [@getClass('pinned'), @getClass('out-of-bounds')] + for constraint in @options.constraints + allClasses.push(constraint.outOfBoundsClass) if constraint.outOfBoundsClass + allClasses.push(constraint.pinnedClass) if constraint.pinnedClass + + for cls in allClasses + for side in ['left', 'top', 'right', 'bottom'] + allClasses.push "#{ cls }-#{ side }" + + addClasses = [] + + tAttachment = extend {}, targetAttachment + eAttachment = extend {}, @attachment + + for constraint in @options.constraints + {to, attachment, pin} = constraint + + attachment ?= '' + + if ' ' in attachment + [changeAttachY, changeAttachX] = attachment.split(' ') + else + changeAttachX = changeAttachY = attachment + + bounds = getBoundingRect @, to + + if changeAttachY in ['target', 'both'] + if (top < bounds[1] and tAttachment.top is 'top') + top += targetHeight + tAttachment.top = 'bottom' + + if (top + height > bounds[3] and tAttachment.top is 'bottom') + top -= targetHeight + tAttachment.top = 'top' + + if changeAttachY is 'together' + if top < bounds[1] and tAttachment.top is 'top' + if eAttachment.top is 'bottom' + top += targetHeight + tAttachment.top = 'bottom' + + top += height + eAttachment.top = 'top' + else if eAttachment.top is 'top' + top += targetHeight + tAttachment.top = 'bottom' + + top -= height + eAttachment.top = 'bottom' + + if top + height > bounds[3] and tAttachment.top is 'bottom' + if eAttachment.top is 'top' + top -= targetHeight + tAttachment.top = 'top' + + top -= height + eAttachment.top = 'bottom' + else if eAttachment.top is 'bottom' + top -= targetHeight + tAttachment.top = 'top' + + top += height + eAttachment.top = 'top' + + if tAttachment.top is 'middle' + if top + height > bounds[3] and eAttachment.top is 'top' + top -= height + eAttachment.top = 'bottom' + + else if top < bounds[1] and eAttachment.top is 'bottom' + top += height + eAttachment.top = 'top' + + if changeAttachX in ['target', 'both'] + if (left < bounds[0] and tAttachment.left is 'left') + left += targetWidth + tAttachment.left = 'right' + + if (left + width > bounds[2] and tAttachment.left is 'right') + left -= targetWidth + tAttachment.left = 'left' + + if changeAttachX is 'together' + if left < bounds[0] and tAttachment.left is 'left' + if eAttachment.left is 'right' + left += targetWidth + tAttachment.left = 'right' + + left += width + eAttachment.left = 'left' + + else if eAttachment.left is 'left' + left += targetWidth + tAttachment.left = 'right' + + left -= width + eAttachment.left = 'right' + + else if left + width > bounds[2] and tAttachment.left is 'right' + if eAttachment.left is 'left' + left -= targetWidth + tAttachment.left = 'left' + + left -= width + eAttachment.left = 'right' + + else if eAttachment.left is 'right' + left -= targetWidth + tAttachment.left = 'left' + + left += width + eAttachment.left = 'left' + + else if tAttachment.left is 'center' + if left + width > bounds[2] and eAttachment.left is 'left' + left -= width + eAttachment.left = 'right' + + else if left < bounds[0] and eAttachment.left is 'right' + left += width + eAttachment.left = 'left' + + if changeAttachY in ['element', 'both'] + if (top < bounds[1] and eAttachment.top is 'bottom') + top += height + eAttachment.top = 'top' + + if (top + height > bounds[3] and eAttachment.top is 'top') + top -= height + eAttachment.top = 'bottom' + + if changeAttachX in ['element', 'both'] + if (left < bounds[0] and eAttachment.left is 'right') + left += width + eAttachment.left = 'left' + + if (left + width > bounds[2] and eAttachment.left is 'left') + left -= width + eAttachment.left = 'right' + + if typeof pin is 'string' + pin = (p.trim() for p in pin.split ',') + else if pin is true + pin = ['top', 'left', 'right', 'bottom'] + + pin or= [] + + pinned = [] + oob = [] + if top < bounds[1] + if 'top' in pin + top = bounds[1] + pinned.push 'top' + else + oob.push 'top' + + if top + height > bounds[3] + if 'bottom' in pin + top = bounds[3] - height + pinned.push 'bottom' + else + oob.push 'bottom' + + if left < bounds[0] + if 'left' in pin + left = bounds[0] + pinned.push 'left' + else + oob.push 'left' + + if left + width > bounds[2] + if 'right' in pin + left = bounds[2] - width + pinned.push 'right' + else + oob.push 'right' + + if pinned.length + pinnedClass = @options.pinnedClass ? @getClass('pinned') + addClasses.push pinnedClass + for side in pinned + addClasses.push "#{ pinnedClass }-#{ side }" + + if oob.length + oobClass = @options.outOfBoundsClass ? @getClass('out-of-bounds') + addClasses.push oobClass + for side in oob + addClasses.push "#{ oobClass }-#{ side }" + + if 'left' in pinned or 'right' in pinned + eAttachment.left = tAttachment.left = false + if 'top' in pinned or 'bottom' in pinned + eAttachment.top = tAttachment.top = false + + if tAttachment.top isnt targetAttachment.top or tAttachment.left isnt targetAttachment.left or eAttachment.top isnt @attachment.top or eAttachment.left isnt @attachment.left + @updateAttachClasses eAttachment, tAttachment + + defer => + updateClasses @target, addClasses, allClasses + updateClasses @element, addClasses, allClasses + + {top, left} diff --git a/afb-client/bower_components/tether/coffee/markAttachment.coffee b/afb-client/bower_components/tether/coffee/markAttachment.coffee new file mode 100644 index 0000000..b29c9ef --- /dev/null +++ b/afb-client/bower_components/tether/coffee/markAttachment.coffee @@ -0,0 +1,30 @@ +@Tether.modules.push + initialize: -> + @markers = {} + + for type in ['target', 'element'] + el = document.createElement 'div' + el.className = @getClass("#{ type }-marker") + + dot = document.createElement 'div' + dot.className = @getClass('marker-dot') + el.appendChild dot + + @[type].appendChild el + + @markers[type] = {dot, el} + + position: ({manualOffset, manualTargetOffset}) -> + offsets = + element: manualOffset + target: manualTargetOffset + + for type, offset of offsets + for side, val of offset + if typeof val isnt 'string' or (val.indexOf('%') is -1 and val.indexOf('px') is -1) + val += 'px' + + if @markers[type].dot.style[side] isnt val + @markers[type].dot.style[side] = val + + true diff --git a/afb-client/bower_components/tether/coffee/shift.coffee b/afb-client/bower_components/tether/coffee/shift.coffee new file mode 100644 index 0000000..4dede41 --- /dev/null +++ b/afb-client/bower_components/tether/coffee/shift.coffee @@ -0,0 +1,27 @@ +@Tether.modules.push + position: ({top, left}) -> + return unless @options.shift + + result = (val) -> + if typeof val is 'function' + val.call @, {top, left} + else + val + + shift = result @options.shift + + if typeof shift is 'string' + shift = shift.split(' ') + shift[1] or= shift[0] + + [shiftTop, shiftLeft] = shift + + shiftTop = parseFloat shiftTop, 10 + shiftLeft = parseFloat shiftLeft, 10 + else + [shiftTop, shiftLeft] = [shift.top, shift.left] + + top += shiftTop + left += shiftLeft + + {top, left} diff --git a/afb-client/bower_components/tether/coffee/tether.coffee b/afb-client/bower_components/tether/coffee/tether.coffee new file mode 100644 index 0000000..3df246f --- /dev/null +++ b/afb-client/bower_components/tether/coffee/tether.coffee @@ -0,0 +1,573 @@ +if not @Tether? + throw new Error "You must include the utils.js file before tether.js" + +Tether = @Tether + +{getScrollParent, getSize, getOuterSize, getBounds, getOffsetParent, extend, addClass, removeClass, updateClasses, defer, flush, getScrollBarSize} = Tether.Utils + +within = (a, b, diff=1) -> + a + diff >= b >= a - diff + +transformKey = do -> + el = document.createElement 'div' + + for key in ['transform', 'webkitTransform', 'OTransform', 'MozTransform', 'msTransform'] + if el.style[key] isnt undefined + return key + +tethers = [] + +position = -> + for tether in tethers + tether.position(false) + + flush() + +now = -> + performance?.now?() ? +new Date + +do -> + lastCall = null + lastDuration = null + pendingTimeout = null + + tick = -> + if lastDuration? and lastDuration > 16 + # We voluntarily throttle ourselves if we can't manage 60fps + lastDuration = Math.min(lastDuration - 16, 250) + + # Just in case this is the last event, remember to position just once more + pendingTimeout = setTimeout tick, 250 + return + + if lastCall? and (now() - lastCall) < 10 + # Some browsers call events a little too frequently, refuse to run more than is reasonable + return + + if pendingTimeout? + clearTimeout pendingTimeout + pendingTimeout = null + + lastCall = now() + + position() + + lastDuration = now() - lastCall + + for event in ['resize', 'scroll', 'touchmove'] + window.addEventListener event, tick + +MIRROR_LR = + center: 'center' + left: 'right' + right: 'left' + +MIRROR_TB = + middle: 'middle' + top: 'bottom' + bottom: 'top' + +OFFSET_MAP = + top: 0 + left: 0 + middle: '50%' + center: '50%' + bottom: '100%' + right: '100%' + +autoToFixedAttachment = (attachment, relativeToAttachment) -> + {left, top} = attachment + + if left is 'auto' + left = MIRROR_LR[relativeToAttachment.left] + + if top is 'auto' + top = MIRROR_TB[relativeToAttachment.top] + + {left, top} + +attachmentToOffset = (attachment) -> + return { + left: OFFSET_MAP[attachment.left] ? attachment.left + top: OFFSET_MAP[attachment.top] ? attachment.top + } + +addOffset = (offsets...) -> + out = {top: 0, left: 0} + + for {top, left} in offsets + if typeof top is 'string' + top = parseFloat(top, 10) + if typeof left is 'string' + left = parseFloat(left, 10) + + out.top += top + out.left += left + + out + +offsetToPx = (offset, size) -> + if typeof offset.left is 'string' and offset.left.indexOf('%') isnt -1 + offset.left = parseFloat(offset.left, 10) / 100 * size.width + if typeof offset.top is 'string' and offset.top.indexOf('%') isnt -1 + offset.top = parseFloat(offset.top, 10) / 100 * size.height + + offset + +parseAttachment = parseOffset = (value) -> + [top, left] = value.split(' ') + + {top, left} + +class _Tether + @modules: [] + + constructor: (options) -> + tethers.push @ + + @history = [] + + @setOptions options, false + + for module in Tether.modules + module.initialize?.call(@) + + @position() + + getClass: (key) -> + if @options.classes?[key] + @options.classes[key] + else if @options.classes?[key] isnt false + if @options.classPrefix + "#{ @options.classPrefix }-#{ key }" + else + key + else + '' + + setOptions: (@options, position=true) -> + defaults = + offset: '0 0' + targetOffset: '0 0' + targetAttachment: 'auto auto' + classPrefix: 'tether' + + @options = extend defaults, @options + + {@element, @target, @targetModifier} = @options + + if @target is 'viewport' + @target = document.body + @targetModifier = 'visible' + else if @target is 'scroll-handle' + @target = document.body + @targetModifier = 'scroll-handle' + + for key in ['element', 'target'] + if not @[key]? + throw new Error "Tether Error: Both element and target must be defined" + + if @[key].jquery? + @[key] = @[key][0] + else if typeof @[key] is 'string' + @[key] = document.querySelector @[key] + + addClass @element, @getClass 'element' + addClass @target, @getClass 'target' + + if not @options.attachment + throw new Error "Tether Error: You must provide an attachment" + + @targetAttachment = parseAttachment @options.targetAttachment + @attachment = parseAttachment @options.attachment + @offset = parseOffset @options.offset + @targetOffset = parseOffset @options.targetOffset + + if @scrollParent? + @disable() + + if @targetModifier is 'scroll-handle' + @scrollParent = @target + else + @scrollParent = getScrollParent @target + + unless @options.enabled is false + @enable(position) + + getTargetBounds: -> + if @targetModifier? + switch @targetModifier + when 'visible' + if @target is document.body + {top: pageYOffset, left: pageXOffset, height: innerHeight, width: innerWidth} + else + bounds = getBounds @target + + out = + height: bounds.height + width: bounds.width + top: bounds.top + left: bounds.left + + out.height = Math.min(out.height, bounds.height - (pageYOffset - bounds.top)) + out.height = Math.min(out.height, bounds.height - ((bounds.top + bounds.height) - (pageYOffset + innerHeight))) + out.height = Math.min(innerHeight, out.height) + out.height -= 2 + + out.width = Math.min(out.width, bounds.width - (pageXOffset - bounds.left)) + out.width = Math.min(out.width, bounds.width - ((bounds.left + bounds.width) - (pageXOffset + innerWidth))) + out.width = Math.min(innerWidth, out.width) + out.width -= 2 + + if out.top < pageYOffset + out.top = pageYOffset + if out.left < pageXOffset + out.left = pageXOffset + + out + + when 'scroll-handle' + target = @target + if target is document.body + target = document.documentElement + + bounds = + left: pageXOffset + top: pageYOffset + height: innerHeight + width: innerWidth + else + bounds = getBounds target + + style = getComputedStyle target + + hasBottomScroll = target.scrollWidth > target.clientWidth or 'scroll' is [style.overflow, style.overflowX] or @target isnt document.body + + scrollBottom = 0 + if hasBottomScroll + scrollBottom = 15 + + height = bounds.height - parseFloat(style.borderTopWidth) - parseFloat(style.borderBottomWidth) - scrollBottom + + out = + width: 15 + height: height * 0.975 * (height / target.scrollHeight) + left: bounds.left + bounds.width - parseFloat(style.borderLeftWidth) - 15 + + fitAdj = 0 + if height < 408 and @target is document.body + fitAdj = -0.00011 * Math.pow(height, 2) - 0.00727 * height + 22.58 + + if @target isnt document.body + out.height = Math.max out.height, 24 + + scrollPercentage = @target.scrollTop / (target.scrollHeight - height) + out.top = scrollPercentage * (height - out.height - fitAdj) + bounds.top + parseFloat(style.borderTopWidth) + + if @target is document.body + out.height = Math.max out.height, 24 + + out + else + getBounds @target + + clearCache: -> + @_cache = {} + + cache: (k, getter) -> + # More than one module will often need the same DOM info, so + # we keep a cache which is cleared on each position call + @_cache ?= {} + + if not @_cache[k]? + @_cache[k] = getter.call(@) + + @_cache[k] + + enable: (position=true) -> + addClass @target, @getClass 'enabled' + addClass @element, @getClass 'enabled' + @enabled = true + + if @scrollParent isnt document + @scrollParent.addEventListener 'scroll', @position + + if position + @position() + + disable: -> + removeClass @target, @getClass 'enabled' + removeClass @element, @getClass 'enabled' + @enabled = false + + if @scrollParent? + @scrollParent.removeEventListener 'scroll', @position + + destroy: -> + @disable() + + for tether, i in tethers + if tether is @ + tethers.splice i, 1 + break + + updateAttachClasses: (elementAttach=@attachment, targetAttach=@targetAttachment) -> + sides = ['left', 'top', 'bottom', 'right', 'middle', 'center'] + + if @_addAttachClasses?.length + # updateAttachClasses can be called more than once in a position call, so + # we need to clean up after ourselves such that when the last defer gets + # ran it doesn't add any extra classes from previous calls. + @_addAttachClasses.splice 0, @_addAttachClasses.length + + add = @_addAttachClasses ?= [] + add.push "#{ @getClass('element-attached') }-#{ elementAttach.top }" if elementAttach.top + add.push "#{ @getClass('element-attached') }-#{ elementAttach.left }" if elementAttach.left + add.push "#{ @getClass('target-attached') }-#{ targetAttach.top }" if targetAttach.top + add.push "#{ @getClass('target-attached') }-#{ targetAttach.left }" if targetAttach.left + + all = [] + all.push "#{ @getClass('element-attached') }-#{ side }" for side in sides + all.push "#{ @getClass('target-attached') }-#{ side }" for side in sides + + defer => + return unless @_addAttachClasses? + + updateClasses @element, @_addAttachClasses, all + updateClasses @target, @_addAttachClasses, all + + @_addAttachClasses = undefined + + position: (flushChanges=true) => + # flushChanges commits the changes immediately, leave true unless you are positioning multiple + # tethers (in which case call Tether.Utils.flush yourself when you're done) + + return unless @enabled + + @clearCache() + + # Turn 'auto' attachments into the appropriate corner or edge + targetAttachment = autoToFixedAttachment(@targetAttachment, @attachment) + + @updateAttachClasses @attachment, targetAttachment + + elementPos = @cache 'element-bounds', => getBounds @element + {width, height} = elementPos + + if width is 0 and height is 0 and @lastSize? + # We cache the height and width to make it possible to position elements that are + # getting hidden. + {width, height} = @lastSize + else + @lastSize = {width, height} + + targetSize = targetPos = @cache 'target-bounds', => @getTargetBounds() + + # Get an actual px offset from the attachment + offset = offsetToPx attachmentToOffset(@attachment), {width, height} + targetOffset = offsetToPx attachmentToOffset(targetAttachment), targetSize + + manualOffset = offsetToPx(@offset, {width, height}) + manualTargetOffset = offsetToPx(@targetOffset, targetSize) + + # Add the manually provided offset + offset = addOffset offset, manualOffset + targetOffset = addOffset targetOffset, manualTargetOffset + + # It's now our goal to make (element position + offset) == (target position + target offset) + left = targetPos.left + targetOffset.left - offset.left + top = targetPos.top + targetOffset.top - offset.top + + for module in Tether.modules + ret = module.position.call(@, {left, top, targetAttachment, targetPos, @attachment, elementPos, offset, targetOffset, manualOffset, manualTargetOffset, scrollbarSize}) + + if not ret? or typeof ret isnt 'object' + continue + else if ret is false + return false + else + {top, left} = ret + + # We describe the position three different ways to give the optimizer + # a chance to decide the best possible way to position the element + # with the fewest repaints. + next = { + # It's position relative to the page (absolute positioning when + # the element is a child of the body) + page: + top: top + left: left + + # It's position relative to the viewport (fixed positioning) + viewport: + top: top - pageYOffset + bottom: pageYOffset - top - height + innerHeight + left: left - pageXOffset + right: pageXOffset - left - width + innerWidth + } + + if document.body.scrollWidth > window.innerWidth + scrollbarSize = @cache 'scrollbar-size', getScrollBarSize + next.viewport.bottom -= scrollbarSize.height + + if document.body.scrollHeight > window.innerHeight + scrollbarSize = @cache 'scrollbar-size', getScrollBarSize + next.viewport.right -= scrollbarSize.width + + if document.body.style.position not in ['', 'static'] or document.body.parentElement.style.position not in ['', 'static'] + # Absolute positioning in the body will be relative to the page, not the 'initial containing block' + next.page.bottom = document.body.scrollHeight - top - height + next.page.right = document.body.scrollWidth - left - width + + if @options.optimizations?.moveElement isnt false and not @targetModifier? + offsetParent = @cache 'target-offsetparent', => getOffsetParent @target + offsetPosition = @cache 'target-offsetparent-bounds', -> getBounds offsetParent + offsetParentStyle = getComputedStyle offsetParent + elementStyle = getComputedStyle @element + offsetParentSize = offsetPosition + + offsetBorder = {} + for side in ['Top', 'Left', 'Bottom', 'Right'] + offsetBorder[side.toLowerCase()] = parseFloat offsetParentStyle["border#{ side }Width"] + + offsetPosition.right = document.body.scrollWidth - offsetPosition.left - offsetParentSize.width + offsetBorder.right + offsetPosition.bottom = document.body.scrollHeight - offsetPosition.top - offsetParentSize.height + offsetBorder.bottom + + if next.page.top >= (offsetPosition.top + offsetBorder.top) and next.page.bottom >= offsetPosition.bottom + if next.page.left >= (offsetPosition.left + offsetBorder.left) and next.page.right >= offsetPosition.right + # We're within the visible part of the target's scroll parent + + scrollTop = offsetParent.scrollTop + scrollLeft = offsetParent.scrollLeft + + # It's position relative to the target's offset parent (absolute positioning when + # the element is moved to be a child of the target's offset parent). + next.offset = + top: next.page.top - offsetPosition.top + scrollTop - offsetBorder.top + left: next.page.left - offsetPosition.left + scrollLeft - offsetBorder.left + + + # We could also travel up the DOM and try each containing context, rather than only + # looking at the body, but we're gonna get diminishing returns. + + @move next + + @history.unshift next + + if @history.length > 3 + @history.pop() + + if flushChanges + flush() + + true + + move: (position) -> + return if not @element.parentNode? + + same = {} + + for type of position + same[type] = {} + + for key of position[type] + found = false + + for point in @history + unless within(point[type]?[key], position[type][key]) + found = true + break + + if not found + same[type][key] = true + + css = {top: '', left: '', right: '', bottom: ''} + + transcribe = (same, pos) => + if @options.optimizations?.gpu isnt false + if same.top + css.top = 0 + yPos = pos.top + else + css.bottom = 0 + yPos = -pos.bottom + + if same.left + css.left = 0 + xPos = pos.left + else + css.right = 0 + xPos = -pos.right + + + css[transformKey] = "translateX(#{ Math.round xPos }px) translateY(#{ Math.round yPos }px)" + + if transformKey isnt 'msTransform' + # The Z transform will keep this in the GPU (faster, and prevents artifacts), + # but IE9 doesn't support 3d transforms and will choke. + css[transformKey] += " translateZ(0)" + + else + if same.top + css.top = "#{ pos.top }px" + else + css.bottom = "#{ pos.bottom }px" + + if same.left + css.left = "#{ pos.left }px" + else + css.right = "#{ pos.right }px" + + moved = false + if (same.page.top or same.page.bottom) and (same.page.left or same.page.right) + css.position = 'absolute' + transcribe same.page, position.page + + else if (same.viewport.top or same.viewport.bottom) and (same.viewport.left or same.viewport.right) + css.position = 'fixed' + + transcribe same.viewport, position.viewport + + else if same.offset? and same.offset.top and same.offset.left + css.position = 'absolute' + + offsetParent = @cache 'target-offsetparent', => getOffsetParent @target + + if getOffsetParent(@element) isnt offsetParent + defer => + @element.parentNode.removeChild @element + offsetParent.appendChild @element + + transcribe same.offset, position.offset + + moved = true + + else + css.position = 'absolute' + transcribe {top: true, left: true}, position.page + + if not moved and @element.parentNode.tagName isnt 'BODY' + @element.parentNode.removeChild @element + document.body.appendChild @element + + # Any css change will trigger a repaint, so let's avoid one if nothing changed + writeCSS = {} + write = false + for key, val of css + elVal = @element.style[key] + + if elVal isnt '' and val isnt '' and key in ['top', 'left', 'bottom', 'right'] + elVal = parseFloat elVal + val = parseFloat val + + if elVal isnt val + write = true + writeCSS[key] = css[key] + + if write + defer => + extend @element.style, writeCSS + +Tether.position = position + +@Tether = extend _Tether, Tether diff --git a/afb-client/bower_components/tether/coffee/utils.coffee b/afb-client/bower_components/tether/coffee/utils.coffee new file mode 100644 index 0000000..ef1c0ed --- /dev/null +++ b/afb-client/bower_components/tether/coffee/utils.coffee @@ -0,0 +1,211 @@ +@Tether ?= {modules: []} + +getScrollParent = (el) -> + position = getComputedStyle(el).position + + if position is 'fixed' + return el + + scrollParent = undefined + + parent = el + while parent = parent.parentNode + try + style = getComputedStyle parent + + return parent if not style? + + if /(auto|scroll)/.test(style['overflow'] + style['overflow-y'] + style['overflow-x']) + if position isnt 'absolute' or style['position'] in ['relative', 'absolute', 'fixed'] + return parent + + return document.body + +uniqueId = do -> + id = 0 + -> + id++ + +zeroPosCache = {} +getOrigin = (doc) -> + # getBoundingClientRect is unfortunately too accurate. It introduces a pixel or two of + # jitter as the user scrolls that messes with our ability to detect if two positions + # are equivilant or not. We place an element at the top left of the page that will + # get the same jitter, so we can cancel the two out. + node = doc._tetherZeroElement + if not node? + node = doc.createElement 'div' + node.setAttribute 'data-tether-id', uniqueId() + extend node.style, + top: 0 + left: 0 + position: 'absolute' + + doc.body.appendChild node + + doc._tetherZeroElement = node + + id = node.getAttribute 'data-tether-id' + if not zeroPosCache[id]? + zeroPosCache[id] = {} + for k, v of node.getBoundingClientRect() + # Can't use extend, as on IE9, elements don't resolve to be hasOwnProperty + zeroPosCache[id][k] = v + + # Clear the cache when this position call is done + defer -> + zeroPosCache[id] = undefined + + return zeroPosCache[id] + +node = null +getBounds = (el) -> + if el is document + doc = document + el = document.documentElement + else + doc = el.ownerDocument + + docEl = doc.documentElement + + box = {} + # The original object returned by getBoundingClientRect is immutable, so we clone it + # We can't use extend because the properties are not considered part of the object by hasOwnProperty in IE9 + for k, v of el.getBoundingClientRect() + box[k] = v + + origin = getOrigin doc + + box.top -= origin.top + box.left -= origin.left + + box.width ?= document.body.scrollWidth - box.left - box.right + box.height ?= document.body.scrollHeight - box.top - box.bottom + + box.top = box.top - docEl.clientTop + box.left = box.left - docEl.clientLeft + box.right = doc.body.clientWidth - box.width - box.left + box.bottom = doc.body.clientHeight - box.height - box.top + + box + +getOffsetParent = (el) -> + el.offsetParent or document.documentElement + +getScrollBarSize = -> + inner = document.createElement 'div' + inner.style.width = '100%' + inner.style.height = '200px' + + outer = document.createElement 'div' + extend outer.style, + position: 'absolute' + top: 0 + left: 0 + pointerEvents: 'none' + visibility: 'hidden' + width: '200px' + height: '150px' + overflow: 'hidden' + + outer.appendChild inner + + document.body.appendChild outer + + widthContained = inner.offsetWidth + outer.style.overflow = 'scroll' + widthScroll = inner.offsetWidth + + if widthContained is widthScroll + widthScroll = outer.clientWidth + + document.body.removeChild outer + + width = widthContained - widthScroll + + {width, height: width} + +extend = (out={}) -> + args = [] + Array::push.apply(args, arguments) + + for obj in args[1..] when obj + for own key, val of obj + out[key] = val + + out + +removeClass = (el, name) -> + if el.classList? + el.classList.remove(cls) for cls in name.split(' ') when cls.trim() + else + el.className = el.className.replace new RegExp("(^| )#{ name.split(' ').join('|') }( |$)", 'gi'), ' ' + +addClass = (el, name) -> + if el.classList? + el.classList.add(cls) for cls in name.split(' ') when cls.trim() + else + removeClass el, name + el.className += " #{ name }" + +hasClass = (el, name) -> + if el.classList? + el.classList.contains(name) + else + new RegExp("(^| )#{ name }( |$)", 'gi').test(el.className) + +updateClasses = (el, add, all) -> + # Of the set of 'all' classes, we need the 'add' classes, and only the + # 'add' classes to be set. + for cls in all when cls not in add + if hasClass(el, cls) + removeClass el, cls + + for cls in add + if not hasClass(el, cls) + addClass el, cls + +deferred = [] + +defer = (fn) -> + deferred.push fn + +flush = -> + fn() while fn = deferred.pop() + +class Evented + on: (event, handler, ctx, once=false) -> + @bindings ?= {} + @bindings[event] ?= [] + @bindings[event].push {handler, ctx, once} + + once: (event, handler, ctx) -> + @on(event, handler, ctx, true) + + off: (event, handler) -> + return unless @bindings?[event]? + + if not handler? + delete @bindings[event] + else + i = 0 + while i < @bindings[event].length + if @bindings[event][i].handler is handler + @bindings[event].splice i, 1 + else + i++ + + trigger: (event, args...) -> + if @bindings?[event] + i = 0 + while i < @bindings[event].length + {handler, ctx, once} = @bindings[event][i] + + handler.apply(ctx ? @, args) + + if once + @bindings[event].splice i, 1 + else + i++ + +@Tether.Utils = {getScrollParent, getBounds, getOffsetParent, extend, addClass, removeClass, hasClass, updateClasses, defer, flush, uniqueId, Evented, getScrollBarSize} |