Для ознакомления с правилами сайта внимательно следите за всплывающими подсказками,
а лучше - разверните эту статью .
  I. Статьи  
1. "WEB-программирование. Коллекция мелочей."


Обмен данными "Клиент/сервер".


Обмен данными между клиентом и сервером, как правило, выполняется с применением технологии AJAX (см. сюда).
Данная технология базируется на применении объекта XMLHttpRequest, который очень хорошо описан здесь.
Для избежания дублирования информации я не буду описывать сам метод, а остановлюсь на некоторых нюансах его применения:

( Несколько в противовес указанной статье хотел бы посоветовать не злоупотреблять асинхронным доступом.
Если запрашиваемые и посылаемые объёмы информации не очень велики, то никакого выигрыша от использования асинхронного доступа Вы не получите.
А вот приобрести массу проблем - сможете!)

1. Отправка данных и вывод ответа.
(AJAX. Передача данных и массивов из JS в PHP и обратно.)

Данные серверы передаются в виде строки элементов <ключ>=<значение>, разделяемых знаком "&".
Ключом может быть любой набор букв английского алфавита, цифр и сивола "_". Прописные и строчные буквы не различаются. Ключ должен быть уникальным в строке.
Значения параметров в JAVASCRIPT следует кодировать используя функцию encodeURIComponent.
Если параметры собраны в объект, то для формирования строки параметров можно использовать следующую функцию:

 function prepParm(params)
 { 
   var parm='',del='';
   if( params )
   {
     for(e in params)
     {
       var z=params[e];
       if( z!=='' )
       {
         parm+=del+e+'='+encodeURIComponent(params[e]);
         del='&';
        }
      }
    }
   return parm;
  }
              

При передаче методом GET полученная строка добавляется к файловому компоненту URL-строки после знака "?",
при методе POST передаётся функцией XMLHttpRequest.send().
(Параметры с пустыми значениями не передаются. Если все параметры будут иметь пустое значение, то запрос выполняться не будет!)
Для иллюстрации приведу функцию передачи данных и приёма ответа:

 function getQueryHtx(host,params,get)
 {
   var xhr=getXmlHttp();
   try
   {
     if( !get )
     {
       xhr.open('POST',host,false);
       xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
       xhr.send(prepParm(params));
      }
     else
     {
       xhr.open('GET',host+'/?'+prepParm(params),false);
       xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
      }
     return {status:xhr.status,text:xhr.responseText};
    }
   catch(e)
   {
     xhr.abort();
     msg=e.message;
     return {status:404,text:msg};
    }
  }
              

В PHP все переданные параметры, в зависимости от выбранного метода передачи, будут размещены в массивах $_GET или $_POST,
имеющих структуру: "<ключ>=><значение>".
(Значения придут в кодировке UTF-8. Если Вам нужна другая кодировка, то для перекодировки полученных данных используйте функцию iconv).
Примечание: для имён файлов всегда используйте кодировку CP1251!

Для передачи объектов или ассоциативных массивов в качестве параметров в PHP реализована пара функций JSON_ENCODE/DECODE, перекодирующих массивы и объекты в строки формата JSON и обратно.
Одной из особенностей этих функций является подавление ключей, имеющих пустое строковое или "нулловое" значение. Так же, при работе с этими функциями, необходимо, чтобы текстовые данные были представлены в кодировке UTF-8. Не всегда это удобно.

В JAVASCRIPT декодирование объектов JSON можно сделать так:

 res=eval('('+r.text+')'); 
кодирование же объектов в JSON придётся выполнять отдельной функцией или плагином. Например такой:
 function objToJSON(obj)
 {
   if( !obj ) 
   {
     if( obj===null  ) return null;
     if( obj===false ) return false;
     return '""';
    }
   var type=typeof(obj);
   if( type!='object' ) return '"'+obj+'"';
   var str="{",del="";
   for(e in obj)
   {
     str+=del+'\n"'+e+'":'+objToJSON(obj[e]);
     del=",";
    }
   str+='}';
   //alert(str);
   return str;
  }
              
Если строка будет в кодировке CP1251, то о перекодировании в UTF-8 можно не задумываться (оно будет выполнено при работе функции encodeURIComponent).
Для декодирования в PHP достаточно будет применить функцию json_encode.
(Внимание: все текстовые поля будут в кодировке UTF-8!)

Для отправки ответа с нормальным статусом достаточно выполнить ECHO с нужным текстом или любой другой способ вывода. Здесь перекодировка не потребуется.

Для передачи ассоциативного массива в качестве объекта JAVASCRIPT можно использовать либо функцию JSON_ENCODE
( в этом случае текст может быть перекодирован в UTF-8),
либо:

 function objToJSON($obj)
 {
   $str='{'; $del='';
   if( !is_array($obj) ) return "{'obj':'".$obj."'}";
   foreach($obj as $fkey=>$field)
   {
     $str.="$del'$fkey': ";
     if( is_array($field) )
     {
       $str.=objToJSON($field);
      }
     else $str.="'".str_replace("\n",'\n',$field)."'";
     $del=", ";
    }
   //file_put_contents('test.log',"JSON:\n$str\n",FILE_APPEND);
   $str.="}";
   return $str;
  }
              

Если же надо вернуть ошибочный статус с каким-либо текстом, то лучше это сделать так:


 // $msg_status - посылаемый статус  
 // $status_name[$msg_status] - стандартное название 
 // О статусах см. "Описание протокола HTTP"
 //
 $hdr="HTTP/1.1 ".$msg_status.' '.$status_name[$msg_status];
 header($hdr);
 print $msg_text; // Любой текст

 $msg_status получим в XmlHttpRequest.status,
 $msg_text - в XmlHttpRequest.responseText.

*) Примечание: 
В некоторых руководствах рекомендуется писать:
$hdr="Status: ".$msg_status.' '.$status_name[$msg_status];
У меня это не работает.
              

