bkdotcom/PHPDebugConsole

View on GitHub
src/Debug/js_src/enhanceObject.js

Summary

Maintainability
A
3 hrs
Test Coverage
import $ from 'jquery'

var config

export function init ($delegateNode) {
  config = $delegateNode.data('config').get()
  $delegateNode.on('click', '[data-toggle=vis]', function () {
    toggleVis(this)
    return false
  })
  $delegateNode.on('click', '[data-toggle=interface]', function () {
    toggleInterface(this)
    return false
  })
}

function addIcons ($node) {
  // console.warn('addIcons', $node)
  $.each(config.iconsObject, function (selector, v) {
    var prepend = true
    var sMatches = selector.match(/(?:parent(\S+)\s)?(?:context(\S+)\s)?(.*)$/)
    var vMatches = v.match(/^([ap])\s*:(.+)$/)
    var $found
    if (sMatches) {
      if (sMatches[1] && $node.parent().filter(sMatches[1]).length === 0) {
        return
      }
      if (sMatches[2]) {
        $node = $node.filter(sMatches[2])
      }
      selector = sMatches[3]
    }
    if (vMatches) {
      prepend = vMatches[1] === 'p'
      v = vMatches[2]
    }
    $found = $node.find(selector)
    prepend
      ? $found.prepend(v)
      : $found.append(v)
  })
}

/**
 * Adds toggle icon & hides target
 * Minimal DOM manipulation -> apply to all descendants
 */
export function enhance ($node) {
  $node.find('> .classname, > .t_const').each(function () {
    var $classname = $(this)
    var $target = $classname.next()
    var isEnhanced = $classname.data('toggle') === 'object'
    if ($target.is('.t_maxDepth, .t_recursion, .excluded')) {
      $classname.addClass('empty')
      return
    }
    if (isEnhanced) {
      return
    }
    if ($target.length === 0) {
      return
    }
    $classname.wrap('<span data-toggle="object"></span>')
      .after(' <i class="fa ' + config.iconsExpand.expand + '"></i>')
    $target.hide()
  })
}

export function enhanceInner ($obj) {
  var $inner = $obj.find('> .object-inner')
  var accessible = $obj.data('accessible')
  var callPostToggle = null // or "local", or "allDesc"
  if ($obj.is('.enhanced')) {
    return
  }
  $inner.find('> dd > ul > li > .interface, > dd > ul > li > .interface + ul .interface').each(function () {
    var iface = $(this).text()
    if (findInterfaceMethods($obj, iface).length === 0) {
      return
    }
    $(this)
      .addClass('toggle-on')
      .prop('title', 'toggle interface methods')
      .attr('data-toggle', 'interface')
      .attr('data-interface', iface)
  }).filter('.toggle-off').removeClass('toggle-off').each(function () {
    // element may have toggle-off to begin with...
    toggleInterface(this)
  })
  $inner.find('> .private, > .protected')
    .filter('.magic, .magic-read, .magic-write')
    .removeClass('private protected')
  if (accessible === 'public') {
    $inner.find('.private, .protected').hide()
    callPostToggle = 'allDesc'
  }
  visToggles($inner, accessible)
  addIcons($inner)
  $inner.find('> .property.forceShow').show().find('> .t_array').debugEnhance('expand')
  if (callPostToggle) {
    postToggle($obj, callPostToggle === 'allDesc')
  }
  $obj.addClass('enhanced')
}

/**
 * Add toggles for protected, private excluded inherited
 */
function visToggles ($inner, accessible) {
  var flags = {
    hasProtected: $inner.children('.protected').not('.magic, .magic-read, .magic-write').length > 0,
    hasPrivate: $inner.children('.private').not('.magic, .magic-read, .magic-write').length > 0,
    hasExcluded: $inner.children('.debuginfo-excluded').hide().length > 0,
    hasInherited: $inner.children('dd[data-inherited-from]').length > 0
  }
  var toggleClass = accessible === 'public'
    ? 'toggle-off'
    : 'toggle-on'
  var toggleVerb = accessible === 'public'
    ? 'show'
    : 'hide'
  var $visToggles = $('<div class="vis-toggles"></div>')
  if (flags.hasProtected) {
    $visToggles.append('<span class="' + toggleClass + '" data-toggle="vis" data-vis="protected">' + toggleVerb + ' protected</span>')
  }
  if (flags.hasPrivate) {
    $visToggles.append('<span class="' + toggleClass + '" data-toggle="vis" data-vis="private">' + toggleVerb + ' private</span>')
  }
  if (flags.hasExcluded) {
    $visToggles.append('<span class="toggle-off" data-toggle="vis" data-vis="debuginfo-excluded">show excluded</span>')
  }
  if (flags.hasInherited) {
    $visToggles.append('<span class="toggle-on" data-toggle="vis" data-vis="inherited">hide inherited</span>')
  }
  if ($inner.find('> dd[class*=t_modifier_]').length) {
    $inner.find('> dd[class*=t_modifier_]').last().after($visToggles)
    return
  }
  $inner.prepend($visToggles)
}

