«Консультант» и IE

Вчера на «Хабре» люди в очередной раз подняли тему сайта «Консультанта» и Internet Explorer — первый, своей интересной частью, работает только на втором. Как я и ожидал, код завязан на особенности Internet Explorer, браузер богатый на возможности, на сайте использовано их немало. Впрочем, всё, если вдуматься, переводится и на более стандартные рельсы.

У меня давно руки чесались разобраться поподробнее как устроены userjs в «Опере» и посмотреть на отличия реализации XSLT-процессинга в IE и остальных браузерах. Userjs в «Опере» устроены исключительно удобно (даже предусмотрели патчи к выполняемому коду), а с XSLT в IE нестандартно, зато много проще.

По результатам появился userjs, который позволяет увидеть две первые страницы базы «Консультанта» в «Опере», если кому-нибудь хочется, допилите его дальше, я это делал исключительно из интереса научиться полезному, сама база мне без надобности.

Текст userjs помещаю под кат.

// ==Userscript==
// @name         Make Consultant base works
// @namespace    /
// @description  Make Consultant base works in non-IE browsers.
// @include      http://base.consultant.ru/*
// @version      1.0
// @licence      MIT
// ==/UserScript==

(function(){

    /* TODO
       saveCard — это behavior userData, сделать эмуляцию
       VBArray  — одна из переменных использует этот специфический массив
       isBaseVisible, ForEachRow — убрать заглушку, сделать нормальную функцию
       showModalDialog — дописать с передачей параметров (например, через window.name)
    */


    document.addEventListener('load', function (e) {
        showModalDialog = window.open;

        return true;
    }, false);

    DOMParser.prototype.loadXML = function (text) {
        return this.parseFromString(text,"text/xml")
    }

    opera.addEventListener('BeforeScript', 
        function(e){
                if (e.element.getAttribute('for')) {
                    var script = e.element.text
                    var id     = e.element.getAttribute('for')
                    var ev     = e.element.getAttribute('event')

                    e.element.text = ''
                    document.addEventListener('load', new Function('',                     
                        'document.getElementById("' + id +
                        '").attachEvent("' + ev +
                        '", function () {'+ script + '})'
                    ))
                }

                var repl = {
                    'document\\.body\\.attachEvent'   : 'document.attachEvent',
                    'document\\.all\\.fieldsXML\\.XMLDocument\\.transformNode': 'document.transformNode',
                    'document\\.all'                  : 'document.getElementsByTagName("*")',
                    'document\\.frames\\('            : 'document.getElementsByTagName("iframe").item(',
                    '\\.XMLDocument\\.text'           : '.textContent',
                    'document\\.body'                 : 'document.documentElement',
                    '\\.parentElement'                : '.parentNode',
                    '\\.XMLDocument\\.documentElement': '',
                    '\\.XMLDocument'                  : '',
                    'basesRow\\.all\\.basesTable'     : 'document.getElementById("basesTable")',
                    '\\.all\\.tags\\('                : '.getElementsByTagName(',
                    '\\.all\\.([^\\.;\\s]+)'          : '.getElementById("$1")',
                    '\\.all\\('                       : '.getElementById(',
                    '\\w+\\.getElementById'           : 'document.getElementById',
                    'p\\.type\\.'                     : 'p.getAttribute("type").',
                    '\\.isEnabled\\s*=([^;]+)'        : '.setAttribute("isEnabled", $1)',
                    '\\.isEnabled'                    : '.getAttribute("isEnabled")',
                    '(\\w+)\\.loadXML'                : '$1=$1.loadXML',
                }

                for (var re in repl) {
                    e.element.text = e.element.text.replace(new RegExp(re, 'g'), repl[re])
                }
            }
        ,false);

    opera.defineMagicFunction('writeInsertion', function () {});

    opera.defineMagicFunction('getBasesTable', function (a, b, tDivRow) {

        var basesRow = tDivRow && tDivRow.id ? document.getElementById("bases_" + tDivRow.id) : null;
        return basesRow == null ? null : document.getElementById('basesTable');
    })

    opera.defineMagicFunction('forEachRow', function (a, b) { return true })

    opera.defineMagicFunction('createXMLStylesheet', function (a, b, url) {
        var r = new XMLHttpRequest()
        r.open('GET', url, false)
        r.send(null)

        if (r.status == 200) {
            var xml = new DOMParser()

            return xml.parseFromString(r.responseText, "text/xml")
        }

        return null
    })

    opera.defineMagicFunction('isBaseVisible', function (a, b, code) {
        return true
    })

    HTMLElement.prototype.transformNode = XMLDocument.prototype.transformNode = function (style) {
        var xsltProcessor=new XSLTProcessor();
        xsltProcessor.importStylesheet(style);

        if (typeof this == 'HTMLElement') {
            var resultDocument = (new DOMParser()).parseFromString(this.innerHTML, 'text/xml');
        } else {
            var resultDocument = xsltProcessor.transformToDocument(this);
        }
          
        var xmls = new XMLSerializer();  
        return xmls.serializeToString(resultDocument);
    }

    HTMLHtmlElement.prototype.selectSingleNode = HTMLInputElement.prototype.selectSingleNode = function (path) {
        var xpe = new XPathEvaluator();
        var nsResolver = xpe.createNSResolver(this.ownerDocument == null ? this.documentElement : this.ownerDocument.documentElement);
        var results = xpe.evaluate(path,this,nsResolver,XPathResult.FIRST_ORDERED_NODE_TYPE, null);
        return results.singleNodeValue;
    }

    ActiveXObject = function (str) {
        return new ({
            'Microsoft.XMLDOM':  DOMParser,
            'Microsoft.XMLHTTP': XMLHttpRequest,
            'Scripting.Dictionary': Array,
        })[str]
    }

})()

Кстати, «Опере» нужно не забыть сказать, чтобы она маскировалась под Internet Explorer (правая клавиша мыши, «Настройки сайта», закладка «Сеть»).

Обращаюсь к тем, кто читает RSS и трансляцию, текст userjs по ссылке на исходный пост.

7 комментариев

iPhone и снимки с GPS

iPhone, который, не спрашивая, записывает в снимок информацию с GPS, чувствую, доставит ещё немало лулзов в интернетах. Одна тётечка, сфоткавшись обнажённой, уже спалилась — координаты есть, дом на Google Maps видно, тем более, что он частный. И это только начало.

14 комментариев

Подводная река

Подводная река (39.98КиБ)

Красиво — фотография Анатолия Белощина, сделана под водой, в Мексике, на глубине 28 метров, «река», над которой видно дайвера — это течёт сероводород (обязательно посмотрите оригинал).

3 комментария