При обработке AJAX-запроса в PHP следует иметь в виду, что в ответ бубет попадать любой вывод в основной поток
(например: предупреждения, ошибки и т.п.).
Рекомендуется отсекать нежелательный вывод с помощью буферизации вывода функциями ob_.

2. Динамическое изменение страниц.

Спектр применения динамического изменения страниц весьма широк.
Этот сайт является иллюстрацией достаточно активного применения техники использования запросов к серверу и динамической подгрузки страниц.
Так, в частности, все статьи подгружаются клиенту по мере надобности, что позволяет сэкономить время начальной загрузки сайта и оптимизировать трафик.
Перерисовка каких-либо элементов страницы может быть выполнена без перезагрузки страницы изменением содержимого innerHTML у требуемого элемента.
(В jQuery - для этого используется метод html).
Однако, следует соблюдать осторожность при перерисовке таблиц.
IE очень трепетно относится к структуре таблиц, у элементов которых меняется свойство innerHTML. Любое несоответствие вставляемого HTML стандартной (и полной!) структуре таблиц при изменении innerHTML вызовет ошибку.
Чтобы избежать этого, HTML-текст описания таблицы лучше формировать отдельно, а потом этот текст вставлять в innerHTML какого-нибудь "обёрточного" контейнера.

Надеюсь, что с учётом всего вышеизложенного (включая, естественно, основную статью ) применение запросов к серверу не породит у Вас каких-либо проблем.



Запрос к внешним URL.


Обращение к внешним URL из программы-клиента (сайта) строго ограничивается. Но иногда возникает необходимость получения контента или обработки запроса с какого-либо внешнего источника. С сервера такие запросы выполнять можно.

Самих запросов может быть великое множество, равно как и методов их исполнения.
Здесь приводится самый простой.
Основан он на отправке HTTP-запросов через сокеты с передачей параметров методом GET. (Об HTTP можно посмотреть, например, здесь ).
Рассмотрим пример функции реализации такого запроса на PHP.
В качестве параметров функция получает адрес хоста внешнего URL (http:\\ добавлять не надо!), страницу, к которой выполняется запрос, с параметрами запроса и порт (как правило, по умолчанию значение порта при работе с HTTP равно 80, но возможны и иные варианты).
В разделе "Как узнать регион по IP" показан пример использования данной функции при обращении к службе geoip.
Здесь же приведён только текст этой функции.


// Простой GET-запрос по HTTP
// $host - хост URL (например: geoip.ru
// $page - страница с параметрами (например: geo?ip=111.111.111.111)
// $port - порт (по умолчанию: 80; для службы geoip: 7020)
function get_url_sock($host,$page,$port=80) 
{
  $fp=@fsockopen($host,$port,$errno,$errstr,30);
  if( !$fp ){ return "Unknown";} 
  $request ="GET $page HTTP/1.0\r\n";
  $request.="Host: $host\r\n";
  $request.="Accept: text/html, application/xml;q=0.9, */*;q=0.1\r\n";
  $request.="Accept-Charset: windows-1251, utf-8;q=0.6, *;q=0.1\r\n";
  $request.="Accept-Encoding: deflate, gzip, identity, *;q=0\r\n";
  $request.="Accept-Language: ru\r\n";
  $request.="Connection: close\r\n";
  $request.="Keep-Alive: 300\r\n";
  $request.="Expires: Thu, 01 Jan 1970 00:00:01 GMT\r\n";
  $request.="Cache-Control: no-store, no-cache, must-revalidate\r\n";
  $request.="Pragma: no-cache\r\n";
  $request.="Cookie: income=1\r\n";
  $request.="Referer: http://$host/\r\n";
  $request.="User-Agent: Mozilla/5.0 (compatible; MSIE 6.0; Windows 98)\r\n";
  $request.="\r\n";
  fwrite($fp,$request);
  $res='';
  while( !feof($fp) ){ $res.=fgets($fp,128); }
  fclose($fp);
  return $res;
 }
              

Результатом работы функции будет обычный контент страницы, к которой мы обратились (т.е. мы получим то же самое, что получил бы браузер при обращении по ссылке 'http://'.$host.':'.$port.'/'.$page) . Дальнейшая обработка результата всецело ложится на наши плечи.
Как видно из текста, основу данной функции составляет сам HTTP-запрос. Всё остальное - обычная работа с сокетами. Модифицируя данный запрос, можно составлять и другие запросы. Но это - отдельная тема.


Как узнать регион по IP.


На эту тему можно выпускать толстый ежемесячный журнал, с описанием всевозможных сервисов whoIs и т.п.
Лично я в данной статье не собираюсь заниматься ни рекламой, ни описанием подобных сервисов. В Инете, слава Богу, информации достаточно.
Просто мне показалось, что данную тему удобно рассмотреть в качестве простой иллюстрации применения методов, описанных в предыдущих разделах.
Рассмотрим следующую задачу:
На клиентской стороне как-то определяется IP-адрес, для которого требуется:
- определить регион (город) с помощью внешнего сервиса;
- Для "неопределённого" IP вернуть IP клиента и город.
Решение задачи разобьём на 2 блока: "клиентский" и "серверный". Оба блока оформим в виде функций.
1) "Клиентский" блок:
передача IP-адреса нашему серверу с ожиданием ответа


// Регулярное выражение для проверки IP-адресов.
// Взято с http://regexlib.com/REDetails.aspx?regexp_id=32.
var IPpattern=new RegExp
              ("^(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\\."
               +"(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\\."
               +"(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\\."
               +"(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])$");

// Функция проверки IP-адреса
function testIP(ip)
{
  var msg='';
  var res=IPpattern.test(ip);
  if( !res )
  {
    msg="Неправильный IP-адрес: "+ip+"!";
   }
  return msg;
 }

