четверг, 31 марта 2011 г.

Совещание

Петров пришел во вторник на совещание. Ему там вынули мозг, разложили по блюдечкам и стали есть, причмокивая и вообще выражая всяческое одобрение. Начальник Петрова, Недозайцев, предусмотрительно раздал присутствующим десертные ложечки. И началось.

— Коллеги, — говорит Морковьева, — перед нашей организацией встала масштабная задача. Нам поступил на реализацию проект, в рамках которого нам требуется изобразить несколько красных линий. Вы готовы взвалить на себя эту задачу?

— Конечно, — говорит Недозайцев. Он директор, и всегда готов взвалить на себя проблему, которую придется нести кому-то из коллектива. Впрочем, он тут же уточняет: — Мы же это можем?

Начальник отдела рисования Сидоряхин торопливо кивает:

— Да, разумеется. Вот у нас как раз сидит Петров, он наш лучший специалист в области рисования красных линий. Мы его специально пригласили на совещание, чтобы он высказал свое компетентное мнение.

— Очень приятно, — говорит Морковьева. — Ну, меня вы все знаете. А это — Леночка, она специалист по дизайну в нашей организации.

Леночка покрывается краской и смущенно улыбается. Она недавно закончила экономический, и к дизайну имеет такое же отношение, как утконос к проектированию дирижаблей.

— Так вот, — говорит Морковьева. — Нам нужно нарисовать семь красных линий. Все они должны быть строго перпендикулярны, и кроме того, некоторые нужно нарисовать зеленым цветом, а еще некоторые — прозрачным. Как вы считаете, это реально?

— Нет, — говорит Петров.

— Давайте не будем торопиться с ответом, Петров, — говорит Сидоряхин. — Задача поставлена, и ее нужно решить. Вы же профессионал, Петров. Не давайте нам повода считать, что вы не профессионал.

— Видите ли, — объясняет Петров, — термин «красная линия» подразумевает, что цвет линии — красный. Нарисовать красную линию зеленым цветом не то, чтобы невозможно, но очень близко к невозможному…

— Петров, ну что значит «невозможно»? — спрашивает Сидоряхин.

— Я просто обрисовываю ситуацию. Возможно, есть люди, страдающие дальтонизмом, для которых действительно не будет иметь значения цвет линии, но я не уверен, что целевая аудитория вашего проекта состоит исключительно из таких людей.

— То есть, в принципе, это возможно, мы правильно вас понимаем, Петров? — спрашивает Морковьева.

Петров осознает, что переборщил с образностью.

— Скажем проще, — говорит он. — Линию, как таковую, можно нарисовать совершенно любым цветом. Но чтобы получилась красная линия, следует использовать только красный цвет.

— Петров, вы нас не путайте, пожалуйста. Только что вы говорили, что это возможно.

Петров молча проклинает свою болтливость.

— Нет, вы неправильно меня поняли. Я хотел лишь сказать, что в некоторых, крайне редких ситуациях, цвет линии не будет иметь значения, но даже и тогда — линия все равно не будет красной. Понимаете, она красной не будет! Она будет зеленой. А вам нужна красная.

Наступает непродолжительное молчание, в котором отчетливо слышится тихое напряженное гудение синапсов.

— А что если, — осененный идеей, произносит Недозайцев, — нарисовать их синим цветом?

— Все равно не получится, — качает головой Петров. — Если нарисовать синим — получатся синие линии.

Опять молчание. На этот раз его прерывает сам Петров.

— И я еще не понял… Что вы имели в виду, когда говорили о линиях прозрачного цвета?

Морковьева смотрит на него снисходительно, как добрая учительница на отстающего ученика.

— Ну, как вам объяснить?.. Петров, вы разве не знаете, что такое «прозрачный»?

— Знаю.

— И что такое «красная линия», надеюсь, вам тоже не надо объяснять?

— Нет, не надо.

— Ну вот. Вы нарисуйте нам красные линии прозрачным цветом.

Петров на секунду замирает, обдумывая ситуацию.

