src/Debug/js_src/enhanceObject.js
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) {
$.each(config.iconsObject, function (selector, v) {
var $found = addIconFind($node, selector)
var vMatches = typeof v === 'string'
? v.match(/^([ap])\s*:(.+)$/)
: null
var prepend = !vMatches || vMatches[1] === 'p'
if (vMatches) {
v = vMatches[2]
}
if (prepend) {
addIconPrepend($found, v)
return
}
$found.append(v)
})
}
function addIconFind ($node, selector) {
var sMatches = selector.match(/(?:parent(:\S+)\s)?(?:context(\S+)\s)?(.*)$/)
if (sMatches === null) {
return $node.find(selector)
}
if (sMatches[1] && $node.parent().filter(sMatches[1]).length === 0) {
// no matches on parent selector.
return $()
}
selector = sMatches[3]
if (sMatches[2]) {
// think of this as scss/sass's & selector
return $node.filter(sMatches[2]).find(selector)
}
return $node.find(selector)
}
function addIconPrepend ($dest, icon) {
// add icon to destinations having two icons
var $existingIcon = $dest.find('> i:first-child + i').after(icon)
$dest = $dest.not($existingIcon.parent())
// add icon to destinations having one icon
$existingIcon = $dest.find('> i:first-child').after(icon)
$dest = $dest.not($existingIcon.parent())
// add icon to destination that did not have icon
$dest.prepend(icon)
}
/**
* Adds toggle icon & hides target
* Minimal DOM manipulation -> apply to all descendants
*/
export function enhance ($node) {
var selectors = $node.find('> .t_identifier').length
? ['> .t_identifier']
: ['> .classname', '> .t_const']
$node.find(selectors.join(',')).each(function () {
var $toggle = $(this)
var $target = $toggle.next()
var isEnhanced = $toggle.data('toggle') === 'object'
if ($target.is('.t_maxDepth, .t_recursion, .excluded')) {
$toggle.addClass('empty')
return
}
if (isEnhanced) {
return
}
if ($target.length === 0) {
return
}
$toggle.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('> .private, > .protected')
.filter('.magic, .magic-read, .magic-write')
.removeClass('private protected')
if (accessible === 'public') {
$inner.find('.private, .protected').hide()
callPostToggle = 'allDesc'
}
enhanceInterfaces($obj)
visToggles($inner, accessible)
addIcons($inner)
$inner.find('> .property.forceShow').show().find('> .t_array').debugEnhance('expand')
if (callPostToggle) {
postToggle($obj, callPostToggle === 'allDesc')
}
$obj.addClass('enhanced')
}
function enhanceInterfaces ($obj) {
var $inner = $obj.find('> .object-inner')
$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)
})
}
/**
* 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 $visToggles = visTogglesGet(flags, accessible)
if ($inner.find('> dd[class*=t_modifier_]').length) {
$inner.find('> dd[class*=t_modifier_]').last().after($visToggles)
return
}
$inner.prepend($visToggles)
}
function visTogglesGet (flags, accessible) {
var $visToggles = $('<div class="vis-toggles"></div>')
var toggleClass = accessible === 'public'
? 'toggle-off'
: 'toggle-on'
var toggleVerb = accessible === 'public'
? 'show'
: 'hide'
var toggles = {
hasProtected: '<span class="' + toggleClass + '" data-toggle="vis" data-vis="protected">' + toggleVerb + ' protected</span>',
hasPrivate: '<span class="' + toggleClass + '" data-toggle="vis" data-vis="private">' + toggleVerb + ' private</span>',
hasExcluded: '<span class="toggle-off" data-toggle="vis" data-vis="debuginfo-excluded">show excluded</span>',
hasInherited: '<span class="toggle-on" data-toggle="vis" data-vis="inherited">hide inherited</span>',
}
$.each(flags, function (name, val) {
if (val) {
$visToggles.append(toggles[name])
}
})
return $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')
}