// Функция создания объекта XmlHttpRequest.
// Широко известная функция. 
// Я встречал её под разными названиями 
// и в разных модификациях не одну сотню раз.
function getXmlHttp()
{
  var xmlhttp;
  try 
  {
    xmlhttp=new ActiveXObject("Msxml2.XMLHTTP");
   } 
  catch(e) 
  {
    try 
    {
      xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
     } 
    catch(E){ xmlhttp=false; }
   }
  if( !xmlhttp && typeof XMLHttpRequest!='undefined' ) 
  {
    xmlhttp=new XMLHttpRequest();
   }
  return xmlhttp;
 }
 
function getIPreg(request,pip)
{ var prm='',err='',ip;
  ip=trimStr(pip);
  if( ip!='' )
  {
    err=testIP(ip);
    if( err!='' ) return err;
    prm='ip='+encodeURIComponent(ip); 
   }
  var httpReq=getXmlHttp();
  httpReq.open('POST',request,false);
  httpReq.setRequestHeader('Content-Type',
                           'application/x-www-form-urlencoded');
  httpReq.send(prm);
  if( httpReq.status!=200 ) 
  { // Ошибку возвращаем как строку
    err=httpReq.status;
    err="Ошибка проверки IP-адреса: "+err+"<br/>";
    err+=httpReq.responseText; 
    return err;
   }
  // А правильный результат, как массив:
  // reply[0] - ip, reply[1] - город
  var reply=httpReq.responseText.split('\n');
  return reply;
 }

// Функция обрезания концевых пробелов.
// Полезная функция, но в стандарте её почему-то нет?!
// Любителям острых ощущений могу предложить 
// оформить её в виде String.prototype.trim
// и вызывать как: <string_object>.trim()
function trimStr(s) 
{
  if( typeof s!=='string' ) return '';
  if( s=='' ) return '';
  s=s.replace(/^\s+/g,'');
  return s.replace(/\s+$/g,'');
 }

              

2) "Серверный"блок:
обращение нашего сервера к внешней службе распознавания IP и возврат ответа клиенту. В качестве внешнего сервиса используем ipgeobase.ru.
Этот сервис предоставляет данные о регионах и городах РФ и Украины.
К сожалению, больше ничего этот сервис при используемом вызове не предоставляет.

<php?
 // Определение региона по IP-адресу с использованием сервиса 
 // http://www.ipgeobase.ru
 function get_region_ip($_POST) 
 {
   if( !isset($_POST['ip']) )
   { // Если IP не задан, используем ip-адрес клиента.
     // Об определении клиентского IP см. http://www.phpfaq.ru/ip              
     $ip=$_SERVER['REMOTE_ADDR']; 
    }
   else $ip=$_POST['ip'];
   // По запросу http://www.ipgeobase.ru:7020/geo?ip=...
   // будет выведено XML-описание ip  
   $res=get_url_sock('ipgeobase.ru','/geo?ip='.$ip,7020);
   // Проверяем ответ на наличие в нём XML.
   $list=explode('<',$res,2);
   if( $list[1]=='' ) 
   { // XML точно нет! Формируем ошибку!
     // И отправляем её по назначению
     $hdr="HTTP/1.1 404 Not Found";
     header($hdr);
     print "Вот такой у нас ответ:\n$res"; 
     exit; // Выключаем сценарий
    }
   else
   { 
     // возвращаем отрезанное начало тега, 
     // и считаем, что с этого момента пошёл XML.
     $data='<'.$list[1]; 
     // разбираем структуру ответа как XML
     @$city=($xml=simplexml_load_string($data))?$xml->ip->city:"Unknown";
     if( $city=='' ) 
     {     
       $city='Unknown';
      }
    }
   // Возвращаем нормальный ответ в виде
   echo $ip."\n".$city;
   // и выключаем сценарий
   exit;
  }

 get_region_ip($_POST);

?>
              

Ну и, естественно, соответствующий пример:


      Определение региона по IP


IP-адрес: 
Регион: 








Перекодировка UTF8-CP1251.


   Как известно, стандартные библиотеки PHP предоставляют достаточно большой набор различных функций перекодировки. Зачем, казалось бы, нужна ещё одна?
   Я, например, достаточно долгое время находился в уверенности, что функции ICONV мне хватит на долгую "счастливую" жизнь, и, действительно, для большинства задач так и было.
   Однако довелось столкнуться и с такими задачами, для которых мне не удалось найти подходящих функций, или же их использование было сопряжено с достаточно большой нагрузкой на программирование.
   Одной из таких задач стала, например, задача вывода текстов, набранных в разных кодировках. Причём случалось, что две кодировки присутствовали в одном и том же тексте.
   В другой задаче кодировка получаемого текста была заранее неизвестна (известно было только, что это либо UTF8, либо CP1251).
   Тем самым возникла потребность написать функцию, которая, не меняя (по возможности) одну из кодировок, другую приводит к ней.
В описываемом случае символы кодировки UTF8 приводятся к CP1251, символы кодировки CP1251 в большинстве случаев остаются неизменными.
   Последнее утверждение не 100%, но тот факт, что последовательность кодов UTF8 для представления CP1251 маловероятна в обычных текстах, позволяет надеяться, что использование описанной ниже функции не приведёт к серьёзным искажениям результата.