— И как должен выглядеть результат, будьте добры, опишите пожалуйста? Как вы себе это представляете?

— Ну-у-у, Петро-о-ов! — говорит Сидоряхин. — Ну давайте не будем… У нас что, детский сад? Кто здесь специалист по красным линиям, Морковьева или вы?

— Я просто пытаюсь прояснить для себя детали задания…

— Ну, а что тут непонятного-то?.. — встревает в разговор Недозайцев. — Вы же знаете, что такое красная линия?

— Да, но…

— И что такое «прозрачный», вам тоже ясно?

— Разумеется, но…

— Так что вам объяснять-то? Петров, ну давайте не будем опускаться до непродуктивных споров. Задача поставлена, задача ясная и четкая. Если у вас есть конкретные вопросы, так задавайте.

— Вы же профессионал, — добавляет Сидоряхин.

— Ладно, — сдается Петров. — Бог с ним, с цветом. Но у вас там еще что-то с перпендикулярностью?..

— Да, — с готовностью подтверждает Морковьева. — Семь линий, все строго перпендикулярны.

— Перпендикулярны чему? — уточняет Петров.

Морковьева начинает просматривать свои бумаги.

— Э-э-э, — говорит она наконец. — Ну, как бы… Всему. Между собой. Ну, или как там… Я не знаю. Я думала, это вы знаете, какие бывают перпендикулярные линии, — наконец находится она.

— Да конечно знает, — взмахивает руками Сидоряхин. — Профессионалы мы тут, или не профессионалы?..

— Перпендикулярны могут быть две линии, — терпеливо объясняет Петров. — Все семь одновременно не могут быть перпендикулярными по отношению друг к другу. Это геометрия, 6 класс.

Морковьева встряхивает головой, отгоняя замаячивший призрак давно забытого школьного образования. Недозайцев хлопает ладонью по столу:

— Петров, давайте без вот этого: «6 класс, 6 класс». Давайте будем взаимно вежливы. Не будем делать намеков и скатываться до оскорблений. Давайте поддерживать конструктивный диалог. Здесь же не идиоты собрались.

— Я тоже так считаю, — говорит Сидоряхин.

Петров придвигает к себе листок бумаги.

— Хорошо, — говорит он. — Давайте, я вам нарисую. Вот линия. Так?

Морковьева утвердительно кивает головой.

— Рисуем другую… — говорит Петров. — Она перпендикулярна первой?

— Ну-у…

— Да, она перпендикулярна.

— Ну вот видите! — радостно восклицает Морковьева.

— Подождите, это еще не все. Теперь рисуем третью… Она перпендикулярна первой линии?..

Вдумчивое молчание. Не дождавшись ответа, Петров отвечает сам:

— Да, первой линии она перпендикулярна. Но со второй линией она не пересекается. Со второй линией они параллельны.

Наступает тишина. Потом Морковьева встает со своего места и, обогнув стол, заходит Петрову с тыла, заглядывая ему через плечо.

— Ну… — неуверенно произносит она. — Наверное, да.

— Вот в этом и дело, — говорит Петров, стремясь закрепить достигнутый успех. — Пока линий две, они могут быть перпендикулярны. Как только их становится больше…

— А можно мне ручку? — просит Морковьева.

Петров отдает ручку. Морковьева осторожно проводит несколько неуверенных линий.

— А если так?..

Петров вздыхает.

— Это называется треугольник. Нет, это не перпендикулярные линии. К тому же их три, а не семь.

Морковьева поджимает губы.

— А почему они синие? — вдруг спрашивает Недозайцев.

— Да, кстати, — поддерживает Сидоряхин. — Сам хотел спросить.

Петров несколько раз моргает, разглядывая рисунок.

— У меня ручка синяя, — наконец говорит он. — Я же просто чтобы продемонстрировать…

— Ну, так может, в этом и дело? — нетерпеливо перебивает его Недозайцев тоном человека, который только что разобрался в сложной концепции и спешит поделиться ею с окружающими, пока мысль не потеряна. — У вас линии синие. Вы нарисуйте красные, и давайте посмотрим, что получится.

