399 lines · 10.1 KB · Text
RawDownload
## Traversing# @$ = (selector, el = document) ->  try el.querySelector(selector) catch @$$ = (selector, el = document) ->  try el.querySelectorAll(selector) catch $.id = (id) ->  document.getElementById(id) $.hasChild = (parent, el) ->  return unless parent  while el    return true if el is parent    return if el is document.body    el = el.parentNode $.closestLink = (el, parent = document.body) ->  while el    return el if el.tagName is 'A'    return if el is parent    el = el.parentNode ## Events# $.on = (el, event, callback, useCapture = false) ->  if event.indexOf(' ') >= 0    $.on el, name, callback for name in event.split(' ')  else    el.addEventListener(event, callback, useCapture)  return $.off = (el, event, callback, useCapture = false) ->  if event.indexOf(' ') >= 0    $.off el, name, callback for name in event.split(' ')  else    el.removeEventListener(event, callback, useCapture)  return $.trigger = (el, type, canBubble = true, cancelable = true) ->  event = document.createEvent 'Event'  event.initEvent(type, canBubble, cancelable)  el.dispatchEvent(event)  return $.click = (el) ->  event = document.createEvent 'MouseEvent'  event.initMouseEvent 'click', true, true, window, null, 0, 0, 0, 0, false, false, false, false, 0, null  el.dispatchEvent(event)  return $.stopEvent = (event) ->  event.preventDefault()  event.stopPropagation()  event.stopImmediatePropagation()  return $.eventTarget = (event) ->  event.target.correspondingUseElement || event.target ## Manipulation# buildFragment = (value) ->  fragment = document.createDocumentFragment()   if $.isCollection(value)    fragment.appendChild(child) for child in $.makeArray(value)  else    fragment.innerHTML = value   fragment $.append = (el, value) ->  if typeof value is 'string'    el.insertAdjacentHTML 'beforeend', value  else    value = buildFragment(value) if $.isCollection(value)    el.appendChild(value)  return $.prepend = (el, value) ->  if not el.firstChild    $.append(value)  else if typeof value is 'string'    el.insertAdjacentHTML 'afterbegin', value  else    value = buildFragment(value) if $.isCollection(value)    el.insertBefore(value, el.firstChild)  return $.before = (el, value) ->  if typeof value is 'string' or $.isCollection(value)    value = buildFragment(value)   el.parentNode.insertBefore(value, el)  return $.after = (el, value) ->  if typeof value is 'string' or $.isCollection(value)    value = buildFragment(value)   if el.nextSibling    el.parentNode.insertBefore(value, el.nextSibling)  else    el.parentNode.appendChild(value)  return $.remove = (value) ->  if $.isCollection(value)    el.parentNode?.removeChild(el) for el in $.makeArray(value)  else    value.parentNode?.removeChild(value)  return $.empty = (el) ->  el.removeChild(el.firstChild) while el.firstChild  return # Calls the function while the element is off the DOM to avoid triggering# unnecessary reflows and repaints.$.batchUpdate = (el, fn) ->  parent = el.parentNode  sibling = el.nextSibling  parent.removeChild(el)   fn(el)   if (sibling)    parent.insertBefore(el, sibling)  else    parent.appendChild(el)  return ## Offset# $.rect = (el) ->  el.getBoundingClientRect() $.offset = (el, container = document.body) ->  top = 0  left = 0   while el and el isnt container    top += el.offsetTop    left += el.offsetLeft    el = el.offsetParent   top: top  left: left $.scrollParent = (el) ->  while (el = el.parentNode) and el.nodeType is 1    break if el.scrollTop > 0    break if getComputedStyle(el)?.overflowY in ['auto', 'scroll']  el $.scrollTo = (el, parent, position = 'center', options = {}) ->  return unless el   parent ?= $.scrollParent(el)  return unless parent   parentHeight = parent.clientHeight  parentScrollHeight = parent.scrollHeight  return unless parentScrollHeight > parentHeight   top = $.offset(el, parent).top  offsetTop = parent.firstElementChild.offsetTop   switch position    when 'top'      parent.scrollTop = top - offsetTop - (if options.margin? then options.margin else 0)    when 'center'      parent.scrollTop = top - Math.round(parentHeight / 2 - el.offsetHeight / 2)    when 'continuous'      scrollTop = parent.scrollTop      height = el.offsetHeight       lastElementOffset = parent.lastElementChild.offsetTop + parent.lastElementChild.offsetHeight      offsetBottom = if lastElementOffset > 0 then parentScrollHeight - lastElementOffset else 0       # If the target element is above the visible portion of its scrollable      # ancestor, move it near the top with a gap = options.topGap * target's height.      if top - offsetTop <= scrollTop + height * (options.topGap or 1)        parent.scrollTop = top - offsetTop - height * (options.topGap or 1)      # If the target element is below the visible portion of its scrollable      # ancestor, move it near the bottom with a gap = options.bottomGap * target's height.      else if top + offsetBottom >= scrollTop + parentHeight - height * ((options.bottomGap or 1) + 1)        parent.scrollTop = top + offsetBottom - parentHeight + height * ((options.bottomGap or 1) + 1)  return $.scrollToWithImageLock = (el, parent, args...) ->  parent ?= $.scrollParent(el)  return unless parent   $.scrollTo el, parent, args...   # Lock the scroll position on the target element for up to 3 seconds while  # nearby images are loaded and rendered.  for image in parent.getElementsByTagName('img') when not image.complete    do ->      onLoad = (event) ->        clearTimeout(timeout)        unbind(event.target)        $.scrollTo el, parent, args...       unbind = (target) ->        $.off target, 'load', onLoad       $.on image, 'load', onLoad      timeout = setTimeout unbind.bind(null, image), 3000  return # Calls the function while locking the element's position relative to the window.$.lockScroll = (el, fn) ->  if parent = $.scrollParent(el)    top = $.rect(el).top    top -= $.rect(parent).top unless parent in [document.body, document.documentElement]    fn()    parent.scrollTop = $.offset(el, parent).top - top  else    fn()  return smoothScroll =  smoothStart = smoothEnd = smoothDistance = smoothDuration = null $.smoothScroll = (el, end) ->  unless window.requestAnimationFrame    el.scrollTop = end    return   smoothEnd = end   if smoothScroll    newDistance = smoothEnd - smoothStart    smoothDuration += Math.min 300, Math.abs(smoothDistance - newDistance)    smoothDistance = newDistance    return   smoothStart = el.scrollTop  smoothDistance = smoothEnd - smoothStart  smoothDuration = Math.min 300, Math.abs(smoothDistance)  startTime = Date.now()   smoothScroll = ->    p = Math.min 1, (Date.now() - startTime) / smoothDuration    y = Math.max 0, Math.floor(smoothStart + smoothDistance * (if p < 0.5 then 2 * p * p else p * (4 - p * 2) - 1))    el.scrollTop = y    if p is 1      smoothScroll = null    else      requestAnimationFrame(smoothScroll)  requestAnimationFrame(smoothScroll) ## Utilities# $.extend = (target, objects...) ->  for object in objects when object    for key, value of object      target[key] = value  target $.makeArray = (object) ->  if Array.isArray(object)    object  else    Array::slice.apply(object) $.arrayDelete = (array, object) ->  index = array.indexOf(object)  if index >= 0    array.splice(index, 1)    true  else    false # Returns true if the object is an array or a collection of DOM elements.$.isCollection = (object) ->  Array.isArray(object) or typeof object?.item is 'function' ESCAPE_HTML_MAP =  '&': '&amp;'  '<': '&lt;'  '>': '&gt;'  '"': '&quot;'  "'": '&#x27;'  '/': '&#x2F;' ESCAPE_HTML_REGEXP = /[&<>"'\/]/g $.escape = (string) ->  string.replace ESCAPE_HTML_REGEXP, (match) -> ESCAPE_HTML_MAP[match] ESCAPE_REGEXP = /([.*+?^=!:${}()|\[\]\/\\])/g $.escapeRegexp = (string) ->  string.replace ESCAPE_REGEXP, "\\$1" $.urlDecode = (string) ->  decodeURIComponent string.replace(/\+/g, '%20') $.classify = (string) ->  string = string.split('_')  for substr, i in string    string[i] = substr[0].toUpperCase() + substr[1..]  string.join('') $.framify = (fn, obj) ->  if window.requestAnimationFrame    (args...) -> requestAnimationFrame(fn.bind(obj, args...))  else    fn $.requestAnimationFrame = (fn) ->  if window.requestAnimationFrame    requestAnimationFrame(fn)  else    setTimeout(fn, 0)  return ## Miscellaneous# $.noop = -> $.popup = (value) ->  try    win = window.open()    win.opener = null if win.opener    win.location = value.href or value  catch    window.open value.href or value, '_blank'  return isMac = null$.isMac = ->  isMac ?= navigator.userAgent?.indexOf('Mac') >= 0 isIE = null$.isIE = ->  isIE ?= navigator.userAgent?.indexOf('MSIE') >= 0 || navigator.userAgent?.indexOf('rv:11.0') >= 0 isChromeForAndroid = null$.isChromeForAndroid = ->  isChromeForAndroid ?= navigator.userAgent?.indexOf('Android') >= 0 && /Chrome\/([.0-9])+ Mobile/.test(navigator.userAgent) isAndroid = null$.isAndroid = ->  isAndroid ?= navigator.userAgent?.indexOf('Android') >= 0 isIOS = null$.isIOS = ->  isIOS ?= navigator.userAgent?.indexOf('iPhone') >= 0 || navigator.userAgent?.indexOf('iPad') >= 0 $.overlayScrollbarsEnabled = ->  return false unless $.isMac()  div = document.createElement('div')  div.setAttribute('style', 'width: 100px; height: 100px; overflow: scroll; position: absolute')  document.body.appendChild(div)  result = div.offsetWidth is div.clientWidth  document.body.removeChild(div)  result HIGHLIGHT_DEFAULTS =  className: 'highlight'  delay: 1000 $.highlight = (el, options = {}) ->  options = $.extend {}, HIGHLIGHT_DEFAULTS, options  el.classList.add(options.className)  setTimeout (-> el.classList.remove(options.className)), options.delay  return $.copyToClipboard = (string) ->  textarea = document.createElement('textarea')  textarea.style.position = 'fixed'  textarea.style.opacity = 0  textarea.value = string  document.body.appendChild(textarea)  try    textarea.select()    result = !!document.execCommand('copy')  catch    result = false  finally    document.body.removeChild(textarea)  result