<?php
/*
 Массив соответствия цепочек кодов UTF8 и CP1251.
 В каждый массив 1-го уровня вложенности для оптимизации
 добавлен служебный ключ lsub.
*/
$tab_UTF8_CP1251=array
(
  0xC2=>array('lsub'=>1,
    0xA0=>0xA0, 0xA4=>0xA4, 0xA6=>0xA6, 0xA7=>0xA7, 0xA9=>0xA9,
    0xAB=>0xAB, 0xAC=>0xAC, 0xAD=>0xAD, 0xAE=>0xAE,
    0xB0=>0xB0, 0xB1=>0xB1, 0xB5=>0xB5, 0xB6=>0xB6, 0xB7=>0xB7, 
    0xBB=>0xBB
   ),
  0xD0=>array('lsub'=>1,
    0x81=>0xA8, 0x82=>0x80, 0x83=>0x81, 0x84=>0xAA, 0x85=>0xBD, 
    0x86=>0xB2, 0x87=>0xAF, 0x88=>0xA3, 0x89=>0x8A, 0x8A=>0x8C, 
    0x8B=>0x8E, 0x8C=>0x8D, 0x8E=>0xA1, 0x8F=>0x8F,
    0x90=>0xC0, 0x91=>0xC1, 0x92=>0xC2, 0x93=>0xC3, 0x94=>0xC4, 
    0x95=>0xC5, 0x96=>0xC6, 0x97=>0xC7, 0x98=>0xC8, 0x99=>0xC9, 
    0x9A=>0xCA, 0x9B=>0xCB, 0x9C=>0xCC, 0x9D=>0xCD, 0x9E=>0xCE, 
    0x9F=>0xCF, 
    0xA0=>0xD0, 0xA1=>0xD1, 0xA2=>0xD2, 0xA3=>0xD3, 0xA4=>0xD4, 
    0xA5=>0xD5, 0xA6=>0xD6, 0xA7=>0xD7, 0xA8=>0xD8, 0xA9=>0xD9, 
    0xAA=>0xDA, 0xAB=>0xDB, 0xAC=>0xDC, 0xAD=>0xDD, 0xAE=>0xDE, 
    0xAF=>0xDF,
    0xB0=>0xE0, 0xB1=>0xE1, 0xB2=>0xE2, 0xB3=>0xE3, 0xB4=>0xE4, 
    0xB5=>0xE5, 0xB6=>0xE6, 0xB7=>0xE7, 0xB8=>0xE8, 0xB9=>0xE9, 
    0xBA=>0xEA, 0xBB=>0xEB, 0xBC=>0xEC, 0xBD=>0xED, 0xBE=>0xEE, 
    0xBF=>0xEF
   ),
  0xD1=>array('lsub'=>1,
    0x80=>0xF0, 0x81=>0xF1, 0x82=>0xF2, 0x83=>0xF3, 0x84=>0xF4, 
    0x85=>0xF5, 0x86=>0xF6, 0x87=>0xF7, 0x88=>0xF8, 0x89=>0xF9, 
    0x8A=>0xFA, 0x8B=>0xFB, 0x8C=>0xFC, 0x8D=>0xFD, 0x8E=>0xFE, 
    0x8F=>0xFF,
    0x91=>0xB8, 0x92=>0x90, 0x93=>0x83, 0x94=>0xBA, 0x95=>0xBE, 
    0x96=>0xB3, 0x97=>0xBF, 0x98=>0xBC, 0x99=>0x9A, 0x9A=>0x9C, 
    0x9B=>0x9E, 0x9C=>0x9D, 0x9E=>0xA2, 0x9F=>0x9F
   ),
  0xD2=>array('lsub'=>1, 0x90=>0xA5, 0x91=>0xB4 ),
  0xE2=>array('lsub'=>2,
    0x80=>array(
      0x93=>0x96, 0x94=>0x97, 0x98=>0x91, 0x99=>0x92, 0x9A=>0x82,
      0x9C=>0x93, 0x9D=>0x94, 0x9E=>0x84,
      0xA0=>0x86, 0xA1=>0x87, 0xA2=>0x95, 0xA6=>0x85,
      0xB0=>0x89, 0xB9=>0x8B, 0xBA=>0x9B
     ),
    0x82=>array( 0xAC=>0x88 ),
    0x84=>array( 0x96=>0xB9, 0xA2=>0x99 )
   )
 );
/*
   Функция декодирования UTF-8 в CP-1251.
   Цепочки кодов, имеющиеся в таблице, заменяются на соответствующий код.
   Цепочки кодов, не найденные в таблице, остаются на месте.
   Бывает полезна при работе с файлами и строками 
   с неопределённой кодировкой.
   В отличие от стандартных функций декодирования не выбрасывает ошибки.
   Возвращает декодированную строку и, в случае заданного 2-го параметра,
   количество декодированных символов.
*/

function decode_utf8_cp1251($str,&$cnt=FALSE)
{ global $tab_UTF8_CP1251;
  $res=''; $len=strlen($str); 
  if( $cnt!==FALSE ) $cnt=0;
  for($i=0;$i<$len;$i++)
  {
    // Код текущего символа
    $c=ord($str{$i});
    // Обрабатываем начало цепочки
    if( $c<0xC0 )
    {
      // Этот код не относится к цепочкам UTF. Оставляем на месте.
      $res.=$str{$i};
      continue;
     }
    // Проверяем на наличие в таблице
    if( !isset($tab_UTF8_CP1251[$c]) ) 
    {
      // Этот код не относится к цепочкам UTF. Оставляем на месте.
      $res.=$str{$i}; continue;
     }
    // Смотрим длину цепочки UTF, соответствующей коду
    $lu=$tab_UTF8_CP1251[$c]['lsub'];
    if( ($i+$lu)>=$len )
    {
      // Этот символ уже не поместится. Оставляем без изменений.
      $res.=$str{$i}; continue;
     }
    // Проверяем цепочку
    $ac=$tab_UTF8_CP1251[$c]; $pres=$str{$i}; 
    for($j=1;$j<=$lu;$j++)
    {
      $c=ord($str{$i+$j});
      // Если нет очередного кода, то - обрыв цепочки.
      // Начальный символ пойдёт без изменения.
      if( !isset($ac[$c]) ) break;	
      // Проверяем на конец цепочки
      if( $j==$lu )
      { // и если он достигнут, 
        // то конец цепочки берём в качестве искомого
        $pres=chr($ac[$c]);
        // смещаемся на следующий символ
        $i+=$j;
        // и увеличиваем счётчик декодированных символов
        if( $cnt!==FALSE ) $cnt++;
        break;
       }
      $ac=$ac[$c]; // Выбираем продолжение цепочки
     }
    $res.=$pres;
   }
  return $res;
 }