— Получится то же самое, — уверенно говорит Петров.

— Ну, как то же самое? — говорит Недозайцев. — Как вы можете быть уверены, если вы даже не попробовали? Вы нарисуйте красные, и посмотрим.

— У меня нет красной ручки с собой, — признается Петров. — Но я могу совершенно…

— А что же вы не подготовились, — укоризненно говорит Сидоряхин. — Знали же, что будет собрание…

— Я абсолютно точно могу вам сказать, — в отчаянии говорит Петров, — что красным цветом получится точно то же самое.

— Вы же сами нам в прошлый раз говорили, — парирует Сидоряхин, — что рисовать красные линии нужно красным цветом. Вот, я записал себе даже. А сами рисуете их синей ручкой. Это что, красные линии по-вашему?

— Кстати, да, — замечает Недозайцев. — Я же еще спрашивал вас про синий цвет. Что вы мне ответили?

Петрова внезапно спасает Леночка, с интересом изучающая его рисунок со своего места.

— Мне кажется, я понимаю, — говорит она. — Вы же сейчас не о цвете говорите, да? Это у вас про вот эту, как вы ее называете? Перпер-чего-то-там?

— Перпендикулярность линий, да, — благодарно отзывается Петров. — Она с цветом линий никак не связана.

— Все, вы меня запутали окончательно, — говорит Недозайцев, переводя взгляд с одного участника собрания на другого. — Так у нас с чем проблемы? С цветом или с перпендикулярностью?

Морковьева издает растерянные звуки и качает головой. Она тоже запуталась.

— И с тем, и с другим, — тихо говорит Петров.

— Я ничего не могу понять, — говорит Недозайцев, разглядывая свои сцепленные в замок пальцы. — Вот есть задача. Нужно всего-то семь красных линий. Я понимаю, их было бы двадцать!.. Но тут-то всего семь. Задача простая. Наши заказчики хотят семь перпендикулярных линий. Верно?

Морковьева кивает.

— И Сидоряхин вот тоже не видит проблемы, — говорит Недозайцев. — Я прав, Сидоряхин?.. Ну вот. Так что нам мешает выполнить задачу?

— Геометрия, — со вздохом говорит Петров.

— Ну, вы просто не обращайте на нее внимания, вот и все! — произносит Морковьева.

Петров молчит, собираясь с мыслями. В его мозгу рождаются одна за другой красочные метафоры, которые позволили бы донести до окружающих сюрреализм происходящего, но как назло, все они, облекаясь в слова, начинаются неизменно словом «Блять!», совершенно неуместным в рамках деловой беседы.

Устав ждать ответа, Недозайцев произносит:

— Петров, вы ответьте просто — вы можете сделать или вы не можете? Я понимаю, что вы узкий специалист и не видите общей картины. Но это же несложно — нарисовать какие-то семь линий? Обсуждаем уже два часа какую-то ерунду, никак не можем прийти к решению.

— Да, — говорит Сидоряхин. — Вы вот только критикуете и говорите: «Невозможно! Невозможно!» Вы предложите нам свое решение проблемы! А то критиковать и дурак может, простите за выражение. Вы же профессионал!

Петров устало изрекает:

— Хорошо. Давайте я нарисую вам две гарантированно перпендикулярные красные линии, а остальные — прозрачным цветом. Они будут прозрачны, и их не будет видно, но я их нарисую. Вас это устроит?

— Нас это устроит? — оборачивается Морковьева к Леночке. — Да, нас устроит.

— Только еще хотя бы пару — зеленым цветом, — добавляет Леночка. — И еще у меня такой вопрос, можно?

— Да, — мертвым голосом разрешает Петров.

— Можно одну линию изобразить в виде котенка?

Петров молчит несколько секунд, а потом переспрашивает:

— Что?

— Ну, в виде котенка. Котеночка. Нашим пользователям нравятся зверюшки. Было бы очень здорово…

— Нет, — говорит Петров.

— А почему?

