Хардкор диагностики. Ориентирование на местности. Часть 2
Продолжаем накапливать инструменты, которые могут облегчить жизнь хардкорщикам. Следующая часть серии статей посвящена перехвату подписывания и инициализации событий через библиотеку BX.
¶BX.addCustomEvent
По аналогии с тем, как устроена работа с событиями в jQuery, методы BX-библиотеки являются функциями-аккумуляторами. Подписывание на событие фактически явлется регистрацией символьного кода и колбэк-функции в глобальном объекте, а выброс события — выполнение колбэк-функции.
Регистрация события производится через функцию BX.addCustomEvent
.
Как правило регистрацию события сопровождают:
- событие в виде произвольного символьного кода («click», «load», «on-location-search-focus», ...);
- колбэк-функция, которая должна быть вызвана при наступлении события.
Как и в случае с jQuery, переопределяем исходный метод и переопределяем его своим.
¶Переопределение метода
Предлагается сделать следующее:
- ставим в девтулзах глобальный брекпоинт на событие
document.onDOMContentLoaded
, перезагружаем страницу; - на первом же срабатывании брекпоинта убеждаемся, что объект
BX
уже доступен и переопределяем ему методaddCustomEvent
; - снимаем брекпоинт и отпускаем выполнение скрипта.
Код для выполнения в консоли:
let originalBxAddCustomEvent = BX.addCustomEvent;
// Глобальный объект,
// в который будет собираться статистика
hardcoreBXFrontLog = {
events: {},
};
/**
* eventOrObj - example: BX.addCustomEvent(opener, 'onOpenerMenuClose', ...)
*/
BX.addCustomEvent = function (eventOrObj, handlerOrEvent, arParams, handlerContextObject) {
let info = {};
let realEvent, realCallback, someObj;
if (typeof eventOrObj === 'string') {
realEvent = eventOrObj;
} else if (typeof handlerOrEvent === 'string') {
realEvent = handlerOrEvent;
}
if (eventOrObj instanceof Function) {
realCallback = eventOrObj;
} else if (handlerOrEvent instanceof Function) {
realCallback = handlerOrEvent;
} else if (arParams instanceof Function) {
realCallback = arParams;
}
if (typeof eventOrObj === 'object') {
someObj = eventOrObj;
} else if (typeof handlerOrEvent === 'object') {
someObj = handlerOrEvent;
} else if (typeof arParams === 'object') {
someObj = arParams;
}
let err = new Error();
info.trace = err.stack;
info.event = realEvent;
info.callback = realCallback;
info.obj = someObj;
info.arguments = arguments;
// live! =)
hardcoreBXFrontPrintResult(info, true);
// Регистрируем в глобальном объекте
let eventNameList = realEvent.split(' ');
eventNameList.forEach(function (evt) {
if (!hardcoreBXFrontLog.events[evt]) {
hardcoreBXFrontLog.events[evt] = [];
}
hardcoreBXFrontLog.events[evt].push(info);
});
// Пинаем оригинальный метод
if (!arParams) {
arParams = [];
}
if (!handlerContextObject) {
handlerContextObject = false;
}
return originalBxAddCustomEvent.call(this, eventOrObj, handlerOrEvent, arParams, handlerContextObject);
};
// Метод для поиска среди собранной статистики
// информации по названию события
hardcoreBXAddEventLookingByEvent = function (event) {
for (let e in hardcoreBXFrontLog.events) {
if (
!hardcoreBXFrontLog.events.hasOwnProperty(e)
|| e !== event
) {
continue;
}
for (let ins = 0; ins < hardcoreBXFrontLog.events[e].length; ins++) {
hardcoreBXFrontPrintResult(hardcoreBXFrontLog.events[e][ins]);
}
}
};
// Вывод в консоль
hardcoreBXFrontPrintResult = function (info, live) {
let localInfo = Object.assign({}, info);
console.log(
'BX.add%c%s',
'background: #1d1b57; color: #fff; ' +
'font-weight: bold; padding: 3px 9px;' +
'border-radius: 0 30px 30px 0;' +
'border-right: 7px solid #fa8544',
info.event
);
if (localInfo.obj) {
console.log(localInfo.obj);
}
console.groupCollapsed('trace');
if (live) {
console.trace();
delete (localInfo.trace);
} else {
console.log(localInfo.trace);
}
console.groupEnd();
console.groupCollapsed('info');
for (let i in localInfo) {
if (localInfo.hasOwnProperty(i)) {
console.log(i + ':%o', localInfo[i]);
}
}
console.groupEnd();
};
¶Статистика
В консоли будет накапливаться информация о каждом событии, которое было проинициализировано через BX.addCustomEvent
, с
трейсингом вызова, что позволит нам понять, откуда именно это было запущено.
Также вся информация будет накапливаться в глобальный объект hardcoreBXFrontLog
,
отсортированная по событиям. К сожалению, полноценный трейсинг в переменную
не запомнить, но часть цепочки сохраняется, через объект Error
. Это
хотя бы частично позволяет сориентироваться в том, откуда пришёл сигнал.
Собранную на текущий момент статистику по событиям получаем через функцию hardcoreBXAddEventLookingByEvent("название_события")
.
¶BX.onCustomEvent
Выброс события производится через BX.onCustomEvent
и происходит в любом случае, вне зависимости от того, есть ли слушатели события или нет. Для нас — разработчиков — это является информацией о том, в каком месте существуют точки входа, на которые при необходимости можно было бы подписаться и реагировать.
Код для выполнения в консоли:
let originalBxOnCustomEvent = BX.onCustomEvent;
// Глобальный объект,
// в который будет собираться статистика
hardcoreBXOnEventFrontLog = {
events: {},
};
BX.onCustomEvent = function (objOrEvent, eventIHope, eventParams, secureParams) {
if (!objOrEvent) {
objOrEvent = null;
}
if (!eventIHope) {
eventIHope = null;
}
if (!eventParams) {
eventParams = null;
}
if (!secureParams) {
secureParams = null;
}
let info = {};
let realEvent, realObj;
if (typeof objOrEvent === 'string') {
realEvent = objOrEvent;
} else if (typeof eventIHope === 'string') {
realEvent = eventIHope;
}
if (typeof objOrEvent === 'object') {
realObj = objOrEvent;
} else if (typeof eventIHope === 'object') {
realObj = eventIHope;
} else if (typeof eventParams === 'object') {
realObj = eventParams;
}
let err = new Error();
info.trace = err.stack;
info.event = realEvent;
info.obj = realObj;
info.params = eventParams;
info.arguments = arguments;
// live! =)
hardcoreBXFrontPrintOnEvent(info, true);
// Регистрируем в глобальном объекте
let eventNameList = realEvent.split(' ');
eventNameList.forEach(function (evt) {
if (!hardcoreBXOnEventFrontLog.events[evt]) {
hardcoreBXOnEventFrontLog.events[evt] = [];
}
hardcoreBXOnEventFrontLog.events[evt].push(info);
});
// Пинаем оригинальный метод
return originalBxOnCustomEvent.call(this, objOrEvent, eventIHope, eventParams, secureParams);
};
// Метод для поиска среди собранной статистики
// информации по названию события
hardcoreBXOnEventLookingByEvent = function (event) {
for (let e in hardcoreBXOnEventFrontLog.events) {
if (
!hardcoreBXOnEventFrontLog.events.hasOwnProperty(e)
|| e !== event
) {
continue;
}
for (let ins = 0; ins < hardcoreBXOnEventFrontLog.events[e].length; ins++) {
hardcoreBXFrontPrintOnEvent(hardcoreBXOnEventFrontLog.events[e][ins]);
}
}
};
// Вывод в консоль
hardcoreBXFrontPrintOnEvent = function (info, live) {
let localInfo = Object.assign({}, info);
console.log(
'BX.on%c%s',
'background: #fa8544; color: #fff; ' +
'font-weight: bold; padding: 3px 9px;' +
'border-radius: 30px 0 0 30px;' +
'border-left: 7px solid #1d1b57',
localInfo.event
);
if (localInfo.obj) {
console.log(localInfo.obj);
}
console.groupCollapsed('trace');
if (live) {
console.trace();
delete (localInfo.trace);
} else {
console.log(localInfo.trace);
}
console.groupEnd();
console.groupCollapsed('info');
for (let i in localInfo) {
if (localInfo.hasOwnProperty(i)) {
console.log(i + ':%o', localInfo[i]);
}
}
console.groupEnd();
};
¶Статистика
В консоли будет накапливаться информация о каждом событии, которое было проинициализировано через BX.onCustomEvent
, с
трейсингом вызова, что позволит нам понять, откуда именно это было запущено.
Также вся информация будет накапливаться в глобальный объект hardcoreBXOnEventFrontLog
,
отсортированная по событиям. К сожалению, полноценный трейсинг в переменную
не запомнить, но часть цепочки сохраняется, через объект Error
. Это
хотя бы частично позволяет сориентироваться в том, откуда пришёл сигнал.
Собранную на текущий момент статистику по событиям получаем через функцию hardcoreBXOnEventLookingByEvent("название_события")
.
Обсуждение статьи 2