вторник, 20 января 2009 г.

jQuery 1.3

Вышел новый релиз jQuery - 1.3
Отмечается значительное увеличение производительности и скорости работы библиотеки.
Подробнее

суббота, 3 января 2009 г.

Приложения в ExtJS

До того как начать

Это пособие подразумевает, что вы уже установили ExtJS - JavaScript библиотеку и что она установлена в директории extjs в вышестоящей от текущей директории, где будет создаваться наше приложение. Если вы установили библиотеку в другом месте вам необходимо будет изменять пути которые мы употребляем в примерах.
Что нам понадобится?

Помимо ExtJS нам понадобится создать два файла:

* applayout.html
* applayout.js


Давайте начнем с файлов с минимумом содержимого. Вот они с детальным описанием:

applayout.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

<link rel="stylesheet" type="text/css" href="../extjs/resources/css/ext-all.css">
<script type="text/javascript" src="../extjs/adapter/ext/ext-base.js"></script>

<script type="text/javascript" src="../extjs/ext-all-debug.js"></script>
<script type="text/javascript" src="applayout.js"></script>

<!-- A Localization Script File comes here -->
<script type="text/javascript">
Ext.onReady(myNameSpace.app.init, myNameSpace.app);
</script>
<title>Application Layout Tutorial</title>

</head>
<body>
 
<div id="btn1-ct"></div>
 
</body>
</html>


Первые две строчки указывают тип документа. Наше приложение будет работать и без указания типа, однако в этом случае броузер перейдет в режим Quirks и приложение не станет совместимым с другими броузерами.
Мы будем использовать HTML 4.0.1 спецификацию, потому что она поддерживается большинством броузеров. Конечно, вы можете использовать другой тип документа если захотите, однако важно помнить, что вы всегда должны указывать тип документа.

ОБНОВЛЕНИЕ: Если вы будете указывать тип документа, то в броузерах IE (всех версиях) есть баги, которые могут вызвать непредсказуемые результаты выполнения. Поэтому больше не рекомундуется указывать тип документа в приложениях.

После начала файла и заголовка head идет указание типа содержимого Content-Type. Ваше приложение будет работать и без него, но лучше указать его.

Затем мы подключаем Ext стили, адаптер и ExtJS. Есть две версии ExtJS:
* ext-all.js - плохо-читаемый, загружается быстрее, используется для готовых приложений
* ext-all-debug.js - легко-читаемый, используется для разработки

Мы собираемся разрабатывать, поэтому включаем вторую версию.
Затем идет наше приложение applayout.js, а после него файлы локализации которые перекрывают английский текст в приложении applayout.js и ExtJS своим переводом, до того как приложение стартует.
Теперь мы готовы установить обработчик событий, который инициализирует (стартует) наше приложение после того, как документ полностью загрузится.
Строка

Ext.onReady(myNameSpace.app.init, myNameSpace.app);


говорит: Когда документ полностью загрузился, запустить init метод myNameSpace.app в области myNameSpace.app
Затем идет текст заголовка, конец head (пустой) и закрывающиеся тэги.
Это все, что сейчас находится в html файле.
applayout.js

/**
* Application Layout
* by Jozef Sakalos, aka Saki
* http://extjs.com/learn/Tutorial:Application_Layout_for_Beginners
*/

 
// reference local blank image
Ext.BLANK_IMAGE_URL = '../extjs/resources/images/default/s.gif';
 
// create namespace
Ext.namespace('myNameSpace');

 
// create application
myNameSpace.app = function() {
// do NOT access DOM from here; elements don't exist yet
 

// private variables
 
// private functions
 
// public space
return {
// public properties, e.g. strings to translate

 
// public methods
init: function() {
alert('Application successfully initialized');
}

};
}(); // end of app
 
// end of file


Первые строки этого файла это комментарии, объясняющие содержимое файла, автора и другую связанную информацию. Уверен, ваши приложения будут работать и без комментариев, но запомните: всегда пишите ваше приложение так, как будто пишите для другого человека. Когда вы когда-нибудь снова вернетесь к этому коду через пару месяцев и не сможете понять чтоже такое написано здесь, потому как нет комментариев, вы измените вашу практику кодирования.
Затем идет ссылка на пустое изображение, указывающее на ваш собственный сервер взамен сервера по-умолчанию extjs.com. Вам ведь не надо загружать что-либо с extjs.com при каждой загрузке приложения, не так ли?
Теперь мы создаем собственное пространство имен. Причина этого в инкапсуляции (изолированиии) всех переменных и методов в одном глобальном объекте чтоб избежать конфликтов имен переменных и избежать изменнений их значений извне.
Ядро приложения в конце.. Мы создаем свойство app в myNameSpace как возвращаемое значение функции которая запускается немедленно.
Если мы запустим следующий код
var o = function() {
return {p1:11, p2:22};

}();


Мы по-сути создаем анонимную функцию (функцию без имени) которая запускается сразу же после обработки (помните () полсе определения функции). Функция возвращает объект который привязывается к переменной о. Наше приложение будет использовать туже логику.
Вы можете разместить инициализацию, частные переменные и\или определения частных функций между началом объявление функции и моментом возврата результата, но помните: Код запускается как часть заголовка и никакие html элементы еще не созданы поэтому любые попытки доступа к ним вызовут ошибку.
С другой стороны, init функция это просто один из методов возвращаемых этой анонимной функцией, запускается только после того, как весь документ полностью загружен и вся структура DOM уже доступна.
Дальше - больше. Если вы не сделали никаких ошибок вы получите сообщение об успехе когда запустите ваше приложение.
Теперь идет самая сложная часть. Это написать что-то работающее внутри этого пустого шаблона.

