Фреймворк Cappuccino – уникальная технология, позволяющая создавать веб-приложения десктопного качества. Он абстрагирует DOM и вместо него предоставляет Cocoa-подобный API. Вместо того, чтобы возиться с CSS-версткой и кроссбраузерными проблемами, вы используете интерфейсы, специально созданные для разработки приложений, а не статических страниц, интерфейсы, взятые с платформ Mac OS X и iOS.
Я заметил, что на русском языке почти нет обучающих материалов о Cappuccino, и решил восполнить пробел. Этот очерк рассчитан на то, чтобы прочитав его, можно было сразу приступать к разработке своего первого Cappuccino приложения. Я познакомился с фреймворком, когда искал средство для реализации онлайновой среды разработки для своего проекта Akshell. Мне нужно было сделать полнофункциональную IDE, работающую в окне браузера, и Cappuccino отлично справился с поставленной задачей.
В 2008 году Франциско Толмаски, Том Робинсон и Росс Баучер (Франциско и Росс – бывшие сотрудники Apple) создали компанию 280 North и выпустили 280 Slides, веб-приложение для подготовки презентаций. Оно и сейчас впечатляет своим look and feel’ом, а тогда это было просто что-то нереальное. Причем 280 Slides не был самостоятельным продуктом, это была лишь демонстрация возможностей нового веб-фреймворка, Cappuccino.
4-го сентября того же года Cappuccino был опубликован под лицензией LGPL и тут же стал хитом на GitHub. Вокруг фреймворка образовалось сообщество программистов, использующих его в своих продуктах. С тех пор это сообщество только увеличивается и принимает все более активное участие в разработке Cappuccino.
В 2009 году 280 North анонсировала новый продукт, Atlas, и он также оказался прорывной технологией. Atlas – это аналог Interface Builder для веб-приложений, в нем вы можете рисовать интерфейсы мышкой, перетаскивая нужные компоненты из библиотеки. Пара минут работы дает более впечатляющий результат, чем день мучений с CSS. В отличие от Cappuccino, Atlas – закрытая платная программа, для скачивания бета-версии нужно заплатить $20. Этот очерк посвящен Cappuccino, поэтому я не буду описывать Atlas более подробно. Скажу только, что он действительно экономит кучу времени при создании интерфейсов, позволяет сосредоточиться на юзабилити, а не на борьбе с различными версиями браузеров.
Cappuccino и Atlas активно развивались, когда летом 2010 года Motorola купила 280 North со всеми ее активами, потратив на это, по слухам, 20 миллионов долларов. Официальная цель покупки – развитие технологий разработки на платформе Android. Многие злые языки начали поговаривать о смерти Cappuccino, однако с тех пор прошло уже полгода, фреймворк активно разрабатывается, а 23-го февраля состоялся релиз версии 0.9.
Итак, Cappuccino является одной из самых продвинутых технологий создания веб-приложений, его развитие обеспечивает большая корпорация. Изучить или хотя бы ознакомиться с фреймворком будет полезно каждому веб-программисту, к чему и приступим.
Главная особенность Cappuccino – это созданный специально для него язык Objective-J. Он является такой же надстройкой над JavaScript, как Objective-C – над C. Для меня, как и для многих, вначале было совершенно неочевидно, зачем делать объектно-ориентированную надстройку над языком, который сам по себе является объектно-ориентированным. Однако в этом есть большой смысл.
Во-первых, благодаря Objective-J Cappuccino полностью повторяет Cocoa API. Это не фантазия на тему и даже не творческая переработка, это именно повторение, вплоть до сигнатур функций. За счет этого программисты, работавшие на Mac OS X и iOS могут безболезненно перейти на веб-разработку. Удобство всех API проверено долговременными использованием на десктопе, начиная с ОС NeXTSTEP. И наконец, при использовании фреймворка можно успешно пользоваться документацией Cocoa, которая значительно превосходит по качеству и проработанности Doxygen документацию Cappuccino.
Во-вторых, отсутствие гибкости иногда является плюсом. Большинство программистов посчитает, что я сейчас высказал большую ересь, поэтому сразу начну оправдываться. Дизайнеры знают, что введение сеток и других искусственных ограничений может улучшить дизайн. Objective-J интерфейсы Cappuccino на первый взгляд кажутся неповоротливыми и допотопными по сравнению с тем, что можно было бы сделать на JavaScript. Вместо парадигмы Target-Action зачастую можно было бы использовать замыкания, вместо явных геттеров и сеттеров –
Поработав с Cappuccino, я понял достоинства такого подхода, и они являются прямыми следствиями недостатков. Более жесткая объектная модель позволяет четче структурировать код, не потонуть в каше вложенных друг в друга функций. Длинные содержательные имена позволяют справляться с иерархиями классов Cocoa, в который один класс обычно имеет многие десятки методов. Вездесущие геттеры и сеттеры позволяют реализовать Key-Value Coding, Key-Value Observing и Key-Value Binding, техники переворачивающие представление о создании сложных интерфейсов пользователя (к сожалению, в этом очерке не хватит места для их описания).
Я начал пользоваться Cappuccino потому, что меня привлек внешний вид приложений, созданных с его помощью. Теперь я понимаю, что настоящая сила заключается в его API, которые сначала казались мне ужасными. Поэтому я призываю вас осознать философию фреймворка прежде, чем сделать окончательное суждение о нем.
Приступим к обзору Objective-J. Главное, что он вносит в JavaScript – это классы. Они определяются с помощью ключевого слова
Между
Взаимодействие между Objective-J объектами происходит с помощью пересылки сообщений, впервые эта техника появилась в языке Smalltalk. Синтаксически это выглядит так:
Например, вызов геттеров и сеттеров объекта класса
Методы объявляются после переменных-членов, до ключевого слова
Создание экземпляра класса в Objective-J происходит в два этапа: сначала метод
Названия конструкторов в Objective-J принято начинать со слова
Ради единства стиля Objective-J определяет три переменных, взятые из Objective-C, это
Как и многие читающие это очерк, я никогда не использовал Objective-C, поэтому для меня было совершенно неочевидно, почему класс определяется ключевым словом
Пора установить Cappuccino. Если вы используете Windows, сначала придется установить Cygwin (неприятное обстоятельство). Все остальные и те, кто все-таки решился на предыдущий шаг, просто скачивают Cappuccino Starter Package и запускают в нем скрипт bootstrap.sh.
После установки в системе должна появиться утилита capp. В качестве примера мы будем создавать (сюрприз!) приложение для поиска в твиттере. Сгенерируем его:
В появившейся папке лежит куча файлов, нас интересуют index-debug.html и AppController.j (*.j – стандартное расширение файлов с Objective-J кодом).
Откроем index-debug.html. Строгая политика безопасности Google Chrome не позволяет Cappuccino загружать нужные ей файлы при работе с протоколом file://, требуется поднимать веб-сервер, во всех же остальных браузерах мы увидим работающее приложение Hello World! Cappuccino компилирует Objective-J код в JavaScript на лету, прямо в браузере, поэтому при разработке можно просто обновлять страницу и не заботиться о пересборке. «Боевую» версию приложения можно скомпилировать заранее, чтобы ускорить загрузку.
Приступим к разработке. В файле AppController.j определен класс
Перед определением класса импортируем
Ключевое слово
Метод
Для инициализации приложения мы создадим окно и текстовое поле:
JavaScript функция
Раньше я был ярым сторонником ограничения длины строки 80-ю символами. Применение этого правила в Cappuccino превращает код в нечитаемую кашу, поэтому большинство разработчиков не ограничивают себя при работе как с Objective-J, так и с Objective-C. Единственным исключением являются гайдлайны Google, но на то он и Google.
Теперь сделаем текстовое поле редактируемым и добавим ему рамки:
Установим Target и Action, т.е. объект и метод, который должен у него вызваться при нажатии кнопки Enter в поле:
Добавляем поле к окну и фокусируем его:
И наконец, показываем окно:
С инициализацией приложения покончено, теперь определим его реакцию на нажатие Enter, т.е. метод
Мы извлекаем значение текстового поля и, если оно не пусто, создаем экземпляр
Создадим файл SearchWindowController.j и отнаследуем наш класс от
Определим конструктор, создающий окно и делающий запрос к твиттеру:
И наконец, напишем обработчик ответа от твиттера:
Для отображения твитов он использует библиотечные классы
Наверное, вы заметили в коде несколько вызовов метода
Осталось разобраться с классом
Метод
Теперь снова откроем index-debug.html и поищем несколько слов. Результат должен получиться примерно такой:

Вы можете просмотреть полный код примера и протестировать его.
Конечно, в этом очерке невозможно было описать и десятую долю функционала Cappuccino. Однако я уверен, что сейчас вы уже готовы начать разработку своих приложений, к чему вас и призываю. Для углубления знаний используйте документацию Cappuccino, также очень рекомендую набор скринкастов по теме. Надеюсь, что титанический труд Франциско, Тома и Росса, а также мой скромный очерк помогут кому-то из вас создать новое интересное веб-приложение.
Я заметил, что на русском языке почти нет обучающих материалов о Cappuccino, и решил восполнить пробел. Этот очерк рассчитан на то, чтобы прочитав его, можно было сразу приступать к разработке своего первого Cappuccino приложения. Я познакомился с фреймворком, когда искал средство для реализации онлайновой среды разработки для своего проекта Akshell. Мне нужно было сделать полнофункциональную IDE, работающую в окне браузера, и Cappuccino отлично справился с поставленной задачей.
История
В 2008 году Франциско Толмаски, Том Робинсон и Росс Баучер (Франциско и Росс – бывшие сотрудники Apple) создали компанию 280 North и выпустили 280 Slides, веб-приложение для подготовки презентаций. Оно и сейчас впечатляет своим look and feel’ом, а тогда это было просто что-то нереальное. Причем 280 Slides не был самостоятельным продуктом, это была лишь демонстрация возможностей нового веб-фреймворка, Cappuccino.
4-го сентября того же года Cappuccino был опубликован под лицензией LGPL и тут же стал хитом на GitHub. Вокруг фреймворка образовалось сообщество программистов, использующих его в своих продуктах. С тех пор это сообщество только увеличивается и принимает все более активное участие в разработке Cappuccino.
В 2009 году 280 North анонсировала новый продукт, Atlas, и он также оказался прорывной технологией. Atlas – это аналог Interface Builder для веб-приложений, в нем вы можете рисовать интерфейсы мышкой, перетаскивая нужные компоненты из библиотеки. Пара минут работы дает более впечатляющий результат, чем день мучений с CSS. В отличие от Cappuccino, Atlas – закрытая платная программа, для скачивания бета-версии нужно заплатить $20. Этот очерк посвящен Cappuccino, поэтому я не буду описывать Atlas более подробно. Скажу только, что он действительно экономит кучу времени при создании интерфейсов, позволяет сосредоточиться на юзабилити, а не на борьбе с различными версиями браузеров.
Cappuccino и Atlas активно развивались, когда летом 2010 года Motorola купила 280 North со всеми ее активами, потратив на это, по слухам, 20 миллионов долларов. Официальная цель покупки – развитие технологий разработки на платформе Android. Многие злые языки начали поговаривать о смерти Cappuccino, однако с тех пор прошло уже полгода, фреймворк активно разрабатывается, а 23-го февраля состоялся релиз версии 0.9.
Итак, Cappuccino является одной из самых продвинутых технологий создания веб-приложений, его развитие обеспечивает большая корпорация. Изучить или хотя бы ознакомиться с фреймворком будет полезно каждому веб-программисту, к чему и приступим.
Теория
Главная особенность Cappuccino – это созданный специально для него язык Objective-J. Он является такой же надстройкой над JavaScript, как Objective-C – над C. Для меня, как и для многих, вначале было совершенно неочевидно, зачем делать объектно-ориентированную надстройку над языком, который сам по себе является объектно-ориентированным. Однако в этом есть большой смысл.
Во-первых, благодаря Objective-J Cappuccino полностью повторяет Cocoa API. Это не фантазия на тему и даже не творческая переработка, это именно повторение, вплоть до сигнатур функций. За счет этого программисты, работавшие на Mac OS X и iOS могут безболезненно перейти на веб-разработку. Удобство всех API проверено долговременными использованием на десктопе, начиная с ОС NeXTSTEP. И наконец, при использовании фреймворка можно успешно пользоваться документацией Cocoa, которая значительно превосходит по качеству и проработанности Doxygen документацию Cappuccino.
Во-вторых, отсутствие гибкости иногда является плюсом. Большинство программистов посчитает, что я сейчас высказал большую ересь, поэтому сразу начну оправдываться. Дизайнеры знают, что введение сеток и других искусственных ограничений может улучшить дизайн. Objective-J интерфейсы Cappuccino на первый взгляд кажутся неповоротливыми и допотопными по сравнению с тем, что можно было бы сделать на JavaScript. Вместо парадигмы Target-Action зачастую можно было бы использовать замыкания, вместо явных геттеров и сеттеров –
__defineGetter__ и __defineSetter__, вместо квадратных скобок – привычные точки, в конце концов. И самое ужасное: Objective-J методы не являются объектами первого класса!Поработав с Cappuccino, я понял достоинства такого подхода, и они являются прямыми следствиями недостатков. Более жесткая объектная модель позволяет четче структурировать код, не потонуть в каше вложенных друг в друга функций. Длинные содержательные имена позволяют справляться с иерархиями классов Cocoa, в который один класс обычно имеет многие десятки методов. Вездесущие геттеры и сеттеры позволяют реализовать Key-Value Coding, Key-Value Observing и Key-Value Binding, техники переворачивающие представление о создании сложных интерфейсов пользователя (к сожалению, в этом очерке не хватит места для их описания).
Я начал пользоваться Cappuccino потому, что меня привлек внешний вид приложений, созданных с его помощью. Теперь я понимаю, что настоящая сила заключается в его API, которые сначала казались мне ужасными. Поэтому я призываю вас осознать философию фреймворка прежде, чем сделать окончательное суждение о нем.
Приступим к обзору Objective-J. Главное, что он вносит в JavaScript – это классы. Они определяются с помощью ключевого слова
@implementation:@implementation Person : CPObject
{
CPString name @accessors;
}
(id)initWithName:(CPString)aName
{
if (self = [super init])
name = aName;
return self;
}
(Person)personWithName:(CPString)aName
{
return [[Person alloc] initWithName:aName];
}
@endМежду
@implementation и @end находится описание класса. Класс Person наследуется от CPObject, вершины иерархии классов Cappuccino. Затем в фигурных скобках объявляются переменные-члены класса. name – строковая переменная-член, для которой автоматически генерируются геттер и сеттер (благодаря указанию @accessors). Правило именования аксессоров в Objective-J несколько нестандартно – это name и setName:. Этого формата стоит придерживаться, т.к. на него полагаются внутренние механизмы Cappuccino.Взаимодействие между Objective-J объектами происходит с помощью пересылки сообщений, впервые эта техника появилась в языке Smalltalk. Синтаксически это выглядит так:
[объект сообщение] [объект сообщениеСПараметром:значение] [объект сообщениеСПараметром1:значение1 параметром2:значение2] ...
Например, вызов геттеров и сеттеров объекта класса
Person выглядит так:var name = [aPerson name]; [aPerson setName:”Вася”];
Методы объявляются после переменных-членов, до ключевого слова
@end. Методы класса начинаются на плюс, методы экземпляра класса – на минус.Создание экземпляра класса в Objective-J происходит в два этапа: сначала метод
alloc этого класса создает неинициализированный объект, а затем конструктор (init-метод) инициализирует его. Часто определяют методы класса, упрощающие этот процесс, personWithName: – пример такого метода.Названия конструкторов в Objective-J принято начинать со слова
init. Каждый конструктор должен сначала позаботится о вызове конструктора суперкласса, затем выполнить инициализацию и вернуть self. Ошибка конструирования сигнализируется возвратом значения nil (да, во времена создания Objective-C исключения еще не вошли в моду, поэтому Cocoa и, как следствие, Cappuccino обходятся без них).Ради единства стиля Objective-J определяет три переменных, взятые из Objective-C, это
nil, YES и NO. Они идентичны null, true и false, соответственно. Cappuccino Coding Style Guidelines рекомендуют использовать именно их.Как и многие читающие это очерк, я никогда не использовал Objective-C, поэтому для меня было совершенно неочевидно, почему класс определяется ключевым словом
@implementation, а не, например, @class. Специально для таких же любопытных: Objective-C, как надстройка над C, использует файлы объявлений (*.h от headers) и файлы определений (*.m от messages), поэтому классы должны объявляться (@interface) и определяться (@implementation). В Objective-J объявления не нужны, поэтому используется только @implementation.Практика
Пора установить Cappuccino. Если вы используете Windows, сначала придется установить Cygwin (неприятное обстоятельство). Все остальные и те, кто все-таки решился на предыдущий шаг, просто скачивают Cappuccino Starter Package и запускают в нем скрипт bootstrap.sh.
После установки в системе должна появиться утилита capp. В качестве примера мы будем создавать (сюрприз!) приложение для поиска в твиттере. Сгенерируем его:
capp gen "Twitter Search"
В появившейся папке лежит куча файлов, нас интересуют index-debug.html и AppController.j (*.j – стандартное расширение файлов с Objective-J кодом).
Откроем index-debug.html. Строгая политика безопасности Google Chrome не позволяет Cappuccino загружать нужные ей файлы при работе с протоколом file://, требуется поднимать веб-сервер, во всех же остальных браузерах мы увидим работающее приложение Hello World! Cappuccino компилирует Objective-J код в JavaScript на лету, прямо в браузере, поэтому при разработке можно просто обновлять страницу и не заботиться о пересборке. «Боевую» версию приложения можно скомпилировать заранее, чтобы ускорить загрузку.
Приступим к разработке. В файле AppController.j определен класс
AppController, в Objective-J принято размещать код каждого класса в одноименном файле. Cappuccino создает один экземпляр AppController при запуске, он должен инициализировать приложение и управлять его дальнейшей работой.Перед определением класса импортируем
Foundation и AppKit, две основных части Cappuccino. Foundation содержит классы для обеспечения бизнес-логики, а AppKit – это библиотека классов пользовательского интерфейса. @import <Foundation/Foundation.j> @import <AppKit/AppKit.j>
Ключевое слово
@import подключает указанный файл. Как и в C, при указании пути в угловых скобках файл будет искаться в системных папах, при использовании двойных кавычек – в текущей папке.Метод
applicationDidFinishLaunching: вызывается у всех объектов непосредственно после запуска приложения. Содержательные имена методов Cappuccino делают код понятным даже для тех, кто не знаком с фреймворком, поэтому я буду давать пояснения только в неочевидных случаях. Cappuccino вряд ли сэкономит вам нажатия клавиш, так пусть хоть сэкономит их мне.Для инициализации приложения мы создадим окно и текстовое поле:
var window = [[CPWindow alloc] initWithContentRect:CGRectMake(100, 100, 250, 70)
styleMask:CPTitledWindowMask],
contentView = [window contentView],
textField = [[CPTextField alloc] initWithFrame:CGRectMake(25, 20, 200, 30)];JavaScript функция
CGRectMake(x, y, width, height) описывает прямоугольник, в котором будет располагаться элемент управления. Метод contentView возвращает представление внутренней области окна.Раньше я был ярым сторонником ограничения длины строки 80-ю символами. Применение этого правила в Cappuccino превращает код в нечитаемую кашу, поэтому большинство разработчиков не ограничивают себя при работе как с Objective-J, так и с Objective-C. Единственным исключением являются гайдлайны Google, но на то он и Google.
Теперь сделаем текстовое поле редактируемым и добавим ему рамки:
[textField setEditable:YES]; [textField setBezeled:YES];
Установим Target и Action, т.е. объект и метод, который должен у него вызваться при нажатии кнопки Enter в поле:
[textField setTarget:self]; [textField setAction:@selector(didSubmitTextField:)];
@selector используется, чтобы превратить метод в передаваемое значение.Добавляем поле к окну и фокусируем его:
[contentView addSubview:textField]; [window makeFirstResponder:textField];
И наконец, показываем окно:
[window center]; [window setTitle:"Twitter Search"]; [window orderFront:self];
С инициализацией приложения покончено, теперь определим его реакцию на нажатие Enter, т.е. метод
didSubmitTextField:- (void)didSubmitTextField:(CPTextField)textField
{
var searchString = [textField stringValue];
if (!searchString)
return;
[[[SearchWindowController alloc] initWithSearchString:searchString] showWindow:nil];
[textField setStringValue:""];
[[textField window] makeKeyAndOrderFront:nil];
}Мы извлекаем значение текстового поля и, если оно не пусто, создаем экземпляр
SearchWindowController, показываем его окно, опустошаем текстовое поле и выдвигаем его окно вперед. Дело за малым – разработать класс SearchWindowController.Создадим файл SearchWindowController.j и отнаследуем наш класс от
CPWindowController:@implementation SearchWindowController : CPWindowController
{
}
@endОпределим конструктор, создающий окно и делающий запрос к твиттеру:
- (id)initWithSearchString:(CPString)searchString
{
if (self = [super init]) {
var window = [[CPWindow alloc] initWithContentRect:CGRectMake(10, 30, 300, 400)
styleMask:CPTitledWindowMask | CPClosableWindowMask | CPResizableWindowMask];
[window setTitle:searchString];
[self setWindow:window];
var request = [CPURLRequest requestWithURL:"http://search.twitter.com/search.json?q=" + encodeURIComponent(searchString)];
[CPJSONPConnection sendRequest:request callback:"callback" delegate:self];
}
return self;
}И наконец, напишем обработчик ответа от твиттера:
- (void)connection:(CPJSONPConnection)connection didReceiveData:(Object)data
{
var contentView = [[self window] contentView];
if (data.results.length) {
var bounds = [contentView bounds],
collectionView = [[CPCollectionView alloc] initWithFrame:bounds];
[collectionView setAutoresizingMask:CPViewWidthSizable];
[collectionView setMaxNumberOfColumns:1];
[collectionView setMinItemSize:CGSizeMake(200, 100)];
[collectionView setMaxItemSize:CGSizeMake(10000, 100)];
var itemPrototype = [[CPCollectionViewItem alloc] init];
[itemPrototype setView:[TweetView new]];
[collectionView setItemPrototype:itemPrototype];
[collectionView setContent:data.results];
var scrollView = [[CPScrollView alloc] initWithFrame:bounds];
[scrollView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable]
[scrollView setDocumentView:collectionView];
[contentView addSubview:scrollView];
} else {
var label = [CPTextField labelWithTitle:"No tweets"],
boundsSize = [contentView boundsSize];
[label setCenter:CGPointMake(boundsSize.width / 2, boundsSize.height / 2)];
[label setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin | CPViewMinYMargin | CPViewMaxYMargin];
[contentView addSubview:label];
}
}Для отображения твитов он использует библиотечные классы
CPCollectionView, CPCollectionViewItem и CPScrollView. А вот класс TweetView придется определить самостоятельно.Наверное, вы заметили в коде несколько вызовов метода
setAutoresizingMask:. Этот метод заставит вас навсегда проклясть позиционирование CSS, если, конечно, вы этого еще не сделали. Позиционирование элементов управления в Cappuccino настолько просто и элегантно, что с ним справится даже ребенок.Осталось разобраться с классом
TweetView:@implementation TweetView : CPView
{
CPImageView imageView;
CPTextField userLabel;
CPTextField tweetLabel;
}
- (void)setRepresentedObject:(id)tweet
{
if (!imageView) {
imageView = [[CPImageView alloc] initWithFrame:CGRectMake(10, 5, 48, 48)];
[self addSubview:imageView];
userLabel = [[CPTextField alloc] initWithFrame:CGRectMake(65, 0, -60, 18)];
[userLabel setAutoresizingMask:CPViewWidthSizable];
[userLabel setFont:[CPFont boldSystemFontOfSize:12]];
[self addSubview:userLabel];
tweetLabel = [[CPTextField alloc] initWithFrame:CGRectMake(65, 18, -60, 100)];
[tweetLabel setAutoresizingMask:CPViewWidthSizable];
[tweetLabel setLineBreakMode:CPLineBreakByWordWrapping];
[self addSubview:tweetLabel];
}
[imageView setImage:[[CPImage alloc] initWithContentsOfFile:tweet.profile_image_url size:CGSizeMake(48, 48)]];
[userLabel setStringValue:tweet.from_user];
[tweetLabel setStringValue:tweet.text];
}
@endМетод
setRepresentedObject: создает изображение аватара и две метки, с именем пользователя и твитом.Теперь снова откроем index-debug.html и поищем несколько слов. Результат должен получиться примерно такой:

Вы можете просмотреть полный код примера и протестировать его.
Что дальше?
Конечно, в этом очерке невозможно было описать и десятую долю функционала Cappuccino. Однако я уверен, что сейчас вы уже готовы начать разработку своих приложений, к чему вас и призываю. Для углубления знаний используйте документацию Cappuccino, также очень рекомендую набор скринкастов по теме. Надеюсь, что титанический труд Франциско, Тома и Росса, а также мой скромный очерк помогут кому-то из вас создать новое интересное веб-приложение.