Библиотека Prototype – ваш путь в Web 2.0::Журнал СА 8.2007
www.samag.ru
     
Поиск   
              
 www.samag.ru    Web  0 товаров , сумма 0 руб.
E-mail
Пароль  
 Запомнить меня
Регистрация | Забыли пароль?
Журнал "Системный администратор"
Журнал «БИТ»
Наука и технологии
Подписка
Где купить
Авторам
Рекламодателям
Магазин
Архив номеров
Вакансии
Контакты
   

  Опросы

Какие курсы вы бы выбрали для себя?  

Очные
Онлайновые
Платные
Бесплатные
Я и так все знаю

 Читать далее...

1001 и 1 книга  
20.12.2019г.
Просмотров: 5400
Комментарии: 0
Dr.Web: всё под контролем

 Читать далее...

04.12.2019г.
Просмотров: 6594
Комментарии: 0
Особенности сертификаций по этичному хакингу

 Читать далее...

28.05.2019г.
Просмотров: 7877
Комментарии: 2
Анализ вредоносных программ

 Читать далее...

28.05.2019г.
Просмотров: 8169
Комментарии: 1
Микросервисы и контейнеры Docker

 Читать далее...

28.05.2019г.
Просмотров: 7165
Комментарии: 0
Django 2 в примерах

 Читать далее...

Друзья сайта  

Форум системных администраторов  

sysadmins.ru

 Библиотека Prototype – ваш путь в Web 2.0

Архив номеров / 2007 / Выпуск №8 (57) / Библиотека Prototype – ваш путь в Web 2.0

Рубрика: Программирование /  Веб-программирование

Кирилл Сухов

Библиотека Prototype – ваш путь в Web 2.0

Часть 2: практика

Библиотека Prototype предоставляет огромные возможности веб-разработчику. Описывать все её классы и методы довольно длительное занятие, лучше попробовать освоить их на практике.

В первой части статьи (см. №7 за 2007 г.) я провел краткий обзор основных функций библиотеки Prototype, к сожалению, недостаточно полный. Во второй части я уже собирался наверстать упущенное, но первые же читатели обратили внимание на тот факт, что в статье не было приведено ни одного внятного примера работы библиотеки. Так что сейчас мы будем рассматривать конкретную программу, созданную на её основе.

Для иллюстрации я выбрал Window Prototype – библиотеку, уже упомянутую в первой части. Конечно, можно было попробовать написать собственное приложение, но боюсь, что его объём значительно увеличит статью. В данном случае я могу не приводить полные варианты кода – читатель всегда может ознакомиться с первоисточником. Те функции Prototype, которые остались за рамками обзора, будут освещаться по мере развития событий.

Итак, наше приложение должно создавать окна, причём окна полноценные – со всеми элементами управления, с заданным контентом, которым может служить как произвольный URL, так и какое-либо сгенерированное сообщение. Кроме того, мы будем писать библиотеку, с понятным и расширяемым интерфейсом (какой и является Window Prototype).

Теперь начнём

Сначала главная HTML-страница нашего Prototype-приложения:

<html>

<head>

<title>Окошки</title>

<script type="text/javascript" src="/javascripts/prototype.js"> </script>

<script type="text/javascript" src="/javascripts/win.js"> </script>

<link href="/themes/default.css" rel="stylesheet" type="text/css">

<link href="/themes/alphacube.css" rel="stylesheet" type="text/css">

</head>

<body>

<script type="text/javascript">

function open_window(id){

win1 = new Window(id, {className: "alphacube", title: "Window1", width:200, height:150, top:200, left:100});

}

</script>

<center><a href="#" onclick="win1.show();">open window</a></center>

</body>

</html>

Сначала подключается JavaScript-файл с самой библиотеки Prototype (prototype.js), затем файл с нашим сценарием, который ещё предстоит написать. При клике по ссылке «open window» будет вызван сценарий, открывающий желаемое окно. Он создаёт объект Window со свойствами, заложенными в аргументах нового объекта. Первый из них это идентификатор создаваемого объекта, второй – записанный в формате JSON массив его свойств.