— Нет, я конечно могу нарисовать вам кота. Я не художник, но могу попытаться. Только это будет уже не линия. Это будет кот. Линия и кот — разные вещи.

— Котенок, — уточняет Морковьева. — Не кот, а котенок, такой маленький, симпатичный. Коты, они…

— Да все равно, — качает головой Петров.

— Совсем никак, да?.. — разочарованно спрашивает Леночка.

— Петров, вы хоть дослушали бы до конца, — раздраженно говорит Недозайцев. — Не дослушали, а уже говорите «Нет».

— Я понял мысль, — не поднимая взгляда от стола, говорит Петров. — Нарисовать линию в виде котенка невозможно.

— Ну и не надо тогда, — разрешает Леночка. — А птичку тоже не получится?

Петров молча поднимает на нее взгляд и Леночка все понимает.

— Ну и не надо тогда, — снова повторяет она.

Недозайцев хлопает ладонью по столу.

— Так на чем мы остановились? Что мы делаем?

— Семь красных линий, — говорит Морковьева. — Две красным цветом, и две зеленым, и остальные прозрачным. Да? Я же правильно поняла?

— Да, — подтверждает Сидоряхин прежде, чем Петров успевает открыть рот.

Недозайцев удовлетворенно кивает.

— Вот и отлично… Ну, тогда все, коллеги?.. Расходимся?.. Еще вопросы есть?..

— Ой, — вспоминает Леночка. — У нас еще есть красный воздушный шарик! Скажите, вы можете его надуть?

— Да, кстати, — говорит Морковьева. — Давайте это тоже сразу обсудим, чтобы два раза не собираться.

— Петров, — поворачивается Недозайцев к Петрову. — Мы это можем?

— А какое отношение ко мне имеет шарик? — удивленно спрашивает Петров.

— Он красный, — поясняет Леночка.

Петров тупо молчит, подрагивая кончиками пальцев.

— Петров, — нервно переспрашивает Недозайцев. — Так вы это можете или не можете? Простой же вопрос.

— Ну, — осторожно говорит Петров, — в принципе, я конечно могу, но…

— Хорошо, — кивает Недозайцев. — Съездите к ним, надуйте. Командировочные, если потребуется, выпишем.

— Завтра можно? — спрашивает Морковьева.

— Конечно, — отвечает Недозайцев. — Я думаю, проблем не будет… Ну, теперь у нас все?.. Отлично. Продуктивно поработали… Всем спасибо и до свидания!

Петров несколько раз моргает, чтобы вернуться в объективную реальность, потом встает и медленно бредет к выходу. У самого выхода Леночка догоняет его.

— А можно еще вас попросить? — краснея, говорит Леночка. — Вы когда шарик будете надувать… Вы можете надуть его в форме котенка?..

Петров вздыхает.

— Я все могу, — говорит он. — Я могу абсолютно все. Я профессионал.

воскресенье, 27 марта 2011 г.

Как написать AJAX-приложение

Компьютерра-Онлайн
Это два года назад AJAX был в диковинку (да и самого слова AJAX тогда ещё не выдумали). Теперь веб-приложения, страницы которых обновлялись на лету, в порядке вещей.
Автор: Виталий Акулов | Раздел: Гид | Дата: 18 апреля 2006 года
Это два года назад AJAX был в диковинку (да и самого слова AJAX тогда ещё не выдумали). Теперь веб-приложения, страницы которых обновляются "на лету", в порядке вещей. Даже наоборот, без AJAX трудно и представить себе некоторые сервисы.
Как работали обычные веб-приложения? Как правило, на событие (клик по ссылке или нажатие на кнопку) браузер реагировал отправкой запроса серверу. Когда с сервера приходил ответ, всё содержимое страницы полностью обновлялось.
Одна из проблем состояла в том, что при обновлении содержимого страницы веб-приложение переходит в новое состояние. Из информации о предыдущем состоянии сохраняются только данные, переданные в запросе. Чем более точная информация о прежнем состоянии системы требуется, тем больше данных необходимо пересылать в запросе.
Другим недостатком является необходимость пересылать повторяющиеся массивы данных клиенту после каждого события. Например, если пользователь ошибся при заполнении формы, то вместо короткого сообщения об ошибке приходится снова загружать и форму, и всю введённую ранее информации.
Современные браузеры, поддерживающие стандарты W3C DOM, позволяют вывести веб-приложение на новый уровень.
Схема взаимодействия остается почти такой же. Вот только отправляет запрос и получает ответ с сервера теперь скрипт на стороне клиента, а вместо обновления всей страницы - обновляется только ее часть (вместо обновления могут предприниматься иные действия, например, отправляться следующий запрос).
Веб-приложение получается распределенным, и часть логики находится на стороне клиента, а часть - на стороне сервера. Такие приложения и называют термином "AJAX Applications" (аббревиатура расшифровывается как Asynchronous Javascript And Xml Applications).
Подробнее про AJAX можно прочитать здесь

