Skip to content

Commit

Permalink
Major rework, offload debuggers work to LayoutHints, add jQuery widge…
Browse files Browse the repository at this point in the history
…t support
  • Loading branch information
lumnn committed May 19, 2023
1 parent f350e08 commit 6ae1abe
Show file tree
Hide file tree
Showing 6 changed files with 347 additions and 133 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Features:

- **Does not affect structure** or existing styles 👌
- Detects **Knockout** components and templates 🤜
- Detects **jQuery** widget registration and usage 💲
- Finds Magento **mage-init scripts** within templates/layouts 📌
- Uses dev-tools like **element picker** to select elements 🔫
- Prints **browseable structure** and internal informations in console 👀
Expand Down
141 changes: 99 additions & 42 deletions view/base/web/js/LayoutHints.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
define([
'./highlights',
'./debug/JQueryWidgetDebugger',
'./debug/KnockoutDebugger',
'./debug/MageLayoutDebugger',
'./debug/MageInitDebugger'
], function (highlights, KnockoutDebugger, MageLayoutDebugger, MageInitDebugger) {
], function (highlights, JQueryWidgetDebugger, KnockoutDebugger, MageLayoutDebugger, MageInitDebugger) {
var colors = {
blue4: "36 47 155",
blue3: "100 111 212",
blue2: "155 163 235",
blue1: "219 223 253",
navy: "42 37 80",
brown: "84 18 18",
blue5: "36 80 190",
blue4: "36 47 155",
blue3: "100 111 212",
blue2: "155 163 235",
blue1: "219 223 253",
navy: "42 37 80",
brown: "84 18 18",
orange: "225 70 0"
}
var labelStyle = "padding-left: 3px; padding-right: 3px; border-radius: 3px; margin-right: .5em; display: inline-block; cursor: pointer;"
var labelStyle = "padding-left: 3px; padding-right: 3px; border-radius: 3px; margin-right: .5em; display: inline-block; font-weight: bold; cursor: pointer;"
var labelStyleAqua = `${labelStyle} background: rgb(${colors.blue5}); color: white;`
var labelStyleBlue = `${labelStyle} background: rgb(${colors.blue4}); color: white;`
var labelStyleNavy = `${labelStyle} background: rgb(${colors.navy}); color: white;`
var labelStyleBrown = `${labelStyle} background: rgb(${colors.brown}); color: white;`
var labelStyleOrange = `${labelStyle} background: rgb(${colors.orange}); color: white;`

return class LayoutHints {
constructor (mageLayoutTree, initOptions) {
Expand All @@ -27,28 +32,33 @@ define([

this.debuggers = {}

this.debuggers.mageInit = new MageInitDebugger(this)

this.debuggers.mageLayout = new MageLayoutDebugger(
this,
mageLayoutTree,
{
largerFontSize: "1.25em",
labelStyleBlue,
labelStyleNavy,
labelStyleBrown,
blockEditUrl: initOptions.blockEditUrl,
mageInitDebugger: this.debuggers.mageInit
}
)

this.debuggers.knockout = new KnockoutDebugger(
this,
{
largerFontSize: "1.25em",
labelStyles: labelStyleBrown,
}
)
this.debuggers.mageInit = new MageInitDebugger({
largerFontSize: "1.25em",
})

this.debuggers.jqueryWidget = new JQueryWidgetDebugger({
largerFontSize: "1.25em",
})

this.debuggers.knockout = new KnockoutDebugger(this, {
largerFontSize: "1.25em",
labelStyles: labelStyleBrown,
})

this.debuggers.mageInit.badgeStyle = labelStyleBlue
this.debuggers.mageLayout.badgeStyle = labelStyleOrange
this.debuggers.jqueryWidget.badgeStyle = labelStyleAqua
this.debuggers.knockout.badgeStyle = labelStyleBrown

// set up basic highlight styles
document.documentElement.style.setProperty('--hl-bg', `rgb(${colors.blue1} / .85)`)
Expand All @@ -67,7 +77,6 @@ define([
}

this.highlight(inspectable, { printOnClick: false })
this.consolePrint(inspectable)
}

findInspectable (element) {
Expand All @@ -79,11 +88,7 @@ define([
continue
}

var inspectable = typeDebugger.getInspectable(element)
inspectable.element = element
inspectable.type = type

return inspectable
return element
}
} while (element = element.parentElement)
}
Expand All @@ -110,42 +115,94 @@ define([
this.highlight(closestHighlightable)
}

highlight (data, options) {
if (!this.debuggers[data.type] || typeof this.debuggers[data.type].highlight !== 'function') {
throw new Error(`Cannot highlight element of type ${data.type}`)
}
highlight (element) {
let elementsData = new Map()

return this.debuggers[data.type].highlight(data, options)
}
// collect data from debuggers
for (let type in this.debuggers) {
const typeDebugger = this.debuggers[type]

if (typeof typeDebugger.isInspectable !== 'function'
|| typeof typeDebugger.getHighlightsData !== 'function'
|| !typeDebugger.isInspectable(element)
) {
continue
}

const highlights = typeDebugger.getHighlightsData(element)

for (const highlight of highlights) {
const highlightedEl = highlight.element || element

consolePrint (data, options) {
if (!this.debuggers[data.type] || typeof this.debuggers[data.type].consolePrint !== 'function') {
throw new Error(`Cannot consolePrint element of type ${data.type}`)
const elementData = elementsData.get(highlightedEl) || []
elementData.push(Object.assign(highlight, { type }))

elementsData.set(highlightedEl, elementData)
}
}

return this.debuggers[data.type].consolePrint(data, options)
for (const [highlightedEl, highlightData] of elementsData) {
let content = ''

for (let highlight of highlightData) {
content += `<div style="margin-bottom: .25em;">`

for (var badge of highlight?.badges || []) {
content += `<span style="${this.debuggers[highlight.type].badgeStyle || labelStyleBlue}">${badge}</span>`
}

content += `<small>@ ${highlight.type}</small>`

if (highlight.content) {
content += `<div>${highlight.content}</div>`
}

content += `</div>`
}

const highlightOverlay = highlights.create(highlightedEl, content)

if (highlightedEl === element) {
highlightOverlay.addEventListener("click", event => this.onClickHighlight(element, event))
highlightOverlay.addEventListener("contextmenu", event => this.onRightClickHighlight(element, event))
}
}
}

onClickHighlight (e) {
if (e.target.tagName === 'A') {
onClickHighlight (element, event) {
if (event.target.tagName === 'A') {
return
}

e.preventDefault()
console.group("Inspecting: ", element)

for (var type in this.debuggers) {
if (typeof this.debuggers[type].consolePrint === 'function') {
try {
this.debuggers[type].consolePrint(element)
} catch (e) {
console.error(e)
}
}
}

console.groupEnd()

event.preventDefault()
highlights.clear()
this.removeMouseTracker()
}

onRightClickHighlight (inspectable, event) {
onRightClickHighlight (element, event) {
event.preventDefault()
highlights.clear()
this.removeMouseTracker()

if (!inspectable.element.parentElement) {
if (!element.parentElement) {
return
}

var parentInspectable = this.findInspectable(inspectable.element.parentElement)
var parentInspectable = this.findInspectable(element.parentElement)

if (!parentInspectable) {
return
Expand Down
104 changes: 104 additions & 0 deletions view/base/web/js/debug/JQueryWidgetDebugger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
define(['jquery', '../highlights', 'jquery-ui-modules/widget'], function ($, highlights) {
const registeredWidgets = []
const initialized = []

$.widget = function (_super) {
const func = function () {
if (!registeredWidgets.includes(arguments[0])) {
registeredWidgets.push(arguments[0])
}

_super.apply(this, arguments)
}

Object.assign(func, _super)

return func
}($.widget)

$.Widget = class extends $.Widget {
_createWidget() {
initialized.push({ widget: this.widgetName, element: arguments[1], options: arguments[0] })
super._createWidget(...arguments)
}
}

return class JQueryWidgetDebugger {
constructor (options = {}) {
this.largerFontSize = options.largerFontSize || '1em'
}

isInspectable (element) {
return !!initialized.find(i => i.element === element)
}

getInspectable (element) {
const inspectable = initialized.find(i => i.element === element)

if (!inspectable) {
throw new Error("This element is not inspectable!")
}

return inspectable
}

getHighlightsData (element) {
const elementInitialized = initialized.filter(i => i.element === element)

const highlightsData = elementInitialized.map(i => {
return {
badges: ["$." + i.widget],
content: `<pre>${JSON.stringify(i.options, null, 2)}</pre>`
}
})

if (element === document.body) {
const unusedWidgets = this.filterUnusedWidgets()

highlightsData.push({
badges: [`${unusedWidgets.length} Unused jQuery Widgets`],
content: `<pre>${unusedWidgets.join('; ')}</pre>`
})
}

return highlightsData
}

consolePrint (element) {
const inits = initialized.filter(i => i.element === element)

if (!inits.length === 0) {
return
}

inits.forEach(initData => {
console.group(
`%c$.${initData.widget}`,
`${this.badgeStyle || ''}; font-size: ${this.largerFontSize}`,
)
console.log("Options: ", initData.options)
console.groupEnd()
})

if (document.body === element) {
console.log("Unused jQuery Widgets: ", this.filterUnusedWidgets())
}
}

filterUnusedWidgets () {
return registeredWidgets.filter(widgetName => {
const parts = widgetName.split('.')
const name = parts[parts.length - 1]

return !initialized.find(init => init.widget === name)
})
}

dump () {
console.log("JQuery Registered Widgets: ", registeredWidgets)
console.log("JQuery Widget Usage: ", initialized)

console.log("Unused widgets: ", this.filterUnusedWidgets())
}
}
})
Loading

0 comments on commit 6ae1abe

Please sign in to comment.