kawamanza/jquery-reticence

View on GitHub
sources/lib/jquery-reticence.js.coffee

Summary

Maintainability
Test Coverage
do ($ = jQuery) ->
  dataNamespace = "reticence"
  reticentClass = "reticent"
  TAG_NAME = /^\s*<\/?([^\s>]+).*$/m

  findContainer = (element) ->
    data = ancestor: element
    element.parents().each ->
      node = $ @
      attr = if node.css("overflow") is "hidden" then "container" else "ancestor"
      data[attr] = node
      attr is "ancestor"
    if data.container
      children = data.container.children()
      ancestor = data.ancestor
      if children.length isnt 1 or ancestor is element
        ancestor = $ '<div style="height: auto;">'
        children.appendTo ancestor
        ancestor.appendTo data.container
        data.ancestor = ancestor
    else
      throw "Could not find container with 'overflow: hidden' style"
    data

  tagName = (str) -> str and str.replace TAG_NAME, "$1"

  checkCapture = (element, o, html) ->
    scan = o.scan
    captures = o.captures
    loop
      capture = scan[o.last--]
      isCapturedTag = capture.indexOf("<") + 1
      if isCapturedTag
        if capture.charAt(1) isnt "/" and captures.length and captures[0].indexOf("</") isnt -1 and tagName(capture) is tagName(captures[0])
          captures.shift()
        else
          captures.unshift capture
      o.len = o.len - capture.length
      html = html.substr 0, o.len
      break if o.last is -1 or not isCapturedTag
    element.html html + "..." + captures.join("")
    unless o.reticent
      o.reticent = true
      o.container.addClass reticentClass
    return

  redraw = (element) ->
    data = element.data dataNamespace
    ancestor = data.ancestor
    o =
      scan: data.scan
      container: data.container
      last: data.scan.length - 1
      captures: []
    loop
      if html
        checkCapture element, o, html
      else
        html = o.scan.join ""
        o.len = html.length
        o.reticent = false
        o.container.removeClass reticentClass
        element.html html
        cHeight = o.container.height()
      aHeight = ancestor.height()
      break if aHeight <= cHeight or o.last is -1
    return

  bindRedraw = (element) ->
    r = null
    f = ->
      redraw element
      r = null
      return
    element.data(dataNamespace).container.resize ->
      clearTimeout r if r
      r = setTimeout f, 30
      return
    return

  applyReticence = (element, options) ->
    element = $ element
    data = element.data dataNamespace
    return redraw element if data
    data = findContainer element
    regex = main.matchers[options.reduceMode or "char"]
    data.text = element.html()
    data.scan = data.text.match regex
    element.data dataNamespace, data
    redraw element
    bindRedraw element if options.resizable
    return

  main = (options) ->
    options = $.extend {}, options
    @each ->
      applyReticence @, options
      return

  main.matchers =
    char: /\s*(?:<.*?>|[^\s<])/gm
    word: /\s*(?:<.*?>|[^\s<]+)/gm

  $.fn.reticence = main

  return