Объект XMLHTTPRequest

Для асинхронных запросов от клиента к серверу на стороне браузера служит специальный объект под названием XMLHTTPRequest.
Перечислим методы и свойства объекта, которые будут использованы далее:
  • XMLHTTPRequest.open("method", "URL", async, "uname", "pswd") – создает запрос к серверу.
    • method – тип запроса, например, GET
    • URL – URL запроса, например http://localhost/file.xml
    • async – если True, то будет использоваться асинхронный запрос, то есть выполнение скрипта продолжится после отправки запроса. В противном случае скрипт будет ожидать ответа от сервера, заморозив UI.
  • uname, pswd – логин и пароль для простой веб-авторизации.
  • XMLHTTPRequest.send("content") – отправляет запрос на сервер. Значением content могут быть данные для POST-запроса или пустая строка.
  • XMLHTTPRequest.onreadystatechange – обработчик событий срабатывающий на каждое изменение состояния объекта. Состояния объекта могут быть следующими:
    • 0 - до того как запрос отправлен (uninitialized)
    • 1 - объект инициализирован (loading)
    • 2 - получен ответ от сервера (loaded)
    • 3 - соединение с сервером активно (interactive)
    • 4 - объект завершил работу (complete)
  • XMLHTTPRequest.responseText – возвращает полученные от сервера данные в виде строки.
  • XMLHTTPRequest.responseXML – если ответ сервера пришел в виде правильного XML, возвращает XML DOM объект.
  • XMLHTTPRequest.status – возвращает статус HTTP-ответа в виде числа. Например, 404 если запрашиваемая страница не была найдена на сервере.
Рассмотрим применение объекта на примере простого AJAX-приложения.

Поле SELECT с поиском

Предположим у нас есть таблица, в которой порядка миллиона записей. Пользователю необходимо выбрать всего одну запись из таблицы (реализация отношения "один ко многим"). Выбор пользователя является всего лишь одним из этапов заполнения большой веб-формы.
Естественно, для того, чтобы пользователь мог выбрать нужную запись из миллиона, нужны какие-то средства поиска этой самой записи. Например, простой текстовый поиск по наименованию.
В традиционном веб-приложении для этой цели пришлось бы использовать отдельную страницу и сохранять остальные данные формы в сессии пользователя, либо разбивать процесс заполнения формы на несколько этапов. В AJAX-приложении дополнительная страница не нужна.
Выбор записи будет реализован с помощью двух элементов веб-формы. Первый элемент - это текстовое поле, где пользователь вводит ключевое слово. Оно отсылается на сервер, а тот возвращает только те строки из таблицы, которые удовлетворяют условию поиска. Ответ сервера (в виде списка) помещается в поле SELECT, в котором пользователь и сделает окончательный выбор. Таким образом, при отправке всей формы на сервер попадет выбранное в поле SELECT значение в виде ID записи из большой таблицы.
В HTML выглядеть это может так:
<input type="text"
   onkeyup="lookup(this.value, 'id_select', 
   'http://localhost/cgi-bin/xmlhttp.cgi')" />
<select id="id_select" name="id_select">
<option selected="selected" value=""></option>
</select>


