yui3 предлагает автоматизировать аугментацию утилиты Attribute в свой кастомный класс, для этого существует класс Base, который и выполняет в себе аугментацию Атрибута. Если нужно реализовать в своем классе специализацию атрибута нужно просто наследовать класс Base.
Кроме аугментации Атрибута Бейс также аугментит класс Plugin.Host. Который позволяет в инстанция бейса подключать внешние обьекты-плагины. Внутренняя реализация плагина не важна, есть общепринятый интерфейс, который приносит дополнительный функционал в конкретную инстанцию. Для этого нужно сделать
ins.plug(pObj, configs)
То есть этот плагин может подключаться к разным инстанциям наследников класса бейс и привносить по желанию опциональный функционал.
В бейсе также реализовано статические методы плагин.хоста, что позволяет во все инстанции выбранного класса подключать выбранный плагин.
Base.plug(baseInheritorClass, pObj, config)
Но все же если в наш класс нужно привнести дополнительный функционал не только в констексте данной стрницы, а вообще расширить класс. Нам нужно воспользовать расширением.
1. То есть, если функционал может как быть в инстанции класса из нашей библиотеки, так и не быть, -- мы должны использовать прототипные методы плагин.хоста из наших инстанций.
2. Если в данный конкретный момент использования нашей библиотеки, всем нашим инстанциям нужен функционал плагина -- пользуемся статическим методом Base.plug.
3. Если наша библиотека всегда будет предлагать класс с расширенным функционалом, то нужно класс расширять.
Расширения делаются так:
Это первый способ с помощью которого можно создать расширение(by Y.Base.build) - он создается новый класс (в конструкторе нового класса вызываются контрукторы раширений). Кстати все классы и расширяемый и расширяющие должны быть наследнками Y.Base.
Base.mix(class, extensions) -- расширяет существующий класс (а не делает его копию и изменяет еe, как build). Тоесть в случае билда, мы можем пользоваться тут же и расширенным классом и его оригинальным коллегой.
Хух! Со сложным и запутанным закончили, теперь переходим к простому и красивому...)
Класс бейс и его наследники дожны иметь обязательно два статических атрибута: NAME и ATTRS.
Первый это имя класса, которое например вставлется префиксом во все события вызванные нашим классом.
Base содержит в себе финальные методы init и destroy. Возникает вопрос, как же мы можем поучавствовать в процессах инициализации и разрушения в нашем классе, наслденике от бейс? Дело в том, что эти два метода внутри себя вызывают методы this.initializer() и this.destructor(). Поэтому мы можем эти методы переоперделить в нашем прототипе:
Собственно плагины)
Простой плагин должен соответвовать простому правилу - иметь статическое свойство неймспейса, через которое к нему потом можно обратиться из хоста(обьекта к торому он был подключен)
И так плагин - обычный класс. Метод Plagin.Host.plug, который проаугменчен в Base, получает конструктор плагина, внутри всебе создает инстанцию, передав в контруктор ссылку на его хост:
Но у нас тажке плагин может не просто раширять функциоанльность обьекта, а и изменнять его функциональность. Например выполнять дополнительные действия, реагируя на события, которые генерит его хост; изменять методы хоста. В таком случае нам нужно наследовать класс Plugin.Base, который позволит все это сделать.
Как обычно этот класс наследник бейса. Ему как и простому плагину нужно обязательно установить статическое поле NS, и того у нас получается 3 статических поля:
Plugin.Base также приносит с собой следующие методы:
Пример для события:
Пример для метода:
Кроме аугментации Атрибута Бейс также аугментит класс Plugin.Host. Который позволяет в инстанция бейса подключать внешние обьекты-плагины. Внутренняя реализация плагина не важна, есть общепринятый интерфейс, который приносит дополнительный функционал в конкретную инстанцию. Для этого нужно сделать
ins.plug(pObj, configs)
То есть этот плагин может подключаться к разным инстанциям наследников класса бейс и привносить по желанию опциональный функционал.
В бейсе также реализовано статические методы плагин.хоста, что позволяет во все инстанции выбранного класса подключать выбранный плагин.
Base.plug(baseInheritorClass, pObj, config)
Но все же если в наш класс нужно привнести дополнительный функционал не только в констексте данной стрницы, а вообще расширить класс. Нам нужно воспользовать расширением.
1. То есть, если функционал может как быть в инстанции класса из нашей библиотеки, так и не быть, -- мы должны использовать прототипные методы плагин.хоста из наших инстанций.
2. Если в данный конкретный момент использования нашей библиотеки, всем нашим инстанциям нужен функционал плагина -- пользуемся статическим методом Base.plug.
3. Если наша библиотека всегда будет предлагать класс с расширенным функционалом, то нужно класс расширять.
Расширения делаются так:
/* Main Class */
function Panel(cfg) {
Panel.superclass.constructor.apply(this, arguments);
}
Panel.ATTRS = {
// Panel attributes
close : { ... },
minimize : { ... },
shadow : { ... },
...
};
Y.extend(Panel, Y.Base, {
// Panel methods
show : function() { ... },
hide : function() { ... },
minimize : function() { ... }
};
/* Additional Resizable Feature */
function Resizable() {
this._initResizable();
}
Resizable.ATTRS = {
handles : { ... },
constrain : { ... }
};
Resizable.prototype = {
_initResizable : function() { ... }
lock : function() { ... }
};
/* Additional Modality Feature */
function Modal() {
this._initModality();
}
Modal.ATTRS = {
modal : { ... },
region : { ... }
};
Modal.prototype = {
_initModality : function() { ... },
showMask() : function() { ... },
hideMask() : function() { ... }
};
// Create a new class WindowPanel, which extends Panel, and
// combines methods/attributes from Resizable and Modal
var WindowPanel = Y.Base.build("windowPanel", Panel, [Resizable, Modal]);
var wp = new WindowPanel({
shadow: true,
modal: true,
handles:["e", "s", "se"]
});
wp.show();
wp.lock();
Это первый способ с помощью которого можно создать расширение(by Y.Base.build) - он создается новый класс (в конструкторе нового класса вызываются контрукторы раширений). Кстати все классы и расширяемый и расширяющие должны быть наследнками Y.Base.
Base.mix(class, extensions) -- расширяет существующий класс (а не делает его копию и изменяет еe, как build). Тоесть в случае билда, мы можем пользоваться тут же и расширенным классом и его оригинальным коллегой.
Base.create ( name , main , extensions , px , sx ) -- это третий способ. Он дает новому классу имя нейм, копирует класс мейн, расширяет его массивом классов из екстеншин, если нужно то добавляет в прототип класса методы из пеикс, и если нужно добавляет статические атрибуты/методы из есикс. Тот же билд только еще с двумя наворотами)Хух! Со сложным и запутанным закончили, теперь переходим к простому и красивому...)
Класс бейс и его наследники дожны иметь обязательно два статических атрибута: NAME и ATTRS.
Первый это имя класса, которое например вставлется префиксом во все события вызванные нашим классом.
// NAME is used to prefix the provided event type, if not already prefixed,
// when publishing, firing and subscribing to events.
MyClass.prototype.doSomething = function() {
// Actually fires the event type "myclass:enabled"
this.fire("enabled");
}
...
var o = new MyClass(cfg);
o.on("enabled", function() {
// Actually listening for "myclass:enabled".
});
o.on("myclass:enabled", function() {
// Also listening for "myclass:enabled"
});
Второй это хранилище атрибутов с его настройками. В момент когда вызовется конструктор нашего класса, в него можно будет передать инициализации наших атрибутов, который пойдут вторым аргументом в метода атрибута this.addAttrs(ClassName.ATTRS, userValues).Base содержит в себе финальные методы init и destroy. Возникает вопрос, как же мы можем поучавствовать в процессах инициализации и разрушения в нашем классе, наслденике от бейс? Дело в том, что эти два метода внутри себя вызывают методы this.initializer() и this.destructor(). Поэтому мы можем эти методы переоперделить в нашем прототипе:
Y.extend(MyClass, Y.Base, {
// Prototype methods for your new class
// Tasks MyClass needs to perform during
// the init() lifecycle phase
initializer : function(cfg) {
this._wrapper = Y.Node.create('');
},
// Tasks MyClass needs to perform during
// the destroy() lifecycle phase
destructor : function() {
Y.Event.purgeElement(this._wrapper);
this._wrapper.get("parentNode").removeChild(this._wrapper);
this._wrapper = null;
}
});
Собственно плагины)
Простой плагин должен соответвовать простому правилу - иметь статическое свойство неймспейса, через которое к нему потом можно обратиться из хоста(обьекта к торому он был подключен)
И так плагин - обычный класс. Метод Plagin.Host.plug, который проаугменчен в Base, получает конструктор плагина, внутри всебе создает инстанцию, передав в контруктор ссылку на его хост:
// This AnchorPlugin is designed to be added to Node instances (the host will be a Node instance)
function AnchorPlugin(config) {
// Hold onto the host instance (a Node in this case),
// for other plugin methods to use.
this._node = config.host;
}
// When plugged into a node instance, the plugin will be
// available on the "anchors" property.
AnchorPlugin.NS = "anchors"
AnchorPlugin.prototype = {
disable: function() {
var node = this._node;
var anchors = node.queryAll("a");
anchors.addClass("disabled");
anchors.setAttribute("disabled", true);
}
};
var container = Y.one("div.actions");
container.plug(AnchorPlugin);
container.anchors.disable();
Но у нас тажке плагин может не просто раширять функциоанльность обьекта, а и изменнять его функциональность. Например выполнять дополнительные действия, реагируя на события, которые генерит его хост; изменять методы хоста. В таком случае нам нужно наследовать класс Plugin.Base, который позволит все это сделать.
Как обычно этот класс наследник бейса. Ему как и простому плагину нужно обязательно установить статическое поле NS, и того у нас получается 3 статических поля:
- NAME;
- ATTRS;
- NS.
Plugin.Base также приносит с собой следующие методы:
- onHostEvent(type, fn, context)
- Можно приктутить обработчик события на том этапе когда можно отменить дефолтовый обработчик
- afterHostEvent(type, fn, context)
- На этом этапе дефолтовый обоработчик отменять поздно.
- beforeHostMethod(strMethod, fn, context)
- Интересная штука, внутри этого метода бейс плагин пользуется улугами Y.Do, который реализует кастомный движок событий. Он предоставляет синтетические события DOM. С помощью этого метода мы можем выполниться некую логику перед каждым вызовом strMethod. И если нужно, то можно даже отмерить родной вызов метода хоста, вернув из fn return new Y.Do.Prevent();
- afterHostMethod(strMethod, fn, context)
- Таже малина только уже после вызова родного метода strMethod, тут его отменять поздно.
- doBefore(strMethod, fn, context)
- Этот интересный метод проверяет оператором IN, если есть метод strMethod в хосте, то догда выполняет beforeHostMethod с пришедшими аргументами, если нет - onHostEvent
- doAfter(strMethod, fn, context)
- Это брат предыдущего метода только на уже после события/вызова метода
Пример для события:
// A plugin which introduces rounded corners to a widget.
function RoundedCornersPlugin(config) {
//...
}
RoundedCornersPlugin.NAME = 'roundedCornersPlugin';
RoundedCornersPlugin.NS = 'corners';
Y.extend(RoundedCornersPlugin, Y.Plugin.Base, {
// Automatically called by Base, during construction
initializer: function(config) {
// "render" is a widget event
this.afterHostEvent('render', this.insertCornerElements);
},
insertCornerElements: function() {
var widget = this.get("host");
var boundingBox = widget.get("boundingBox");
var tl = Y.Node.create(TL_TEMPLATE);
//...
boundingBox.appendChild(tlNode);
boundingBox.appendChild(trNode);
boundingBox.appendChild(blNode);
boundingBox.appendChild(brNode);
}
});
Пример для метода:
// A plugin class designed to animate Widget's show and hide methods.
function WidgetAnimPlugin(config) {
//...
}
WidgetAnimPlugin.NAME = 'widgetAnimPlugin';
WidgetAnimPlugin.NS = 'fx';
WidgetAnimPlugin.ATTRS = {
animHidden : {
//...
},
animVisible: {
//...
}
};
// Extend Plugin.Base, and override the default
// method _uiSetVisible, used by Widget to flip the visibility
Y.extend(WidgetAnimPlugin, Y.Plugin.Base, {
initializer : function(config) {
// Override Widget's _uiSetVisible method, with the custom animated method
this.beforeHostMethod("_uiSetVisible", this._uiAnimSetVisible);
},
_uiAnimSetVisible : function(show) {
// Instead of flipping visibility, use the animation
// instances configured for the plugin to animate
// hide/show.
if (this.get("host").get("rendered")) {
if (show) {
this.get("animHidden").stop();
this.get("animVisible").run();
} else {
this.get("animVisible").stop();
this.get("animHidden").run();
}
// Prevent the default method from being invoked.
return new Y.Do.Prevent();
}
}
});
Комментариев нет:
Отправить комментарий