?>
              

   В дальнейшем я обнаружил, что в JAVASCRIPT стандартная функция перекодировки отсутствует. Что, по меньшей мере, удивительно, поскольку JAVASCRIPT во внутреннем представлении все тексты хранит в кодировке UTF8.
   Используя приведённую выше функцию удалось за 20 минут написать такую же на JAVASCRIPT.

              

tab_UTF8_CP1251={
  0xC2: {'lsub':1,
          0xA0:0xA0, 0xA4:0xA4, 0xA6:0xA6, 0xA7:0xA7, 0xA9:0xA9,
          0xAB:0xAB, 0xAC:0xAC, 0xAD:0xAD, 0xAE:0xAE, 0xB0:0xB0,
          0xB1:0xB1, 0xB5:0xB5, 0xB6:0xB6, 0xB7:0xB7, 0xBB:0xBB
         },
  0xD0: {'lsub':1,
          0x81:0xA8, 0x82:0x80, 0x83:0x81, 0x84:0xAA, 0x85:0xBD,
          0x86:0xB2, 0x87:0xAF, 0x88:0xA3, 0x89:0x8A, 0x8A:0x8C,
          0x8B:0x8E, 0x8C:0x8D, 0x8E:0xA1, 0x8F:0x8F, 0x90:0xC0,
          0x91:0xC1, 0x92:0xC2, 0x93:0xC3, 0x94:0xC4, 0x95:0xC5,
          0x96:0xC6, 0x97:0xC7, 0x98:0xC8, 0x99:0xC9, 0x9A:0xCA,
          0x9B:0xCB, 0x9C:0xCC, 0x9D:0xCD, 0x9E:0xCE, 0x9F:0xCF,
          0xA0:0xD0, 0xA1:0xD1, 0xA2:0xD2, 0xA3:0xD3, 0xA4:0xD4,
          0xA5:0xD5, 0xA6:0xD6, 0xA7:0xD7, 0xA8:0xD8, 0xA9:0xD9,
          0xAA:0xDA, 0xAB:0xDB, 0xAC:0xDC, 0xAD:0xDD, 0xAE:0xDE,
          0xAF:0xDF, 0xB0:0xE0, 0xB1:0xE1, 0xB2:0xE2, 0xB3:0xE3,
          0xB4:0xE4, 0xB5:0xE5, 0xB6:0xE6, 0xB7:0xE7, 0xB8:0xE8,
          0xB9:0xE9, 0xBA:0xEA, 0xBB:0xEB, 0xBC:0xEC, 0xBD:0xED,
          0xBE:0xEE, 0xBF:0xEF
         },
  0xD1: {'lsub':1,
          0x80:0xF0, 0x81:0xF1, 0x82:0xF2, 0x83:0xF3, 0x84:0xF4,
          0x85:0xF5, 0x86:0xF6, 0x87:0xF7, 0x88:0xF8, 0x89:0xF9,
          0x8A:0xFA, 0x8B:0xFB, 0x8C:0xFC, 0x8D:0xFD, 0x8E:0xFE,
          0x8F:0xFF, 0x91:0xB8, 0x92:0x90, 0x93:0x83, 0x94:0xBA,
          0x95:0xBE, 0x96:0xB3, 0x97:0xBF, 0x98:0xBC, 0x99:0x9A,
          0x9A:0x9C, 0x9B:0x9E, 0x9C:0x9D, 0x9E:0xA2, 0x9F:0x9F
         },
  0xD2: {'lsub':1, 0x90:0xA5, 0x91:0xB4 },
  0xE2: {'lsub':2,
    0x80: {
            0x93:0x96, 0x94:0x97, 0x98:0x91, 0x99:0x92, 0x9A:0x82,
            0x9C:0x93, 0x9D:0x94, 0x9E:0x84, 0xA0:0x86, 0xA1:0x87,
            0xA2:0x95, 0xA6:0x85, 0xB0:0x89, 0xB9:0x8B, 0xBA:0x9B
           },
    0x82: { 0xAC:0x88 },
    0x84: { 0x96:0xB9, 0xA2:0x99 }
   }
 };

/*
   Функция декодирования UTF-8 в CP-1251.
   Кодировки, не соответствующие таблице, остаются на месте.
   Бывает полезна при работе с файлами и строками 
   с неопределённой кодировкой.
   В отличие от стандартных функций декодирования не выбрасывает ошибки.
   Возвращает декодированную строку и количество декодированных символов.
*/
function decode_utf8_cp1251(str)
{ 
  var res='',len=str.length,cnt=0;
  var c,pres,i,j;
  str=new String(str);
  for(i=0;i=len )
    {
      // Этот символ уже не поместится. Оставляем без изменений.
      res+=str.charAt(i); continue;
     }
    // Проверяем цепочку
    var ac=tab_UTF8_CP1251[c]; pres=str.charAt(i); 
    for(j=1;j<=lu;j++)
    {
      c=str.charCodeAt(i+j);
      // Если нет очередного символа, то - обрыв цепочки.
      // Символ пойдёт без изменения.
      if( c in ac ) break;	
      // Проверяем на конец цепочки
      if( j==lu )
      { // и если он достигнут, 
        // то конец цепочки берём в качестве искомого
        pres=String.fromCharCode(ac[c]);
        // смещаемся на следующий символ
        i+=j;
        // и увеличиваем счётчик декодированных символов
        cnt++;
        break;
       }
      ac=ac[c];
     }
    res+=pres;
   }
  return { dec:res, cnt:cnt };
 }

              

   Несколько слов об используемом массиве.
Сперва я планировал написать данный массив вручную (скачал таблицы соответствий, по 2 минуты медитировал над каждой строкой и т.п.).
   По истечении 10 минут, когда это занятие мне прискучило, я решил написать функцию, которая бы сама генерила требуемый массив.