На любое событие KeyUp (отжатие кнопки) в текстовом поле вызывается функция lookup ('текст', 'id-selecta', 'url')

function lookup(text, select_id, url) {
        // Получаем объект XMLHTTPRequest
        if(!this.http){
            this.http = get_http();
            this.working = false;
        }
        // Запрос
        if (!this.working && this.http) {
            var http = this.http;
            // Если в текстовом поле менее трёх
            // символов – не делаем ничего
            if (text.length <3 ) return;
  //добавляем закодированный текст
                //в URL запроса
            url = url + "?text="+encodeURIComponent(text);
      //создаём запрос
            this.http.open("GET", url, true);
            //прикрепляем к запросу функцию-обработчик
            //событий
            this.http.onreadystatechange = function() {
 // 4 – данные готовы для обработки
                if (http.readyState == 4) {
                    fill(select_id, http.responseText);
                    this.working = false;
                  }else{
                     // данные в процессе получения, 
                     // можно повеселить пользователя
                     //сообщениями 
                     // ЖДИТЕ ОТВЕТА
                  }
            }
            this.working = true;
            this.http.send(null);
        }
        if(!this.http){
              alert('Ошибка при создании XMLHTTP объекта!')
        }
    }

Как видно, в начале мы получаем XMLHTTP-объект с помощью функции get_http(). Затем поисковый текст кодируется в стиле URL и формируется GET-запрос к серверу. URL запроса в данном случае будет выглядеть приблизительно так: http://localhost/cgi-bin/xmlhttp.cgi?text=...
Скрипт на сервере, получив значение text, делает поиск в таблице и отсылает результат клиенту. В обработчике событий объекта XMLHTTP, когда данные от сервера получены и готовы к использованию, вызывается функция fill('select_id', 'data'), которая заполнит список SELECT полученными данными.
Функция get_http() – это кросс-браузерная реализация получения объекта XMLHTTP (в каждом браузере он получается по-своему). Её реализацию с комментариями вы можете легко найти в интернете, это, так сказать, пример из учебника.
function get_http(){
    var xmlhttp;
    /*@cc_on
    @if (@_jscript_version >= 5)
        try {
            xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
        } catch (e) {
            try {
                xmlhttp = new 
                ActiveXObject("Microsoft.XMLHTTP");
            } catch (E) {
                xmlhttp = false;
            }
        }
    @else
        xmlhttp = false;
    @end @*/
    if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
        try {
            xmlhttp = new XMLHttpRequest();
        } catch (e) {
            xmlhttp = false;
        }
    }
    return xmlhttp;
}
Функция fill() получает на вход значение параметра ID списка SELECT, который необходимо заполнить, и сами данные, полученные с сервера.

Для простоты предположим, что данные с сервера мы получаем в виде таблицы, поля которой разделены символом табуляции '\t', а строки - символом переноса строки '\n':
id1 \t name1 \n
id2 \t name2 \n
...
На основании содержимого этой таблицы мы будем заполнять поле SELECT элементами OPTION.

function fill (select_id, data){
    // поле SELECT в переменную в виде объекта
    var select = document.getElementById(select_id);
    // очищаем SELECT
    select.options.length = 0;
    // если данных нет – не делаем больше ничего
    if(data.length == 0) return;
    // в массиве arr – строки полученной таблицы
    var arr = data.split('\n');
    // для каждой строки
    for(var i in arr){
        // в массиве val – поля полученной таблицы
        val = arr[i].split('\t');
        // добавляем новый объект OPTION к нашему SELECT
        select.options[select.options.length]=
        new Option(val[1], val[0], false, false);
    }
}

Готово. Теперь для любой веб-формы приложения мы можем реализовать подобный выбор значения из многомиллионного списка, который для пользователя будет выглядеть как считанные нажатия клавиш. В локальной сети выбор происходит практически мгновенно. В случае нестабильного или низкоскоростного соединения с сервером, необходимо также оповещать пользователя о том, что загрузка данных с сервера еще не завершена. Полезно предусмотреть и средства для реакции на обрыв соединения.