summaryrefslogtreecommitdiffstats
path: root/afb-client/bower_components/tether/coffee
diff options
context:
space:
mode:
authorStephane Desneux <stephane.desneux@iot.bzh>2016-05-31 18:16:48 +0200
committerStephane Desneux <stephane.desneux@iot.bzh>2016-05-31 18:16:48 +0200
commit5b1e6cc132f44262a873fa8296a2a3e1017b0278 (patch)
tree43b2cd54e2e300b399ff3f2af4458a2c4ed8a144 /afb-client/bower_components/tether/coffee
parentf7d2f9ac4168ee5064580c666d508667a73cefc0 (diff)
parent85ace9c1ce9a98e9b8a22f045c7dd752b38d9129 (diff)
Merge afb-client
Diffstat (limited to 'afb-client/bower_components/tether/coffee')
-rw-r--r--afb-client/bower_components/tether/coffee/abutment.coffee40
-rw-r--r--afb-client/bower_components/tether/coffee/constraint.coffee260
-rw-r--r--afb-client/bower_components/tether/coffee/markAttachment.coffee30
-rw-r--r--afb-client/bower_components/tether/coffee/shift.coffee27
-rw-r--r--afb-client/bower_components/tether/coffee/tether.coffee573
-rw-r--r--afb-client/bower_components/tether/coffee/utils.coffee211
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}