/**
* Ext JS Library 3.3.0
* @class Ext.Component
* @extends Ext.util.Observable
*
* Ext.Component是其他组件的基类,可以说是Ext.js框架里边最为基础的一个类。所有的子类组件都自动的参与Ext component的生命周期(lifecycle)中来,
包括其创建(creation),渲染(rendering)以及销毁(destruction),当然,这些是由Ext.Container类提供。
* 在Ext.Comtainer创建的同时,可以通过的其items配置项来添加组件,除了这个方法,还可以通过Ext.Container的add方法动态地添加进容器里边。
* Component基类支持hide/show、enable/disable基本事件操作。所有的组件在构建的时候都被注册进Ext.ComponentMgr里边,所有可以随时随地的通过
Ext.getCmp("id")来访问里边的组件。
* 每个组件都有唯一的xtype名称,可以通过getXType以及isXType来查看相关信息。
*/
/**
* @constructor
* @param {Ext.Element/String/Object}
* element: 内部元素,其id被作为组件id使用。
*
* string: 字符串ID。
*
* Object: it is applied to the component
*/
Ext.Component = function(config){
config = config || {};
if(config.initialConfig){
if(config.isAction){ // actions
this.baseAction = config;
}
config = config.initialConfig; // component cloning / action set up
}else if(config.tagName || config.dom || Ext.isString(config)){ // element object
config = {applyTo: config, id: config.id || config};
}
/**
* 组件初始化配置说明 Read-only.
* @type Object
* @property initialConfig
*/
this.initialConfig = config;
Ext.apply(this, config); //LifeCycle_初始化Initialization_1、配置项对象生效 The config object is applied
//注册事件
this.addEvents( //LifeCycle_初始化Initialization_2、底层事件创建
/**
* @event added
* 当组件被添加到容器时触发。
* Fires when a component is added to an Ext.Container
* @param {Ext.Component} this 自身
* @param {Ext.Container} ownerCt 组件拥有者,既容器 Container which holds the component
* @param {number} index 索引(组件被添加到的位置) Position at which the component was added
*/
'added',
/**
* @event disable 禁用
* Fires after the component is disabled.
* @param {Ext.Component} this
*/
'disable',
/**
* @event enable 激活
* Fires after the component is enabled.
* @param {Ext.Component} this
*/
'enable',
/**
* @event beforeshow
* Fires before the component is shown by calling the show method.
* Return false from an event handler to stop the show.
* @param {Ext.Component} this
*/
'beforeshow',
/**
* @event show
* Fires after the component is shown when calling the show method.
* @param {Ext.Component} this
*/
'show',
/**
* @event beforehide
* Fires before the component is hidden by calling the hide method.
* Return false from an event handler to stop the hide.
* @param {Ext.Component} this
*/
'beforehide',
/**
* @event hide
* Fires after the component is hidden.
* Fires after the component is hidden when calling the hide method.
* @param {Ext.Component} this
*/
'hide',
/**
* @event removed 移除组件
* Fires when a component is removed from an Ext.Container
* @param {Ext.Component} this
* @param {Ext.Container} ownerCt Container which holds the component
*/
'removed',
/**
* @event beforerender
* Fires before the component is rendered. Return false from an
* event handler to stop the render.
* @param {Ext.Component} this
*/
'beforerender',
/**
* @event render
* Fires after the component markup is rendered.
* @param {Ext.Component} this
*/
'render',
/**
* @event afterrender
* Fires after the component rendering is finished.
* @param {Ext.Component} this
*/
'afterrender',
/**
* @event beforedestroy
* Fires before the component is destroyed. Return false from an event handler to stop the destroy.
* @param {Ext.Component} this
*/
'beforedestroy',
/**
* @event destroy
* Fires after the component is destroyed.
* @param {Ext.Component} this
*/
'destroy',
/**
* @event beforestaterestore 恢复状态之前
* Fires before the state of the component is restored. Return false from an event handler to stop the restore.
* @param {Ext.Component} this
* @param {Object} state
*/
'beforestaterestore',
/**
* @event staterestore 恢复状态
* Fires after the state of the component is restored.
* @param {Ext.Component} this
* @param {Object} state
*/
'staterestore',
/**
* @event beforestatesave
* Fires before the state of the component is saved to the configured state provider. Return false to stop the save.
* @param {Ext.Component} this
* @param {Object} state The hash of state values(状态哈希值).
* 可以通过getState()得到组件的状态
*/
'beforestatesave',
/**
* @event statesave
* Fires after the state of the component is saved to the configured state provider.
* @param {Ext.Component} this
* @param {Object} state
*/
'statesave'
);
this.getId();
Ext.ComponentMgr.register(this); //LifeCycle_初始化Initialization_3、在组件管理器里登记组件
Ext.Component.superclass.constructor.call(this);
if(this.baseAction){
this.baseAction.addComponent(this);
}
this.initComponent();
if(this.plugins){ //LifeCycle_初始化Initialization_4、加载插件
if(Ext.isArray(this.plugins)){
for(var i = 0, len = this.plugins.length; i < len; i++){ //遍历所有插件
this.plugins[i] = this.initPlugin(this.plugins[i]);
}
}else{
this.plugins = this.initPlugin(this.plugins);
}
}
if(this.stateful !== false){ //LifeCycle_初始化Initialization_5、状态感知进行初始化
this.initState();
}
if(this.applyTo){ //LifeCycle_初始化Initialization_6、渲染插件
this.applyToMarkup(this.applyTo);
delete this.applyTo;
}else if(this.renderTo){
this.render(this.renderTo);
delete this.renderTo;
}
}; //end of Ext.Component
// private
Ext.Component.AUTO_ID = 1000;
Ext.extend(Ext.Component, Ext.util.Observable, { //Ext.Component继承于Ext.util.Observable
//当FormLayout渲染时,下面的这些配置可以被用作所有组件。
// Configs below are used for all Components when rendered by FormLayout.
/**
* @cfg {String} fieldLabel 标签名称
* The label text to display next to this Component (defaults to ''_默认为空).
* 特别说明:只用使用form布局时,标签名称才会显示。
* Note: this config is only used when this Component is rendered by a Container which
* has been configured to use the Ext.layout.FormLayout layout manager (e.g.
* Ext.form.FormPanel or specifying layout:'form').
*
* @cfg {Boolean} hideLabel
* 如果为true,则不显示fieldLabel,如果为false则反之。
* Example use:
new Ext.FormPanel({
height: 100,
renderTo: Ext.getBody(),
items: [{
xtype: 'textfield',
fieldLabel: 'Name'
}]
});
new Ext.FormPanel({
height: 100,
renderTo: Ext.getBody(),
items: [{
xtype: 'textfield'
hideLabel: true
}]
});
*/
/**
* @cfg {String} labelStyle 标签样式
* Note: see the note for clearCls.
* Example use:
new Ext.FormPanel({
height: 100,
renderTo: Ext.getBody(),
items: [{
xtype: 'textfield',
fieldLabel: 'Name',
labelStyle: 'font-weight:bold;'
}]
});
*/
/**
* @cfg {String} labelSeparator 标签分隔符
* labelSeparator defaults to colon ':'
* Note: see the note for {@link #clearCls}.
* Example use:
new Ext.FormPanel({
height: 100,
renderTo: Ext.getBody(),
layoutConfig: {
labelSeparator: '~'
},
labelSeparator: '>>', // 直接给容器配置的级别比布局配置(layout config)级别高,故Field2 显示的是'>>'
items: [{
xtype: 'textfield',
fieldLabel: 'Field 1',
labelSeparator: '...'
},{
xtype: 'textfield',
fieldLabel: 'Field 2'
}]
});
* @cfg {Ext.Template} fieldTpl
* Example use:
new Ext.Template(
'<div class="x-form-item {itemCls}" tabIndex="-1">',
'<label for="{id}" style="{labelStyle}" class="x-form-item-label">{label}{labelSeparator}</label>',
'<div class="x-form-element" id="x-form-el-{id}" style="{elementStyle}">',
'</div><div class="{clearCls}"></div>',
'</div>'
);
*/
/**
* @cfg {Boolean} hideLabel
* Note: see the note for clearCls.
* Example use:
new Ext.FormPanel({
height: 100,
renderTo: Ext.getBody(),
items: [{
xtype: 'textfield'
hideLabel: true
}]
});
*/
/**
* @cfg {String} clearCls
* Note: 只有使用form布局的才能起作用。
*/
/**
* @cfg {String} itemCls
* Note: 只有使用form布局的才能起作用。
* Example use:
// Apply a style to the field's label:
<style>
.required .x-form-item-label {font-weight:bold;color:red;}
</style>
new Ext.FormPanel({
height: 100,
renderTo: Ext.getBody(),
items: [{
xtype: 'textfield',
fieldLabel: 'Name',
itemCls: 'required' //this label will be styled
},{
xtype: 'textfield',
fieldLabel: 'Favorite Color'
}]
});
*/
/**
* @cfg {String} id
*/
/**
* @cfg {String} itemId
* Example use:
var c = new Ext.Panel({
height: 300,
renderTo: document.body,
layout: 'auto',
items: [
{
itemId: 'p1',
title: 'Panel 1',
height: 150
},
{
itemId: 'p2',
title: 'Panel 2',
height: 150
}
]
})
p1 = c.getComponent('p1'); // not the same as Ext.getCmp()
p2 = p1.ownerCt.getComponent('p2');
*
*/
/**
* @cfg {String} xtype
*/
/**
* @cfg {String} ptype
*/
/**
* @cfg {String} cls
* An optional extra CSS class that will be added to this component's Element (defaults to '').
*/
/**
* @cfg {String} overCls
* An optional extra CSS class that will be added to this component's Element when the mouse moves
* over the Element, and removed when the mouse moves out. (defaults to '').
/**
* @cfg {String} style
* Example use:
new Ext.Panel({
title: 'Some Title',
renderTo: Ext.getBody(),
width: 400, height: 300,
layout: 'form',
items: [{
xtype: 'textarea',
style: {
width: '95%',
marginBottom: '10px'
}
},
new Ext.Button({
text: 'Send',
minWidth: '100',
style: {
marginBottom: '10px'
}
})
]
});
*/
/**
* @cfg {String} ctCls
* An optional extra CSS class that will be added to this component's container.
* Note: ctCls defaults to '' except for(除了...之外) the following class
*/
/**
* @cfg {Boolean} disabled 是否禁用
* Render this component disabled (default is false).
*/
disabled : false,
/**
* @cfg {Boolean} hidden 是否隐藏
* Render this component hidden (default is false). If true, the
* hide method will be called internally(内部地).
*/
hidden : false,
/**
* @cfg {Object/Array} plugins 插件
* An object or array of objects that will provide custom functionality for this component.
*/
/**
* @cfg {Mixed} applyTo 渲染之地
*/
/**
* @cfg {Mixed} renderTo
*/
/**
* @cfg {Boolean} stateful
Ext.state.Manager.setProvider(new Ext.state.CookieProvider({
expires: new Date(new Date().getTime()+(1000*60*60*24*7)), //7 days from now
}));
*/
/**
* @cfg {String} stateId
*/
/**
* @cfg {Array} stateEvents
*/
/**
* @cfg {Mixed} autoEl
* Example usage:
{
xtype: 'box',
autoEl: {
tag: 'img',
src: 'http://www.example.com/example.jpg'
}
}, {
xtype: 'box',
autoEl: {
tag: 'blockquote',
html: 'autoEl is cool!'
}
}, {
xtype: 'container',
autoEl: 'ul',
cls: 'ux-unordered-list',
items: {
xtype: 'box',
autoEl: 'li',
html: 'First list item'
}
}
*/
autoEl : 'div',
/**
* @cfg {String} disabledClass
* CSS class added to the component when it is disabled (defaults to 'x-item-disabled').
*/
disabledClass : 'x-item-disabled',
/**
* @cfg {Boolean} allowDomMove
* Whether the component can move the Dom node when rendering (defaults to true).
*/
allowDomMove : true,
/**
* @cfg {Boolean} autoShow
*/
autoShow : false,
/**
* @cfg {String} hideMode
* 'visibility' 'offsets' and 'display'
*/
hideMode : 'display',
/**
* @cfg {Boolean} hideParent
*/
hideParent : false,
/**
new Ext.Panel({
title: 'The Clickable Panel',
listeners: {
render: function(p) {
p.getEl().on('click', handlePanelClick.createDelegate(null, [p], true));
},
single: true // Remove the listener after first invocation
}
});
* @type Ext.Element
* @property el
*/
/**
* True if this component has been rendered. Read-only.
* @type Boolean
* @property rendered
*/
rendered : false,
/**
* @cfg {String} contentEl
*/
/**
* @cfg {String/Object} html
*/
/**
* @cfg {Mixed} tpl
*/
/**
* @cfg {String} tplWriteMode The Ext.(X)Template method to use when
* updating the content area of the Component. Defaults to 'overwrite'
*/
tplWriteMode : 'overwrite',
/**
* @cfg {Mixed} data
*/
/**
* @cfg {Array} bubbleEvents
*/
bubbleEvents: [],
// private
ctype : 'Ext.Component',
// private
actionMode : 'el',
// private
getActionEl : function(){
return this[this.actionMode];
},
initPlugin : function(p){
if(p.ptype && !Ext.isFunction(p.init)){
p = Ext.ComponentMgr.createPlugin(p);
}else if(Ext.isString(p)){
p = Ext.ComponentMgr.createPlugin({
ptype: p
});
}
p.init(this);
return p;
},
/* // protected
* Function to be implemented by Component subclasses to be part of standard component initialization flow (it is empty by default).
*
// Traditional constructor:
Ext.Foo = function(config){
// call superclass constructor:
Ext.Foo.superclass.constructor.call(this, config);
this.addEvents({
// add events
});
};
Ext.extend(Ext.Foo, Ext.Bar, {
// class body
}
// initComponent replaces the constructor:
Ext.Foo = Ext.extend(Ext.Bar, {
initComponent : function(){
// call superclass initComponent
Ext.Foo.superclass.initComponent.call(this);
this.addEvents({
// add events
});
}
}
*/
initComponent : function(){
/*
* this is double processing, however it allows people to be able to do
* Ext.apply(this, {
* listeners: {
* //here
* }
* });
* MyClass.superclass.initComponent.call(this);
*/
if(this.listeners){
this.on(this.listeners);
delete this.listeners;
}
this.enableBubble(this.bubbleEvents);
},
/**
* 渲染
* Render this Component into the passed HTML element.
*
* If you are using a Container object to house this Component, then
* do not use the render method.
*
* A Container's child Components are rendered by that Container's
* layout manager when the Container is first rendered.
*
* Certain(肯定的) layout managers allow dynamic addition of child components. Those that do
* include CardLayout, AnchorLayout, FormLayout, TableLayout.
*
* If the Container is already rendered when a new child Component is added, you may need to call
* the Container's doLayout to refresh the view which causes any
* unrendered child Components to be rendered. This is required so that you can add multiple
* child components if needed while only refreshing the layout once.
*
* When creating complex(复合的) UIs, it is important to remember that sizing and positioning
* of child items is the responsibility(责任) of the Container's layout manager.
* If you expect child items to be sized in response to user interactions, you must
* configure the Container with a layout manager which creates and manages the type of layout you
* have in mind.
*
* Omitting(省略) the Container's layout config means that a basic
* layout manager is used which does nothing but render child components sequentially into the
* Container. No sizing or positioning will be performed in this situation.
*
* @param {Element/HTMLElement/String} container (optional) The element this Component should be
* rendered into. If it is being created from existing markup, this should be omitted.
* @param {String/Number} position (optional) The element ID or DOM node index within the container before
* which this component will be inserted (defaults to appending to the end of the container)
*/
render : function(container, position){ //LifeCycle_渲染过程 Rendering
//LifeCycle_渲染过程 Rendering_1、触发beforerender事件
if(!this.rendered && this.fireEvent('beforerender', this) !== false){
//LifeCycle_渲染过程 Rendering_2、设置容器
if(!container && this.el){
this.el = Ext.get(this.el);
container = this.el.dom.parentNode;
this.allowDomMove = false;
}
this.container = Ext.get(container);
if(this.ctCls){
this.container.addClass(this.ctCls); //给容器添加样式。
}
this.rendered = true; //标志已经渲染。
if(position !== undefined){
if(Ext.isNumber(position)){
position = this.container.dom.childNodes[position];
}else{
position = Ext.getDom(position);
}
}
//LifeCycle_渲染过程 Rendering_3、调用onRender方法
this.onRender(this.container, position || null);
//LifeCycle_渲染过程 Rendering_4、组件是“隐藏”状态的
if(this.autoShow){
this.el.removeClass(['x-hidden','x-hide-' + this.hideMode]);
}
//LifeCycle_渲染过程 Rendering_5、自定义的类、样式生效了
if(this.cls){
this.el.addClass(this.cls);
delete this.cls;
}
if(this.style){
this.el.applyStyles(this.style);
delete this.style;
}
if(this.overCls){ //鼠标动作样式
this.el.addClassOnOver(this.overCls);
}
//LifeCycle_渲染过程 Rendering_6、触发render事件
this.fireEvent('render', this);
// Populate content of the component with html, contentEl or a tpl.
var contentTarget = this.getContentTarget();
if (this.html){
contentTarget.update(Ext.DomHelper.markup(this.html));
delete this.html;
}
if (this.contentEl){
var ce = Ext.getDom(this.contentEl);
Ext.fly(ce).removeClass(['x-hidden', 'x-hide-display']);
contentTarget.appendChild(ce);
}
if (this.tpl) {
if (!this.tpl.compile) {
this.tpl = new Ext.XTemplate(this.tpl);
}
if (this.data) {
this.tpl[this.tplWriteMode](contentTarget, this.data);
delete this.data;
}
}
//LifeCycle_渲染过程 Rendering_7、调用了afterRender方法
this.afterRender(this.container);
//LifeCycle_渲染过程 Rendering_8、组件被隐藏或禁用
if(this.hidden){
// call this so we don't fire initial hide events.
this.doHide();
}
if(this.disabled){
// pass silent so the event doesn't fire the first time.
this.disable(true);
}
//LifeCycle_渲染过程 Rendering_9、所有状态感知的事件初始化
if(this.stateful !== false){
this.initStateEvents();
}
this.fireEvent('afterrender', this);
}
return this;
},
/**
* Update the content area of a component.
* @param {Mixed} htmlOrData
* If this component has been configured with a template via the tpl config
* then it will use this argument as data to populate the template.
* If this component was not configured with a template, the components
* content area will be updated via Ext.Element update
* @param {Boolean} loadScripts
* (optional) Only legitimate(合情合理的) when using the html configuration. Defaults to false
* @param {Function} callback
* (optional) Only legitimate when using the html configuration. Callback to execute when scripts have finished loading
*/
update: function(htmlOrData, loadScripts, cb) {
var contentTarget = this.getContentTarget();
if (this.tpl && typeof htmlOrData !== "string") {
this.tpl[this.tplWriteMode](contentTarget, htmlOrData || {});
} else {
var html = Ext.isObject(htmlOrData) ? Ext.DomHelper.markup(htmlOrData) : htmlOrData;
contentTarget.update(html, loadScripts, cb);
}
},
/**
* @private
* Method to manage awareness(能力) of when components are added to their
* respective(涉及到的) Container, firing an added event.
* References are established(被证实的) at add time rather than at render time.
* @param {Ext.Container} container Container which holds the component
* @param {number} pos Position at which the component was added
*/
onAdded : function(container, pos) {
this.ownerCt = container;
this.initRef();
this.fireEvent('added', this, container, pos);
},
/**
* @private
* Method to manage awareness of when components are removed from their
* respective Container, firing an removed event. References are properly
* cleaned up after removing a component from its owning container.
*/
onRemoved : function() {
this.removeRef();
this.fireEvent('removed', this, this.ownerCt);
delete this.ownerCt;
},
/**
* @private
* Method to establish(指定) a reference to a component.
*/
initRef : function() {
/**
* @cfg {String} ref
* Example use:
var myGrid = new Ext.grid.EditorGridPanel({
title: 'My EditorGridPanel',
store: myStore,
colModel: myColModel,
tbar: [{
text: 'Save',
handler: saveChanges,
disabled: true,
ref: '../saveButton'
}],
listeners: {
afteredit: function() {
// The button reference is in the GridPanel
myGrid.saveButton.enable();
}
}
});
*
*/
if(this.ref && !this.refOwner){
var levels = this.ref.split('/'),
last = levels.length,
i = 0,
t = this;
while(t && i < last){
t = t.ownerCt;
++i;
}
if(t){
t[this.refName = levels[--i]] = this;
/**
* @type Ext.Container
* @property refOwner
*/
this.refOwner = t;
}
}
},
removeRef : function() {
if (this.refOwner && this.refName) {
delete this.refOwner[this.refName];
delete this.refOwner;
}
},
// private
initState : function(){
if(Ext.state.Manager){
var id = this.getStateId();
if(id){
var state = Ext.state.Manager.get(id);
if(state){
if(this.fireEvent('beforestaterestore', this, state) !== false){
this.applyState(Ext.apply({}, state));
this.fireEvent('staterestore', this, state);
}
}
}
}
},
// private
getStateId : function(){
return this.stateId || ((/^(ext-comp-|ext-gen)/).test(String(this.id)) ? null : this.id);
},
// private
initStateEvents : function(){
if(this.stateEvents){
for(var i = 0, e; e = this.stateEvents[i]; i++){
this.on(e, this.saveState, this, {delay:100});
}
}
},
// private
applyState : function(state){
if(state){
Ext.apply(this, state);
}
},
// private
getState : function(){
return null;
},
// private
saveState : function(){
if(Ext.state.Manager && this.stateful !== false){
var id = this.getStateId();
if(id){
var state = this.getState();
if(this.fireEvent('beforestatesave', this, state) !== false){
Ext.state.Manager.set(id, state);
this.fireEvent('statesave', this, state);
}
}
}
},
/**
* Apply this component to existing markup that is valid. With this function, no call to render() is required.
* @param {String/HTMLElement} el
*/
applyToMarkup : function(el){
this.allowDomMove = false;
this.el = Ext.get(el);
this.render(this.el.dom.parentNode);
},
/**
* Adds a CSS class to the component's underlying element.
* @param {string} cls The CSS class name to add
* @return {Ext.Component} this
*/
addClass : function(cls){
if(this.el){
this.el.addClass(cls);
}else{
this.cls = this.cls ? this.cls + ' ' + cls : cls;
}
return this;
},
/**
* Removes a CSS class from the component's underlying element.
* @param {string} cls The CSS class name to remove
* @return {Ext.Component} this
*/
removeClass : function(cls){
if(this.el){
this.el.removeClass(cls);
}else if(this.cls){
this.cls = this.cls.split(' ').remove(cls).join(' ');
}
return this;
},
// private
// default function is not really useful
onRender : function(ct, position){
if(!this.el && this.autoEl){
if(Ext.isString(this.autoEl)){
this.el = document.createElement(this.autoEl);
}else{
var div = document.createElement('div');
Ext.DomHelper.overwrite(div, this.autoEl);
this.el = div.firstChild;
}
if (!this.el.id) {
this.el.id = this.getId();
}
}
if(this.el){
this.el = Ext.get(this.el);
if(this.allowDomMove !== false){
ct.dom.insertBefore(this.el.dom, position);
if (div) {
Ext.removeNode(div);
div = null;
}
}
}
},
// private
getAutoCreate : function(){
var cfg = Ext.isObject(this.autoCreate) ?
this.autoCreate : Ext.apply({}, this.defaultAutoCreate);
if(this.id && !cfg.id){
cfg.id = this.id;
}
return cfg;
},
// private
afterRender : Ext.emptyFn,
/**
* Destroys this component by purging(清除) any event listeners, removing the component's element from the DOM,
* removing the component from its Container (if applicable) and unregistering it from
* ComponentMgr. Destruction is generally handled automatically by the framework and this method
* should usually not need to be called directly.
*
*/
destroy : function(){ //LifeCycle_销毁过程 Destruction
if(!this.isDestroyed){
//LifeCycle_销毁过程 Destruction_1、触发beforedestroy事件
if(this.fireEvent('beforedestroy', this) !== false){
this.destroying = true;
//LifeCycle_销毁过程 Destruction_2、调用了beforeDestroy方法
this.beforeDestroy();
//LifeCycle_销毁过程 Destruction_3、元素及其侦听器被移除
if(this.ownerCt && this.ownerCt.remove){
this.ownerCt.remove(this, false);
}
if(this.rendered){
this.el.remove();
if(this.actionMode == 'container' || this.removeMode == 'container'){
this.container.remove();
}
}
// Stop any buffered tasks
if(this.focusTask && this.focusTask.cancel){
this.focusTask.cancel();
}
//LifeCycle_销毁过程 Destruction_4、调用了onDestroy方法
this.onDestroy();
//LifeCycle_销毁过程 Destruction_5、在组件管理器中撤销组件对象的登记
Ext.ComponentMgr.unregister(this);
//LifeCycle_销毁过程 Destruction_6、触发destroy事件
this.fireEvent('destroy', this);
//LifeCycle_销毁过程 Destruction_7、组件上的事件侦听器被移除
this.purgeListeners();
this.destroying = false;
this.isDestroyed = true;
}
}
},
deleteMembers : function(){
var args = arguments;
for(var i = 0, len = args.length; i < len; ++i){
delete this[args[i]];
}
},
// private
beforeDestroy : Ext.emptyFn,
// private
onDestroy : Ext.emptyFn,
/**
* Example use:
new Ext.Panel({
title: 'The Clickable Panel',
listeners: {
render: function(p) {
p.getEl().on('click', handlePanelClick.createDelegate(null, [p], true));
},
single: true // Remove the listener after first invocation
}
});
* @return {Ext.Element} The Element which encapsulates this Component.
*/
getEl : function(){
return this.el;
},
// private
getContentTarget : function(){
return this.el;
},
/**
* @return {String} id
*/
getId : function(){
return this.id || (this.id = 'ext-comp-' + (++Ext.Component.AUTO_ID));
},
/**
* Returns the itemId of this component.
* @return {String}
*/
getItemId : function(){
return this.itemId || this.getId();
},
/**
* Try to focus this component.
* @param {Boolean} selectText (optional) If applicable, true to also select the text in this component
* @param {Boolean/Number} delay (optional) Delay the focus this number of milliseconds (true for 10 milliseconds)
* @return {Ext.Component} this
*/
focus : function(selectText, delay){
if(delay){
this.focusTask = new Ext.util.DelayedTask(this.focus, this, [selectText, false]);
this.focusTask.delay(Ext.isNumber(delay) ? delay : 10);
return this;
}
if(this.rendered && !this.isDestroyed){
this.el.focus();
if(selectText === true){
this.el.dom.select();
}
}
return this;
},
// private
blur : function(){
if(this.rendered){
this.el.blur();
}
return this;
},
/**
* Disable this component and fire the 'disable' event.
* @return {Ext.Component} this
*/
disable : function(/* private */ silent){
if(this.rendered){
this.onDisable();
}
this.disabled = true;
if(silent !== true){
this.fireEvent('disable', this);
}
return this;
},
// private
onDisable : function(){
this.getActionEl().addClass(this.disabledClass);
this.el.dom.disabled = true;
},
/**
* Enable this component and fire the 'enable' event.
* @return {Ext.Component} this
*/
enable : function(){
if(this.rendered){
this.onEnable();
}
this.disabled = false;
this.fireEvent('enable', this);
return this;
},
// private
onEnable : function(){
this.getActionEl().removeClass(this.disabledClass);
this.el.dom.disabled = false;
},
/**
* Convenience(公共) function for setting disabled/enabled by boolean.
* @param {Boolean} disabled
* @return {Ext.Component} this
*/
setDisabled : function(disabled){
return this[disabled ? 'disable' : 'enable']();
},
/**
* Show this component. Listen to the beforeshow event and return
* false to cancel showing the component. Fires the show
* event after showing the component.
* @return {Ext.Component} this
*/
show : function(){
if(this.fireEvent('beforeshow', this) !== false){
this.hidden = false;
if(this.autoRender){
this.render(Ext.isBoolean(this.autoRender) ? Ext.getBody() : this.autoRender);
}
if(this.rendered){
this.onShow();
}
this.fireEvent('show', this);
}
return this;
},
// private
onShow : function(){
this.getVisibilityEl().removeClass('x-hide-' + this.hideMode);
},
/**
* Hide this component. Listen to the beforehide event and return
* false to cancel hiding the component. Fires the hide
* event after hiding the component. Note this method is called internally if
* the component is configured to be hidden.
* @return {Ext.Component} this
*/
hide : function(){
if(this.fireEvent('beforehide', this) !== false){
this.doHide();
this.fireEvent('hide', this);
}
return this;
},
// private
doHide: function(){
this.hidden = true;
if(this.rendered){
this.onHide();
}
},
// private
onHide : function(){
this.getVisibilityEl().addClass('x-hide-' + this.hideMode);
},
// private
getVisibilityEl : function(){
return this.hideParent ? this.container : this.getActionEl();
},
/**
* Convenience function to hide or show this component by boolean.
* @param {Boolean} visible True to show, false to hide
* @return {Ext.Component} this
*/
setVisible : function(visible){
return this[visible ? 'show' : 'hide']();
},
/**
* Returns true if this component is visible.
* @return {Boolean} True if this component is visible, false otherwise.
*/
isVisible : function(){
return this.rendered && this.getVisibilityEl().isVisible();
},
/**
* Clone the current component using the original(原始的) config values passed into this instance by default.
* @param {Object} overrides A new config containing any properties to override in the cloned version.
* An id property can be passed on this object, otherwise(否则) one will be generated to avoid duplicates.
* @return {Ext.Component} clone(复制) The cloned copy of this component
*/
cloneConfig : function(overrides){
overrides = overrides || {};
var id = overrides.id || Ext.id();
var cfg = Ext.applyIf(overrides, this.initialConfig);
cfg.id = id; // prevent dup id
return new this.constructor(cfg);
},
/**
* Gets the xtype for this component as registered with Ext.ComponentMgr. For a list of all
* available xtypes, see the Ext.Component header.
* Example usage:
var t = new Ext.form.TextField();
alert(t.getXType()); // alerts 'textfield'
* @return {String} The xtype
*/
getXType : function(){
return this.constructor.xtype;
},
/**
* Example usage:
var t = new Ext.form.TextField();
var isText = t.isXType('textfield'); // true
var isBoxSubclass = t.isXType('box'); // true, descended from BoxComponent
var isBoxInstance = t.isXType('box', true); // false, not a direct BoxComponent instance
* @param {String/Ext.Component/Class} xtype The xtype to check for this Component.
* @param {Boolean} shallow (optional) False to check whether this Component is descended from the xtype (this is
* the default), or true to check whether this Component is directly of the specified xtype.
* @return {Boolean} True if this component descends from the specified xtype, false otherwise.
*/
isXType : function(xtype, shallow){
//assume(假设) a string by default
if (Ext.isFunction(xtype)){
xtype = xtype.xtype; //handle being passed the class, e.g. Ext.Component
}else if (Ext.isObject(xtype)){
xtype = xtype.constructor.xtype; //handle being passed an instance
}
return !shallow ? ('/' + this.getXTypes() + '/').indexOf('/' + xtype + '/') != -1 : this.constructor.xtype == xtype;
},
/**
* Example usage:
var t = new Ext.form.TextField();
alert(t.getXTypes()); // alerts 'component/box/field/textfield'
* @return {String} The xtype hierarchy(等级) string
*/
getXTypes : function(){
var tc = this.constructor;
if(!tc.xtypes){
var c = [], sc = this;
while(sc && sc.constructor.xtype){
c.unshift(sc.constructor.xtype);
sc = sc.constructor.superclass;
}
tc.xtypeChain = c;
tc.xtypes = c.join('/');
}
return tc.xtypes;
},
/**
* @param {Function} fn The custom function(自定义函数) to call with the arguments (container, this component).
* @return {Ext.Container} The first Container for which the custom function returns true
*/
findParentBy : function(fn) {
for (var p = this.ownerCt; (p != null) && !fn(p, this); p = p.ownerCt);
return p || null;
},
/**
* @param {String/Ext.Component/Class} xtype The xtype to check for this Component.
* @param {Boolean} shallow (optional) False to check whether this Component is descended from the xtype (this is
* the default), or true to check whether this Component is directly of the specified xtype.
* @return {Ext.Container} The first Container which matches the given xtype or class
*/
findParentByType : function(xtype, shallow){
return this.findParentBy(function(c){
return c.isXType(xtype, shallow);
});
},
/**
* @param {Function} fn The function to call
* @param {Object} scope (optional) The scope of the function (defaults to current node)
* @param {Array} args (optional) The args to call the function with (default to passing the current component)
* @return {Ext.Component} this
*/
bubble : function(fn, scope, args){ //bubble冒泡
var p = this;
while(p){
if(fn.apply(scope || p, args || [p]) === false){
break;
}
p = p.ownerCt;
}
return this;
},
// protected
getPositionEl : function(){
return this.positionEl || this.el;
},
// private
purgeListeners : function(){
Ext.Component.superclass.purgeListeners.call(this);
if(this.mons){
this.on('beforedestroy', this.clearMons, this, {single: true});
}
},
// private
clearMons : function(){
Ext.each(this.mons, function(m){
m.item.un(m.ename, m.fn, m.scope);
}, this);
this.mons = [];
},
// private
createMons: function(){
if(!this.mons){
this.mons = [];
this.on('beforedestroy', this.clearMons, this, {single: true});
}
},
/**
* Usage:
myGridPanel.mon(myGridPanel.getSelectionModel(), 'selectionchange', handleSelectionChange, null, {buffer: 50});
or:
myGridPanel.mon(myGridPanel.getSelectionModel(), {
selectionchange: handleSelectionChange,
buffer: 50
});
* @param {Observable|Element} item The item to which to add a listener/listeners.
* @param {Object|String} ename The event name, or an object containing event name properties.
* @param {Function} fn Optional. If the ename parameter was an event name, this
* is the handler function.
* @param {Object} scope Optional. If the ename parameter was an event name, this
* is the scope (this reference) in which the handler function is executed.
* @param {Object} opt Optional. If the ename parameter was an event name, this
* is the addListener options.
*/
mon : function(item, ename, fn, scope, opt){
this.createMons();
if(Ext.isObject(ename)){
var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
var o = ename;
for(var e in o){
if(propRe.test(e)){
continue;
}
if(Ext.isFunction(o[e])){
// shared options
this.mons.push({
item: item, ename: e, fn: o[e], scope: o.scope
});
item.on(e, o[e], o.scope, o);
}else{
// individual(独特的) options
this.mons.push({
item: item, ename: e, fn: o[e], scope: o.scope
});
item.on(e, o[e]);
}
}
return;
}
this.mons.push({
item: item, ename: ename, fn: fn, scope: scope
});
item.on(ename, fn, scope, opt);
},
/**
* Removes listeners that were added by the mon method.
* @param {Observable|Element} item The item from which to remove a listener/listeners.
* @param {Object|String} ename The event name, or an object containing event name properties.
* @param {Function} fn Optional. If the ename parameter was an event name, this
* is the handler function.
* @param {Object} scope Optional. If the ename parameter was an event name, this
* is the scope (this reference) in which the handler function is executed.
*/
mun : function(item, ename, fn, scope){
var found, mon;
this.createMons();
for(var i = 0, len = this.mons.length; i < len; ++i){
mon = this.mons[i];
if(item === mon.item && ename == mon.ename && fn === mon.fn && scope === mon.scope){
this.mons.splice(i, 1);
item.un(ename, fn, scope);
found = true;
break;
}
}
return found;
},
/**
* Returns the next component in the owning container
* @return Ext.Component
*/
nextSibling : function(){ //Sibling兄妹
if(this.ownerCt){
var index = this.ownerCt.items.indexOf(this);
if(index != -1 && index+1 < this.ownerCt.items.getCount()){
return this.ownerCt.items.itemAt(index+1);
}
}
return null;
},
/**
* Returns the previous component in the owning container
* @return Ext.Component
*/
previousSibling : function(){
if(this.ownerCt){
var index = this.ownerCt.items.indexOf(this);
if(index > 0){
return this.ownerCt.items.itemAt(index-1);
}
}
return null;
},
/**
* @return {Ext.Container} the Container which owns this Component.
*/
getBubbleTarget : function(){
return this.ownerCt;
}
});
Ext.reg('component', Ext.Component);