Подключаемые файлы каскадных таблиц стилей (default.css и spread.css) были взяты также из Window Prototype и имеют смысл только для наглядности, визуализации создаваемых объектов. Их код я здесь приводить не буду. Теперь осталась сущая ерунда – создать класс, на основе которого создаётся объект, и описать его поведение. Вперёд, препарируем библиотеку и создаём сценарий в файле win.js.

Начнём с создания нашего объекта:

var Window = Class.create();

Тут необходимы пояснения. Объект Class используется для объявления пользовательских классов библиотеки. По аналогии с Java он имеет метод initailize(), используемый как конструктор, cамо объявление класса осуществляется методом create(), также знакомым по высокоуровневым ООП языкам программирования.

Теперь определим реализацию класса:

Window.prototype = {initialize: function() {

    var optionIndex = 0;

    if (arguments.length > 0) {

      if (typeof arguments[0] == "string" ) {

        id = arguments[0];

        optionIndex = 1;

      }

      else

        id = arguments[0] ? arguments[0].id : null;

    }

...............

Тут, мне кажется, всё должно быть понятно – мы просто оставляем возможность опустить первый аргумент (идентификатор). Это понадобится для того, чтобы создавать произвольное количество объектов. Впрочем, без id всё равно не обойтись, и он будет сгенерирован:

if (!id)

 id = "window_" + new Date().getTime();

Далее перечисляем желаемые свойства объекта:

this.options = Object.extend({

className:         "dialog",

blurClassName:     null,

minWidth:          100,

minHeight:         20,

resizable:         true,

closable:          true,

minimizable:       true,

maximizable:       true,

draggable:         true,

userData:          null,

showEffect:        (Window.hasEffectLib ? Effect.Appear : Element.show),

hideEffect:        (Window.hasEffectLib ? Effect.Fade : Element.hide),

showEffectOptions: {},

      hideEffectOptions: {},

      effectOptions:     null,

      parent:            document.body,

      title:             "&nbsp;",

      url:               null,

      onload:            Prototype.emptyFunction,

      width:             200,

      height:            300,

      opacity:           1,

      recenterAuto:      true,

      wiredDrag:         false,

      closeCallback:     null,

      destroyOnClose:    false,

      gridX:             1,

      gridY:             1     

    }, arguments[optionIndex] || {});

Мы сразу задали все свойства окна, некоторые из них в нашей конкретной задаче точно не понадобятся, но давайте не забывать, мы пишем не приложение, а библиотеку. Надо заметить, что JavaScript, довольно демократичный язык, допускающий многие «вольности». В числе таковых неопределённое количество аргументов функции. Использовав объект options, мы создали массив свойств нашего окна, которые используются, если явно не указаны при его иницмализации.

Далее задаём область, в которой будет создаваться окно (по умолчанию это document.body):

    if (this.options.parent != document.body) 

     this.options.parent = $(this.options.parent);

Теперь создаём окно:

    this.element = this._createWindow(id);      

     this.element.win = this;

Далее в конструкторе стоит задать некоторые методы, но это отложим, пока установим только размеры и заголовок:

    if (this.width && this.height)

     this.setSize(this.options.width, this.options.height);

     this.setTitle(this.options.title)

На этом конструктор пока закончим:

    Windows.register(this);      

 },

Смысл последней строчки будет ясен позже, объект Windows мы ещё опишем.

Теперь очередь метода _createWindow(). Тут всё довольно просто. Создаётся объект, представляющий собой HTML-код (три таблицы), каждому элементу которого присвоено стандартное имя класса (производное от базового):

    _createWindow: function(id) {

    var className = this.options.className;

    var win = document.createElement("div");

    win.setAttribute('id', id);

    win.className = "dialog";

    var content;

    if (this.options.url)

      content= "<iframe frameborder=\"0\" name=\"" + id + "_content\"  id=\"" + id + "_content\" src=\"" + this.options.url + "\"> </iframe>";

    else

      content ="<div id=\"" + id + "_content\" class=\"" +className + "_content\"> </div>";

    var closeDiv = this.options.closable ? "<div class='"+ className +"_close' id='"+ id +"_close' onclick='Windows.close(\""+ id +"\", event)'> </div>" : "";

    var minDiv = this.options.minimizable ? "<div class='"+ className + "_minimize' id='"+ id +"_minimize' onclick='Windows.minimize(\""+ id +"\", event)'> </div>" : "";

    var maxDiv = this.options.maximizable ? "<div class='"+ className + "_maximize' id='"+ id +"_maximize' onclick='Windows.maximize(\""+ id +"\", event)'> </div>" : "";

    var seAttributes = this.options.resizable ? "class='" + className + "_sizer' id='" + id + "_sizer'" : "class='"  + className + "_se'";

    var blank = "../themes/default/blank.gif";

    win.innerHTML = closeDiv + minDiv + maxDiv + "\

      <table id='"+ id +"_row1' class=\"top table_window\">\

        <tr>\

          <td class='"+ className +"_nw'></td>\

          <td class='"+ className +"_n'><div id='"+ id +"_top' class='"+ className +"_title title_window'>"+ this.options.title +"</div></td>\

          <td class='"+ className +"_ne'></td>\

        </tr>\

      </table>\

      <table id='"+ id +"_row2' class=\"mid table_window\">\

        <tr>\

          <td class='"+ className +"_w'></td>\

            <td id='"+ id +"_table_content' class='"+ className +"_content' valign='top'>" + content + "</td>\

          <td class='"+ className +"_e'></td>\

        </tr>\

      </table>\

        <table id='"+ id +"_row3' class=\"bot table_window\">\

        <tr>\

          <td class='"+ className +"_sw'></td>\

            <td class='"+ className +"_s'><div id='"+ id +"_bottom' class='status_bar'><span style='float:left; width:1px; height:1px'></span></div></td>\

            <td " + seAttributes + "></td>\

        </tr>\

      </table>\

    ";

    Event.observe($(id + "_content"), "load", this.options.onload);

    return win;

  },

Структура окна, конечно, может быть другой, но я хочу обратить внимание на ключевые моменты:

  • все элементы окна помещены в один (родительский) элемент div, снабженный уникальным идентификатором;
  • ко всем элементам управления (стандартные «свернуть», «развернуть», «закрыть», «перетащить») добавлены обработчики событий (да, да их ещё предстоит создать);
  • окно может отображать, как заданный URL, так и сгенерированный контент.

Результат мы можем уже наблюдать (см. рис. 1), но он пока не очень впечатляет. В самом деле, зачем было писать столько строк кода, когда то же самое можно получить простейшими DHTML-приёмами? На самом деле все нормально. Кода придется написать ещё втрое больше, но в прототипе мы сразу же заложили возможности для любых манипуляций с объектом-окном. Правда, окно мы не собираемся создавать в одном экземпляре (а какой смысл?). Для работы с множеством окон мы создадим ещё один объект, который разработчики Windows Prototipe назвали не слишком оригинально.

Рисунок 1. Окошко появилось

Рисунок 1. Окошко появилось

Итак, объект Windows:

  var Windows = {

  windows: [],

  observers: [],

  focusedWindow: null,

  maxZIndex: 0,

  overlayShowEffectOptions: {duration: 0.5},

  overlayHideEffectOptions: {duration: 0.5},

  addObserver: function(observer) {

    this.removeObserver(observer);

    this.observers.push(observer);

  },

  removeObserver: function(observer) { 

    this.observers = this.observers.reject( function(o) { return o==observer });

  },

  ...

}

Задача данного объекта хранить сведения о коллекции объектов Window и переадресовывать события к обработчику для заданного окна. Как вполне ясно из кода, в массиве windows сохраняются все созданные объекты-окошки, в массиве observers – заданные обработчики.

Методы addObserver и removeObserver оперируют с обработчиками событий, добавляя или убирая их у объектов.

Сюда же добавляем два необходимых для манипуляций с объектами метода:

notify: function(eventName, win) { 

    this.observers.each( function(o) {if(o[eventName]) o[eventName](eventName, win);});

  },

getWindow: function(id) {

    return this.windows.detect(function(d) { return d.getId() ==id });

  },notify: function(eventName, win) { 

    this.observers.each( function(o) {if(o[eventName]) o[eventName](eventName, win);});

  },

  getWindow: function(id) {

    return this.windows.detect(function(d) { return d.getId() ==id });

  },

Последний метод, как вы помните, вызывается конструктором при создании окна, реализация getId() и других вспомогательных манипуляций в данном случае принципиально не важна.

Осталось задать поведение создаваемых объектов, прописав все необходимые методы в объекте Windows и их реализацию в объекте Window. Естественно, мы это не будем делать в полном объёме (тем более что разработчики Windows Prototipe давно обо всём позаботились), но кое-что попробуем.

Оживляем объект

Прежде всего нужно связать составляющие созданного объекта с событиями (наведением мыши, перетаскиванием, кликами). Опять-таки это будет несколько сложнее, чем в традиционном DHTML, но зато и возможностей будет больше. Сначала добавим в конструктор следующие строки:

this.eventMouseDown = this._initDrag.bindAsEventListener(this);

this.eventMouseUp = this._endDrag.bindAsEventListener(this);

this.eventMouseMove = this._updateDrag.bindAsEventListener(this);

this.eventOnLoad = this._getWindowBorderSize.bindAsEventListener(this);

this.eventMouseDownContent = this.toFront.bindAsEventListener(this);

this.eventResize = this._recenter.bindAsEventListener(this);

this.topbar = $(this.element.id + "_top");

this.bottombar = $(this.element.id + "_bottom");

this.content = $(this.element.id + "_content");

Event.observe(this.topbar, "mousedown", this.eventMouseDown);

Event.observe(this.bottombar, "mousedown", this.eventMouseDown);

Event.observe(this.content, "mousedown", this.eventMouseDownContent);

Event.observe(window, "load", this.eventOnLoad);

Event.observe(window, "resize", this.eventResize);

Event.observe(window, "scroll", this.eventResize);

Event.observe(this.options.parent, "scroll", this.eventResize);

Всё, теперь можно описывать поведение объектов. Для примера возьмём «сворачивание» окна при нажатии мышкой на соответствующую кнопку.

Для достижения этого эффекта сначала пропишем соответствующий метод объекта Windows:

minimize: function(id, event) {

    var win = this.getWindow(id)

    if (win && win.visible)

      win.minimize();

    Event.stop(event);

  },

Затем его реализацию в объекте Window:

minimize: function() {

    if (this.resizing)

      return;

    var r2 = $(this.getId() + "_row2");

    if (!this.minimized) {

      this.minimized = true;

      var dh = r2.getDimensions().height;

      this.r2Height = dh;

      var h  = this.element.getHeight() - dh;

 

      if (! this.useTop) {

        var bottom = parseFloat(this.element.getStyle('bottom'));

        this.element.setStyle({bottom: (bottom + dh) + 'px'});

      }

    }

    else {      

      this.minimized = false;

      var dh = this.r2Height;

      this.r2Height = null;

      if (this.useLeft && this.useTop && Window.hasEffectLib && Effect.ResizeWindow) {

        new Effect.ResizeWindow(this, null, null, null, this.height + dh, {duration: Window.resizeEffectDuration});

      }

      else {

        var h  = this.element.getHeight() + dh;

        this.height += dh;

        this.element.setStyle({height: h + "px"})

        r2.show();

      }

      if (! this.useTop) {

        var bottom = parseFloat(this.element.getStyle('bottom'));

        this.element.setStyle({bottom: (bottom - dh) + 'px'});

      }

      this.toFront();

    }

  },

Я понимаю, что выглядит всё не очень прозрачно, но тем не менее описывать методы вроде show(), getHeight() или toFront() не буду (их реализацию читатель всегда может посмотреть в исходниках). Объект Effect и метод ResizeWindow являются специфичными для Windows prototype, я бы с удовольствием разобрал их работу, но объём статьи при этом возрастёт в несколько раз. Чтобы воспользоваться нашим объектом, нужно только всего ничего – прописать все остальные методы, задающие его поведение.

Работаем с библиотекой

Теперь представим, что мы прописали все методы и свойства и можем пожинать плоды нудной работы (пусть и не нашей, но всё же). Сначала чуть модифицируем вызов нашего окошка:

function open_window(id){

win1 = new Window(id, {className: "alphacube", title: "Window1", width:200, height:150, top:200, left:100});

win1.getContent().innerHTML = "<h1>Preved</h1>";

win1.show();

}

Теперь окно живёт – его можно свернуть, развернуть на весь экран, перетащить, наконец, закрыть. Все свойства окна можно установить посредством вызова методов, что и делаем, наполняя окно чрезвычайно полезным контентом (см. рис. 2).

Рисунок 2. Hellow Word

Рисунок 2. Hellow Word

Теперь создадим окно на основе другого класса (см. рис. 3):

function next_window(){

win2 = new Window( {className: "spread", title: "Window2",opacity:0.5, width:200, height:150, top:200, left:100});

win2.getContent().innerHTML = "<h1>Preved</h1>";

win2.show();

}

Рисунок 3. Меняем стиль, прозрачность

Рисунок 3. Меняем стиль, прозрачность

Поскольку мы не задали жёстко идентификатор объекта, создать таких окон можно сколько угодно и сколько угодно издеваться над ними (см. рис. 4, 5).

Рисунок 4. Размножаемся

Рисунок 4. Размножаемся

Рисунок 5. Трансформация окон

Рисунок 5. Трансформация окон

В конце концов создадим самое правильное окно:

win2 = new Window( {className: "spread", title: "samag",opacity:0.5, width:200, height:150, top:200, left:100, url=”http://samag.ru”});

Результат представлен на рис. 6.

Рисунок 6. Загрузка правильного URL

Рисунок 6. Загрузка правильного URL

Документация

Несмотря на то, что библиотека Prototype создана и активно используется довольно давно, до самого последнего времени она не имела ни официальной документации, ни даже полноценной домашней страницы. К счастью, сейчас эти проблемы решены – на сайте библиотеки http://www.prototypejs.org/api доступно описание классов и методов.

Джонатан Снук (Jonathan Snook, http://www.snook.ca) проделал колоссальную работу, собрав элементы библиотеки в единую диаграмму, отражающую связь и иерархию объектов Prototype. Рекомендую использовать её для изучения структуры библиотеки (см. рис. 7).

Рисунок 7. Связь и иерархия объектов библиотеки Prototype

Рисунок 7. Связь и иерархия объектов библиотеки Prototype

Приложения на JavaScript?

В первой части этого опуса я упомянул о нескольких библиотеках, созданных на основе prototype. Они впечатляют своими возможностями, но всё же это библиотеки – инструменты разработчика, до которых нет дела конечному пользователю.

В качестве иллюстрации готового JavaScript-приложения я собирался привести несколько примеров, но в процессе подготовки наткнулся на один, самый эффектный на настоящее время, которым решил и ограничиться. Знакомьтесь – Cumulate Draw (см. рис. 8).

Рисунок 8. Работа сервиса Cumulate Draw

Рисунок 8. Работа сервиса Cumulate Draw

Честно говоря, будь я разработчиком этого сервиса, я непременно брал бы деньги за его использование. Это вполне достойная замена среды MSVisio прямо в окне браузера. Конечно, сказать, что данное приложение целиком сделано на основе Prototype, нельзя, скорее всего, оно создано с использованием этой библиотеки. Тем не менее серверная часть составляет в ней явно не так много, основная задача по реализации функционала приходится на интерфейс, в чём можно убедиться, посмотрев исходники на JavaScript, которые вполне доступны для изучения.

Я думаю, наличие таких приложений не оставляет сомнения в возможностях и библиотеки Pritotype.

  1. Библиотека Window Prototype – http://prototype-window.xilinus.com/index.html.
  2. Сервис Cumulate Draw – http://www.cumulatelabs.com/draw/draw.html?release=0.4.7.

Комментарии отсутствуют

Добавить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

               Copyright © Системный администратор

Яндекс.Метрика
Tel.: (499) 277-12-41
Fax: (499) 277-12-45
E-mail: sa@samag.ru