<?php
function cp1251_utf8_construct()
{
  file_put_contents('alpha.txt',"");
  function addCode($scode,$code,&$res)
  {
    $len=strlen($scode);
    $c=ord($scode{0}); 
    $c=sprintf('0x%X',$c);
    if( !isset($res[$c]) ) $res[$c]=array('lsub'=>$len-1);
    if( $len==1 ) 
    {
      //$res[$c]=$code;
      $res[$c]=sprintf('0x%X',$code);
      return;
     }
    $scode=substr($scode,1);
    addCode($scode,$code,$res[$c]);
   }
  $ucodes=array();
  $len=count($str);
  for($i=1;$i<256;$i++)
  {
    if( $i==152 ) continue;
    $ustr=iconv('cp1251','utf-8',chr($i));
    if( strlen($ustr)<=1 ) continue;
    addCode($ustr,$i,$ucodes);
   }
  function codesort(&$arr)
  {
    foreach($arr as $k=>$v)
    {
      if( is_array($v) ) 
      {
        //file_put_contents('alpha.txt',"Sort $k=>var_export($v,true).\n---\n",FILE_APPEND);
        codesort($v);
        //file_put_contents('alpha.txt',"Have $k=>var_export($v,true).\n---\n",FILE_APPEND);
        $arr[$k]=$v;
       }
     }
    ksort($arr);
   }
  codesort($ucodes);

  function printsub($sub,$spc,$tbfile)
  { 
    $del='';
    foreach($sub as $k=>$v)
    {
      if( $k=='lsub' ) continue;
      $str="$del\n$spc$k=>";
      file_put_contents($tbfile,$str,FILE_APPEND); $del=',';
      if( !is_array($v) ) 
      {
        file_put_contents($tbfile,$v,FILE_APPEND);
        continue;
       }
      file_put_contents($tbfile,'array(',FILE_APPEND);
      printsub($v,$spc.'  ',$tbfile);
      $str="\n$spc )";
      file_put_contents($tbfile,$str,FILE_APPEND);
     }
   }
  $tbfile='utf8_cp1251.log';
  file_put_contents($tbfile,'<?php'."\n".'$tab_UTF8_CP1251=array'."\n("); 
  $del='';
  foreach($ucodes as $k=>$v)
  {
    $str="$del\n  $k=>array('lsub'=>".$v['lsub'].",";
    file_put_contents($tbfile,$str,FILE_APPEND); 
    printsub($v,'    ',$tbfile);
    $str="\n   )"; $del=',';
    file_put_contents($tbfile,$str,FILE_APPEND); 
   }
  file_put_contents($tbfile,"\n );\n".'?>',FILE_APPEND); 

  file_put_contents('alpha.txt',var_export($ucodes,true)."\n",FILE_APPEND);
 }
?>



Загрузка файлов на сервер.


http://www.php.ru/manual/features.file-upload.html http://phpclub.ru/detail/article/upload

Об использовании Ajax для пересылки клиентских файлов (и не только ) можно посмотреть здесь: http://sefirut.ru/publ/uroki_po_html_css/raznoe/simple_ajax_ili_razbiraem_vsjo_po_polochkam/3-1-0-13.
Здесь же можно скачать довольно интересный и простой пример simpleAjax, изучение которого может оказаться полезным.

Загрузка файла на сервер при помощи AJAX.
Файл будет переименован в <uploaded_test.txt>.
Его размер не должен превышать 100 Кб.


Пример 1.
Используем simpleAjax.





Про клиентскую часть загрузки файлов через Ajax очень неплохо написано здесь: http://web-linux.ru/?p=417.
И тут же можно найти неплохие плагины (в частности, AjaxFileUpload).


Пример 2.
Ajax File Upload






Работа с файлами Excel.


Для создания или чтения файлов XLS можно использовать библиотеку PHPExcel. Найти её можно здесь: http://www.codeplex.com/PHPExcel

Имейте в виду, что данная библиотека требует наличия классов ZipArchive и XmlReader/XmlWriter.

Примеров описания работы с этой библиотекой - море.
Например:
- чтение файлов: http://www.cleverscript.ru/php/scripts-php/28-phpexel.html
- создание файлов: http://www.web-junior.net/sozdanie-excel-fajjlov-s-pomoshhyu-phpexcel/"



"Карты".


Используйте карты Yandex!
Хорошая штука!

http://api.yandex.ru/maps/intro/concepts/intro.xml




Модальные окна.


Окна, описываемые в этом разделе, не совсем модальные. Скорее их можно назвать "перекрывающими" ("оверлейными") или "накладывающимися".

Тем не менее их применение достаточно удобно во многих случаях. На этом сайте я использую их в качестве прогресс-бара, форм для регистрации, отправки сообщений, "мягкого" алерта и т.п.

Основная идея этих окон следующая:
Создаём класс стиля с запрещённым отображением и достаточно большим z-индексом.
Например, так:

.overwindow
{
  position: absolute; width: 440px; height: 220px; display: none; 
  /* Пока данное значение срабатывало */
  z-index: 12000;
  padding: 20px;
 }

*)   Конечно, лучше всего было бы написать функцию для нахождения 
     максимального z-индекса на странице, 
     но здесь я оставляю этот вопрос открытым!

               

Требуемые окошки размещаем в элементах этого класса. В качестве таких элементов лучше всего использовать div-контейнеры (с уникальными idами).
Теперь надо решить вопрос "включения"/"отключения" данных окошек.
Для этого напишем две функции. Например такие:


// Функция отключения элемента класса overwindow c заданным id
function hideWindow(id)
{
  $('#'+id+'.overwindow').hide(); 
 };   

