Авторские статьи JavaScript - полезные трюки и решения

Discussion in 'Статьи' started by gemaglabin, 29 Jan 2007.

  1. gemaglabin

    gemaglabin Green member

    Joined:
    1 Aug 2006
    Messages:
    772
    Likes Received:
    842
    Reputations:
    1,369
    Начнем

    Потребовалось точно узнать что за браузер использует пользователь . Тк способ с HTTP_USER_AGENT не является стопроцентным надо было найти другое решение. После ковыряния интернета в поисках нужной информации , наткнулся на соответсвующий док от mozilla dev team http://www.mozilla.org/docs/web-developer/sniffer/browser_type.html , но к сожалению они наивно верят что браузер можно точно определить используя navigator.userAgent; , поэтому я прибег к другому выходу - нашел различия в обработке js и результат перед вами - скрипт точно обрабатывает движки на Gecko ( Firefox,Netscape,Mozilla) , Оперу и ИЕ.

    PHP:
    function out(value)
     {
      
    document.getElementById("result").innerHTML document.getElementById("result").innerHTML value;
     }

     function 
    testBrowser()
     {
      
    productVersion "";
      
    out("Suggested browser&nbsp;:&nbsp;"+window.navigator.userAgent+"<br/>");
      if ( 
    document.defaultCharset != '' && document.defaultCharset != undefined && document.characterSet == undefined && document.body)
      { 
       
    productVersion=window.navigator.userAgent.substr(window.navigator.userAgent.indexOf("MSIE")+5,3);
       
    out("Detected browser&nbsp;:&nbsp; Internet Explorer&nbsp;<br/>Browser version in User-Agent is:&nbsp;"parseFloat(productVersion));
      }
      if (
    window.opera && document.defaultCharset == undefined &&  document.characterSet != "" &&  document.characterSet != undefined && self.innerHeight)
      { 
       
    productVersion=window.navigator.userAgent.substr(window.navigator.userAgent.indexOf("Opera")+6,4);
       
    out("Detected browser&nbsp;:&nbsp; Opera&nbsp;<br/>Browser version in User-Agent is:&nbsp;"parseFloat(productVersion));
      }
      if (
    document.defaultCharset == undefined && !window.opera  &&  document.characterSet != "" && (self.innerHeight))
      { 
       
    productVersion=window.navigator.userAgent.substr(window.navigator.userAgent.indexOf("Gecko")+6,8)+ ' ('window.navigator.userAgent.substr(8,3) + ')';
       
    out("Detected browser&nbsp;:&nbsp; Mozilla&nbsp;<br/>Browser version in User-Agent is:&nbsp;"parseFloat(productVersion));
      }
      if (
    productVersion == "")
      {
       
    out("Failed to fingerprint browser name , but i suggest it is&nbsp;"+window.navigator.userAgent);
      }
    }

    Пример страницы _http://underwater.xtoolz.ru/test.html
     
    7 people like this.
  2. LeverOne

    LeverOne Elder - Старейшина

    Joined:
    22 Feb 2006
    Messages:
    52
    Likes Received:
    128
    Reputations:
    115
    Способы решения проблем с кодировкой в Ajax

    [= Суть проблемы =]

    Ответ сервера при использовании Ajax по умолчанию приходит в кодировке "utf-8".
    Если запрашиваемая страница изначально имеет кодировку "windows-1251", то кириллические символы будут безвозвратно искажены свойством responseText. В таком случае вывод ответа сервера будет представлен знаками вопроса "?", или другими неопознанными символами.

    [= Способ решения =]
    Их много. Все способы можно разделить на две группы: изменение кодировки на стороне сервера и на стороне клиента.

    Когда мы имеем доступ к серверу, особых проблем обычно не возникает: хедеры, встроенные функции, перекодировка по таблицам символов. Описаний этого полно в нете.

    Обратная ситуация. Например, нашли мы уязвимость XSS, желаем использовать Ajax для незаметного получения каких-либо данных с других страниц на сервере. При этом: во-первых, кодировка у страниц "windows-1251", во-вторых, сервер в ответе на Ajax-запрос не указывает кодировку пересылаемых данных.

    В таких случаях возникает необходимость изменить кодировку уже на стороне клиента
    .

    Универсального способа для этого нет. Для каждого браузера (группы браузеров) они свои. Эти способы, являясь от части продуктом моего обобщения разрозненной информации, а отчасти моего собственного творчества, я представляю.

    [= Вариант, работающий во всех основных браузерах =]

    test.html (необходимо убирать пробелы-разрывы, которые вставляет сайт)

    PHP:
    <html>
    <
    div id=qwerty></div>
    <
    script>

    if (
    window.execScript)
       
    window.execScript('Function BinaryToString(Binary) \r cl1=1 \r cl2=1 \r cl3=1 \r L=LenB(Binary) \r Do While cl1<=L pl3=pl3&Chr(AscB(MidB(Binary,cl1,1))) \r cl1=cl1+1 \r cl3=cl3+1 \r If cl3>300 Then \r pl2=pl2&pl3 \r pl3="" \r cl3=1 \r cl2=cl2+1 \r If cl2>200 Then \r pl1=pl1&pl2 \r pl2="" \r cl2=1 \r End If \r End If \r Loop \r BinaryToString=pl1&pl2&pl3 \r End Function''VBScript');

    var 
    request=false;
    try {
    request=new XMLHttpRequest();}
    catch (
    forIE) {request=new ActiveXObject('Msxml2.XMLHTTP');}

    function 
    ReadFile() 
    {
     
    request.open("GET"'text.html');
      if (
    request.overrideMimeType)
         { 
           
    request.overrideMimeType('text/html; charset=windows-1251');
           
    request.onreadystatechange=override;
         }
     else
           
    request.onreadystatechange=readResponse;
     
    request.send(null);
    }

    function 
    override()
    {
     if (
    request.readyState == 4
       {
        
    request.open("GET"'text.html');
        
    request.onreadystatechange=readResponse;
        
    request.send(null);
       }
    }

    function 
    readResponse()

     if (
    request.readyState == 4)
        { 
         if (
    request.overrideMimeType
            
    document.getElementById('qwerty').innerHTML=request.responseText;
         else
            
    document.getElementById('qwerty').innerHTML=BinaryToString(request.responseBody);
        }
    }
    </script>
    <input type=button value='Жмах' onclick='ReadFile()'>
    </html>
    text.html
    Code:
    <html>Здесь что-нибудь на русском</html>
    
    А теперь по каждому браузеру в отдельности, чтобы стало понятно тем, кто не разобрался.


    1) Internet Explorer


    Объекты Msxml2.XMLHTTP и XMLHttpRequest (IE 7.0) в этом браузере имеют фирменное свойство - "responseBody". В нем находится ответ сервера не в обычном строчном, а в бинарном виде. То есть в том виде, когда еще он не распарсился в соответствии с какой-либо кодировкой. В данном случае именно эта сторона для наc имеет значение. Будем парсить бинарный ответ самостоятельно.

    В стандартном JavaScript функций для работы с таким бинарным ответом сервера нет. IE имеет несколько элементов управления ActieX, которые могут это делать, но в некоторых случаях они из соображений безопасности бывают выключены.

    Имеется отличный способ обработки такого ответа с помощью VBScript, в котором присутствуют необходимые для этого встроенные функции.

    VBScript-функция

    Code:
     <script language=vbscript>
    Function BinaryToString(Binary)
      'Antonin Foller, http://www.pstruh.cz
      'Optimized version of a simple BinaryToString algorithm.
     
      Dim cl1, cl2, cl3, pl1, pl2, pl3, L
      cl1 = 1
      cl2 = 1
      cl3 = 1
      L = LenB(Binary)
      Do While cl1<=L
        pl3 = pl3 & Chr(AscB(MidB(Binary,cl1,1)))  
    'Аналогов функций AscB() и MidB() в js нет.
        cl1 = cl1 + 1
        cl3 = cl3 + 1
        If cl3 > 300 Then
          pl2 = pl2 & pl3
          pl3 = ""
          cl3 = 1
          cl2 = cl2 + 1
          If cl2 > 200 Then
            pl1 = pl1 & pl2
            pl2 = ""
            cl2 = 1
          End If
        End If
      Loop
      BinaryToString = pl1 & pl2 & pl3
    End Function
     
    </script>
    
    Внедряем vbs-функцию в js с помощью фирменной функции IE - execScript()

    (убрать пробел в предпоследнем упоминании функции BinaryToString)

    PHP:
    <html>
    <
    div id=qwerty></div>

    <
    script>

    window.execScript('Function BinaryToString(Binary) \r cl1=1 \r cl2=1 \r cl3=1 \r L=LenB(Binary) \r Do While cl1<=L pl3=pl3&Chr(AscB(MidB(Binary,cl1,1))) \r cl1=cl1+1 \r cl3=cl3+1 \r If cl3>300 Then \r pl2=pl2&pl3 \r pl3="" \r cl3=1 \r cl2=cl2+1 \r If cl2>200 Then \r pl1=pl1&pl2 \r pl2="" \r cl2=1 \r End If \r End If \r Loop \r BinaryToString=pl1&pl2&pl3 \r End Function''VBScript');

    var 
    request = new ActiveXObject('Msxml2.XMLHTTP');

    function 
    ReadFile() 
    {
     
    request.open('GET''text.html');
     
    request.onreadystatechange=readResponse;
     
    request.send(null);
    }

    function 
    readResponse()

     if (
    request.readyState == 4document.getElementById('qwerty').innerHTML=BinaryToString(request.responseBody); // передаем в функцию BinaryToString бинарный ответ сервера в качестве параметра и помещаем в див результат ее работы
    }
    </script>
    <input type=button value='Жмах' onclick='ReadFile()'>
    </html>
    2) FireFox, Netscape

    Используется метод overrideMimeType() объекта реквест

    PHP:
    <html>
    <
    div id=qwerty></div>
    <
    script>

    var 
    request = new XMLHttpRequest();
    function 
    ReadFile() 
    {
     
    request.open("GET"'text.html');
     
    request.overrideMimeType('text/html; charset=windows-1251');    // метод переопределяет заголовок "Content-Type" от сервера
     
    request.onreadystatechange=readResponse;
     
    request.send(null);
    }

    function 
    readResponse() 

     if (
    request.readyState == 4)
       
    document.getElementById('qwerty').innerHTML=request.responseText; }
    </script>

    <input type=button value='Жмах' onclick='ReadFile()'>
    </html>
    3) Opera

    Метод overrideMimeType() предусмотрен в этом браузере в объекте реквест, однако в текущей версии 9.24 кодировку он не переопределяет.

    У данного браузера имеется другая особенность. Состоит она в автоматическом определении кодировки контента. То есть, когда запрашиваешь аяксом страницу в 'windows-1251' первый раз (с хоста), она выводится искаженной. Во второй раз та же страница отображается уже корректно. Данную особенность мы и будем использовать.

    PHP:
    <html>
    <
    div id=qwerty></div>
    <
    script>

    var 
    request = new XMLHttpRequest();

    function 
    ReadFile() 
    {
     
    request.open("GET"'text.html');             // первый запрос
     
    request.onreadystatechange=override;
     
    request.send(null);
    }

    function 
    override()
    {
     if (
    request.readyState == 4
       {
        
    request.open("GET"'text.html');     // второй запрос
        
    request.onreadystatechange=readResponse;
        
    request.send(null);
       }
    }

    function 
    readResponse()

     if (
    request.readyState == 4)    // во второй раз, поскольку мы не разорвали соединение, состояние готовности сразу равно 4, в свойство попадает ответ сервера, находящийся в памяти.
     
    document.getElementById('qwerty').innerHTML=request.responseText
    }
    </script>
    <input type=button value='Жмах' onclick='ReadFile()'>
    </html>
    В действительности не происходит ожидания ответа сервера при втором запросе, результат выводится сразу. Потеря времени ничтожна мала.

    Автоматическое определение кодировки Оперой производится независимо от установленной пользователем (пользовательской кодировкой может не быть "win-1251"), а также от настроек кэширования.

    Ключи поиска: кодировка Ajax кодировка перекодировка Ajax перекодировка UTF-8 Win-1251
     
    #2 LeverOne, 3 Dec 2007
    Last edited: 26 Dec 2007
    1 person likes this.
  3. LeverOne

    LeverOne Elder - Старейшина

    Joined:
    22 Feb 2006
    Messages:
    52
    Likes Received:
    128
    Reputations:
    115
    Распознавание браузера на стороне клиента

    [= Вводные замечания =]​


    1. Скрипт предназначен для распознавания браузера юзера самим браузером юзера, минуя navigator.userAgent. Аналогов ему нет.

    2. Нижепредставленный скрипт отличается от скрипта распознавания gemaglabin'a в первом сообщении тем, что:
    А) распознает не только группу браузеров, основанных на движке Gecko (Firefox, Netscape Navigator, Mozilla), но и сами эти браузеры внутри группы;
    Б) распознает конкретные версии браузеров или диапазоны версий, внутри которых наборы объектов, свойств и методов реализованной модели javascript характеризуются определенным постоянством.
    В) Мда, и код у него больше. :)

    3. В качестве признаков идентификации были взяты две особенности:
    Первая: само наличие или отсутствие объектов, свойств и методов в модели javascript браузера, набор которых изменяется от версии к версии.
    Вторая: возможность вывода этих объектов, свойств и методов в цикле for ... in. Даже имеющиеся объекты-методы не всегда доступны к просмотру в цикле for ... in. Этот показатель также переменен от версии к версии.

    4. Разумеется, в строгом смысле скрипт нельзя назвать скриптом на javascript. В нем использованы как средства Jscript, так и фирменные возможности других браузеров, не предусмотренные стандартами javascript.

    [= Скрипт =]

    PHP:
    var result='';                                                      // сюда будем записывать результат идентификации

    if(window.opera)
    {
     
    result+='Opera ';                                                  // уже точно Opera
     
    var d=new Array();
     for (
    e in documentd.push(e);
     
    d=d.toString();

     if (
    window.XSLTProcessor)                                           // 9.0-9.25
        
    {
         if (
    d.indexOf('execCommand')==-1result+='9.24'
         else
            { 
             if (
    document.selectNodes)
                {
                 if (
    d.indexOf('postMessage')==-1result+='9.25'
                 else 
    result+='9.02-9.23';
                }
             else
                { 
                 if (
    d.indexOf('onload')!=-1result+='9.01'
                 else 
    result+='9.0';
                }
            }
        }
     else
        if (
    document.all)                                               // 8.0-8.54
           
    {
            if (!
    opera.SRPNumberresult+='8.50-8.54';
            else
               {
                if (
    document.postMessageresult+='8.01-8.02';
                else 
    result+='Opera 8.0';
               }
           }
        else                                                            
    // 7.02-7.54
           
    {
            if (
    window.navigateresult+='7.50-7.54';
            else
               {
                if (
    window.getComputedStyleresult+='7.20-7.23';
                else
                   {
                    if (
    window.VXMLAudioRecordingresult+='7.11';
                    else 
    result+='7.02-7.1';
                   }
               }
           }
    }

    else if (
    document.layersresult+='Netscape Navigator 4.x';         // Old Netscape

    else if (document.defaultCharset == undefined && !window.opera)     // Gecko based
    {
     var 
    a=new Array();
     for (
    b in document.bodya.push(b);
     
    a=a.toString();

     var 
    h=new Array();
     for (
    i in documenth.push(i);
     
    h=h.toString();


     if (
    document.defaultView.globalStorage)
        {
         if (
    document.defaultView.getInterfaceresult+='Firefox 2.0-2.0.0.11';
         else 
    result+='Netscape Navigator 9.0-9.0.0.5';
        }
     else
        { 
         if (
    document.defaultView.getInterface && window.Componentsresult+='Firefox 1.5-1.5.0.8';
         else
            {
             if (
    a.indexOf('addEventListener')==-1)
                {
                 if (
    h.indexOf('preferredStylesheetSet')!=-1result+='Firefox 1.0.6-1.0.7';
                 else 
    result+='Mozilla 1.7.10-1.7.13'
                }
             else
                {
                 if (
    document.defaultView.setTransparencyresult+='Netscape Navigator 8.1';
                 else
                    {
                     if (
    window.pkcs11.addmodule && window.OPEN_NEW_WIN_ONLYresult+='Netscape Navigator 8.0.2-8.0.4';
                     else
                        {
                         if (
    document.actualEncoding)
                            {
                             if (
    h.indexOf('preferredStylesheetSet')!=-1result+='Firefox 1.0-1.0.4';
                             else 
    result+='Firefox 0.8-0.9 or Mozilla 1.7-1.7.8 or Netscape Navigator 7.2';
                            }
                         else
                            if (
    sidebar.CHECK_ANONYMOUSresult+='Netscape Navigator 7.0-7.02';
                        }
                    }
                }
            }
        }
    }
    else                                    
    //  IE
    {
     var 
    JScript='';
     
    /*@cc_on @*/
     /*@ JScript=@_jscript_version @*/
     
    JScript==3.0result+='Internet Explorer 4.0'JScript==5.0result+='Internet Explorer 5.0'JScript==5.5result+='Internet Explorer 5.5'JScript==5.6result+='Internet Explorer 6.0'JScript==5.7result+='Internet Explorer 7.0'result+='Browser unknown';  
    }

    document.write('<b><font size=4>Result of fingerprinting:</font></b> '+result+'<br /><b><font size=4>Browserinfo (navigator.userAgent):</font></b> '+window.navigator.userAgent);
                              
    //\\ LeverOne //\\

    [= Заключительные замечания =]


    1. О недостатках
    А) Распознавание основано на предположении, что клиент использует один из тех браузеров, на которые нацелено распознавание: FireFox, Mozilla, Netscape Navigator, Opera, Internet Explorer. Остальные могут быть распознаны некорректно, хотя многое сделано, чтобы такие браузеры были обозначены отдельно как "неопознанные".
    Б) Брались во внимание только финальные версии, но не альфа, бета и т.д.
    В) Просмотр исходного кода страницы до распознавания может повлечь ошибку идентификации. Необходимо просматривать его после получения результата.
    Г) Из-за того, что объекты-методы могут то появляться, то исчезать в новых версиях (ибо процесс расширения возможностей нелинеен во времени) скрипт может давать ошибочные результаты при распознавании более новых версий, чем заложено в нем самом. Например, Opera 9.25 ближе к 9.23, чем к 9.24.
    Д) Тестировалось всё под Виндовс, как те же самые браузеры поведут в других OC, утверждать не могу.

    2. Версии распознающихся браузеров:

    IE: 4.0, 5.0, 5.5, 6.0, 7.0

    Opera: 7.02, 7.03, 7.10, 7.11, 7.20, 7.21, 7.22, 7.23, 7.50, 7.51, 7.52, 7.53, 7.54, 8.0, 8.01, 8.02, 8.50, 8.51, 8.52, 8.53, 8.54, 9.0, 9.01, 9.02, 9.10, 9.20-9.25

    _http://arc.opera.com/pub/opera/win

    Firefox: 0.8, 0.9, 1.0, 1.0.1-1.0.7, 1.5, 1.5.0.1-1.5.0.8, 2.0, 2.0.0.1-2.0.0.11

    Netscape Navigator: 4.x, 7.0, 7.01, 7.02, 7.2, 8.02, 8.03, 8.04, 8.1, 9.0, 9.0.0.1-9.0.0.5

    _http://browser.netscape.com/downloads/archive

    Mozilla: 1.7, 1.7.1-1.7.13

    _http://www.mozilla.org/releases/
     
    #3 LeverOne, 6 Jan 2008
    Last edited: 12 Jan 2008
    4 people like this.
  4. gibson

    gibson Elder - Старейшина

    Joined:
    24 Feb 2006
    Messages:
    391
    Likes Received:
    247
    Reputations:
    88
    Кодировка утф предназначена для англоязычных стран восновном. Не понимаю зачем нужно посылать инфу в одной кодировке а получать в другой?
    PHP:
    $objAjax = new xajax(); // компонент класса xajax
    $objAjax->setCharEncoding("windows-1251"); // выставляем кодировку
    Что делать если отключен js и другие компоненты у пользователя все проверки пойдут лесом? И еще как можно точно определить поисковых ботов на js.
     
  5. banned

    banned Banned

    Joined:
    20 Nov 2006
    Messages:
    3,324
    Likes Received:
    1,193
    Reputations:
    252
    Зачем на js их определять?
    Есть определенные диапазоны и юзер-агенты их...