На днях вышла 23-я версия «Файерфокс» и у нас внезапно сломался программный комплекс. Не катастрофа, ибо целевая версия - 3.6, а когда-нибудь в ещё даже не анонсированном светлом будущем - 15… Но - хотя целевой браузер у меня установлен и я с ним периодически сверяюсь, разработку веду всё-таки в актуальной версии (и не я один такой), и успел обновить, а генеральный директор вообще мне неподконтролен и смотрит нашу работу спасибо что не в «Опере». Короче, неприятность. А причина её в том, что перестал работать нестандартный (мораль!) метод watch на элементе select, говорит, «TypeError: can’t watch non-native objects of class Proxy». А у нас на него были завязаны иерархические комбо-боксы, а они почти в каждом фильтре, а фильтр почти в каждом модуле…
Захожу в мозилловский баг-трекер и вижу, что
народ тоже уже заметил, а ведущий разработчик «Файерфокс» Борис Збарский любезно отвечает, что так, дескать, и должно быть, комбо-бокс это теперь «типа, прокси».
Это навело меня на мысль попытаться обернуть мой HTMLSelectElement в реальный Proxy (не путать с прокси-серверами, это новая
конструкция Javascript такая, некоторым образом обобщение сеттеров/геттеров). Поначалу всё шло хорошо - пока не обнаружилось, что проксю нельзя пихать вместо настоящего DOM-узла в DOM-методы (например, навесить листенер или просто добавить дочерний узел). В умопомрачении я попытался отслеживать перемещение по опциям комбо-бокса HTML-атрибута selected посредством
MutationObserver, не сообразив сразу, что никуда он не перемещается при смене значения хоть пользователем, хоть скриптом.
«Но что же нам делать?» - возопил я, и получил от Збарского лаконичную подсказку: «You can capture that by interposing your own setter that calls through to the canonical setter». Но нельзя просто взять и… Хм. В общем, он, может, такими вещами и занимается втихомолку, но я не смог нагуглить ничего внятного в качестве примера. Пришлось разбираться во всём самому методом тыка. Сначала оказалось, что если я переопределяю сеттер, то должен определить и геттер, потом, что в мозилловской реализации геттеров
сломан контекст. В итоге у меня получился скрипт, состоящий сплошь из конструкций, которые я не применял никогда, которые применяю редко и которые применяю не так редко, но единственный в фирме.
let({get: canonicalGetter, set: canonicalSetter}=Object.getOwnPropertyDescriptor(Object.getPrototypeOf(combo), "value")){
Object.defineProperty(combo, "value", {
configurable: true,
enumerable: true,
get: function() canonicalGetter.call(combo),//this нельзя, он сломан!
set: function(value){
onChangeValue.call(this, "value", this.value, value);//onChangeValue - это мой обработчик
canonicalSetter.call(this, value);
}
});
};
let({get: canonicalGetter, set: canonicalSetter}=Object.getOwnPropertyDescriptor(Object.getPrototypeOf(combo), "selectedIndex")){
Object.defineProperty(combo, "selectedIndex", {
configurable: true,
enumerable: true,
get: function() canonicalGetter.call(combo),
set: function(value){
let oldValue=this.value;
canonicalSetter.call(this, value);
onChangeValue.call(this, "value", oldValue, this.value);
}
});
};
combo.addEventListener(
"change",
function(){
onChangeValue.call(this, "value", comboValue, this.value);//comboValue - переменная замыкания, в которой запоминается текущее значение комбо-бокса в функции onChangeValue (если при изменении нас не интересует прежнее значение, можно обойтись без неё)
},
false
);
Напоследок - немного профессионального юмора. Ушедший в отпуск сотрудник оставил один свой новый модуль неработающим в новой версии браузера. Полез туда, а там необъявленная переменная. Это ладно, но имя этой переменной - quesyStringОptional. Само по себе уже забавно (должно быть query, а quesy в английском сленге означает «тошноту с бодуна»), а тут ещё и редактор на него ругается. Почему? Оказывается, там «О» в серёдке кириллическая. Нет, это
допустимый идентификатор Javascript. Но как, как можно так опечататься случайно?