Public, Private, Privileged?

Давайте добавим некоторую жизнь нашему приложению. Добавьте следующие строки в applayout.html внутрь тэга body:
<div id="btn1-ct"></div>

Это пустой div который послужит как контейнер для кнопки. Затем добавим парочку строк в applayout.js и вот что получится:

/**
* Application Layout
* by Jozef Sakalos, aka Saki
* http://extjs.com/learn/Tutorial:Application_Layout_for_Beginners
*/

 
// reference local blank image
Ext.BLANK_IMAGE_URL = '../extjs/resources/images/default/s.gif';
 
// create namespace
Ext.namespace('myNameSpace');

 
// Just to allow this tutorial to work for 1.1 and 2.
Ext.Ext2 = (Ext.version && (Ext.version.indexOf("2") == 0));

 
// create application
myNameSpace.app = function() {
// do NOT access DOM from here; elements don't exist yet
 

// private variables
var btn1;
var privVar1 = 11;
 
// private functions
var btn1Handler = function(button, event) {

alert('privVar1=' + privVar1);
alert('this.btn1Text=' + this.btn1Text);
};

 
// public space
return {
// public properties, e.g. strings to translate
btn1Text: 'Button 1',
 

// public methods
init: function() {
if (Ext.Ext2) {

btn1 = new Ext.Button({
renderTo: 'btn1-ct',
text: this.btn1Text,
handler: btn1Handler
});
} else {

btn1 = new Ext.Button('btn1-ct', {
text: this.btn1Text,
handler: btn1Handler
});
}

}
};
}(); // end of app
 
// end of file


Переменные btn1 и privVar1 - закрытые, private. Это значит что к ним можно получить доступ только из приложения а вне его они невидимы. Тоже самое справедливо для частной функции btn1Handler.
С другой стороны, btn1Text это публичная public переменная которая видима и может быть доступна или изменена извне приложения (мы продемонстрируем это позже).
Функция init привелигирована в том что может работать как с public так и с private переменными и функциями. В нашем примере она работает с публичной переменной this.btn1Text, а также с private функцией btn1Handler. Функция init публичная (public) и может быть вызвана откуда угодно.
Ок, давайте запустим его. Видите кнопку в верхнем левом углу? Хорошо. Нажмите ее. Вы получите сообщение privVar1=11, так? Частная private функция может работать с частными private переменными.
Однако что то не так при втором показе сообщения и мы видим что this.btn1Text=undefined; это не то, что мы ожидали. Причина в том, что переменная this внутри обработчика теперь указывает на кнопку а не на наше приложение. Добавьте scope:this в конфигурацию кнопки вот так:

if (Ext.Ext2) {
btn1 = new Ext.Button({

renderTo: 'btn1-ct',
text: this.btn1Text,
handler: btn1Handler,
scope:this
});
} else {

btn1 = new Ext.Button('btn1-ct', {
text: this.btn1Text,
handler: btn1Handler,
scope:this

});
}


и перезагрузите страницу. Работает теперь?

Перезагрузка (перекрытие) публичных переменных.
Добавьте следующий код в конце applayout.js чтобы было так:
Ext.apply(myNameSpace.app, {
btn1Text:'Taste 1'
});
 
// end of file


Что теперь делает весь этот код? Сначала он создает объект нашего приложения и затем изменяет (перезагружает) значение публичной переменной btn1Text. Запустите его, чтобы увидеть что текст на кнопке изменился.
Хорошая практика кодирования это хранить весь текст используемый приложением в публичных переменных, так они могут быть легко переведены на другие языки без изменения оригинального кода приложения.
Конечно, следует хранить также другие значения, такие как размеры по-умолчанию, стили и другие настраиваемые опции в публичных переменных. Тогда вам не понадобится исследовать тысячи строк кода приложения чтобы изменить цвет какого нибудь элемента например.


Перегрузка (перекрытие) публичных функций

Давайте изменим наш код на следующий:

Ext.apply(myNameSpace.app, {
btn1Text:'Taste 1',
init: function() {

try {
btn1 = new Ext.Button('btn1-ct', {
text: this.btn1Text,
handler: btn1Handler,
scope: this

});
}
catch(e) {
alert('Error: "' + e.message + '" at line: ' + e.lineNumber);
}

}
});
 
// end of file


Что мы здесь сделали? Просто переопределили функцию init тем же кодом что и до того, только заключенным в блок try/catch чтобы мы могли обрабатывать любые ошибки. Должны мы ожидать прежнего результата или другого? Запустите и увидите.
Да, будет ошибка о том что btn1Handler не определен. Это потому, что обновленная функция пытается получить доступ к частной переменной но не может. Вы видите, оригинальный init привелоигирован и имеет доступ к частному пространству но новый не имеет. В этом есть аспект безопасности: Никакой внешний код не должен получать доступ к частному пространству даже если пытается перегрузить привелигированные методы.