function toggleInterface (toggle) {
  var $toggle = $(toggle)
  var $obj = $toggle.closest('.t_object')
  $toggle = $toggle.is('.toggle-off')
    ? $toggle.add($toggle.next().find('.toggle-off'))
    : $toggle.add($toggle.next().find('.toggle-on'))
  $toggle.each(function () {
    var $toggle = $(this)
    var iface = $toggle.data('interface')
    var $methods = findInterfaceMethods($obj, iface)
    if ($toggle.is('.toggle-off')) {
      $toggle.addClass('toggle-on').removeClass('toggle-off')
      $methods.show()
    } else {
      $toggle.addClass('toggle-off').removeClass('toggle-on')
      $methods.hide()
    }
  })
  postToggle($obj)
}

function findInterfaceMethods ($obj, iface) {
    var selector = '> .object-inner > dd[data-implements="' + CSS.escape(iface) + '"]'
    return $obj.find(selector)
}

/**
 * Toggle visibility for private/protected properties and methods
 */
function toggleVis (toggle) {
  // console.log('toggleVis', toggle)
  var $toggle = $(toggle)
  var vis = $toggle.data('vis')
  var $obj = $toggle.closest('.t_object')
  var $objInner = $obj.find('> .object-inner')
  var $toggles = $objInner.find('[data-toggle=vis][data-vis=' + vis + ']')
  var selector = vis === 'inherited'
    ? 'dd[data-inherited-from], .private-ancestor'
    : '.' + vis
  var $nodes = $objInner.find(selector)
  var show = $toggle.hasClass('toggle-off')
  $toggles
    .html($toggle.html().replace(
      show ? 'show ' : 'hide ',
      show ? 'hide ' : 'show '
    ))
    .addClass(show ? 'toggle-on' : 'toggle-off')
    .removeClass(show ? 'toggle-off' : 'toggle-on')
  show
    ? toggleVisNodes($nodes) // show for this and all descendants.. unless hidden by other toggle
    : $nodes.hide() // simply hide for this and all descendants
  postToggle($obj, true)
}

function toggleVisNodes ($nodes) {
  $nodes.each(function () {
    var $node = $(this)
    var $objInner = $node.closest('.object-inner')
    var show = true
    $objInner.find('> .vis-toggles [data-toggle]').each(function () {
      var $toggle = $(this)
      var isOn = $toggle.hasClass('toggle-on')
      var vis = $toggle.data('vis')
      var filter = vis === 'inherited'
        ? 'dd[data-inherited-from], .private-ancestor'
        : '.' + vis
      if (!isOn && $node.filter(filter).length === 1) {
        show = false
        return false // break
      }
    })
    if (show) {
      $node.show()
    }
  })
}

function postToggle ($obj, allDescendants) {
  var selector = allDescendants
    ? '.object-inner > dt'
    : '> .object-inner > dt'
  var selector2 = allDescendants
    ? '.object-inner > .heading'
    : '> .object-inner > .heading'
  $obj.find(selector).each(function (i, dt) {
    var $dds = $(dt).nextUntil('dt')
    var $ddsVis = $dds.not('.heading').filter(function (index, node) {
      return $(node).css('display') !== 'none'
    })
    var allHidden = $dds.length > 0 && $ddsVis.length === 0
    $(dt).toggleClass('text-muted', allHidden)
  })
  $obj.find(selector2).each(function (i, heading) {
    var $dds = $(heading).nextUntil('dt, .heading')
    var $ddsVis = $dds.filter(function (index, node) {
      return $(node).css('display') !== 'none'
    })
    var allHidden = $dds.length > 0 && $ddsVis.length === 0
    $(heading).toggleClass('text-muted', allHidden)
  })

  $obj.trigger('expanded.debug.object')
}