// Функция отображения элемента класса overwindow c заданным id
function showWindow(id,x,y)
{ 
  // Устанавливаем координаты окна, если они заданы.
  // Если координаты не заданы, то работаем по принципу: 
  // "Где выросло, там выросло!"
  if( y!=undefined ) $('#'+id+'.overwindow').css('top',y); 
  if( x!=undefined ) $('#'+id+'.overwindow').css('left',x);
  $('#'+id+'.overwindow').fadeIn(100);
 }
               

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

Для статических объектов, определённых в HTML-коде, на каждом элементе overwindow можно задать функцию отображения окна по событию "onclick".
Например так:


$(document).ready(
function()
{
  $('.overwindow').each(
  function(i,el)
  {
    var id=$(el).attr('id');
    $(el).click(
    function(e,id)
    { 
      // Из e можно вытащить координаты клика и на них рисовать окно. 
      // Но я бы не советовал очень уж полагаться на эти координаты.
      showWindow(id);
     }
   });
 });
               
Вызов функции закрытия придётся вызывать для каждого окна отдельно.

Для примера рассмотрим функцию "мягкого" alertа.
(Аналог функции MessageBox в некоторых средах).
Данная функция выводит окошко с заданным содержимым в центре видимой области окна браузера.
"Немодальность" функции слегка компенсируется постоянным расположением окна в центре видимой области экрана.


// Функция закрытия окна. Используется как обработчик submit. 
var msgvalue; // Переменная идентификации кнопок
var alertStop=function()
{
  $('div#modalmsg.overwindow').hide();
  $('div#modalmsg.overwindow').remove(); 
  $(window).unbind('scroll');
  return false;
 }

// Функция "Мягкий alert" 
function softAlert(msg,hdr)
{
  if( !hdr ) hdr='Внимание!';
  modalMsg(msg,['OK'],alertStop,hdr);
  return false;
 }

// Основная функция создания окна сообщения 
function modalMsg(msg,buttons,subFunction,hdr)
{
  // Блокируем выполнение при уже существующем окне
  if( $('div#modalmsg.overwindow').length ) return;
  if( !hdr ) hdr='Внимание';
  // Создаём новый div и привязываем его к BODY 
  var div=$('<div>').appendTo('body');
  // Присваиваем ему класс overwindow 
  $(div).attr('class','overwindow');
  // Присваиваем ему id modalmsg 
  $(div).attr('id','modalmsg');
  $(div).width('320');
  // Создаём контент (форма с id=modalAForm) 
  var msgHtml=setMsgContent(hdr,msg,buttons,'modalAForm');
  // Устанавливаем его 
  $(div).html(msgHtml);
  // Обработчик submit приделываем ручками 
  var frm=$('#modalAForm').bind('submit',subFunction);
  // Вычисляем координаты точки привязки 
  var vp=jqViewPort(); // получаем координаты видимой части окна 
  var dw=$(div).width(),dh=$(div).height();
  var mx=(vp.x0+vp.x1-dw)/2;  
  var my=(vp.y0+vp.y1-dh)/2;  
  // Рисуем окошко 
  showWindow('modalmsg',mx,my); 
  // И оставляем его на месте при скроллинге 
  $(window).bind('scroll',function(e)
  { // Функция перерисовки окна
    vp=jqViewPort();
    mx=(vp.x0+vp.x1-dw)/2;  
    my=(vp.y0+vp.y1-dh)/2;  
    showWindow('modalmsg',mx,my);
    return true;  
  });
 }

// Функция вычисления видимых координат окна 
function jqViewPort() 
{ 
  var top=$(window).scrollTop(),
      lft=$(window).scrollLeft(),
      bot=top+$(window).height(),
      rgt=lft+$(window).width();
  return {x0: lft, y0: top, x1: rgt, y1: bot};
 }

// Функция создания контента сообщения
function setMsgContent(hdr,msg,buttons,frmid)
{ var msgHtml;
  if( !frmid ) frmid='Unnamed';
  msgHtml='<table align="center" width="310" style="border:1px solid #003333">' 
         +'<tr><td>'
         +'<table align="center" width="310" style="border:2px solid #003333">'
         +'<tr><td>'
         +'<form id="'+frmid+'">'
         +'<div style="color:#FF3333; text-align: center; font-size: 12px; background-color: #C2DBED">'
         +'<br/<'+hdr+'<br/><br/>'
         +'</div>' 
         +'<div style="text-align: center; font-size: 12px; color:black; background-color: white">'
         +'<br/>'
         +msg
         +'<br/>'
         +'</div>' 
         +'<div style="text-align: center; font-size: 12px; background-color: #C2DBED">'
         +'<br/>';
  for(i=0;i<buttons.length;i++)
  {
    msgHtml+='<input id="'+frmid+i+'" value="'+buttons[i]+'" class="button" type="submit"'
            +'       align="center" onclick="msgvalue='+i+';">';
   }
  msgHtml+='<br/><br/>'
          +'</div>'
          +'</div>'
          +'</form>'
          +'</td></tr>'
          +'</table>'
          +'</td></tr>'
          +'</table>';
  return msgHtml;
 }

               

Вроде бы и всё. Можно посмотреть результат.

Данную функцию можно было бы сделать полностью модальной, если бы удалось перехватывать и запрещать все события окна при самом их зарождении.
Мне, к сожалению, это пока не удалось.
Буду благодарен любому, кто поделится информацией на эту тему.

Написать комментарий
 


Прокрутка фотоплёнки.



.film
{ 
  border: solid 2px color: black; 
  height: 100px; width: 100px;
  /* background-image: url("..."); */
  overflow: hidden;
 }

.slide
{
  position: relative;
  top: 0px; left: 0px;
  background: #C0C0C0;
 }

              

Прокрутка фотоплёнки


// Функция-конструктор объекта Films
// film  - id соответствующего img
// ncadr - количество кадров
// pos   - регулируемая позиция (top или left)   
function Films(film,ncadr,pos)
{
  try
  {
  this.pos='left'; if( pos ) this.pos=pos;
  this.cadr=0; this.nc=ncadr; this.fint=null;
  this.film=film; this.wid=0; this.dis=0;
  this.efilm=$('#'+film); this.loop=false;
  if( this.efilm.length )
  {
    if( this.pos=='left' ) this.wid=this.efilm.width();
    else this.wid=this.efilm.height();
    this.dis=this.wid/ncadr;
   }
   }
  catch(e){ alert('Films\n'+e); }
 }

// Прототип объекта Films
Films.prototype=
{ // Анимированный сдвиг кадра
  moveFilm: function(dir,obj)
  {
    try
    {
    if( !obj ){ obj=this; /*alert('use this');*/ }
    var lft=obj.efilm.css(obj.pos);
    var epos=lft.indexOf('px');
    if( epos==-1 ) epos=lft.length;
    lft=lft.substring(0,epos);
    if( dir=='r' ) 
    {
      obj.cadr++; 
      if( obj.cadr>=obj.nc ) obj.cadr=0;
     }
    if( dir=='l' ) 
    {
      obj.cadr--; 
      if( obj.cadr<0 )
      { 
        obj.cadr=obj.nc-1; lft=-obj.wid;
        obj.efilm.css(obj.pos,lft+'px'); 
       }
     }
    lft=Math.round(obj.dis*obj.cadr);
    var ecd=lft+obj.dis,jump=false;
    if( ecd>obj.wid ){ jump=true; lft=Math.floor(obj.wid-obj.dis)-2; }
    if( (lft-obj.dis)<0 ){ jump=true; lft=0; }
    lft=-lft;
    if( jump )
    {
      if( dir=='r' )
      {
        obj.efilm.css(obj.pos,obj.dis+'px');
       }
     }
    if( obj.pos=='left' ) obj.efilm.animate({left: lft+'px'},500);
    else obj.efilm.animate({top: lft+'px'},500);
    }
   catch(e){ alert('moveFilm\n'+e); }
   },

  // Прокрутка ленты в соответствующем направлении
  playFilm: function(dir)
  {
    try
    {
    if( this.fint!=null ) return false;
    var mf=this.moveFilm,obj=this;
    this.fint=setInterval(function(){ mf(dir,obj); },1000);
     }
    catch(e){ alert('playFilm\n'+e); }
   },

  // "Прыжковая" смена кадра
  setCadr: function(dir,obj)
  {
    try
    {
    if( !obj ){ obj=this; /*alert('use this');*/ }
    if( dir=='r' ) 
    {
      obj.cadr++; 
      if( obj.cadr>=obj.nc ) obj.cadr=0;
     }
    if( dir=='l' ) 
    {
      obj.cadr--; 
      if( obj.cadr<0 )
      { 
        obj.cadr=obj.nc-1; 
       }
     }
    var lft=-Math.round(obj.dis*obj.cadr);
    obj.efilm.css(obj.pos,lft+'px');
     }
    catch(e){ alert('playFilm\n'+e); }
   },

  // Просмотр ленты с "прыжковой" сменой кадров 
  rollFilm: function(dir,tmi)
  {
    try
    {
      if( this.fint!=null ) return false;
      var sc=this.setCadr,obj=this;
      // Выясняем, является ли tmi массивом
      var tmitype=Object.prototype.toString.call(tmi);
      if( tmitype==='[object Array]' )
      { var i=0;
        this.loop=true; 
        setTimeout(function(){ A(obj,tmi,dir,i); },tmi[0]);
       }
      else this.fint=setInterval(function(){ sc(dir,obj); },tmi);
     }
    catch(e){ alert('playFilm\n'+e); }
   },

  stopFilm: function()
  {
    if( this.loop ) this.loop=false;
    if( this.fint!=null ){ clearInterval(this.fint); this.fint=null; }
   }
 }

  function A(obj,tmi,dir,i)
  {
    if( !obj.loop ) return;
    obj.setCadr(dir,obj);
    i++; if( i>=tmi.length ) i=0;
    setTimeout(function(){ A(obj,tmi,dir,i); },tmi[i]);
   } 

              

Почувствуйте разницу

На кадрах внизу слева-направо расположены:
- плёнка, раскадрированная по вертикали;
- анимационная GIF-картинка;
- плёнка, раскадрированная по горизонтали;



А вот функция управления:


var film4,vcarcV,vcarcH;
function bClick()
{
  var ecarp=document.getElementById('carcP');
  if( !tmp ) tmp=document.getElementById('carcTmp');
  var si=ecarp.replaceChild(tmp,ecarp.firstChild);
  /*alert(si.tagName);*/
  $(ecarp.firstChild).show();
  var dbutt=document.getElementById('Carcusha');
  tm=[2900,300,600,500,400,300,200,3000];
  vcarcV.rollFilm('r',tm); 
  vcarcH.rollFilm('r',tm);
  dbutt.value='Остановить!';
  dbutt.onclick=function()
  {
    dbutt.value='Посмотреть!';
    tmp=ecarp.replaceChild(si,tmp);
    vcarcV.stopFilm(); vcarcV.cadr=vcarcV.nc-1; vcarcV.setCadr('r');
    vcarcH.stopFilm(); vcarcH.cadr=vcarcH.nc-1; vcarcH.setCadr('r');
    dbutt.onclick=function(){ return bClick(); };
    return false;
   };
  return false;
 };

              

Конечно, по сравнению, с анимационным GIF мы получаем дополнительные заботы, но за это приобретаем полную управляемость процессом анимации.


Создание таблицы на весь контент.



<div align="center" 
     style="margin:0; padding: 0; 
            height:100%; width:100%; left: 0;
            position: absolute;">

              


<table width="100%" height="100%" ... >

              

Можно добавить  style="table-layout: fixed;"

2. "Программирование на JAVA. Разные полезные мелочи."
  II. Проекты  

III. Композиции