You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ngcp-csc/ext/build/ext-debug.js

35981 lines
1.2 MiB

/*
This file is part of Ext JS 6.2.0.981
Copyright (c) 2011-2016 Sencha Inc
Contact: http://www.sencha.com/contact
GNU General Public License Usage
This file may be used under the terms of the GNU General Public License version 3.0 as
published by the Free Software Foundation and appearing in the file LICENSE included in the
packaging of this file.
Please review the following information to ensure the GNU General Public License version 3.0
requirements will be met: http://www.gnu.org/copyleft/gpl.html.
If you are unsure which license is appropriate for your use, please contact the sales department
at http://www.sencha.com/contact.
Version: 6.2.0.981 Build date: 2016-08-31 14:49:44 (08dbbd0ec0b8bc0e014d725fdb7d9650d510b343)
*/
var Ext = Ext || {};
(function(manifest){
if(!Ext.manifest) {
Ext.manifest = manifest;
} else {
for(var name in manifest) {
Ext.manifest[name] = manifest[name];
}
}
})({
"paths": {
"Ext": "../classic/classic/src",
"Ext.AbstractManager": "../packages/core/src/AbstractManager.js",
"Ext.Ajax": "../packages/core/src/Ajax.js",
"Ext.AnimationQueue": "../packages/core/src/AnimationQueue.js",
"Ext.ComponentManager": "../packages/core/src/ComponentManager.js",
"Ext.ComponentQuery": "../packages/core/src/ComponentQuery.js",
"Ext.Deferred": "../packages/core/src/Deferred.js",
"Ext.Evented": "../packages/core/src/Evented.js",
"Ext.Factory": "../packages/core/src/mixin/Factoryable.js",
"Ext.GlobalEvents": "../packages/core/src/GlobalEvents.js",
"Ext.Glyph": "../packages/core/src/Glyph.js",
"Ext.JSON": "../packages/core/src/JSON.js",
"Ext.Mixin": "../packages/core/src/class/Mixin.js",
"Ext.Msg": "../classic/classic/src/window/MessageBox.js",
"Ext.Progress": "../packages/core/src/Progress.js",
"Ext.ProgressBase": "../packages/core/src/ProgressBase.js",
"Ext.Promise": "../packages/core/src/Promise.js",
"Ext.String.format": "../packages/core/src/Template.js",
"Ext.TaskQueue": "../packages/core/src/TaskQueue.js",
"Ext.Template": "../packages/core/src/Template.js",
"Ext.Widget": "../packages/core/src/Widget.js",
"Ext.XTemplate": "../packages/core/src/XTemplate.js",
"Ext.app": "../packages/core/src/app",
"Ext.data": "../packages/core/src/data",
"Ext.direct": "../packages/core/src/direct",
"Ext.dom": "../packages/core/src/dom",
"Ext.dom.ButtonElement": "../classic/classic/src/dom/ButtonElement.js",
"Ext.dom.Layer": "../classic/classic/src/dom/Layer.js",
"Ext.drag": "../packages/core/src/drag",
"Ext.event": "../packages/core/src/event",
"Ext.event.publisher.MouseEnterLeave": "../classic/classic/src/event/publisher/MouseEnterLeave.js",
"Ext.fx.Animation": "../packages/core/src/fx/Animation.js",
"Ext.fx.Runner": "../packages/core/src/fx/Runner.js",
"Ext.fx.State": "../packages/core/src/fx/State.js",
"Ext.fx.animation": "../packages/core/src/fx/animation",
"Ext.fx.easing": "../packages/core/src/fx/easing",
"Ext.fx.layout": "../packages/core/src/fx/layout",
"Ext.fx.runner": "../packages/core/src/fx/runner",
"Ext.list": "../packages/core/src/list",
"Ext.mixin": "../packages/core/src/mixin",
"Ext.parse": "../packages/core/src/parse",
"Ext.perf": "../packages/core/src/perf",
"Ext.plugin.Abstract": "../packages/core/src/plugin/Abstract.js",
"Ext.plugin.LazyItems": "../packages/core/src/plugin/LazyItems.js",
"Ext.plugin.MouseEnter": "../packages/core/src/plugin/MousEnter.js",
"Ext.promise": "../packages/core/src/promise",
"Ext.scroll.Scroller": "../packages/core/src/scroll/Scroller.js",
"Ext.sparkline": "../packages/core/src/sparkline",
"Ext.util": "../packages/core/src/util",
"Ext.util.Animate": "../classic/classic/src/util/Animate.js",
"Ext.util.ClickRepeater": "../classic/classic/src/util/ClickRepeater.js",
"Ext.util.ComponentDragger": "../classic/classic/src/util/ComponentDragger.js",
"Ext.util.Cookies": "../classic/classic/src/util/Cookies.js",
"Ext.util.ElementContainer": "../classic/classic/src/util/ElementContainer.js",
"Ext.util.Floating": "../classic/classic/src/util/Floating.js",
"Ext.util.Focusable": "../classic/classic/src/util/Focusable.js",
"Ext.util.FocusableContainer": "../classic/classic/src/util/FocusableContainer.js",
"Ext.util.Format.format": "../packages/core/src/Template.js",
"Ext.util.KeyMap": "../classic/classic/src/util/KeyMap.js",
"Ext.util.KeyNav": "../classic/classic/src/util/KeyNav.js",
"Ext.util.Memento": "../classic/classic/src/util/Memento.js",
"Ext.util.ProtoElement": "../classic/classic/src/util/ProtoElement.js",
"Ext.util.Queue": "../classic/classic/src/util/Queue.js",
"Ext.util.Renderable": "../classic/classic/src/util/Renderable.js",
"Ext.util.StoreHolder": "../classic/classic/src/util/StoreHolder.js"
},
"loadOrder": [
{
"path": "../packages/core/src/class/Mixin.js",
"requires": [],
"uses": [],
"idx": 0
},
{
"path": "../packages/core/src/util/DelayedTask.js",
"requires": [],
"uses": [
76
],
"idx": 1
},
{
"path": "../packages/core/src/util/Event.js",
"requires": [
1
],
"uses": [
23
],
"idx": 2
},
{
"path": "../packages/core/src/mixin/Identifiable.js",
"requires": [],
"uses": [],
"idx": 3
},
{
"path": "../packages/core/src/mixin/Observable.js",
"requires": [
0,
2,
3
],
"uses": [
51
],
"idx": 4
},
{
"path": "../packages/core/src/util/HashMap.js",
"requires": [
4
],
"uses": [],
"idx": 5
},
{
"path": "../packages/core/src/AbstractManager.js",
"requires": [
5
],
"uses": [],
"idx": 6
},
{
"path": "../packages/core/src/promise/Consequence.js",
"requires": [],
"uses": [
8
],
"idx": 7
},
{
"path": "../packages/core/src/promise/Deferred.js",
"requires": [
7
],
"uses": [
9
],
"idx": 8
},
{
"path": "../packages/core/src/promise/Promise.js",
"requires": [
8
],
"uses": [],
"idx": 9
},
{
"path": "../packages/core/src/Promise.js",
"requires": [
9
],
"uses": [
8
],
"idx": 10
},
{
"path": "../packages/core/src/Deferred.js",
"requires": [
8,
10
],
"uses": [
9
],
"idx": 11
},
{
"path": "../packages/core/src/mixin/Factoryable.js",
"requires": [],
"uses": [],
"idx": 12
},
{
"path": "../packages/core/src/data/request/Base.js",
"requires": [
11,
12
],
"uses": [
17
],
"idx": 13
},
{
"path": "../packages/core/src/data/flash/BinaryXhr.js",
"requires": [],
"uses": [
76
],
"idx": 14
},
{
"path": "../packages/core/src/data/request/Ajax.js",
"requires": [
13,
14
],
"uses": [
76
],
"idx": 15
},
{
"path": "../packages/core/src/data/request/Form.js",
"requires": [
13
],
"uses": [],
"idx": 16
},
{
"path": "../packages/core/src/data/Connection.js",
"requires": [
4,
11,
14,
15,
16
],
"uses": [
12,
49
],
"idx": 17
},
{
"path": "../packages/core/src/Ajax.js",
"requires": [
17
],
"uses": [],
"idx": 18
},
{
"path": "../packages/core/src/AnimationQueue.js",
"requires": [],
"uses": [],
"idx": 19
},
{
"path": "../packages/core/src/ComponentManager.js",
"requires": [],
"uses": [
23,
49
],
"idx": 20
},
{
"path": "../packages/core/src/util/Operators.js",
"requires": [],
"uses": [],
"idx": 21
},
{
"path": "../packages/core/src/util/LruCache.js",
"requires": [
5
],
"uses": [],
"idx": 22
},
{
"path": "../packages/core/src/ComponentQuery.js",
"requires": [
20,
21,
22
],
"uses": [
87
],
"idx": 23
},
{
"path": "../packages/core/src/Evented.js",
"requires": [
4
],
"uses": [],
"idx": 24
},
{
"path": "../packages/core/src/util/Positionable.js",
"requires": [],
"uses": [
33,
49
],
"idx": 25
},
{
"path": "../packages/core/src/dom/UnderlayPool.js",
"requires": [],
"uses": [
49
],
"idx": 26
},
{
"path": "../packages/core/src/dom/Underlay.js",
"requires": [
26
],
"uses": [],
"idx": 27
},
{
"path": "../packages/core/src/dom/Shadow.js",
"requires": [
27
],
"uses": [],
"idx": 28
},
{
"path": "../packages/core/src/dom/Shim.js",
"requires": [
27
],
"uses": [],
"idx": 29
},
{
"path": "../packages/core/src/dom/ElementEvent.js",
"requires": [
2
],
"uses": [
36
],
"idx": 30
},
{
"path": "../packages/core/src/event/publisher/Publisher.js",
"requires": [],
"uses": [],
"idx": 31
},
{
"path": "../packages/core/src/util/Offset.js",
"requires": [],
"uses": [],
"idx": 32
},
{
"path": "../packages/core/src/util/Region.js",
"requires": [
32
],
"uses": [],
"idx": 33
},
{
"path": "../packages/core/src/util/Point.js",
"requires": [
33
],
"uses": [],
"idx": 34
},
{
"path": "../packages/core/src/event/Event.js",
"requires": [
34
],
"uses": [],
"idx": 35
},
{
"path": "../packages/core/src/event/publisher/Dom.js",
"requires": [
31,
35
],
"uses": [
76
],
"idx": 36
},
{
"path": "../packages/core/src/event/publisher/Gesture.js",
"requires": [
19,
34,
36
],
"uses": [
35,
49,
265,
276,
277,
278,
279,
280,
281,
282,
283,
284,
285,
286
],
"idx": 37
},
{
"path": "../packages/core/src/mixin/Templatable.js",
"requires": [
0
],
"uses": [
49
],
"idx": 38
},
{
"path": "../packages/core/src/TaskQueue.js",
"requires": [
19
],
"uses": [],
"idx": 39
},
{
"path": "../packages/core/src/util/sizemonitor/Abstract.js",
"requires": [
38,
39
],
"uses": [],
"idx": 40
},
{
"path": "../packages/core/src/util/sizemonitor/Scroll.js",
"requires": [
40
],
"uses": [
39
],
"idx": 41
},
{
"path": "../packages/core/src/util/sizemonitor/OverflowChange.js",
"requires": [
40
],
"uses": [
39
],
"idx": 42
},
{
"path": "../packages/core/src/util/SizeMonitor.js",
"requires": [
41,
42
],
"uses": [],
"idx": 43
},
{
"path": "../packages/core/src/event/publisher/ElementSize.js",
"requires": [
31,
43
],
"uses": [
39
],
"idx": 44
},
{
"path": "../packages/core/src/util/paintmonitor/Abstract.js",
"requires": [],
"uses": [
49
],
"idx": 45
},
{
"path": "../packages/core/src/util/paintmonitor/CssAnimation.js",
"requires": [
45
],
"uses": [],
"idx": 46
},
{
"path": "../packages/core/src/util/PaintMonitor.js",
"requires": [
46
],
"uses": [],
"idx": 47
},
{
"path": "../packages/core/src/event/publisher/ElementPaint.js",
"requires": [
31,
39,
47
],
"uses": [],
"idx": 48
},
{
"path": "../packages/core/src/dom/Element.js",
"requires": [
4,
25,
28,
29,
30,
36,
37,
44,
48
],
"uses": [
31,
33,
74,
75,
76,
87,
94,
233,
266,
287,
297,
299
],
"idx": 49
},
{
"path": "../packages/core/src/util/Filter.js",
"requires": [],
"uses": [],
"idx": 50
},
{
"path": "../packages/core/src/util/Observable.js",
"requires": [
4
],
"uses": [],
"idx": 51
},
{
"path": "../packages/core/src/util/AbstractMixedCollection.js",
"requires": [
50,
51
],
"uses": [],
"idx": 52
},
{
"path": "../packages/core/src/util/Sorter.js",
"requires": [],
"uses": [],
"idx": 53
},
{
"path": "../packages/core/src/util/Sortable.js",
"requires": [
53
],
"uses": [
55
],
"idx": 54
},
{
"path": "../packages/core/src/util/MixedCollection.js",
"requires": [
52,
54
],
"uses": [],
"idx": 55
},
{
"path": "../packages/core/src/util/TaskRunner.js",
"requires": [],
"uses": [
76
],
"idx": 56
},
{
"path": "../classic/classic/src/fx/target/Target.js",
"requires": [],
"uses": [],
"idx": 57
},
{
"path": "../classic/classic/src/fx/target/Element.js",
"requires": [
57
],
"uses": [],
"idx": 58
},
{
"path": "../classic/classic/src/fx/target/ElementCSS.js",
"requires": [
58
],
"uses": [],
"idx": 59
},
{
"path": "../classic/classic/src/fx/target/CompositeElement.js",
"requires": [
58
],
"uses": [],
"idx": 60
},
{
"path": "../classic/classic/src/fx/target/CompositeElementCSS.js",
"requires": [
59,
60
],
"uses": [],
"idx": 61
},
{
"path": "../classic/classic/src/fx/target/Sprite.js",
"requires": [
57
],
"uses": [],
"idx": 62
},
{
"path": "../classic/classic/src/fx/target/CompositeSprite.js",
"requires": [
62
],
"uses": [],
"idx": 63
},
{
"path": "../classic/classic/src/fx/target/Component.js",
"requires": [
57
],
"uses": [
76
],
"idx": 64
},
{
"path": "../classic/classic/src/fx/Queue.js",
"requires": [
5
],
"uses": [],
"idx": 65
},
{
"path": "../classic/classic/src/fx/Manager.js",
"requires": [
55,
56,
58,
59,
60,
61,
62,
63,
64,
65
],
"uses": [],
"idx": 66
},
{
"path": "../classic/classic/src/fx/Animator.js",
"requires": [
51,
66
],
"uses": [
72
],
"idx": 67
},
{
"path": "../classic/classic/src/fx/CubicBezier.js",
"requires": [],
"uses": [],
"idx": 68
},
{
"path": "../classic/classic/src/fx/Easing.js",
"requires": [
68
],
"uses": [],
"idx": 69
},
{
"path": "../classic/classic/src/fx/DrawPath.js",
"requires": [],
"uses": [],
"idx": 70
},
{
"path": "../classic/classic/src/fx/PropertyHandler.js",
"requires": [
70
],
"uses": [],
"idx": 71
},
{
"path": "../classic/classic/src/fx/Anim.js",
"requires": [
51,
66,
67,
68,
69,
71
],
"uses": [],
"idx": 72
},
{
"path": "../classic/classic/src/util/Animate.js",
"requires": [
66,
72
],
"uses": [],
"idx": 73
},
{
"path": "../packages/core/src/dom/Fly.js",
"requires": [
49
],
"uses": [],
"idx": 74
},
{
"path": "../packages/core/src/dom/CompositeElementLite.js",
"requires": [
74
],
"uses": [
49
],
"idx": 75
},
{
"path": "../packages/core/src/GlobalEvents.js",
"requires": [
4,
49
],
"uses": [],
"idx": 76
},
{
"path": "../packages/core/src/Glyph.js",
"requires": [],
"uses": [],
"idx": 77
},
{
"path": "../packages/core/src/JSON.js",
"requires": [],
"uses": [],
"idx": 78
},
{
"path": "../packages/core/src/mixin/Inheritable.js",
"requires": [
0
],
"uses": [
20
],
"idx": 79
},
{
"path": "../packages/core/src/mixin/Bindable.js",
"requires": [],
"uses": [
12
],
"idx": 80
},
{
"path": "../packages/core/src/mixin/ComponentDelegation.js",
"requires": [
0,
4
],
"uses": [
2
],
"idx": 81
},
{
"path": "../packages/core/src/mixin/Pluggable.js",
"requires": [],
"uses": [],
"idx": 82
},
{
"path": "../packages/core/src/Widget.js",
"requires": [
24,
49,
79,
80,
81,
82
],
"uses": [
20,
23,
90
],
"idx": 83
},
{
"path": "../packages/core/src/ProgressBase.js",
"requires": [],
"uses": [
90
],
"idx": 84
},
{
"path": "../packages/core/src/Progress.js",
"requires": [
83,
84
],
"uses": [],
"idx": 85
},
{
"path": "../packages/core/src/util/Format.js",
"requires": [],
"uses": [
87,
233
],
"idx": 86
},
{
"path": "../packages/core/src/Template.js",
"requires": [
86
],
"uses": [
233
],
"idx": 87
},
{
"path": "../packages/core/src/util/XTemplateParser.js",
"requires": [],
"uses": [],
"idx": 88
},
{
"path": "../packages/core/src/util/XTemplateCompiler.js",
"requires": [
88
],
"uses": [],
"idx": 89
},
{
"path": "../packages/core/src/XTemplate.js",
"requires": [
87,
89
],
"uses": [],
"idx": 90
},
{
"path": "../packages/core/src/app/EventDomain.js",
"requires": [
2
],
"uses": [],
"idx": 91
},
{
"path": "../packages/core/src/app/domain/Component.js",
"requires": [
83,
91
],
"uses": [],
"idx": 92
},
{
"path": "../classic/classic/src/util/ProtoElement.js",
"requires": [],
"uses": [
49,
233
],
"idx": 93
},
{
"path": "../packages/core/src/dom/CompositeElement.js",
"requires": [
75
],
"uses": [],
"idx": 94
},
{
"path": "../packages/core/src/util/CSS.js",
"requires": [],
"uses": [
49
],
"idx": 95
},
{
"path": "../packages/core/src/fx/easing/Abstract.js",
"requires": [],
"uses": [],
"idx": 96
},
{
"path": "../packages/core/src/fx/easing/Linear.js",
"requires": [
96
],
"uses": [],
"idx": 97
},
{
"path": "../packages/core/src/util/translatable/Abstract.js",
"requires": [
24,
97
],
"uses": [
19
],
"idx": 98
},
{
"path": "../packages/core/src/util/translatable/Dom.js",
"requires": [
98
],
"uses": [],
"idx": 99
},
{
"path": "../packages/core/src/util/translatable/ScrollPosition.js",
"requires": [
99
],
"uses": [],
"idx": 100
},
{
"path": "../packages/core/src/scroll/Scroller.js",
"requires": [
12,
24,
95,
100
],
"uses": [
76
],
"idx": 101
},
{
"path": "../classic/classic/src/util/Floating.js",
"requires": [],
"uses": [
20,
49,
76,
376
],
"idx": 102
},
{
"path": "../classic/classic/src/util/ElementContainer.js",
"requires": [],
"uses": [],
"idx": 103
},
{
"path": "../classic/classic/src/util/Renderable.js",
"requires": [
49
],
"uses": [
90,
111,
233
],
"idx": 104
},
{
"path": "../classic/classic/src/state/Provider.js",
"requires": [
51
],
"uses": [],
"idx": 105
},
{
"path": "../classic/classic/src/state/Manager.js",
"requires": [
105
],
"uses": [],
"idx": 106
},
{
"path": "../classic/classic/src/state/Stateful.js",
"requires": [
56,
106
],
"uses": [],
"idx": 107
},
{
"path": "../classic/classic/src/util/Focusable.js",
"requires": [
1
],
"uses": [
20,
23,
35,
49
],
"idx": 108
},
{
"path": "../packages/core/src/mixin/Accessible.js",
"requires": [
0
],
"uses": [],
"idx": 109
},
{
"path": "../packages/core/src/mixin/Keyboard.js",
"requires": [
0
],
"uses": [
35
],
"idx": 110
},
{
"path": "../classic/classic/src/Component.js",
"requires": [
20,
23,
25,
51,
73,
76,
79,
80,
81,
93,
94,
101,
102,
103,
104,
107,
108,
109,
110
],
"uses": [
1,
49,
66,
90,
233,
371,
372,
373,
376,
383,
385,
455,
602,
617,
621
],
"idx": 111
},
{
"path": "../classic/classic/src/layout/container/border/Region.js",
"requires": [],
"uses": [],
"idx": 112
},
{
"path": "../packages/core/src/app/EventBus.js",
"requires": [
92
],
"uses": [
91
],
"idx": 113
},
{
"path": "../packages/core/src/app/domain/Global.js",
"requires": [
76,
91
],
"uses": [],
"idx": 114
},
{
"path": "../packages/core/src/app/BaseController.js",
"requires": [
4,
113,
114
],
"uses": [
171,
172,
212
],
"idx": 115
},
{
"path": "../packages/core/src/app/Util.js",
"requires": [],
"uses": [],
"idx": 116
},
{
"path": "../packages/core/src/util/CollectionKey.js",
"requires": [
3
],
"uses": [],
"idx": 117
},
{
"path": "../packages/core/src/util/Grouper.js",
"requires": [
53
],
"uses": [],
"idx": 118
},
{
"path": "../packages/core/src/util/Collection.js",
"requires": [
4,
50,
53,
117,
118
],
"uses": [
161,
162,
163
],
"idx": 119
},
{
"path": "../packages/core/src/util/ObjectTemplate.js",
"requires": [
90
],
"uses": [],
"idx": 120
},
{
"path": "../packages/core/src/data/schema/Role.js",
"requires": [],
"uses": [
12
],
"idx": 121
},
{
"path": "../packages/core/src/data/schema/Association.js",
"requires": [
121
],
"uses": [],
"idx": 122
},
{
"path": "../packages/core/src/data/schema/OneToOne.js",
"requires": [
122
],
"uses": [],
"idx": 123
},
{
"path": "../packages/core/src/data/schema/ManyToOne.js",
"requires": [
122
],
"uses": [],
"idx": 124
},
{
"path": "../packages/core/src/data/schema/ManyToMany.js",
"requires": [
122
],
"uses": [],
"idx": 125
},
{
"path": "../packages/core/src/util/Inflector.js",
"requires": [],
"uses": [],
"idx": 126
},
{
"path": "../packages/core/src/data/schema/Namer.js",
"requires": [
12,
126
],
"uses": [],
"idx": 127
},
{
"path": "../packages/core/src/data/schema/Schema.js",
"requires": [
12,
120,
123,
124,
125,
127
],
"uses": [],
"idx": 128
},
{
"path": "../packages/core/src/data/AbstractStore.js",
"requires": [
4,
12,
50,
119,
128
],
"uses": [
167
],
"idx": 129
},
{
"path": "../packages/core/src/data/Error.js",
"requires": [],
"uses": [],
"idx": 130
},
{
"path": "../packages/core/src/data/ErrorCollection.js",
"requires": [
55,
130
],
"uses": [
139
],
"idx": 131
},
{
"path": "../packages/core/src/data/operation/Operation.js",
"requires": [],
"uses": [],
"idx": 132
},
{
"path": "../packages/core/src/data/operation/Create.js",
"requires": [
132
],
"uses": [],
"idx": 133
},
{
"path": "../packages/core/src/data/operation/Destroy.js",
"requires": [
132
],
"uses": [],
"idx": 134
},
{
"path": "../packages/core/src/data/operation/Read.js",
"requires": [
132
],
"uses": [],
"idx": 135
},
{
"path": "../packages/core/src/data/operation/Update.js",
"requires": [
132
],
"uses": [],
"idx": 136
},
{
"path": "../packages/core/src/data/SortTypes.js",
"requires": [],
"uses": [],
"idx": 137
},
{
"path": "../packages/core/src/data/validator/Validator.js",
"requires": [
12
],
"uses": [],
"idx": 138
},
{
"path": "../packages/core/src/data/field/Field.js",
"requires": [
12,
137,
138
],
"uses": [],
"idx": 139
},
{
"path": "../packages/core/src/data/field/Boolean.js",
"requires": [
139
],
"uses": [],
"idx": 140
},
{
"path": "../packages/core/src/data/field/Date.js",
"requires": [
139
],
"uses": [],
"idx": 141
},
{
"path": "../packages/core/src/data/field/Integer.js",
"requires": [
139
],
"uses": [],
"idx": 142
},
{
"path": "../packages/core/src/data/field/Number.js",
"requires": [
142
],
"uses": [],
"idx": 143
},
{
"path": "../packages/core/src/data/field/String.js",
"requires": [
139
],
"uses": [],
"idx": 144
},
{
"path": "../packages/core/src/data/identifier/Generator.js",
"requires": [
12
],
"uses": [],
"idx": 145
},
{
"path": "../packages/core/src/data/identifier/Sequential.js",
"requires": [
145
],
"uses": [],
"idx": 146
},
{
"path": "../packages/core/src/data/Model.js",
"requires": [
128,
131,
132,
133,
134,
135,
136,
138,
139,
140,
141,
142,
143,
144,
145,
146
],
"uses": [
12,
149,
232
],
"idx": 147
},
{
"path": "../packages/core/src/data/ResultSet.js",
"requires": [],
"uses": [],
"idx": 148
},
{
"path": "../packages/core/src/data/reader/Reader.js",
"requires": [
4,
12,
22,
90,
148
],
"uses": [
128
],
"idx": 149
},
{
"path": "../packages/core/src/data/writer/Writer.js",
"requires": [
12
],
"uses": [],
"idx": 150
},
{
"path": "../packages/core/src/data/proxy/Proxy.js",
"requires": [
4,
12,
128,
149,
150
],
"uses": [
132,
133,
134,
135,
136,
147,
180
],
"idx": 151
},
{
"path": "../packages/core/src/data/proxy/Client.js",
"requires": [
151
],
"uses": [],
"idx": 152
},
{
"path": "../packages/core/src/data/proxy/Memory.js",
"requires": [
152
],
"uses": [
50,
54
],
"idx": 153
},
{
"path": "../packages/core/src/data/ProxyStore.js",
"requires": [
129,
132,
133,
134,
135,
136,
147,
151,
153
],
"uses": [
128
],
"idx": 154
},
{
"path": "../packages/core/src/data/LocalStore.js",
"requires": [
0
],
"uses": [
119
],
"idx": 155
},
{
"path": "../packages/core/src/data/proxy/Server.js",
"requires": [
151
],
"uses": [
87,
229
],
"idx": 156
},
{
"path": "../packages/core/src/data/proxy/Ajax.js",
"requires": [
18,
156
],
"uses": [],
"idx": 157
},
{
"path": "../packages/core/src/data/reader/Json.js",
"requires": [
78,
149
],
"uses": [],
"idx": 158
},
{
"path": "../packages/core/src/data/writer/Json.js",
"requires": [
150
],
"uses": [],
"idx": 159
},
{
"path": "../packages/core/src/util/Group.js",
"requires": [
119
],
"uses": [],
"idx": 160
},
{
"path": "../packages/core/src/util/SorterCollection.js",
"requires": [
53,
119
],
"uses": [],
"idx": 161
},
{
"path": "../packages/core/src/util/FilterCollection.js",
"requires": [
50,
119
],
"uses": [],
"idx": 162
},
{
"path": "../packages/core/src/util/GroupCollection.js",
"requires": [
119,
160,
161,
162
],
"uses": [],
"idx": 163
},
{
"path": "../packages/core/src/data/Store.js",
"requires": [
1,
147,
154,
155,
157,
158,
159,
163
],
"uses": [
118,
167,
217
],
"idx": 164
},
{
"path": "../packages/core/src/data/reader/Array.js",
"requires": [
158
],
"uses": [],
"idx": 165
},
{
"path": "../packages/core/src/data/ArrayStore.js",
"requires": [
153,
164,
165
],
"uses": [],
"idx": 166
},
{
"path": "../packages/core/src/data/StoreManager.js",
"requires": [
55,
166
],
"uses": [
12,
153,
159,
164,
165
],
"idx": 167
},
{
"path": "../packages/core/src/app/domain/Store.js",
"requires": [
91,
129
],
"uses": [],
"idx": 168
},
{
"path": "../packages/core/src/app/route/Queue.js",
"requires": [],
"uses": [
55
],
"idx": 169
},
{
"path": "../packages/core/src/app/route/Route.js",
"requires": [],
"uses": [
86,
87
],
"idx": 170
},
{
"path": "../packages/core/src/util/History.js",
"requires": [
51
],
"uses": [
362
],
"idx": 171
},
{
"path": "../packages/core/src/app/route/Router.js",
"requires": [
169,
170,
171
],
"uses": [],
"idx": 172
},
{
"path": "../packages/core/src/app/Controller.js",
"requires": [
20,
92,
115,
116,
167,
168,
172
],
"uses": [
23,
128
],
"idx": 173
},
{
"path": "../packages/core/src/app/Application.js",
"requires": [
55,
171,
173
],
"uses": [
172
],
"idx": 174
},
{
"path": "../packages/core/src/app/Profile.js",
"requires": [
4
],
"uses": [
173
],
"idx": 175
},
{
"path": "../packages/core/src/app/domain/View.js",
"requires": [
83,
91
],
"uses": [],
"idx": 176
},
{
"path": "../packages/core/src/app/ViewController.js",
"requires": [
12,
115,
176
],
"uses": [],
"idx": 177
},
{
"path": "../packages/core/src/util/Bag.js",
"requires": [],
"uses": [],
"idx": 178
},
{
"path": "../packages/core/src/util/Scheduler.js",
"requires": [
4,
178
],
"uses": [
76
],
"idx": 179
},
{
"path": "../packages/core/src/data/Batch.js",
"requires": [
4
],
"uses": [],
"idx": 180
},
{
"path": "../packages/core/src/data/matrix/Slice.js",
"requires": [],
"uses": [],
"idx": 181
},
{
"path": "../packages/core/src/data/matrix/Side.js",
"requires": [
181
],
"uses": [],
"idx": 182
},
{
"path": "../packages/core/src/data/matrix/Matrix.js",
"requires": [
182
],
"uses": [],
"idx": 183
},
{
"path": "../packages/core/src/data/session/ChangesVisitor.js",
"requires": [],
"uses": [],
"idx": 184
},
{
"path": "../packages/core/src/data/session/ChildChangesVisitor.js",
"requires": [
184
],
"uses": [],
"idx": 185
},
{
"path": "../packages/core/src/data/session/BatchVisitor.js",
"requires": [],
"uses": [
180
],
"idx": 186
},
{
"path": "../packages/core/src/mixin/Dirty.js",
"requires": [],
"uses": [],
"idx": 187
},
{
"path": "../packages/core/src/data/Session.js",
"requires": [
4,
128,
180,
183,
184,
185,
186,
187
],
"uses": [],
"idx": 188
},
{
"path": "../packages/core/src/util/Schedulable.js",
"requires": [],
"uses": [],
"idx": 189
},
{
"path": "../packages/core/src/app/bind/BaseBinding.js",
"requires": [
189
],
"uses": [],
"idx": 190
},
{
"path": "../packages/core/src/app/bind/Binding.js",
"requires": [
190
],
"uses": [],
"idx": 191
},
{
"path": "../packages/core/src/app/bind/AbstractStub.js",
"requires": [
189,
191
],
"uses": [],
"idx": 192
},
{
"path": "../packages/core/src/app/bind/Stub.js",
"requires": [
191,
192
],
"uses": [
197
],
"idx": 193
},
{
"path": "../packages/core/src/app/bind/LinkStub.js",
"requires": [
193
],
"uses": [],
"idx": 194
},
{
"path": "../packages/core/src/app/bind/RootStub.js",
"requires": [
192,
193,
194
],
"uses": [],
"idx": 195
},
{
"path": "../packages/core/src/app/bind/Multi.js",
"requires": [
190
],
"uses": [],
"idx": 196
},
{
"path": "../packages/core/src/app/bind/Formula.js",
"requires": [
22,
189
],
"uses": [],
"idx": 197
},
{
"path": "../packages/core/src/util/Fly.js",
"requires": [],
"uses": [],
"idx": 198
},
{
"path": "../packages/core/src/parse/Tokenizer.js",
"requires": [
198
],
"uses": [],
"idx": 199
},
{
"path": "../packages/core/src/parse/Symbol.js",
"requires": [],
"uses": [],
"idx": 200
},
{
"path": "../packages/core/src/parse/symbol/Constant.js",
"requires": [
200
],
"uses": [],
"idx": 201
},
{
"path": "../packages/core/src/parse/symbol/Infix.js",
"requires": [
200
],
"uses": [],
"idx": 202
},
{
"path": "../packages/core/src/parse/symbol/InfixRight.js",
"requires": [
202
],
"uses": [],
"idx": 203
},
{
"path": "../packages/core/src/parse/symbol/Paren.js",
"requires": [
200
],
"uses": [],
"idx": 204
},
{
"path": "../packages/core/src/parse/symbol/Prefix.js",
"requires": [
200
],
"uses": [],
"idx": 205
},
{
"path": "../packages/core/src/parse/Parser.js",
"requires": [
198,
199,
201,
203,
204,
205
],
"uses": [
200,
202
],
"idx": 206
},
{
"path": "../packages/core/src/app/bind/Parser.js",
"requires": [
86,
206
],
"uses": [],
"idx": 207
},
{
"path": "../packages/core/src/app/bind/Template.js",
"requires": [
86,
207
],
"uses": [],
"idx": 208
},
{
"path": "../packages/core/src/app/bind/TemplateBinding.js",
"requires": [
190,
196,
208
],
"uses": [],
"idx": 209
},
{
"path": "../packages/core/src/data/ChainedStore.js",
"requires": [
129,
155
],
"uses": [
87,
167
],
"idx": 210
},
{
"path": "../packages/core/src/app/ViewModel.js",
"requires": [
3,
12,
179,
188,
194,
195,
196,
197,
209,
210
],
"uses": [
1,
128
],
"idx": 211
},
{
"path": "../packages/core/src/app/domain/Controller.js",
"requires": [
91,
173
],
"uses": [
115
],
"idx": 212
},
{
"path": "../packages/core/src/direct/Manager.js",
"requires": [
4,
55
],
"uses": [
87
],
"idx": 213
},
{
"path": "../packages/core/src/direct/Provider.js",
"requires": [
4,
213
],
"uses": [
18
],
"idx": 214
},
{
"path": "../packages/core/src/app/domain/Direct.js",
"requires": [
91,
214
],
"uses": [],
"idx": 215
},
{
"path": "../packages/core/src/data/PageMap.js",
"requires": [
22
],
"uses": [],
"idx": 216
},
{
"path": "../packages/core/src/data/BufferedStore.js",
"requires": [
50,
53,
118,
154,
216
],
"uses": [
161,
162,
163
],
"idx": 217
},
{
"path": "../packages/core/src/data/proxy/Direct.js",
"requires": [
156,
213
],
"uses": [],
"idx": 218
},
{
"path": "../packages/core/src/data/DirectStore.js",
"requires": [
164,
218
],
"uses": [],
"idx": 219
},
{
"path": "../packages/core/src/data/JsonP.js",
"requires": [],
"uses": [
76
],
"idx": 220
},
{
"path": "../packages/core/src/data/proxy/JsonP.js",
"requires": [
156,
220
],
"uses": [],
"idx": 221
},
{
"path": "../packages/core/src/data/JsonPStore.js",
"requires": [
158,
164,
221
],
"uses": [],
"idx": 222
},
{
"path": "../packages/core/src/data/JsonStore.js",
"requires": [
157,
158,
159,
164
],
"uses": [],
"idx": 223
},
{
"path": "../packages/core/src/data/ModelManager.js",
"requires": [
128
],
"uses": [
147
],
"idx": 224
},
{
"path": "../packages/core/src/data/NodeInterface.js",
"requires": [
4,
140,
142,
144,
159
],
"uses": [
128
],
"idx": 225
},
{
"path": "../packages/core/src/mixin/Queryable.js",
"requires": [],
"uses": [
23
],
"idx": 226
},
{
"path": "../packages/core/src/data/TreeModel.js",
"requires": [
147,
225,
226
],
"uses": [],
"idx": 227
},
{
"path": "../packages/core/src/data/NodeStore.js",
"requires": [
164,
225,
227
],
"uses": [
147
],
"idx": 228
},
{
"path": "../packages/core/src/data/Request.js",
"requires": [],
"uses": [],
"idx": 229
},
{
"path": "../packages/core/src/data/TreeStore.js",
"requires": [
53,
164,
225,
227
],
"uses": [
147
],
"idx": 230
},
{
"path": "../packages/core/src/data/Types.js",
"requires": [
137
],
"uses": [],
"idx": 231
},
{
"path": "../packages/core/src/data/Validation.js",
"requires": [
147
],
"uses": [],
"idx": 232
},
{
"path": "../packages/core/src/dom/Helper.js",
"requires": [],
"uses": [
87
],
"idx": 233
},
{
"path": "../packages/core/src/dom/Query.js",
"requires": [
21,
233
],
"uses": [
22
],
"idx": 234
},
{
"path": "../packages/core/src/data/reader/Xml.js",
"requires": [
149,
234
],
"uses": [],
"idx": 235
},
{
"path": "../packages/core/src/data/writer/Xml.js",
"requires": [
150
],
"uses": [],
"idx": 236
},
{
"path": "../packages/core/src/data/XmlStore.js",
"requires": [
157,
164,
235,
236
],
"uses": [],
"idx": 237
},
{
"path": "../packages/core/src/data/identifier/Negative.js",
"requires": [
146
],
"uses": [],
"idx": 238
},
{
"path": "../packages/core/src/data/identifier/Uuid.js",
"requires": [
145
],
"uses": [],
"idx": 239
},
{
"path": "../packages/core/src/data/proxy/WebStorage.js",
"requires": [
146,
152
],
"uses": [
53,
87,
148
],
"idx": 240
},
{
"path": "../packages/core/src/data/proxy/LocalStorage.js",
"requires": [
240
],
"uses": [],
"idx": 241
},
{
"path": "../packages/core/src/data/proxy/Rest.js",
"requires": [
157
],
"uses": [],
"idx": 242
},
{
"path": "../packages/core/src/data/proxy/SessionStorage.js",
"requires": [
240
],
"uses": [],
"idx": 243
},
{
"path": "../packages/core/src/data/schema/BelongsTo.js",
"requires": [],
"uses": [],
"idx": 244
},
{
"path": "../packages/core/src/data/schema/HasMany.js",
"requires": [],
"uses": [],
"idx": 245
},
{
"path": "../packages/core/src/data/schema/HasOne.js",
"requires": [],
"uses": [],
"idx": 246
},
{
"path": "../packages/core/src/data/schema/Reference.js",
"requires": [],
"uses": [],
"idx": 247
},
{
"path": "../packages/core/src/data/validator/Bound.js",
"requires": [
138
],
"uses": [
87
],
"idx": 248
},
{
"path": "../packages/core/src/data/validator/Format.js",
"requires": [
138
],
"uses": [],
"idx": 249
},
{
"path": "../packages/core/src/data/validator/Email.js",
"requires": [
249
],
"uses": [],
"idx": 250
},
{
"path": "../packages/core/src/data/validator/List.js",
"requires": [
138
],
"uses": [],
"idx": 251
},
{
"path": "../packages/core/src/data/validator/Exclusion.js",
"requires": [
251
],
"uses": [],
"idx": 252
},
{
"path": "../packages/core/src/data/validator/Inclusion.js",
"requires": [
251
],
"uses": [],
"idx": 253
},
{
"path": "../packages/core/src/data/validator/Length.js",
"requires": [
248
],
"uses": [],
"idx": 254
},
{
"path": "../packages/core/src/data/validator/Presence.js",
"requires": [
138
],
"uses": [],
"idx": 255
},
{
"path": "../packages/core/src/data/validator/Range.js",
"requires": [
248
],
"uses": [],
"idx": 256
},
{
"path": "../packages/core/src/direct/Event.js",
"requires": [],
"uses": [],
"idx": 257
},
{
"path": "../packages/core/src/direct/RemotingEvent.js",
"requires": [
257
],
"uses": [
213
],
"idx": 258
},
{
"path": "../packages/core/src/direct/ExceptionEvent.js",
"requires": [
258
],
"uses": [],
"idx": 259
},
{
"path": "../packages/core/src/direct/JsonProvider.js",
"requires": [
214
],
"uses": [
213,
259
],
"idx": 260
},
{
"path": "../packages/core/src/direct/PollingProvider.js",
"requires": [
18,
56,
259,
260
],
"uses": [
213,
362
],
"idx": 261
},
{
"path": "../packages/core/src/direct/RemotingMethod.js",
"requires": [],
"uses": [],
"idx": 262
},
{
"path": "../packages/core/src/direct/Transaction.js",
"requires": [],
"uses": [],
"idx": 263
},
{
"path": "../packages/core/src/direct/RemotingProvider.js",
"requires": [
1,
55,
213,
260,
262,
263
],
"uses": [
78,
259
],
"idx": 264
},
{
"path": "../packages/core/src/dom/GarbageCollector.js",
"requires": [],
"uses": [
49
],
"idx": 265
},
{
"path": "../packages/core/src/dom/TouchAction.js",
"requires": [
34,
49
],
"uses": [],
"idx": 266
},
{
"path": "../packages/core/src/drag/Constraint.js",
"requires": [
12
],
"uses": [],
"idx": 267
},
{
"path": "../packages/core/src/drag/Info.js",
"requires": [
10
],
"uses": [],
"idx": 268
},
{
"path": "../packages/core/src/drag/Item.js",
"requires": [
3,
4
],
"uses": [],
"idx": 269
},
{
"path": "../packages/core/src/drag/Manager.js",
"requires": [],
"uses": [
49,
268
],
"idx": 270
},
{
"path": "../packages/core/src/drag/Source.js",
"requires": [
76,
267,
269
],
"uses": [
12,
268
],
"idx": 271
},
{
"path": "../packages/core/src/drag/Target.js",
"requires": [
269,
270
],
"uses": [],
"idx": 272
},
{
"path": "../packages/core/src/drag/proxy/None.js",
"requires": [
12
],
"uses": [],
"idx": 273
},
{
"path": "../packages/core/src/drag/proxy/Original.js",
"requires": [
273
],
"uses": [],
"idx": 274
},
{
"path": "../packages/core/src/drag/proxy/Placeholder.js",
"requires": [
273
],
"uses": [],
"idx": 275
},
{
"path": "../packages/core/src/event/gesture/Recognizer.js",
"requires": [
3,
37
],
"uses": [],
"idx": 276
},
{
"path": "../packages/core/src/event/gesture/SingleTouch.js",
"requires": [
276
],
"uses": [],
"idx": 277
},
{
"path": "../packages/core/src/event/gesture/DoubleTap.js",
"requires": [
277
],
"uses": [
49
],
"idx": 278
},
{
"path": "../packages/core/src/event/gesture/Drag.js",
"requires": [
277
],
"uses": [
49
],
"idx": 279
},
{
"path": "../packages/core/src/event/gesture/Swipe.js",
"requires": [
277
],
"uses": [],
"idx": 280
},
{
"path": "../packages/core/src/event/gesture/EdgeSwipe.js",
"requires": [
280
],
"uses": [
49
],
"idx": 281
},
{
"path": "../packages/core/src/event/gesture/LongPress.js",
"requires": [
277
],
"uses": [
37,
49,
279
],
"idx": 282
},
{
"path": "../packages/core/src/event/gesture/MultiTouch.js",
"requires": [
276
],
"uses": [],
"idx": 283
},
{
"path": "../packages/core/src/event/gesture/Pinch.js",
"requires": [
283
],
"uses": [],
"idx": 284
},
{
"path": "../packages/core/src/event/gesture/Rotate.js",
"requires": [
283
],
"uses": [],
"idx": 285
},
{
"path": "../packages/core/src/event/gesture/Tap.js",
"requires": [
277
],
"uses": [
49
],
"idx": 286
},
{
"path": "../packages/core/src/event/publisher/Focus.js",
"requires": [
36,
49,
76
],
"uses": [
35
],
"idx": 287
},
{
"path": "../packages/core/src/fx/State.js",
"requires": [],
"uses": [],
"idx": 288
},
{
"path": "../packages/core/src/fx/animation/Abstract.js",
"requires": [
24,
288
],
"uses": [],
"idx": 289
},
{
"path": "../packages/core/src/fx/animation/Slide.js",
"requires": [
289
],
"uses": [],
"idx": 290
},
{
"path": "../packages/core/src/fx/animation/SlideOut.js",
"requires": [
290
],
"uses": [],
"idx": 291
},
{
"path": "../packages/core/src/fx/animation/Fade.js",
"requires": [
289
],
"uses": [],
"idx": 292
},
{
"path": "../packages/core/src/fx/animation/FadeOut.js",
"requires": [
292
],
"uses": [],
"idx": 293
},
{
"path": "../packages/core/src/fx/animation/Flip.js",
"requires": [
289
],
"uses": [],
"idx": 294
},
{
"path": "../packages/core/src/fx/animation/Pop.js",
"requires": [
289
],
"uses": [],
"idx": 295
},
{
"path": "../packages/core/src/fx/animation/PopOut.js",
"requires": [
295
],
"uses": [],
"idx": 296
},
{
"path": "../packages/core/src/fx/Animation.js",
"requires": [
290,
291,
292,
293,
294,
295,
296
],
"uses": [
289
],
"idx": 297
},
{
"path": "../packages/core/src/fx/runner/Css.js",
"requires": [
24,
297
],
"uses": [],
"idx": 298
},
{
"path": "../packages/core/src/fx/runner/CssTransition.js",
"requires": [
19,
298
],
"uses": [
297
],
"idx": 299
},
{
"path": "../packages/core/src/fx/Runner.js",
"requires": [
299
],
"uses": [],
"idx": 300
},
{
"path": "../packages/core/src/fx/animation/Cube.js",
"requires": [
289
],
"uses": [],
"idx": 301
},
{
"path": "../packages/core/src/fx/animation/Wipe.js",
"requires": [
297
],
"uses": [],
"idx": 302
},
{
"path": "../packages/core/src/fx/animation/WipeOut.js",
"requires": [
302
],
"uses": [],
"idx": 303
},
{
"path": "../packages/core/src/fx/easing/Bounce.js",
"requires": [
96
],
"uses": [],
"idx": 304
},
{
"path": "../packages/core/src/fx/easing/Momentum.js",
"requires": [
96
],
"uses": [],
"idx": 305
},
{
"path": "../packages/core/src/fx/easing/BoundMomentum.js",
"requires": [
96,
304,
305
],
"uses": [],
"idx": 306
},
{
"path": "../packages/core/src/fx/easing/EaseIn.js",
"requires": [
97
],
"uses": [],
"idx": 307
},
{
"path": "../packages/core/src/fx/easing/EaseOut.js",
"requires": [
97
],
"uses": [],
"idx": 308
},
{
"path": "../packages/core/src/fx/easing/Easing.js",
"requires": [
97
],
"uses": [],
"idx": 309
},
{
"path": "../packages/core/src/fx/layout/card/Abstract.js",
"requires": [
24
],
"uses": [],
"idx": 310
},
{
"path": "../packages/core/src/fx/layout/card/Style.js",
"requires": [
297,
310
],
"uses": [
299
],
"idx": 311
},
{
"path": "../packages/core/src/fx/layout/card/Slide.js",
"requires": [
311
],
"uses": [],
"idx": 312
},
{
"path": "../packages/core/src/fx/layout/card/Cover.js",
"requires": [
311
],
"uses": [],
"idx": 313
},
{
"path": "../packages/core/src/fx/layout/card/Reveal.js",
"requires": [
311
],
"uses": [],
"idx": 314
},
{
"path": "../packages/core/src/fx/layout/card/Fade.js",
"requires": [
311
],
"uses": [],
"idx": 315
},
{
"path": "../packages/core/src/fx/layout/card/Flip.js",
"requires": [
311
],
"uses": [],
"idx": 316
},
{
"path": "../packages/core/src/fx/layout/card/Pop.js",
"requires": [
311
],
"uses": [],
"idx": 317
},
{
"path": "../packages/core/src/fx/layout/card/Scroll.js",
"requires": [
97,
310
],
"uses": [
19
],
"idx": 318
},
{
"path": "../packages/core/src/fx/layout/Card.js",
"requires": [
312,
313,
314,
315,
316,
317,
318
],
"uses": [
310
],
"idx": 319
},
{
"path": "../packages/core/src/fx/layout/card/Cube.js",
"requires": [
311
],
"uses": [],
"idx": 320
},
{
"path": "../packages/core/src/fx/layout/card/ScrollCover.js",
"requires": [
318
],
"uses": [],
"idx": 321
},
{
"path": "../packages/core/src/fx/layout/card/ScrollReveal.js",
"requires": [
318
],
"uses": [],
"idx": 322
},
{
"path": "../packages/core/src/fx/runner/CssAnimation.js",
"requires": [
298
],
"uses": [
297
],
"idx": 323
},
{
"path": "../packages/core/src/list/AbstractTreeItem.js",
"requires": [
83
],
"uses": [],
"idx": 324
},
{
"path": "../packages/core/src/list/RootTreeItem.js",
"requires": [
324
],
"uses": [],
"idx": 325
},
{
"path": "../packages/core/src/list/TreeItem.js",
"requires": [
83,
324
],
"uses": [],
"idx": 326
},
{
"path": "../packages/core/src/list/Tree.js",
"requires": [
83,
325,
326
],
"uses": [
167
],
"idx": 327
},
{
"path": "../packages/core/src/mixin/ConfigState.js",
"requires": [
0
],
"uses": [],
"idx": 328
},
{
"path": "../packages/core/src/mixin/Container.js",
"requires": [
0
],
"uses": [
20
],
"idx": 329
},
{
"path": "../packages/core/src/mixin/Hookable.js",
"requires": [
0
],
"uses": [],
"idx": 330
},
{
"path": "../packages/core/src/mixin/Mashup.js",
"requires": [
0
],
"uses": [],
"idx": 331
},
{
"path": "../packages/core/src/mixin/Responsive.js",
"requires": [
0,
76
],
"uses": [
49
],
"idx": 332
},
{
"path": "../packages/core/src/mixin/Selectable.js",
"requires": [
0
],
"uses": [
55
],
"idx": 333
},
{
"path": "../packages/core/src/mixin/StyleCacher.js",
"requires": [
0
],
"uses": [],
"idx": 334
},
{
"path": "../packages/core/src/mixin/Traversable.js",
"requires": [
0
],
"uses": [],
"idx": 335
},
{
"path": "../packages/core/src/perf/Accumulator.js",
"requires": [
90
],
"uses": [],
"idx": 336
},
{
"path": "../packages/core/src/perf/Monitor.js",
"requires": [
336
],
"uses": [],
"idx": 337
},
{
"path": "../packages/core/src/plugin/Abstract.js",
"requires": [],
"uses": [],
"idx": 338
},
{
"path": "../packages/core/src/plugin/LazyItems.js",
"requires": [
338
],
"uses": [],
"idx": 339
},
{
"path": "../packages/core/src/plugin/MousEnter.js",
"requires": [
338
],
"uses": [],
"idx": 340
},
{
"path": "../packages/core/src/sparkline/Shape.js",
"requires": [],
"uses": [],
"idx": 341
},
{
"path": "../packages/core/src/sparkline/CanvasBase.js",
"requires": [
341
],
"uses": [],
"idx": 342
},
{
"path": "../packages/core/src/sparkline/CanvasCanvas.js",
"requires": [
342
],
"uses": [],
"idx": 343
},
{
"path": "../packages/core/src/sparkline/VmlCanvas.js",
"requires": [
342
],
"uses": [],
"idx": 344
},
{
"path": "../packages/core/src/util/Color.js",
"requires": [],
"uses": [],
"idx": 345
},
{
"path": "../packages/core/src/sparkline/Base.js",
"requires": [
83,
90,
343,
344,
345
],
"uses": [],
"idx": 346
},
{
"path": "../packages/core/src/sparkline/BarBase.js",
"requires": [
346
],
"uses": [],
"idx": 347
},
{
"path": "../packages/core/src/sparkline/RangeMap.js",
"requires": [],
"uses": [],
"idx": 348
},
{
"path": "../packages/core/src/sparkline/Bar.js",
"requires": [
347,
348
],
"uses": [],
"idx": 349
},
{
"path": "../packages/core/src/sparkline/Box.js",
"requires": [
346
],
"uses": [],
"idx": 350
},
{
"path": "../packages/core/src/sparkline/Bullet.js",
"requires": [
346
],
"uses": [],
"idx": 351
},
{
"path": "../packages/core/src/sparkline/Discrete.js",
"requires": [
347
],
"uses": [],
"idx": 352
},
{
"path": "../packages/core/src/sparkline/Line.js",
"requires": [
346,
348
],
"uses": [],
"idx": 353
},
{
"path": "../packages/core/src/sparkline/Pie.js",
"requires": [
346
],
"uses": [],
"idx": 354
},
{
"path": "../packages/core/src/sparkline/TriState.js",
"requires": [
347,
348
],
"uses": [],
"idx": 355
},
{
"path": "../packages/core/src/util/Base64.js",
"requires": [],
"uses": [],
"idx": 356
},
{
"path": "../packages/core/src/util/DelimitedValue.js",
"requires": [],
"uses": [],
"idx": 357
},
{
"path": "../packages/core/src/util/CSV.js",
"requires": [
357
],
"uses": [],
"idx": 358
},
{
"path": "../packages/core/src/util/ItemCollection.js",
"requires": [
55
],
"uses": [],
"idx": 359
},
{
"path": "../packages/core/src/util/LocalStorage.js",
"requires": [],
"uses": [],
"idx": 360
},
{
"path": "../packages/core/src/util/TSV.js",
"requires": [
357
],
"uses": [],
"idx": 361
},
{
"path": "../packages/core/src/util/TaskManager.js",
"requires": [
56
],
"uses": [],
"idx": 362
},
{
"path": "../packages/core/src/util/TextMetrics.js",
"requires": [
49
],
"uses": [],
"idx": 363
},
{
"path": "../packages/core/src/util/translatable/CssTransform.js",
"requires": [
99
],
"uses": [],
"idx": 364
},
{
"path": "../packages/core/src/util/translatable/ScrollParent.js",
"requires": [
99
],
"uses": [],
"idx": 365
},
{
"path": "../packages/core/src/util/translatable/CssPosition.js",
"requires": [
99
],
"uses": [],
"idx": 366
},
{
"path": "../packages/core/src/util/Translatable.js",
"requires": [
100,
364,
365,
366
],
"uses": [],
"idx": 367
},
{
"path": "../packages/core/src/util/paintmonitor/OverflowChange.js",
"requires": [
45
],
"uses": [],
"idx": 368
},
{
"path": "../classic/classic/src/Action.js",
"requires": [],
"uses": [],
"idx": 369
},
{
"path": "../classic/classic/src/ElementLoader.js",
"requires": [
51
],
"uses": [
17,
18
],
"idx": 370
},
{
"path": "../classic/classic/src/ComponentLoader.js",
"requires": [
370
],
"uses": [],
"idx": 371
},
{
"path": "../classic/classic/src/layout/SizeModel.js",
"requires": [],
"uses": [],
"idx": 372
},
{
"path": "../classic/classic/src/layout/Layout.js",
"requires": [
12,
90,
372
],
"uses": [
49,
602
],
"idx": 373
},
{
"path": "../classic/classic/src/layout/container/Container.js",
"requires": [
90,
103,
373
],
"uses": [
233
],
"idx": 374
},
{
"path": "../classic/classic/src/layout/container/Auto.js",
"requires": [
374
],
"uses": [
90
],
"idx": 375
},
{
"path": "../classic/classic/src/ZIndexManager.js",
"requires": [
76,
161,
162
],
"uses": [
49,
119
],
"idx": 376
},
{
"path": "../classic/classic/src/container/Container.js",
"requires": [
55,
111,
226,
329,
359,
369,
375,
376
],
"uses": [
12,
20,
23,
49
],
"idx": 377
},
{
"path": "../classic/classic/src/layout/container/Editor.js",
"requires": [
374
],
"uses": [],
"idx": 378
},
{
"path": "../classic/classic/src/Editor.js",
"requires": [
377,
378
],
"uses": [
1,
20
],
"idx": 379
},
{
"path": "../classic/classic/src/EventManager.js",
"requires": [],
"uses": [
76
],
"idx": 380
},
{
"path": "../classic/classic/src/Img.js",
"requires": [
77,
111
],
"uses": [],
"idx": 381
},
{
"path": "../classic/classic/src/util/StoreHolder.js",
"requires": [
167
],
"uses": [],
"idx": 382
},
{
"path": "../classic/classic/src/LoadMask.js",
"requires": [
111,
382
],
"uses": [
49,
76,
167
],
"idx": 383
},
{
"path": "../classic/classic/src/layout/component/Component.js",
"requires": [
373
],
"uses": [],
"idx": 384
},
{
"path": "../classic/classic/src/layout/component/Auto.js",
"requires": [
384
],
"uses": [],
"idx": 385
},
{
"path": "../classic/classic/src/layout/component/ProgressBar.js",
"requires": [
385
],
"uses": [],
"idx": 386
},
{
"path": "../classic/classic/src/ProgressBar.js",
"requires": [
84,
87,
94,
111,
362,
386
],
"uses": [
72
],
"idx": 387
},
{
"path": "../classic/classic/src/dom/ButtonElement.js",
"requires": [
49
],
"uses": [],
"idx": 388
},
{
"path": "../classic/classic/src/button/Manager.js",
"requires": [],
"uses": [],
"idx": 389
},
{
"path": "../classic/classic/src/menu/Manager.js",
"requires": [],
"uses": [
20,
101,
111,
565
],
"idx": 390
},
{
"path": "../classic/classic/src/util/ClickRepeater.js",
"requires": [
51
],
"uses": [],
"idx": 391
},
{
"path": "../classic/classic/src/button/Button.js",
"requires": [
77,
111,
226,
363,
388,
389,
390,
391
],
"uses": [
509
],
"idx": 392
},
{
"path": "../classic/classic/src/button/Split.js",
"requires": [
392
],
"uses": [
49
],
"idx": 393
},
{
"path": "../classic/classic/src/button/Cycle.js",
"requires": [
393
],
"uses": [],
"idx": 394
},
{
"path": "../classic/classic/src/layout/container/SegmentedButton.js",
"requires": [
374
],
"uses": [],
"idx": 395
},
{
"path": "../classic/classic/src/button/Segmented.js",
"requires": [
377,
392,
395
],
"uses": [],
"idx": 396
},
{
"path": "../classic/classic/src/panel/Bar.js",
"requires": [
377
],
"uses": [],
"idx": 397
},
{
"path": "../classic/classic/src/panel/Title.js",
"requires": [
77,
111
],
"uses": [],
"idx": 398
},
{
"path": "../classic/classic/src/panel/Tool.js",
"requires": [
77,
111
],
"uses": [
509
],
"idx": 399
},
{
"path": "../classic/classic/src/util/KeyMap.js",
"requires": [],
"uses": [],
"idx": 400
},
{
"path": "../classic/classic/src/util/KeyNav.js",
"requires": [
400
],
"uses": [],
"idx": 401
},
{
"path": "../classic/classic/src/util/FocusableContainer.js",
"requires": [
0,
401
],
"uses": [
111
],
"idx": 402
},
{
"path": "../classic/classic/src/panel/Header.js",
"requires": [
385,
397,
398,
399,
402
],
"uses": [
20
],
"idx": 403
},
{
"path": "../classic/classic/src/layout/container/boxOverflow/None.js",
"requires": [
12
],
"uses": [],
"idx": 404
},
{
"path": "../classic/classic/src/layout/container/boxOverflow/Scroller.js",
"requires": [
4,
49,
391,
404
],
"uses": [],
"idx": 405
},
{
"path": "../classic/classic/src/dd/DragDropManager.js",
"requires": [
33,
34
],
"uses": [
49,
438,
509
],
"idx": 406
},
{
"path": "../classic/classic/src/resizer/Splitter.js",
"requires": [
90,
111
],
"uses": [
434
],
"idx": 407
},
{
"path": "../classic/classic/src/layout/container/Box.js",
"requires": [
86,
374,
404,
405,
406,
407
],
"uses": [
12,
372,
385
],
"idx": 408
},
{
"path": "../classic/classic/src/layout/container/HBox.js",
"requires": [
408
],
"uses": [],
"idx": 409
},
{
"path": "../classic/classic/src/layout/container/VBox.js",
"requires": [
408
],
"uses": [],
"idx": 410
},
{
"path": "../classic/classic/src/toolbar/Toolbar.js",
"requires": [
377,
385,
402,
409,
410
],
"uses": [
491,
512,
637,
638
],
"idx": 411
},
{
"path": "../classic/classic/src/dd/DragDrop.js",
"requires": [
406
],
"uses": [
49
],
"idx": 412
},
{
"path": "../classic/classic/src/dd/DD.js",
"requires": [
406,
412
],
"uses": [
49
],
"idx": 413
},
{
"path": "../classic/classic/src/dd/DDProxy.js",
"requires": [
413
],
"uses": [
406
],
"idx": 414
},
{
"path": "../classic/classic/src/dd/StatusProxy.js",
"requires": [
111
],
"uses": [],
"idx": 415
},
{
"path": "../classic/classic/src/dd/DragSource.js",
"requires": [
406,
414,
415
],
"uses": [
385
],
"idx": 416
},
{
"path": "../classic/classic/src/panel/Proxy.js",
"requires": [],
"uses": [
49
],
"idx": 417
},
{
"path": "../classic/classic/src/panel/DD.js",
"requires": [
416,
417
],
"uses": [],
"idx": 418
},
{
"path": "../classic/classic/src/layout/component/Dock.js",
"requires": [
384
],
"uses": [
23,
49,
372
],
"idx": 419
},
{
"path": "../classic/classic/src/util/Memento.js",
"requires": [],
"uses": [],
"idx": 420
},
{
"path": "../classic/classic/src/container/DockingContainer.js",
"requires": [
49,
55
],
"uses": [
23,
233,
359
],
"idx": 421
},
{
"path": "../classic/classic/src/panel/Panel.js",
"requires": [
49,
55,
72,
90,
377,
403,
411,
418,
419,
420,
421
],
"uses": [
1,
20,
33,
86,
93,
94,
111,
233,
375,
385,
399,
401,
455
],
"idx": 422
},
{
"path": "../classic/classic/src/layout/container/Table.js",
"requires": [
374
],
"uses": [],
"idx": 423
},
{
"path": "../classic/classic/src/container/ButtonGroup.js",
"requires": [
402,
422,
423
],
"uses": [],
"idx": 424
},
{
"path": "../classic/classic/src/container/Monitor.js",
"requires": [],
"uses": [
23,
55
],
"idx": 425
},
{
"path": "../classic/classic/src/plugin/Responsive.js",
"requires": [
332
],
"uses": [],
"idx": 426
},
{
"path": "../classic/classic/src/plugin/Viewport.js",
"requires": [
426
],
"uses": [
49,
372
],
"idx": 427
},
{
"path": "../classic/classic/src/container/Viewport.js",
"requires": [
332,
377,
427
],
"uses": [],
"idx": 428
},
{
"path": "../classic/classic/src/layout/container/Anchor.js",
"requires": [
375
],
"uses": [],
"idx": 429
},
{
"path": "../classic/classic/src/dashboard/Panel.js",
"requires": [
422
],
"uses": [
20
],
"idx": 430
},
{
"path": "../classic/classic/src/dashboard/Column.js",
"requires": [
377,
429,
430
],
"uses": [],
"idx": 431
},
{
"path": "../classic/classic/src/layout/container/Column.js",
"requires": [
375
],
"uses": [],
"idx": 432
},
{
"path": "../classic/classic/src/dd/DragTracker.js",
"requires": [
51
],
"uses": [
20,
33,
401
],
"idx": 433
},
{
"path": "../classic/classic/src/resizer/SplitterTracker.js",
"requires": [
33,
433
],
"uses": [
49,
97
],
"idx": 434
},
{
"path": "../classic/classic/src/layout/container/ColumnSplitterTracker.js",
"requires": [
434
],
"uses": [],
"idx": 435
},
{
"path": "../classic/classic/src/layout/container/ColumnSplitter.js",
"requires": [
407,
435
],
"uses": [],
"idx": 436
},
{
"path": "../classic/classic/src/layout/container/Dashboard.js",
"requires": [
432,
436
],
"uses": [
385
],
"idx": 437
},
{
"path": "../classic/classic/src/dd/DDTarget.js",
"requires": [
412
],
"uses": [],
"idx": 438
},
{
"path": "../classic/classic/src/dd/ScrollManager.js",
"requires": [
406
],
"uses": [],
"idx": 439
},
{
"path": "../classic/classic/src/dd/DropTarget.js",
"requires": [
438,
439
],
"uses": [],
"idx": 440
},
{
"path": "../classic/classic/src/dashboard/DropZone.js",
"requires": [
440
],
"uses": [],
"idx": 441
},
{
"path": "../classic/classic/src/dashboard/Part.js",
"requires": [
3,
12,
120
],
"uses": [],
"idx": 442
},
{
"path": "../classic/classic/src/dashboard/Dashboard.js",
"requires": [
422,
431,
437,
441,
442
],
"uses": [
12,
106,
119
],
"idx": 443
},
{
"path": "../classic/classic/src/dd/DragZone.js",
"requires": [
416
],
"uses": [
439,
445
],
"idx": 444
},
{
"path": "../classic/classic/src/dd/Registry.js",
"requires": [],
"uses": [],
"idx": 445
},
{
"path": "../classic/classic/src/dd/DropZone.js",
"requires": [
440,
445
],
"uses": [
406
],
"idx": 446
},
{
"path": "../classic/classic/src/dom/Layer.js",
"requires": [
49
],
"uses": [
233
],
"idx": 447
},
{
"path": "../classic/classic/src/enums.js",
"requires": [],
"uses": [],
"idx": 448
},
{
"path": "../classic/classic/src/event/publisher/MouseEnterLeave.js",
"requires": [
36
],
"uses": [],
"idx": 449
},
{
"path": "../classic/classic/src/flash/Component.js",
"requires": [
111
],
"uses": [],
"idx": 450
},
{
"path": "../classic/classic/src/form/action/Action.js",
"requires": [],
"uses": [],
"idx": 451
},
{
"path": "../classic/classic/src/form/action/Load.js",
"requires": [
17,
451
],
"uses": [
18
],
"idx": 452
},
{
"path": "../classic/classic/src/form/action/Submit.js",
"requires": [
451
],
"uses": [
18,
233
],
"idx": 453
},
{
"path": "../classic/classic/src/form/action/StandardSubmit.js",
"requires": [
453
],
"uses": [],
"idx": 454
},
{
"path": "../classic/classic/src/util/ComponentDragger.js",
"requires": [
433
],
"uses": [
33,
49
],
"idx": 455
},
{
"path": "../classic/classic/src/window/Window.js",
"requires": [
33,
422,
455
],
"uses": [],
"idx": 456
},
{
"path": "../classic/classic/src/form/Labelable.js",
"requires": [
0,
90
],
"uses": [
49,
508
],
"idx": 457
},
{
"path": "../classic/classic/src/form/field/Field.js",
"requires": [],
"uses": [],
"idx": 458
},
{
"path": "../classic/classic/src/form/field/Base.js",
"requires": [
1,
90,
111,
457,
458
],
"uses": [
87,
233
],
"idx": 459
},
{
"path": "../classic/classic/src/form/field/VTypes.js",
"requires": [],
"uses": [],
"idx": 460
},
{
"path": "../classic/classic/src/form/trigger/Trigger.js",
"requires": [
12,
391
],
"uses": [
49,
90
],
"idx": 461
},
{
"path": "../classic/classic/src/form/field/Text.js",
"requires": [
363,
459,
460,
461
],
"uses": [
86,
87,
94
],
"idx": 462
},
{
"path": "../classic/classic/src/form/field/TextArea.js",
"requires": [
1,
90,
462
],
"uses": [
86,
363
],
"idx": 463
},
{
"path": "../classic/classic/src/window/MessageBox.js",
"requires": [
387,
392,
409,
411,
429,
456,
462,
463
],
"uses": [
111,
377,
385,
386
],
"idx": 464
},
{
"path": "../classic/classic/src/form/Basic.js",
"requires": [
1,
51,
55,
131,
452,
453,
454,
464
],
"uses": [
425
],
"idx": 465
},
{
"path": "../classic/classic/src/form/FieldAncestor.js",
"requires": [
0,
425
],
"uses": [],
"idx": 466
},
{
"path": "../classic/classic/src/layout/component/field/FieldContainer.js",
"requires": [
385
],
"uses": [],
"idx": 467
},
{
"path": "../classic/classic/src/form/FieldContainer.js",
"requires": [
377,
457,
466,
467
],
"uses": [],
"idx": 468
},
{
"path": "../classic/classic/src/layout/container/CheckboxGroup.js",
"requires": [
374
],
"uses": [
233
],
"idx": 469
},
{
"path": "../classic/classic/src/form/CheckboxManager.js",
"requires": [
55
],
"uses": [],
"idx": 470
},
{
"path": "../classic/classic/src/form/field/Checkbox.js",
"requires": [
90,
459,
470
],
"uses": [],
"idx": 471
},
{
"path": "../classic/classic/src/form/CheckboxGroup.js",
"requires": [
458,
459,
468,
469,
471
],
"uses": [],
"idx": 472
},
{
"path": "../classic/classic/src/form/FieldSet.js",
"requires": [
377,
466
],
"uses": [
49,
93,
111,
233,
385,
399,
429,
471,
605
],
"idx": 473
},
{
"path": "../classic/classic/src/form/Label.js",
"requires": [
86,
111
],
"uses": [],
"idx": 474
},
{
"path": "../classic/classic/src/form/Panel.js",
"requires": [
56,
422,
465,
466
],
"uses": [],
"idx": 475
},
{
"path": "../classic/classic/src/form/RadioManager.js",
"requires": [
55
],
"uses": [],
"idx": 476
},
{
"path": "../classic/classic/src/form/field/Radio.js",
"requires": [
471,
476
],
"uses": [],
"idx": 477
},
{
"path": "../classic/classic/src/form/RadioGroup.js",
"requires": [
472,
477
],
"uses": [
476
],
"idx": 478
},
{
"path": "../classic/classic/src/form/action/DirectAction.js",
"requires": [
0
],
"uses": [
213
],
"idx": 479
},
{
"path": "../classic/classic/src/form/action/DirectLoad.js",
"requires": [
213,
452,
479
],
"uses": [],
"idx": 480
},
{
"path": "../classic/classic/src/form/action/DirectSubmit.js",
"requires": [
213,
453,
479
],
"uses": [],
"idx": 481
},
{
"path": "../classic/classic/src/form/field/Picker.js",
"requires": [
401,
462
],
"uses": [],
"idx": 482
},
{
"path": "../classic/classic/src/selection/Model.js",
"requires": [
4,
12,
178,
382
],
"uses": [],
"idx": 483
},
{
"path": "../classic/classic/src/selection/DataViewModel.js",
"requires": [
401,
483
],
"uses": [],
"idx": 484
},
{
"path": "../classic/classic/src/view/NavigationModel.js",
"requires": [
12,
51,
382
],
"uses": [
401
],
"idx": 485
},
{
"path": "../classic/classic/src/view/AbstractView.js",
"requires": [
75,
95,
111,
382,
383,
484,
485
],
"uses": [
12,
19,
49,
87,
90,
167,
233,
362
],
"idx": 486
},
{
"path": "../classic/classic/src/view/View.js",
"requires": [
486
],
"uses": [],
"idx": 487
},
{
"path": "../classic/classic/src/view/BoundListKeyNav.js",
"requires": [
485
],
"uses": [
35,
401
],
"idx": 488
},
{
"path": "../classic/classic/src/layout/component/BoundList.js",
"requires": [
385
],
"uses": [],
"idx": 489
},
{
"path": "../classic/classic/src/toolbar/Item.js",
"requires": [
111,
411
],
"uses": [],
"idx": 490
},
{
"path": "../classic/classic/src/toolbar/TextItem.js",
"requires": [
90,
411,
490
],
"uses": [],
"idx": 491
},
{
"path": "../classic/classic/src/form/trigger/Spinner.js",
"requires": [
461
],
"uses": [],
"idx": 492
},
{
"path": "../classic/classic/src/form/field/Spinner.js",
"requires": [
401,
462,
492
],
"uses": [],
"idx": 493
},
{
"path": "../classic/classic/src/form/field/Number.js",
"requires": [
493
],
"uses": [
86,
87
],
"idx": 494
},
{
"path": "../classic/classic/src/toolbar/Paging.js",
"requires": [
382,
411,
491,
494
],
"uses": [
87,
385,
492
],
"idx": 495
},
{
"path": "../classic/classic/src/view/BoundList.js",
"requires": [
49,
226,
487,
488,
489,
495
],
"uses": [
90,
385
],
"idx": 496
},
{
"path": "../classic/classic/src/form/field/ComboBox.js",
"requires": [
1,
167,
382,
482,
496
],
"uses": [
49,
50,
90,
119,
147,
162,
233,
401,
484,
488,
489
],
"idx": 497
},
{
"path": "../classic/classic/src/picker/Month.js",
"requires": [
90,
111,
391,
392
],
"uses": [
385
],
"idx": 498
},
{
"path": "../classic/classic/src/picker/Date.js",
"requires": [
66,
90,
111,
391,
392,
393,
401,
498
],
"uses": [
87,
233,
385
],
"idx": 499
},
{
"path": "../classic/classic/src/form/field/Date.js",
"requires": [
482,
499
],
"uses": [
87,
385
],
"idx": 500
},
{
"path": "../classic/classic/src/form/field/Display.js",
"requires": [
86,
90,
459
],
"uses": [],
"idx": 501
},
{
"path": "../classic/classic/src/form/field/FileButton.js",
"requires": [
392
],
"uses": [],
"idx": 502
},
{
"path": "../classic/classic/src/form/trigger/Component.js",
"requires": [
461
],
"uses": [],
"idx": 503
},
{
"path": "../classic/classic/src/form/field/File.js",
"requires": [
462,
502,
503
],
"uses": [
385
],
"idx": 504
},
{
"path": "../classic/classic/src/form/field/Hidden.js",
"requires": [
459
],
"uses": [],
"idx": 505
},
{
"path": "../classic/classic/src/tip/Tip.js",
"requires": [
422
],
"uses": [
34,
111
],
"idx": 506
},
{
"path": "../classic/classic/src/tip/ToolTip.js",
"requires": [
32,
506
],
"uses": [
34,
74
],
"idx": 507
},
{
"path": "../classic/classic/src/tip/QuickTip.js",
"requires": [
507
],
"uses": [],
"idx": 508
},
{
"path": "../classic/classic/src/tip/QuickTipManager.js",
"requires": [
508
],
"uses": [],
"idx": 509
},
{
"path": "../classic/classic/src/picker/Color.js",
"requires": [
90,
111
],
"uses": [],
"idx": 510
},
{
"path": "../classic/classic/src/layout/component/field/HtmlEditor.js",
"requires": [
467
],
"uses": [],
"idx": 511
},
{
"path": "../classic/classic/src/toolbar/Separator.js",
"requires": [
411,
490
],
"uses": [],
"idx": 512
},
{
"path": "../classic/classic/src/layout/container/boxOverflow/Menu.js",
"requires": [
392,
404,
512
],
"uses": [
385,
405,
410,
419,
471,
563,
565,
637
],
"idx": 513
},
{
"path": "../classic/classic/src/form/field/HtmlEditor.js",
"requires": [
86,
362,
410,
411,
458,
468,
490,
509,
510,
511,
513
],
"uses": [
1,
87,
111,
233,
385,
405,
419,
565
],
"idx": 514
},
{
"path": "../classic/classic/src/view/TagKeyNav.js",
"requires": [
488
],
"uses": [],
"idx": 515
},
{
"path": "../classic/classic/src/form/field/Tag.js",
"requires": [
164,
210,
483,
497,
515
],
"uses": [
50,
87,
90,
153,
158,
159
],
"idx": 516
},
{
"path": "../classic/classic/src/picker/Time.js",
"requires": [
164,
496
],
"uses": [
50
],
"idx": 517
},
{
"path": "../classic/classic/src/form/field/Time.js",
"requires": [
488,
497,
500,
517
],
"uses": [
87,
90,
484,
489
],
"idx": 518
},
{
"path": "../classic/classic/src/form/field/Trigger.js",
"requires": [
233,
391,
462
],
"uses": [],
"idx": 519
},
{
"path": "../classic/classic/src/grid/CellContext.js",
"requires": [],
"uses": [],
"idx": 520
},
{
"path": "../classic/classic/src/grid/CellEditor.js",
"requires": [
379
],
"uses": [
49,
377
],
"idx": 521
},
{
"path": "../classic/classic/src/grid/ColumnComponentLayout.js",
"requires": [
385
],
"uses": [],
"idx": 522
},
{
"path": "../classic/classic/src/layout/container/Fit.js",
"requires": [
374
],
"uses": [],
"idx": 523
},
{
"path": "../classic/classic/src/panel/Table.js",
"requires": [
422,
523
],
"uses": [
1,
76,
167,
233,
527,
534,
544,
578,
579,
622,
623,
624
],
"idx": 524
},
{
"path": "../classic/classic/src/grid/ColumnLayout.js",
"requires": [
409,
524
],
"uses": [],
"idx": 525
},
{
"path": "../classic/classic/src/grid/ColumnManager.js",
"requires": [],
"uses": [],
"idx": 526
},
{
"path": "../classic/classic/src/grid/NavigationModel.js",
"requires": [
485
],
"uses": [
20,
35,
49,
74,
111,
401,
520
],
"idx": 527
},
{
"path": "../classic/classic/src/view/TableLayout.js",
"requires": [
385
],
"uses": [],
"idx": 528
},
{
"path": "../classic/classic/src/grid/locking/RowSynchronizer.js",
"requires": [],
"uses": [],
"idx": 529
},
{
"path": "../classic/classic/src/view/NodeCache.js",
"requires": [
75
],
"uses": [
49,
74
],
"idx": 530
},
{
"path": "../classic/classic/src/scroll/TableScroller.js",
"requires": [
101
],
"uses": [],
"idx": 531
},
{
"path": "../classic/classic/src/view/Table.js",
"requires": [
1,
55,
226,
487,
520,
528,
529,
530,
531
],
"uses": [
12,
49,
74,
90,
111,
147,
544
],
"idx": 532
},
{
"path": "../classic/classic/src/grid/Panel.js",
"requires": [
524,
532
],
"uses": [],
"idx": 533
},
{
"path": "../classic/classic/src/grid/RowContext.js",
"requires": [],
"uses": [
12
],
"idx": 534
},
{
"path": "../classic/classic/src/grid/RowEditorButtons.js",
"requires": [
377
],
"uses": [
385,
392,
422
],
"idx": 535
},
{
"path": "../classic/classic/src/grid/RowEditor.js",
"requires": [
401,
475,
507,
535
],
"uses": [
49,
66,
76,
375,
377,
385,
419,
501,
520
],
"idx": 536
},
{
"path": "../classic/classic/src/grid/Scroller.js",
"requires": [],
"uses": [],
"idx": 537
},
{
"path": "../classic/classic/src/view/DropZone.js",
"requires": [
446
],
"uses": [
111,
385
],
"idx": 538
},
{
"path": "../classic/classic/src/grid/ViewDropZone.js",
"requires": [
538
],
"uses": [],
"idx": 539
},
{
"path": "../classic/classic/src/grid/plugin/HeaderResizer.js",
"requires": [
33,
338,
433
],
"uses": [
545
],
"idx": 540
},
{
"path": "../classic/classic/src/grid/header/DragZone.js",
"requires": [
444
],
"uses": [],
"idx": 541
},
{
"path": "../classic/classic/src/grid/header/DropZone.js",
"requires": [
446
],
"uses": [
406
],
"idx": 542
},
{
"path": "../classic/classic/src/grid/plugin/HeaderReorderer.js",
"requires": [
338,
541,
542
],
"uses": [],
"idx": 543
},
{
"path": "../classic/classic/src/grid/header/Container.js",
"requires": [
377,
401,
402,
525,
540,
543
],
"uses": [
1,
111,
385,
405,
410,
419,
526,
545,
563,
564,
565
],
"idx": 544
},
{
"path": "../classic/classic/src/grid/column/Column.js",
"requires": [
207,
522,
525,
544
],
"uses": [
53,
86,
540
],
"idx": 545
},
{
"path": "../classic/classic/src/grid/column/ActionProxy.js",
"requires": [],
"uses": [],
"idx": 546
},
{
"path": "../classic/classic/src/grid/column/Action.js",
"requires": [
77,
545,
546
],
"uses": [
49
],
"idx": 547
},
{
"path": "../classic/classic/src/grid/column/Boolean.js",
"requires": [
545
],
"uses": [],
"idx": 548
},
{
"path": "../classic/classic/src/grid/column/Check.js",
"requires": [
545
],
"uses": [
520
],
"idx": 549
},
{
"path": "../classic/classic/src/grid/column/Date.js",
"requires": [
545
],
"uses": [
86
],
"idx": 550
},
{
"path": "../classic/classic/src/grid/column/Number.js",
"requires": [
86,
545
],
"uses": [],
"idx": 551
},
{
"path": "../classic/classic/src/grid/column/RowNumberer.js",
"requires": [
545
],
"uses": [],
"idx": 552
},
{
"path": "../classic/classic/src/grid/column/Template.js",
"requires": [
90,
545
],
"uses": [
549
],
"idx": 553
},
{
"path": "../classic/classic/src/grid/column/Widget.js",
"requires": [
334,
545
],
"uses": [],
"idx": 554
},
{
"path": "../classic/classic/src/grid/feature/Feature.js",
"requires": [
51
],
"uses": [],
"idx": 555
},
{
"path": "../classic/classic/src/grid/feature/AbstractSummary.js",
"requires": [
555
],
"uses": [],
"idx": 556
},
{
"path": "../classic/classic/src/grid/feature/GroupStore.js",
"requires": [
51
],
"uses": [
119
],
"idx": 557
},
{
"path": "../classic/classic/src/grid/feature/Grouping.js",
"requires": [
555,
556,
557
],
"uses": [
90,
147,
544
],
"idx": 558
},
{
"path": "../classic/classic/src/grid/feature/GroupingSummary.js",
"requires": [
558
],
"uses": [],
"idx": 559
},
{
"path": "../classic/classic/src/grid/feature/RowBody.js",
"requires": [
555
],
"uses": [
90
],
"idx": 560
},
{
"path": "../classic/classic/src/grid/feature/Summary.js",
"requires": [
556
],
"uses": [
90,
111,
147,
385
],
"idx": 561
},
{
"path": "../classic/classic/src/menu/Item.js",
"requires": [
77,
111,
226
],
"uses": [
390,
509
],
"idx": 562
},
{
"path": "../classic/classic/src/menu/CheckItem.js",
"requires": [
562
],
"uses": [
390
],
"idx": 563
},
{
"path": "../classic/classic/src/menu/Separator.js",
"requires": [
562
],
"uses": [],
"idx": 564
},
{
"path": "../classic/classic/src/menu/Menu.js",
"requires": [
390,
402,
410,
422,
562,
563,
564
],
"uses": [
1,
20,
35,
49,
385,
401
],
"idx": 565
},
{
"path": "../classic/classic/src/grid/filters/filter/Base.js",
"requires": [
12,
405,
410,
419,
565
],
"uses": [
1,
50
],
"idx": 566
},
{
"path": "../classic/classic/src/grid/filters/filter/SingleFilter.js",
"requires": [
566
],
"uses": [],
"idx": 567
},
{
"path": "../classic/classic/src/grid/filters/filter/Boolean.js",
"requires": [
567
],
"uses": [],
"idx": 568
},
{
"path": "../classic/classic/src/grid/filters/filter/TriFilter.js",
"requires": [
566
],
"uses": [],
"idx": 569
},
{
"path": "../classic/classic/src/grid/filters/filter/Date.js",
"requires": [
385,
563,
569
],
"uses": [
405,
410,
419,
499,
615
],
"idx": 570
},
{
"path": "../classic/classic/src/grid/filters/filter/List.js",
"requires": [
567
],
"uses": [
164,
167
],
"idx": 571
},
{
"path": "../classic/classic/src/grid/filters/filter/Number.js",
"requires": [
385,
492,
569
],
"uses": [
494
],
"idx": 572
},
{
"path": "../classic/classic/src/grid/filters/filter/String.js",
"requires": [
385,
462,
567
],
"uses": [
50
],
"idx": 573
},
{
"path": "../classic/classic/src/grid/filters/Filters.js",
"requires": [
338,
382,
566,
567,
568,
569,
570,
571,
572,
573
],
"uses": [
12
],
"idx": 574
},
{
"path": "../classic/classic/src/grid/locking/HeaderContainer.js",
"requires": [
526,
544
],
"uses": [],
"idx": 575
},
{
"path": "../classic/classic/src/grid/locking/View.js",
"requires": [
51,
108,
111,
382,
486,
532
],
"uses": [
101,
383,
520
],
"idx": 576
},
{
"path": "../classic/classic/src/scroll/LockingScroller.js",
"requires": [
101
],
"uses": [],
"idx": 577
},
{
"path": "../classic/classic/src/grid/locking/Lockable.js",
"requires": [
111,
532,
544,
575,
576,
577
],
"uses": [
1,
33,
101,
167,
375,
385,
407,
408,
422,
524
],
"idx": 578
},
{
"path": "../classic/classic/src/grid/plugin/BufferedRenderer.js",
"requires": [
338
],
"uses": [
1,
49,
111,
529
],
"idx": 579
},
{
"path": "../classic/classic/src/grid/plugin/Editing.js",
"requires": [
4,
338,
401,
459,
532,
545
],
"uses": [
20,
111,
385,
520
],
"idx": 580
},
{
"path": "../classic/classic/src/grid/plugin/CellEditing.js",
"requires": [
1,
521,
580
],
"uses": [
55,
520
],
"idx": 581
},
{
"path": "../classic/classic/src/plugin/AbstractClipboard.js",
"requires": [
338,
400
],
"uses": [
49
],
"idx": 582
},
{
"path": "../classic/classic/src/grid/plugin/Clipboard.js",
"requires": [
86,
361,
582
],
"uses": [
520
],
"idx": 583
},
{
"path": "../classic/classic/src/grid/plugin/DragDrop.js",
"requires": [
338
],
"uses": [
539,
643
],
"idx": 584
},
{
"path": "../classic/classic/src/grid/plugin/RowEditing.js",
"requires": [
536,
580
],
"uses": [],
"idx": 585
},
{
"path": "../classic/classic/src/grid/plugin/RowExpander.js",
"requires": [
338,
560
],
"uses": [
90,
545
],
"idx": 586
},
{
"path": "../classic/classic/src/grid/plugin/RowWidget.js",
"requires": [
3,
334,
586
],
"uses": [
338,
560
],
"idx": 587
},
{
"path": "../classic/classic/src/grid/property/Grid.js",
"requires": [
533
],
"uses": [
20,
90,
147,
378,
385,
459,
462,
492,
494,
497,
500,
520,
521,
532,
581,
589,
592
],
"idx": 588
},
{
"path": "../classic/classic/src/grid/property/HeaderContainer.js",
"requires": [
86,
544
],
"uses": [],
"idx": 589
},
{
"path": "../classic/classic/src/grid/property/Property.js",
"requires": [
147
],
"uses": [],
"idx": 590
},
{
"path": "../classic/classic/src/grid/property/Reader.js",
"requires": [
149
],
"uses": [
148
],
"idx": 591
},
{
"path": "../classic/classic/src/grid/property/Store.js",
"requires": [
153,
164,
590,
591
],
"uses": [
159
],
"idx": 592
},
{
"path": "../classic/classic/src/grid/selection/Selection.js",
"requires": [],
"uses": [],
"idx": 593
},
{
"path": "../classic/classic/src/grid/selection/Cells.js",
"requires": [
593
],
"uses": [
520
],
"idx": 594
},
{
"path": "../classic/classic/src/grid/selection/Columns.js",
"requires": [
593
],
"uses": [
520
],
"idx": 595
},
{
"path": "../classic/classic/src/grid/selection/Replicator.js",
"requires": [
338
],
"uses": [],
"idx": 596
},
{
"path": "../classic/classic/src/grid/selection/Rows.js",
"requires": [
119,
593
],
"uses": [
520
],
"idx": 597
},
{
"path": "../classic/classic/src/grid/selection/SelectionExtender.js",
"requires": [
433
],
"uses": [
49,
362
],
"idx": 598
},
{
"path": "../classic/classic/src/grid/selection/SpreadsheetModel.js",
"requires": [
483,
552,
593,
594,
595,
597,
598
],
"uses": [
375,
439,
520,
522,
549
],
"idx": 599
},
{
"path": "../classic/classic/src/util/Queue.js",
"requires": [],
"uses": [],
"idx": 600
},
{
"path": "../classic/classic/src/layout/ContextItem.js",
"requires": [],
"uses": [
55,
66,
72,
372
],
"idx": 601
},
{
"path": "../classic/classic/src/layout/Context.js",
"requires": [
66,
72,
337,
373,
600,
601
],
"uses": [],
"idx": 602
},
{
"path": "../classic/classic/src/layout/SizePolicy.js",
"requires": [],
"uses": [],
"idx": 603
},
{
"path": "../classic/classic/src/layout/component/Body.js",
"requires": [
385
],
"uses": [],
"idx": 604
},
{
"path": "../classic/classic/src/layout/component/FieldSet.js",
"requires": [
604
],
"uses": [],
"idx": 605
},
{
"path": "../classic/classic/src/layout/container/Absolute.js",
"requires": [
429
],
"uses": [],
"idx": 606
},
{
"path": "../classic/classic/src/layout/container/Accordion.js",
"requires": [
410
],
"uses": [],
"idx": 607
},
{
"path": "../classic/classic/src/resizer/BorderSplitter.js",
"requires": [
407
],
"uses": [
618
],
"idx": 608
},
{
"path": "../classic/classic/src/layout/container/Border.js",
"requires": [
72,
112,
374,
608
],
"uses": [
86,
385
],
"idx": 609
},
{
"path": "../classic/classic/src/layout/container/Card.js",
"requires": [
523
],
"uses": [
49
],
"idx": 610
},
{
"path": "../classic/classic/src/layout/container/Center.js",
"requires": [
523
],
"uses": [],
"idx": 611
},
{
"path": "../classic/classic/src/layout/container/Form.js",
"requires": [
375
],
"uses": [],
"idx": 612
},
{
"path": "../classic/classic/src/menu/Bar.js",
"requires": [
565
],
"uses": [],
"idx": 613
},
{
"path": "../classic/classic/src/menu/ColorPicker.js",
"requires": [
510,
565
],
"uses": [
385,
390
],
"idx": 614
},
{
"path": "../classic/classic/src/menu/DatePicker.js",
"requires": [
499,
565
],
"uses": [
385,
390
],
"idx": 615
},
{
"path": "../classic/classic/src/panel/Pinnable.js",
"requires": [
0
],
"uses": [
385,
399
],
"idx": 616
},
{
"path": "../classic/classic/src/plugin/Manager.js",
"requires": [],
"uses": [],
"idx": 617
},
{
"path": "../classic/classic/src/resizer/BorderSplitterTracker.js",
"requires": [
33,
434
],
"uses": [],
"idx": 618
},
{
"path": "../classic/classic/src/resizer/Handle.js",
"requires": [
111
],
"uses": [],
"idx": 619
},
{
"path": "../classic/classic/src/resizer/ResizeTracker.js",
"requires": [
433
],
"uses": [
49
],
"idx": 620
},
{
"path": "../classic/classic/src/resizer/Resizer.js",
"requires": [
51
],
"uses": [
49,
87,
111,
620
],
"idx": 621
},
{
"path": "../classic/classic/src/selection/CellModel.js",
"requires": [
484,
520
],
"uses": [],
"idx": 622
},
{
"path": "../classic/classic/src/selection/RowModel.js",
"requires": [
484,
520
],
"uses": [],
"idx": 623
},
{
"path": "../classic/classic/src/selection/CheckboxModel.js",
"requires": [
549,
623
],
"uses": [
375,
520,
522
],
"idx": 624
},
{
"path": "../classic/classic/src/selection/TreeModel.js",
"requires": [
623
],
"uses": [],
"idx": 625
},
{
"path": "../classic/classic/src/slider/Thumb.js",
"requires": [
86,
433
],
"uses": [
72
],
"idx": 626
},
{
"path": "../classic/classic/src/slider/Tip.js",
"requires": [
506
],
"uses": [],
"idx": 627
},
{
"path": "../classic/classic/src/slider/Multi.js",
"requires": [
86,
87,
459,
626,
627
],
"uses": [
233
],
"idx": 628
},
{
"path": "../classic/classic/src/slider/Single.js",
"requires": [
628
],
"uses": [],
"idx": 629
},
{
"path": "../classic/classic/src/slider/Widget.js",
"requires": [
83,
628
],
"uses": [
72,
86
],
"idx": 630
},
{
"path": "../classic/classic/src/state/CookieProvider.js",
"requires": [
105
],
"uses": [],
"idx": 631
},
{
"path": "../classic/classic/src/state/LocalStorageProvider.js",
"requires": [
105,
360
],
"uses": [],
"idx": 632
},
{
"path": "../classic/classic/src/tab/Tab.js",
"requires": [
392
],
"uses": [],
"idx": 633
},
{
"path": "../classic/classic/src/tab/Bar.js",
"requires": [
34,
397,
402,
604,
633
],
"uses": [
33
],
"idx": 634
},
{
"path": "../classic/classic/src/tab/Panel.js",
"requires": [
422,
610,
634
],
"uses": [
385,
633
],
"idx": 635
},
{
"path": "../classic/classic/src/toolbar/Breadcrumb.js",
"requires": [
230,
377,
393,
402
],
"uses": [
23,
167
],
"idx": 636
},
{
"path": "../classic/classic/src/toolbar/Fill.js",
"requires": [
111,
411
],
"uses": [],
"idx": 637
},
{
"path": "../classic/classic/src/toolbar/Spacer.js",
"requires": [
111,
411
],
"uses": [],
"idx": 638
},
{
"path": "../classic/classic/src/tree/Column.js",
"requires": [
545
],
"uses": [
77
],
"idx": 639
},
{
"path": "../classic/classic/src/tree/NavigationModel.js",
"requires": [
527
],
"uses": [
35
],
"idx": 640
},
{
"path": "../classic/classic/src/tree/View.js",
"requires": [
532
],
"uses": [
49
],
"idx": 641
},
{
"path": "../classic/classic/src/tree/Panel.js",
"requires": [
230,
524,
625,
639,
640,
641
],
"uses": [
167,
375,
522
],
"idx": 642
},
{
"path": "../classic/classic/src/view/DragZone.js",
"requires": [
444
],
"uses": [
49,
87
],
"idx": 643
},
{
"path": "../classic/classic/src/tree/ViewDragZone.js",
"requires": [
643
],
"uses": [
87
],
"idx": 644
},
{
"path": "../classic/classic/src/tree/ViewDropZone.js",
"requires": [
538
],
"uses": [],
"idx": 645
},
{
"path": "../classic/classic/src/tree/plugin/TreeViewDragDrop.js",
"requires": [
338
],
"uses": [
644,
645
],
"idx": 646
},
{
"path": "../classic/classic/src/util/Cookies.js",
"requires": [],
"uses": [],
"idx": 647
},
{
"path": "../classic/classic/src/view/MultiSelectorSearch.js",
"requires": [
422
],
"uses": [
50,
167,
385,
419,
462,
523,
533
],
"idx": 648
},
{
"path": "../classic/classic/src/view/MultiSelector.js",
"requires": [
419,
523,
533,
648
],
"uses": [],
"idx": 649
},
{
"path": "../classic/classic/src/window/Toast.js",
"requires": [
456
],
"uses": [
1
],
"idx": 650
}
],
"classes": {
"Ext.AbstractManager": {
"idx": 6,
"alias": [],
"alternates": []
},
"Ext.Action": {
"idx": 369,
"alias": [],
"alternates": []
},
"Ext.Ajax": {
"idx": 18,
"alias": [],
"alternates": []
},
"Ext.AnimationQueue": {
"idx": 19,
"alias": [],
"alternates": []
},
"Ext.Component": {
"idx": 111,
"alias": [
"widget.box",
"widget.component"
],
"alternates": [
"Ext.AbstractComponent"
]
},
"Ext.ComponentLoader": {
"idx": 371,
"alias": [],
"alternates": []
},
"Ext.ComponentManager": {
"idx": 20,
"alias": [],
"alternates": [
"Ext.ComponentMgr"
]
},
"Ext.ComponentQuery": {
"idx": 23,
"alias": [],
"alternates": []
},
"Ext.Deferred": {
"idx": 11,
"alias": [],
"alternates": []
},
"Ext.Editor": {
"idx": 379,
"alias": [
"widget.editor"
],
"alternates": []
},
"Ext.ElementLoader": {
"idx": 370,
"alias": [],
"alternates": []
},
"Ext.EventManager": {
"idx": 380,
"alias": [],
"alternates": []
},
"Ext.Evented": {
"idx": 24,
"alias": [],
"alternates": [
"Ext.EventedBase"
]
},
"Ext.GlobalEvents": {
"idx": 76,
"alias": [],
"alternates": [
"Ext.globalEvents"
]
},
"Ext.Glyph": {
"idx": 77,
"alias": [],
"alternates": []
},
"Ext.Img": {
"idx": 381,
"alias": [
"widget.image",
"widget.imagecomponent"
],
"alternates": []
},
"Ext.LoadMask": {
"idx": 383,
"alias": [
"widget.loadmask"
],
"alternates": []
},
"Ext.Mixin": {
"idx": 0,
"alias": [],
"alternates": []
},
"Ext.Progress": {
"idx": 85,
"alias": [
"widget.progress",
"widget.progressbarwidget"
],
"alternates": [
"Ext.ProgressBarWidget"
]
},
"Ext.ProgressBar": {
"idx": 387,
"alias": [
"widget.progressbar"
],
"alternates": []
},
"Ext.ProgressBase": {
"idx": 84,
"alias": [],
"alternates": []
},
"Ext.Promise": {
"idx": 10,
"alias": [],
"alternates": []
},
"Ext.TaskQueue": {
"idx": 39,
"alias": [],
"alternates": []
},
"Ext.Template": {
"idx": 87,
"alias": [],
"alternates": []
},
"Ext.Widget": {
"idx": 83,
"alias": [
"widget.widget"
],
"alternates": [
"Ext.Gadget"
]
},
"Ext.XTemplate": {
"idx": 90,
"alias": [],
"alternates": []
},
"Ext.ZIndexManager": {
"idx": 376,
"alias": [],
"alternates": [
"Ext.WindowGroup"
]
},
"Ext.app.Application": {
"idx": 174,
"alias": [],
"alternates": []
},
"Ext.app.BaseController": {
"idx": 115,
"alias": [],
"alternates": []
},
"Ext.app.Controller": {
"idx": 173,
"alias": [],
"alternates": []
},
"Ext.app.EventBus": {
"idx": 113,
"alias": [],
"alternates": []
},
"Ext.app.EventDomain": {
"idx": 91,
"alias": [],
"alternates": []
},
"Ext.app.Profile": {
"idx": 175,
"alias": [],
"alternates": []
},
"Ext.app.Util": {
"idx": 116,
"alias": [],
"alternates": []
},
"Ext.app.ViewController": {
"idx": 177,
"alias": [
"controller.controller"
],
"alternates": []
},
"Ext.app.ViewModel": {
"idx": 211,
"alias": [
"viewmodel.default"
],
"alternates": []
},
"Ext.app.bind.AbstractStub": {
"idx": 192,
"alias": [],
"alternates": []
},
"Ext.app.bind.BaseBinding": {
"idx": 190,
"alias": [],
"alternates": []
},
"Ext.app.bind.Binding": {
"idx": 191,
"alias": [],
"alternates": []
},
"Ext.app.bind.Formula": {
"idx": 197,
"alias": [],
"alternates": []
},
"Ext.app.bind.LinkStub": {
"idx": 194,
"alias": [],
"alternates": []
},
"Ext.app.bind.Multi": {
"idx": 196,
"alias": [],
"alternates": []
},
"Ext.app.bind.Parser": {
"idx": 207,
"alias": [],
"alternates": []
},
"Ext.app.bind.RootStub": {
"idx": 195,
"alias": [],
"alternates": []
},
"Ext.app.bind.Stub": {
"idx": 193,
"alias": [],
"alternates": []
},
"Ext.app.bind.Template": {
"idx": 208,
"alias": [],
"alternates": []
},
"Ext.app.bind.TemplateBinding": {
"idx": 209,
"alias": [],
"alternates": []
},
"Ext.app.domain.Component": {
"idx": 92,
"alias": [],
"alternates": []
},
"Ext.app.domain.Controller": {
"idx": 212,
"alias": [],
"alternates": []
},
"Ext.app.domain.Direct": {
"idx": 215,
"alias": [],
"alternates": []
},
"Ext.app.domain.Global": {
"idx": 114,
"alias": [],
"alternates": []
},
"Ext.app.domain.Store": {
"idx": 168,
"alias": [],
"alternates": []
},
"Ext.app.domain.View": {
"idx": 176,
"alias": [],
"alternates": []
},
"Ext.app.route.Queue": {
"idx": 169,
"alias": [],
"alternates": []
},
"Ext.app.route.Route": {
"idx": 170,
"alias": [],
"alternates": []
},
"Ext.app.route.Router": {
"idx": 172,
"alias": [],
"alternates": []
},
"Ext.button.Button": {
"idx": 392,
"alias": [
"widget.button"
],
"alternates": [
"Ext.Button"
]
},
"Ext.button.Cycle": {
"idx": 394,
"alias": [
"widget.cycle"
],
"alternates": [
"Ext.CycleButton"
]
},
"Ext.button.Manager": {
"idx": 389,
"alias": [],
"alternates": [
"Ext.ButtonToggleManager"
]
},
"Ext.button.Segmented": {
"idx": 396,
"alias": [
"widget.segmentedbutton"
],
"alternates": []
},
"Ext.button.Split": {
"idx": 393,
"alias": [
"widget.splitbutton"
],
"alternates": [
"Ext.SplitButton"
]
},
"Ext.container.ButtonGroup": {
"idx": 424,
"alias": [
"widget.buttongroup"
],
"alternates": [
"Ext.ButtonGroup"
]
},
"Ext.container.Container": {
"idx": 377,
"alias": [
"widget.container"
],
"alternates": [
"Ext.Container",
"Ext.AbstractContainer"
]
},
"Ext.container.DockingContainer": {
"idx": 421,
"alias": [],
"alternates": []
},
"Ext.container.Monitor": {
"idx": 425,
"alias": [],
"alternates": []
},
"Ext.container.Viewport": {
"idx": 428,
"alias": [
"widget.viewport"
],
"alternates": [
"Ext.Viewport"
]
},
"Ext.dashboard.Column": {
"idx": 431,
"alias": [
"widget.dashboard-column"
],
"alternates": []
},
"Ext.dashboard.Dashboard": {
"idx": 443,
"alias": [
"widget.dashboard"
],
"alternates": []
},
"Ext.dashboard.DropZone": {
"idx": 441,
"alias": [],
"alternates": []
},
"Ext.dashboard.Panel": {
"idx": 430,
"alias": [
"widget.dashboard-panel"
],
"alternates": []
},
"Ext.dashboard.Part": {
"idx": 442,
"alias": [
"part.part"
],
"alternates": []
},
"Ext.data.AbstractStore": {
"idx": 129,
"alias": [],
"alternates": []
},
"Ext.data.ArrayStore": {
"idx": 166,
"alias": [
"store.array"
],
"alternates": [
"Ext.data.SimpleStore"
]
},
"Ext.data.Batch": {
"idx": 180,
"alias": [],
"alternates": []
},
"Ext.data.BufferedStore": {
"idx": 217,
"alias": [
"store.buffered"
],
"alternates": []
},
"Ext.data.ChainedStore": {
"idx": 210,
"alias": [
"store.chained"
],
"alternates": []
},
"Ext.data.Connection": {
"idx": 17,
"alias": [],
"alternates": []
},
"Ext.data.DirectStore": {
"idx": 219,
"alias": [
"store.direct"
],
"alternates": []
},
"Ext.data.Error": {
"idx": 130,
"alias": [],
"alternates": []
},
"Ext.data.ErrorCollection": {
"idx": 131,
"alias": [],
"alternates": [
"Ext.data.Errors"
]
},
"Ext.data.JsonP": {
"idx": 220,
"alias": [],
"alternates": []
},
"Ext.data.JsonPStore": {
"idx": 222,
"alias": [
"store.jsonp"
],
"alternates": []
},
"Ext.data.JsonStore": {
"idx": 223,
"alias": [
"store.json"
],
"alternates": []
},
"Ext.data.LocalStore": {
"idx": 155,
"alias": [],
"alternates": []
},
"Ext.data.Model": {
"idx": 147,
"alias": [],
"alternates": [
"Ext.data.Record"
]
},
"Ext.data.ModelManager": {
"idx": 224,
"alias": [],
"alternates": [
"Ext.ModelMgr"
]
},
"Ext.data.NodeInterface": {
"idx": 225,
"alias": [],
"alternates": []
},
"Ext.data.NodeStore": {
"idx": 228,
"alias": [
"store.node"
],
"alternates": []
},
"Ext.data.PageMap": {
"idx": 216,
"alias": [],
"alternates": []
},
"Ext.data.ProxyStore": {
"idx": 154,
"alias": [],
"alternates": []
},
"Ext.data.Request": {
"idx": 229,
"alias": [],
"alternates": []
},
"Ext.data.ResultSet": {
"idx": 148,
"alias": [],
"alternates": []
},
"Ext.data.Session": {
"idx": 188,
"alias": [],
"alternates": []
},
"Ext.data.SortTypes": {
"idx": 137,
"alias": [],
"alternates": []
},
"Ext.data.Store": {
"idx": 164,
"alias": [
"store.store"
],
"alternates": []
},
"Ext.data.StoreManager": {
"idx": 167,
"alias": [],
"alternates": [
"Ext.StoreMgr",
"Ext.data.StoreMgr",
"Ext.StoreManager"
]
},
"Ext.data.TreeModel": {
"idx": 227,
"alias": [],
"alternates": []
},
"Ext.data.TreeStore": {
"idx": 230,
"alias": [
"store.tree"
],
"alternates": []
},
"Ext.data.Types": {
"idx": 231,
"alias": [],
"alternates": []
},
"Ext.data.Validation": {
"idx": 232,
"alias": [],
"alternates": []
},
"Ext.data.XmlStore": {
"idx": 237,
"alias": [
"store.xml"
],
"alternates": []
},
"Ext.data.field.Boolean": {
"idx": 140,
"alias": [
"data.field.bool",
"data.field.boolean"
],
"alternates": []
},
"Ext.data.field.Date": {
"idx": 141,
"alias": [
"data.field.date"
],
"alternates": []
},
"Ext.data.field.Field": {
"idx": 139,
"alias": [
"data.field.auto"
],
"alternates": [
"Ext.data.Field"
]
},
"Ext.data.field.Integer": {
"idx": 142,
"alias": [
"data.field.int",
"data.field.integer"
],
"alternates": []
},
"Ext.data.field.Number": {
"idx": 143,
"alias": [
"data.field.float",
"data.field.number"
],
"alternates": []
},
"Ext.data.field.String": {
"idx": 144,
"alias": [
"data.field.string"
],
"alternates": []
},
"Ext.data.flash.BinaryXhr": {
"idx": 14,
"alias": [],
"alternates": []
},
"Ext.data.identifier.Generator": {
"idx": 145,
"alias": [
"data.identifier.default"
],
"alternates": []
},
"Ext.data.identifier.Negative": {
"idx": 238,
"alias": [
"data.identifier.negative"
],
"alternates": []
},
"Ext.data.identifier.Sequential": {
"idx": 146,
"alias": [
"data.identifier.sequential"
],
"alternates": []
},
"Ext.data.identifier.Uuid": {
"idx": 239,
"alias": [
"data.identifier.uuid"
],
"alternates": []
},
"Ext.data.matrix.Matrix": {
"idx": 183,
"alias": [],
"alternates": []
},
"Ext.data.matrix.Side": {
"idx": 182,
"alias": [],
"alternates": []
},
"Ext.data.matrix.Slice": {
"idx": 181,
"alias": [],
"alternates": []
},
"Ext.data.operation.Create": {
"idx": 133,
"alias": [
"data.operation.create"
],
"alternates": []
},
"Ext.data.operation.Destroy": {
"idx": 134,
"alias": [
"data.operation.destroy"
],
"alternates": []
},
"Ext.data.operation.Operation": {
"idx": 132,
"alias": [],
"alternates": [
"Ext.data.Operation"
]
},
"Ext.data.operation.Read": {
"idx": 135,
"alias": [
"data.operation.read"
],
"alternates": []
},
"Ext.data.operation.Update": {
"idx": 136,
"alias": [
"data.operation.update"
],
"alternates": []
},
"Ext.data.proxy.Ajax": {
"idx": 157,
"alias": [
"proxy.ajax"
],
"alternates": [
"Ext.data.HttpProxy",
"Ext.data.AjaxProxy"
]
},
"Ext.data.proxy.Client": {
"idx": 152,
"alias": [],
"alternates": [
"Ext.data.ClientProxy"
]
},
"Ext.data.proxy.Direct": {
"idx": 218,
"alias": [
"proxy.direct"
],
"alternates": [
"Ext.data.DirectProxy"
]
},
"Ext.data.proxy.JsonP": {
"idx": 221,
"alias": [
"proxy.jsonp",
"proxy.scripttag"
],
"alternates": [
"Ext.data.ScriptTagProxy"
]
},
"Ext.data.proxy.LocalStorage": {
"idx": 241,
"alias": [
"proxy.localstorage"
],
"alternates": [
"Ext.data.LocalStorageProxy"
]
},
"Ext.data.proxy.Memory": {
"idx": 153,
"alias": [
"proxy.memory"
],
"alternates": [
"Ext.data.MemoryProxy"
]
},
"Ext.data.proxy.Proxy": {
"idx": 151,
"alias": [
"proxy.proxy"
],
"alternates": [
"Ext.data.DataProxy",
"Ext.data.Proxy"
]
},
"Ext.data.proxy.Rest": {
"idx": 242,
"alias": [
"proxy.rest"
],
"alternates": [
"Ext.data.RestProxy"
]
},
"Ext.data.proxy.Server": {
"idx": 156,
"alias": [
"proxy.server"
],
"alternates": [
"Ext.data.ServerProxy"
]
},
"Ext.data.proxy.SessionStorage": {
"idx": 243,
"alias": [
"proxy.sessionstorage"
],
"alternates": [
"Ext.data.SessionStorageProxy"
]
},
"Ext.data.proxy.WebStorage": {
"idx": 240,
"alias": [],
"alternates": [
"Ext.data.WebStorageProxy"
]
},
"Ext.data.reader.Array": {
"idx": 165,
"alias": [
"reader.array"
],
"alternates": [
"Ext.data.ArrayReader"
]
},
"Ext.data.reader.Json": {
"idx": 158,
"alias": [
"reader.json"
],
"alternates": [
"Ext.data.JsonReader"
]
},
"Ext.data.reader.Reader": {
"idx": 149,
"alias": [
"reader.base"
],
"alternates": [
"Ext.data.Reader",
"Ext.data.DataReader"
]
},
"Ext.data.reader.Xml": {
"idx": 235,
"alias": [
"reader.xml"
],
"alternates": [
"Ext.data.XmlReader"
]
},
"Ext.data.request.Ajax": {
"idx": 15,
"alias": [
"request.ajax"
],
"alternates": []
},
"Ext.data.request.Base": {
"idx": 13,
"alias": [],
"alternates": []
},
"Ext.data.request.Form": {
"idx": 16,
"alias": [
"request.form"
],
"alternates": []
},
"Ext.data.schema.Association": {
"idx": 122,
"alias": [],
"alternates": []
},
"Ext.data.schema.ManyToMany": {
"idx": 125,
"alias": [],
"alternates": []
},
"Ext.data.schema.ManyToOne": {
"idx": 124,
"alias": [],
"alternates": []
},
"Ext.data.schema.Namer": {
"idx": 127,
"alias": [
"namer.default"
],
"alternates": []
},
"Ext.data.schema.OneToOne": {
"idx": 123,
"alias": [],
"alternates": []
},
"Ext.data.schema.Role": {
"idx": 121,
"alias": [],
"alternates": []
},
"Ext.data.schema.Schema": {
"idx": 128,
"alias": [
"schema.default"
],
"alternates": []
},
"Ext.data.session.BatchVisitor": {
"idx": 186,
"alias": [],
"alternates": []
},
"Ext.data.session.ChangesVisitor": {
"idx": 184,
"alias": [],
"alternates": []
},
"Ext.data.session.ChildChangesVisitor": {
"idx": 185,
"alias": [],
"alternates": []
},
"Ext.data.validator.Bound": {
"idx": 248,
"alias": [
"data.validator.bound"
],
"alternates": []
},
"Ext.data.validator.Email": {
"idx": 250,
"alias": [
"data.validator.email"
],
"alternates": []
},
"Ext.data.validator.Exclusion": {
"idx": 252,
"alias": [
"data.validator.exclusion"
],
"alternates": []
},
"Ext.data.validator.Format": {
"idx": 249,
"alias": [
"data.validator.format"
],
"alternates": []
},
"Ext.data.validator.Inclusion": {
"idx": 253,
"alias": [
"data.validator.inclusion"
],
"alternates": []
},
"Ext.data.validator.Length": {
"idx": 254,
"alias": [
"data.validator.length"
],
"alternates": []
},
"Ext.data.validator.List": {
"idx": 251,
"alias": [
"data.validator.list"
],
"alternates": []
},
"Ext.data.validator.Presence": {
"idx": 255,
"alias": [
"data.validator.presence"
],
"alternates": []
},
"Ext.data.validator.Range": {
"idx": 256,
"alias": [
"data.validator.range"
],
"alternates": []
},
"Ext.data.validator.Validator": {
"idx": 138,
"alias": [
"data.validator.base"
],
"alternates": []
},
"Ext.data.writer.Json": {
"idx": 159,
"alias": [
"writer.json"
],
"alternates": [
"Ext.data.JsonWriter"
]
},
"Ext.data.writer.Writer": {
"idx": 150,
"alias": [
"writer.base"
],
"alternates": [
"Ext.data.DataWriter",
"Ext.data.Writer"
]
},
"Ext.data.writer.Xml": {
"idx": 236,
"alias": [
"writer.xml"
],
"alternates": [
"Ext.data.XmlWriter"
]
},
"Ext.dd.DD": {
"idx": 413,
"alias": [],
"alternates": []
},
"Ext.dd.DDProxy": {
"idx": 414,
"alias": [],
"alternates": []
},
"Ext.dd.DDTarget": {
"idx": 438,
"alias": [],
"alternates": []
},
"Ext.dd.DragDrop": {
"idx": 412,
"alias": [],
"alternates": []
},
"Ext.dd.DragDropManager": {
"idx": 406,
"alias": [],
"alternates": [
"Ext.dd.DragDropMgr",
"Ext.dd.DDM"
]
},
"Ext.dd.DragSource": {
"idx": 416,
"alias": [],
"alternates": []
},
"Ext.dd.DragTracker": {
"idx": 433,
"alias": [],
"alternates": []
},
"Ext.dd.DragZone": {
"idx": 444,
"alias": [],
"alternates": []
},
"Ext.dd.DropTarget": {
"idx": 440,
"alias": [],
"alternates": []
},
"Ext.dd.DropZone": {
"idx": 446,
"alias": [],
"alternates": []
},
"Ext.dd.Registry": {
"idx": 445,
"alias": [],
"alternates": []
},
"Ext.dd.ScrollManager": {
"idx": 439,
"alias": [],
"alternates": []
},
"Ext.dd.StatusProxy": {
"idx": 415,
"alias": [],
"alternates": []
},
"Ext.direct.Event": {
"idx": 257,
"alias": [
"direct.event"
],
"alternates": []
},
"Ext.direct.ExceptionEvent": {
"idx": 259,
"alias": [
"direct.exception"
],
"alternates": []
},
"Ext.direct.JsonProvider": {
"idx": 260,
"alias": [
"direct.jsonprovider"
],
"alternates": []
},
"Ext.direct.Manager": {
"idx": 213,
"alias": [],
"alternates": []
},
"Ext.direct.PollingProvider": {
"idx": 261,
"alias": [
"direct.pollingprovider"
],
"alternates": []
},
"Ext.direct.Provider": {
"idx": 214,
"alias": [
"direct.provider"
],
"alternates": []
},
"Ext.direct.RemotingEvent": {
"idx": 258,
"alias": [
"direct.rpc"
],
"alternates": []
},
"Ext.direct.RemotingMethod": {
"idx": 262,
"alias": [],
"alternates": []
},
"Ext.direct.RemotingProvider": {
"idx": 264,
"alias": [
"direct.remotingprovider"
],
"alternates": []
},
"Ext.direct.Transaction": {
"idx": 263,
"alias": [
"direct.transaction"
],
"alternates": []
},
"Ext.dom.ButtonElement": {
"idx": 388,
"alias": [],
"alternates": []
},
"Ext.dom.CompositeElement": {
"idx": 94,
"alias": [],
"alternates": [
"Ext.CompositeElement"
]
},
"Ext.dom.CompositeElementLite": {
"idx": 75,
"alias": [],
"alternates": [
"Ext.CompositeElementLite"
]
},
"Ext.dom.Element": {
"idx": 49,
"alias": [],
"alternates": [
"Ext.Element"
]
},
"Ext.dom.ElementEvent": {
"idx": 30,
"alias": [],
"alternates": []
},
"Ext.dom.Fly": {
"idx": 74,
"alias": [],
"alternates": [
"Ext.dom.Element.Fly"
]
},
"Ext.dom.GarbageCollector": {
"idx": 265,
"alias": [],
"alternates": []
},
"Ext.dom.Helper": {
"idx": 233,
"alias": [],
"alternates": [
"Ext.DomHelper",
"Ext.core.DomHelper"
]
},
"Ext.dom.Layer": {
"idx": 447,
"alias": [],
"alternates": [
"Ext.Layer"
]
},
"Ext.dom.Query": {
"idx": 234,
"alias": [],
"alternates": [
"Ext.core.DomQuery",
"Ext.DomQuery"
]
},
"Ext.dom.Shadow": {
"idx": 28,
"alias": [],
"alternates": [
"Ext.Shadow"
]
},
"Ext.dom.Shim": {
"idx": 29,
"alias": [],
"alternates": []
},
"Ext.dom.TouchAction": {
"idx": 266,
"alias": [],
"alternates": []
},
"Ext.dom.Underlay": {
"idx": 27,
"alias": [],
"alternates": []
},
"Ext.dom.UnderlayPool": {
"idx": 26,
"alias": [],
"alternates": []
},
"Ext.drag.Constraint": {
"idx": 267,
"alias": [
"drag.constraint.base"
],
"alternates": []
},
"Ext.drag.Info": {
"idx": 268,
"alias": [],
"alternates": []
},
"Ext.drag.Item": {
"idx": 269,
"alias": [],
"alternates": []
},
"Ext.drag.Manager": {
"idx": 270,
"alias": [],
"alternates": []
},
"Ext.drag.Source": {
"idx": 271,
"alias": [],
"alternates": []
},
"Ext.drag.Target": {
"idx": 272,
"alias": [],
"alternates": []
},
"Ext.drag.proxy.None": {
"idx": 273,
"alias": [
"drag.proxy.none"
],
"alternates": []
},
"Ext.drag.proxy.Original": {
"idx": 274,
"alias": [
"drag.proxy.original"
],
"alternates": []
},
"Ext.drag.proxy.Placeholder": {
"idx": 275,
"alias": [
"drag.proxy.placeholder"
],
"alternates": []
},
"Ext.event.Event": {
"idx": 35,
"alias": [],
"alternates": [
"Ext.EventObjectImpl"
]
},
"Ext.event.gesture.DoubleTap": {
"idx": 278,
"alias": [],
"alternates": []
},
"Ext.event.gesture.Drag": {
"idx": 279,
"alias": [],
"alternates": []
},
"Ext.event.gesture.EdgeSwipe": {
"idx": 281,
"alias": [],
"alternates": []
},
"Ext.event.gesture.LongPress": {
"idx": 282,
"alias": [],
"alternates": []
},
"Ext.event.gesture.MultiTouch": {
"idx": 283,
"alias": [],
"alternates": []
},
"Ext.event.gesture.Pinch": {
"idx": 284,
"alias": [],
"alternates": []
},
"Ext.event.gesture.Recognizer": {
"idx": 276,
"alias": [],
"alternates": []
},
"Ext.event.gesture.Rotate": {
"idx": 285,
"alias": [],
"alternates": []
},
"Ext.event.gesture.SingleTouch": {
"idx": 277,
"alias": [],
"alternates": []
},
"Ext.event.gesture.Swipe": {
"idx": 280,
"alias": [],
"alternates": []
},
"Ext.event.gesture.Tap": {
"idx": 286,
"alias": [],
"alternates": []
},
"Ext.event.publisher.Dom": {
"idx": 36,
"alias": [],
"alternates": []
},
"Ext.event.publisher.ElementPaint": {
"idx": 48,
"alias": [],
"alternates": []
},
"Ext.event.publisher.ElementSize": {
"idx": 44,
"alias": [],
"alternates": []
},
"Ext.event.publisher.Focus": {
"idx": 287,
"alias": [],
"alternates": []
},
"Ext.event.publisher.Gesture": {
"idx": 37,
"alias": [],
"alternates": []
},
"Ext.event.publisher.MouseEnterLeave": {
"idx": 449,
"alias": [],
"alternates": []
},
"Ext.event.publisher.Publisher": {
"idx": 31,
"alias": [],
"alternates": []
},
"Ext.flash.Component": {
"idx": 450,
"alias": [
"widget.flash"
],
"alternates": [
"Ext.FlashComponent"
]
},
"Ext.form.Basic": {
"idx": 465,
"alias": [],
"alternates": [
"Ext.form.BasicForm"
]
},
"Ext.form.CheckboxGroup": {
"idx": 472,
"alias": [
"widget.checkboxgroup"
],
"alternates": []
},
"Ext.form.CheckboxManager": {
"idx": 470,
"alias": [],
"alternates": []
},
"Ext.form.FieldAncestor": {
"idx": 466,
"alias": [],
"alternates": []
},
"Ext.form.FieldContainer": {
"idx": 468,
"alias": [
"widget.fieldcontainer"
],
"alternates": []
},
"Ext.form.FieldSet": {
"idx": 473,
"alias": [
"widget.fieldset"
],
"alternates": []
},
"Ext.form.Label": {
"idx": 474,
"alias": [
"widget.label"
],
"alternates": []
},
"Ext.form.Labelable": {
"idx": 457,
"alias": [],
"alternates": []
},
"Ext.form.Panel": {
"idx": 475,
"alias": [
"widget.form"
],
"alternates": [
"Ext.FormPanel",
"Ext.form.FormPanel"
]
},
"Ext.form.RadioGroup": {
"idx": 478,
"alias": [
"widget.radiogroup"
],
"alternates": []
},
"Ext.form.RadioManager": {
"idx": 476,
"alias": [],
"alternates": []
},
"Ext.form.action.Action": {
"idx": 451,
"alias": [],
"alternates": [
"Ext.form.Action"
]
},
"Ext.form.action.DirectAction": {
"idx": 479,
"alias": [],
"alternates": []
},
"Ext.form.action.DirectLoad": {
"idx": 480,
"alias": [
"formaction.directload"
],
"alternates": [
"Ext.form.Action.DirectLoad"
]
},
"Ext.form.action.DirectSubmit": {
"idx": 481,
"alias": [
"formaction.directsubmit"
],
"alternates": [
"Ext.form.Action.DirectSubmit"
]
},
"Ext.form.action.Load": {
"idx": 452,
"alias": [
"formaction.load"
],
"alternates": [
"Ext.form.Action.Load"
]
},
"Ext.form.action.StandardSubmit": {
"idx": 454,
"alias": [
"formaction.standardsubmit"
],
"alternates": []
},
"Ext.form.action.Submit": {
"idx": 453,
"alias": [
"formaction.submit"
],
"alternates": [
"Ext.form.Action.Submit"
]
},
"Ext.form.field.Base": {
"idx": 459,
"alias": [
"widget.field"
],
"alternates": [
"Ext.form.Field",
"Ext.form.BaseField"
]
},
"Ext.form.field.Checkbox": {
"idx": 471,
"alias": [
"widget.checkbox",
"widget.checkboxfield"
],
"alternates": [
"Ext.form.Checkbox"
]
},
"Ext.form.field.ComboBox": {
"idx": 497,
"alias": [
"widget.combo",
"widget.combobox"
],
"alternates": [
"Ext.form.ComboBox"
]
},
"Ext.form.field.Date": {
"idx": 500,
"alias": [
"widget.datefield"
],
"alternates": [
"Ext.form.DateField",
"Ext.form.Date"
]
},
"Ext.form.field.Display": {
"idx": 501,
"alias": [
"widget.displayfield"
],
"alternates": [
"Ext.form.DisplayField",
"Ext.form.Display"
]
},
"Ext.form.field.Field": {
"idx": 458,
"alias": [],
"alternates": []
},
"Ext.form.field.File": {
"idx": 504,
"alias": [
"widget.filefield",
"widget.fileuploadfield"
],
"alternates": [
"Ext.form.FileUploadField",
"Ext.ux.form.FileUploadField",
"Ext.form.File"
]
},
"Ext.form.field.FileButton": {
"idx": 502,
"alias": [
"widget.filebutton"
],
"alternates": []
},
"Ext.form.field.Hidden": {
"idx": 505,
"alias": [
"widget.hidden",
"widget.hiddenfield"
],
"alternates": [
"Ext.form.Hidden"
]
},
"Ext.form.field.HtmlEditor": {
"idx": 514,
"alias": [
"widget.htmleditor"
],
"alternates": [
"Ext.form.HtmlEditor"
]
},
"Ext.form.field.Number": {
"idx": 494,
"alias": [
"widget.numberfield"
],
"alternates": [
"Ext.form.NumberField",
"Ext.form.Number"
]
},
"Ext.form.field.Picker": {
"idx": 482,
"alias": [
"widget.pickerfield"
],
"alternates": [
"Ext.form.Picker"
]
},
"Ext.form.field.Radio": {
"idx": 477,
"alias": [
"widget.radio",
"widget.radiofield"
],
"alternates": [
"Ext.form.Radio"
]
},
"Ext.form.field.Spinner": {
"idx": 493,
"alias": [
"widget.spinnerfield"
],
"alternates": [
"Ext.form.Spinner"
]
},
"Ext.form.field.Tag": {
"idx": 516,
"alias": [
"widget.tagfield"
],
"alternates": []
},
"Ext.form.field.Text": {
"idx": 462,
"alias": [
"widget.textfield"
],
"alternates": [
"Ext.form.TextField",
"Ext.form.Text"
]
},
"Ext.form.field.TextArea": {
"idx": 463,
"alias": [
"widget.textarea",
"widget.textareafield"
],
"alternates": [
"Ext.form.TextArea"
]
},
"Ext.form.field.Time": {
"idx": 518,
"alias": [
"widget.timefield"
],
"alternates": [
"Ext.form.TimeField",
"Ext.form.Time"
]
},
"Ext.form.field.Trigger": {
"idx": 519,
"alias": [
"widget.trigger",
"widget.triggerfield"
],
"alternates": [
"Ext.form.TriggerField",
"Ext.form.TwinTriggerField",
"Ext.form.Trigger"
]
},
"Ext.form.field.VTypes": {
"idx": 460,
"alias": [],
"alternates": [
"Ext.form.VTypes"
]
},
"Ext.form.trigger.Component": {
"idx": 503,
"alias": [
"trigger.component"
],
"alternates": []
},
"Ext.form.trigger.Spinner": {
"idx": 492,
"alias": [
"trigger.spinner"
],
"alternates": []
},
"Ext.form.trigger.Trigger": {
"idx": 461,
"alias": [
"trigger.trigger"
],
"alternates": []
},
"Ext.fx.Anim": {
"idx": 72,
"alias": [],
"alternates": []
},
"Ext.fx.Animation": {
"idx": 297,
"alias": [],
"alternates": []
},
"Ext.fx.Animator": {
"idx": 67,
"alias": [],
"alternates": []
},
"Ext.fx.CubicBezier": {
"idx": 68,
"alias": [],
"alternates": []
},
"Ext.fx.DrawPath": {
"idx": 70,
"alias": [],
"alternates": []
},
"Ext.fx.Easing": {
"idx": 69,
"alias": [],
"alternates": []
},
"Ext.fx.Manager": {
"idx": 66,
"alias": [],
"alternates": []
},
"Ext.fx.PropertyHandler": {
"idx": 71,
"alias": [],
"alternates": []
},
"Ext.fx.Queue": {
"idx": 65,
"alias": [],
"alternates": []
},
"Ext.fx.Runner": {
"idx": 300,
"alias": [],
"alternates": []
},
"Ext.fx.State": {
"idx": 288,
"alias": [],
"alternates": []
},
"Ext.fx.animation.Abstract": {
"idx": 289,
"alias": [],
"alternates": []
},
"Ext.fx.animation.Cube": {
"idx": 301,
"alias": [
"animation.cube"
],
"alternates": []
},
"Ext.fx.animation.Fade": {
"idx": 292,
"alias": [
"animation.fade",
"animation.fadeIn"
],
"alternates": [
"Ext.fx.animation.FadeIn"
]
},
"Ext.fx.animation.FadeOut": {
"idx": 293,
"alias": [
"animation.fadeOut"
],
"alternates": []
},
"Ext.fx.animation.Flip": {
"idx": 294,
"alias": [
"animation.flip"
],
"alternates": []
},
"Ext.fx.animation.Pop": {
"idx": 295,
"alias": [
"animation.pop",
"animation.popIn"
],
"alternates": [
"Ext.fx.animation.PopIn"
]
},
"Ext.fx.animation.PopOut": {
"idx": 296,
"alias": [
"animation.popOut"
],
"alternates": []
},
"Ext.fx.animation.Slide": {
"idx": 290,
"alias": [
"animation.slide",
"animation.slideIn"
],
"alternates": [
"Ext.fx.animation.SlideIn"
]
},
"Ext.fx.animation.SlideOut": {
"idx": 291,
"alias": [
"animation.slideOut"
],
"alternates": []
},
"Ext.fx.animation.Wipe": {
"idx": 302,
"alias": [],
"alternates": [
"Ext.fx.animation.WipeIn"
]
},
"Ext.fx.animation.WipeOut": {
"idx": 303,
"alias": [],
"alternates": []
},
"Ext.fx.easing.Abstract": {
"idx": 96,
"alias": [],
"alternates": []
},
"Ext.fx.easing.Bounce": {
"idx": 304,
"alias": [],
"alternates": []
},
"Ext.fx.easing.BoundMomentum": {
"idx": 306,
"alias": [],
"alternates": []
},
"Ext.fx.easing.EaseIn": {
"idx": 307,
"alias": [
"easing.ease-in"
],
"alternates": []
},
"Ext.fx.easing.EaseOut": {
"idx": 308,
"alias": [
"easing.ease-out"
],
"alternates": []
},
"Ext.fx.easing.Easing": {
"idx": 309,
"alias": [],
"alternates": []
},
"Ext.fx.easing.Linear": {
"idx": 97,
"alias": [
"easing.linear"
],
"alternates": []
},
"Ext.fx.easing.Momentum": {
"idx": 305,
"alias": [],
"alternates": []
},
"Ext.fx.layout.Card": {
"idx": 319,
"alias": [],
"alternates": []
},
"Ext.fx.layout.card.Abstract": {
"idx": 310,
"alias": [],
"alternates": []
},
"Ext.fx.layout.card.Cover": {
"idx": 313,
"alias": [
"fx.layout.card.cover"
],
"alternates": []
},
"Ext.fx.layout.card.Cube": {
"idx": 320,
"alias": [
"fx.layout.card.cube"
],
"alternates": []
},
"Ext.fx.layout.card.Fade": {
"idx": 315,
"alias": [
"fx.layout.card.fade"
],
"alternates": []
},
"Ext.fx.layout.card.Flip": {
"idx": 316,
"alias": [
"fx.layout.card.flip"
],
"alternates": []
},
"Ext.fx.layout.card.Pop": {
"idx": 317,
"alias": [
"fx.layout.card.pop"
],
"alternates": []
},
"Ext.fx.layout.card.Reveal": {
"idx": 314,
"alias": [
"fx.layout.card.reveal"
],
"alternates": []
},
"Ext.fx.layout.card.Scroll": {
"idx": 318,
"alias": [
"fx.layout.card.scroll"
],
"alternates": []
},
"Ext.fx.layout.card.ScrollCover": {
"idx": 321,
"alias": [
"fx.layout.card.scrollcover"
],
"alternates": []
},
"Ext.fx.layout.card.ScrollReveal": {
"idx": 322,
"alias": [
"fx.layout.card.scrollreveal"
],
"alternates": []
},
"Ext.fx.layout.card.Slide": {
"idx": 312,
"alias": [
"fx.layout.card.slide"
],
"alternates": []
},
"Ext.fx.layout.card.Style": {
"idx": 311,
"alias": [],
"alternates": []
},
"Ext.fx.runner.Css": {
"idx": 298,
"alias": [],
"alternates": []
},
"Ext.fx.runner.CssAnimation": {
"idx": 323,
"alias": [],
"alternates": []
},
"Ext.fx.runner.CssTransition": {
"idx": 299,
"alias": [],
"alternates": [
"Ext.Animator"
]
},
"Ext.fx.target.Component": {
"idx": 64,
"alias": [],
"alternates": []
},
"Ext.fx.target.CompositeElement": {
"idx": 60,
"alias": [],
"alternates": []
},
"Ext.fx.target.CompositeElementCSS": {
"idx": 61,
"alias": [],
"alternates": []
},
"Ext.fx.target.CompositeSprite": {
"idx": 63,
"alias": [],
"alternates": []
},
"Ext.fx.target.Element": {
"idx": 58,
"alias": [],
"alternates": []
},
"Ext.fx.target.ElementCSS": {
"idx": 59,
"alias": [],
"alternates": []
},
"Ext.fx.target.Sprite": {
"idx": 62,
"alias": [],
"alternates": []
},
"Ext.fx.target.Target": {
"idx": 57,
"alias": [],
"alternates": []
},
"Ext.grid.CellContext": {
"idx": 520,
"alias": [],
"alternates": []
},
"Ext.grid.CellEditor": {
"idx": 521,
"alias": [],
"alternates": []
},
"Ext.grid.ColumnComponentLayout": {
"idx": 522,
"alias": [
"layout.columncomponent"
],
"alternates": []
},
"Ext.grid.ColumnLayout": {
"idx": 525,
"alias": [
"layout.gridcolumn"
],
"alternates": []
},
"Ext.grid.ColumnManager": {
"idx": 526,
"alias": [],
"alternates": [
"Ext.grid.ColumnModel"
]
},
"Ext.grid.NavigationModel": {
"idx": 527,
"alias": [
"view.navigation.grid"
],
"alternates": []
},
"Ext.grid.Panel": {
"idx": 533,
"alias": [
"widget.grid",
"widget.gridpanel"
],
"alternates": [
"Ext.list.ListView",
"Ext.ListView",
"Ext.grid.GridPanel"
]
},
"Ext.grid.RowContext": {
"idx": 534,
"alias": [],
"alternates": []
},
"Ext.grid.RowEditor": {
"idx": 536,
"alias": [
"widget.roweditor"
],
"alternates": []
},
"Ext.grid.RowEditorButtons": {
"idx": 535,
"alias": [
"widget.roweditorbuttons"
],
"alternates": []
},
"Ext.grid.Scroller": {
"idx": 537,
"alias": [],
"alternates": []
},
"Ext.grid.ViewDropZone": {
"idx": 539,
"alias": [],
"alternates": []
},
"Ext.grid.column.Action": {
"idx": 547,
"alias": [
"widget.actioncolumn"
],
"alternates": [
"Ext.grid.ActionColumn"
]
},
"Ext.grid.column.ActionProxy": {
"idx": 546,
"alias": [],
"alternates": []
},
"Ext.grid.column.Boolean": {
"idx": 548,
"alias": [
"widget.booleancolumn"
],
"alternates": [
"Ext.grid.BooleanColumn"
]
},
"Ext.grid.column.Check": {
"idx": 549,
"alias": [
"widget.checkcolumn"
],
"alternates": [
"Ext.ux.CheckColumn",
"Ext.grid.column.CheckColumn"
]
},
"Ext.grid.column.Column": {
"idx": 545,
"alias": [
"widget.gridcolumn"
],
"alternates": [
"Ext.grid.Column"
]
},
"Ext.grid.column.Date": {
"idx": 550,
"alias": [
"widget.datecolumn"
],
"alternates": [
"Ext.grid.DateColumn"
]
},
"Ext.grid.column.Number": {
"idx": 551,
"alias": [
"widget.numbercolumn"
],
"alternates": [
"Ext.grid.NumberColumn"
]
},
"Ext.grid.column.RowNumberer": {
"idx": 552,
"alias": [
"widget.rownumberer"
],
"alternates": [
"Ext.grid.RowNumberer"
]
},
"Ext.grid.column.Template": {
"idx": 553,
"alias": [
"widget.templatecolumn"
],
"alternates": [
"Ext.grid.TemplateColumn"
]
},
"Ext.grid.column.Widget": {
"idx": 554,
"alias": [
"widget.widgetcolumn"
],
"alternates": []
},
"Ext.grid.feature.AbstractSummary": {
"idx": 556,
"alias": [
"feature.abstractsummary"
],
"alternates": []
},
"Ext.grid.feature.Feature": {
"idx": 555,
"alias": [
"feature.feature"
],
"alternates": []
},
"Ext.grid.feature.GroupStore": {
"idx": 557,
"alias": [],
"alternates": []
},
"Ext.grid.feature.Grouping": {
"idx": 558,
"alias": [
"feature.grouping"
],
"alternates": []
},
"Ext.grid.feature.GroupingSummary": {
"idx": 559,
"alias": [
"feature.groupingsummary"
],
"alternates": []
},
"Ext.grid.feature.RowBody": {
"idx": 560,
"alias": [
"feature.rowbody"
],
"alternates": []
},
"Ext.grid.feature.Summary": {
"idx": 561,
"alias": [
"feature.summary"
],
"alternates": []
},
"Ext.grid.filters.Filters": {
"idx": 574,
"alias": [
"plugin.gridfilters"
],
"alternates": []
},
"Ext.grid.filters.filter.Base": {
"idx": 566,
"alias": [],
"alternates": []
},
"Ext.grid.filters.filter.Boolean": {
"idx": 568,
"alias": [
"grid.filter.boolean"
],
"alternates": []
},
"Ext.grid.filters.filter.Date": {
"idx": 570,
"alias": [
"grid.filter.date"
],
"alternates": []
},
"Ext.grid.filters.filter.List": {
"idx": 571,
"alias": [
"grid.filter.list"
],
"alternates": []
},
"Ext.grid.filters.filter.Number": {
"idx": 572,
"alias": [
"grid.filter.number",
"grid.filter.numeric"
],
"alternates": []
},
"Ext.grid.filters.filter.SingleFilter": {
"idx": 567,
"alias": [],
"alternates": []
},
"Ext.grid.filters.filter.String": {
"idx": 573,
"alias": [
"grid.filter.string"
],
"alternates": []
},
"Ext.grid.filters.filter.TriFilter": {
"idx": 569,
"alias": [],
"alternates": []
},
"Ext.grid.header.Container": {
"idx": 544,
"alias": [
"widget.headercontainer"
],
"alternates": []
},
"Ext.grid.header.DragZone": {
"idx": 541,
"alias": [],
"alternates": []
},
"Ext.grid.header.DropZone": {
"idx": 542,
"alias": [],
"alternates": []
},
"Ext.grid.locking.HeaderContainer": {
"idx": 575,
"alias": [],
"alternates": []
},
"Ext.grid.locking.Lockable": {
"idx": 578,
"alias": [],
"alternates": [
"Ext.grid.Lockable"
]
},
"Ext.grid.locking.RowSynchronizer": {
"idx": 529,
"alias": [],
"alternates": []
},
"Ext.grid.locking.View": {
"idx": 576,
"alias": [],
"alternates": [
"Ext.grid.LockingView"
]
},
"Ext.grid.plugin.BufferedRenderer": {
"idx": 579,
"alias": [
"plugin.bufferedrenderer"
],
"alternates": []
},
"Ext.grid.plugin.CellEditing": {
"idx": 581,
"alias": [
"plugin.cellediting"
],
"alternates": []
},
"Ext.grid.plugin.Clipboard": {
"idx": 583,
"alias": [
"plugin.clipboard"
],
"alternates": []
},
"Ext.grid.plugin.DragDrop": {
"idx": 584,
"alias": [
"plugin.gridviewdragdrop"
],
"alternates": []
},
"Ext.grid.plugin.Editing": {
"idx": 580,
"alias": [
"editing.editing"
],
"alternates": []
},
"Ext.grid.plugin.HeaderReorderer": {
"idx": 543,
"alias": [
"plugin.gridheaderreorderer"
],
"alternates": []
},
"Ext.grid.plugin.HeaderResizer": {
"idx": 540,
"alias": [
"plugin.gridheaderresizer"
],
"alternates": []
},
"Ext.grid.plugin.RowEditing": {
"idx": 585,
"alias": [
"plugin.rowediting"
],
"alternates": []
},
"Ext.grid.plugin.RowExpander": {
"idx": 586,
"alias": [
"plugin.rowexpander"
],
"alternates": []
},
"Ext.grid.plugin.RowWidget": {
"idx": 587,
"alias": [
"plugin.rowwidget"
],
"alternates": []
},
"Ext.grid.property.Grid": {
"idx": 588,
"alias": [
"widget.propertygrid"
],
"alternates": [
"Ext.grid.PropertyGrid"
]
},
"Ext.grid.property.HeaderContainer": {
"idx": 589,
"alias": [],
"alternates": [
"Ext.grid.PropertyColumnModel"
]
},
"Ext.grid.property.Property": {
"idx": 590,
"alias": [],
"alternates": [
"Ext.PropGridProperty"
]
},
"Ext.grid.property.Reader": {
"idx": 591,
"alias": [],
"alternates": []
},
"Ext.grid.property.Store": {
"idx": 592,
"alias": [],
"alternates": [
"Ext.grid.PropertyStore"
]
},
"Ext.grid.selection.Cells": {
"idx": 594,
"alias": [],
"alternates": []
},
"Ext.grid.selection.Columns": {
"idx": 595,
"alias": [],
"alternates": []
},
"Ext.grid.selection.Replicator": {
"idx": 596,
"alias": [
"plugin.selectionreplicator"
],
"alternates": []
},
"Ext.grid.selection.Rows": {
"idx": 597,
"alias": [],
"alternates": []
},
"Ext.grid.selection.Selection": {
"idx": 593,
"alias": [],
"alternates": []
},
"Ext.grid.selection.SelectionExtender": {
"idx": 598,
"alias": [],
"alternates": []
},
"Ext.grid.selection.SpreadsheetModel": {
"idx": 599,
"alias": [
"selection.spreadsheet"
],
"alternates": []
},
"Ext.layout.Context": {
"idx": 602,
"alias": [],
"alternates": []
},
"Ext.layout.ContextItem": {
"idx": 601,
"alias": [],
"alternates": []
},
"Ext.layout.Layout": {
"idx": 373,
"alias": [],
"alternates": []
},
"Ext.layout.SizeModel": {
"idx": 372,
"alias": [],
"alternates": []
},
"Ext.layout.component.Auto": {
"idx": 385,
"alias": [
"layout.autocomponent"
],
"alternates": []
},
"Ext.layout.component.Body": {
"idx": 604,
"alias": [
"layout.body"
],
"alternates": []
},
"Ext.layout.component.BoundList": {
"idx": 489,
"alias": [
"layout.boundlist"
],
"alternates": []
},
"Ext.layout.component.Component": {
"idx": 384,
"alias": [],
"alternates": []
},
"Ext.layout.component.Dock": {
"idx": 419,
"alias": [
"layout.dock"
],
"alternates": [
"Ext.layout.component.AbstractDock"
]
},
"Ext.layout.component.FieldSet": {
"idx": 605,
"alias": [
"layout.fieldset"
],
"alternates": []
},
"Ext.layout.component.ProgressBar": {
"idx": 386,
"alias": [
"layout.progressbar"
],
"alternates": []
},
"Ext.layout.component.field.FieldContainer": {
"idx": 467,
"alias": [
"layout.fieldcontainer"
],
"alternates": []
},
"Ext.layout.component.field.HtmlEditor": {
"idx": 511,
"alias": [
"layout.htmleditor"
],
"alternates": []
},
"Ext.layout.container.Absolute": {
"idx": 606,
"alias": [
"layout.absolute"
],
"alternates": [
"Ext.layout.AbsoluteLayout"
]
},
"Ext.layout.container.Accordion": {
"idx": 607,
"alias": [
"layout.accordion"
],
"alternates": [
"Ext.layout.AccordionLayout"
]
},
"Ext.layout.container.Anchor": {
"idx": 429,
"alias": [
"layout.anchor"
],
"alternates": [
"Ext.layout.AnchorLayout"
]
},
"Ext.layout.container.Auto": {
"idx": 375,
"alias": [
"layout.auto",
"layout.autocontainer"
],
"alternates": []
},
"Ext.layout.container.Border": {
"idx": 609,
"alias": [
"layout.border"
],
"alternates": [
"Ext.layout.BorderLayout"
]
},
"Ext.layout.container.Box": {
"idx": 408,
"alias": [
"layout.box"
],
"alternates": [
"Ext.layout.BoxLayout"
]
},
"Ext.layout.container.Card": {
"idx": 610,
"alias": [
"layout.card"
],
"alternates": [
"Ext.layout.CardLayout"
]
},
"Ext.layout.container.Center": {
"idx": 611,
"alias": [
"layout.center",
"layout.ux.center"
],
"alternates": [
"Ext.ux.layout.Center"
]
},
"Ext.layout.container.CheckboxGroup": {
"idx": 469,
"alias": [
"layout.checkboxgroup"
],
"alternates": []
},
"Ext.layout.container.Column": {
"idx": 432,
"alias": [
"layout.column"
],
"alternates": [
"Ext.layout.ColumnLayout"
]
},
"Ext.layout.container.ColumnSplitter": {
"idx": 436,
"alias": [
"widget.columnsplitter"
],
"alternates": []
},
"Ext.layout.container.ColumnSplitterTracker": {
"idx": 435,
"alias": [],
"alternates": []
},
"Ext.layout.container.Container": {
"idx": 374,
"alias": [
"layout.container"
],
"alternates": [
"Ext.layout.ContainerLayout"
]
},
"Ext.layout.container.Dashboard": {
"idx": 437,
"alias": [
"layout.dashboard"
],
"alternates": []
},
"Ext.layout.container.Editor": {
"idx": 378,
"alias": [
"layout.editor"
],
"alternates": []
},
"Ext.layout.container.Fit": {
"idx": 523,
"alias": [
"layout.fit"
],
"alternates": [
"Ext.layout.FitLayout"
]
},
"Ext.layout.container.Form": {
"idx": 612,
"alias": [
"layout.form"
],
"alternates": [
"Ext.layout.FormLayout"
]
},
"Ext.layout.container.HBox": {
"idx": 409,
"alias": [
"layout.hbox"
],
"alternates": [
"Ext.layout.HBoxLayout"
]
},
"Ext.layout.container.SegmentedButton": {
"idx": 395,
"alias": [
"layout.segmentedbutton"
],
"alternates": []
},
"Ext.layout.container.Table": {
"idx": 423,
"alias": [
"layout.table"
],
"alternates": [
"Ext.layout.TableLayout"
]
},
"Ext.layout.container.VBox": {
"idx": 410,
"alias": [
"layout.vbox"
],
"alternates": [
"Ext.layout.VBoxLayout"
]
},
"Ext.layout.container.border.Region": {
"idx": 112,
"alias": [],
"alternates": []
},
"Ext.layout.container.boxOverflow.Menu": {
"idx": 513,
"alias": [
"box.overflow.Menu",
"box.overflow.menu"
],
"alternates": [
"Ext.layout.boxOverflow.Menu"
]
},
"Ext.layout.container.boxOverflow.None": {
"idx": 404,
"alias": [
"box.overflow.None",
"box.overflow.none"
],
"alternates": [
"Ext.layout.boxOverflow.None"
]
},
"Ext.layout.container.boxOverflow.Scroller": {
"idx": 405,
"alias": [
"box.overflow.Scroller",
"box.overflow.scroller"
],
"alternates": [
"Ext.layout.boxOverflow.Scroller"
]
},
"Ext.list.AbstractTreeItem": {
"idx": 324,
"alias": [],
"alternates": []
},
"Ext.list.RootTreeItem": {
"idx": 325,
"alias": [],
"alternates": []
},
"Ext.list.Tree": {
"idx": 327,
"alias": [
"widget.treelist"
],
"alternates": []
},
"Ext.list.TreeItem": {
"idx": 326,
"alias": [
"widget.treelistitem"
],
"alternates": []
},
"Ext.menu.Bar": {
"idx": 613,
"alias": [
"widget.menubar"
],
"alternates": []
},
"Ext.menu.CheckItem": {
"idx": 563,
"alias": [
"widget.menucheckitem"
],
"alternates": []
},
"Ext.menu.ColorPicker": {
"idx": 614,
"alias": [
"widget.colormenu"
],
"alternates": []
},
"Ext.menu.DatePicker": {
"idx": 615,
"alias": [
"widget.datemenu"
],
"alternates": []
},
"Ext.menu.Item": {
"idx": 562,
"alias": [
"widget.menuitem"
],
"alternates": [
"Ext.menu.TextItem"
]
},
"Ext.menu.Manager": {
"idx": 390,
"alias": [],
"alternates": [
"Ext.menu.MenuMgr"
]
},
"Ext.menu.Menu": {
"idx": 565,
"alias": [
"widget.menu"
],
"alternates": []
},
"Ext.menu.Separator": {
"idx": 564,
"alias": [
"widget.menuseparator"
],
"alternates": []
},
"Ext.mixin.Accessible": {
"idx": 109,
"alias": [],
"alternates": []
},
"Ext.mixin.Bindable": {
"idx": 80,
"alias": [],
"alternates": []
},
"Ext.mixin.ComponentDelegation": {
"idx": 81,
"alias": [],
"alternates": []
},
"Ext.mixin.ConfigState": {
"idx": 328,
"alias": [],
"alternates": []
},
"Ext.mixin.Container": {
"idx": 329,
"alias": [],
"alternates": []
},
"Ext.mixin.Dirty": {
"idx": 187,
"alias": [],
"alternates": []
},
"Ext.mixin.Factoryable": {
"idx": 12,
"alias": [],
"alternates": []
},
"Ext.mixin.Hookable": {
"idx": 330,
"alias": [],
"alternates": []
},
"Ext.mixin.Identifiable": {
"idx": 3,
"alias": [],
"alternates": []
},
"Ext.mixin.Inheritable": {
"idx": 79,
"alias": [],
"alternates": []
},
"Ext.mixin.Keyboard": {
"idx": 110,
"alias": [],
"alternates": []
},
"Ext.mixin.Mashup": {
"idx": 331,
"alias": [],
"alternates": []
},
"Ext.mixin.Observable": {
"idx": 4,
"alias": [],
"alternates": []
},
"Ext.mixin.Pluggable": {
"idx": 82,
"alias": [],
"alternates": []
},
"Ext.mixin.Queryable": {
"idx": 226,
"alias": [],
"alternates": []
},
"Ext.mixin.Responsive": {
"idx": 332,
"alias": [],
"alternates": []
},
"Ext.mixin.Selectable": {
"idx": 333,
"alias": [],
"alternates": []
},
"Ext.mixin.StyleCacher": {
"idx": 334,
"alias": [],
"alternates": []
},
"Ext.mixin.Templatable": {
"idx": 38,
"alias": [],
"alternates": []
},
"Ext.mixin.Traversable": {
"idx": 335,
"alias": [],
"alternates": []
},
"Ext.panel.Bar": {
"idx": 397,
"alias": [],
"alternates": []
},
"Ext.panel.DD": {
"idx": 418,
"alias": [],
"alternates": []
},
"Ext.panel.Header": {
"idx": 403,
"alias": [
"widget.header"
],
"alternates": []
},
"Ext.panel.Panel": {
"idx": 422,
"alias": [
"widget.panel"
],
"alternates": [
"Ext.Panel"
]
},
"Ext.panel.Pinnable": {
"idx": 616,
"alias": [],
"alternates": []
},
"Ext.panel.Proxy": {
"idx": 417,
"alias": [],
"alternates": [
"Ext.dd.PanelProxy"
]
},
"Ext.panel.Table": {
"idx": 524,
"alias": [
"widget.tablepanel"
],
"alternates": []
},
"Ext.panel.Title": {
"idx": 398,
"alias": [
"widget.title"
],
"alternates": []
},
"Ext.panel.Tool": {
"idx": 399,
"alias": [
"widget.tool"
],
"alternates": []
},
"Ext.parse.Parser": {
"idx": 206,
"alias": [],
"alternates": []
},
"Ext.parse.Symbol": {
"idx": 200,
"alias": [],
"alternates": []
},
"Ext.parse.Tokenizer": {
"idx": 199,
"alias": [],
"alternates": []
},
"Ext.parse.symbol.Constant": {
"idx": 201,
"alias": [],
"alternates": []
},
"Ext.parse.symbol.Infix": {
"idx": 202,
"alias": [],
"alternates": []
},
"Ext.parse.symbol.InfixRight": {
"idx": 203,
"alias": [],
"alternates": []
},
"Ext.parse.symbol.Paren": {
"idx": 204,
"alias": [],
"alternates": []
},
"Ext.parse.symbol.Prefix": {
"idx": 205,
"alias": [],
"alternates": []
},
"Ext.perf.Accumulator": {
"idx": 336,
"alias": [],
"alternates": []
},
"Ext.perf.Monitor": {
"idx": 337,
"alias": [],
"alternates": [
"Ext.Perf"
]
},
"Ext.picker.Color": {
"idx": 510,
"alias": [
"widget.colorpicker"
],
"alternates": [
"Ext.ColorPalette"
]
},
"Ext.picker.Date": {
"idx": 499,
"alias": [
"widget.datepicker"
],
"alternates": [
"Ext.DatePicker"
]
},
"Ext.picker.Month": {
"idx": 498,
"alias": [
"widget.monthpicker"
],
"alternates": [
"Ext.MonthPicker"
]
},
"Ext.picker.Time": {
"idx": 517,
"alias": [
"widget.timepicker"
],
"alternates": []
},
"Ext.plugin.Abstract": {
"idx": 338,
"alias": [],
"alternates": [
"Ext.AbstractPlugin"
]
},
"Ext.plugin.AbstractClipboard": {
"idx": 582,
"alias": [],
"alternates": []
},
"Ext.plugin.LazyItems": {
"idx": 339,
"alias": [
"plugin.lazyitems"
],
"alternates": []
},
"Ext.plugin.Manager": {
"idx": 617,
"alias": [],
"alternates": [
"Ext.PluginManager",
"Ext.PluginMgr"
]
},
"Ext.plugin.MouseEnter": {
"idx": 340,
"alias": [
"plugin.mouseenter"
],
"alternates": []
},
"Ext.plugin.Responsive": {
"idx": 426,
"alias": [
"plugin.responsive"
],
"alternates": []
},
"Ext.plugin.Viewport": {
"idx": 427,
"alias": [
"plugin.viewport"
],
"alternates": []
},
"Ext.promise.Consequence": {
"idx": 7,
"alias": [],
"alternates": []
},
"Ext.promise.Deferred": {
"idx": 8,
"alias": [],
"alternates": []
},
"Ext.promise.Promise": {
"idx": 9,
"alias": [],
"alternates": []
},
"Ext.resizer.BorderSplitter": {
"idx": 608,
"alias": [
"widget.bordersplitter"
],
"alternates": []
},
"Ext.resizer.BorderSplitterTracker": {
"idx": 618,
"alias": [],
"alternates": []
},
"Ext.resizer.Handle": {
"idx": 619,
"alias": [],
"alternates": []
},
"Ext.resizer.ResizeTracker": {
"idx": 620,
"alias": [],
"alternates": []
},
"Ext.resizer.Resizer": {
"idx": 621,
"alias": [],
"alternates": [
"Ext.Resizable"
]
},
"Ext.resizer.Splitter": {
"idx": 407,
"alias": [
"widget.splitter"
],
"alternates": []
},
"Ext.resizer.SplitterTracker": {
"idx": 434,
"alias": [],
"alternates": []
},
"Ext.scroll.LockingScroller": {
"idx": 577,
"alias": [
"scroller.locking"
],
"alternates": []
},
"Ext.scroll.Scroller": {
"idx": 101,
"alias": [
"scroller.scroller"
],
"alternates": []
},
"Ext.scroll.TableScroller": {
"idx": 531,
"alias": [
"scroller.table"
],
"alternates": []
},
"Ext.selection.CellModel": {
"idx": 622,
"alias": [
"selection.cellmodel"
],
"alternates": []
},
"Ext.selection.CheckboxModel": {
"idx": 624,
"alias": [
"selection.checkboxmodel"
],
"alternates": []
},
"Ext.selection.DataViewModel": {
"idx": 484,
"alias": [
"selection.dataviewmodel"
],
"alternates": []
},
"Ext.selection.Model": {
"idx": 483,
"alias": [
"selection.abstract"
],
"alternates": [
"Ext.AbstractSelectionModel"
]
},
"Ext.selection.RowModel": {
"idx": 623,
"alias": [
"selection.rowmodel"
],
"alternates": []
},
"Ext.selection.TreeModel": {
"idx": 625,
"alias": [
"selection.treemodel"
],
"alternates": []
},
"Ext.slider.Multi": {
"idx": 628,
"alias": [
"widget.multislider"
],
"alternates": [
"Ext.slider.MultiSlider"
]
},
"Ext.slider.Single": {
"idx": 629,
"alias": [
"widget.slider",
"widget.sliderfield"
],
"alternates": [
"Ext.Slider",
"Ext.form.SliderField",
"Ext.slider.SingleSlider",
"Ext.slider.Slider"
]
},
"Ext.slider.Thumb": {
"idx": 626,
"alias": [],
"alternates": []
},
"Ext.slider.Tip": {
"idx": 627,
"alias": [
"widget.slidertip"
],
"alternates": []
},
"Ext.slider.Widget": {
"idx": 630,
"alias": [
"widget.sliderwidget"
],
"alternates": []
},
"Ext.sparkline.Bar": {
"idx": 349,
"alias": [
"widget.sparklinebar"
],
"alternates": []
},
"Ext.sparkline.BarBase": {
"idx": 347,
"alias": [],
"alternates": []
},
"Ext.sparkline.Base": {
"idx": 346,
"alias": [
"widget.sparkline"
],
"alternates": []
},
"Ext.sparkline.Box": {
"idx": 350,
"alias": [
"widget.sparklinebox"
],
"alternates": []
},
"Ext.sparkline.Bullet": {
"idx": 351,
"alias": [
"widget.sparklinebullet"
],
"alternates": []
},
"Ext.sparkline.CanvasBase": {
"idx": 342,
"alias": [],
"alternates": []
},
"Ext.sparkline.CanvasCanvas": {
"idx": 343,
"alias": [],
"alternates": []
},
"Ext.sparkline.Discrete": {
"idx": 352,
"alias": [
"widget.sparklinediscrete"
],
"alternates": []
},
"Ext.sparkline.Line": {
"idx": 353,
"alias": [
"widget.sparklineline"
],
"alternates": []
},
"Ext.sparkline.Pie": {
"idx": 354,
"alias": [
"widget.sparklinepie"
],
"alternates": []
},
"Ext.sparkline.RangeMap": {
"idx": 348,
"alias": [],
"alternates": []
},
"Ext.sparkline.Shape": {
"idx": 341,
"alias": [],
"alternates": []
},
"Ext.sparkline.TriState": {
"idx": 355,
"alias": [
"widget.sparklinetristate"
],
"alternates": []
},
"Ext.sparkline.VmlCanvas": {
"idx": 344,
"alias": [],
"alternates": []
},
"Ext.state.CookieProvider": {
"idx": 631,
"alias": [],
"alternates": []
},
"Ext.state.LocalStorageProvider": {
"idx": 632,
"alias": [
"state.localstorage"
],
"alternates": []
},
"Ext.state.Manager": {
"idx": 106,
"alias": [],
"alternates": []
},
"Ext.state.Provider": {
"idx": 105,
"alias": [],
"alternates": []
},
"Ext.state.Stateful": {
"idx": 107,
"alias": [],
"alternates": []
},
"Ext.tab.Bar": {
"idx": 634,
"alias": [
"widget.tabbar"
],
"alternates": []
},
"Ext.tab.Panel": {
"idx": 635,
"alias": [
"widget.tabpanel"
],
"alternates": [
"Ext.TabPanel"
]
},
"Ext.tab.Tab": {
"idx": 633,
"alias": [
"widget.tab"
],
"alternates": []
},
"Ext.tip.QuickTip": {
"idx": 508,
"alias": [
"widget.quicktip"
],
"alternates": [
"Ext.QuickTip"
]
},
"Ext.tip.QuickTipManager": {
"idx": 509,
"alias": [],
"alternates": [
"Ext.QuickTips"
]
},
"Ext.tip.Tip": {
"idx": 506,
"alias": [
"widget.tip"
],
"alternates": [
"Ext.Tip"
]
},
"Ext.tip.ToolTip": {
"idx": 507,
"alias": [
"widget.tooltip"
],
"alternates": [
"Ext.ToolTip"
]
},
"Ext.toolbar.Breadcrumb": {
"idx": 636,
"alias": [
"widget.breadcrumb"
],
"alternates": []
},
"Ext.toolbar.Fill": {
"idx": 637,
"alias": [
"widget.tbfill"
],
"alternates": [
"Ext.Toolbar.Fill"
]
},
"Ext.toolbar.Item": {
"idx": 490,
"alias": [
"widget.tbitem"
],
"alternates": [
"Ext.Toolbar.Item"
]
},
"Ext.toolbar.Paging": {
"idx": 495,
"alias": [
"widget.pagingtoolbar"
],
"alternates": [
"Ext.PagingToolbar"
]
},
"Ext.toolbar.Separator": {
"idx": 512,
"alias": [
"widget.tbseparator"
],
"alternates": [
"Ext.Toolbar.Separator"
]
},
"Ext.toolbar.Spacer": {
"idx": 638,
"alias": [
"widget.tbspacer"
],
"alternates": [
"Ext.Toolbar.Spacer"
]
},
"Ext.toolbar.TextItem": {
"idx": 491,
"alias": [
"widget.tbtext"
],
"alternates": [
"Ext.Toolbar.TextItem"
]
},
"Ext.toolbar.Toolbar": {
"idx": 411,
"alias": [
"widget.toolbar"
],
"alternates": [
"Ext.Toolbar"
]
},
"Ext.tree.Column": {
"idx": 639,
"alias": [
"widget.treecolumn"
],
"alternates": []
},
"Ext.tree.NavigationModel": {
"idx": 640,
"alias": [
"view.navigation.tree"
],
"alternates": []
},
"Ext.tree.Panel": {
"idx": 642,
"alias": [
"widget.treepanel"
],
"alternates": [
"Ext.tree.TreePanel",
"Ext.TreePanel"
]
},
"Ext.tree.View": {
"idx": 641,
"alias": [
"widget.treeview"
],
"alternates": []
},
"Ext.tree.ViewDragZone": {
"idx": 644,
"alias": [],
"alternates": []
},
"Ext.tree.ViewDropZone": {
"idx": 645,
"alias": [],
"alternates": []
},
"Ext.tree.plugin.TreeViewDragDrop": {
"idx": 646,
"alias": [
"plugin.treeviewdragdrop"
],
"alternates": []
},
"Ext.util.AbstractMixedCollection": {
"idx": 52,
"alias": [],
"alternates": []
},
"Ext.util.Animate": {
"idx": 73,
"alias": [],
"alternates": []
},
"Ext.util.Bag": {
"idx": 178,
"alias": [],
"alternates": []
},
"Ext.util.Base64": {
"idx": 356,
"alias": [],
"alternates": []
},
"Ext.util.CSS": {
"idx": 95,
"alias": [],
"alternates": []
},
"Ext.util.CSV": {
"idx": 358,
"alias": [],
"alternates": []
},
"Ext.util.ClickRepeater": {
"idx": 391,
"alias": [],
"alternates": []
},
"Ext.util.Collection": {
"idx": 119,
"alias": [],
"alternates": []
},
"Ext.util.CollectionKey": {
"idx": 117,
"alias": [],
"alternates": []
},
"Ext.util.Color": {
"idx": 345,
"alias": [],
"alternates": [
"Ext.draw.Color"
]
},
"Ext.util.ComponentDragger": {
"idx": 455,
"alias": [],
"alternates": []
},
"Ext.util.Cookies": {
"idx": 647,
"alias": [],
"alternates": []
},
"Ext.util.DelimitedValue": {
"idx": 357,
"alias": [],
"alternates": []
},
"Ext.util.ElementContainer": {
"idx": 103,
"alias": [],
"alternates": []
},
"Ext.util.Event": {
"idx": 2,
"alias": [],
"alternates": []
},
"Ext.util.Filter": {
"idx": 50,
"alias": [],
"alternates": []
},
"Ext.util.FilterCollection": {
"idx": 162,
"alias": [],
"alternates": []
},
"Ext.util.Floating": {
"idx": 102,
"alias": [],
"alternates": []
},
"Ext.util.Fly": {
"idx": 198,
"alias": [],
"alternates": []
},
"Ext.util.Focusable": {
"idx": 108,
"alias": [],
"alternates": []
},
"Ext.util.FocusableContainer": {
"idx": 402,
"alias": [],
"alternates": []
},
"Ext.util.Format": {
"idx": 86,
"alias": [],
"alternates": []
},
"Ext.util.Group": {
"idx": 160,
"alias": [],
"alternates": []
},
"Ext.util.GroupCollection": {
"idx": 163,
"alias": [],
"alternates": []
},
"Ext.util.Grouper": {
"idx": 118,
"alias": [],
"alternates": []
},
"Ext.util.HashMap": {
"idx": 5,
"alias": [],
"alternates": []
},
"Ext.util.History": {
"idx": 171,
"alias": [],
"alternates": [
"Ext.History"
]
},
"Ext.util.Inflector": {
"idx": 126,
"alias": [],
"alternates": []
},
"Ext.util.ItemCollection": {
"idx": 359,
"alias": [],
"alternates": [
"Ext.ItemCollection"
]
},
"Ext.util.KeyMap": {
"idx": 400,
"alias": [],
"alternates": [
"Ext.KeyMap"
]
},
"Ext.util.KeyNav": {
"idx": 401,
"alias": [],
"alternates": [
"Ext.KeyNav"
]
},
"Ext.util.LocalStorage": {
"idx": 360,
"alias": [],
"alternates": []
},
"Ext.util.LruCache": {
"idx": 22,
"alias": [],
"alternates": []
},
"Ext.util.Memento": {
"idx": 420,
"alias": [],
"alternates": []
},
"Ext.util.MixedCollection": {
"idx": 55,
"alias": [],
"alternates": []
},
"Ext.util.ObjectTemplate": {
"idx": 120,
"alias": [],
"alternates": []
},
"Ext.util.Observable": {
"idx": 51,
"alias": [],
"alternates": []
},
"Ext.util.Offset": {
"idx": 32,
"alias": [],
"alternates": []
},
"Ext.util.PaintMonitor": {
"idx": 47,
"alias": [],
"alternates": []
},
"Ext.util.Point": {
"idx": 34,
"alias": [],
"alternates": []
},
"Ext.util.Positionable": {
"idx": 25,
"alias": [],
"alternates": []
},
"Ext.util.ProtoElement": {
"idx": 93,
"alias": [],
"alternates": []
},
"Ext.util.Queue": {
"idx": 600,
"alias": [],
"alternates": []
},
"Ext.util.Region": {
"idx": 33,
"alias": [],
"alternates": []
},
"Ext.util.Renderable": {
"idx": 104,
"alias": [],
"alternates": []
},
"Ext.util.Schedulable": {
"idx": 189,
"alias": [],
"alternates": []
},
"Ext.util.Scheduler": {
"idx": 179,
"alias": [],
"alternates": []
},
"Ext.util.SizeMonitor": {
"idx": 43,
"alias": [],
"alternates": []
},
"Ext.util.Sortable": {
"idx": 54,
"alias": [],
"alternates": []
},
"Ext.util.Sorter": {
"idx": 53,
"alias": [],
"alternates": []
},
"Ext.util.SorterCollection": {
"idx": 161,
"alias": [],
"alternates": []
},
"Ext.util.StoreHolder": {
"idx": 382,
"alias": [],
"alternates": []
},
"Ext.util.TSV": {
"idx": 361,
"alias": [],
"alternates": []
},
"Ext.util.TaskManager": {
"idx": 362,
"alias": [],
"alternates": [
"Ext.TaskManager"
]
},
"Ext.util.TaskRunner": {
"idx": 56,
"alias": [],
"alternates": []
},
"Ext.util.TextMetrics": {
"idx": 363,
"alias": [],
"alternates": []
},
"Ext.util.Translatable": {
"idx": 367,
"alias": [],
"alternates": []
},
"Ext.util.XTemplateCompiler": {
"idx": 89,
"alias": [],
"alternates": []
},
"Ext.util.XTemplateParser": {
"idx": 88,
"alias": [],
"alternates": []
},
"Ext.util.paintmonitor.Abstract": {
"idx": 45,
"alias": [],
"alternates": []
},
"Ext.util.paintmonitor.CssAnimation": {
"idx": 46,
"alias": [],
"alternates": []
},
"Ext.util.paintmonitor.OverflowChange": {
"idx": 368,
"alias": [],
"alternates": []
},
"Ext.util.sizemonitor.Abstract": {
"idx": 40,
"alias": [],
"alternates": []
},
"Ext.util.sizemonitor.OverflowChange": {
"idx": 42,
"alias": [],
"alternates": []
},
"Ext.util.sizemonitor.Scroll": {
"idx": 41,
"alias": [],
"alternates": []
},
"Ext.util.translatable.Abstract": {
"idx": 98,
"alias": [],
"alternates": []
},
"Ext.util.translatable.CssPosition": {
"idx": 366,
"alias": [],
"alternates": []
},
"Ext.util.translatable.CssTransform": {
"idx": 364,
"alias": [],
"alternates": []
},
"Ext.util.translatable.Dom": {
"idx": 99,
"alias": [],
"alternates": []
},
"Ext.util.translatable.ScrollParent": {
"idx": 365,
"alias": [],
"alternates": []
},
"Ext.util.translatable.ScrollPosition": {
"idx": 100,
"alias": [],
"alternates": []
},
"Ext.view.AbstractView": {
"idx": 486,
"alias": [],
"alternates": []
},
"Ext.view.BoundList": {
"idx": 496,
"alias": [
"widget.boundlist"
],
"alternates": [
"Ext.BoundList"
]
},
"Ext.view.BoundListKeyNav": {
"idx": 488,
"alias": [
"view.navigation.boundlist"
],
"alternates": []
},
"Ext.view.DragZone": {
"idx": 643,
"alias": [],
"alternates": []
},
"Ext.view.DropZone": {
"idx": 538,
"alias": [],
"alternates": []
},
"Ext.view.MultiSelector": {
"idx": 649,
"alias": [
"widget.multiselector"
],
"alternates": []
},
"Ext.view.MultiSelectorSearch": {
"idx": 648,
"alias": [
"widget.multiselector-search"
],
"alternates": []
},
"Ext.view.NavigationModel": {
"idx": 485,
"alias": [
"view.navigation.default"
],
"alternates": []
},
"Ext.view.NodeCache": {
"idx": 530,
"alias": [],
"alternates": []
},
"Ext.view.Table": {
"idx": 532,
"alias": [
"widget.gridview",
"widget.tableview"
],
"alternates": [
"Ext.grid.View"
]
},
"Ext.view.TableLayout": {
"idx": 528,
"alias": [
"layout.tableview"
],
"alternates": []
},
"Ext.view.TagKeyNav": {
"idx": 515,
"alias": [
"view.navigation.tagfield"
],
"alternates": []
},
"Ext.view.View": {
"idx": 487,
"alias": [
"widget.dataview"
],
"alternates": [
"Ext.DataView"
]
},
"Ext.window.MessageBox": {
"idx": 464,
"alias": [
"widget.messagebox"
],
"alternates": []
},
"Ext.window.Toast": {
"idx": 650,
"alias": [
"widget.toast"
],
"alternates": []
},
"Ext.window.Window": {
"idx": 456,
"alias": [
"widget.window"
],
"alternates": [
"Ext.Window"
]
}
},
"packages": {
"classic": {
"build": {
"dir": "${package.output}"
},
"creator": "Sencha",
"namespace": "Ext",
"requires": [
"ext",
"core",
"classic"
],
"type": "toolkit",
"version": "6.2.0.981"
},
"cmd": {
"version": "6.2.0.103"
},
"core": {
"alternateName": [
"sencha-core"
],
"creator": "Sencha",
"requires": [
"ext"
],
"type": "code",
"version": "6.2.0.981"
},
"ext": {
"build": {
"dir": "${package.output.base}"
},
"creator": "Sencha",
"license": "gpl",
"namespace": "Ext",
"requires": [],
"resource": {
"paths": [
"resources"
]
},
"type": "framework",
"version": "6.2.0.981"
}
},
"bootRelative": true
});
// @tag core
// @define Ext.Boot
var Ext = Ext || {};
//<editor-fold desc="Boot">
/**
* @class Ext.Boot
* @singleton
* @private
*/
Ext.Boot = Ext.Boot || (function(emptyFn) {
var doc = document,
_emptyArray = [],
_config = {
/**
* @cfg {Boolean} [disableCaching=true]
* If `true` current timestamp is added to script URL's to prevent caching.
* In debug builds, adding a "cache" or "disableCacheBuster" query parameter
* to the page's URL will set this to `false`.
*/
disableCaching: (/[?&](?:cache|disableCacheBuster)\b/i.test(location.search) || !(/http[s]?\:/i.test(location.href)) || /(^|[ ;])ext-cache=1/.test(doc.cookie)) ? false : true,
/**
* @cfg {String} [disableCachingParam="_dc"]
* The query parameter name for the cache buster's timestamp.
*/
disableCachingParam: '_dc',
/**
* @cfg {Boolean} loadDelay
* Millisecond delay between asynchronous script injection (prevents stack
* overflow on some user agents) 'false' disables delay but potentially
* increases stack load.
*/
loadDelay: false,
/**
* @cfg {Boolean} preserveScripts
* `false` to remove asynchronously loaded scripts, `true` to retain script
* element for browser debugger compatibility and improved load performance.
*/
preserveScripts: true,
/**
* @cfg {String} [charset=UTF-8]
* Optional charset to specify encoding of dynamic content.
*/
charset: 'UTF-8'
},
_assetConfig = {},
cssRe = /\.css(?:\?|$)/i,
resolverEl = doc.createElement('a'),
isBrowser = typeof window !== 'undefined',
_environment = {
browser: isBrowser,
node: !isBrowser && (typeof require === 'function'),
phantom: (window && (window._phantom || window.callPhantom)) || /PhantomJS/.test(window.navigator.userAgent)
},
_tags = (Ext.platformTags = {}),
// All calls to _debug are commented out to speed up old browsers a bit;
// yes that makes a difference because the cost of concatenating strings
// and passing them into _debug() adds up pretty quickly.
_debug = function(message) {},
//console.log(message);
_apply = function(object, config, defaults) {
if (defaults) {
_apply(object, defaults);
}
if (object && config && typeof config === 'object') {
for (var i in config) {
object[i] = config[i];
}
}
return object;
},
_merge = function() {
var lowerCase = false,
obj = Array.prototype.shift.call(arguments),
index, i, len, value;
if (typeof arguments[arguments.length - 1] === 'boolean') {
lowerCase = Array.prototype.pop.call(arguments);
}
len = arguments.length;
for (index = 0; index < len; index++) {
value = arguments[index];
if (typeof value === 'object') {
for (i in value) {
obj[lowerCase ? i.toLowerCase() : i] = value[i];
}
}
}
return obj;
},
_getKeys = (typeof Object.keys == 'function') ? function(object) {
if (!object) {
return [];
}
return Object.keys(object);
} : function(object) {
var keys = [],
property;
for (property in object) {
if (object.hasOwnProperty(property)) {
keys.push(property);
}
}
return keys;
},
/*
* The Boot loader class manages Request objects that contain one or
* more individual urls that need to be loaded. Requests can be performed
* synchronously or asynchronously, but will always evaluate urls in the
* order specified on the request object.
*/
Boot = {
loading: 0,
loaded: 0,
apply: _apply,
env: _environment,
config: _config,
/**
* @cfg {Object} assetConfig
* A map (url->assetConfig) that contains information about assets loaded by the Microlaoder.
*/
assetConfig: _assetConfig,
// Keyed by absolute URL this object holds "true" if that URL is already loaded
// or an array of callbacks to call once it loads.
scripts: {},
/*
Entry objects
'http://foo.com/bar/baz/Thing.js': {
done: true,
el: scriptEl || linkEl,
preserve: true,
requests: [ request1, ... ]
}
*/
/**
* contains the current script name being loaded
* (loadSync or sequential load only)
*/
currentFile: null,
suspendedQueue: [],
currentRequest: null,
// when loadSync is called, need to cause subsequent load requests to also be loadSync,
// eg, when Ext.require(...) is called
syncMode: false,
/*
* simple helper method for debugging
*/
debug: _debug,
/**
* enables / disables loading scripts via script / link elements rather
* than using ajax / eval
*/
useElements: true,
listeners: [],
Request: Request,
Entry: Entry,
allowMultipleBrowsers: false,
browserNames: {
ie: 'IE',
firefox: 'Firefox',
safari: 'Safari',
chrome: 'Chrome',
opera: 'Opera',
dolfin: 'Dolfin',
edge: 'Edge',
webosbrowser: 'webOSBrowser',
chromeMobile: 'ChromeMobile',
chromeiOS: 'ChromeiOS',
silk: 'Silk',
other: 'Other'
},
osNames: {
ios: 'iOS',
android: 'Android',
windowsPhone: 'WindowsPhone',
webos: 'webOS',
blackberry: 'BlackBerry',
rimTablet: 'RIMTablet',
mac: 'MacOS',
win: 'Windows',
tizen: 'Tizen',
linux: 'Linux',
bada: 'Bada',
chromeOS: 'ChromeOS',
other: 'Other'
},
browserPrefixes: {
ie: 'MSIE ',
edge: 'Edge/',
firefox: 'Firefox/',
chrome: 'Chrome/',
safari: 'Version/',
opera: 'OPR/',
dolfin: 'Dolfin/',
webosbrowser: 'wOSBrowser/',
chromeMobile: 'CrMo/',
chromeiOS: 'CriOS/',
silk: 'Silk/'
},
// When a UA reports multiple browsers this list is used to prioritize the 'real' browser
// lower index number will win
browserPriority: [
'edge',
'opera',
'dolfin',
'webosbrowser',
'silk',
'chromeiOS',
'chromeMobile',
'ie',
'firefox',
'safari',
'chrome'
],
osPrefixes: {
tizen: '(Tizen )',
ios: 'i(?:Pad|Phone|Pod)(?:.*)CPU(?: iPhone)? OS ',
android: '(Android |HTC_|Silk/)',
// Some HTC devices ship with an OSX userAgent by default,
// so we need to add a direct check for HTC_
windowsPhone: 'Windows Phone ',
blackberry: '(?:BlackBerry|BB)(?:.*)Version/',
rimTablet: 'RIM Tablet OS ',
webos: '(?:webOS|hpwOS)/',
bada: 'Bada/',
chromeOS: 'CrOS '
},
fallbackOSPrefixes: {
windows: 'win',
mac: 'mac',
linux: 'linux'
},
devicePrefixes: {
iPhone: 'iPhone',
iPod: 'iPod',
iPad: 'iPad'
},
maxIEVersion: 12,
/**
* The default function that detects various platforms and sets tags
* in the platform map accordingly. Examples are iOS, android, tablet, etc.
* @param tags the set of tags to populate
*/
detectPlatformTags: function() {
var me = this,
ua = navigator.userAgent,
isMobile = /Mobile(\/|\s)/.test(ua),
element = document.createElement('div'),
isEventSupported = function(name, tag) {
if (tag === undefined) {
tag = window;
}
var eventName = 'on' + name.toLowerCase(),
isSupported = (eventName in element);
if (!isSupported) {
if (element.setAttribute && element.removeAttribute) {
element.setAttribute(eventName, '');
isSupported = typeof element[eventName] === 'function';
if (typeof element[eventName] !== 'undefined') {
element[eventName] = undefined;
}
element.removeAttribute(eventName);
}
}
return isSupported;
},
// Browser Detection
getBrowsers = function() {
var browsers = {},
maxIEVersion, prefix, value, key, index, len, match, version, matched;
// MS Edge browser (and possibly others) can report multiple browsers in the UserAgent
// "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10240"
// we use this to prioritize the actual browser in this situation
len = me.browserPriority.length;
for (index = 0; index < len; index++) {
key = me.browserPriority[index];
if (!matched) {
value = me.browserPrefixes[key];
match = ua.match(new RegExp('(' + value + ')([\\w\\._]+)'));
version = match && match.length > 1 ? parseInt(match[2]) : 0;
if (version) {
matched = true;
}
} else {
version = 0;
}
browsers[key] = version;
}
//Deal with IE document mode
if (browsers.ie) {
var mode = document.documentMode;
if (mode >= 8) {
browsers.ie = mode;
}
}
// Fancy IE greater than and less then quick tags
version = browsers.ie || false;
maxIEVersion = Math.max(version, me.maxIEVersion);
for (index = 8; index <= maxIEVersion; ++index) {
prefix = 'ie' + index;
browsers[prefix + 'm'] = version ? version <= index : 0;
browsers[prefix] = version ? version === index : 0;
browsers[prefix + 'p'] = version ? version >= index : 0;
}
return browsers;
},
//OS Detection
getOperatingSystems = function() {
var systems = {},
value, key, keys, index, len, match, matched, version, activeCount;
keys = _getKeys(me.osPrefixes);
len = keys.length;
for (index = 0 , activeCount = 0; index < len; index++) {
key = keys[index];
value = me.osPrefixes[key];
match = ua.match(new RegExp('(' + value + ')([^\\s;]+)'));
matched = match ? match[1] : null;
// This is here because some HTC android devices show an OSX Snow Leopard userAgent by default.
// And the Kindle Fire doesn't have any indicator of Android as the OS in its User Agent
if (matched && (matched === 'HTC_' || matched === 'Silk/')) {
version = 2.3;
} else {
version = match && match.length > 1 ? parseFloat(match[match.length - 1]) : 0;
}
if (version) {
activeCount++;
}
systems[key] = version;
}
keys = _getKeys(me.fallbackOSPrefixes);
// If no OS could be found we resort to the fallbacks, otherwise we just
// falsify the fallbacks
len = keys.length;
for (index = 0; index < len; index++) {
key = keys[index];
// No OS was detected from osPrefixes
if (activeCount === 0) {
value = me.fallbackOSPrefixes[key];
match = ua.toLowerCase().match(new RegExp(value));
systems[key] = match ? true : 0;
} else {
systems[key] = 0;
}
}
return systems;
},
// Device Detection
getDevices = function() {
var devices = {},
value, key, keys, index, len, match;
keys = _getKeys(me.devicePrefixes);
len = keys.length;
for (index = 0; index < len; index++) {
key = keys[index];
value = me.devicePrefixes[key];
match = ua.match(new RegExp(value));
devices[key] = match ? true : 0;
}
return devices;
},
browsers = getBrowsers(),
systems = getOperatingSystems(),
devices = getDevices(),
platformParams = Boot.loadPlatformsParam();
// We apply platformParams from the query here first to allow for forced user valued
// to be used in calculation of generated tags
_merge(_tags, browsers, systems, devices, platformParams, true);
_tags.phone = !!((_tags.iphone || _tags.ipod) || (!_tags.silk && (_tags.android && (_tags.android < 3 || isMobile))) || (_tags.blackberry && isMobile) || (_tags.windowsphone));
_tags.tablet = !!(!_tags.phone && (_tags.ipad || _tags.android || _tags.silk || _tags.rimtablet || (_tags.ie10 && /; Touch/.test(ua))));
_tags.touch = // if the browser has touch events we can be reasonably sure the device has
// a touch screen
isEventSupported('touchend') || // browsers that use pointer event have maxTouchPoints > 0 if the
// device supports touch input
// http://www.w3.org/TR/pointerevents/#widl-Navigator-maxTouchPoints
navigator.maxTouchPoints || // IE10 uses a vendor-prefixed maxTouchPoints property
navigator.msMaxTouchPoints;
_tags.desktop = !_tags.phone && !_tags.tablet;
_tags.cordova = _tags.phonegap = !!(window.PhoneGap || window.Cordova || window.cordova);
_tags.webview = /(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)(?!.*FBAN)/i.test(ua);
_tags.androidstock = (_tags.android <= 4.3) && (_tags.safari || _tags.silk);
// Re-apply any query params here to allow for user override of generated tags (desktop, touch, tablet, etc)
_merge(_tags, platformParams, true);
},
/**
* Extracts user supplied platform tags from the "platformTags" query parameter
* of the form:
*
* ?platformTags=name:state,name:state,...
*
* (each tag defaults to true when state is unspecified)
*
* Example:
*
* ?platformTags=isTablet,isPhone:false,isDesktop:0,iOS:1,Safari:true, ...
*
* @returns {Object} the platform tags supplied by the query string
*/
loadPlatformsParam: function() {
// Check if the ?platform parameter is set in the URL
var paramsString = window.location.search.substr(1),
paramsArray = paramsString.split("&"),
params = {},
i,
platforms = {},
tmpArray, tmplen, platform, name, enabled;
for (i = 0; i < paramsArray.length; i++) {
tmpArray = paramsArray[i].split("=");
params[tmpArray[0]] = tmpArray[1];
}
if (params.platformTags) {
tmpArray = params.platformTags.split(",");
for (tmplen = tmpArray.length , i = 0; i < tmplen; i++) {
platform = tmpArray[i].split(":");
name = platform[0];
enabled = true;
if (platform.length > 1) {
enabled = platform[1];
if (enabled === 'false' || enabled === '0') {
enabled = false;
}
}
platforms[name] = enabled;
}
}
return platforms;
},
filterPlatform: function(platform, excludes) {
platform = _emptyArray.concat(platform || _emptyArray);
excludes = _emptyArray.concat(excludes || _emptyArray);
var plen = platform.length,
elen = excludes.length,
include = (!plen && elen),
// default true if only excludes specified
i, tag;
for (i = 0; i < plen && !include; i++) {
tag = platform[i];
include = !!_tags[tag];
}
for (i = 0; i < elen && include; i++) {
tag = excludes[i];
include = !_tags[tag];
}
return include;
},
init: function() {
var scriptEls = doc.getElementsByTagName('script'),
script = scriptEls[0],
len = scriptEls.length,
re = /\/ext(\-[a-z\-]+)?\.js$/,
entry, src, state, baseUrl, key, n, origin;
// No check for script definedness because there always should be at least one
Boot.hasReadyState = ("readyState" in script);
Boot.hasAsync = ("async" in script);
Boot.hasDefer = ("defer" in script);
Boot.hasOnLoad = ("onload" in script);
// Feature detecting IE
Boot.isIE8 = Boot.hasReadyState && !Boot.hasAsync && Boot.hasDefer && !Boot.hasOnLoad;
Boot.isIE9 = Boot.hasReadyState && !Boot.hasAsync && Boot.hasDefer && Boot.hasOnLoad;
Boot.isIE10p = Boot.hasReadyState && Boot.hasAsync && Boot.hasDefer && Boot.hasOnLoad;
Boot.isIE10 = (new Function('/*@cc_on return @_jscript_version @*/')()) === 10;
Boot.isIE10m = Boot.isIE10 || Boot.isIE9 || Boot.isIE8;
// IE11 does not support conditional compilation so we detect it by exclusion
Boot.isIE11 = Boot.isIE10p && !Boot.isIE10;
// Since we are loading after other scripts, and we needed to gather them
// anyway, we track them in _scripts so we don't have to ask for them all
// repeatedly.
for (n = 0; n < len; n++) {
src = (script = scriptEls[n]).src;
if (!src) {
continue;
}
state = script.readyState || null;
// If we find a script file called "ext-*.js", then the base path is that file's base path.
if (!baseUrl && re.test(src)) {
baseUrl = src;
}
if (!Boot.scripts[key = Boot.canonicalUrl(src)]) {
// _debug("creating entry " + key + " in Boot.init");
entry = new Entry({
key: key,
url: src,
done: state === null || // non-IE
state === 'loaded' || state === 'complete',
// IE only
el: script,
prop: 'src'
});
}
}
if (!baseUrl) {
script = scriptEls[scriptEls.length - 1];
baseUrl = script.src;
}
Boot.baseUrl = baseUrl.substring(0, baseUrl.lastIndexOf('/') + 1);
origin = window.location.origin || window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port : '');
Boot.origin = origin;
Boot.detectPlatformTags();
Ext.filterPlatform = Boot.filterPlatform;
},
/**
* This method returns a canonical URL for the given URL.
*
* For example, the following all produce the same canonical URL (which is the
* last one):
*
* http://foo.com/bar/baz/zoo/derp/../../goo/Thing.js?_dc=12345
* http://foo.com/bar/baz/zoo/derp/../../goo/Thing.js
* http://foo.com/bar/baz/zoo/derp/../jazz/../../goo/Thing.js
* http://foo.com/bar/baz/zoo/../goo/Thing.js
* http://foo.com/bar/baz/goo/Thing.js
*
* @private
*/
canonicalUrl: function(url) {
// *WARNING WARNING WARNING*
// This method yields the most correct result we can get but it is EXPENSIVE!
// In ALL browsers! When called multiple times in a sequence, as if when
// we resolve dependencies for entries, it will cause garbage collection events
// and overall painful slowness. This is why we try to avoid it as much as we can.
//
// @TODO - see if we need this fallback logic
// http://stackoverflow.com/questions/470832/getting-an-absolute-url-from-a-relative-one-ie6-issue
resolverEl.href = url;
var ret = resolverEl.href,
dc = _config.disableCachingParam,
pos = dc ? ret.indexOf(dc + '=') : -1,
c, end;
// If we have a _dc query parameter we need to remove it from the canonical
// URL.
if (pos > 0 && ((c = ret.charAt(pos - 1)) === '?' || c === '&')) {
end = ret.indexOf('&', pos);
end = (end < 0) ? '' : ret.substring(end);
if (end && c === '?') {
++pos;
// keep the '?'
end = end.substring(1);
}
// remove the '&'
ret = ret.substring(0, pos - 1) + end;
}
return ret;
},
/**
* Get the config value corresponding to the specified name. If no name is given, will return the config object
* @param {String} name The config property name
* @return {Object}
*/
getConfig: function(name) {
return name ? Boot.config[name] : Boot.config;
},
/**
* Set the configuration.
* @param {Object} config The config object to override the default values.
* @return {Ext.Boot} this
*/
setConfig: function(name, value) {
if (typeof name === 'string') {
Boot.config[name] = value;
} else {
for (var s in name) {
Boot.setConfig(s, name[s]);
}
}
return Boot;
},
getHead: function() {
return Boot.docHead || (Boot.docHead = doc.head || doc.getElementsByTagName('head')[0]);
},
create: function(url, key, cfg) {
var config = cfg || {};
config.url = url;
config.key = key;
return Boot.scripts[key] = new Entry(config);
},
getEntry: function(url, cfg, canonicalPath) {
var key, entry;
// Canonicalizing URLs via anchor element href yields the most correct result
// but is *extremely* resource heavy so we need to avoid it whenever possible
key = canonicalPath ? url : Boot.canonicalUrl(url);
entry = Boot.scripts[key];
if (!entry) {
entry = Boot.create(url, key, cfg);
if (canonicalPath) {
entry.canonicalPath = true;
}
}
return entry;
},
registerContent: function(url, type, content) {
var cfg = {
content: content,
loaded: true,
css: type === 'css'
};
return Boot.getEntry(url, cfg);
},
processRequest: function(request, sync) {
request.loadEntries(sync);
},
load: function(request) {
// _debug("Boot.load called");
var request = new Request(request);
if (request.sync || Boot.syncMode) {
return Boot.loadSync(request);
}
// If there is a request in progress, we must
// queue this new request to be fired when the current request completes.
if (Boot.currentRequest) {
// _debug("current active request, suspending this request");
// trigger assignment of entries now to ensure that overlapping
// entries with currently running requests will synchronize state
// with this pending one as they complete
request.getEntries();
Boot.suspendedQueue.push(request);
} else {
Boot.currentRequest = request;
Boot.processRequest(request, false);
}
return Boot;
},
loadSync: function(request) {
// _debug("Boot.loadSync called");
var request = new Request(request);
Boot.syncMode++;
Boot.processRequest(request, true);
Boot.syncMode--;
return Boot;
},
loadBasePrefix: function(request) {
request = new Request(request);
request.prependBaseUrl = true;
return Boot.load(request);
},
loadSyncBasePrefix: function(request) {
request = new Request(request);
request.prependBaseUrl = true;
return Boot.loadSync(request);
},
requestComplete: function(request) {
var next;
if (Boot.currentRequest === request) {
Boot.currentRequest = null;
while (Boot.suspendedQueue.length > 0) {
next = Boot.suspendedQueue.shift();
if (!next.done) {
// _debug("resuming suspended request");
Boot.load(next);
break;
}
}
}
if (!Boot.currentRequest && Boot.suspendedQueue.length == 0) {
Boot.fireListeners();
}
},
isLoading: function() {
return !Boot.currentRequest && Boot.suspendedQueue.length == 0;
},
fireListeners: function() {
var listener;
while (Boot.isLoading() && (listener = Boot.listeners.shift())) {
listener();
}
},
onBootReady: function(listener) {
if (!Boot.isLoading()) {
listener();
} else {
Boot.listeners.push(listener);
}
},
/**
* this is a helper function used by Ext.Loader to flush out
* 'uses' arrays for classes in some Ext versions
*/
getPathsFromIndexes: function(indexMap, loadOrder) {
// In older versions indexMap was an object instead of a sparse array
if (!('length' in indexMap)) {
var indexArray = [],
index;
for (index in indexMap) {
if (!isNaN(+index)) {
indexArray[+index] = indexMap[index];
}
}
indexMap = indexArray;
}
return Request.prototype.getPathsFromIndexes(indexMap, loadOrder);
},
createLoadOrderMap: function(loadOrder) {
return Request.prototype.createLoadOrderMap(loadOrder);
},
fetch: function(url, complete, scope, async) {
async = (async === undefined) ? !!complete : async;
var xhr = new XMLHttpRequest(),
result, status, content,
exception = false,
readyStateChange = function() {
if (xhr && xhr.readyState == 4) {
status = (xhr.status === 1223) ? 204 : (xhr.status === 0 && ((self.location || {}).protocol === 'file:' || (self.location || {}).protocol === 'ionp:')) ? 200 : xhr.status;
content = xhr.responseText;
result = {
content: content,
status: status,
exception: exception
};
if (complete) {
complete.call(scope, result);
}
xhr.onreadystatechange = emptyFn;
xhr = null;
}
};
if (async) {
xhr.onreadystatechange = readyStateChange;
}
try {
// _debug("fetching " + url + " " + (async ? "async" : "sync"));
xhr.open('GET', url, async);
xhr.send(null);
} catch (err) {
exception = err;
readyStateChange();
return result;
}
if (!async) {
readyStateChange();
}
return result;
},
notifyAll: function(entry) {
entry.notifyRequests();
}
};
function Request(cfg) {
//The request class encapsulates a series of Entry objects
//and provides notification around the completion of all Entries
//in this request.
if (cfg.$isRequest) {
return cfg;
}
var cfg = cfg.url ? cfg : {
url: cfg
},
url = cfg.url,
urls = url.charAt ? [
url
] : url,
charset = cfg.charset || Boot.config.charset;
_apply(this, cfg);
delete this.url;
this.urls = urls;
this.charset = charset;
}
Request.prototype = {
$isRequest: true,
createLoadOrderMap: function(loadOrder) {
var len = loadOrder.length,
loadOrderMap = {},
i, element;
for (i = 0; i < len; i++) {
element = loadOrder[i];
loadOrderMap[element.path] = element;
}
return loadOrderMap;
},
getLoadIndexes: function(item, indexMap, loadOrder, includeUses, skipLoaded) {
var resolved = [],
queue = [
item
],
itemIndex = item.idx,
queue, entry, dependencies, depIndex, i, len;
if (indexMap[itemIndex]) {
// prevent cycles
return resolved;
}
// Both indexMap and resolved are sparse arrays keyed by indexes.
// This gives us a naturally sorted sequence of indexes later on
// when we need to convert them to paths.
// indexMap is the map of all indexes we have visited at least once
// per the current expandUrls() invocation, and resolved is the map
// of all dependencies for the current item that are not included
// in indexMap.
indexMap[itemIndex] = resolved[itemIndex] = true;
while (item = queue.shift()) {
// Canonicalizing URLs is expensive, we try to avoid it
if (item.canonicalPath) {
entry = Boot.getEntry(item.path, null, true);
} else {
entry = Boot.getEntry(this.prepareUrl(item.path));
}
if (!(skipLoaded && entry.done)) {
if (includeUses && item.uses && item.uses.length) {
dependencies = item.requires.concat(item.uses);
} else {
dependencies = item.requires;
}
for (i = 0 , len = dependencies.length; i < len; i++) {
depIndex = dependencies[i];
if (!indexMap[depIndex]) {
indexMap[depIndex] = resolved[depIndex] = true;
queue.push(loadOrder[depIndex]);
}
}
}
}
return resolved;
},
getPathsFromIndexes: function(indexes, loadOrder) {
var paths = [],
index, len;
// indexes is a sparse array with values being true for defined indexes
for (index = 0 , len = indexes.length; index < len; index++) {
if (indexes[index]) {
paths.push(loadOrder[index].path);
}
}
return paths;
},
expandUrl: function(url, loadOrder, loadOrderMap, indexMap, includeUses, skipLoaded) {
var item, resolved;
if (loadOrder) {
item = loadOrderMap[url];
if (item) {
resolved = this.getLoadIndexes(item, indexMap, loadOrder, includeUses, skipLoaded);
if (resolved.length) {
return this.getPathsFromIndexes(resolved, loadOrder);
}
}
}
return [
url
];
},
expandUrls: function(urls, includeUses) {
var me = this,
loadOrder = me.loadOrder,
expanded = [],
expandMap = {},
indexMap = [],
loadOrderMap, tmpExpanded, i, len, t, tlen, tUrl;
if (typeof urls === "string") {
urls = [
urls
];
}
if (loadOrder) {
loadOrderMap = me.loadOrderMap;
if (!loadOrderMap) {
loadOrderMap = me.loadOrderMap = me.createLoadOrderMap(loadOrder);
}
}
for (i = 0 , len = urls.length; i < len; i++) {
// We don't want to skip loaded entries (last argument === false).
// There are some overrides that get loaded before their respective classes,
// and when the class dependencies are processed we don't want to skip over
// the overrides' dependencies just because they were loaded first.
tmpExpanded = this.expandUrl(urls[i], loadOrder, loadOrderMap, indexMap, includeUses, false);
for (t = 0 , tlen = tmpExpanded.length; t < tlen; t++) {
tUrl = tmpExpanded[t];
if (!expandMap[tUrl]) {
expandMap[tUrl] = true;
expanded.push(tUrl);
}
}
}
if (expanded.length === 0) {
expanded = urls;
}
return expanded;
},
expandLoadOrder: function() {
var me = this,
urls = me.urls,
expanded;
if (!me.expanded) {
expanded = this.expandUrls(urls, true);
me.expanded = true;
} else {
expanded = urls;
}
me.urls = expanded;
// if we added some urls to the request to honor the indicated
// load order, the request needs to be sequential
if (urls.length != expanded.length) {
me.sequential = true;
}
return me;
},
getUrls: function() {
this.expandLoadOrder();
return this.urls;
},
prepareUrl: function(url) {
if (this.prependBaseUrl) {
return Boot.baseUrl + url;
}
return url;
},
getEntries: function() {
var me = this,
entries = me.entries,
loadOrderMap, item, i, entry, urls, url;
if (!entries) {
entries = [];
urls = me.getUrls();
// If we have loadOrder array then the map will be expanded by now
if (me.loadOrder) {
loadOrderMap = me.loadOrderMap;
}
for (i = 0; i < urls.length; i++) {
url = me.prepareUrl(urls[i]);
if (loadOrderMap) {
item = loadOrderMap[url];
}
entry = Boot.getEntry(url, {
buster: me.buster,
charset: me.charset
}, item && item.canonicalPath);
entry.requests.push(me);
entries.push(entry);
}
me.entries = entries;
}
return entries;
},
loadEntries: function(sync) {
var me = this,
entries = me.getEntries(),
len = entries.length,
start = me.loadStart || 0,
continueLoad, entries, entry, i;
if (sync !== undefined) {
me.sync = sync;
}
me.loaded = me.loaded || 0;
me.loading = me.loading || len;
for (i = start; i < len; i++) {
entry = entries[i];
if (!entry.loaded) {
continueLoad = entries[i].load(me.sync);
} else {
continueLoad = true;
}
if (!continueLoad) {
me.loadStart = i;
entry.onDone(function() {
me.loadEntries(sync);
});
break;
}
}
me.processLoadedEntries();
},
processLoadedEntries: function() {
var me = this,
entries = me.getEntries(),
len = entries.length,
start = me.startIndex || 0,
i, entry;
if (!me.done) {
for (i = start; i < len; i++) {
entry = entries[i];
if (!entry.loaded) {
me.startIndex = i;
return;
}
if (!entry.evaluated) {
entry.evaluate();
}
if (entry.error) {
me.error = true;
}
}
me.notify();
}
},
notify: function() {
var me = this;
if (!me.done) {
var error = me.error,
fn = me[error ? 'failure' : 'success'],
delay = ('delay' in me) ? me.delay : (error ? 1 : Boot.config.chainDelay),
scope = me.scope || me;
me.done = true;
if (fn) {
if (delay === 0 || delay > 0) {
// Free the stack (and defer the next script)
setTimeout(function() {
fn.call(scope, me);
}, delay);
} else {
fn.call(scope, me);
}
}
me.fireListeners();
Boot.requestComplete(me);
}
},
onDone: function(listener) {
var me = this,
listeners = me.listeners || (me.listeners = []);
if (me.done) {
listener(me);
} else {
listeners.push(listener);
}
},
fireListeners: function() {
var listeners = this.listeners,
listener;
if (listeners) {
// _debug("firing request listeners");
while ((listener = listeners.shift())) {
listener(this);
}
}
}
};
function Entry(cfg) {
//The Entry class is a token to manage the load and evaluation
//state of a particular url. It is used to notify all Requests
//interested in this url that the content is available.
if (cfg.$isEntry) {
return cfg;
}
// _debug("creating entry for " + cfg.url);
var charset = cfg.charset || Boot.config.charset,
manifest = Ext.manifest,
loader = manifest && manifest.loader,
cache = (cfg.cache !== undefined) ? cfg.cache : (loader && loader.cache),
buster, busterParam;
if (Boot.config.disableCaching) {
if (cache === undefined) {
cache = !Boot.config.disableCaching;
}
if (cache === false) {
buster = +new Date();
} else if (cache !== true) {
buster = cache;
}
if (buster) {
busterParam = (loader && loader.cacheParam) || Boot.config.disableCachingParam;
buster = busterParam + "=" + buster;
}
}
_apply(this, cfg);
this.charset = charset;
this.buster = buster;
this.requests = [];
}
Entry.prototype = {
$isEntry: true,
done: false,
evaluated: false,
loaded: false,
isCrossDomain: function() {
var me = this;
if (me.crossDomain === undefined) {
// _debug("checking " + me.getLoadUrl() + " for prefix " + Boot.origin);
me.crossDomain = (me.getLoadUrl().indexOf(Boot.origin) !== 0);
}
return me.crossDomain;
},
isCss: function() {
var me = this;
if (me.css === undefined) {
if (me.url) {
var assetConfig = Boot.assetConfig[me.url];
me.css = assetConfig ? assetConfig.type === "css" : cssRe.test(me.url);
} else {
me.css = false;
}
}
return this.css;
},
getElement: function(tag) {
var me = this,
el = me.el;
if (!el) {
// _debug("creating element for " + me.url);
if (me.isCss()) {
tag = tag || "link";
el = doc.createElement(tag);
if (tag == "link") {
el.rel = 'stylesheet';
me.prop = 'href';
} else {
me.prop = "textContent";
}
el.type = "text/css";
} else {
tag = tag || "script";
el = doc.createElement(tag);
el.type = 'text/javascript';
me.prop = 'src';
if (me.charset) {
el.charset = me.charset;
}
if (Boot.hasAsync) {
el.async = false;
}
}
me.el = el;
}
return el;
},
getLoadUrl: function() {
var me = this,
url;
url = me.canonicalPath ? me.url : Boot.canonicalUrl(me.url);
if (!me.loadUrl) {
me.loadUrl = !!me.buster ? (url + (url.indexOf('?') === -1 ? '?' : '&') + me.buster) : url;
}
return me.loadUrl;
},
fetch: function(req) {
var url = this.getLoadUrl(),
async = !!req.async,
complete = req.complete;
Boot.fetch(url, complete, this, async);
},
onContentLoaded: function(response) {
var me = this,
status = response.status,
content = response.content,
exception = response.exception,
url = this.getLoadUrl();
me.loaded = true;
if ((exception || status === 0) && !_environment.phantom) {
me.error = ("Failed loading synchronously via XHR: '" + url + "'. It's likely that the file is either being loaded from a " + "different domain or from the local file system where cross " + "origin requests are not allowed for security reasons. Try " + "asynchronous loading instead.") || true;
me.evaluated = true;
} else if ((status >= 200 && status < 300) || status === 304 || _environment.phantom || (status === 0 && content.length > 0)) {
me.content = content;
} else {
me.error = ("Failed loading synchronously via XHR: '" + url + "'. Please verify that the file exists. XHR status code: " + status) || true;
me.evaluated = true;
}
},
createLoadElement: function(callback) {
var me = this,
el = me.getElement();
me.preserve = true;
el.onerror = function() {
me.error = true;
if (callback) {
callback();
callback = null;
}
};
if (Boot.isIE10m) {
el.onreadystatechange = function() {
if (this.readyState === 'loaded' || this.readyState === 'complete') {
if (callback) {
callback();
callback = this.onreadystatechange = this.onerror = null;
}
}
};
} else {
el.onload = function() {
callback();
callback = this.onload = this.onerror = null;
};
}
// IE starts loading here
el[me.prop] = me.getLoadUrl();
},
onLoadElementReady: function() {
Boot.getHead().appendChild(this.getElement());
this.evaluated = true;
},
inject: function(content, asset) {
// _debug("injecting content for " + this.url);
var me = this,
head = Boot.getHead(),
url = me.url,
key = me.key,
base, el, ieMode, basePath;
if (me.isCss()) {
me.preserve = true;
basePath = key.substring(0, key.lastIndexOf("/") + 1);
base = doc.createElement('base');
base.href = basePath;
if (head.firstChild) {
head.insertBefore(base, head.firstChild);
} else {
head.appendChild(base);
}
// reset the href attribute to cuase IE to pick up the change
base.href = base.href;
if (url) {
content += "\n/*# sourceURL=" + key + " */";
}
// create element after setting base
el = me.getElement("style");
ieMode = ('styleSheet' in el);
head.appendChild(base);
if (ieMode) {
head.appendChild(el);
el.styleSheet.cssText = content;
} else {
el.textContent = content;
head.appendChild(el);
}
head.removeChild(base);
} else {
// Debugger friendly, file names are still shown even though they're
// eval'ed code. Breakpoints work on both Firebug and Chrome's Web
// Inspector.
if (url) {
content += "\n//# sourceURL=" + key;
}
Ext.globalEval(content);
}
return me;
},
loadCrossDomain: function() {
var me = this,
complete = function() {
me.el.onerror = me.el.onload = emptyFn;
me.el = null;
me.loaded = me.evaluated = me.done = true;
me.notifyRequests();
};
me.createLoadElement(function() {
complete();
});
me.evaluateLoadElement();
// at this point, we need sequential evaluation,
// which means we can't advance the load until
// this entry has fully completed
return false;
},
loadElement: function() {
var me = this,
complete = function() {
me.el.onerror = me.el.onload = emptyFn;
me.el = null;
me.loaded = me.evaluated = me.done = true;
me.notifyRequests();
};
me.createLoadElement(function() {
complete();
});
me.evaluateLoadElement();
return true;
},
loadSync: function() {
var me = this;
me.fetch({
async: false,
complete: function(response) {
me.onContentLoaded(response);
}
});
me.evaluate();
me.notifyRequests();
},
load: function(sync) {
var me = this;
if (!me.loaded) {
if (me.loading) {
// if we're calling back through load and we're loading but haven't
// yet loaded, then we should be in a sequential, cross domain
// load scenario which means we can't continue the load on the
// request until this entry has fully evaluated, which will mean
// loaded = evaluated = done = true in one step. For css files, this
// will happen immediately upon <link> element creation / insertion,
// but <script> elements will set this upon load notification
return false;
}
me.loading = true;
// for async modes, we have some options
if (!sync) {
// if cross domain, just inject the script tag and let the onload
// events drive the progression.
// IE10 also needs sequential loading because of a bug that makes it
// fire readystate event prematurely:
// https://connect.microsoft.com/IE/feedback/details/729164/ie10-dynamic-script-element-fires-loaded-readystate-prematurely
if (Boot.isIE10 || me.isCrossDomain()) {
return me.loadCrossDomain();
}
// for IE, use the readyStateChange allows us to load scripts in parallel
// but serialize the evaluation by appending the script node to the
// document
else if (!me.isCss() && Boot.hasReadyState) {
me.createLoadElement(function() {
me.loaded = true;
me.notifyRequests();
});
} else if (Boot.useElements && // older webkit, phantomjs included, won't fire load for link elements
!(me.isCss() && _environment.phantom)) {
return me.loadElement();
} else // for other browsers, just ajax the content down in parallel, and use
// globalEval to serialize evaluation
{
me.fetch({
async: !sync,
complete: function(response) {
me.onContentLoaded(response);
me.notifyRequests();
}
});
}
} else // for sync mode in js, global eval FTW. IE won't honor the comment
// paths in the debugger, so eventually we need a sync mode for IE that
// uses the readyStateChange mechanism
{
me.loadSync();
}
}
// signal that the load process can continue
return true;
},
evaluateContent: function() {
this.inject(this.content);
this.content = null;
},
evaluateLoadElement: function() {
Boot.getHead().appendChild(this.getElement());
},
evaluate: function() {
var me = this;
if (!me.evaluated) {
if (me.evaluating) {
return;
}
me.evaluating = true;
if (me.content !== undefined) {
me.evaluateContent();
} else if (!me.error) {
me.evaluateLoadElement();
}
me.evaluated = me.done = true;
me.cleanup();
}
},
cleanup: function() {
var me = this,
el = me.el,
prop;
if (!el) {
return;
}
if (!me.preserve) {
me.el = null;
el.parentNode.removeChild(el);
// Remove, since its useless now
for (prop in el) {
try {
if (prop !== me.prop) {
// If we set the src property to null IE
// will try and request a script at './null'
el[prop] = null;
}
delete el[prop];
} // and prepare for GC
catch (cleanEx) {}
}
}
//ignore
// Setting to null can cause exceptions if IE ever needs to call these
// again (like onreadystatechange). This emptyFn has nothing locked in
// closure scope so it is about as safe as null for memory leaks.
el.onload = el.onerror = el.onreadystatechange = emptyFn;
},
notifyRequests: function() {
var requests = this.requests,
len = requests.length,
i, request;
for (i = 0; i < len; i++) {
request = requests[i];
request.processLoadedEntries();
}
if (this.done) {
this.fireListeners();
}
},
onDone: function(listener) {
var me = this,
listeners = me.listeners || (me.listeners = []);
if (me.done) {
listener(me);
} else {
listeners.push(listener);
}
},
fireListeners: function() {
var listeners = this.listeners,
listener;
if (listeners && listeners.length > 0) {
// _debug("firing event listeners for url " + this.url);
while ((listener = listeners.shift())) {
listener(this);
}
}
}
};
/**
* Turns on or off the "cache buster" applied to dynamically loaded scripts. Normally
* dynamically loaded scripts have an extra query parameter appended to avoid stale
* cached scripts. This method can be used to disable this mechanism, and is primarily
* useful for testing. This is done using a cookie.
* @param {Boolean} disable True to disable the cache buster.
* @param {String} [path="/"] An optional path to scope the cookie.
*/
Ext.disableCacheBuster = function(disable, path) {
var date = new Date();
date.setTime(date.getTime() + (disable ? 10 * 365 : -1) * 24 * 60 * 60 * 1000);
date = date.toGMTString();
doc.cookie = 'ext-cache=1; expires=' + date + '; path=' + (path || '/');
};
Boot.init();
return Boot;
}(// NOTE: We run the eval at global scope to protect the body of the function and allow
// compressors to still process it.
function() {}));
//(eval("/*@cc_on!@*/!1"));
/**
* This method evaluates the given code free of any local variable. This
* will be at global scope, in others it will be in a function.
* @param {String} code The code to evaluate.
* @private
* @method
* @member Ext
*/
Ext.globalEval = Ext.globalEval || (this.execScript ? function(code) {
execScript(code);
} : function($$code) {
eval.call(window, $$code);
});
/*
* Only IE8 & IE/Quirks lack Function.prototype.bind so we polyfill that here.
*/
if (!Function.prototype.bind) {
(function() {
var slice = Array.prototype.slice,
// To reduce overhead on call of the bound fn we have two flavors based on
// whether we have args to prepend or not:
bind = function(me) {
var args = slice.call(arguments, 1),
method = this;
if (args.length) {
return function() {
var t = arguments;
// avoid the slice/concat if the caller does not supply args
return method.apply(me, t.length ? args.concat(slice.call(t)) : args);
};
}
// this is the majority use case - just fn.bind(this) and no args
args = null;
return function() {
return method.apply(me, arguments);
};
};
Function.prototype.bind = bind;
bind.$extjs = true;
}());
}
// to detect this polyfill if one want to improve it
//</editor-fold>
Ext.setResourcePath = function(poolName, path) {
var manifest = Ext.manifest || (Ext.manifest = {}),
paths = manifest.resources || (manifest.resources = {});
if (manifest) {
if (typeof poolName !== 'string') {
Ext.apply(paths, poolName);
} else {
paths[poolName] = path;
}
manifest.resources = paths;
}
};
Ext.getResourcePath = function(path, poolName, packageName) {
if (typeof path !== 'string') {
poolName = path.pool;
packageName = path.packageName;
path = path.path;
}
var manifest = Ext.manifest,
paths = manifest && manifest.resources,
poolPath = paths[poolName],
output = [];
if (poolPath == null) {
poolPath = paths.path;
if (poolPath == null) {
poolPath = 'resources';
}
}
if (poolPath) {
output.push(poolPath);
}
if (packageName) {
output.push(packageName);
}
output.push(path);
return output.join('/');
};
// @tag core
/**
* @class Ext
*
* The Ext namespace (global object) encapsulates all classes, singletons, and
* utility methods provided by Sencha's libraries.
*
* Most user interface Components are at a lower level of nesting in the namespace,
* but many common utility functions are provided as direct properties of the Ext namespace.
*
* Also many frequently used methods from other classes are provided as shortcuts
* within the Ext namespace. For example {@link Ext#getCmp Ext.getCmp} aliases
* {@link Ext.ComponentManager#get Ext.ComponentManager.get}.
*
* Many applications are initiated with {@link Ext#application Ext.application} which is
* called once the DOM is ready. This ensures all scripts have been loaded, preventing
* dependency issues. For example:
*
* Ext.application({
* name: 'MyApp',
*
* launch: function () {
* Ext.Msg.alert(this.name, 'Ready to go!');
* }
* });
*
* <b><a href="http://www.sencha.com/products/sencha-cmd/">Sencha Cmd</a></b> is a free tool
* for helping you generate and build Ext JS (and Sencha Touch) applications. See
* `{@link Ext.app.Application Application}` for more information about creating an app.
*
* A lower-level technique that does not use the `Ext.app.Application` architecture is
* {@link Ext#onReady Ext.onReady}.
*
* For more information about how to use the Ext classes, see:
*
* - <a href="http://www.sencha.com/learn/">The Learning Center</a>
* - <a href="http://www.sencha.com/learn/Ext_FAQ">The FAQ</a>
* - <a href="http://www.sencha.com/forum/">The forums</a>
*
* @singleton
*/
var Ext = Ext || {};
// jshint ignore:line
// @define Ext
(function() {
var global = this,
objectPrototype = Object.prototype,
toString = objectPrototype.toString,
enumerables = [
//'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
'valueOf',
'toLocaleString',
'toString',
'constructor'
],
emptyFn = function() {},
privateFn = function() {},
identityFn = function(o) {
return o;
},
// This is the "$previous" method of a hook function on an instance. When called, it
// calls through the class prototype by the name of the called method.
callOverrideParent = function() {
var method = callOverrideParent.caller.caller;
// skip callParent (our caller)
return method.$owner.prototype[method.$name].apply(this, arguments);
},
manifest = Ext.manifest || {},
i,
iterableRe = /\[object\s*(?:Array|Arguments|\w*Collection|\w*List|HTML\s+document\.all\s+class)\]/,
MSDateRe = /^\\?\/Date\(([-+])?(\d+)(?:[+-]\d{4})?\)\\?\/$/;
Ext.global = global;
/**
* Returns the current timestamp.
* @return {Number} Milliseconds since UNIX epoch.
* @method now
* @member Ext
*/
Ext.now = Date.now || (Date.now = function() {
return +new Date();
});
/**
* Returns the current high-resolution timestamp.
* @return {Number} Milliseconds ellapsed since arbitrary epoch.
* @method ticks
* @member Ext
* @since 6.0.1
*/
Ext.ticks = (global.performance && global.performance.now) ? function() {
return performance.now();
} : // jshint ignore:line
Ext.now;
Ext._startTime = Ext.ticks();
// Mark these special fn's for easy identification:
emptyFn.$nullFn = identityFn.$nullFn = emptyFn.$emptyFn = identityFn.$identityFn = privateFn.$nullFn = true;
privateFn.$privacy = 'framework';
// We also want to prevent these functions from being cleaned up on destroy
emptyFn.$noClearOnDestroy = identityFn.$noClearOnDestroy = true;
privateFn.$noClearOnDestroy = true;
// These are emptyFn's in core and are redefined only in Ext JS (we use this syntax
// so Cmd does not detect them):
Ext['suspendLayouts'] = Ext['resumeLayouts'] = emptyFn;
// jshint ignore:line
for (i in {
toString: 1
}) {
enumerables = null;
}
/**
* An array containing extra enumerables for old browsers
* @property {String[]}
*/
Ext.enumerables = enumerables;
/**
* Copies all the properties of `config` to the specified `object`. There are two levels
* of defaulting supported:
*
* Ext.apply(obj, { a: 1 }, { a: 2 });
* //obj.a === 1
*
* Ext.apply(obj, { }, { a: 2 });
* //obj.a === 2
*
* Note that if recursive merging and cloning without referencing the original objects
* or arrays is needed, use {@link Ext.Object#merge} instead.
*
* @param {Object} object The receiver of the properties.
* @param {Object} config The primary source of the properties.
* @param {Object} [defaults] An object that will also be applied for default values.
* @return {Object} returns `object`.
*/
Ext.apply = function(object, config, defaults) {
if (defaults) {
Ext.apply(object, defaults);
}
if (object && config && typeof config === 'object') {
var i, j, k;
for (i in config) {
object[i] = config[i];
}
if (enumerables) {
for (j = enumerables.length; j--; ) {
k = enumerables[j];
if (config.hasOwnProperty(k)) {
object[k] = config[k];
}
}
}
}
return object;
};
// Used by Ext.override
function addInstanceOverrides(target, owner, overrides) {
var name, value;
for (name in overrides) {
if (overrides.hasOwnProperty(name)) {
value = overrides[name];
if (typeof value === 'function') {
if (owner.$className) {
value.name = owner.$className + '#' + name;
}
value.$name = name;
value.$owner = owner;
value.$previous = target.hasOwnProperty(name) ? target[name] : // already hooked, so call previous hook
callOverrideParent;
}
// calls by name on prototype
target[name] = value;
}
}
}
Ext.buildSettings = Ext.apply({
baseCSSPrefix: 'x-'
}, Ext.buildSettings || {});
Ext.apply(Ext, {
/**
* @private
*/
idSeed: 0,
/**
* @private
*/
idPrefix: 'ext-',
/**
* @property {Boolean} isSecure
* True if the page is running over SSL
* @readonly
*/
isSecure: /^https/i.test(window.location.protocol),
/**
* `true` to automatically uncache orphaned Ext.Elements periodically. If set to
* `false`, the application will be required to clean up orphaned Ext.Elements and
* it's listeners as to not cause memory leakage.
*/
enableGarbageCollector: false,
/**
* True to automatically purge event listeners during garbageCollection.
*/
enableListenerCollection: true,
/**
* @property {String} [name='Ext']
* <p>The name of the property in the global namespace (The <code>window</code> in browser environments) which refers to the current instance of Ext.</p>
* <p>This is usually <code>"Ext"</code>, but if a sandboxed build of ExtJS is being used, this will be an alternative name.</p>
* <p>If code is being generated for use by <code>eval</code> or to create a <code>new Function</code>, and the global instance
* of Ext must be referenced, this is the name that should be built into the code.</p>
*/
name: Ext.sandboxName || 'Ext',
/**
* @property {Function}
* A reusable empty function for use as `privates` members.
*
* Ext.define('MyClass', {
* nothing: Ext.emptyFn,
*
* privates: {
* privateNothing: Ext.privateFn
* }
* });
*
*/
privateFn: privateFn,
/**
* @property {Function}
* A reusable empty function.
*/
emptyFn: emptyFn,
/**
* @property {Function}
* A reusable identity function that simply returns its first argument.
*/
identityFn: identityFn,
/**
* This indicate the start timestamp of current cycle.
* It is only reliable during dom-event-initiated cycles and
* {@link Ext.draw.Animator} initiated cycles.
*/
frameStartTime: Ext.now(),
/**
* This object is initialized prior to loading the framework (Ext JS or Sencha
* Touch) and contains settings and other information describing the application.
*
* For applications built using Sencha Cmd, this is produced from the `"app.json"`
* file with information extracted from all of the required packages' `"package.json"`
* files. This can be set to a string when your application is using the
* (microloader)[#/guide/microloader]. In this case, the string of "foo" will be
* requested as `"foo.json"` and the object in that JSON file will parsed and set
* as this object.
*
* @cfg {String/Object} manifest
*
* @cfg {String/Object} manifest.compatibility An object keyed by package name with
* the value being to desired compatibility level as a version number. If this is
* just a string, this version is assumed to apply to the framework ('ext' or
* 'touch'). Setting this value to less than 5 for 'ext' will enable the compatibility
* layer to assist in the application upgrade process. For details on the upgrade
* process, see the (Upgrade Guide)[#/guides/upgrade_50].
*
* @cfg {Object} manifest.debug An object configuring the debugging characteristics
* of the framework. See `Ext.debugConfig` which is set to this value.
*
* @cfg {Object} manifest.packages An object keyed by package name with the value
* being a subset of the package's `"package.json"` descriptor.
* @since 5.0.0
*/
manifest: manifest,
/**
* @cfg {Object} [debugConfig]
* This object is used to enable or disable debugging for classes or namespaces. The
* default instance looks like this:
*
* Ext.debugConfig = {
* hooks: {
* '*': true
* }
* };
*
* Typically applications will set this in their `"app.json"` like so:
*
* {
* "debug": {
* "hooks": {
* // Default for all namespaces:
* '*': true,
*
* // Except for Ext namespace which is disabled
* 'Ext': false,
*
* // Except for Ext.layout namespace which is enabled
* 'Ext.layout': true
* }
* }
* }
*
* Alternatively, because this property is consumed very early in the load process of
* the framework, this can be set in a `script` tag that is defined prior to loading
* the framework itself.
*
* For example, to enable debugging for the `Ext.layout` namespace only:
*
* var Ext = Ext || {};
* Ext.debugConfig = {
* hooks: {
* //...
* }
* };
*
* For any class declared, the longest matching namespace specified determines if its
* `debugHooks` will be enabled. The default setting is specified by the '*' property.
*
* **NOTE:** This option only applies to debug builds. All debugging is disabled in
* production builds.
*/
debugConfig: Ext.debugConfig || manifest.debug || {
hooks: {
'*': true
}
},
/**
* @property {Boolean} [enableAria=true] This property is provided for backward
* compatibility with previous versions of Ext JS. Accessibility is always enabled
* in Ext JS 6.0+.
*
* This property is deprecated. To disable WAI-ARIA compatibility warnings,
* override `Ext.ariaWarn` function in your application startup code:
*
* Ext.application({
* launch: function() {
* Ext.ariaWarn = Ext.emptyFn;
* }
* });
*
* For stricter compatibility with WAI-ARIA requirements, replace `Ext.ariaWarn`
* with a function that will raise an error instead:
*
* Ext.application({
* launch: function() {
* Ext.ariaWarn = function(target, msg) {
* Ext.raise({
* msg: msg,
* component: target
* });
* };
* }
* });
*
* @since 6.0.0
* @deprecated 6.0.2
*/
enableAria: true,
startsWithHashRe: /^#/,
/**
* @property {RegExp}
* @private
* Regular expression used for validating identifiers.
*/
validIdRe: /^[a-z_][a-z0-9\-_]*$/i,
/**
* @property {String} BLANK_IMAGE_URL
* URL to a 1x1 transparent gif image used by Ext to create inline icons with
* CSS background images.
*/
BLANK_IMAGE_URL: 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
/**
* Converts an id (`'foo'`) into an id selector (`'#foo'`). This method is used
* internally by the framework whenever an id needs to be converted into a selector
* and is provided as a hook for those that need to escape IDs selectors since,
* as of Ext 5.0, the framework no longer escapes IDs by default.
* @private
* @param {String} id
* @return {String}
*/
makeIdSelector: function(id) {
if (!Ext.validIdRe.test(id)) {
Ext.raise('Invalid id selector: "' + id + '"');
}
return '#' + id;
},
/**
* Generates unique ids. If the object/element is passes and it already has an `id`, it is unchanged.
* @param {Object} [o] The object to generate an id for.
* @param {String} [prefix=ext-gen] (optional) The `id` prefix.
* @return {String} The generated `id`.
*/
id: function(o, prefix) {
if (o && o.id) {
return o.id;
}
var id = (prefix || Ext.idPrefix) + (++Ext.idSeed);
if (o) {
o.id = id;
}
return id;
},
/**
* A reusable function which returns the value of `getId()` called upon a single passed parameter.
* Useful when creating a {@link Ext.util.MixedCollection} of objects keyed by an identifier returned from a `getId` method.
*/
returnId: function(o) {
return o.getId();
},
/**
* A reusable function which returns `true`.
*/
returnTrue: function() {
return true;
},
/**
* A zero length string which will pass a truth test. Useful for passing to methods
* which use a truth test to reject <i>falsy</i> values where a string value must be cleared.
*/
emptyString: new String(),
// jshint ignore:line
/**
* @property {String} [baseCSSPrefix='x-']
* The base prefix to use for all `Ext` components. To configure this property, you should use the
* Ext.buildSettings object before the framework is loaded:
*
* Ext.buildSettings = {
* baseCSSPrefix : 'abc-'
* };
*
* or you can change it before any components are rendered:
*
* Ext.baseCSSPrefix = Ext.buildSettings.baseCSSPrefix = 'abc-';
*
* This will change what CSS classes components will use and you should
* then recompile the SASS changing the `$prefix` SASS variable to match.
*/
baseCSSPrefix: Ext.buildSettings.baseCSSPrefix,
/**
* @property {Object} $eventNameMap
* A map of event names which contained the lower-cased versions of any mixed
* case event names.
* @private
*/
$eventNameMap: {},
// Vendor-specific events do not work if lower-cased. This regex specifies event
// prefixes for names that should NOT be lower-cased by Ext.canonicalEventName()
$vendorEventRe: /^(DOMMouse|Moz.+|MS.+|webkit.+)/,
// TODO: inlinable function - SDKTOOLS-686
/**
* @private
* @inline
*/
canonicalEventName: function(name) {
return Ext.$eventNameMap[name] || (Ext.$eventNameMap[name] = (Ext.$vendorEventRe.test(name) ? name : name.toLowerCase()));
},
/**
* Copies all the properties of config to object if they don't already exist.
* @param {Object} object The receiver of the properties
* @param {Object} config The source of the properties
* @return {Object} returns obj
*/
applyIf: function(object, config) {
var property;
if (object) {
for (property in config) {
if (object[property] === undefined) {
object[property] = config[property];
}
}
}
return object;
},
/**
* Destroys all of the given objects. If arrays are passed, the elements of these
* are destroyed recursively.
*
* What it means to "destroy" an object depends on the type of object.
*
* * `Array`: Each element of the array is destroyed recursively.
* * `Object`: Any object with a `destroy` method will have that method called.
*
* @param {Mixed...} args Any number of objects or arrays.
*/
destroy: function() {
var ln = arguments.length,
i, arg;
for (i = 0; i < ln; i++) {
arg = arguments[i];
if (arg) {
if (Ext.isArray(arg)) {
this.destroy.apply(this, arg);
} else if (Ext.isFunction(arg.destroy)) {
arg.destroy();
}
}
}
return null;
},
/**
* Destroys the specified named members of the given object using `Ext.destroy`. These
* properties will be set to `null`.
* @param {Object} object The object who's properties you wish to destroy.
* @param {String...} args One or more names of the properties to destroy and remove from the object.
*/
destroyMembers: function(object) {
for (var ref, name,
i = 1,
a = arguments,
len = a.length; i < len; i++) {
ref = object[name = a[i]];
// Avoid adding the property if it does not already exist
if (ref != null) {
object[name] = Ext.destroy(ref);
}
}
},
/**
* Overrides members of the specified `target` with the given values.
*
* If the `target` is a class declared using {@link Ext#define Ext.define}, the
* `override` method of that class is called (see {@link Ext.Base#override}) given
* the `overrides`.
*
* If the `target` is a function, it is assumed to be a constructor and the contents
* of `overrides` are applied to its `prototype` using {@link Ext#apply Ext.apply}.
*
* If the `target` is an instance of a class declared using {@link Ext#define Ext.define},
* the `overrides` are applied to only that instance. In this case, methods are
* specially processed to allow them to use {@link Ext.Base#callParent}.
*
* var panel = new Ext.Panel({ ... });
*
* Ext.override(panel, {
* initComponent: function () {
* // extra processing...
*
* this.callParent();
* }
* });
*
* If the `target` is none of these, the `overrides` are applied to the `target`
* using {@link Ext#apply Ext.apply}.
*
* Please refer to {@link Ext#define Ext.define} and {@link Ext.Base#override} for
* further details.
*
* @param {Object} target The target to override.
* @param {Object} overrides The properties to add or replace on `target`.
* @method override
*/
override: function(target, overrides) {
if (target.$isClass) {
target.override(overrides);
} else if (typeof target === 'function') {
Ext.apply(target.prototype, overrides);
} else {
var owner = target.self,
privates;
if (owner && owner.$isClass) {
// if (instance of Ext.define'd class)
privates = overrides.privates;
if (privates) {
overrides = Ext.apply({}, overrides);
delete overrides.privates;
addInstanceOverrides(target, owner, privates);
}
addInstanceOverrides(target, owner, overrides);
} else {
Ext.apply(target, overrides);
}
}
return target;
},
/**
* Returns the given value itself if it's not empty, as described in {@link Ext#isEmpty}; returns the default
* value (second argument) otherwise.
*
* @param {Object} value The value to test.
* @param {Object} defaultValue The value to return if the original value is empty.
* @param {Boolean} [allowBlank=false] `true` to allow zero length strings to qualify as non-empty.
* @return {Object} value, if non-empty, else defaultValue.
*/
valueFrom: function(value, defaultValue, allowBlank) {
return Ext.isEmpty(value, allowBlank) ? defaultValue : value;
},
/**
* Returns true if the passed value is empty, false otherwise. The value is deemed to be empty if it is either:
*
* - `null`
* - `undefined`
* - a zero-length array
* - a zero-length string (Unless the `allowEmptyString` parameter is set to `true`)
*
* @param {Object} value The value to test.
* @param {Boolean} [allowEmptyString=false] `true` to allow empty strings.
* @return {Boolean}
*/
isEmpty: function(value, allowEmptyString) {
return (value == null) || (!allowEmptyString ? value === '' : false) || (Ext.isArray(value) && value.length === 0);
},
/**
* Returns `true` if the passed value is a JavaScript Array, `false` otherwise.
*
* @param {Object} target The target to test.
* @return {Boolean}
* @method
*/
isArray: ('isArray' in Array) ? Array.isArray : function(value) {
return toString.call(value) === '[object Array]';
},
/**
* Returns `true` if the passed value is a JavaScript Date object, `false` otherwise.
* @param {Object} object The object to test.
* @return {Boolean}
*/
isDate: function(value) {
return toString.call(value) === '[object Date]';
},
/**
* Returns 'true' if the passed value is a String that matches the MS Date JSON
* encoding format.
* @param {String} value The string to test.
* @return {Boolean}
*/
isMSDate: function(value) {
if (!Ext.isString(value)) {
return false;
}
return MSDateRe.test(value);
},
/**
* Returns `true` if the passed value is a JavaScript Object, `false` otherwise.
* @param {Object} value The value to test.
* @return {Boolean}
* @method
*/
isObject: (toString.call(null) === '[object Object]') ? function(value) {
// check ownerDocument here as well to exclude DOM nodes
return value !== null && value !== undefined && toString.call(value) === '[object Object]' && value.ownerDocument === undefined;
} : function(value) {
return toString.call(value) === '[object Object]';
},
/**
* @private
*/
isSimpleObject: function(value) {
return value instanceof Object && value.constructor === Object;
},
/**
* Returns `true` if the passed value is a JavaScript 'primitive', a string, number
* or boolean.
* @param {Object} value The value to test.
* @return {Boolean}
*/
isPrimitive: function(value) {
var type = typeof value;
return type === 'string' || type === 'number' || type === 'boolean';
},
/**
* Returns `true` if the passed value is a JavaScript Function, `false` otherwise.
* @param {Object} value The value to test.
* @return {Boolean}
* @method
*/
isFunction: // Safari 3.x and 4.x returns 'function' for typeof <NodeList>, hence we need to fall back to using
// Object.prototype.toString (slower)
(typeof document !== 'undefined' && typeof document.getElementsByTagName('body') === 'function') ? function(value) {
return !!value && toString.call(value) === '[object Function]';
} : function(value) {
return !!value && typeof value === 'function';
},
/**
* Returns `true` if the passed value is a number. Returns `false` for non-finite numbers.
* @param {Object} value The value to test.
* @return {Boolean}
*/
isNumber: function(value) {
return typeof value === 'number' && isFinite(value);
},
/**
* Validates that a value is numeric.
* @param {Object} value Examples: 1, '1', '2.34'
* @return {Boolean} True if numeric, false otherwise
*/
isNumeric: function(value) {
return !isNaN(parseFloat(value)) && isFinite(value);
},
/**
* Returns `true `if the passed value is a string.
* @param {Object} value The value to test.
* @return {Boolean}
*/
isString: function(value) {
return typeof value === 'string';
},
/**
* Returns `true` if the passed value is a boolean.
*
* @param {Object} value The value to test.
* @return {Boolean}
*/
isBoolean: function(value) {
return typeof value === 'boolean';
},
/**
* Returns `true` if the passed value is an HTMLElement
* @param {Object} value The value to test.
* @return {Boolean}
*/
isElement: function(value) {
return value ? value.nodeType === 1 : false;
},
/**
* Returns `true` if the passed value is a TextNode
* @param {Object} value The value to test.
* @return {Boolean}
*/
isTextNode: function(value) {
return value ? value.nodeName === "#text" : false;
},
/**
* Returns `true` if the passed value is defined.
* @param {Object} value The value to test.
* @return {Boolean}
*/
isDefined: function(value) {
return typeof value !== 'undefined';
},
/**
* Returns `true` if the passed value is iterable, that is, if elements of it are addressable using array
* notation with numeric indices, `false` otherwise.
*
* Arrays and function `arguments` objects are iterable. Also HTML collections such as `NodeList` and `HTMLCollection'
* are iterable.
*
* @param {Object} value The value to test
* @return {Boolean}
*/
isIterable: function(value) {
// To be iterable, the object must have a numeric length property and must not be a string or function.
if (!value || typeof value.length !== 'number' || typeof value === 'string' || Ext.isFunction(value)) {
return false;
}
// Certain "standard" collections in IE (such as document.images) do not offer the correct
// Javascript Object interface; specifically, they lack the propertyIsEnumerable method.
// And the item property while it does exist is not typeof "function"
if (!value.propertyIsEnumerable) {
return !!value.item;
}
// If it is a regular, interrogatable JS object (not an IE ActiveX object), then...
// If it has its own property called "length", but not enumerable, it's iterable
if (value.hasOwnProperty('length') && !value.propertyIsEnumerable('length')) {
return true;
}
// Test against whitelist which includes known iterable collection types
return iterableRe.test(toString.call(value));
},
/**
* This method returns `true` if debug is enabled for the specified class. This is
* done by checking the `Ext.debugConfig.hooks` config for the closest match to the
* given `className`.
* @param {String} className The name of the class.
* @return {Boolean} `true` if debug is enabled for the specified class.
*/
isDebugEnabled: function(className, defaultEnabled) {
var debugConfig = Ext.debugConfig.hooks;
if (debugConfig.hasOwnProperty(className)) {
return debugConfig[className];
}
var enabled = debugConfig['*'],
prefixLength = 0;
if (defaultEnabled !== undefined) {
enabled = defaultEnabled;
}
if (!className) {
return enabled;
}
for (var prefix in debugConfig) {
var value = debugConfig[prefix];
// if prefix=='Ext' match 'Ext.foo.Bar' but not 'Ext4.foo.Bar'
if (className.charAt(prefix.length) === '.') {
if (className.substring(0, prefix.length) === prefix) {
if (prefixLength < prefix.length) {
prefixLength = prefix.length;
enabled = value;
}
}
}
}
return enabled;
} || emptyFn,
/**
* Clone simple variables including array, {}-like objects, DOM nodes and Date without keeping the old reference.
* A reference for the object itself is returned if it's not a direct descendant of Object. For model cloning,
* see {@link Ext.data.Model#copy Model.copy}.
*
* @param {Object} item The variable to clone
* @param {Boolean} [cloneDom=true] `true` to clone DOM nodes.
* @return {Object} clone
*/
clone: function(item, cloneDom) {
if (item === null || item === undefined) {
return item;
}
// DOM nodes
// TODO proxy this to Ext.Element.clone to handle automatic id attribute changing
// recursively
if (cloneDom !== false && item.nodeType && item.cloneNode) {
return item.cloneNode(true);
}
var type = toString.call(item),
i, j, k, clone, key;
// Date
if (type === '[object Date]') {
return new Date(item.getTime());
}
// Array
if (type === '[object Array]') {
i = item.length;
clone = [];
while (i--) {
clone[i] = Ext.clone(item[i], cloneDom);
}
}
// Object
else if (type === '[object Object]' && item.constructor === Object) {
clone = {};
for (key in item) {
clone[key] = Ext.clone(item[key], cloneDom);
}
if (enumerables) {
for (j = enumerables.length; j--; ) {
k = enumerables[j];
if (item.hasOwnProperty(k)) {
clone[k] = item[k];
}
}
}
}
return clone || item;
},
/**
* @private
* Generate a unique reference of Ext in the global scope, useful for sandboxing
*/
getUniqueGlobalNamespace: function() {
var uniqueGlobalNamespace = this.uniqueGlobalNamespace,
i;
if (uniqueGlobalNamespace === undefined) {
i = 0;
do {
uniqueGlobalNamespace = 'ExtBox' + (++i);
} while (global[uniqueGlobalNamespace] !== undefined);
global[uniqueGlobalNamespace] = Ext;
this.uniqueGlobalNamespace = uniqueGlobalNamespace;
}
return uniqueGlobalNamespace;
},
/**
* @private
*/
functionFactoryCache: {},
cacheableFunctionFactory: function() {
var me = this,
args = Array.prototype.slice.call(arguments),
cache = me.functionFactoryCache,
idx, fn, ln;
if (Ext.isSandboxed) {
ln = args.length;
if (ln > 0) {
ln--;
args[ln] = 'var Ext=window.' + Ext.name + ';' + args[ln];
}
}
idx = args.join('');
fn = cache[idx];
if (!fn) {
fn = Function.prototype.constructor.apply(Function.prototype, args);
cache[idx] = fn;
}
return fn;
},
functionFactory: function() {
var args = Array.prototype.slice.call(arguments),
ln;
if (Ext.isSandboxed) {
ln = args.length;
if (ln > 0) {
ln--;
args[ln] = 'var Ext=window.' + Ext.name + ';' + args[ln];
}
}
return Function.prototype.constructor.apply(Function.prototype, args);
},
/**
* @private
*/
Logger: {
log: function(message, priority) {
if (message && global.console) {
if (!priority || !(priority in global.console)) {
priority = 'log';
}
message = '[' + priority.toUpperCase() + '] ' + message;
global.console[priority](message);
}
},
verbose: function(message) {
this.log(message, 'verbose');
},
info: function(message) {
this.log(message, 'info');
},
warn: function(message) {
this.log(message, 'warn');
},
error: function(message) {
throw new Error(message);
},
deprecate: function(message) {
this.log(message, 'warn');
}
} || {
verbose: emptyFn,
log: emptyFn,
info: emptyFn,
warn: emptyFn,
error: function(message) {
throw new Error(message);
},
deprecate: emptyFn
},
ariaWarn: function(target, msg) {
// The checks still can be disabled by setting Ext.enableAria to false;
// this is for backwards compatibility. Also make sure we're not running
// under the slicer, warnings are pointless in that case.
if (Ext.enableAria && !Ext.slicer) {
if (!Ext.ariaWarn.first) {
Ext.ariaWarn.first = true;
Ext.log.warn("WAI-ARIA compatibility warnings can be suppressed " + "by adding the following to application startup code:");
Ext.log.warn(" Ext.ariaWarn = Ext.emptyFn;");
}
Ext.log.warn({
msg: msg,
dump: target
});
}
},
/**
* @private
*/
getElementById: function(id) {
return document.getElementById(id);
},
/**
* @member Ext
* @private
*/
splitAndUnescape: (function() {
var cache = {};
return function(origin, delimiter) {
if (!origin) {
return [];
} else if (!delimiter) {
return [
origin
];
}
var replaceRe = cache[delimiter] || (cache[delimiter] = new RegExp('\\\\' + delimiter, 'g')),
result = [],
parts, part;
parts = origin.split(delimiter);
while ((part = parts.shift()) !== undefined) {
// If any of the parts ends with the delimiter that means
// the delimiter was escaped and the split was invalid. Roll back.
while (part.charAt(part.length - 1) === '\\' && parts.length > 0) {
part = part + delimiter + parts.shift();
}
// Now that we have split the parts, unescape the delimiter char
part = part.replace(replaceRe, delimiter);
result.push(part);
}
return result;
};
})()
});
// Ext.apply(Ext
Ext.returnTrue.$nullFn = Ext.returnId.$nullFn = true;
}());
// @override Ext
// This file is order extremely early (typically right after Ext.js) due to the
// above Cmd directive. This ensures that the "modern" and "classic" platform tags
// are properly set up as soon as possible.
Ext.platformTags.modern = !(Ext.platformTags.classic = Ext.isClassic = true);
/**
* A helper class for the native JavaScript Error object that adds a few useful capabilities for handling
* errors in an application. When you use Ext.Error to {@link #raise} an error from within any class that
* uses the Class System, the Error class can automatically add the source class and method from which
* the error was raised. It also includes logic to automatically log the error to the console, if available,
* with additional metadata about the error. In all cases, the error will always be thrown at the end so that
* execution will halt.
*
* Ext.Error also offers a global error {@link #handle handling} method that can be overridden in order to
* handle application-wide errors in a single spot. You can optionally {@link #ignore} errors altogether,
* although in a real application it's usually a better idea to override the handling function and perform
* logging or some other method of reporting the errors in a way that is meaningful to the application.
*
* At its simplest you can simply raise an error as a simple string from within any code:
*
* Example usage:
*
* Ext.raise('Something bad happened!');
*
* If raised from plain JavaScript code, the error will be logged to the console (if available) and the message
* displayed. In most cases however you'll be raising errors from within a class, and it may often be useful to add
* additional metadata about the error being raised. The {@link #raise} method can also take a config object.
* In this form the `msg` attribute becomes the error description, and any other data added to the config gets
* added to the error object and, if the console is available, logged to the console for inspection.
*
* Example usage:
*
* Ext.define('Ext.Foo', {
* doSomething: function(option){
* if (someCondition === false) {
* Ext.raise({
* msg: 'You cannot do that!',
* option: option, // whatever was passed into the method
* 'error code': 100 // other arbitrary info
* });
* }
* }
* });
*
* If a console is available (that supports the `console.dir` function) you'll see console output like:
*
* An error was raised with the following data:
* option: Object { foo: "bar"}
* foo: "bar"
* error code: 100
* msg: "You cannot do that!"
* sourceClass: "Ext.Foo"
* sourceMethod: "doSomething"
*
* uncaught exception: You cannot do that!
*
* As you can see, the error will report exactly where it was raised and will include as much information as the
* raising code can usefully provide.
*
* If you want to handle all application errors globally you can simply override the static {@link #handle} method
* and provide whatever handling logic you need. If the method returns true then the error is considered handled
* and will not be thrown to the browser. If anything but true is returned then the error will be thrown normally.
*
* Example usage:
*
* Ext.Error.handle = function(err) {
* if (err.someProperty == 'NotReallyAnError') {
* // maybe log something to the application here if applicable
* return true;
* }
* // any non-true return value (including none) will cause the error to be thrown
* }
*
* @class Ext.Error
*/
(function() {
// @define Ext.lang.Error
// @define Ext.Error
// @require Ext
function toString() {
var me = this,
cls = me.sourceClass,
method = me.sourceMethod,
msg = me.msg;
if (method) {
if (msg) {
method += '(): ';
method += msg;
} else {
method += '()';
}
}
if (cls) {
method = method ? (cls + '.' + method) : cls;
}
return method || msg || '';
}
Ext.Error = function(config) {
if (Ext.isString(config)) {
config = {
msg: config
};
}
var error = new Error();
Ext.apply(error, config);
error.message = error.message || error.msg;
// 'message' is standard ('msg' is non-standard)
// note: the above does not work in old WebKit (me.message is readonly) (Safari 4)
error.toString = toString;
return error;
};
Ext.apply(Ext.Error, {
/**
* @property {Boolean} ignore
* Static flag that can be used to globally disable error reporting to the browser if set to true
* (defaults to false). Note that if you ignore Ext errors it's likely that some other code may fail
* and throw a native JavaScript error thereafter, so use with caution. In most cases it will probably
* be preferable to supply a custom error {@link #handle handling} function instead.
*
* Example usage:
*
* Ext.Error.ignore = true;
*
* @static
*/
ignore: false,
/**
* This method is called internally by {@link Ext#raise}. Application code should
* call {@link Ext#raise} instead of calling this method directly.
*
* @static
* @deprecated 6.0.0 Use {@link Ext#raise} instead.
*/
raise: function(err) {
err = err || {};
if (Ext.isString(err)) {
err = {
msg: err
};
}
var me = this,
method = me.raise.caller,
msg, name;
if (method === Ext.raise) {
method = method.caller;
}
if (method) {
if (!err.sourceMethod && (name = method.$name)) {
err.sourceMethod = name;
}
if (!err.sourceClass && (name = method.$owner) && (name = name.$className)) {
err.sourceClass = name;
}
}
if (me.handle(err) !== true) {
msg = toString.call(err);
Ext.log({
msg: msg,
level: 'error',
dump: err,
stack: true
});
throw new Ext.Error(err);
}
},
/**
* Globally handle any Ext errors that may be raised, optionally providing custom logic to
* handle different errors individually. Return true from the function to bypass throwing the
* error to the browser, otherwise the error will be thrown and execution will halt.
*
* Example usage:
*
* Ext.Error.handle = function(err) {
* if (err.someProperty == 'NotReallyAnError') {
* // maybe log something to the application here if applicable
* return true;
* }
* // any non-true return value (including none) will cause the error to be thrown
* }
*
* @param {Object} err The error being raised. It will contain any attributes that were originally
* raised with it, plus properties about the method and class from which the error originated
* (if raised from a class that uses the Class System).
* @static
*/
handle: function() {
return this.ignore;
}
});
})();
/**
* Create a function that will throw an error if called (in debug mode) with a message that
* indicates the method has been removed.
* @param {String} suggestion Optional text to include in the message (a workaround perhaps).
* @return {Function} The generated function.
* @private
*/
Ext.deprecated = function(suggestion) {
if (!suggestion) {
suggestion = '';
}
function fail() {
Ext.raise('The method "' + fail.$owner.$className + '.' + fail.$name + '" has been removed. ' + suggestion);
}
return fail;
return Ext.emptyFn;
};
/**
* Raise an error that can include additional data and supports automatic console logging
* if available. You can pass a string error message or an object with the `msg` attribute
* which will be used as the error message. The object can contain any other name-value
* attributes (or objects) to be logged along with the error.
*
* Note that after displaying the error message a JavaScript error will ultimately be
* thrown so that execution will halt.
*
* Example usage:
*
* Ext.raise('A simple string error message');
*
* // or...
*
* Ext.define('Ext.Foo', {
* doSomething: function(option){
* if (someCondition === false) {
* Ext.raise({
* msg: 'You cannot do that!',
* option: option, // whatever was passed into the method
* code: 100 // other arbitrary info
* });
* }
* }
* });
*
* @param {String/Object} err The error message string, or an object containing the
* attribute "msg" that will be used as the error message. Any other data included in the
* object will also be logged to the browser console, if available.
* @method raise
* @member Ext
*/
Ext.raise = function() {
Ext.Error.raise.apply(Ext.Error, arguments);
};
/*
* This mechanism is used to notify the user of the first error encountered on the page. In
* most cases errors go unobserved especially on IE. This mechanism pushes this information
* to the status bar so that users don't miss it.
*/
(function() {
if (typeof window === 'undefined') {
return;
}
// build system or some such environment...
var last = 0,
// This method is called to notify the user of the current error status.
notify = function() {
var cnt = Ext.log && Ext.log.counters,
n = cnt && (cnt.error + cnt.warn + cnt.info + cnt.log),
msg;
// Put log counters to the status bar (for most browsers):
if (n && last !== n) {
msg = [];
if (cnt.error) {
msg.push('Errors: ' + cnt.error);
}
if (cnt.warn) {
msg.push('Warnings: ' + cnt.warn);
}
if (cnt.info) {
msg.push('Info: ' + cnt.info);
}
if (cnt.log) {
msg.push('Log: ' + cnt.log);
}
window.status = '*** ' + msg.join(' -- ');
last = n;
}
};
// window.onerror sounds ideal but it prevents the built-in error dialog from doing
// its (better) thing.
setInterval(notify, 1000);
}());
/**
* @class Ext.Array
* @singleton
*
* A set of useful static methods to deal with arrays; provide missing methods for
* older browsers.
*/
Ext.Array = (function() {
// @define Ext.lang.Array
// @define Ext.Array
// @require Ext
// @require Ext.lang.Error
var arrayPrototype = Array.prototype,
slice = arrayPrototype.slice,
supportsSplice = (function() {
var array = [],
lengthBefore,
j = 20;
if (!array.splice) {
return false;
}
// This detects a bug in IE8 splice method:
// see http://social.msdn.microsoft.com/Forums/en-US/iewebdevelopment/thread/6e946d03-e09f-4b22-a4dd-cd5e276bf05a/
while (j--) {
array.push("A");
}
array.splice(15, 0, "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F");
lengthBefore = array.length;
//41
array.splice(13, 0, "XXX");
// add one element
if (lengthBefore + 1 !== array.length) {
return false;
}
// end IE8 bug
return true;
}()),
supportsIndexOf = 'indexOf' in arrayPrototype,
supportsSliceOnNodeList = true;
// Sort an array using the comparator, but if the comparator returns zero, use the objects' original indices to tiebreak
// This results in a stable sort.
function stableSort(array, userComparator) {
var len = array.length,
indices = new Array(len),
i;
// generate 0-n index map from original array
for (i = 0; i < len; i++) {
indices[i] = i;
}
// Sort indices array using a comparator which compares the original values at the two indices, and uses those indices as a tiebreaker
indices.sort(function(index1, index2) {
return userComparator(array[index1], array[index2]) || (index1 - index2);
});
// Reconsitute a sorted array using the array that the indices have been sorted into
for (i = 0; i < len; i++) {
indices[i] = array[indices[i]];
}
// Rebuild the original array
for (i = 0; i < len; i++) {
array[i] = indices[i];
}
return array;
}
try {
// IE 6 - 8 will throw an error when using Array.prototype.slice on NodeList
if (typeof document !== 'undefined') {
slice.call(document.getElementsByTagName('body'));
}
} catch (e) {
supportsSliceOnNodeList = false;
}
var fixArrayIndex = function(array, index) {
return (index < 0) ? Math.max(0, array.length + index) : Math.min(array.length, index);
},
/*
Does the same work as splice, but with a slightly more convenient signature. The splice
method has bugs in IE8, so this is the implementation we use on that platform.
The rippling of items in the array can be tricky. Consider two use cases:
index=2
removeCount=2
/=====\
+---+---+---+---+---+---+---+---+
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
+---+---+---+---+---+---+---+---+
/ \/ \/ \/ \
/ /\ /\ /\ \
/ / \/ \/ \ +--------------------------+
/ / /\ /\ +--------------------------+ \
/ / / \/ +--------------------------+ \ \
/ / / /+--------------------------+ \ \ \
/ / / / \ \ \ \
v v v v v v v v
+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+
| 0 | 1 | 4 | 5 | 6 | 7 | | 0 | 1 | a | b | c | 4 | 5 | 6 | 7 |
+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+---+
A B \=========/
insert=[a,b,c]
In case A, it is obvious that copying of [4,5,6,7] must be left-to-right so
that we don't end up with [0,1,6,7,6,7]. In case B, we have the opposite; we
must go right-to-left or else we would end up with [0,1,a,b,c,4,4,4,4].
*/
replaceSim = function(array, index, removeCount, insert) {
var add = insert ? insert.length : 0,
length = array.length,
pos = fixArrayIndex(array, index);
// we try to use Array.push when we can for efficiency...
if (pos === length) {
if (add) {
array.push.apply(array, insert);
}
} else {
var remove = Math.min(removeCount, length - pos),
tailOldPos = pos + remove,
tailNewPos = tailOldPos + add - remove,
tailCount = length - tailOldPos,
lengthAfterRemove = length - remove,
i;
if (tailNewPos < tailOldPos) {
// case A
for (i = 0; i < tailCount; ++i) {
array[tailNewPos + i] = array[tailOldPos + i];
}
} else if (tailNewPos > tailOldPos) {
// case B
for (i = tailCount; i--; ) {
array[tailNewPos + i] = array[tailOldPos + i];
}
}
// else, add == remove (nothing to do)
if (add && pos === lengthAfterRemove) {
array.length = lengthAfterRemove;
// truncate array
array.push.apply(array, insert);
} else {
array.length = lengthAfterRemove + add;
// reserves space
for (i = 0; i < add; ++i) {
array[pos + i] = insert[i];
}
}
}
return array;
},
replaceNative = function(array, index, removeCount, insert) {
if (insert && insert.length) {
// Inserting at index zero with no removing: use unshift
if (index === 0 && !removeCount) {
array.unshift.apply(array, insert);
}
// Inserting/replacing in middle of array
else if (index < array.length) {
array.splice.apply(array, [
index,
removeCount
].concat(insert));
} else // Appending to array
{
array.push.apply(array, insert);
}
} else {
array.splice(index, removeCount);
}
return array;
},
eraseSim = function(array, index, removeCount) {
return replaceSim(array, index, removeCount);
},
eraseNative = function(array, index, removeCount) {
array.splice(index, removeCount);
return array;
},
spliceSim = function(array, index, removeCount) {
var len = arguments.length,
pos = fixArrayIndex(array, index),
removed;
if (len < 3) {
removeCount = array.length - pos;
}
removed = array.slice(index, fixArrayIndex(array, pos + removeCount));
if (len < 4) {
replaceSim(array, pos, removeCount);
} else {
replaceSim(array, pos, removeCount, slice.call(arguments, 3));
}
return removed;
},
spliceNative = function(array) {
return array.splice.apply(array, slice.call(arguments, 1));
},
erase = supportsSplice ? eraseNative : eraseSim,
replace = supportsSplice ? replaceNative : replaceSim,
splice = supportsSplice ? spliceNative : spliceSim,
// NOTE: from here on, use erase, replace or splice (not native methods)...
ExtArray = {
/**
* This method returns the index that a given item would be inserted into the
* given (sorted) `array`. Note that the given `item` may or may not be in the
* array. This method will return the index of where the item *should* be.
*
* For example:
*
* var array = [ 'A', 'D', 'G', 'K', 'O', 'R', 'X' ];
* var index = Ext.Array.binarySearch(array, 'E');
*
* console.log('index: ' + index);
* // logs "index: 2"
*
* array.splice(index, 0, 'E');
*
* console.log('array : ' + array.join(''));
* // logs "array: ADEGKORX"
*
* @param {Object[]} array The array to search.
* @param {Object} item The item that you want to insert into the `array`.
* @param {Number} [begin=0] The first index in the `array` to consider.
* @param {Number} [end=array.length] The index that marks the end of the range
* to consider. The item at this index is *not* considered.
* @param {Function} [compareFn] The comparison function that matches the sort
* order of the `array`. The default `compareFn` compares items using less-than
* and greater-than operators.
* @return {Number} The index for the given item in the given array based on
* the current sorters.
*/
binarySearch: function(array, item, begin, end, compareFn) {
var length = array.length,
middle, comparison;
if (begin instanceof Function) {
compareFn = begin;
begin = 0;
end = length;
} else if (end instanceof Function) {
compareFn = end;
end = length;
} else {
if (begin === undefined) {
begin = 0;
}
if (end === undefined) {
end = length;
}
compareFn = compareFn || ExtArray.lexicalCompare;
}
--end;
while (begin <= end) {
middle = (begin + end) >> 1;
comparison = compareFn(item, array[middle]);
if (comparison >= 0) {
begin = middle + 1;
} else if (comparison < 0) {
end = middle - 1;
}
}
return begin;
},
defaultCompare: function(lhs, rhs) {
return (lhs < rhs) ? -1 : ((lhs > rhs) ? 1 : 0);
},
// Default comparator to use when no comparator is specified for the sort method.
// Javascript sort does LEXICAL comparison.
lexicalCompare: function(lhs, rhs) {
lhs = String(lhs);
rhs = String(rhs);
return (lhs < rhs) ? -1 : ((lhs > rhs) ? 1 : 0);
},
/**
* Iterates an array or an iterable value and invoke the given callback function for each item.
*
* var countries = ['Vietnam', 'Singapore', 'United States', 'Russia'];
*
* Ext.Array.each(countries, function(name, index, countriesItSelf) {
* console.log(name);
* });
*
* var sum = function() {
* var sum = 0;
*
* Ext.Array.each(arguments, function(value) {
* sum += value;
* });
*
* return sum;
* };
*
* sum(1, 2, 3); // returns 6
*
* The iteration can be stopped by returning `false` from the callback function.
* Returning `undefined` (i.e `return;`) will only exit the callback function and
* proceed with the next iteration of the loop.
*
* Ext.Array.each(countries, function(name, index, countriesItSelf) {
* if (name === 'Singapore') {
* return false; // break here
* }
* });
*
* {@link Ext#each Ext.each} is alias for {@link Ext.Array#each Ext.Array.each}
*
* @param {Array/NodeList/Object} iterable The value to be iterated. If this
* argument is not iterable, the callback function is called once.
* @param {Function} fn The callback function. If it returns `false`, the iteration
* stops and this method returns the current `index`. Returning `undefined` (i.e
* `return;`) will only exit the callback function and proceed with the next iteration
* in the loop.
* @param {Object} fn.item The item at the current `index` in the passed `array`
* @param {Number} fn.index The current `index` within the `array`
* @param {Array} fn.allItems The `array` itself which was passed as the first argument
* @param {Boolean} fn.return Return `false` to stop iteration.
* @param {Object} [scope] The scope (`this` reference) in which the specified function is executed.
* @param {Boolean} [reverse=false] Reverse the iteration order (loop from the end to the beginning).
* @return {Boolean} See description for the `fn` parameter.
*/
each: function(array, fn, scope, reverse) {
array = ExtArray.from(array);
var i,
ln = array.length;
if (reverse !== true) {
for (i = 0; i < ln; i++) {
if (fn.call(scope || array[i], array[i], i, array) === false) {
return i;
}
}
} else {
for (i = ln - 1; i > -1; i--) {
if (fn.call(scope || array[i], array[i], i, array) === false) {
return i;
}
}
}
return true;
},
/**
* @method
* Iterates an array and invoke the given callback function for each item. Note that this will simply
* delegate to the native `Array.prototype.forEach` method if supported. It doesn't support stopping the
* iteration by returning `false` in the callback function like {@link Ext.Array#each}. However, performance
* could be much better in modern browsers comparing with {@link Ext.Array#each}
*
* @param {Array} array The array to iterate.
* @param {Function} fn The callback function.
* @param {Object} fn.item The item at the current `index` in the passed `array`.
* @param {Number} fn.index The current `index` within the `array`.
* @param {Array} fn.allItems The `array` itself which was passed as the first argument.
* @param {Object} scope (Optional) The execution scope (`this`) in which the
* specified function is executed.
*/
forEach: ('forEach' in arrayPrototype) ? function(array, fn, scope) {
return array.forEach(fn, scope);
} : function(array, fn, scope) {
for (var i = 0,
ln = array.length; i < ln; i++) {
fn.call(scope, array[i], i, array);
}
},
/**
* @method
* Get the index of the provided `item` in the given `array`, a supplement for the
* missing arrayPrototype.indexOf in Internet Explorer.
*
* @param {Array} array The array to check.
* @param {Object} item The item to find.
* @param {Number} from (Optional) The index at which to begin the search.
* @return {Number} The index of item in the array (or -1 if it is not found).
*/
indexOf: supportsIndexOf ? function(array, item, from) {
// May be called with no array which causes an error.
return array ? arrayPrototype.indexOf.call(array, item, from) : -1;
} : function(array, item, from) {
var i,
length = array ? array.length : 0;
for (i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++) {
if (array[i] === item) {
return i;
}
}
return -1;
},
/**
* @method
* Checks whether or not the given `array` contains the specified `item`.
*
* @param {Array} array The array to check.
* @param {Object} item The item to find.
* @return {Boolean} `true` if the array contains the item, `false` otherwise.
*/
contains: supportsIndexOf ? function(array, item) {
return arrayPrototype.indexOf.call(array, item) !== -1;
} : function(array, item) {
var i, ln;
for (i = 0 , ln = array.length; i < ln; i++) {
if (array[i] === item) {
return true;
}
}
return false;
},
/**
* Converts any iterable (numeric indices and a length property) into a true array.
*
* function test() {
* var args = Ext.Array.toArray(arguments),
* fromSecondToLastArgs = Ext.Array.toArray(arguments, 1);
*
* alert(args.join(' '));
* alert(fromSecondToLastArgs.join(' '));
* }
*
* test('just', 'testing', 'here'); // alerts 'just testing here';
* // alerts 'testing here';
*
* Ext.Array.toArray(document.getElementsByTagName('div')); // will convert the NodeList into an array
* Ext.Array.toArray('splitted'); // returns ['s', 'p', 'l', 'i', 't', 't', 'e', 'd']
* Ext.Array.toArray('splitted', 0, 3); // returns ['s', 'p', 'l']
*
* {@link Ext#toArray Ext.toArray} is alias for {@link Ext.Array#toArray Ext.Array.toArray}
*
* @param {Object} iterable the iterable object to be turned into a true Array.
* @param {Number} [start=0] a zero-based index that specifies the start of extraction.
* @param {Number} [end=-1] a 1-based index that specifies the end of extraction.
* @return {Array}
*/
toArray: function(iterable, start, end) {
if (!iterable || !iterable.length) {
return [];
}
if (typeof iterable === 'string') {
iterable = iterable.split('');
}
if (supportsSliceOnNodeList) {
return slice.call(iterable, start || 0, end || iterable.length);
}
var array = [],
i;
start = start || 0;
end = end ? ((end < 0) ? iterable.length + end : end) : iterable.length;
for (i = start; i < end; i++) {
array.push(iterable[i]);
}
return array;
},
/**
* Plucks the value of a property from each item in the Array. Example:
*
* Ext.Array.pluck(Ext.query("p"), "className"); // [el1.className, el2.className, ..., elN.className]
*
* @param {Array/NodeList} array The Array of items to pluck the value from.
* @param {String} propertyName The property name to pluck from each element.
* @return {Array} The value from each item in the Array.
*/
pluck: function(array, propertyName) {
var ret = [],
i, ln, item;
for (i = 0 , ln = array.length; i < ln; i++) {
item = array[i];
ret.push(item[propertyName]);
}
return ret;
},
/**
* @method
* Creates a new array with the results of calling a provided function on every element in this array.
*
* @param {Array} array
* @param {Function} fn Callback function for each item.
* @param {Mixed} fn.item Current item.
* @param {Number} fn.index Index of the item.
* @param {Array} fn.array The whole array that's being iterated.
* @param {Object} [scope] Callback function scope
* @return {Array} results
*/
map: ('map' in arrayPrototype) ? function(array, fn, scope) {
Ext.Assert.isFunction(fn, 'Ext.Array.map must have a callback function passed as second argument.');
return array.map(fn, scope);
} : function(array, fn, scope) {
Ext.Assert.isFunction(fn, 'Ext.Array.map must have a callback function passed as second argument.');
var len = array.length,
results = new Array(len),
i;
for (i = 0; i < len; i++) {
results[i] = fn.call(scope, array[i], i, array);
}
return results;
},
/**
* @method
* Executes the specified function for each array element until the function returns a falsy value.
* If such an item is found, the function will return `false` immediately.
* Otherwise, it will return `true`.
*
* @param {Array} array
* @param {Function} fn Callback function for each item.
* @param {Mixed} fn.item Current item.
* @param {Number} fn.index Index of the item.
* @param {Array} fn.array The whole array that's being iterated.
* @param {Object} scope Callback function scope.
* @return {Boolean} `treu` if no false value is returned by the callback function.
*/
every: ('every' in arrayPrototype) ? function(array, fn, scope) {
Ext.Assert.isFunction(fn, 'Ext.Array.every must have a callback function passed as second argument.');
return array.every(fn, scope);
} : function(array, fn, scope) {
Ext.Assert.isFunction(fn, 'Ext.Array.every must have a callback function passed as second argument.');
var i = 0,
ln = array.length;
for (; i < ln; ++i) {
if (!fn.call(scope, array[i], i, array)) {
return false;
}
}
return true;
},
/**
* @method
* Executes the specified function for each array element until the function returns a truthy value.
* If such an item is found, the function will return `true` immediately. Otherwise, it will return `false`.
*
* @param {Array} array
* @param {Function} fn Callback function for each item.
* @param {Mixed} fn.item Current item.
* @param {Number} fn.index Index of the item.
* @param {Array} fn.array The whole array that's being iterated.
* @param {Object} scope Callback function scope.
* @return {Boolean} `true` if the callback function returns a truthy value.
*/
some: ('some' in arrayPrototype) ? function(array, fn, scope) {
Ext.Assert.isFunction(fn, 'Ext.Array.some must have a callback function passed as second argument.');
return array.some(fn, scope);
} : function(array, fn, scope) {
Ext.Assert.isFunction(fn, 'Ext.Array.some must have a callback function passed as second argument.');
var i = 0,
ln = array.length;
for (; i < ln; ++i) {
if (fn.call(scope, array[i], i, array)) {
return true;
}
}
return false;
},
/**
* Shallow compares the contents of 2 arrays using strict equality.
* @param {Array} array1
* @param {Array} array2
* @return {Boolean} `true` if the arrays are equal.
*/
equals: function(array1, array2) {
var len1 = array1.length,
len2 = array2.length,
i;
// Short circuit if the same array is passed twice
if (array1 === array2) {
return true;
}
if (len1 !== len2) {
return false;
}
for (i = 0; i < len1; ++i) {
if (array1[i] !== array2[i]) {
return false;
}
}
return true;
},
/**
* Filter through an array and remove empty item as defined in {@link Ext#isEmpty Ext.isEmpty}.
*
* See {@link Ext.Array#filter}
*
* @param {Array} array
* @return {Array} results
*/
clean: function(array) {
var results = [],
i = 0,
ln = array.length,
item;
for (; i < ln; i++) {
item = array[i];
if (!Ext.isEmpty(item)) {
results.push(item);
}
}
return results;
},
/**
* Returns a new array with unique items.
*
* @param {Array} array
* @return {Array} results
*/
unique: function(array) {
var clone = [],
i = 0,
ln = array.length,
item;
for (; i < ln; i++) {
item = array[i];
if (ExtArray.indexOf(clone, item) === -1) {
clone.push(item);
}
}
return clone;
},
/**
* @method
* Creates a new array with all of the elements of this array for which
* the provided filtering function returns a truthy value.
*
* @param {Array} array
* @param {Function} fn Callback function for each item.
* @param {Mixed} fn.item Current item.
* @param {Number} fn.index Index of the item.
* @param {Array} fn.array The whole array that's being iterated.
* @param {Object} scope Callback function scope.
* @return {Array} results
*/
filter: ('filter' in arrayPrototype) ? function(array, fn, scope) {
Ext.Assert.isFunction(fn, 'Ext.Array.filter must have a filter function passed as second argument.');
return array.filter(fn, scope);
} : function(array, fn, scope) {
Ext.Assert.isFunction(fn, 'Ext.Array.filter must have a filter function passed as second argument.');
var results = [],
i = 0,
ln = array.length;
for (; i < ln; i++) {
if (fn.call(scope, array[i], i, array)) {
results.push(array[i]);
}
}
return results;
},
/**
* Returns the first item in the array which elicits a truthy return value from the
* passed selection function.
* @param {Array} array The array to search
* @param {Function} fn The selection function to execute for each item.
* @param {Mixed} fn.item The array item.
* @param {Number} fn.index The index of the array item.
* @param {Object} scope (optional) The scope (<code>this</code> reference) in which the
* function is executed. Defaults to the array
* @return {Object} The first item in the array which returned true from the selection
* function, or null if none was found.
*/
findBy: function(array, fn, scope) {
var i = 0,
len = array.length;
for (; i < len; i++) {
if (fn.call(scope || array, array[i], i)) {
return array[i];
}
}
return null;
},
/**
* Converts a value to an array if it's not already an array; returns:
*
* - An empty array if given value is `undefined` or `null`
* - Itself if given value is already an array
* - An array copy if given value is {@link Ext#isIterable iterable} (arguments, NodeList and alike)
* - An array with one item which is the given value, otherwise
*
* @param {Object} value The value to convert to an array if it's not already is an array.
* @param {Boolean} [newReference] `true` to clone the given array and return a new reference if necessary.
* @return {Array} array
*/
from: function(value, newReference) {
if (value === undefined || value === null) {
return [];
}
if (Ext.isArray(value)) {
return (newReference) ? slice.call(value) : value;
}
var type = typeof value;
// Both strings and functions will have a length property. In phantomJS, NodeList
// instances report typeof=='function' but don't have an apply method...
if (value && value.length !== undefined && type !== 'string' && (type !== 'function' || !value.apply)) {
return ExtArray.toArray(value);
}
return [
value
];
},
/**
* Removes the specified item from the array if it exists.
*
* @param {Array} array The array.
* @param {Object} item The item to remove.
* @return {Array} The passed array.
*/
remove: function(array, item) {
var index = ExtArray.indexOf(array, item);
if (index !== -1) {
erase(array, index, 1);
}
return array;
},
/**
* Removes item/s at the specified index.
*
* @param {Array} array The array.
* @param {Number} index The index of the item to be removed.
* @param {Number} [count=1] The number of items to be removed.
* @return {Array} The passed array.
*/
removeAt: function(array, index, count) {
var len = array.length;
if (index >= 0 && index < len) {
count = count || 1;
count = Math.min(count, len - index);
erase(array, index, count);
}
return array;
},
/**
* Push an item into the array only if the array doesn't contain it yet.
*
* @param {Array} array The array.
* @param {Object} item The item to include.
*/
include: function(array, item) {
if (!ExtArray.contains(array, item)) {
array.push(item);
}
},
/**
* Clone a flat array without referencing the previous one. Note that this is different
* from `Ext.clone` since it doesn't handle recursive cloning. It's simply a convenient, easy-to-remember method
* for `Array.prototype.slice.call(array)`.
*
* @param {Array} array The array.
* @return {Array} The clone array.
*/
clone: function(array) {
return slice.call(array);
},
/**
* Merge multiple arrays into one with unique items.
*
* {@link Ext.Array#union} is alias for {@link Ext.Array#merge}
*
* @param {Array} array1
* @param {Array} array2
* @param {Array} etc
* @return {Array} merged
*/
merge: function() {
var args = slice.call(arguments),
array = [],
i, ln;
for (i = 0 , ln = args.length; i < ln; i++) {
array = array.concat(args[i]);
}
return ExtArray.unique(array);
},
/**
* Merge multiple arrays into one with unique items that exist in all of the arrays.
*
* @param {Array} array1
* @param {Array} array2
* @param {Array} etc
* @return {Array} intersect
*/
intersect: function() {
var intersection = [],
arrays = slice.call(arguments),
arraysLength, array, arrayLength, minArray, minArrayIndex, minArrayCandidate, minArrayLength, element, elementCandidate, elementCount, i, j, k;
if (!arrays.length) {
return intersection;
}
// Find the smallest array
arraysLength = arrays.length;
for (i = minArrayIndex = 0; i < arraysLength; i++) {
minArrayCandidate = arrays[i];
if (!minArray || minArrayCandidate.length < minArray.length) {
minArray = minArrayCandidate;
minArrayIndex = i;
}
}
minArray = ExtArray.unique(minArray);
erase(arrays, minArrayIndex, 1);
// Use the smallest unique'd array as the anchor loop. If the other array(s) do contain
// an item in the small array, we're likely to find it before reaching the end
// of the inner loop and can terminate the search early.
minArrayLength = minArray.length;
arraysLength = arrays.length;
for (i = 0; i < minArrayLength; i++) {
element = minArray[i];
elementCount = 0;
for (j = 0; j < arraysLength; j++) {
array = arrays[j];
arrayLength = array.length;
for (k = 0; k < arrayLength; k++) {
elementCandidate = array[k];
if (element === elementCandidate) {
elementCount++;
break;
}
}
}
if (elementCount === arraysLength) {
intersection.push(element);
}
}
return intersection;
},
/**
* Perform a set difference A-B by subtracting all items in array B from array A.
*
* @param {Array} arrayA
* @param {Array} arrayB
* @return {Array} difference
*/
difference: function(arrayA, arrayB) {
var clone = slice.call(arrayA),
ln = clone.length,
i, j, lnB;
for (i = 0 , lnB = arrayB.length; i < lnB; i++) {
for (j = 0; j < ln; j++) {
if (clone[j] === arrayB[i]) {
erase(clone, j, 1);
j--;
ln--;
}
}
}
return clone;
},
/**
* This method applies the `reduceFn` function against an accumulator and each
* value of the `array` (from left-to-right) to reduce it to a single value.
*
* If no `initialValue` is specified, the first element of the array is used as
* the initial value. For example:
*
* function reducer (previous, value, index) {
* console.log('[' + index + ']: (' + previous + ',' + value + '}');
* return previous * 10 + value;
* }
*
* v = Ext.Array.reduce([2, 3, 4], reducer);
* console.log('v = ' + v);
*
* > [1]: (2, 3)
* > [2]: (23, 4)
* > v = 234
*
* v = Ext.Array.reduce([2, 3, 4], reducer, 1);
* console.log('v = ' + v);
*
* > [0]: (1, 2)
* > [1]: (12, 3)
* > [2]: (123, 4)
* > v = 1234
*
* @param {Array} array The array to process.
* @param {Function} reduceFn The reducing callback function.
* @param {Mixed} reduceFn.previous The previous value.
* @param {Mixed} reduceFn.value The current value.
* @param {Number} reduceFn.index The index in the array of the current `value`.
* @param {Array} reduceFn.array The array to being processed.
* @param {Mixed} [initialValue] The starting value.
* @return {Mixed} The reduced value.
* @method reduce
* @since 6.0.0
*/
reduce: Array.prototype.reduce ? function(array, reduceFn, initialValue) {
if (arguments.length === 3) {
return Array.prototype.reduce.call(array, reduceFn, initialValue);
}
return Array.prototype.reduce.call(array, reduceFn);
} : function(array, reduceFn, initialValue) {
array = Object(array);
if (!Ext.isFunction(reduceFn)) {
Ext.raise('Invalid parameter: expected a function.');
}
var index = 0,
length = array.length >>> 0,
reduced = initialValue;
if (arguments.length < 3) {
while (true) {
if (index in array) {
reduced = array[index++];
break;
}
if (++index >= length) {
throw new TypeError('Reduce of empty array with no initial value');
}
}
}
for (; index < length; ++index) {
if (index in array) {
reduced = reduceFn(reduced, array[index], index, array);
}
}
return reduced;
},
/**
* Returns a shallow copy of a part of an array. This is equivalent to the native
* call `Array.prototype.slice.call(array, begin, end)`. This is often used when "array"
* is "arguments" since the arguments object does not supply a slice method but can
* be the context object to `Array.prototype.slice`.
*
* @param {Array} array The array (or arguments object).
* @param {Number} begin The index at which to begin. Negative values are offsets from
* the end of the array.
* @param {Number} end The index at which to end. The copied items do not include
* end. Negative values are offsets from the end of the array. If end is omitted,
* all items up to the end of the array are copied.
* @return {Array} The copied piece of the array.
* @method slice
*/
// Note: IE8 will return [] on slice.call(x, undefined).
slice: ([
1,
2
].slice(1, undefined).length ? function(array, begin, end) {
return slice.call(array, begin, end);
} : function(array, begin, end) {
// see http://jsperf.com/slice-fix
if (typeof begin === 'undefined') {
return slice.call(array);
}
if (typeof end === 'undefined') {
return slice.call(array, begin);
}
return slice.call(array, begin, end);
}),
/**
* Sorts the elements of an Array in a stable manner (equivalently keyed values do not move relative to each other).
* By default, this method sorts the elements alphabetically and ascending.
* **Note:** This method modifies the passed array, in the same manner as the
* native javascript Array.sort.
*
* @param {Array} array The array to sort.
* @param {Function} [sortFn] The comparison function.
* @param {Mixed} sortFn.a The first item to compare.
* @param {Mixed} sortFn.b The second item to compare.
* @param {Number} sortFn.return `-1` if a < b, `1` if a > b, otherwise `0`.
* @return {Array} The sorted array.
*/
sort: function(array, sortFn) {
return stableSort(array, sortFn || ExtArray.lexicalCompare);
},
/**
* Recursively flattens into 1-d Array. Injects Arrays inline.
*
* @param {Array} array The array to flatten
* @return {Array} The 1-d array.
*/
flatten: function(array) {
var worker = [];
function rFlatten(a) {
var i, ln, v;
for (i = 0 , ln = a.length; i < ln; i++) {
v = a[i];
if (Ext.isArray(v)) {
rFlatten(v);
} else {
worker.push(v);
}
}
return worker;
}
return rFlatten(array);
},
/**
* Returns the minimum value in the Array.
*
* @param {Array/NodeList} array The Array from which to select the minimum value.
* @param {Function} comparisonFn (optional) a function to perform the comparison which determines minimization.
* If omitted the "<" operator will be used.
* __Note:__ gt = 1; eq = 0; lt = -1
* @param {Mixed} comparisonFn.min Current minimum value.
* @param {Mixed} comparisonFn.item The value to compare with the current minimum.
* @return {Object} minValue The minimum value.
*/
min: function(array, comparisonFn) {
var min = array[0],
i, ln, item;
for (i = 0 , ln = array.length; i < ln; i++) {
item = array[i];
if (comparisonFn) {
if (comparisonFn(min, item) === 1) {
min = item;
}
} else {
if (item < min) {
min = item;
}
}
}
return min;
},
/**
* Returns the maximum value in the Array.
*
* @param {Array/NodeList} array The Array from which to select the maximum value.
* @param {Function} comparisonFn (optional) a function to perform the comparison which determines maximization.
* If omitted the ">" operator will be used.
* __Note:__ gt = 1; eq = 0; lt = -1
* @param {Mixed} comparisonFn.max Current maximum value.
* @param {Mixed} comparisonFn.item The value to compare with the current maximum.
* @return {Object} maxValue The maximum value.
*/
max: function(array, comparisonFn) {
var max = array[0],
i, ln, item;
for (i = 0 , ln = array.length; i < ln; i++) {
item = array[i];
if (comparisonFn) {
if (comparisonFn(max, item) === -1) {
max = item;
}
} else {
if (item > max) {
max = item;
}
}
}
return max;
},
/**
* Calculates the mean of all items in the array.
*
* @param {Array} array The Array to calculate the mean value of.
* @return {Number} The mean.
*/
mean: function(array) {
return array.length > 0 ? ExtArray.sum(array) / array.length : undefined;
},
/**
* Calculates the sum of all items in the given array.
*
* @param {Array} array The Array to calculate the sum value of.
* @return {Number} The sum.
*/
sum: function(array) {
var sum = 0,
i, ln, item;
for (i = 0 , ln = array.length; i < ln; i++) {
item = array[i];
sum += item;
}
return sum;
},
/**
* Creates a map (object) keyed by the elements of the given array. The values in
* the map are the index+1 of the array element. For example:
*
* var map = Ext.Array.toMap(['a','b','c']);
*
* // map = { a: 1, b: 2, c: 3 };
*
* Or a key property can be specified:
*
* var map = Ext.Array.toMap([
* { name: 'a' },
* { name: 'b' },
* { name: 'c' }
* ], 'name');
*
* // map = { a: 1, b: 2, c: 3 };
*
* Lastly, a key extractor can be provided:
*
* var map = Ext.Array.toMap([
* { name: 'a' },
* { name: 'b' },
* { name: 'c' }
* ], function (obj) { return obj.name.toUpperCase(); });
*
* // map = { A: 1, B: 2, C: 3 };
*
* @param {Array} array The Array to create the map from.
* @param {String/Function} [getKey] Name of the object property to use
* as a key or a function to extract the key.
* @param {Object} [scope] Value of `this` inside callback specified for `getKey`.
* @return {Object} The resulting map.
*/
toMap: function(array, getKey, scope) {
var map = {},
i = array.length;
if (!getKey) {
while (i--) {
map[array[i]] = i + 1;
}
} else if (typeof getKey === 'string') {
while (i--) {
map[array[i][getKey]] = i + 1;
}
} else {
while (i--) {
map[getKey.call(scope, array[i])] = i + 1;
}
}
return map;
},
/**
* Creates a map (object) keyed by a property of elements of the given array. The values in
* the map are the array element. For example:
*
* var map = Ext.Array.toValueMap(['a','b','c']);
*
* // map = { a: 'a', b: 'b', c: 'c' };
*
* Or a key property can be specified:
*
* var map = Ext.Array.toValueMap([
* { name: 'a' },
* { name: 'b' },
* { name: 'c' }
* ], 'name');
*
* // map = { a: {name: 'a'}, b: {name: 'b'}, c: {name: 'c'} };
*
* Lastly, a key extractor can be provided:
*
* var map = Ext.Array.toValueMap([
* { name: 'a' },
* { name: 'b' },
* { name: 'c' }
* ], function (obj) { return obj.name.toUpperCase(); });
*
* // map = { A: {name: 'a'}, B: {name: 'b'}, C: {name: 'c'} };
*
* @param {Array} array The Array to create the map from.
* @param {String/Function} [getKey] Name of the object property to use
* as a key or a function to extract the key.
* @param {Object} [scope] Value of this inside callback. This parameter is only
* passed when `getKey` is a function. If `getKey` is not a function, the 3rd
* argument is `arrayify`.
* @param {Number} [arrayify] Pass `1` to create arrays for all map entries
* or `2` to create arrays for map entries that have 2 or more items with the
* same key. This only applies when `getKey` is specified. By default the map will
* hold the last entry with a given key.
* @return {Object} The resulting map.
*/
toValueMap: function(array, getKey, scope, arrayify) {
var map = {},
i = array.length,
autoArray, alwaysArray, entry, fn, key, value;
if (!getKey) {
while (i--) {
value = array[i];
map[value] = value;
}
} else {
if (!(fn = (typeof getKey !== 'string'))) {
arrayify = scope;
}
alwaysArray = arrayify === 1;
autoArray = arrayify === 2;
while (i--) {
value = array[i];
key = fn ? getKey.call(scope, value) : value[getKey];
if (alwaysArray) {
if (key in map) {
map[key].push(value);
} else {
map[key] = [
value
];
}
} else if (autoArray && (key in map)) {
if ((entry = map[key]) instanceof Array) {
entry.push(value);
} else {
map[key] = [
entry,
value
];
}
} else {
map[key] = value;
}
}
}
return map;
},
_replaceSim: replaceSim,
// for unit testing
_spliceSim: spliceSim,
/**
* Removes items from an array. This is functionally equivalent to the splice method
* of Array, but works around bugs in IE8's splice method and does not copy the
* removed elements in order to return them (because very often they are ignored).
*
* @param {Array} array The Array on which to replace.
* @param {Number} index The index in the array at which to operate.
* @param {Number} removeCount The number of items to remove at index.
* @return {Array} The array passed.
* @method
*/
erase: erase,
/**
* Inserts items in to an array.
*
* @param {Array} array The Array in which to insert.
* @param {Number} index The index in the array at which to operate.
* @param {Array} items The array of items to insert at index.
* @return {Array} The array passed.
*/
insert: function(array, index, items) {
return replace(array, index, 0, items);
},
move: function(array, fromIdx, toIdx) {
if (toIdx === fromIdx) {
return;
}
var item = array[fromIdx],
incr = toIdx > fromIdx ? 1 : -1,
i;
for (i = fromIdx; i != toIdx; i += incr) {
array[i] = array[i + incr];
}
array[toIdx] = item;
},
/**
* Replaces items in an array. This is functionally equivalent to the splice method
* of Array, but works around bugs in IE8's splice method and is often more convenient
* to call because it accepts an array of items to insert rather than use a variadic
* argument list.
*
* @param {Array} array The Array on which to replace.
* @param {Number} index The index in the array at which to operate.
* @param {Number} removeCount The number of items to remove at index (can be 0).
* @param {Array} insert (optional) An array of items to insert at index.
* @return {Array} The array passed.
* @method
*/
replace: replace,
/**
* Replaces items in an array. This is equivalent to the splice method of Array, but
* works around bugs in IE8's splice method. The signature is exactly the same as the
* splice method except that the array is the first argument. All arguments following
* removeCount are inserted in the array at index.
*
* @param {Array} array The Array on which to replace.
* @param {Number} index The index in the array at which to operate.
* @param {Number} removeCount The number of items to remove at index (can be 0).
* @param {Object...} elements The elements to add to the array. If you don't specify
* any elements, splice simply removes elements from the array.
* @return {Array} An array containing the removed items.
* @method
*/
splice: splice,
/**
* Pushes new items onto the end of an Array.
*
* Passed parameters may be single items, or arrays of items. If an Array is found in the argument list, all its
* elements are pushed into the end of the target Array.
*
* @param {Array} target The Array onto which to push new items
* @param {Object...} elements The elements to add to the array. Each parameter may
* be an Array, in which case all the elements of that Array will be pushed into the end of the
* destination Array.
* @return {Array} An array containing all the new items push onto the end.
*
*/
push: function(target) {
var len = arguments.length,
i = 1,
newItem;
if (target === undefined) {
target = [];
} else if (!Ext.isArray(target)) {
target = [
target
];
}
for (; i < len; i++) {
newItem = arguments[i];
Array.prototype.push[Ext.isIterable(newItem) ? 'apply' : 'call'](target, newItem);
}
return target;
},
/**
* A function used to sort an array by numeric value. By default, javascript array values
* are coerced to strings when sorting, which can be problematic when using numeric values. To
* ensure that the values are sorted numerically, this method can be passed to the sort method:
*
* Ext.Array.sort(myArray, Ext.Array.numericSortFn);
*/
numericSortFn: function(a, b) {
return a - b;
}
};
/**
* @method each
* @member Ext
* @inheritdoc Ext.Array#each
*/
Ext.each = ExtArray.each;
/**
* @method union
* @member Ext.Array
* @inheritdoc Ext.Array#merge
*/
ExtArray.union = ExtArray.merge;
/**
* Old alias to {@link Ext.Array#min}
* @deprecated 4.0.0 Use {@link Ext.Array#min} instead
* @method min
* @member Ext
* @inheritdoc Ext.Array#min
*/
Ext.min = ExtArray.min;
/**
* Old alias to {@link Ext.Array#max}
* @deprecated 4.0.0 Use {@link Ext.Array#max} instead
* @method max
* @member Ext
* @inheritdoc Ext.Array#max
*/
Ext.max = ExtArray.max;
/**
* Old alias to {@link Ext.Array#sum}
* @deprecated 4.0.0 Use {@link Ext.Array#sum} instead
* @method sum
* @member Ext
* @inheritdoc Ext.Array#sum
*/
Ext.sum = ExtArray.sum;
/**
* Old alias to {@link Ext.Array#mean}
* @deprecated 4.0.0 Use {@link Ext.Array#mean} instead
* @method mean
* @member Ext
* @inheritdoc Ext.Array#mean
*/
Ext.mean = ExtArray.mean;
/**
* Old alias to {@link Ext.Array#flatten}
* @deprecated 4.0.0 Use {@link Ext.Array#flatten} instead
* @method flatten
* @member Ext
* @inheritdoc Ext.Array#flatten
*/
Ext.flatten = ExtArray.flatten;
/**
* Old alias to {@link Ext.Array#clean}
* @deprecated 4.0.0 Use {@link Ext.Array#clean} instead
* @method clean
* @member Ext
* @inheritdoc Ext.Array#clean
*/
Ext.clean = ExtArray.clean;
/**
* Old alias to {@link Ext.Array#unique}
* @deprecated 4.0.0 Use {@link Ext.Array#unique} instead
* @method unique
* @member Ext
* @inheritdoc Ext.Array#unique
*/
Ext.unique = ExtArray.unique;
/**
* Old alias to {@link Ext.Array#pluck Ext.Array.pluck}
* @deprecated 4.0.0 Use {@link Ext.Array#pluck Ext.Array.pluck} instead
* @method pluck
* @member Ext
* @inheritdoc Ext.Array#pluck
*/
Ext.pluck = ExtArray.pluck;
/**
* @method toArray
* @member Ext
* @inheritdoc Ext.Array#toArray
*/
Ext.toArray = function() {
return ExtArray.toArray.apply(ExtArray, arguments);
};
return ExtArray;
}());
// @define Ext.lang.Assert
// @define Ext.Assert
// @require Ext.lang.Error
/**
* @class Ext.Assert
* This class provides help value testing methods useful for diagnostics. These are often
* used in `debugHooks`:
*
* Ext.define('Foo.bar.Class', {
*
* debugHooks: {
* method: function (a) {
* Ext.Assert.truthy(a, 'Expected "a" to be truthy');
* },
*
* foo: function (object) {
* Ext.Assert.isFunctionProp(object, 'doSomething');
* }
* }
* });
*
* **NOTE:** This class is entirely removed in production builds so all uses of it should
* be either in `debug` conditional comments or `debugHooks`.
*
* The following type detection methods from the `Ext` object are wrapped as assertions
* by this class:
*
* * `isEmpty`
* * `isArray`
* * `isDate`
* * `isObject`
* * `isSimpleObject`
* * `isPrimitive`
* * `isFunction`
* * `isNumber`
* * `isNumeric`
* * `isString`
* * `isBoolean`
* * `isElement`
* * `isTextNode`
* * `isDefined`
* * `isIterable`
*
* These appear both their exact name and with a "Prop" suffix for checking a property on
* an object. For example, these are almost identical:
*
* Ext.Assert.isFunction(object.foo);
*
* Ext.Assert.isFunctionProp(object, 'foo');
*
* The difference is the default error message generated is better in the second use case
* than the first.
*
* The above list are also expanded for "Not" flavors (and "Not...Prop"):
*
* * `isNotEmpty`
* * `isNotArray`
* * `isNotDate`
* * `isNotObject`
* * `isNotSimpleObject`
* * `isNotPrimitive`
* * `isNotFunction`
* * `isNotNumber`
* * `isNotNumeric`
* * `isNotString`
* * `isNotBoolean`
* * `isNotElement`
* * `isNotTextNode`
* * `isNotDefined`
* * `isNotIterable`
*/
Ext.Assert = {
/**
* Checks that the first argument is falsey and throws an `Error` if it is not.
*/
falsey: function(b, msg) {
if (b) {
Ext.raise(msg || ('Expected a falsey value but was ' + b));
}
},
/**
* Checks that the first argument is falsey and throws an `Error` if it is not.
*/
falseyProp: function(object, property) {
Ext.Assert.truthy(object);
var b = object[property];
if (b) {
if (object.$className) {
property = object.$className + '#' + property;
}
Ext.raise('Expected a falsey value for ' + property + ' but was ' + b);
}
},
/**
* Checks that the first argument is truthy and throws an `Error` if it is not.
*/
truthy: function(b, msg) {
if (!b) {
Ext.raise(msg || ('Expected a truthy value but was ' + typeof b));
}
},
/**
* Checks that the first argument is truthy and throws an `Error` if it is not.
*/
truthyProp: function(object, property) {
Ext.Assert.truthy(object);
var b = object[property];
if (!b) {
if (object.$className) {
property = object.$className + '#' + property;
}
Ext.raise('Expected a truthy value for ' + property + ' but was ' + typeof b);
}
}
};
(function() {
function makeAssert(name, kind) {
var testFn = Ext[name],
def;
return function(value, msg) {
if (!testFn(value)) {
Ext.raise(msg || def || (def = 'Expected value to be ' + kind));
}
};
}
function makeAssertProp(name, kind) {
var testFn = Ext[name],
def;
return function(object, prop) {
Ext.Assert.truthy(object);
if (!testFn(object[prop])) {
Ext.raise(def || (def = 'Expected ' + (object.$className ? object.$className + '#' : '') + prop + ' to be ' + kind));
}
};
}
function makeNotAssert(name, kind) {
var testFn = Ext[name],
def;
return function(value, msg) {
if (testFn(value)) {
Ext.raise(msg || def || (def = 'Expected value to NOT be ' + kind));
}
};
}
function makeNotAssertProp(name, kind) {
var testFn = Ext[name],
def;
return function(object, prop) {
Ext.Assert.truthy(object);
if (testFn(object[prop])) {
Ext.raise(def || (def = 'Expected ' + (object.$className ? object.$className + '#' : '') + prop + ' to NOT be ' + kind));
}
};
}
for (var name in Ext) {
if (name.substring(0, 2) == "is" && Ext.isFunction(Ext[name])) {
var kind = name.substring(2);
Ext.Assert[name] = makeAssert(name, kind);
Ext.Assert[name + 'Prop'] = makeAssertProp(name, kind);
Ext.Assert['isNot' + kind] = makeNotAssert(name, kind);
Ext.Assert['isNot' + kind + 'Prop'] = makeNotAssertProp(name, kind);
}
}
}());
/**
* @class Ext.String
*
* A collection of useful static methods to deal with strings.
* @singleton
*/
Ext.String = (function() {
// @define Ext.lang.String
// @define Ext.String
// @require Ext
// @require Ext.lang.Array
var trimRegex = /^[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+|[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+$/g,
escapeRe = /('|\\)/g,
escapeRegexRe = /([-.*+?\^${}()|\[\]\/\\])/g,
basicTrimRe = /^\s+|\s+$/g,
whitespaceRe = /\s+/,
varReplace = /(^[^a-z]*|[^\w])/gi,
charToEntity, entityToChar, charToEntityRegex, entityToCharRegex,
htmlEncodeReplaceFn = function(match, capture) {
return charToEntity[capture];
},
htmlDecodeReplaceFn = function(match, capture) {
return (capture in entityToChar) ? entityToChar[capture] : String.fromCharCode(parseInt(capture.substr(2), 10));
},
boundsCheck = function(s, other) {
if (s === null || s === undefined || other === null || other === undefined) {
return false;
}
return other.length <= s.length;
},
fromCharCode = String.fromCharCode,
ExtString;
return ExtString = {
/**
* Creates a string created by using the specified sequence of code points.
* @param {Number...} codePoint Codepoints from which to build the string.
* @return {String} A string built from the sequence of code points passed.
*/
fromCodePoint: String.fromCodePoint || function() {
var codePoint,
result = '',
codeUnits = [],
index = -1,
length = arguments.length;
while (++index < length) {
codePoint = Number(arguments[index]);
if (!isFinite(codePoint) || // `NaN`, `+Infinity`, or `-Infinity`
codePoint < 0 || // not a valid Unicode code point
codePoint > 1114111 || // not a valid Unicode code point
Math.floor(codePoint) !== codePoint) // not an integer
{
Ext.raise('Invalid code point: ' + codePoint);
}
if (codePoint <= 65535) {
// BMP code point
codeUnits.push(codePoint);
} else {
// Astral code point; split in surrogate halves
// http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
codePoint -= 65536;
codeUnits.push((codePoint >> 10) + 55296, (codePoint % 1024) + 56320);
}
if (index + 1 === length) {
result += fromCharCode(codeUnits);
codeUnits.length = 0;
}
}
return result;
},
/**
* Inserts a substring into a string.
* @param {String} s The original string.
* @param {String} value The substring to insert.
* @param {Number} index The index to insert the substring. Negative indexes will insert from the end of
* the string. Example:
*
* Ext.String.insert("abcdefg", "h", -1); // abcdefhg
*
* @return {String} The value with the inserted substring
*/
insert: function(s, value, index) {
if (!s) {
return value;
}
if (!value) {
return s;
}
var len = s.length;
if (!index && index !== 0) {
index = len;
}
if (index < 0) {
index *= -1;
if (index >= len) {
// negative overflow, insert at start
index = 0;
} else {
index = len - index;
}
}
if (index === 0) {
s = value + s;
} else if (index >= s.length) {
s += value;
} else {
s = s.substr(0, index) + value + s.substr(index);
}
return s;
},
/**
* Checks if a string starts with a substring
* @param {String} s The original string
* @param {String} start The substring to check
* @param {Boolean} [ignoreCase=false] True to ignore the case in the comparison
*/
startsWith: function(s, start, ignoreCase) {
var result = boundsCheck(s, start);
if (result) {
if (ignoreCase) {
s = s.toLowerCase();
start = start.toLowerCase();
}
result = s.lastIndexOf(start, 0) === 0;
}
return result;
},
/**
* Checks if a string ends with a substring
* @param {String} s The original string
* @param {String} end The substring to check
* @param {Boolean} [ignoreCase=false] True to ignore the case in the comparison
*/
endsWith: function(s, end, ignoreCase) {
var result = boundsCheck(s, end);
if (result) {
if (ignoreCase) {
s = s.toLowerCase();
end = end.toLowerCase();
}
result = s.indexOf(end, s.length - end.length) !== -1;
}
return result;
},
/**
* Converts a string of characters into a legal, parse-able JavaScript `var` name as long as the passed
* string contains at least one alphabetic character. Non alphanumeric characters, and *leading* non alphabetic
* characters will be removed.
* @param {String} s A string to be converted into a `var` name.
* @return {String} A legal JavaScript `var` name.
*/
createVarName: function(s) {
return s.replace(varReplace, '');
},
/**
* Convert certain characters (&, <, >, ', and ") to their HTML character equivalents for literal display in web pages.
* @param {String} value The string to encode.
* @return {String} The encoded text.
* @method
*/
htmlEncode: function(value) {
return (!value) ? value : String(value).replace(charToEntityRegex, htmlEncodeReplaceFn);
},
/**
* Convert certain characters (&, <, >, ', and ") from their HTML character equivalents.
* @param {String} value The string to decode.
* @return {String} The decoded text.
* @method
*/
htmlDecode: function(value) {
return (!value) ? value : String(value).replace(entityToCharRegex, htmlDecodeReplaceFn);
},
/**
* Checks if a string has values needing to be html encoded.
* @private
* @param {String} s The string to test
* @return {Boolean} `true` if the string contains HTML characters
*/
hasHtmlCharacters: function(s) {
return charToEntityRegex.test(s);
},
/**
* Adds a set of character entity definitions to the set used by
* {@link Ext.String#htmlEncode} and {@link Ext.String#htmlDecode}.
*
* This object should be keyed by the entity name sequence,
* with the value being the textual representation of the entity.
*
* Ext.String.addCharacterEntities({
* '&amp;Uuml;':'Ü',
* '&amp;ccedil;':'ç',
* '&amp;ntilde;':'ñ',
* '&amp;egrave;':'è'
* });
* var s = Ext.String.htmlEncode("A string with entities: èÜçñ");
*
* __Note:__ the values of the character entities defined on this object are expected
* to be single character values. As such, the actual values represented by the
* characters are sensitive to the character encoding of the JavaScript source
* file when defined in string literal form. Script tags referencing server
* resources with character entities must ensure that the 'charset' attribute
* of the script node is consistent with the actual character encoding of the
* server resource.
*
* The set of character entities may be reset back to the default state by using
* the {@link Ext.String#resetCharacterEntities} method
*
* @param {Object} newEntities The set of character entities to add to the current
* definitions.
*/
addCharacterEntities: function(newEntities) {
var charKeys = [],
entityKeys = [],
key, echar;
for (key in newEntities) {
echar = newEntities[key];
entityToChar[key] = echar;
charToEntity[echar] = key;
charKeys.push(echar);
entityKeys.push(key);
}
charToEntityRegex = new RegExp('(' + charKeys.join('|') + ')', 'g');
entityToCharRegex = new RegExp('(' + entityKeys.join('|') + '|&#[0-9]{1,5};' + ')', 'g');
},
/**
* Resets the set of character entity definitions used by
* {@link Ext.String#htmlEncode} and {@link Ext.String#htmlDecode} back to the
* default state.
*/
resetCharacterEntities: function() {
charToEntity = {};
entityToChar = {};
// add the default set
this.addCharacterEntities({
'&amp;': '&',
'&gt;': '>',
'&lt;': '<',
'&quot;': '"',
'&#39;': "'"
});
},
/**
* Appends content to the query string of a URL, handling logic for whether to place
* a question mark or ampersand.
* @param {String} url The URL to append to.
* @param {String} string The content to append to the URL.
* @return {String} The resulting URL
*/
urlAppend: function(url, string) {
if (!Ext.isEmpty(string)) {
return url + (url.indexOf('?') === -1 ? '?' : '&') + string;
}
return url;
},
/**
* Trims whitespace from either end of a string, leaving spaces within the string intact. Example:
*
* var s = ' foo bar ';
* alert('-' + s + '-'); //alerts "- foo bar -"
* alert('-' + Ext.String.trim(s) + '-'); //alerts "-foo bar-"
*
* @param {String} string The string to trim.
* @return {String} The trimmed string.
*/
trim: function(string) {
if (string) {
string = string.replace(trimRegex, "");
}
return string || '';
},
/**
* Capitalize the first letter of the given string.
* @param {String} string
* @return {String}
*/
capitalize: function(string) {
if (string) {
string = string.charAt(0).toUpperCase() + string.substr(1);
}
return string || '';
},
/**
* Uncapitalize the first letter of a given string.
* @param {String} string
* @return {String}
*/
uncapitalize: function(string) {
if (string) {
string = string.charAt(0).toLowerCase() + string.substr(1);
}
return string || '';
},
/**
* Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length.
* @param {String} value The string to truncate.
* @param {Number} length The maximum length to allow before truncating.
* @param {Boolean} [word=false] `true` to try to find a common word break.
* @return {String} The converted text.
*/
ellipsis: function(value, length, word) {
if (value && value.length > length) {
if (word) {
var vs = value.substr(0, length - 2),
index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
if (index !== -1 && index >= (length - 15)) {
return vs.substr(0, index) + "...";
}
}
return value.substr(0, length - 3) + "...";
}
return value;
},
/**
* Escapes the passed string for use in a regular expression.
* @param {String} string The string to escape.
* @return {String} The escaped string.
*/
escapeRegex: function(string) {
return string.replace(escapeRegexRe, "\\$1");
},
/**
* Creates a `RegExp` given a string `value` and optional flags. For example, the
* following two regular expressions are equivalent.
*
* var regex1 = Ext.String.createRegex('hello');
*
* var regex2 = /^hello$/i;
*
* The following two regular expressions are also equivalent:
*
* var regex1 = Ext.String.createRegex('world', false, false, false);
*
* var regex2 = /world/;
*
* @param {String/RegExp} value The String to convert to a `RegExp`.
* @param {Boolean} [startsWith=true] Pass `false` to allow a match to start
* anywhere in the string. By default the `value` will match only at the start
* of the string.
* @param {Boolean} [endsWith=true] Pass `false` to allow the match to end before
* the end of the string. By default the `value` will match only at the end of the
* string.
* @param {Boolean} [ignoreCase=true] Pass `false` to make the `RegExp` case
* sensitive (removes the 'i' flag).
* @since 5.0.0
* @return {RegExp}
*/
createRegex: function(value, startsWith, endsWith, ignoreCase) {
var ret = value;
if (value != null && !value.exec) {
// not a regex
ret = ExtString.escapeRegex(String(value));
if (startsWith !== false) {
ret = '^' + ret;
}
if (endsWith !== false) {
ret += '$';
}
ret = new RegExp(ret, (ignoreCase !== false) ? 'i' : '');
}
return ret;
},
/**
* Escapes the passed string for ' and \.
* @param {String} string The string to escape.
* @return {String} The escaped string.
*/
escape: function(string) {
return string.replace(escapeRe, "\\$1");
},
/**
* Utility function that allows you to easily switch a string between two alternating values. The passed value
* is compared to the current string, and if they are equal, the other value that was passed in is returned. If
* they are already different, the first value passed in is returned. Note that this method returns the new value
* but does not change the current string.
*
* // alternate sort directions
* sort = Ext.String.toggle(sort, 'ASC', 'DESC');
*
* // instead of conditional logic:
* sort = (sort === 'ASC' ? 'DESC' : 'ASC');
*
* @param {String} string The current string.
* @param {String} value The value to compare to the current string.
* @param {String} other The new value to use if the string already equals the first value passed in.
* @return {String} The new value.
*/
toggle: function(string, value, other) {
return string === value ? other : value;
},
/**
* Pads the left side of a string with a specified character. This is especially useful
* for normalizing number and date strings. Example usage:
*
* var s = Ext.String.leftPad('123', 5, '0');
* // s now contains the string: '00123'
*
* @param {String} string The original string.
* @param {Number} size The total length of the output string.
* @param {String} [character=' '] (optional) The character with which to pad the original string.
* @return {String} The padded string.
*/
leftPad: function(string, size, character) {
var result = String(string);
character = character || " ";
while (result.length < size) {
result = character + result;
}
return result;
},
/**
* Returns a string with a specified number of repetitions a given string pattern.
* The pattern be separated by a different string.
*
* var s = Ext.String.repeat('---', 4); // = '------------'
* var t = Ext.String.repeat('--', 3, '/'); // = '--/--/--'
*
* @param {String} pattern The pattern to repeat.
* @param {Number} count The number of times to repeat the pattern (may be 0).
* @param {String} sep An option string to separate each pattern.
*/
repeat: function(pattern, count, sep) {
if (count < 1) {
count = 0;
}
for (var buf = [],
i = count; i--; ) {
buf.push(pattern);
}
return buf.join(sep || '');
},
/**
* Splits a string of space separated words into an array, trimming as needed. If the
* words are already an array, it is returned.
*
* @param {String/Array} words
*/
splitWords: function(words) {
if (words && typeof words == 'string') {
return words.replace(basicTrimRe, '').split(whitespaceRe);
}
return words || [];
}
};
}());
// initialize the default encode / decode entities
Ext.String.resetCharacterEntities();
/**
* Old alias to {@link Ext.String#htmlEncode}
* @deprecated Use {@link Ext.String#htmlEncode} instead
* @method htmlEncode
* @member Ext
* @inheritdoc Ext.String#htmlEncode
*/
Ext.htmlEncode = Ext.String.htmlEncode;
/**
* Old alias to {@link Ext.String#htmlDecode}
* @deprecated Use {@link Ext.String#htmlDecode} instead
* @method htmlDecode
* @member Ext
* @inheritdoc Ext.String#htmlDecode
*/
Ext.htmlDecode = Ext.String.htmlDecode;
/**
* Old alias to {@link Ext.String#urlAppend}
* @deprecated Use {@link Ext.String#urlAppend} instead
* @method urlAppend
* @member Ext
* @inheritdoc Ext.String#urlAppend
*/
Ext.urlAppend = Ext.String.urlAppend;
/**
* @class Ext.Date
* This class defines some basic methods for handling dates.
*
* The date parsing and formatting syntax contains a subset of
* [PHP's `date()` function](http://www.php.net/date), and the formats that are
* supported will provide results equivalent to their PHP versions.
*
* The following is a list of all currently supported formats:
*
* Format Description Example returned values
* ------ ----------------------------------------------------------------------- -----------------------
* d Day of the month, 2 digits with leading zeros 01 to 31
* D A short textual representation of the day of the week Mon to Sun
* j Day of the month without leading zeros 1 to 31
* l A full textual representation of the day of the week Sunday to Saturday
* N ISO-8601 numeric representation of the day of the week 1 (for Monday) through 7 (for Sunday)
* S English ordinal suffix for the day of the month, 2 characters st, nd, rd or th. Works well with j
* w Numeric representation of the day of the week 0 (for Sunday) to 6 (for Saturday)
* z The day of the year (starting from 0) 0 to 364 (365 in leap years)
* W ISO-8601 week number of year, weeks starting on Monday 01 to 53
* F A full textual representation of a month, such as January or March January to December
* m Numeric representation of a month, with leading zeros 01 to 12
* M A short textual representation of a month Jan to Dec
* n Numeric representation of a month, without leading zeros 1 to 12
* t Number of days in the given month 28 to 31
* L Whether it&#39;s a leap year 1 if it is a leap year, 0 otherwise.
* o ISO-8601 year number (identical to (Y), but if the ISO week number (W) Examples: 1998 or 2004
* belongs to the previous or next year, that year is used instead)
* Y A full numeric representation of a year, 4 digits Examples: 1999 or 2003
* y A two digit representation of a year Examples: 99 or 03
* a Lowercase Ante meridiem and Post meridiem am or pm
* A Uppercase Ante meridiem and Post meridiem AM or PM
* g 12-hour format of an hour without leading zeros 1 to 12
* G 24-hour format of an hour without leading zeros 0 to 23
* h 12-hour format of an hour with leading zeros 01 to 12
* H 24-hour format of an hour with leading zeros 00 to 23
* i Minutes, with leading zeros 00 to 59
* s Seconds, with leading zeros 00 to 59
* u Decimal fraction of a second Examples:
* (minimum 1 digit, arbitrary number of digits allowed) 001 (i.e. 0.001s) or
* 100 (i.e. 0.100s) or
* 999 (i.e. 0.999s) or
* 999876543210 (i.e. 0.999876543210s)
* O Difference to Greenwich time (GMT) in hours and minutes Example: +1030
* P Difference to Greenwich time (GMT) with colon between hours and minutes Example: -08:00
* T Timezone abbreviation of the machine running the code Examples: EST, MDT, PDT ...
* Z Timezone offset in seconds (negative if west of UTC, positive if east) -43200 to 50400
* c ISO 8601 date represented as the local time with an offset to UTC appended.
* Notes: Examples:
* 1) If unspecified, the month / day defaults to the current month / day, 1991 or
* the time defaults to midnight, while the timezone defaults to the 1992-10 or
* browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
* and minutes. The "T" delimiter, seconds, milliseconds and timezone 1994-08-19T16:20+01:00 or
* are optional. 1995-07-18T17:21:28-02:00 or
* 2) The decimal fraction of a second, if specified, must contain at 1996-06-17T18:22:29.98765+03:00 or
* least 1 digit (there is no limit to the maximum number 1997-05-16T19:23:30,12345-0400 or
* of digits allowed), and may be delimited by either a '.' or a ',' 1998-04-15T20:24:31.2468Z or
* Refer to the examples on the right for the various levels of 1999-03-14T20:24:32Z or
* date-time granularity which are supported, or see 2000-02-13T21:25:33
* http://www.w3.org/TR/NOTE-datetime for more info. 2001-01-12 22:26:34
* C An ISO date string as implemented by the native Date object's 1962-06-17T09:21:34.125Z
* [Date.toISOString](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
* method. This outputs the numeric part with *UTC* hour and minute
* values, and indicates this by appending the `'Z'` timezone
* identifier.
* U Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) 1193432466 or -2138434463
* MS Microsoft AJAX serialized dates \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
* \/Date(1238606590509+0800)\/
* time A javascript millisecond timestamp 1350024476440
* timestamp A UNIX timestamp (same as U) 1350024866
*
* Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
*
* // Sample date:
* // 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
*
* var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
* console.log(Ext.Date.format(dt, 'Y-m-d')); // 2007-01-10
* console.log(Ext.Date.format(dt, 'F j, Y, g:i a')); // January 10, 2007, 3:05 pm
* console.log(Ext.Date.format(dt, 'l, \\t\\he jS \\of F Y h:i:s A')); // Wednesday, the 10th of January 2007 03:05:01 PM
*
* Here are some standard date/time patterns that you might find helpful. They
* are not part of the source of Ext.Date, but to use them you can simply copy this
* block of code into any script that is included after Ext.Date and they will also become
* globally available on the Date object. Feel free to add or remove patterns as needed in your code.
*
* Ext.Date.patterns = {
* ISO8601Long:"Y-m-d H:i:s",
* ISO8601Short:"Y-m-d",
* ShortDate: "n/j/Y",
* LongDate: "l, F d, Y",
* FullDateTime: "l, F d, Y g:i:s A",
* MonthDay: "F d",
* ShortTime: "g:i A",
* LongTime: "g:i:s A",
* SortableDateTime: "Y-m-d\\TH:i:s",
* UniversalSortableDateTime: "Y-m-d H:i:sO",
* YearMonth: "F, Y"
* };
*
* Example usage:
*
* var dt = new Date();
* console.log(Ext.Date.format(dt, Ext.Date.patterns.ShortDate));
*
* Developer-written, custom formats may be used by supplying both a formatting and a parsing function
* which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}.
* @singleton
*/
Ext.Date = (function() {
// @define Ext.lang.Date
// @define Ext.Date
// @require Ext
// @require Ext.lang.String
var utilDate,
nativeDate = Date,
stripEscapeRe = /(\\.)/g,
hourInfoRe = /([gGhHisucUOPZ]|MS)/,
dateInfoRe = /([djzmnYycU]|MS)/,
slashRe = /\\/gi,
numberTokenRe = /\{(\d+)\}/g,
MSFormatRe = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/'),
pad = Ext.String.leftPad,
// Most of the date-formatting functions below are the excellent work of Baron Schwartz.
// (see http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/)
// They generate precompiled functions from format patterns instead of parsing and
// processing each pattern every time a date is formatted.
code = [
// date calculations (note: the code below creates a dependency on Ext.Number.from())
"var me = this, dt, y, m, d, h, i, s, ms, o, O, z, zz, u, v, W, year, jan4, week1monday, daysInMonth, dayMatched,",
"def = me.defaults,",
"from = Ext.Number.from,",
"results = String(input).match(me.parseRegexes[{0}]);",
// either null, or an array of matched strings
"if(results){",
"{1}",
"if(u != null){",
// i.e. unix time is defined
"v = new Date(u * 1000);",
// give top priority to UNIX time
"}else{",
// create Date object representing midnight of the current day;
// this will provide us with our date defaults
// (note: clearTime() handles Daylight Saving Time automatically)
"dt = me.clearTime(new Date);",
"y = from(y, from(def.y, dt.getFullYear()));",
"m = from(m, from(def.m - 1, dt.getMonth()));",
"dayMatched = d !== undefined;",
"d = from(d, from(def.d, dt.getDate()));",
// Attempt to validate the day. Since it defaults to today, it may go out
// of range, for example parsing m/Y where the value is 02/2000 on the 31st of May.
// It will attempt to parse 2000/02/31, which will overflow to March and end up
// returning 03/2000. We only do this when we default the day. If an invalid day value
// was set to be parsed by the user, continue on and either let it overflow or return null
// depending on the strict value. This will be in line with the normal Date behaviour.
"if (!dayMatched) {",
"dt.setDate(1);",
"dt.setMonth(m);",
"dt.setFullYear(y);",
"daysInMonth = me.getDaysInMonth(dt);",
"if (d > daysInMonth) {",
"d = daysInMonth;",
"}",
"}",
"h = from(h, from(def.h, dt.getHours()));",
"i = from(i, from(def.i, dt.getMinutes()));",
"s = from(s, from(def.s, dt.getSeconds()));",
"ms = from(ms, from(def.ms, dt.getMilliseconds()));",
"if(z >= 0 && y >= 0){",
// both the year and zero-based day of year are defined and >= 0.
// these 2 values alone provide sufficient info to create a full date object
// create Date object representing January 1st for the given year
// handle years < 100 appropriately
"v = me.add(new Date(y < 100 ? 100 : y, 0, 1, h, i, s, ms), me.YEAR, y < 100 ? y - 100 : 0);",
// then add day of year, checking for Date "rollover" if necessary
"v = !strict? v : (strict === true && (z <= 364 || (me.isLeapYear(v) && z <= 365))? me.add(v, me.DAY, z) : null);",
"}else if(strict === true && !me.isValid(y, m + 1, d, h, i, s, ms)){",
// check for Date "rollover"
"v = null;",
// invalid date, so return null
"}else{",
"if (W) {",
// support ISO-8601
// http://en.wikipedia.org/wiki/ISO_week_date
//
// Mutually equivalent definitions for week 01 are:
// a. the week starting with the Monday which is nearest in time to 1 January
// b. the week with 4 January in it
// ... there are many others ...
//
// We'll use letter b above to determine the first week of the year.
//
// So, first get a Date object for January 4th of whatever calendar year is desired.
//
// Then, the first Monday of the year can easily be determined by (operating on this Date):
// 1. Getting the day of the week.
// 2. Subtracting that by one.
// 3. Multiplying that by 86400000 (one day in ms).
// 4. Subtracting this number of days (in ms) from the January 4 date (represented in ms).
//
// Example #1 ...
//
// January 2012
// Su Mo Tu We Th Fr Sa
// 1 2 3 4 5 6 7
// 8 9 10 11 12 13 14
// 15 16 17 18 19 20 21
// 22 23 24 25 26 27 28
// 29 30 31
//
// 1. January 4th is a Wednesday.
// 2. Its day number is 3.
// 3. Simply substract 2 days from Wednesday.
// 4. The first week of the year begins on Monday, January 2. Simple!
//
// Example #2 ...
// January 1992
// Su Mo Tu We Th Fr Sa
// 1 2 3 4
// 5 6 7 8 9 10 11
// 12 13 14 15 16 17 18
// 19 20 21 22 23 24 25
// 26 27 28 29 30 31
//
// 1. January 4th is a Saturday.
// 2. Its day number is 6.
// 3. Simply subtract 5 days from Saturday.
// 4. The first week of the year begins on Monday, December 30. Simple!
//
// v = Ext.Date.clearTime(new Date(week1monday.getTime() + ((W - 1) * 604800000 + 43200000)));
// (This is essentially doing the same thing as above but for the week rather than the day)
"year = y || (new Date()).getFullYear();",
"jan4 = new Date(year, 0, 4, 0, 0, 0);",
"d = jan4.getDay();",
// If the 1st is a Thursday, then the 4th will be a Sunday, so we need the appropriate
// day number here, which is why we use the day === checks.
"week1monday = new Date(jan4.getTime() - ((d === 0 ? 6 : d - 1) * 86400000));",
// The reason for adding 43200000 (12 hours) is to avoid any complication with daylight saving
// switch overs. For example, if the clock is rolled back, an hour will repeat, so adding 7 days
// will leave us 1 hour short (Sun <date> 23:00:00). By setting is to 12:00, subtraction
// or addition of an hour won't make any difference.
"v = Ext.Date.clearTime(new Date(week1monday.getTime() + ((W - 1) * 604800000 + 43200000)));",
"} else {",
// plain old Date object
// handle years < 100 properly
"v = me.add(new Date(y < 100 ? 100 : y, m, d, h, i, s, ms), me.YEAR, y < 100 ? y - 100 : 0);",
"}",
"}",
"}",
"}",
"if(v){",
// favor UTC offset over GMT offset
"if(zz != null){",
// reset to UTC, then add offset
"v = me.add(v, me.SECOND, -v.getTimezoneOffset() * 60 - zz);",
"}else if(o){",
// reset to GMT, then add offset
"v = me.add(v, me.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
"}",
"}",
"return (v != null) ? v : null;"
].join('\n');
// Polyfill Date's toISOString instance method where not implemented.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString
// TODO: Remove this when IE8 retires.
if (!Date.prototype.toISOString) {
Date.prototype.toISOString = function() {
var me = this;
return pad(me.getUTCFullYear(), 4, '0') + '-' + pad(me.getUTCMonth() + 1, 2, '0') + '-' + pad(me.getUTCDate(), 2, '0') + 'T' + pad(me.getUTCHours(), 2, '0') + ':' + pad(me.getUTCMinutes(), 2, '0') + ':' + pad(me.getUTCSeconds(), 2, '0') + '.' + pad(me.getUTCMilliseconds(), 3, '0') + 'Z';
};
}
/**
* @method xf
* @private
* @param format
* Create private copy of Ext JS's `Ext.util.Format.format()` method
* + to remove unnecessary dependency
* + to resolve namespace conflict with MS-Ajax's implementation
*/
function xf(format) {
var args = Array.prototype.slice.call(arguments, 1);
return format.replace(numberTokenRe, function(m, i) {
return args[i];
});
}
utilDate = {
/** @ignore */
now: nativeDate.now,
// always available due to polyfill in Ext.js
/**
* @private
*/
toString: function(date) {
if (!date) {
date = new nativeDate();
}
return date.getFullYear() + "-" + pad(date.getMonth() + 1, 2, '0') + "-" + pad(date.getDate(), 2, '0') + "T" + pad(date.getHours(), 2, '0') + ":" + pad(date.getMinutes(), 2, '0') + ":" + pad(date.getSeconds(), 2, '0');
},
/**
* Returns the number of milliseconds between two dates.
* @param {Date} dateA The first date.
* @param {Date} [dateB=new Date()] (optional) The second date.
* @return {Number} The difference in milliseconds
*/
getElapsed: function(dateA, dateB) {
return Math.abs(dateA - (dateB || utilDate.now()));
},
/**
* Global flag which determines if strict date parsing should be used.
* Strict date parsing will not roll-over invalid dates, which is the
* default behavior of JavaScript Date objects.
* (see {@link #parse} for more information)
* @type Boolean
*/
useStrict: false,
/**
* @private
*/
formatCodeToRegex: function(character, currentGroup) {
// Note: currentGroup - position in regex result array (see notes for Ext.Date.parseCodes below)
var p = utilDate.parseCodes[character];
if (p) {
p = typeof p === 'function' ? p() : p;
utilDate.parseCodes[character] = p;
}
// reassign function result to prevent repeated execution
return p ? Ext.applyIf({
c: p.c ? xf(p.c, currentGroup || "{0}") : p.c
}, p) : {
g: 0,
c: null,
s: Ext.String.escapeRegex(character)
};
},
// treat unrecognized characters as literals
/**
* An object hash in which each property is a date parsing function. The property name is the
* format string which that function parses.
*
* This object is automatically populated with date parsing functions as
* date formats are requested for Ext standard formatting strings.
*
* Custom parsing functions may be inserted into this object, keyed by a name which from then on
* may be used as a format string to {@link #parse}.
*
* Example:
*
* Ext.Date.parseFunctions['x-date-format'] = myDateParser;
*
* A parsing function should return a Date object, and is passed the following parameters:
*
* - `date`: {@link String} - The date string to parse.
* - `strict`: {@link Boolean} - `true` to validate date strings while parsing
* (i.e. prevent JavaScript Date "rollover"). __The default must be `false`.__
* Invalid date strings should return `null` when parsed.
*
* To enable Dates to also be _formatted_ according to that format, a corresponding
* formatting function must be placed into the {@link #formatFunctions} property.
* @property parseFunctions
* @type Object
*/
parseFunctions: {
"MS": function(input, strict) {
// note: the timezone offset is ignored since the MS Ajax server sends
// a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
var r = (input || '').match(MSFormatRe);
return r ? new nativeDate(((r[1] || '') + r[2]) * 1) : null;
},
"time": function(input, strict) {
var num = parseInt(input, 10);
if (num || num === 0) {
return new nativeDate(num);
}
return null;
},
"timestamp": function(input, strict) {
var num = parseInt(input, 10);
if (num || num === 0) {
return new nativeDate(num * 1000);
}
return null;
}
},
parseRegexes: [],
/**
* An object hash in which each property is a date formatting function. The property name is the
* format string which corresponds to the produced formatted date string.
*
* This object is automatically populated with date formatting functions as
* date formats are requested for Ext standard formatting strings.
*
* Custom formatting functions may be inserted into this object, keyed by a name which from then on
* may be used as a format string to {@link #format}.
*
* Example:
*
* Ext.Date.formatFunctions['x-date-format'] = myDateFormatter;
*
* A formatting function should return a string representation of the Date object which
* is the scope (this) of the function.
*
* To enable date strings to also be _parsed_ according to that format, a corresponding
* parsing function must be placed into the {@link #parseFunctions} property.
* @property formatFunctions
* @type Object
*/
formatFunctions: {
"MS": function() {
// UTC milliseconds since Unix epoch (MS-AJAX serialized date format (MRSF))
return '\\/Date(' + this.getTime() + ')\\/';
},
"time": function() {
return this.getTime().toString();
},
"timestamp": function() {
return utilDate.format(this, 'U');
}
},
y2kYear: 50,
/**
* Date interval constant.
* @type String
*/
MILLI: "ms",
/**
* Date interval constant.
* @type String
*/
SECOND: "s",
/**
* Date interval constant.
* @type String
*/
MINUTE: "mi",
/** Date interval constant.
* @type String
*/
HOUR: "h",
/**
* Date interval constant.
* @type String
*/
DAY: "d",
/**
* Date interval constant.
* @type String
*/
MONTH: "mo",
/**
* Date interval constant.
* @type String
*/
YEAR: "y",
/**
* The number of days in a week.
* @type Number
*/
DAYS_IN_WEEK: 7,
/**
* The number of months in a year.
* @type Number
*/
MONTHS_IN_YEAR: 12,
/**
* The maximum number of days in a month.
* @type {Number}
*/
MAX_DAYS_IN_MONTH: 31,
SUNDAY: 0,
MONDAY: 1,
TUESDAY: 2,
WEDNESDAY: 3,
THURSDAY: 4,
FRIDAY: 5,
SATURDAY: 6,
/**
* An object hash containing default date values used during date parsing.
*
* The following properties are available:
*
* - `y`: {@link Number} - The default year value. Defaults to `undefined`.
* - `m`: {@link Number} - The default 1-based month value. Defaults to `undefined`.
* - `d`: {@link Number} - The default day value. Defaults to `undefined`.
* - `h`: {@link Number} - The default hour value. Defaults to `undefined`.
* - `i`: {@link Number} - The default minute value. Defaults to `undefined`.
* - `s`: {@link Number} - The default second value. Defaults to `undefined`.
* - `ms`: {@link Number} - The default millisecond value. Defaults to `undefined`.
*
* Override these properties to customize the default date values used by the {@link #parse} method.
*
* __Note:__ In countries which experience Daylight Saving Time (i.e. DST), the `h`, `i`, `s`
* and `ms` properties may coincide with the exact time in which DST takes effect.
* It is the responsibility of the developer to account for this.
*
* Example Usage:
*
* // set default day value to the first day of the month
* Ext.Date.defaults.d = 1;
*
* // parse a February date string containing only year and month values.
* // setting the default day value to 1 prevents weird date rollover issues
* // when attempting to parse the following date string on, for example, March 31st 2009.
* Ext.Date.parse('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009.
*
* @property defaults
* @type Object
*/
defaults: {},
//<locale type="array">
/**
* @property {String[]} dayNames
* An array of textual day names.
* Override these values for international dates.
*
* Example:
*
* Ext.Date.dayNames = [
* 'SundayInYourLang',
* 'MondayInYourLang'
* // ...
* ];
*/
dayNames: [
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday"
],
//</locale>
//<locale type="array">
/**
* @property {String[]} monthNames
* An array of textual month names.
* Override these values for international dates.
*
* Example:
*
* Ext.Date.monthNames = [
* 'JanInYourLang',
* 'FebInYourLang'
* // ...
* ];
*/
monthNames: [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
],
//</locale>
//<locale type="object">
/**
* @property {Object} monthNumbers
* An object hash of zero-based JavaScript month numbers (with short month names as keys).
*
* __Note:__ keys are case-sensitive.
*
* Override these values for international dates.
*
* Example:
*
* Ext.Date.monthNumbers = {
* 'LongJanNameInYourLang': 0,
* 'ShortJanNameInYourLang':0,
* 'LongFebNameInYourLang':1,
* 'ShortFebNameInYourLang':1
* // ...
* };
*/
monthNumbers: {
January: 0,
Jan: 0,
February: 1,
Feb: 1,
March: 2,
Mar: 2,
April: 3,
Apr: 3,
May: 4,
June: 5,
Jun: 5,
July: 6,
Jul: 6,
August: 7,
Aug: 7,
September: 8,
Sep: 8,
October: 9,
Oct: 9,
November: 10,
Nov: 10,
December: 11,
Dec: 11
},
//</locale>
//<locale>
/**
* @property {String} defaultFormat
* The date format string that the {@link Ext.util.Format#dateRenderer}
* and {@link Ext.util.Format#date} functions use. See {@link Ext.Date} for details.
*
* This may be overridden in a locale file.
*/
defaultFormat: "m/d/Y",
//</locale>
//<locale>
/**
* @property {Number} firstDayOfWeek
* The day on which the week starts. `0` being Sunday, through `6` being Saturday.
*
* This may be overridden in a locale file.
*/
firstDayOfWeek: 0,
//</locale>
//<locale>
/**
* @property {Number[]} weekendDays
* The days on which weekend falls. `0` being Sunday, through `6` being Saturday.
*
* This may be overridden in a locale file.
*/
weekendDays: [
0,
6
],
//</locale>
//<locale type="function">
/**
* Get the short month name for the given month number.
* Override this function for international dates.
* @param {Number} month A zero-based JavaScript month number.
* @return {String} The short month name.
*/
getShortMonthName: function(month) {
return utilDate.monthNames[month].substring(0, 3);
},
//</locale>
//<locale type="function">
/**
* Get the short day name for the given day number.
* Override this function for international dates.
* @param {Number} day A zero-based JavaScript day number.
* @return {String} The short day name.
*/
getShortDayName: function(day) {
return utilDate.dayNames[day].substring(0, 3);
},
//</locale>
//<locale type="function">
/**
* Get the zero-based JavaScript month number for the given short/full month name.
* Override this function for international dates.
* @param {String} name The short/full month name.
* @return {Number} The zero-based JavaScript month number.
*/
getMonthNumber: function(name) {
// handle camel casing for English month names (since the keys for the Ext.Date.monthNumbers hash are case sensitive)
return utilDate.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
},
//</locale>
/**
* Checks if the specified format contains hour information
* @param {String} format The format to check
* @return {Boolean} True if the format contains hour information
* @method
*/
formatContainsHourInfo: function(format) {
return hourInfoRe.test(format.replace(stripEscapeRe, ''));
},
/**
* Checks if the specified format contains information about
* anything other than the time.
* @param {String} format The format to check
* @return {Boolean} True if the format contains information about
* date/day information.
* @method
*/
formatContainsDateInfo: function(format) {
return dateInfoRe.test(format.replace(stripEscapeRe, ''));
},
/**
* Removes all escaping for a date format string. In date formats,
* using a '\' can be used to escape special characters.
* @param {String} format The format to unescape
* @return {String} The unescaped format
* @method
*/
unescapeFormat: function(format) {
// Escape the format, since \ can be used to escape special
// characters in a date format. For example, in a Spanish
// locale the format may be: 'd \\de F \\de Y'
return format.replace(slashRe, '');
},
/**
* The base format-code to formatting-function hashmap used by the {@link #format} method.
* Formatting functions are strings (or functions which return strings) which
* will return the appropriate value when evaluated in the context of the Date object
* from which the {@link #format} method is called.
* Add to / override these mappings for custom date formatting.
*
* __Note:__ `Ext.Date.format()` treats characters as literals if an appropriate mapping cannot be found.
*
* Example:
*
* Ext.Date.formatCodes.x = "Ext.util.Format.leftPad(this.getDate(), 2, '0')";
* console.log(Ext.Date.format(new Date(), 'X'); // returns the current day of the month
* @type Object
*/
formatCodes: {
d: "Ext.String.leftPad(m.getDate(), 2, '0')",
D: "Ext.Date.getShortDayName(m.getDay())",
// get localized short day name
j: "m.getDate()",
l: "Ext.Date.dayNames[m.getDay()]",
N: "(m.getDay() ? m.getDay() : 7)",
S: "Ext.Date.getSuffix(m)",
w: "m.getDay()",
z: "Ext.Date.getDayOfYear(m)",
W: "Ext.String.leftPad(Ext.Date.getWeekOfYear(m), 2, '0')",
F: "Ext.Date.monthNames[m.getMonth()]",
m: "Ext.String.leftPad(m.getMonth() + 1, 2, '0')",
M: "Ext.Date.getShortMonthName(m.getMonth())",
// get localized short month name
n: "(m.getMonth() + 1)",
t: "Ext.Date.getDaysInMonth(m)",
L: "(Ext.Date.isLeapYear(m) ? 1 : 0)",
o: "(m.getFullYear() + (Ext.Date.getWeekOfYear(m) == 1 && m.getMonth() > 0 ? +1 : (Ext.Date.getWeekOfYear(m) >= 52 && m.getMonth() < 11 ? -1 : 0)))",
Y: "Ext.String.leftPad(m.getFullYear(), 4, '0')",
y: "('' + m.getFullYear()).substring(2, 4)",
a: "(m.getHours() < 12 ? 'am' : 'pm')",
A: "(m.getHours() < 12 ? 'AM' : 'PM')",
g: "((m.getHours() % 12) ? m.getHours() % 12 : 12)",
G: "m.getHours()",
h: "Ext.String.leftPad((m.getHours() % 12) ? m.getHours() % 12 : 12, 2, '0')",
H: "Ext.String.leftPad(m.getHours(), 2, '0')",
i: "Ext.String.leftPad(m.getMinutes(), 2, '0')",
s: "Ext.String.leftPad(m.getSeconds(), 2, '0')",
u: "Ext.String.leftPad(m.getMilliseconds(), 3, '0')",
O: "Ext.Date.getGMTOffset(m)",
P: "Ext.Date.getGMTOffset(m, true)",
T: "Ext.Date.getTimezone(m)",
Z: "(m.getTimezoneOffset() * -60)",
c: function() {
// ISO-8601 -- GMT format
var c = "Y-m-dTH:i:sP",
code = [],
i,
l = c.length,
e;
for (i = 0; i < l; ++i) {
e = c.charAt(i);
code.push(e === "T" ? "'T'" : utilDate.getFormatCode(e));
}
// treat T as a character literal
return code.join(" + ");
},
C: function() {
// ISO-1601 -- browser format. UTC numerics with the 'Z' TZ id.
return 'm.toISOString()';
},
U: "Math.round(m.getTime() / 1000)"
},
/**
* Checks if the passed Date parameters will cause a JavaScript Date "rollover".
* @param {Number} year 4-digit year.
* @param {Number} month 1-based month-of-year.
* @param {Number} day Day of month.
* @param {Number} hour (optional) Hour.
* @param {Number} minute (optional) Minute.
* @param {Number} second (optional) Second.
* @param {Number} millisecond (optional) Millisecond.
* @return {Boolean} `true` if the passed parameters do not cause a Date "rollover", `false` otherwise.
*/
isValid: function(year, month, day, hour, minute, second, millisecond) {
// setup defaults
hour = hour || 0;
minute = minute || 0;
second = second || 0;
millisecond = millisecond || 0;
// Special handling for year < 100
var dt = utilDate.add(new nativeDate(year < 100 ? 100 : year, month - 1, day, hour, minute, second, millisecond), utilDate.YEAR, year < 100 ? year - 100 : 0);
return year === dt.getFullYear() && month === dt.getMonth() + 1 && day === dt.getDate() && hour === dt.getHours() && minute === dt.getMinutes() && second === dt.getSeconds() && millisecond === dt.getMilliseconds();
},
/**
* Parses the passed string using the specified date format.
* Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
* The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
* which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
* the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
* Keep in mind that the input date string must precisely match the specified format string
* in order for the parse operation to be successful (failed parse operations return a
* `null` value).
*
* Example:
*
* //dt = Fri May 25 2007 (current date)
* var dt = new Date();
*
* //dt = Thu May 25 2006 (today&#39;s month/day in 2006)
* dt = Ext.Date.parse("2006", "Y");
*
* //dt = Sun Jan 15 2006 (all date parts specified)
* dt = Ext.Date.parse("2006-01-15", "Y-m-d");
*
* //dt = Sun Jan 15 2006 15:20:01
* dt = Ext.Date.parse("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
*
* // attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
* dt = Ext.Date.parse("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
*
* @param {String} input The raw date string.
* @param {String} format The expected date string format.
* @param {Boolean} [strict=false] (optional) `true` to validate date strings while parsing (i.e. prevents JavaScript Date "rollover").
* Invalid date strings will return `null` when parsed.
* @return {Date/null} The parsed Date, or `null` if an invalid date string.
*/
parse: function(input, format, strict) {
var p = utilDate.parseFunctions;
if (p[format] == null) {
utilDate.createParser(format);
}
return p[format].call(utilDate, input, Ext.isDefined(strict) ? strict : utilDate.useStrict);
},
// Backwards compat
parseDate: function(input, format, strict) {
return utilDate.parse(input, format, strict);
},
/**
* @private
*/
getFormatCode: function(character) {
var f = utilDate.formatCodes[character];
if (f) {
f = typeof f === 'function' ? f() : f;
utilDate.formatCodes[character] = f;
}
// reassign function result to prevent repeated execution
// note: unknown characters are treated as literals
return f || ("'" + Ext.String.escape(character) + "'");
},
/**
* @private
*/
createFormat: function(format) {
var code = [],
special = false,
ch = '',
i;
for (i = 0; i < format.length; ++i) {
ch = format.charAt(i);
if (!special && ch === "\\") {
special = true;
} else if (special) {
special = false;
code.push("'" + Ext.String.escape(ch) + "'");
} else {
if (ch === '\n') {
code.push("'\\n'");
} else {
code.push(utilDate.getFormatCode(ch));
}
}
}
utilDate.formatFunctions[format] = Ext.functionFactory("var m=this;return " + code.join('+'));
},
/**
* @private
*/
createParser: function(format) {
var regexNum = utilDate.parseRegexes.length,
currentGroup = 1,
calc = [],
regex = [],
special = false,
ch = "",
i = 0,
len = format.length,
atEnd = [],
obj;
for (; i < len; ++i) {
ch = format.charAt(i);
if (!special && ch === "\\") {
special = true;
} else if (special) {
special = false;
regex.push(Ext.String.escape(ch));
} else {
obj = utilDate.formatCodeToRegex(ch, currentGroup);
currentGroup += obj.g;
regex.push(obj.s);
if (obj.g && obj.c) {
if (obj.calcAtEnd) {
atEnd.push(obj.c);
} else {
calc.push(obj.c);
}
}
}
}
calc = calc.concat(atEnd);
utilDate.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", 'i');
utilDate.parseFunctions[format] = Ext.functionFactory("input", "strict", xf(code, regexNum, calc.join('')));
},
/**
* @private
*/
parseCodes: {
/**
* Notes:
* g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
* c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
* s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
*/
d: {
g: 1,
c: "d = parseInt(results[{0}], 10);\n",
s: "(3[0-1]|[1-2][0-9]|0[1-9])"
},
// day of month with leading zeroes (01 - 31)
j: {
g: 1,
c: "d = parseInt(results[{0}], 10);\n",
s: "(3[0-1]|[1-2][0-9]|[1-9])"
},
// day of month without leading zeroes (1 - 31)
D: function() {
for (var a = [],
i = 0; i < 7; a.push(utilDate.getShortDayName(i)) , ++i){}
// get localised short day names
return {
g: 0,
c: null,
s: "(?:" + a.join("|") + ")"
};
},
l: function() {
return {
g: 0,
c: null,
s: "(?:" + utilDate.dayNames.join("|") + ")"
};
},
N: {
g: 0,
c: null,
s: "[1-7]"
},
// ISO-8601 day number (1 (monday) - 7 (sunday))
//<locale type="object" property="parseCodes">
S: {
g: 0,
c: null,
s: "(?:st|nd|rd|th)"
},
//</locale>
w: {
g: 0,
c: null,
s: "[0-6]"
},
// JavaScript day number (0 (sunday) - 6 (saturday))
z: {
g: 1,
c: "z = parseInt(results[{0}], 10);\n",
s: "(\\d{1,3})"
},
// day of the year (0 - 364 (365 in leap years))
W: {
g: 1,
c: "W = parseInt(results[{0}], 10);\n",
s: "(\\d{2})"
},
// ISO-8601 week number (with leading zero)
F: function() {
return {
g: 1,
c: "m = parseInt(me.getMonthNumber(results[{0}]), 10);\n",
// get localised month number
s: "(" + utilDate.monthNames.join("|") + ")"
};
},
M: function() {
for (var a = [],
i = 0; i < 12; a.push(utilDate.getShortMonthName(i)) , ++i){}
// get localised short month names
return Ext.applyIf({
s: "(" + a.join("|") + ")"
}, utilDate.formatCodeToRegex("F"));
},
m: {
g: 1,
c: "m = parseInt(results[{0}], 10) - 1;\n",
s: "(1[0-2]|0[1-9])"
},
// month number with leading zeros (01 - 12)
n: {
g: 1,
c: "m = parseInt(results[{0}], 10) - 1;\n",
s: "(1[0-2]|[1-9])"
},
// month number without leading zeros (1 - 12)
t: {
g: 0,
c: null,
s: "(?:\\d{2})"
},
// no. of days in the month (28 - 31)
L: {
g: 0,
c: null,
s: "(?:1|0)"
},
o: {
g: 1,
c: "y = parseInt(results[{0}], 10);\n",
s: "(\\d{4})"
},
// ISO-8601 year number (with leading zero)
Y: {
g: 1,
c: "y = parseInt(results[{0}], 10);\n",
s: "(\\d{4})"
},
// 4-digit year
y: {
g: 1,
c: "var ty = parseInt(results[{0}], 10);\n" + "y = ty > me.y2kYear ? 1900 + ty : 2000 + ty;\n",
// 2-digit year
s: "(\\d{2})"
},
/**
* In the am/pm parsing routines, we allow both upper and lower case
* even though it doesn't exactly match the spec. It gives much more flexibility
* in being able to specify case insensitive regexes.
*/
//<locale type="object" property="parseCodes">
a: {
g: 1,
c: "if (/(am)/i.test(results[{0}])) {\n" + "if (!h || h == 12) { h = 0; }\n" + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
s: "(am|pm|AM|PM)",
calcAtEnd: true
},
//</locale>
//<locale type="object" property="parseCodes">
A: {
g: 1,
c: "if (/(am)/i.test(results[{0}])) {\n" + "if (!h || h == 12) { h = 0; }\n" + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
s: "(AM|PM|am|pm)",
calcAtEnd: true
},
//</locale>
g: {
g: 1,
c: "h = parseInt(results[{0}], 10);\n",
s: "(1[0-2]|[0-9])"
},
// 12-hr format of an hour without leading zeroes (1 - 12)
G: {
g: 1,
c: "h = parseInt(results[{0}], 10);\n",
s: "(2[0-3]|1[0-9]|[0-9])"
},
// 24-hr format of an hour without leading zeroes (0 - 23)
h: {
g: 1,
c: "h = parseInt(results[{0}], 10);\n",
s: "(1[0-2]|0[1-9])"
},
// 12-hr format of an hour with leading zeroes (01 - 12)
H: {
g: 1,
c: "h = parseInt(results[{0}], 10);\n",
s: "(2[0-3]|[0-1][0-9])"
},
// 24-hr format of an hour with leading zeroes (00 - 23)
i: {
g: 1,
c: "i = parseInt(results[{0}], 10);\n",
s: "([0-5][0-9])"
},
// minutes with leading zeros (00 - 59)
s: {
g: 1,
c: "s = parseInt(results[{0}], 10);\n",
s: "([0-5][0-9])"
},
// seconds with leading zeros (00 - 59)
u: {
g: 1,
c: "ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
s: "(\\d+)"
},
// decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
O: {
g: 1,
c: [
"o = results[{0}];",
"var sn = o.substring(0,1),",
// get + / - sign
"hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),",
// get hours (performs minutes-to-hour conversion also, just in case)
"mn = o.substring(3,5) % 60;",
// get minutes
"o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + Ext.String.leftPad(hr, 2, '0') + Ext.String.leftPad(mn, 2, '0')) : null;\n"
].// -12hrs <= GMT offset <= 14hrs
join("\n"),
s: "([+-]\\d{4})"
},
// GMT offset in hrs and mins
P: {
g: 1,
c: [
"o = results[{0}];",
"var sn = o.substring(0,1),",
// get + / - sign
"hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),",
// get hours (performs minutes-to-hour conversion also, just in case)
"mn = o.substring(4,6) % 60;",
// get minutes
"o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + Ext.String.leftPad(hr, 2, '0') + Ext.String.leftPad(mn, 2, '0')) : null;\n"
].// -12hrs <= GMT offset <= 14hrs
join("\n"),
s: "([+-]\\d{2}:\\d{2})"
},
// GMT offset in hrs and mins (with colon separator)
T: {
g: 0,
c: null,
s: "[A-Z]{1,5}"
},
// timezone abbrev. may be between 1 - 5 chars
Z: {
g: 1,
c: "zz = results[{0}] * 1;\n" + // -43200 <= UTC offset <= 50400
"zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
s: "([+-]?\\d{1,5})"
},
// leading '+' sign is optional for UTC offset
c: function() {
var calc = [],
arr = [
utilDate.formatCodeToRegex("Y", 1),
// year
utilDate.formatCodeToRegex("m", 2),
// month
utilDate.formatCodeToRegex("d", 3),
// day
utilDate.formatCodeToRegex("H", 4),
// hour
utilDate.formatCodeToRegex("i", 5),
// minute
utilDate.formatCodeToRegex("s", 6),
// second
{
c: "ms = results[7] || '0'; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n"
},
// decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
{
c: [
// allow either "Z" (i.e. UTC) or "-0530" or "+08:00" (i.e. UTC offset) timezone delimiters. assumes local timezone if no timezone is specified
"if(results[8]) {",
// timezone specified
"if(results[8] == 'Z'){",
"zz = 0;",
// UTC
"}else if (results[8].indexOf(':') > -1){",
utilDate.formatCodeToRegex("P", 8).c,
// timezone offset with colon separator
"}else{",
utilDate.formatCodeToRegex("O", 8).c,
// timezone offset without colon separator
"}",
"}"
].join('\n')
}
],
i, l;
for (i = 0 , l = arr.length; i < l; ++i) {
calc.push(arr[i].c);
}
return {
g: 1,
c: calc.join(""),
s: [
arr[0].s,
// year (required)
"(?:",
"-",
arr[1].s,
// month (optional)
"(?:",
"-",
arr[2].s,
// day (optional)
"(?:",
"(?:T| )?",
// time delimiter -- either a "T" or a single blank space
arr[3].s,
":",
arr[4].s,
// hour AND minute, delimited by a single colon (optional). MUST be preceded by either a "T" or a single blank space
"(?::",
arr[5].s,
")?",
// seconds (optional)
"(?:(?:\\.|,)(\\d+))?",
// decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
"(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?",
// "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
")?",
")?",
")?"
].join("")
};
},
U: {
g: 1,
c: "u = parseInt(results[{0}], 10);\n",
s: "(-?\\d+)"
}
},
// leading minus sign indicates seconds before UNIX epoch
//Old Ext.Date prototype methods.
/**
* @private
*/
dateFormat: function(date, format) {
return utilDate.format(date, format);
},
/**
* Compares if two dates are equal by comparing their values.
* @param {Date} date1
* @param {Date} date2
* @return {Boolean} `true` if the date values are equal
*/
isEqual: function(date1, date2) {
// check we have 2 date objects
if (date1 && date2) {
return (date1.getTime() === date2.getTime());
}
// one or both isn't a date, only equal if both are falsey
return !(date1 || date2);
},
/**
* Formats a date given the supplied format string.
* @param {Date} date The date to format
* @param {String} format The format string
* @return {String} The formatted date or an empty string if date parameter is not a JavaScript Date object
*/
format: function(date, format) {
var formatFunctions = utilDate.formatFunctions;
if (!Ext.isDate(date)) {
return '';
}
if (formatFunctions[format] == null) {
utilDate.createFormat(format);
}
return formatFunctions[format].call(date) + '';
},
/**
* Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
*
* __Note:__ The date string returned by the JavaScript Date object's `toString()` method varies
* between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
* For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
* `getTimezone()` first tries to get the timezone abbreviation from between a pair of parentheses
* (which may or may not be present), failing which it proceeds to get the timezone abbreviation
* from the GMT offset portion of the date string.
*
* @example
* var dt = new Date('9/17/2011');
* console.log(Ext.Date.getTimezone(dt));
*
* @param {Date} date The date
* @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
*/
getTimezone: function(date) {
// the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
//
// Opera : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
// Safari : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone (same as FF)
// FF : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
// IE : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
// IE : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
//
// this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
// step 1: (?:\((.*)\) -- find timezone in parentheses
// step 2: ([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?) -- if nothing was found in step 1, find timezone from timezone offset portion of date string
// step 3: remove all non uppercase characters found in step 1 and 2
return date.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,5})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
},
/**
* Get the offset from GMT of the current date (equivalent to the format specifier 'O').
*
* @example
* var dt = new Date('9/17/2011');
* console.log(Ext.Date.getGMTOffset(dt));
*
* @param {Date} date The date
* @param {Boolean} [colon=false] `true` to separate the hours and minutes with a colon.
* @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
*/
getGMTOffset: function(date, colon) {
var offset = date.getTimezoneOffset();
return (offset > 0 ? "-" : "+") + Ext.String.leftPad(Math.floor(Math.abs(offset) / 60), 2, "0") + (colon ? ":" : "") + Ext.String.leftPad(Math.abs(offset % 60), 2, "0");
},
/**
* Get the numeric day number of the year, adjusted for leap year.
*
* @example
* var dt = new Date('9/17/2011');
* console.log(Ext.Date.getDayOfYear(dt)); // 259
*
* @param {Date} date The date
* @return {Number} 0 to 364 (365 in leap years).
*/
getDayOfYear: function(date) {
var num = 0,
d = utilDate.clone(date),
m = date.getMonth(),
i;
for (i = 0 , d.setDate(1) , d.setMonth(0); i < m; d.setMonth(++i)) {
num += utilDate.getDaysInMonth(d);
}
return num + date.getDate() - 1;
},
/**
* Get the numeric ISO-8601 week number of the year.
* (equivalent to the format specifier 'W', but without a leading zero).
*
* @example
* var dt = new Date('9/17/2011');
* console.log(Ext.Date.getWeekOfYear(dt)); // 37
*
* @param {Date} date The date.
* @return {Number} 1 to 53.
* @method
*/
getWeekOfYear: (function() {
// adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
var ms1d = 86400000,
// milliseconds in a day
ms7d = 7 * ms1d;
// milliseconds in a week
return function(date) {
// return a closure so constants get calculated only once
var DC3 = nativeDate.UTC(date.getFullYear(), date.getMonth(), date.getDate() + 3) / ms1d,
// an Absolute Day Number
AWN = Math.floor(DC3 / 7),
// an Absolute Week Number
Wyr = new nativeDate(AWN * ms7d).getUTCFullYear();
return AWN - Math.floor(nativeDate.UTC(Wyr, 0, 7) / ms7d) + 1;
};
}()),
/**
* Checks if the current date falls within a leap year.
*
* @example
* var dt = new Date('1/10/2011');
* console.log(Ext.Date.isLeapYear(dt)); // false
*
* @param {Date} date The date
* @return {Boolean} `true` if the current date falls within a leap year, `false` otherwise.
*/
isLeapYear: function(date) {
var year = date.getFullYear();
return !!((year & 3) === 0 && (year % 100 || (year % 400 === 0 && year)));
},
/**
* Get the first day of the current month, adjusted for leap year. The returned value
* is the numeric day index within the week (0-6) which can be used in conjunction with
* the {@link #monthNames} array to retrieve the textual day name.
*
* @example
* var dt = new Date('1/10/2007'),
* firstDay = Ext.Date.getFirstDayOfMonth(dt);
*
* console.log(Ext.Date.dayNames[firstDay]); // output: 'Monday'
*
* @param {Date} date The date
* @return {Number} The day number (0-6).
*/
getFirstDayOfMonth: function(date) {
var day = (date.getDay() - (date.getDate() - 1)) % 7;
return (day < 0) ? (day + 7) : day;
},
/**
* Get the last day of the current month, adjusted for leap year. The returned value
* is the numeric day index within the week (0-6) which can be used in conjunction with
* the {@link #monthNames} array to retrieve the textual day name.
*
* @example
* var dt = new Date('1/10/2007'),
* lastDay = Ext.Date.getLastDayOfMonth(dt);
*
* console.log(Ext.Date.dayNames[lastDay]); // output: 'Wednesday'
*
* @param {Date} date The date
* @return {Number} The day number (0-6).
*/
getLastDayOfMonth: function(date) {
return utilDate.getLastDateOfMonth(date).getDay();
},
/**
* Get the date of the first day of the month in which this date resides.
* @param {Date} date The date
* @return {Date}
*/
getFirstDateOfMonth: function(date) {
return new nativeDate(date.getFullYear(), date.getMonth(), 1);
},
/**
* Get the date of the last day of the month in which this date resides.
* @param {Date} date The date
* @return {Date}
*/
getLastDateOfMonth: function(date) {
return new nativeDate(date.getFullYear(), date.getMonth(), utilDate.getDaysInMonth(date));
},
/**
* Get the number of days in the current month, adjusted for leap year.
* @param {Date} date The date
* @return {Number} The number of days in the month.
* @method
*/
getDaysInMonth: (function() {
var daysInMonth = [
31,
28,
31,
30,
31,
30,
31,
31,
30,
31,
30,
31
];
return function(date) {
// return a closure for efficiency
var m = date.getMonth();
return m === 1 && utilDate.isLeapYear(date) ? 29 : daysInMonth[m];
};
}()),
//<locale type="function">
/**
* Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
* @param {Date} date The date
* @return {String} 'st, 'nd', 'rd' or 'th'.
*/
getSuffix: function(date) {
switch (date.getDate()) {
case 1:
case 21:
case 31:
return "st";
case 2:
case 22:
return "nd";
case 3:
case 23:
return "rd";
default:
return "th";
}
},
//</locale>
/**
* Creates and returns a new Date instance with the exact same date value as the called instance.
* Dates are copied and passed by reference, so if a copied date variable is modified later, the original
* variable will also be changed. When the intention is to create a new variable that will not
* modify the original instance, you should create a clone.
*
* Example of correctly cloning a date:
*
* //wrong way:
* var orig = new Date('10/1/2006');
* var copy = orig;
* copy.setDate(5);
* console.log(orig); // returns 'Thu Oct 05 2006'!
*
* //correct way:
* var orig = new Date('10/1/2006'),
* copy = Ext.Date.clone(orig);
* copy.setDate(5);
* console.log(orig); // returns 'Thu Oct 01 2006'
*
* @param {Date} date The date.
* @return {Date} The new Date instance.
*/
clone: function(date) {
return new nativeDate(date.getTime());
},
/**
* Checks if the current date is affected by Daylight Saving Time (DST).
* @param {Date} date The date
* @return {Boolean} `true` if the current date is affected by DST.
*/
isDST: function(date) {
// adapted from http://sencha.com/forum/showthread.php?p=247172#post247172
// courtesy of @geoffrey.mcgill
return new nativeDate(date.getFullYear(), 0, 1).getTimezoneOffset() !== date.getTimezoneOffset();
},
/**
* Attempts to clear all time information from this Date by setting the time to midnight of the same day,
* automatically adjusting for Daylight Saving Time (DST) where applicable.
*
* __Note:__ DST timezone information for the browser's host operating system is assumed to be up-to-date.
* @param {Date} date The date
* @param {Boolean} [clone=false] `true` to create a clone of this date, clear the time and return it.
* @return {Date} this or the clone.
*/
clearTime: function(date, clone) {
// handles invalid dates preventing the browser from crashing.
if (isNaN(date.getTime())) {
return date;
}
if (clone) {
return utilDate.clearTime(utilDate.clone(date));
}
// get current date before clearing time
var d = date.getDate(),
hr, c;
// clear time
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
date.setMilliseconds(0);
if (date.getDate() !== d) {
// account for DST (i.e. day of month changed when setting hour = 0)
// note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
// refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
// increment hour until cloned date == current date
for (hr = 1 , c = utilDate.add(date, utilDate.HOUR, hr); c.getDate() !== d; hr++ , c = utilDate.add(date, utilDate.HOUR, hr)){}
date.setDate(d);
date.setHours(c.getHours());
}
return date;
},
/**
* Provides a convenient method for performing basic date arithmetic. This method
* does not modify the Date instance being called - it creates and returns
* a new Date instance containing the resulting date value.
*
* Examples:
*
* // Basic usage:
* var dt = Ext.Date.add(new Date('10/29/2006'), Ext.Date.DAY, 5);
* console.log(dt); // returns 'Fri Nov 03 2006 00:00:00'
*
* // Negative values will be subtracted:
* var dt2 = Ext.Date.add(new Date('10/1/2006'), Ext.Date.DAY, -5);
* console.log(dt2); // returns 'Tue Sep 26 2006 00:00:00'
*
* // Decimal values can be used:
* var dt3 = Ext.Date.add(new Date('10/1/2006'), Ext.Date.DAY, 1.25);
* console.log(dt3); // returns 'Mon Oct 02 2006 06:00:00'
*
* @param {Date} date The date to modify
* @param {String} interval A valid date interval enum value.
* @param {Number} value The amount to add to the current date.
* @return {Date} The new Date instance.
*/
add: function(date, interval, value) {
var d = utilDate.clone(date),
base = 0,
day, decimalValue;
if (!interval || value === 0) {
return d;
}
decimalValue = value - parseInt(value, 10);
value = parseInt(value, 10);
if (value) {
switch (interval.toLowerCase()) {
// See EXTJSIV-7418. We use setTime() here to deal with issues related to
// the switchover that occurs when changing to daylight savings and vice
// versa. setTime() handles this correctly where setHour/Minute/Second/Millisecond
// do not. Let's assume the DST change occurs at 2am and we're incrementing using add
// for 15 minutes at time. When entering DST, we should see:
// 01:30am
// 01:45am
// 03:00am // skip 2am because the hour does not exist
// ...
// Similarly, leaving DST, we should see:
// 01:30am
// 01:45am
// 01:00am // repeat 1am because that's the change over
// 01:30am
// 01:45am
// 02:00am
// ....
//
case utilDate.MILLI:
d.setTime(d.getTime() + value);
break;
case utilDate.SECOND:
d.setTime(d.getTime() + value * 1000);
break;
case utilDate.MINUTE:
d.setTime(d.getTime() + value * 60 * 1000);
break;
case utilDate.HOUR:
d.setTime(d.getTime() + value * 60 * 60 * 1000);
break;
case utilDate.DAY:
d.setTime(d.getTime() + value * 24 * 60 * 60 * 1000);
break;
case utilDate.MONTH:
day = date.getDate();
if (day > 28) {
day = Math.min(day, utilDate.getLastDateOfMonth(utilDate.add(utilDate.getFirstDateOfMonth(date), utilDate.MONTH, value)).getDate());
};
d.setDate(day);
d.setMonth(date.getMonth() + value);
break;
case utilDate.YEAR:
day = date.getDate();
if (day > 28) {
day = Math.min(day, utilDate.getLastDateOfMonth(utilDate.add(utilDate.getFirstDateOfMonth(date), utilDate.YEAR, value)).getDate());
};
d.setDate(day);
d.setFullYear(date.getFullYear() + value);
break;
}
}
if (decimalValue) {
switch (interval.toLowerCase()) {
case utilDate.MILLI:
base = 1;
break;
case utilDate.SECOND:
base = 1000;
break;
case utilDate.MINUTE:
base = 1000 * 60;
break;
case utilDate.HOUR:
base = 1000 * 60 * 60;
break;
case utilDate.DAY:
base = 1000 * 60 * 60 * 24;
break;
case utilDate.MONTH:
day = utilDate.getDaysInMonth(d);
base = 1000 * 60 * 60 * 24 * day;
break;
case utilDate.YEAR:
day = (utilDate.isLeapYear(d) ? 366 : 365);
base = 1000 * 60 * 60 * 24 * day;
break;
}
if (base) {
d.setTime(d.getTime() + base * decimalValue);
}
}
return d;
},
/**
* Provides a convenient method for performing basic date arithmetic. This method
* does not modify the Date instance being called - it creates and returns
* a new Date instance containing the resulting date value.
*
* Examples:
*
* // Basic usage:
* var dt = Ext.Date.subtract(new Date('10/29/2006'), Ext.Date.DAY, 5);
* console.log(dt); // returns 'Tue Oct 24 2006 00:00:00'
*
* // Negative values will be added:
* var dt2 = Ext.Date.subtract(new Date('10/1/2006'), Ext.Date.DAY, -5);
* console.log(dt2); // returns 'Fri Oct 6 2006 00:00:00'
*
* // Decimal values can be used:
* var dt3 = Ext.Date.subtract(new Date('10/1/2006'), Ext.Date.DAY, 1.25);
* console.log(dt3); // returns 'Fri Sep 29 2006 06:00:00'
*
* @param {Date} date The date to modify
* @param {String} interval A valid date interval enum value.
* @param {Number} value The amount to subtract from the current date.
* @return {Date} The new Date instance.
*/
subtract: function(date, interval, value) {
return utilDate.add(date, interval, -value);
},
/**
* Checks if a date falls on or between the given start and end dates.
* @param {Date} date The date to check
* @param {Date} start Start date
* @param {Date} end End date
* @return {Boolean} `true` if this date falls on or between the given start and end dates.
*/
between: function(date, start, end) {
var t = date.getTime();
return start.getTime() <= t && t <= end.getTime();
},
/**
* Checks if the date is a weekend day. Uses {@link #weekendDays}.
* @param {Date} date The date.
* @return {Boolean} `true` if the day falls on a weekend.
*
* @since 6.2.0
*/
isWeekend: function(date) {
return Ext.Array.indexOf(this.weekendDays, date.getDay()) > -1;
},
/**
* Converts the passed UTC date into a local date.
* For example, if the passed date is:
* `Wed Jun 01 2016 00:10:00 GMT+1000 (AUS Eastern Standard Time)`, then
* the returned date will be `Wed Jun 01 2016 00:00:00 GMT+1000 (AUS Eastern Standard Time)`.
* @param {Date} d The date to convert.
* @return {Date} The date as a local. Does not modify the passed date.
*
* @since 6.2.0
*/
utcToLocal: function(d) {
return new Date(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds(), d.getUTCMilliseconds());
},
/**
* Converts the passed local date into a UTC date.
* For example, if the passed date is:
* `Wed Jun 01 2016 00:00:00 GMT+1000 (AUS Eastern Standard Time)`, then
* the returned date will be `Wed Jun 01 2016 10:00:00 GMT+1000 (AUS Eastern Standard Time)`.
* @param {Date} d The date to convert.
* @return {Date} The date as UTC. Does not modify the passed date.
*
* @since 6.2.0
*/
localToUtc: function(d) {
return utilDate.utc(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds());
},
/**
* Create a UTC date.
* @param {Number} year The year.
* @param {Number} month The month.
* @param {Number} day The day.
* @param {Number} [hour=0] The hour.
* @param {Number} [min=0] The minutes.
* @param {Number} [s=0] The seconds.
* @param {Number} [ms=0] The milliseconds.
* @return {Date} The UTC date.
*
* @since 6.2.0
*/
utc: function(year, month, day, hour, min, s, ms) {
return new Date(Date.UTC(year, month, day, hour || 0, min || 0, s || 0, ms || 0));
},
//Maintains compatibility with old static and prototype window.Date methods.
compat: function() {
var p,
statics = [
'useStrict',
'formatCodeToRegex',
'parseFunctions',
'parseRegexes',
'formatFunctions',
'y2kYear',
'MILLI',
'SECOND',
'MINUTE',
'HOUR',
'DAY',
'MONTH',
'YEAR',
'defaults',
'dayNames',
'monthNames',
'monthNumbers',
'getShortMonthName',
'getShortDayName',
'getMonthNumber',
'formatCodes',
'isValid',
'parseDate',
'getFormatCode',
'createFormat',
'createParser',
'parseCodes'
],
proto = [
'dateFormat',
'format',
'getTimezone',
'getGMTOffset',
'getDayOfYear',
'getWeekOfYear',
'isLeapYear',
'getFirstDayOfMonth',
'getLastDayOfMonth',
'getDaysInMonth',
'getSuffix',
'clone',
'isDST',
'clearTime',
'add',
'between'
],
sLen = statics.length,
pLen = proto.length,
stat, prot, s;
//Append statics
for (s = 0; s < sLen; s++) {
stat = statics[s];
nativeDate[stat] = utilDate[stat];
}
//Append to prototype
for (p = 0; p < pLen; p++) {
prot = proto[p];
nativeDate.prototype[prot] = function() {
var args = Array.prototype.slice.call(arguments);
args.unshift(this);
return utilDate[prot].apply(utilDate, args);
};
}
},
/**
* Calculate how many units are there between two time.
* @param {Date} min The first time.
* @param {Date} max The second time.
* @param {String} unit The unit. This unit is compatible with the date interval constants.
* @return {Number} The maximum number n of units that min + n * unit <= max.
*/
diff: function(min, max, unit) {
var est,
diff = +max - min;
switch (unit) {
case utilDate.MILLI:
return diff;
case utilDate.SECOND:
return Math.floor(diff / 1000);
case utilDate.MINUTE:
return Math.floor(diff / 60000);
case utilDate.HOUR:
return Math.floor(diff / 3600000);
case utilDate.DAY:
return Math.floor(diff / 86400000);
case 'w':
return Math.floor(diff / 604800000);
case utilDate.MONTH:
est = (max.getFullYear() * 12 + max.getMonth()) - (min.getFullYear() * 12 + min.getMonth());
if (utilDate.add(min, unit, est) > max) {
return est - 1;
};
return est;
case utilDate.YEAR:
est = max.getFullYear() - min.getFullYear();
if (utilDate.add(min, unit, est) > max) {
return est - 1;
} else {
return est;
};
}
},
/**
* Align the date to `unit`.
* @param {Date} date The date to be aligned.
* @param {String} unit The unit. This unit is compatible with the date interval constants.
* @param {Number} step
* @return {Date} The aligned date.
*/
align: function(date, unit, step) {
var num = new nativeDate(+date);
switch (unit.toLowerCase()) {
case utilDate.MILLI:
return num;
case utilDate.SECOND:
num.setUTCSeconds(num.getUTCSeconds() - num.getUTCSeconds() % step);
num.setUTCMilliseconds(0);
return num;
case utilDate.MINUTE:
num.setUTCMinutes(num.getUTCMinutes() - num.getUTCMinutes() % step);
num.setUTCSeconds(0);
num.setUTCMilliseconds(0);
return num;
case utilDate.HOUR:
num.setUTCHours(num.getUTCHours() - num.getUTCHours() % step);
num.setUTCMinutes(0);
num.setUTCSeconds(0);
num.setUTCMilliseconds(0);
return num;
case utilDate.DAY:
if (step === 7 || step === 14) {
num.setUTCDate(num.getUTCDate() - num.getUTCDay() + 1);
};
num.setUTCHours(0);
num.setUTCMinutes(0);
num.setUTCSeconds(0);
num.setUTCMilliseconds(0);
return num;
case utilDate.MONTH:
num.setUTCMonth(num.getUTCMonth() - (num.getUTCMonth() - 1) % step, 1);
num.setUTCHours(0);
num.setUTCMinutes(0);
num.setUTCSeconds(0);
num.setUTCMilliseconds(0);
return num;
case utilDate.YEAR:
num.setUTCFullYear(num.getUTCFullYear() - num.getUTCFullYear() % step, 1, 1);
num.setUTCHours(0);
num.setUTCMinutes(0);
num.setUTCSeconds(0);
num.setUTCMilliseconds(0);
return date;
}
}
};
utilDate.parseCodes.C = utilDate.parseCodes.c;
return utilDate;
}());
/**
* @class Ext.Function
*
* A collection of useful static methods to deal with function callbacks.
* @singleton
*/
Ext.Function = (function() {
// @define Ext.lang.Function
// @define Ext.Function
// @require Ext
// @require Ext.lang.Array
var lastTime = 0,
animFrameId,
animFrameHandlers = [],
animFrameNoArgs = [],
idSource = 0,
animFrameMap = {},
win = window,
global = Ext.global,
hasImmediate = !!(global.setImmediate && global.clearImmediate),
requestAnimFrame = win.requestAnimationFrame || win.webkitRequestAnimationFrame || win.mozRequestAnimationFrame || win.oRequestAnimationFrame || function(callback) {
var currTime = Ext.now(),
timeToCall = Math.max(0, 16 - (currTime - lastTime)),
id = win.setTimeout(function() {
callback(currTime + timeToCall);
}, timeToCall);
lastTime = currTime + timeToCall;
return id;
},
fireHandlers = function() {
var len = animFrameHandlers.length,
id, i, handler;
animFrameId = null;
// Fire all animation frame handlers in one go
for (i = 0; i < len; i++) {
handler = animFrameHandlers[i];
id = handler[3];
// Check if this timer has been canceled; its map entry is going to be removed
if (animFrameMap[id]) {
handler[0].apply(handler[1] || global, handler[2] || animFrameNoArgs);
delete animFrameMap[id];
}
}
// Clear all fired animation frame handlers, don't forget that new handlers
// could have been created in user handler functions called in the loop above
animFrameHandlers = animFrameHandlers.slice(len);
},
fireElevatedHandlers = function() {
Ext.elevateFunction(fireHandlers);
},
ExtFunction = {
/**
* A very commonly used method throughout the framework. It acts as a wrapper around another method
* which originally accepts 2 arguments for `name` and `value`.
* The wrapped function then allows "flexible" value setting of either:
*
* - `name` and `value` as 2 arguments
* - one single object argument with multiple key - value pairs
*
* For example:
*
* var setValue = Ext.Function.flexSetter(function(name, value) {
* this[name] = value;
* });
*
* // Afterwards
* // Setting a single name - value
* setValue('name1', 'value1');
*
* // Settings multiple name - value pairs
* setValue({
* name1: 'value1',
* name2: 'value2',
* name3: 'value3'
* });
*
* @param {Function} setter The single value setter method.
* @param {String} setter.name The name of the value being set.
* @param {Object} setter.value The value being set.
* @return {Function}
*/
flexSetter: function(setter) {
return function(name, value) {
var k, i;
if (name !== null) {
if (typeof name !== 'string') {
for (k in name) {
if (name.hasOwnProperty(k)) {
setter.call(this, k, name[k]);
}
}
if (Ext.enumerables) {
for (i = Ext.enumerables.length; i--; ) {
k = Ext.enumerables[i];
if (name.hasOwnProperty(k)) {
setter.call(this, k, name[k]);
}
}
}
} else {
setter.call(this, name, value);
}
}
return this;
};
},
/**
* Create a new function from the provided `fn`, change `this` to the provided scope,
* optionally overrides arguments for the call. Defaults to the arguments passed by
* the caller.
*
* {@link Ext#bind Ext.bind} is alias for {@link Ext.Function#bind Ext.Function.bind}
*
* **NOTE:** This method is deprecated. Use the standard `bind` method of JavaScript
* `Function` instead:
*
* function foo () {
* ...
* }
*
* var fn = foo.bind(this);
*
* This method is unavailable natively on IE8 and IE/Quirks but Ext JS provides a
* "polyfill" to emulate the important features of the standard `bind` method. In
* particular, the polyfill only provides binding of "this" and optional arguments.
*
* @param {Function} fn The function to delegate.
* @param {Object} scope (optional) The scope (`this` reference) in which the function is executed.
* **If omitted, defaults to the default global environment object (usually the browser window).**
* @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
* @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
* if a number the args are inserted at the specified position.
* @return {Function} The new function.
*/
bind: function(fn, scope, args, appendArgs) {
if (arguments.length === 2) {
return function() {
return fn.apply(scope, arguments);
};
}
var method = fn,
slice = Array.prototype.slice;
return function() {
var callArgs = args || arguments;
if (appendArgs === true) {
callArgs = slice.call(arguments, 0);
callArgs = callArgs.concat(args);
} else if (typeof appendArgs === 'number') {
callArgs = slice.call(arguments, 0);
// copy arguments first
Ext.Array.insert(callArgs, appendArgs, args);
}
return method.apply(scope || global, callArgs);
};
},
/**
* Captures the given parameters for a later call to `Ext.callback`. This binding is
* most useful for resolving scopes for example to an `Ext.app.ViewController`.
*
* The arguments match that of `Ext.callback` except for the `args` which, if provided
* to this method, are prepended to any arguments supplied by the eventual caller of
* the returned function.
*
* @return {Function} A function that, when called, uses `Ext.callback` to call the
* captured `callback`.
* @since 5.0.0
*/
bindCallback: function(callback, scope, args, delay, caller) {
return function() {
var a = Ext.Array.slice(arguments);
return Ext.callback(callback, scope, args ? args.concat(a) : a, delay, caller);
};
},
/**
* Create a new function from the provided `fn`, the arguments of which are pre-set to `args`.
* New arguments passed to the newly created callback when it's invoked are appended after the pre-set ones.
* This is especially useful when creating callbacks.
*
* For example:
*
* var originalFunction = function(){
* alert(Ext.Array.from(arguments).join(' '));
* };
*
* var callback = Ext.Function.pass(originalFunction, ['Hello', 'World']);
*
* callback(); // alerts 'Hello World'
* callback('by Me'); // alerts 'Hello World by Me'
*
* {@link Ext#pass Ext.pass} is alias for {@link Ext.Function#pass Ext.Function.pass}
*
* @param {Function} fn The original function.
* @param {Array} args The arguments to pass to new callback.
* @param {Object} scope (optional) The scope (`this` reference) in which the function is executed.
* @return {Function} The new callback function.
*/
pass: function(fn, args, scope) {
if (!Ext.isArray(args)) {
if (Ext.isIterable(args)) {
args = Ext.Array.clone(args);
} else {
args = args !== undefined ? [
args
] : [];
}
}
return function() {
var fnArgs = args.slice();
fnArgs.push.apply(fnArgs, arguments);
return fn.apply(scope || this, fnArgs);
};
},
/**
* Create an alias to the provided method property with name `methodName` of `object`.
* Note that the execution scope will still be bound to the provided `object` itself.
*
* @param {Object/Function} object
* @param {String} methodName
* @return {Function} aliasFn
*/
alias: function(object, methodName) {
return function() {
return object[methodName].apply(object, arguments);
};
},
/**
* Create a "clone" of the provided method. The returned method will call the given
* method passing along all arguments and the "this" pointer and return its result.
*
* @param {Function} method
* @return {Function} cloneFn
*/
clone: function(method) {
return function() {
return method.apply(this, arguments);
};
},
/**
* Creates an interceptor function. The passed function is called before the original one. If it returns false,
* the original one is not called. The resulting function returns the results of the original function.
* The passed function is called with the parameters of the original function. Example usage:
*
* var sayHi = function(name){
* alert('Hi, ' + name);
* };
*
* sayHi('Fred'); // alerts "Hi, Fred"
*
* // create a new function that validates input without
* // directly modifying the original function:
* var sayHiToFriend = Ext.Function.createInterceptor(sayHi, function(name){
* return name === 'Brian';
* });
*
* sayHiToFriend('Fred'); // no alert
* sayHiToFriend('Brian'); // alerts "Hi, Brian"
*
* @param {Function} origFn The original function.
* @param {Function} newFn The function to call before the original.
* @param {Object} [scope] The scope (`this` reference) in which the passed function is executed.
* **If omitted, defaults to the scope in which the original function is called or the browser window.**
* @param {Object} [returnValue=null] The value to return if the passed function return `false`.
* @return {Function} The new function.
*/
createInterceptor: function(origFn, newFn, scope, returnValue) {
if (!Ext.isFunction(newFn)) {
return origFn;
} else {
returnValue = Ext.isDefined(returnValue) ? returnValue : null;
return function() {
var me = this,
args = arguments;
return (newFn.apply(scope || me || global, args) !== false) ? origFn.apply(me || global, args) : returnValue;
};
}
},
/**
* Creates a delegate (callback) which, when called, executes after a specific delay.
*
* @param {Function} fn The function which will be called on a delay when the returned function is called.
* Optionally, a replacement (or additional) argument list may be specified.
* @param {Number} delay The number of milliseconds to defer execution by whenever called.
* @param {Object} scope (optional) The scope (`this` reference) used by the function at execution time.
* @param {Array} args (optional) Override arguments for the call. (Defaults to the arguments passed by the caller)
* @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
* if a number the args are inserted at the specified position.
* @return {Function} A function which, when called, executes the original function after the specified delay.
*/
createDelayed: function(fn, delay, scope, args, appendArgs) {
if (scope || args) {
fn = Ext.Function.bind(fn, scope, args, appendArgs);
}
return function() {
var me = this,
args = Array.prototype.slice.call(arguments);
setTimeout(function() {
if (Ext.elevateFunction) {
Ext.elevateFunction(fn, me, args);
} else {
fn.apply(me, args);
}
}, delay);
};
},
/**
* Calls this function after the number of milliseconds specified, optionally in a specific scope. Example usage:
*
* var sayHi = function(name){
* alert('Hi, ' + name);
* }
*
* // executes immediately:
* sayHi('Fred');
*
* // executes after 2 seconds:
* Ext.Function.defer(sayHi, 2000, this, ['Fred']);
*
* // this syntax is sometimes useful for deferring
* // execution of an anonymous function:
* Ext.Function.defer(function(){
* alert('Anonymous');
* }, 100);
*
* {@link Ext#defer Ext.defer} is alias for {@link Ext.Function#defer Ext.Function.defer}
*
* @param {Function} fn The function to defer.
* @param {Number} millis The number of milliseconds for the `setTimeout` call
* (if less than or equal to 0 the function is executed immediately).
* @param {Object} scope (optional) The scope (`this` reference) in which the function is executed.
* **If omitted, defaults to the browser window.**
* @param {Array} [args] Overrides arguments for the call. Defaults to the arguments passed by the caller.
* @param {Boolean/Number} [appendArgs=false] If `true` args are appended to call args instead of overriding,
* or, if a number, then the args are inserted at the specified position.
* @return {Number} The timeout id that can be used with `clearTimeout`.
*/
defer: function(fn, millis, scope, args, appendArgs) {
fn = Ext.Function.bind(fn, scope, args, appendArgs);
if (millis > 0) {
return setTimeout(function() {
if (Ext.elevateFunction) {
Ext.elevateFunction(fn);
} else {
fn();
}
}, millis);
}
fn();
return 0;
},
/**
* Calls this function repeatedly at a given interval, optionally in a specific scope.
*
* {@link Ext#defer Ext.defer} is alias for {@link Ext.Function#defer Ext.Function.defer}
*
* @param {Function} fn The function to defer.
* @param {Number} millis The number of milliseconds for the `setInterval` call
* @param {Object} scope (optional) The scope (`this` reference) in which the function is executed.
* **If omitted, defaults to the browser window.**
* @param {Array} [args] Overrides arguments for the call. Defaults to the arguments passed by the caller.
* @param {Boolean/Number} [appendArgs=false] If `true` args are appended to call args instead of overriding,
* or, if a number, then the args are inserted at the specified position.
* @return {Number} The interval id that can be used with `clearInterval`.
*/
interval: function(fn, millis, scope, args, appendArgs) {
fn = Ext.Function.bind(fn, scope, args, appendArgs);
return setInterval(function() {
if (Ext.elevateFunction) {
Ext.elevateFunction(fn);
} else {
fn();
}
}, millis);
},
/**
* Create a combined function call sequence of the original function + the passed function.
* The resulting function returns the results of the original function.
* The passed function is called with the parameters of the original function. Example usage:
*
* var sayHi = function(name){
* alert('Hi, ' + name);
* };
*
* sayHi('Fred'); // alerts "Hi, Fred"
*
* var sayGoodbye = Ext.Function.createSequence(sayHi, function(name){
* alert('Bye, ' + name);
* });
*
* sayGoodbye('Fred'); // both alerts show
*
* @param {Function} originalFn The original function.
* @param {Function} newFn The function to sequence.
* @param {Object} [scope] The scope (`this` reference) in which the passed function is executed.
* If omitted, defaults to the scope in which the original function is called or the
* default global environment object (usually the browser window).
* @return {Function} The new function.
*/
createSequence: function(originalFn, newFn, scope) {
if (!newFn) {
return originalFn;
} else {
return function() {
var result = originalFn.apply(this, arguments);
newFn.apply(scope || this, arguments);
return result;
};
}
},
/**
* Creates a delegate function, optionally with a bound scope which, when called, buffers
* the execution of the passed function for the configured number of milliseconds.
* If called again within that period, the impending invocation will be canceled, and the
* timeout period will begin again.
*
* @param {Function} fn The function to invoke on a buffered timer.
* @param {Number} buffer The number of milliseconds by which to buffer the invocation of the
* function.
* @param {Object} [scope] The scope (`this` reference) in which.
* the passed function is executed. If omitted, defaults to the scope specified by the caller.
* @param {Array} [args] Override arguments for the call. Defaults to the arguments
* passed by the caller.
* @return {Function} A function which invokes the passed function after buffering for the specified time.
*/
createBuffered: function(fn, buffer, scope, args) {
var timerId;
return function() {
var callArgs = args || Array.prototype.slice.call(arguments, 0),
me = scope || this;
if (timerId) {
clearTimeout(timerId);
}
timerId = setTimeout(function() {
if (Ext.elevateFunction) {
Ext.elevateFunction(fn, me, callArgs);
} else {
fn.apply(me, callArgs);
}
}, buffer);
};
},
/**
* Creates a wrapped function that, when invoked, defers execution until the next
* animation frame
* @private
* @param {Function} fn The function to call.
* @param {Object} [scope] The scope (`this` reference) in which the function is executed. Defaults to the window object.
* @param {Array} [args] The argument list to pass to the function.
* @param {Number} [queueStrategy=3] A bit flag that indicates how multiple calls to
* the returned function within the same animation frame should be handled.
*
* - 1: All calls will be queued - FIFO order
* - 2: Only the first call will be queued
* - 3: The last call will replace all previous calls
*
* @return {Function}
*/
createAnimationFrame: function(fn, scope, args, queueStrategy) {
var timerId;
queueStrategy = queueStrategy || 3;
return function() {
var callArgs = args || Array.prototype.slice.call(arguments, 0);
scope = scope || this;
if (queueStrategy === 3 && timerId) {
ExtFunction.cancelAnimationFrame(timerId);
}
if ((queueStrategy & 1) || !timerId) {
timerId = ExtFunction.requestAnimationFrame(function() {
timerId = null;
fn.apply(scope, callArgs);
});
}
};
},
/**
* @private
* Schedules the passed function to be called on the next animation frame.
* @param {Function} fn The function to call.
* @param {Object} [scope] The scope (`this` reference) in which the function is executed. Defaults to the window object.
* @param {Mixed[]} [args] The argument list to pass to the function.
*
* @return {Number} Timer id for the new animation frame to use when canceling it.
*/
requestAnimationFrame: function(fn, scope, args) {
var id = ++idSource,
// Ids start at 1
handler = Array.prototype.slice.call(arguments, 0);
handler[3] = id;
animFrameMap[id] = 1;
// A flag to indicate that the timer exists
// We might be in fireHandlers at this moment but this new entry will not
// be executed until the next frame
animFrameHandlers.push(handler);
if (!animFrameId) {
animFrameId = requestAnimFrame(Ext.elevateFunction ? fireElevatedHandlers : fireHandlers);
}
return id;
},
cancelAnimationFrame: function(id) {
// Don't remove any handlers from animFrameHandlers array, because
// the might be in use at the moment (when cancelAnimationFrame is called).
// Just remove the handler id from the map so it will not be executed
delete animFrameMap[id];
},
/**
* Creates a throttled version of the passed function which, when called repeatedly and
* rapidly, invokes the passed function only after a certain interval has elapsed since the
* previous invocation.
*
* This is useful for wrapping functions which may be called repeatedly, such as
* a handler of a mouse move event when the processing is expensive.
*
* @param {Function} fn The function to execute at a regular time interval.
* @param {Number} interval The interval in milliseconds on which the passed function is executed.
* @param {Object} [scope] The scope (`this` reference) in which
* the passed function is executed. If omitted, defaults to the scope specified by the caller.
* @return {Function} A function which invokes the passed function at the specified interval.
*/
createThrottled: function(fn, interval, scope) {
var lastCallTime = 0,
elapsed, lastArgs, timer,
execute = function() {
if (Ext.elevateFunction) {
Ext.elevateFunction(fn, scope, lastArgs);
} else {
fn.apply(scope, lastArgs);
}
lastCallTime = Ext.now();
timer = null;
};
return function() {
// Use scope of last call unless the creator specified a scope
if (!scope) {
scope = this;
}
elapsed = Ext.now() - lastCallTime;
lastArgs = arguments;
// If this is the first invocation, or the throttle interval has been reached, clear any
// pending invocation, and call the target function now.
if (elapsed >= interval) {
clearTimeout(timer);
execute();
}
// Throttle interval has not yet been reached. Only set the timer to fire if not already set.
else if (!timer) {
timer = Ext.defer(execute, interval - elapsed);
}
};
},
/**
* Wraps the passed function in a barrier function which will call the passed function after the passed number of invocations.
* @param {Number} count The number of invocations which will result in the calling of the passed function.
* @param {Function} fn The function to call after the required number of invocations.
* @param {Object} scope The scope (`this` reference) in which the function will be called.
*/
createBarrier: function(count, fn, scope) {
return function() {
if (!--count) {
fn.apply(scope, arguments);
}
};
},
/**
* Adds behavior to an existing method that is executed before the
* original behavior of the function. For example:
*
* var soup = {
* contents: [],
* add: function(ingredient) {
* this.contents.push(ingredient);
* }
* };
* Ext.Function.interceptBefore(soup, "add", function(ingredient){
* if (!this.contents.length && ingredient !== "water") {
* // Always add water to start with
* this.contents.push("water");
* }
* });
* soup.add("onions");
* soup.add("salt");
* soup.contents; // will contain: water, onions, salt
*
* @param {Object} object The target object
* @param {String} methodName Name of the method to override
* @param {Function} fn Function with the new behavior. It will
* be called with the same arguments as the original method. The
* return value of this function will be the return value of the
* new method.
* @param {Object} [scope] The scope to execute the interceptor function. Defaults to the object.
* @return {Function} The new function just created.
*/
interceptBefore: function(object, methodName, fn, scope) {
var method = object[methodName] || Ext.emptyFn;
return (object[methodName] = function() {
var ret = fn.apply(scope || this, arguments);
method.apply(this, arguments);
return ret;
});
},
/**
* Adds behavior to an existing method that is executed after the
* original behavior of the function. For example:
*
* var soup = {
* contents: [],
* add: function(ingredient) {
* this.contents.push(ingredient);
* }
* };
* Ext.Function.interceptAfter(soup, "add", function(ingredient){
* // Always add a bit of extra salt
* this.contents.push("salt");
* });
* soup.add("water");
* soup.add("onions");
* soup.contents; // will contain: water, salt, onions, salt
*
* @param {Object} object The target object
* @param {String} methodName Name of the method to override
* @param {Function} fn Function with the new behavior. It will
* be called with the same arguments as the original method. The
* return value of this function will be the return value of the
* new method.
* @param {Object} [scope] The scope to execute the interceptor function. Defaults to the object.
* @return {Function} The new function just created.
*/
interceptAfter: function(object, methodName, fn, scope) {
var method = object[methodName] || Ext.emptyFn;
return (object[methodName] = function() {
method.apply(this, arguments);
return fn.apply(scope || this, arguments);
});
},
interceptAfterOnce: function(object, methodName, fn, scope) {
var origMethod = object[methodName],
newMethod;
newMethod = function() {
var ret;
if (origMethod) {
origMethod.apply(this, arguments);
}
ret = fn.apply(scope || this, arguments);
object[methodName] = origMethod;
object = methodName = fn = scope = origMethod = newMethod = null;
return ret;
};
object[methodName] = newMethod;
return newMethod;
},
makeCallback: function(callback, scope) {
if (!scope[callback]) {
if (scope.$className) {
Ext.raise('No method "' + callback + '" on ' + scope.$className);
}
Ext.raise('No method "' + callback + '"');
}
return function() {
return scope[callback].apply(scope, arguments);
};
},
/**
* Returns a wrapper function that caches the return value for previously
* processed function argument(s).
*
* For example:
*
* function factorial (value) {
* var ret = value;
*
* while (--value > 1) {
* ret *= value;
* }
*
* return ret;
* }
*
* Each call to `factorial` will loop and multiply to produce the answer. Using
* this function we can wrap the above and cache its answers:
*
* factorial = Ext.Function.memoize(factorial);
*
* The returned function operates in the same manner as before, but results are
* stored in a cache to avoid calling the wrapped function when given the same
* arguments.
*
* var x = factorial(20); // first time; call real factorial()
* var y = factorial(20); // second time; return value from first call
*
* To support multi-argument methods, you will need to provide a `hashFn`.
*
* function permutation (n, k) {
* return factorial(n) / factorial(n - k);
* }
*
* permutation = Ext.Function.memoize(permutation, null, function (n, k) {
* n + '-' + k;
* });
*
* In this case, the `memoize` of `factorial` is sufficient optimization, but the
* example is simply to illustrate how to generate a unique key for an expensive,
* multi-argument method.
*
* **IMPORTANT**: This cache is unbounded so be cautious of memory leaks if the
* `memoize`d function is kept indefinitely or is given an unbounded set of
* possible arguments.
*
* @param {Function} fn Function to wrap.
* @param {Object} scope Optional scope in which to execute the wrapped function.
* @param {Function} hashFn Optional function used to compute a hash key for
* storing the result, based on the arguments to the original function.
* @return {Function} The caching wrapper function.
* @since 6.0.0
*/
memoize: function(fn, scope, hashFn) {
var memo = {},
isFunc = hashFn && Ext.isFunction(hashFn);
return function(value) {
var key = isFunc ? hashFn.apply(scope, arguments) : value;
if (!(key in memo)) {
memo[key] = fn.apply(scope, arguments);
}
return memo[key];
};
}
};
// ExtFunction
/**
* @member Ext
* @method asap
* Schedules the specified callback function to be executed on the next turn of the
* event loop. Where available, this method uses the browser's `setImmediate` API. If
* not available, this method substitutes `setTimeout(0)`. Though not a perfect
* replacement for `setImmediate` it is sufficient for many use cases.
*
* For more details see [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Window/setImmediate).
*
* @param {Function} fn Callback function.
* @param {Object} [scope] The scope for the callback (`this` pointer).
* @param {Mixed[]} [parameters] Additional parameters to pass to `fn`.
* @return {Number} A cancelation id for `{@link Ext#asapCancel}`.
*/
Ext.asap = hasImmediate ? function(fn, scope, parameters) {
if (scope != null || parameters != null) {
fn = ExtFunction.bind(fn, scope, parameters);
}
return setImmediate(function() {
if (Ext.elevateFunction) {
Ext.elevateFunction(fn);
} else {
fn();
}
});
} : function(fn, scope, parameters) {
if (scope != null || parameters != null) {
fn = ExtFunction.bind(fn, scope, parameters);
}
return setTimeout(function() {
if (Ext.elevateFunction) {
Ext.elevateFunction(fn);
} else {
fn();
}
}, 0, true);
} , /**
* @member Ext
* @method asapCancel
* Cancels a previously scheduled call to `{@link Ext#asap}`.
*
* var asapId = Ext.asap(me.method, me);
* ...
*
* if (nevermind) {
* Ext.apasCancel(asapId);
* }
*
* @param {Number} id The id returned by `{@link Ext#asap}`.
*/
Ext.asapCancel = hasImmediate ? function(id) {
clearImmediate(id);
} : function(id) {
clearTimeout(id);
};
/**
* @method defer
* @member Ext
* @inheritdoc Ext.Function#defer
*/
Ext.defer = ExtFunction.defer;
/**
* @method interval
* @member Ext
* @inheritdoc Ext.Function#interval
*/
Ext.interval = ExtFunction.interval;
/**
* @method pass
* @member Ext
* @inheritdoc Ext.Function#pass
*/
Ext.pass = ExtFunction.pass;
/**
* @method bind
* @member Ext
* @inheritdoc Ext.Function#bind
*/
Ext.bind = ExtFunction.bind;
Ext.deferCallback = ExtFunction.requestAnimationFrame;
return ExtFunction;
})();
/**
* @class Ext.Number
*
* A collection of useful static methods to deal with numbers
* @singleton
*/
Ext.Number = (new function() {
// jshint ignore:line
// @define Ext.lang.Number
// @define Ext.Number
// @require Ext
var ExtNumber = this,
isToFixedBroken = (0.9).toFixed() !== '1',
math = Math,
ClipDefault = {
count: false,
inclusive: false,
wrap: true
};
Ext.apply(ExtNumber, {
MIN_SAFE_INTEGER: Number.MIN_SAFE_INTEGER || -(math.pow(2, 53) - 1),
MAX_SAFE_INTEGER: Number.MAX_SAFE_INTEGER || math.pow(2, 53) - 1,
Clip: {
DEFAULT: ClipDefault,
COUNT: Ext.applyIf({
count: true
}, ClipDefault),
INCLUSIVE: Ext.applyIf({
inclusive: true
}, ClipDefault),
NOWRAP: Ext.applyIf({
wrap: false
}, ClipDefault)
},
/**
* Coerces a given index into a valid index given a `length`.
*
* Negative indexes are interpreted starting at the end of the collection. That is,
* a value of -1 indicates the last item, or equivalent to `length - 1`.
*
* When handling methods that take "begin" and "end" arguments like most array or
* string methods, this method can be used like so:
*
* function foo (array, begin, end) {
* var range = Ext.Number.clipIndices(array.length, [begin, end]);
*
* begin = range[0];
* end = range[1];
*
* // 0 <= begin <= end <= array.length
*
* var length = end - begin;
* }
*
* For example:
*
* +---+---+---+---+---+---+---+---+
* | | | | | | | | | length = 8
* +---+---+---+---+---+---+---+---+
* 0 1 2 3 4 5 6 7
* -8 -7 -6 -5 -4 -3 -2 -1
*
* console.log(Ext.Number.clipIndices(8, [3, 10]); // logs "[3, 8]"
* console.log(Ext.Number.clipIndices(8, [-5]); // logs "[3, 8]"
* console.log(Ext.Number.clipIndices(8, []);
* console.log(Ext.Number.clipIndices(8, []);
*
* @param {Number} length
* @param {Number[]} indices
* @param {Object} [options] An object with different option flags.
* @param {Boolean} [options.count=false] The second number in `indices` is the
* count not and an index.
* @param {Boolean} [options.inclusive=false] The second number in `indices` is
* "inclusive" meaning that the item should be considered in the range. Normally,
* the second number is considered the first item outside the range or as an
* "exclusive" bound.
* @param {Boolean} [options.wrap=true] Wraps negative numbers backwards from the
* end of the array. Passing `false` simply clips negative index values at 0.
* @return {Number[]} The normalized `[begin, end]` array where `end` is now
* exclusive such that `length = end - begin`. Both values are between 0 and the
* given `length` and `end` will not be less-than `begin`.
*/
clipIndices: function(length, indices, options) {
options = options || ClipDefault;
var defaultValue = 0,
// default value for "begin"
wrap = options.wrap,
begin, end, i;
indices = indices || [];
for (i = 0; i < 2; ++i) {
// names are off on first pass but used this way so things make sense
// following the loop..
begin = end;
// pick up and keep the result from the first loop
end = indices[i];
if (end == null) {
end = defaultValue;
} else if (i && options.count) {
end += begin;
// this is the length not "end" so convert to "end"
end = (end > length) ? length : end;
} else {
if (wrap) {
end = (end < 0) ? (length + end) : end;
}
if (i && options.inclusive) {
++end;
}
end = (end < 0) ? 0 : ((end > length) ? length : end);
}
defaultValue = length;
}
// default value for "end"
// after loop:
// 0 <= begin <= length (calculated from indices[0])
// 0 <= end <= length (calculated from indices[1])
indices[0] = begin;
indices[1] = (end < begin) ? begin : end;
return indices;
},
/**
* Checks whether or not the passed number is within a desired range. If the number is already within the
* range it is returned, otherwise the min or max value is returned depending on which side of the range is
* exceeded. Note that this method returns the constrained value but does not change the current number.
* @param {Number} number The number to check
* @param {Number} min The minimum number in the range
* @param {Number} max The maximum number in the range
* @return {Number} The constrained value if outside the range, otherwise the current value
*/
constrain: function(number, min, max) {
var x = parseFloat(number);
// (x < Nan) || (x < undefined) == false
// same for (x > NaN) || (x > undefined)
// sadly this is not true of null - (1 > null)
if (min === null) {
min = number;
}
if (max === null) {
max = number;
}
// Watch out for NaN in Chrome 18
// V8bug: http://code.google.com/p/v8/issues/detail?id=2056
// Operators are faster than Math.min/max. See http://jsperf.com/number-constrain
return (x < min) ? min : ((x > max) ? max : x);
},
/**
* Snaps the passed number between stopping points based upon a passed increment value.
*
* The difference between this and {@link #snapInRange} is that {@link #snapInRange} uses the minValue
* when calculating snap points:
*
* r = Ext.Number.snap(56, 2, 55, 65); // Returns 56 - snap points are zero based
*
* r = Ext.Number.snapInRange(56, 2, 55, 65); // Returns 57 - snap points are based from minValue
*
* @param {Number} value The unsnapped value.
* @param {Number} increment The increment by which the value must move.
* @param {Number} minValue The minimum value to which the returned value must be constrained. Overrides the increment.
* @param {Number} maxValue The maximum value to which the returned value must be constrained. Overrides the increment.
* @return {Number} The value of the nearest snap target.
*/
snap: function(value, increment, minValue, maxValue) {
var m;
// If no value passed, or minValue was passed and value is less than minValue (anything < undefined is false)
// Then use the minValue (or zero if the value was undefined)
if (value === undefined || value < minValue) {
return minValue || 0;
}
if (increment) {
m = value % increment;
if (m !== 0) {
value -= m;
if (m * 2 >= increment) {
value += increment;
} else if (m * 2 < -increment) {
value -= increment;
}
}
}
return ExtNumber.constrain(value, minValue, maxValue);
},
/**
* Snaps the passed number between stopping points based upon a passed increment value.
*
* The difference between this and {@link #snap} is that {@link #snap} does not use the minValue
* when calculating snap points:
*
* r = Ext.Number.snap(56, 2, 55, 65); // Returns 56 - snap points are zero based
*
* r = Ext.Number.snapInRange(56, 2, 55, 65); // Returns 57 - snap points are based from minValue
*
* @param {Number} value The unsnapped value.
* @param {Number} increment The increment by which the value must move.
* @param {Number} [minValue=0] The minimum value to which the returned value must be constrained.
* @param {Number} [maxValue=Infinity] The maximum value to which the returned value must be constrained.
* @return {Number} The value of the nearest snap target.
*/
snapInRange: function(value, increment, minValue, maxValue) {
var tween;
// default minValue to zero
minValue = (minValue || 0);
// If value is undefined, or less than minValue, use minValue
if (value === undefined || value < minValue) {
return minValue;
}
// Calculate how many snap points from the minValue the passed value is.
if (increment && (tween = ((value - minValue) % increment))) {
value -= tween;
tween *= 2;
if (tween >= increment) {
value += increment;
}
}
// If constraining within a maximum, ensure the maximum is on a snap point
if (maxValue !== undefined) {
if (value > (maxValue = ExtNumber.snapInRange(maxValue, increment, minValue))) {
value = maxValue;
}
}
return value;
},
/**
* Round a number to the nearest interval.
* @param {Number} value The value to round.
* @param {Number} interval The interval to round to.
* @return {Number} The rounded value.
*
* @since 6.2.0
*/
roundToNearest: function(value, interval) {
interval = interval || 1;
return interval * math.round(value / interval);
},
/**
* Returns the sign of the given number. See also MDN for Math.sign documentation
* for the standard method this method emulates.
* @param {Number} x The number.
* @return {Number} The sign of the number `x`, indicating whether the number is
* positive (1), negative (-1) or zero (0).
* @method sign
*/
sign: math.sign || function(x) {
x = +x;
// force to a Number
if (x === 0 || isNaN(x)) {
return x;
}
return (x > 0) ? 1 : -1;
},
/**
* Returns the base 10 logarithm of a number.
* This will use Math.log10, if available (ES6).
* @param {Number} x The number.
* @return {Number} Base 10 logarithm of the number 'x'.
* @method log10
*/
log10: math.log10 || function(x) {
return math.log(x) * math.LOG10E;
},
/**
* Determines if two numbers `n1` and `n2` are equal within a given
* margin of precision `epsilon`.
* @param {Number} n1 First number.
* @param {Number} n2 Second number.
* @param {Number} epsilon Margin of precision.
* @returns {Boolean} `true`, if numbers are equal. `false` otherwise.
*/
isEqual: function(n1, n2, epsilon) {
if (!(typeof n1 === 'number' && typeof n2 === 'number' && typeof epsilon === 'number')) {
Ext.raise("All parameters should be valid numbers.");
}
return math.abs(n1 - n2) < epsilon;
},
/**
* Determines if the value passed is a number and also finite.
* This a Polyfill version of Number.isFinite(),differently than
* the isFinite() function, this method doesn't convert the parameter to a number.
* @param {Number} value Number to be tested.
* @returns {Boolean} `true`, if the parameter is a number and finite, `false` otherwise.
* @since 6.2
*/
isFinite: Number.isFinite || function(value) {
return typeof value === 'number' && isFinite(value);
},
/**
* @method
* Formats a number using fixed-point notation
* @param {Number} value The number to format
* @param {Number} precision The number of digits to show after the decimal point
*/
toFixed: isToFixedBroken ? function(value, precision) {
precision = precision || 0;
var pow = math.pow(10, precision);
return (math.round(value * pow) / pow).toFixed(precision);
} : function(value, precision) {
return value.toFixed(precision);
},
/**
* Validate that a value is numeric and convert it to a number if necessary. Returns the specified default value if
* it is not.
Ext.Number.from('1.23', 1); // returns 1.23
Ext.Number.from('abc', 1); // returns 1
* @param {Object} value
* @param {Number} defaultValue The value to return if the original value is non-numeric
* @return {Number} value, if numeric, defaultValue otherwise
*/
from: function(value, defaultValue) {
if (isFinite(value)) {
value = parseFloat(value);
}
return !isNaN(value) ? value : defaultValue;
},
/**
* Returns a random integer between the specified range (inclusive)
* @param {Number} from Lowest value to return.
* @param {Number} to Highest value to return.
* @return {Number} A random integer within the specified range.
*/
randomInt: function(from, to) {
return math.floor(math.random() * (to - from + 1) + from);
},
/**
* Corrects floating point numbers that overflow to a non-precise
* value because of their floating nature, for example `0.1 + 0.2`
* @param {Number} n The number
* @return {Number} The correctly rounded number
*/
correctFloat: function(n) {
// This is to correct the type of errors where 2 floats end with
// a long string of decimals, eg 0.1 + 0.2. When they overflow in this
// manner, they usually go to 15-16 decimals, so we cut it off at 14.
return parseFloat(n.toPrecision(14));
}
});
/**
* @deprecated 4.0.0 Please use {@link Ext.Number#from} instead.
* @member Ext
* @method num
* @inheritdoc Ext.Number#from
*/
Ext.num = function() {
return ExtNumber.from.apply(this, arguments);
};
}());
/**
* @class Ext.Object
*
* A collection of useful static methods to deal with objects.
*
* @singleton
*/
(function() {
// The "constructor" for chain:
var TemplateClass = function() {},
queryRe = /^\?/,
keyRe = /(\[):?([^\]]*)\]/g,
nameRe = /^([^\[]+)/,
plusRe = /\+/g,
ExtObject = Ext.Object = {
// @define Ext.lang.Object
// @define Ext.Object
// @require Ext
// @require Ext.lang.Date
/**
* @method
* Returns a new object with the given object as the prototype chain. This method is
* designed to mimic the ECMA standard `Object.create` method and is assigned to that
* function when it is available.
*
* **NOTE** This method does not support the property definitions capability of the
* `Object.create` method. Only the first argument is supported.
*
* @param {Object} object The prototype chain for the new object.
*/
chain: Object.create || function(object) {
TemplateClass.prototype = object;
var result = new TemplateClass();
TemplateClass.prototype = null;
return result;
},
/**
* This method removes all keys from the given object.
* @param {Object} object The object from which to remove all keys.
* @return {Object} The given object.
*/
clear: function(object) {
// Safe to delete during iteration
for (var key in object) {
delete object[key];
}
return object;
},
/**
* Freezes the given object making it immutable. This operation is by default shallow
* and does not effect objects referenced by the given object.
*
* @method
* @param {Object} obj The object to freeze.
* @param {Boolean} [deep=false] Pass `true` to freeze sub-objects recursively.
* @return {Object} The given object `obj`.
*/
freeze: Object.freeze ? function(obj, deep) {
if (obj && typeof obj === 'object' && !Object.isFrozen(obj)) {
Object.freeze(obj);
if (deep) {
for (var name in obj) {
ExtObject.freeze(obj[name], deep);
}
}
}
return obj;
} : Ext.identityFn,
/**
* Converts a `name` - `value` pair to an array of objects with support for nested structures. Useful to construct
* query strings. For example:
*
* var objects = Ext.Object.toQueryObjects('hobbies', ['reading', 'cooking', 'swimming']);
*
* // objects then equals:
* [
* { name: 'hobbies', value: 'reading' },
* { name: 'hobbies', value: 'cooking' },
* { name: 'hobbies', value: 'swimming' },
* ];
*
* var objects = Ext.Object.toQueryObjects('dateOfBirth', {
* day: 3,
* month: 8,
* year: 1987,
* extra: {
* hour: 4
* minute: 30
* }
* }, true); // Recursive
*
* // objects then equals:
* [
* { name: 'dateOfBirth[day]', value: 3 },
* { name: 'dateOfBirth[month]', value: 8 },
* { name: 'dateOfBirth[year]', value: 1987 },
* { name: 'dateOfBirth[extra][hour]', value: 4 },
* { name: 'dateOfBirth[extra][minute]', value: 30 },
* ];
*
* @param {String} name
* @param {Object/Array} value
* @param {Boolean} [recursive=false] True to traverse object recursively
* @return {Object[]}
*/
toQueryObjects: function(name, value, recursive) {
var self = ExtObject.toQueryObjects,
objects = [],
i, ln;
if (Ext.isArray(value)) {
for (i = 0 , ln = value.length; i < ln; i++) {
if (recursive) {
objects = objects.concat(self(name + '[' + i + ']', value[i], true));
} else {
objects.push({
name: name,
value: value[i]
});
}
}
} else if (Ext.isObject(value)) {
for (i in value) {
if (value.hasOwnProperty(i)) {
if (recursive) {
objects = objects.concat(self(name + '[' + i + ']', value[i], true));
} else {
objects.push({
name: name,
value: value[i]
});
}
}
}
} else {
objects.push({
name: name,
value: value
});
}
return objects;
},
/**
* Takes an object and converts it to an encoded query string.
*
* Non-recursive:
*
* Ext.Object.toQueryString({foo: 1, bar: 2}); // returns "foo=1&bar=2"
* Ext.Object.toQueryString({foo: null, bar: 2}); // returns "foo=&bar=2"
* Ext.Object.toQueryString({'some price': '$300'}); // returns "some%20price=%24300"
* Ext.Object.toQueryString({date: new Date(2011, 0, 1)}); // returns "date=%222011-01-01T00%3A00%3A00%22"
* Ext.Object.toQueryString({colors: ['red', 'green', 'blue']}); // returns "colors=red&colors=green&colors=blue"
*
* Recursive:
*
* Ext.Object.toQueryString({
* username: 'Jacky',
* dateOfBirth: {
* day: 1,
* month: 2,
* year: 1911
* },
* hobbies: ['coding', 'eating', 'sleeping', ['nested', 'stuff']]
* }, true); // returns the following string (broken down and url-decoded for ease of reading purpose):
* // username=Jacky
* // &dateOfBirth[day]=1&dateOfBirth[month]=2&dateOfBirth[year]=1911
* // &hobbies[0]=coding&hobbies[1]=eating&hobbies[2]=sleeping&hobbies[3][0]=nested&hobbies[3][1]=stuff
*
* @param {Object} object The object to encode
* @param {Boolean} [recursive=false] Whether or not to interpret the object in recursive format.
* (PHP / Ruby on Rails servers and similar).
* @return {String} queryString
*/
toQueryString: function(object, recursive) {
var paramObjects = [],
params = [],
i, j, ln, paramObject, value;
for (i in object) {
if (object.hasOwnProperty(i)) {
paramObjects = paramObjects.concat(ExtObject.toQueryObjects(i, object[i], recursive));
}
}
for (j = 0 , ln = paramObjects.length; j < ln; j++) {
paramObject = paramObjects[j];
value = paramObject.value;
if (Ext.isEmpty(value)) {
value = '';
} else if (Ext.isDate(value)) {
value = Ext.Date.toString(value);
}
params.push(encodeURIComponent(paramObject.name) + '=' + encodeURIComponent(String(value)));
}
return params.join('&');
},
/**
* Converts a query string back into an object.
*
* Non-recursive:
*
* Ext.Object.fromQueryString("foo=1&bar=2"); // returns {foo: '1', bar: '2'}
* Ext.Object.fromQueryString("foo=&bar=2"); // returns {foo: '', bar: '2'}
* Ext.Object.fromQueryString("some%20price=%24300"); // returns {'some price': '$300'}
* Ext.Object.fromQueryString("colors=red&colors=green&colors=blue"); // returns {colors: ['red', 'green', 'blue']}
*
* Recursive:
*
* Ext.Object.fromQueryString(
* "username=Jacky&"+
* "dateOfBirth[day]=1&dateOfBirth[month]=2&dateOfBirth[year]=1911&"+
* "hobbies[0]=coding&hobbies[1]=eating&hobbies[2]=sleeping&"+
* "hobbies[3][0]=nested&hobbies[3][1]=stuff", true);
*
* // returns
* {
* username: 'Jacky',
* dateOfBirth: {
* day: '1',
* month: '2',
* year: '1911'
* },
* hobbies: ['coding', 'eating', 'sleeping', ['nested', 'stuff']]
* }
*
* @param {String} queryString The query string to decode
* @param {Boolean} [recursive=false] Whether or not to recursively decode the string. This format is supported by
* PHP / Ruby on Rails servers and similar.
* @return {Object}
*/
fromQueryString: function(queryString, recursive) {
var parts = queryString.replace(queryRe, '').split('&'),
object = {},
temp, components, name, value, i, ln, part, j, subLn, matchedKeys, matchedName, keys, key, nextKey;
for (i = 0 , ln = parts.length; i < ln; i++) {
part = parts[i];
if (part.length > 0) {
components = part.split('=');
name = components[0];
name = name.replace(plusRe, '%20');
name = decodeURIComponent(name);
value = components[1];
if (value !== undefined) {
value = value.replace(plusRe, '%20');
value = decodeURIComponent(value);
} else {
value = '';
}
if (!recursive) {
if (object.hasOwnProperty(name)) {
if (!Ext.isArray(object[name])) {
object[name] = [
object[name]
];
}
object[name].push(value);
} else {
object[name] = value;
}
} else {
matchedKeys = name.match(keyRe);
matchedName = name.match(nameRe);
if (!matchedName) {
throw new Error('[Ext.Object.fromQueryString] Malformed query string given, failed parsing name from "' + part + '"');
}
name = matchedName[0];
keys = [];
if (matchedKeys === null) {
object[name] = value;
continue;
}
for (j = 0 , subLn = matchedKeys.length; j < subLn; j++) {
key = matchedKeys[j];
key = (key.length === 2) ? '' : key.substring(1, key.length - 1);
keys.push(key);
}
keys.unshift(name);
temp = object;
for (j = 0 , subLn = keys.length; j < subLn; j++) {
key = keys[j];
if (j === subLn - 1) {
if (Ext.isArray(temp) && key === '') {
temp.push(value);
} else {
temp[key] = value;
}
} else {
if (temp[key] === undefined || typeof temp[key] === 'string') {
nextKey = keys[j + 1];
temp[key] = (Ext.isNumeric(nextKey) || nextKey === '') ? [] : {};
}
temp = temp[key];
}
}
}
}
}
return object;
},
/**
* Iterates through an object and invokes the given callback function for each iteration.
* The iteration can be stopped by returning `false` in the callback function. For example:
*
* var person = {
* name: 'Jacky'
* hairColor: 'black'
* loves: ['food', 'sleeping', 'wife']
* };
*
* Ext.Object.each(person, function(key, value, myself) {
* console.log(key + ":" + value);
*
* if (key === 'hairColor') {
* return false; // stop the iteration
* }
* });
*
* @param {Object} object The object to iterate
* @param {Function} fn The callback function.
* @param {String} fn.key
* @param {Object} fn.value
* @param {Object} fn.object The object itself
* @param {Object} [scope] The execution scope (`this`) of the callback function
*/
each: function(object, fn, scope) {
var enumerables = Ext.enumerables,
i, property;
if (object) {
scope = scope || object;
for (property in object) {
if (object.hasOwnProperty(property)) {
if (fn.call(scope, property, object[property], object) === false) {
return;
}
}
}
if (enumerables) {
for (i = enumerables.length; i--; ) {
if (object.hasOwnProperty(property = enumerables[i])) {
if (fn.call(scope, property, object[property], object) === false) {
return;
}
}
}
}
}
},
/**
* Iterates through an object and invokes the given callback function for each iteration.
* The iteration can be stopped by returning `false` in the callback function. For example:
*
* var items = {
* 1: 'Hello',
* 2: 'World'
* };
*
* Ext.Object.eachValue(items, function (value) {
* console.log("Value: " + value);
* });
*
* This will log 'Hello' and 'World' in no particular order. This method is useful
* in cases where the keys are not important to the processing, just the values.
*
* @param {Object} object The object to iterate
* @param {Function} fn The callback function.
* @param {Object} fn.value The value of
* @param {Object} [scope] The execution scope (`this`) of the callback function
*/
eachValue: function(object, fn, scope) {
var enumerables = Ext.enumerables,
i, property;
scope = scope || object;
for (property in object) {
if (object.hasOwnProperty(property)) {
if (fn.call(scope, object[property]) === false) {
return;
}
}
}
if (enumerables) {
for (i = enumerables.length; i--; ) {
if (object.hasOwnProperty(property = enumerables[i])) {
if (fn.call(scope, object[property]) === false) {
return;
}
}
}
}
},
/**
* Merges any number of objects recursively without referencing them or their children.
*
* var extjs = {
* companyName: 'Ext JS',
* products: ['Ext JS', 'Ext GWT', 'Ext Designer'],
* isSuperCool: true,
* office: {
* size: 2000,
* location: 'Palo Alto',
* isFun: true
* }
* };
*
* var newStuff = {
* companyName: 'Sencha Inc.',
* products: ['Ext JS', 'Ext GWT', 'Ext Designer', 'Sencha Touch', 'Sencha Animator'],
* office: {
* size: 40000,
* location: 'Redwood City'
* }
* };
*
* var sencha = Ext.Object.merge(extjs, newStuff);
*
* // extjs and sencha then equals to
* {
* companyName: 'Sencha Inc.',
* products: ['Ext JS', 'Ext GWT', 'Ext Designer', 'Sencha Touch', 'Sencha Animator'],
* isSuperCool: true,
* office: {
* size: 40000,
* location: 'Redwood City',
* isFun: true
* }
* }
*
* @param {Object} destination The object into which all subsequent objects are merged.
* @param {Object...} object Any number of objects to merge into the destination.
* @return {Object} merged The destination object with all passed objects merged in.
*/
merge: function(destination) {
var i = 1,
ln = arguments.length,
mergeFn = ExtObject.merge,
cloneFn = Ext.clone,
object, key, value, sourceKey;
for (; i < ln; i++) {
object = arguments[i];
for (key in object) {
value = object[key];
if (value && value.constructor === Object) {
sourceKey = destination[key];
if (sourceKey && sourceKey.constructor === Object) {
mergeFn(sourceKey, value);
} else {
destination[key] = cloneFn(value);
}
} else {
destination[key] = value;
}
}
}
return destination;
},
/**
* @private
* @param destination
*/
mergeIf: function(destination) {
var i = 1,
ln = arguments.length,
cloneFn = Ext.clone,
object, key, value;
for (; i < ln; i++) {
object = arguments[i];
for (key in object) {
if (!(key in destination)) {
value = object[key];
if (value && value.constructor === Object) {
destination[key] = cloneFn(value);
} else {
destination[key] = value;
}
}
}
}
return destination;
},
/**
* Returns all keys of the given object as an array.
*
* @param {Object} object
* @return {String[]} An array of keys from the object or any of its prototypes.
* @method
*/
getAllKeys: function(object) {
var keys = [],
property;
for (property in object) {
keys.push(property);
}
return keys;
},
/**
* Returns the first matching key corresponding to the given value.
* If no matching value is found, null is returned.
*
* var person = {
* name: 'Jacky',
* loves: 'food'
* };
*
* alert(Ext.Object.getKey(person, 'food')); // alerts 'loves'
*
* @param {Object} object
* @param {Object} value The value to find
*/
getKey: function(object, value) {
for (var property in object) {
if (object.hasOwnProperty(property) && object[property] === value) {
return property;
}
}
return null;
},
/**
* Gets all values of the given object as an array.
*
* var values = Ext.Object.getValues({
* name: 'Jacky',
* loves: 'food'
* }); // ['Jacky', 'food']
*
* @param {Object} object
* @return {Array} An array of values from the object
*/
getValues: function(object) {
var values = [],
property;
for (property in object) {
if (object.hasOwnProperty(property)) {
values.push(object[property]);
}
}
return values;
},
/**
* Returns the `hasOwnProperty` keys of the given object as an array.
*
* var values = Ext.Object.getKeys({
* name: 'Jacky',
* loves: 'food'
* }); // ['name', 'loves']
*
* @param {Object} object
* @return {String[]} An array of keys from the object
* @method
*/
getKeys: (typeof Object.keys == 'function') ? function(object) {
if (!object) {
return [];
}
return Object.keys(object);
} : function(object) {
var keys = [],
property;
for (property in object) {
if (object.hasOwnProperty(property)) {
keys.push(property);
}
}
return keys;
},
/**
* Gets the total number of this object's own properties
*
* var size = Ext.Object.getSize({
* name: 'Jacky',
* loves: 'food'
* }); // size equals 2
*
* @param {Object} object
* @return {Number} size
*/
getSize: function(object) {
var size = 0,
property;
for (property in object) {
if (object.hasOwnProperty(property)) {
size++;
}
}
return size;
},
/**
* Checks if there are any properties on this object.
* @param {Object} object
* @return {Boolean} `true` if there no properties on the object.
*/
isEmpty: function(object) {
for (var key in object) {
if (object.hasOwnProperty(key)) {
return false;
}
}
return true;
},
/**
* @method
* Shallow compares the contents of 2 objects using strict equality. Objects are
* considered equal if they both have the same set of properties and the
* value for those properties equals the other in the corresponding object.
*
* // Returns true
* Ext.Object.equals({
* foo: 1,
* bar: 2
* }, {
* foo: 1,
* bar: 2
* });
*
* @param {Object} object1
* @param {Object} object2
* @return {Boolean} `true` if the objects are equal.
*/
equals: (function() {
var check = function(o1, o2) {
var key;
for (key in o1) {
if (o1.hasOwnProperty(key)) {
if (o1[key] !== o2[key]) {
return false;
}
}
}
return true;
};
return function(object1, object2) {
// Short circuit if the same object is passed twice
if (object1 === object2) {
return true;
}
if (object1 && object2) {
// Do the second check because we could have extra keys in
// object2 that don't exist in object1.
return check(object1, object2) && check(object2, object1);
} else if (!object1 && !object2) {
return object1 === object2;
} else {
return false;
}
};
})(),
/**
* @private
*/
fork: function(obj) {
var ret, key, value;
if (obj && obj.constructor === Object) {
ret = ExtObject.chain(obj);
for (key in obj) {
value = obj[key];
if (value) {
if (value.constructor === Object) {
ret[key] = ExtObject.fork(value);
} else if (value instanceof Array) {
ret[key] = Ext.Array.clone(value);
}
}
}
} else {
ret = obj;
}
return ret;
},
defineProperty: ('defineProperty' in Object) ? Object.defineProperty : function(object, name, descriptor) {
if (!Object.prototype.__defineGetter__) {
return;
}
if (descriptor.get) {
object.__defineGetter__(name, descriptor.get);
}
if (descriptor.set) {
object.__defineSetter__(name, descriptor.set);
}
},
/**
* @private
*/
classify: function(object) {
var prototype = object,
objectProperties = [],
propertyClassesMap = {},
objectClass = function() {
var i = 0,
ln = objectProperties.length,
property;
for (; i < ln; i++) {
property = objectProperties[i];
this[property] = new propertyClassesMap[property]();
}
},
key, value;
for (key in object) {
if (object.hasOwnProperty(key)) {
value = object[key];
if (value && value.constructor === Object) {
objectProperties.push(key);
propertyClassesMap[key] = ExtObject.classify(value);
}
}
}
objectClass.prototype = prototype;
return objectClass;
}
};
/**
* A convenient alias method for {@link Ext.Object#merge}.
*
* @member Ext
* @method merge
* @inheritdoc Ext.Object#merge
*/
Ext.merge = Ext.Object.merge;
/**
* @private
* @member Ext
*/
Ext.mergeIf = Ext.Object.mergeIf;
}());
/*
* This file contains miscellaneous utility methods that depends on various helper classes
* like `Ext.Array` and `Ext.Date`. Historically these methods were defined in Ext.js or
* Ext-more.js but that creates circular dependencies so they were consolidated here.
*/
Ext.apply(Ext, {
// @define Ext.Util
// @require Ext
// @require Ext.lang.*
// shortcut for the special named scopes for listener scope resolution
_namedScopes: {
'this': {
isThis: 1
},
controller: {
isController: 1
},
// these two are private, used to indicate that listeners were declared on the
// class body with either an unspecified scope, or scope:'controller'
self: {
isSelf: 1
},
'self.controller': {
isSelf: 1,
isController: 1
}
},
escapeId: (function() {
var validIdRe = /^[a-zA-Z_][a-zA-Z0-9_\-]*$/i,
escapeRx = /([\W]{1})/g,
leadingNumRx = /^(\d)/g,
escapeFn = function(match, capture) {
return "\\" + capture;
},
numEscapeFn = function(match, capture) {
return '\\00' + capture.charCodeAt(0).toString(16) + ' ';
};
return function(id) {
return validIdRe.test(id) ? id : // replace the number portion last to keep the trailing ' '
// from being escaped
id.replace(escapeRx, escapeFn).replace(leadingNumRx, numEscapeFn);
};
}()),
/**
* @method callback
* @member Ext
* Execute a callback function in a particular scope. If `callback` argument is a
* function reference, that is called. If it is a string, the string is assumed to
* be the name of a method on the given `scope`. If no function is passed the call
* is ignored.
*
* For example, these calls are equivalent:
*
* var myFunc = this.myFunc;
*
* Ext.callback('myFunc', this, [arg1, arg2]);
* Ext.callback(myFunc, this, [arg1, arg2]);
*
* Ext.isFunction(myFunc) && this.myFunc(arg1, arg2);
*
* @param {Function/String} callback The callback function to execute or the name of
* the callback method on the provided `scope`.
* @param {Object} [scope] The scope in which `callback` should be invoked. If `callback`
* is a string this object provides the method by that name. If this is `null` then
* the `caller` is used to resolve the scope to a `ViewController` or the proper
* `defaultListenerScope`.
* @param {Array} [args] The arguments to pass to the function.
* @param {Number} [delay] Pass a number to delay the call by a number of milliseconds.
* @param {Object} [caller] The object calling the callback. This is used to resolve
* named methods when no explicit `scope` is provided.
* @param {Object} [defaultScope=caller] The default scope to return if none is found.
* @return The value returned by the callback or `undefined` (if there is a `delay`
* or if the `callback` is not a function).
*/
callback: function(callback, scope, args, delay, caller, defaultScope) {
if (!callback) {
return;
}
var namedScope = (scope in Ext._namedScopes);
if (callback.charAt) {
// if (isString(fn))
if ((!scope || namedScope) && caller) {
scope = caller.resolveListenerScope(namedScope ? scope : defaultScope);
}
if (!scope || !Ext.isObject(scope)) {
Ext.raise('Named method "' + callback + '" requires a scope object');
}
if (!Ext.isFunction(scope[callback])) {
Ext.raise('No method named "' + callback + '" on ' + (scope.$className || 'scope object'));
}
callback = scope[callback];
} else if (namedScope) {
scope = defaultScope || caller;
} else if (!scope) {
scope = caller;
}
var ret;
if (callback && Ext.isFunction(callback)) {
scope = scope || Ext.global;
if (delay) {
Ext.defer(callback, delay, scope, args);
} else if (Ext.elevateFunction) {
ret = Ext.elevateFunction(callback, scope, args);
} else if (args) {
ret = callback.apply(scope, args);
} else {
ret = callback.call(scope);
}
}
return ret;
},
/**
* @method coerce
* @member Ext
* Coerces the first value if possible so that it is comparable to the second value.
*
* Coercion only works between the basic atomic data types String, Boolean, Number, Date, null and undefined.
*
* Numbers and numeric strings are coerced to Dates using the value as the millisecond era value.
*
* Strings are coerced to Dates by parsing using the {@link Ext.Date#defaultFormat defaultFormat}.
*
* For example
*
* Ext.coerce('false', true);
*
* returns the boolean value `false` because the second parameter is of type `Boolean`.
*
* @param {Mixed} from The value to coerce
* @param {Mixed} to The value it must be compared against
* @return The coerced value.
*/
coerce: function(from, to) {
var fromType = Ext.typeOf(from),
toType = Ext.typeOf(to),
isString = typeof from === 'string';
if (fromType !== toType) {
switch (toType) {
case 'string':
return String(from);
case 'number':
return Number(from);
case 'boolean':
// See http://ecma262-5.com/ELS5_HTML.htm#Section_11.9.3 as to why '0'.
// TL;DR => ('0' == 0), so if given string '0', we must return boolean false.
return isString && (!from || from === 'false' || from === '0') ? false : Boolean(from);
case 'null':
return isString && (!from || from === 'null') ? null : false;
case 'undefined':
return isString && (!from || from === 'undefined') ? undefined : false;
case 'date':
return isString && isNaN(from) ? Ext.Date.parse(from, Ext.Date.defaultFormat) : Date(Number(from));
}
}
return from;
},
/**
* @method copyTo
* @member Ext
* Copies a set of named properties fom the source object to the destination object.
*
* Example:
*
* var foo = { a: 1, b: 2, c: 3 };
*
* var bar = Ext.copyTo({}, foo, 'a,c');
* // bar = { a: 1, c: 3 };
*
* Important note: To borrow class prototype methods, use {@link Ext.Base#borrow} instead.
*
* @param {Object} dest The destination object.
* @param {Object} source The source object.
* @param {String/String[]} names Either an Array of property names, or a comma-delimited list
* of property names to copy.
* @param {Boolean} [usePrototypeKeys=false] Pass `true` to copy keys off of the
* prototype as well as the instance.
* @return {Object} The `dest` object.
* @deprecated 6.0.1 Use {@link Ext#copy Ext.copy} instead. This old method
* would copy the named preoperties even if they did not exist in the source which
* could produce `undefined` values in the destination.
*/
copyTo: function(dest, source, names, usePrototypeKeys) {
if (typeof names === 'string') {
names = names.split(Ext.propertyNameSplitRe);
}
for (var name,
i = 0,
n = names ? names.length : 0; i < n; i++) {
name = names[i];
if (usePrototypeKeys || source.hasOwnProperty(name)) {
dest[name] = source[name];
}
}
return dest;
},
/**
* @method copy
* @member Ext
* Copies a set of named properties fom the source object to the destination object.
*
* Example:
*
* var foo = { a: 1, b: 2, c: 3 };
*
* var bar = Ext.copy({}, foo, 'a,c');
* // bar = { a: 1, c: 3 };
*
* Important note: To borrow class prototype methods, use {@link Ext.Base#borrow} instead.
*
* @param {Object} dest The destination object.
* @param {Object} source The source object.
* @param {String/String[]} names Either an Array of property names, or a comma-delimited list
* of property names to copy.
* @param {Boolean} [usePrototypeKeys=false] Pass `true` to copy keys off of the
* prototype as well as the instance.
* @return {Object} The `dest` object.
*/
copy: function(dest, source, names, usePrototypeKeys) {
if (typeof names === 'string') {
names = names.split(Ext.propertyNameSplitRe);
}
for (var name,
i = 0,
n = names ? names.length : 0; i < n; i++) {
name = names[i];
// Only copy a property if the source actually *has* that property.
// If we are including prototype properties, then ensure that a property of
// that name can be found *somewhere* in the prototype chain (otherwise we'd be copying undefined in which may break things)
if (source.hasOwnProperty(name) || (usePrototypeKeys && name in source)) {
dest[name] = source[name];
}
}
return dest;
},
propertyNameSplitRe: /[,;\s]+/,
/**
* @method copyToIf
* @member Ext
* Copies a set of named properties fom the source object to the destination object
* if the destination object does not already have them.
*
* Example:
*
* var foo = { a: 1, b: 2, c: 3 };
*
* var bar = Ext.copyToIf({ a:42 }, foo, 'a,c');
* // bar = { a: 42, c: 3 };
*
* @param {Object} destination The destination object.
* @param {Object} source The source object.
* @param {String/String[]} names Either an Array of property names, or a single string
* with a list of property names separated by ",", ";" or spaces.
* @return {Object} The `dest` object.
* @deprecated 6.0.1 Use {@link Ext#copyIf Ext.copyIf} instead. This old method
* would copy the named preoperties even if they did not exist in the source which
* could produce `undefined` values in the destination.
*/
copyToIf: function(destination, source, names) {
if (typeof names === 'string') {
names = names.split(Ext.propertyNameSplitRe);
}
for (var name,
i = 0,
n = names ? names.length : 0; i < n; i++) {
name = names[i];
if (destination[name] === undefined) {
destination[name] = source[name];
}
}
return destination;
},
/**
* @method copyIf
* @member Ext
* Copies a set of named properties fom the source object to the destination object
* if the destination object does not already have them.
*
* Example:
*
* var foo = { a: 1, b: 2, c: 3 };
*
* var bar = Ext.copyIf({ a:42 }, foo, 'a,c');
* // bar = { a: 42, c: 3 };
*
* @param {Object} destination The destination object.
* @param {Object} source The source object.
* @param {String/String[]} names Either an Array of property names, or a single string
* with a list of property names separated by ",", ";" or spaces.
* @return {Object} The `dest` object.
*/
copyIf: function(destination, source, names) {
if (typeof names === 'string') {
names = names.split(Ext.propertyNameSplitRe);
}
for (var name,
i = 0,
n = names ? names.length : 0; i < n; i++) {
name = names[i];
// Only copy a property if the destination has no property by that name
if (!(name in destination) && (name in source)) {
destination[name] = source[name];
}
}
return destination;
},
/**
* @method extend
* @member Ext
* This method deprecated. Use {@link Ext#define Ext.define} instead.
* @param {Function} superclass
* @param {Object} overrides
* @return {Function} The subclass constructor from the <tt>overrides</tt> parameter, or a generated one if not provided.
* @deprecated 4.0.0 Use {@link Ext#define Ext.define} instead
*/
extend: (function() {
// inline overrides
var objectConstructor = Object.prototype.constructor,
inlineOverrides = function(o) {
var m;
for (m in o) {
if (!o.hasOwnProperty(m)) {
continue;
}
this[m] = o[m];
}
};
return function(subclass, superclass, overrides) {
// First we check if the user passed in just the superClass with overrides
if (Ext.isObject(superclass)) {
overrides = superclass;
superclass = subclass;
subclass = overrides.constructor !== objectConstructor ? overrides.constructor : function() {
superclass.apply(this, arguments);
};
}
if (!superclass) {
Ext.raise({
sourceClass: 'Ext',
sourceMethod: 'extend',
msg: 'Attempting to extend from a class which has not been loaded on the page.'
});
}
// We create a new temporary class
var F = function() {},
subclassProto,
superclassProto = superclass.prototype;
F.prototype = superclassProto;
subclassProto = subclass.prototype = new F();
subclassProto.constructor = subclass;
subclass.superclass = superclassProto;
if (superclassProto.constructor === objectConstructor) {
superclassProto.constructor = superclass;
}
subclass.override = function(overrides) {
Ext.override(subclass, overrides);
};
subclassProto.override = inlineOverrides;
subclassProto.proto = subclassProto;
subclass.override(overrides);
subclass.extend = function(o) {
return Ext.extend(subclass, o);
};
return subclass;
};
}()),
/**
* @method iterate
* @member Ext
* Iterates either an array or an object. This method delegates to
* {@link Ext.Array#each Ext.Array.each} if the given value is iterable, and {@link Ext.Object#each Ext.Object.each} otherwise.
*
* @param {Object/Array} object The object or array to be iterated.
* @param {Function} fn The function to be called for each iteration. See and {@link Ext.Array#each Ext.Array.each} and
* {@link Ext.Object#each Ext.Object.each} for detailed lists of arguments passed to this function depending on the given object
* type that is being iterated.
* @param {Object} [scope] The scope (`this` reference) in which the specified function is executed.
* Defaults to the object being iterated itself.
*/
iterate: function(object, fn, scope) {
if (Ext.isEmpty(object)) {
return;
}
if (scope === undefined) {
scope = object;
}
if (Ext.isIterable(object)) {
Ext.Array.each.call(Ext.Array, object, fn, scope);
} else {
Ext.Object.each.call(Ext.Object, object, fn, scope);
}
},
_resourcePoolRe: /^[<]([^<>@:]*)(?:[@]([^<>@:]+))?[>](.+)$/,
/**
* Resolves a resource URL that may contain a resource pool identifier token at the
* front. The tokens are formatted as HTML tags "&lt;poolName@packageName&gt;" followed
* by a normal relative path. This token is only processed if present at the first
* character of the given string.
*
* These tokens are parsed and the pieces are then passed to the
* {@link Ext#getResourcePath} method.
*
* For example:
*
* [{
* xtype: 'image',
* src: '<shared>images/foo.png'
* },{
* xtype: 'image',
* src: '<@package>images/foo.png'
* },{
* xtype: 'image',
* src: '<shared@package>images/foo.png'
* }]
*
* In the above example, "shared" is the name of a Sencha Cmd resource pool and
* "package" is the name of a Sencha Cmd package.
* @member Ext
* @param {String} url The URL that may contain a resource pool token at the front.
* @return {String}
* @since 6.0.1
*/
resolveResource: function(url) {
var ret = url,
m;
if (url && url.charAt(0) === '<') {
m = Ext._resourcePoolRe.exec(url);
if (m) {
ret = Ext.getResourcePath(m[3], m[1], m[2]);
}
}
return ret;
},
/**
*
* @member Ext
* @method urlEncode
* @inheritdoc Ext.Object#toQueryString
* @deprecated 4.0.0 Use {@link Ext.Object#toQueryString} instead
*/
urlEncode: function() {
var args = Ext.Array.from(arguments),
prefix = '';
// Support for the old `pre` argument
if (Ext.isString(args[1])) {
prefix = args[1] + '&';
args[1] = false;
}
return prefix + Ext.Object.toQueryString.apply(Ext.Object, args);
},
/**
* Alias for {@link Ext.Object#fromQueryString}.
*
* @member Ext
* @method urlDecode
* @inheritdoc Ext.Object#fromQueryString
* @deprecated 4.0.0 Use {@link Ext.Object#fromQueryString} instead
*/
urlDecode: function() {
return Ext.Object.fromQueryString.apply(Ext.Object, arguments);
},
/**
* @method getScrollbarSize
* @member Ext
* Returns the size of the browser scrollbars. This can differ depending on
* operating system settings, such as the theme or font size.
* @param {Boolean} [force] true to force a recalculation of the value.
* @return {Object} An object containing scrollbar sizes.
* @return {Number} return.width The width of the vertical scrollbar.
* @return {Number} return.height The height of the horizontal scrollbar.
*/
getScrollbarSize: function(force) {
if (!Ext.isDomReady) {
Ext.raise("getScrollbarSize called before DomReady");
}
var scrollbarSize = Ext._scrollbarSize;
if (force || !scrollbarSize) {
var db = document.body,
div = document.createElement('div');
div.style.width = div.style.height = '100px';
div.style.overflow = 'scroll';
div.style.position = 'absolute';
db.appendChild(div);
// now we can measure the div...
// at least in iE9 the div is not 100px - the scrollbar size is removed!
Ext._scrollbarSize = scrollbarSize = {
width: div.offsetWidth - div.clientWidth,
height: div.offsetHeight - div.clientHeight
};
db.removeChild(div);
}
return scrollbarSize;
},
/**
* @method typeOf
* @member Ext
* Returns the type of the given variable in string format. List of possible values are:
*
* - `undefined`: If the given value is `undefined`
* - `null`: If the given value is `null`
* - `string`: If the given value is a string
* - `number`: If the given value is a number
* - `boolean`: If the given value is a boolean value
* - `date`: If the given value is a `Date` object
* - `function`: If the given value is a function reference
* - `object`: If the given value is an object
* - `array`: If the given value is an array
* - `regexp`: If the given value is a regular expression
* - `element`: If the given value is a DOM Element
* - `textnode`: If the given value is a DOM text node and contains something other than whitespace
* - `whitespace`: If the given value is a DOM text node and contains only whitespace
*
* @param {Object} value
* @return {String}
*/
typeOf: (function() {
var nonWhitespaceRe = /\S/,
toString = Object.prototype.toString,
typeofTypes = {
number: 1,
string: 1,
'boolean': 1,
'undefined': 1
},
toStringTypes = {
'[object Array]': 'array',
'[object Date]': 'date',
'[object Boolean]': 'boolean',
'[object Number]': 'number',
'[object RegExp]': 'regexp'
};
return function(value) {
if (value === null) {
return 'null';
}
var type = typeof value,
ret, typeToString;
if (typeofTypes[type]) {
return type;
}
ret = toStringTypes[typeToString = toString.call(value)];
if (ret) {
return ret;
}
if (type === 'function') {
return 'function';
}
if (type === 'object') {
if (value.nodeType !== undefined) {
if (value.nodeType === 3) {
return nonWhitespaceRe.test(value.nodeValue) ? 'textnode' : 'whitespace';
} else {
return 'element';
}
}
return 'object';
}
Ext.raise({
sourceClass: 'Ext',
sourceMethod: 'typeOf',
msg: 'Failed to determine the type of "' + value + '".'
});
return typeToString;
};
}()),
/**
* A global factory method to instantiate a class from a config object. For example,
* these two calls are equivalent:
*
* Ext.factory({ text: 'My Button' }, 'Ext.Button');
* Ext.create('Ext.Button', { text: 'My Button' });
*
* If an existing instance is also specified, it will be updated with the supplied config object. This is useful
* if you need to either create or update an object, depending on if an instance already exists. For example:
*
* var button;
* button = Ext.factory({ text: 'New Button' }, 'Ext.Button', button); // Button created
* button = Ext.factory({ text: 'Updated Button' }, 'Ext.Button', button); // Button updated
*
* @param {Object} config The config object to instantiate or update an instance with.
* @param {String} [classReference] The class to instantiate from (if there is a default).
* @param {Object} [instance] The instance to update.
* @param [aliasNamespace]
* @member Ext
*/
factory: function(config, classReference, instance, aliasNamespace) {
var manager = Ext.ClassManager,
newInstance;
// If config is falsy or a valid instance, destroy the current instance
// (if it exists) and replace with the new one
if (!config || config.isInstance) {
if (instance && instance !== config) {
instance.destroy();
}
return config;
}
if (aliasNamespace) {
// If config is a string value, treat it as an alias
if (typeof config === 'string') {
return manager.instantiateByAlias(aliasNamespace + '.' + config);
}
// Same if 'type' is given in config
else if (Ext.isObject(config) && 'type' in config) {
return manager.instantiateByAlias(aliasNamespace + '.' + config.type, config);
}
}
if (config === true) {
if (!instance && !classReference) {
Ext.raise('[Ext.factory] Cannot determine type of class to create');
}
return instance || Ext.create(classReference);
}
if (!Ext.isObject(config)) {
Ext.raise("Invalid config, must be a valid config object");
}
if ('xtype' in config) {
newInstance = manager.instantiateByAlias('widget.' + config.xtype, config);
} else if ('xclass' in config) {
newInstance = Ext.create(config.xclass, config);
}
if (newInstance) {
if (instance) {
instance.destroy();
}
return newInstance;
}
if (instance) {
return instance.setConfig(config);
}
return Ext.create(classReference, config);
},
/**
* @method log
* @member Ext
* Logs a message. If a console is present it will be used. On Opera, the method
* "opera.postError" is called. In other cases, the message is logged to an array
* "Ext.log.out". An attached debugger can watch this array and view the log. The
* log buffer is limited to a maximum of "Ext.log.max" entries (defaults to 250).
*
* If additional parameters are passed, they are joined and appended to the message.
* A technique for tracing entry and exit of a function is this:
*
* function foo () {
* Ext.log({ indent: 1 }, '>> foo');
*
* // log statements in here or methods called from here will be indented
* // by one step
*
* Ext.log({ outdent: 1 }, '<< foo');
* }
*
* This method does nothing in a release build.
*
* @param {String/Object} [options] The message to log or an options object with any
* of the following properties:
*
* - `msg`: The message to log (required).
* - `level`: One of: "error", "warn", "info" or "log" (the default is "log").
* - `dump`: An object to dump to the log as part of the message.
* - `stack`: True to include a stack trace in the log.
* - `indent`: Cause subsequent log statements to be indented one step.
* - `outdent`: Cause this and following statements to be one step less indented.
*
* @param {String...} [message] The message to log (required unless specified in
* options object).
*/
log: (function() {
/*
* Iterate through an object to dump its content into a string.
* For example:
* {
* style: {
* lineWidth: 1
* },
* label: {},
* marker: {
* strokeStyle: "#555",
* radius: 3,
* size: 3
* },
* subStyle: {
* fillStyle: [
* 0: "#133987",
* 1: "#1c55ca",
* 2: "#4d7fe6"
* ]
* },
* markerSubStyle: {}
* }
*
* @param {Object} object The object to iterate
* @param {Number} [level] Current level of identation (and recursion). Default is 0.
* @param {Number} [maxLevel] Maximum level of recursion. Default is 3.
* @param {Boolean} [withFunctions] Include functions in the output.
* @return {String} The string with the contents of the object
*/
var primitiveRe = /string|number|boolean/;
function dumpObject(object, level, maxLevel, withFunctions) {
var member, type, value, name, prefix, suffix,
members = [];
if (Ext.isArray(object)) {
prefix = '[';
suffix = ']';
} else if (Ext.isObject(object)) {
prefix = '{';
suffix = '}';
}
if (!maxLevel) {
maxLevel = 3;
}
if (level > maxLevel) {
return prefix + '...' + suffix;
}
level = level || 1;
var spacer = (new Array(level)).join(' ');
// Cannot use Ext.encode since it can recurse endlessly
for (name in object) {
if (object.hasOwnProperty(name)) {
value = object[name];
type = typeof value;
if (type === 'function') {
if (!withFunctions) {
continue;
}
member = type;
} else if (type === 'undefined') {
member = type;
} else if (value === null || primitiveRe.test(type) || Ext.isDate(value)) {
member = Ext.encode(value);
} else if (Ext.isArray(value)) {
member = dumpObject(value, level + 1, maxLevel, withFunctions);
} else if (Ext.isObject(value)) {
member = dumpObject(value, level + 1, maxLevel, withFunctions);
} else {
member = type;
}
members.push(spacer + name + ': ' + member);
}
}
// or Ext.encode(name)
if (members.length) {
return prefix + '\n ' + members.join(',\n ') + '\n' + spacer + suffix;
}
return prefix + suffix;
}
function log(message) {
var options, dump,
con = Ext.global.console,
level = 'log',
indent = log.indent || 0,
prefix, stack, fn, out, max;
log.indent = indent;
if (typeof message !== 'string') {
options = message;
message = options.msg || '';
level = options.level || level;
dump = options.dump;
stack = options.stack;
prefix = options.prefix;
fn = options.fn;
if (options.indent) {
++log.indent;
} else if (options.outdent) {
log.indent = indent = Math.max(indent - 1, 0);
}
if (dump && !(con && con.dir)) {
message += dumpObject(dump);
dump = null;
}
}
if (arguments.length > 1) {
message += Array.prototype.slice.call(arguments, 1).join('');
}
if (prefix) {
message = prefix + ' - ' + message;
}
message = indent ? Ext.String.repeat(' ', log.indentSize * indent) + message : message;
// w/o console, all messages are equal, so munge the level into the message:
if (level !== 'log') {
message = '[' + level.charAt(0).toUpperCase() + '] ' + message;
}
if (fn) {
message += '\nCaller: ' + fn.toString();
}
// Not obvious, but 'console' comes and goes when Firebug is turned on/off, so
// an early test may fail either direction if Firebug is toggled.
//
if (con) {
// if (Firebug-like console)
if (con[level]) {
con[level](message);
} else {
con.log(message);
}
if (dump) {
con.dir(dump);
}
if (stack && con.trace) {
// Firebug's console.error() includes a trace already...
if (!con.firebug || level !== 'error') {
con.trace();
}
}
} else if (Ext.isOpera) {
opera.postError(message);
} else // jshint ignore:line
{
out = log.out;
max = log.max;
if (out.length >= max) {
// this formula allows out.max to change (via debugger), where the
// more obvious "max/4" would not quite be the same
Ext.Array.erase(out, 0, out.length - 3 * Math.floor(max / 4));
}
// keep newest 75%
out.push(message);
}
// Mostly informational, but the Ext.Error notifier uses them:
++log.count;
++log.counters[level];
}
function logx(level, args) {
if (typeof args[0] === 'string') {
args.unshift({});
}
args[0].level = level;
log.apply(this, args);
}
log.error = function() {
logx('error', Array.prototype.slice.call(arguments));
};
log.info = function() {
logx('info', Array.prototype.slice.call(arguments));
};
log.warn = function() {
logx('warn', Array.prototype.slice.call(arguments));
};
log.count = 0;
log.counters = {
error: 0,
warn: 0,
info: 0,
log: 0
};
log.indentSize = 2;
log.out = [];
log.max = 750;
return log;
}()) || (function() {
var nullLog = function() {};
nullLog.info = nullLog.warn = nullLog.error = Ext.emptyFn;
return nullLog;
}())
});
/**
* @class Ext.Version
*
* A utility class that wraps around a version number string and provides convenient methods
* to perform comparisons. A version number is expressed in the following general format:
*
* major[.minor[.patch[.build[release]]]]
*
* The `Version` instance holds various readonly properties that contain the digested form
* of the version string. The numeric componnets of `major`, `minor`, `patch` and `build`
* as well as the textual suffix called `release`.
*
* Not depicted in the above syntax are three possible prefixes used to control partial
* matching. These are '^' (the default), '>' and '~'. These are discussed below.
*
* Examples:
*
* var version = new Ext.Version('1.0.2beta'); // or maybe "1.0" or "1.2.3.4RC"
* console.log("Version is " + version); // Version is 1.0.2beta
*
* console.log(version.getMajor()); // 1
* console.log(version.getMinor()); // 0
* console.log(version.getPatch()); // 2
* console.log(version.getBuild()); // 0
* console.log(version.getRelease()); // beta
*
* The understood values of `release` are assigned numberic equivalents for the sake of
* comparsion. The order of these from smallest to largest is as follows:
*
* * `"dev"`
* * `"alpha"` or `"a"`
* * `"beta"` or `"b"`
* * `"RC"` or `"rc"`
* * `"#"`
* * `"pl"` or `"p"`
*
* Any other (unrecognized) suffix is consider greater than any of these.
*
* ## Comparisons
* There are two forms of comparison that are commonly needed: full and partial. Full
* comparison is simpler and is also the default.
*
* Example:
*
* var version = new Ext.Version('1.0.2beta');
*
* console.log(version.isGreaterThan('1.0.1')); // True
* console.log(version.isGreaterThan('1.0.2alpha')); // True
* console.log(version.isGreaterThan('1.0.2RC')); // False
* console.log(version.isGreaterThan('1.0.2')); // False
* console.log(version.isLessThan('1.0.2')); // True
*
* console.log(version.match(1.0)); // True (using a Number)
* console.log(version.match('1.0.2')); // True (using a String)
*
* These comparisons are ultimately implemented by {@link Ext.Version#compareTo compareTo}
* which returns -1, 0 or 1 depending on whether the `Version' instance is less than, equal
* to, or greater than the given "other" version.
*
* For example:
*
* var n = version.compareTo('1.0.1'); // == 1 (because 1.0.2beta > 1.0.1)
*
* n = version.compareTo('1.1'); // == -1
* n = version.compareTo(version); // == 0
*
* ### Partial Comparisons
* By default, unspecified version number fields are filled with 0. In other words, the
* version number fields are 0-padded on the right or a "lower bound". This produces the
* most commonly used forms of comparsion:
*
* var ver = new Version('4.2');
*
* n = ver.compareTo('4.2.1'); // == -1 (4.2 promotes to 4.2.0 and is less than 4.2.1)
*
* There are two other ways to interpret comparisons of versions of different length. The
* first of these is to change the padding on the right to be a large number (scuh as
* Infinity) instead of 0. This has the effect of making the version an upper bound. For
* example:
*
* var ver = new Version('^4.2'); // NOTE: the '^' prefix used
*
* n = ver.compareTo('4.3'); // == -1 (less than 4.3)
*
* n = ver.compareTo('4.2'); // == 1 (greater than all 4.2's)
* n = ver.compareTo('4.2.1'); // == 1
* n = ver.compareTo('4.2.9'); // == 1
*
* The second way to interpret this comparison is to ignore the extra digits, making the
* match a prefix match. For example:
*
* var ver = new Version('~4.2'); // NOTE: the '~' prefix used
*
* n = ver.compareTo('4.3'); // == -1
*
* n = ver.compareTo('4.2'); // == 0
* n = ver.compareTo('4.2.1'); // == 0
*
* This final form can be useful when version numbers contain more components than are
* important for certain comparisons. For example, the full version of Ext JS 4.2.1 is
* "4.2.1.883" where 883 is the `build` number.
*
* This is how to create a "partial" `Version` and compare versions to it:
*
* var version421ish = new Version('~4.2.1');
*
* n = version421ish.compareTo('4.2.1.883'); // == 0
* n = version421ish.compareTo('4.2.1.2'); // == 0
* n = version421ish.compareTo('4.2.1'); // == 0
*
* n = version421ish.compareTo('4.2'); // == 1
*
* In the above example, '4.2.1.2' compares as equal to '4.2.1' because digits beyond the
* given "4.2.1" are ignored. However, '4.2' is less than the '4.2.1' prefix; its missing
* digit is filled with 0.
*/
(function() {
// @define Ext.Version
// @require Ext.String
var // used by checkVersion to avoid temp arrays:
checkVerTemp = [
''
],
endOfVersionRe = /([^\d\.])/,
notDigitsRe = /[^\d]/g,
plusMinusRe = /[\-+]/g,
stripRe = /\s/g,
underscoreRe = /_/g,
toolkitNames = {
classic: 1,
modern: 1
},
Version;
Ext.Version = Version = function(version, defaultMode) {
var me = this,
padModes = me.padModes,
ch, i, pad, parts, release, releaseStartIndex, ver;
if (version.isVersion) {
version = version.version;
}
me.version = ver = String(version).toLowerCase().replace(underscoreRe, '.').replace(plusMinusRe, '');
ch = ver.charAt(0);
if (ch in padModes) {
ver = ver.substring(1);
pad = padModes[ch];
} else {
pad = defaultMode ? padModes[defaultMode] : 0;
}
// careful - NaN is falsey!
me.pad = pad;
releaseStartIndex = ver.search(endOfVersionRe);
me.shortVersion = ver;
if (releaseStartIndex !== -1) {
me.release = release = ver.substr(releaseStartIndex, version.length);
me.shortVersion = ver.substr(0, releaseStartIndex);
release = Version.releaseValueMap[release] || release;
}
me.releaseValue = release || pad;
me.shortVersion = me.shortVersion.replace(notDigitsRe, '');
/**
* @property {Number[]} parts
* The split array of version number components found in the version string.
* For example, for "1.2.3", this would be `[1, 2, 3]`.
* @readonly
* @private
*/
me.parts = parts = ver.split('.');
for (i = parts.length; i--; ) {
parts[i] = parseInt(parts[i], 10);
}
if (pad === Infinity) {
// have to add this to the end to create an upper bound:
parts.push(pad);
}
/**
* @property {Number} major
* The first numeric part of the version number string.
* @readonly
*/
me.major = parts[0] || pad;
/**
* @property {Number} [minor]
* The second numeric part of the version number string.
* @readonly
*/
me.minor = parts[1] || pad;
/**
* @property {Number} [patch]
* The third numeric part of the version number string.
* @readonly
*/
me.patch = parts[2] || pad;
/**
* @property {Number} [build]
* The fourth numeric part of the version number string.
* @readonly
*/
me.build = parts[3] || pad;
return me;
};
Version.prototype = {
isVersion: true,
padModes: {
'~': NaN,
'^': Infinity
},
/**
* @property {String} [release=""]
* The release level. The following values are understood:
*
* * `"dev"`
* * `"alpha"` or `"a"`
* * `"beta"` or `"b"`
* * `"RC"` or `"rc"`
* * `"#"`
* * `"pl"` or `"p"`
* @readonly
*/
release: '',
/**
* Compares this version instance to the specified `other` version.
*
* @param {String/Number/Ext.Version} other The other version to which to compare.
* @return {Number} -1 if this version is less than the target version, 1 if this
* version is greater, and 0 if they are equal.
*/
compareTo: function(other) {
// "lhs" == "left-hand-side"
// "rhs" == "right-hand-side"
var me = this,
lhsPad = me.pad,
lhsParts = me.parts,
lhsLength = lhsParts.length,
rhsVersion = other.isVersion ? other : new Version(other),
rhsPad = rhsVersion.pad,
rhsParts = rhsVersion.parts,
rhsLength = rhsParts.length,
length = Math.max(lhsLength, rhsLength),
i, lhs, rhs;
for (i = 0; i < length; i++) {
lhs = (i < lhsLength) ? lhsParts[i] : lhsPad;
rhs = (i < rhsLength) ? rhsParts[i] : rhsPad;
// When one or both of the values are NaN these tests produce false
// and we end up treating NaN as equal to anything.
if (lhs < rhs) {
return -1;
}
if (lhs > rhs) {
return 1;
}
}
// same comments about NaN apply here...
lhs = me.releaseValue;
rhs = rhsVersion.releaseValue;
if (lhs < rhs) {
return -1;
}
if (lhs > rhs) {
return 1;
}
return 0;
},
/**
* Override the native `toString` method
* @private
* @return {String} version
*/
toString: function() {
return this.version;
},
/**
* Override the native `valueOf` method
* @private
* @return {String} version
*/
valueOf: function() {
return this.version;
},
/**
* Returns the major component value.
* @return {Number}
*/
getMajor: function() {
return this.major;
},
/**
* Returns the minor component value.
* @return {Number}
*/
getMinor: function() {
return this.minor;
},
/**
* Returns the patch component value.
* @return {Number}
*/
getPatch: function() {
return this.patch;
},
/**
* Returns the build component value.
* @return {Number}
*/
getBuild: function() {
return this.build;
},
/**
* Returns the release component text (e.g., "beta").
* @return {String}
*/
getRelease: function() {
return this.release;
},
/**
* Returns the release component value for comparison purposes.
* @return {Number/String}
*/
getReleaseValue: function() {
return this.releaseValue;
},
/**
* Returns whether this version if greater than the supplied argument
* @param {String/Number} target The version to compare with
* @return {Boolean} `true` if this version if greater than the target, `false` otherwise
*/
isGreaterThan: function(target) {
return this.compareTo(target) > 0;
},
/**
* Returns whether this version if greater than or equal to the supplied argument
* @param {String/Number} target The version to compare with
* @return {Boolean} `true` if this version if greater than or equal to the target, `false` otherwise
*/
isGreaterThanOrEqual: function(target) {
return this.compareTo(target) >= 0;
},
/**
* Returns whether this version if smaller than the supplied argument
* @param {String/Number} target The version to compare with
* @return {Boolean} `true` if this version if smaller than the target, `false` otherwise
*/
isLessThan: function(target) {
return this.compareTo(target) < 0;
},
/**
* Returns whether this version if less than or equal to the supplied argument
* @param {String/Number} target The version to compare with
* @return {Boolean} `true` if this version if less than or equal to the target, `false` otherwise
*/
isLessThanOrEqual: function(target) {
return this.compareTo(target) <= 0;
},
/**
* Returns whether this version equals to the supplied argument
* @param {String/Number} target The version to compare with
* @return {Boolean} `true` if this version equals to the target, `false` otherwise
*/
equals: function(target) {
return this.compareTo(target) === 0;
},
/**
* Returns whether this version matches the supplied argument. Example:
*
* var version = new Ext.Version('1.0.2beta');
* console.log(version.match(1)); // true
* console.log(version.match(1.0)); // true
* console.log(version.match('1.0.2')); // true
* console.log(version.match('1.0.2RC')); // false
*
* @param {String/Number} target The version to compare with
* @return {Boolean} `true` if this version matches the target, `false` otherwise
*/
match: function(target) {
target = String(target);
return this.version.substr(0, target.length) === target;
},
/**
* Returns this format: [major, minor, patch, build, release]. Useful for comparison.
* @return {Number[]}
*/
toArray: function() {
var me = this;
return [
me.getMajor(),
me.getMinor(),
me.getPatch(),
me.getBuild(),
me.getRelease()
];
},
/**
* Returns shortVersion version without dots and release
* @return {String}
*/
getShortVersion: function() {
return this.shortVersion;
},
/**
* Convenient alias to {@link Ext.Version#isGreaterThan isGreaterThan}
* @param {String/Number/Ext.Version} target
* @return {Boolean}
*/
gt: function(target) {
return this.compareTo(target) > 0;
},
/**
* Convenient alias to {@link Ext.Version#isLessThan isLessThan}
* @param {String/Number/Ext.Version} target
* @return {Boolean}
*/
lt: function(target) {
return this.compareTo(target) < 0;
},
/**
* Convenient alias to {@link Ext.Version#isGreaterThanOrEqual isGreaterThanOrEqual}
* @param {String/Number/Ext.Version} target
* @return {Boolean}
*/
gtEq: function(target) {
return this.compareTo(target) >= 0;
},
/**
* Convenient alias to {@link Ext.Version#isLessThanOrEqual isLessThanOrEqual}
* @param {String/Number/Ext.Version} target
* @return {Boolean}
*/
ltEq: function(target) {
return this.compareTo(target) <= 0;
}
};
Ext.apply(Version, {
aliases: {
from: {
extjs: 'ext',
core: 'core',
touch: 'modern'
},
to: {
ext: [
'extjs'
],
'core': [
'core'
],
modern: [
'touch'
]
}
},
/**
* @private
*/
releaseValueMap: {
dev: -6,
alpha: -5,
a: -5,
beta: -4,
b: -4,
rc: -3,
'#': -2,
p: -1,
pl: -1
},
/**
* Converts a version component to a comparable value
*
* @static
* @param {Object} value The value to convert
* @return {Object}
*/
getComponentValue: function(value) {
return !value ? 0 : (isNaN(value) ? this.releaseValueMap[value] || value : parseInt(value, 10));
},
/**
* Compare 2 specified versions by ensuring the first parameter is a `Version`
* instance and then calling the `compareTo` method.
*
* @static
* @param {String} current The current version to compare to
* @param {String} target The target version to compare to
* @return {Number} Returns -1 if the current version is smaller than the target version, 1 if greater, and 0 if they're equivalent
*/
compare: function(current, target) {
var ver = current.isVersion ? current : new Version(current);
return ver.compareTo(target);
},
set: function(collection, packageName, version) {
var aliases = Version.aliases.to[packageName],
ver = version.isVersion ? version : new Version(version),
i;
collection[packageName] = ver;
if (aliases) {
for (i = aliases.length; i-- > 0; ) {
collection[aliases[i]] = ver;
}
}
return ver;
}
});
/**
* @class Ext
*/
Ext.apply(Ext, {
/**
* @private
*/
compatVersions: {},
/**
* @private
*
* Object containing version information for all packages utilized by your
* application.
*
* For a public getter, please see `Ext.getVersion()`.
*/
versions: {},
/**
* @private
*/
lastRegisteredVersion: null,
/**
* Get the compatibility level (a version number) for the given package name. If
* none has been registered with `Ext.setCompatVersion` then `Ext.getVersion` is
* used to get the current version.
*
* @param {String} packageName The package name, e.g. 'core', 'touch', 'ext'.
* @since 5.0.0
* @private
*/
getCompatVersion: function(packageName) {
var versions = Ext.compatVersions,
compat;
if (!packageName) {
compat = versions.ext || versions.touch || versions.core;
} else {
compat = versions[Version.aliases.from[packageName] || packageName];
}
return compat || Ext.getVersion(packageName);
},
/**
* Set the compatibility level (a version number) for the given package name.
*
* @param {String} packageName The package name, e.g. 'core', 'touch', 'ext'.
* @param {String/Ext.Version} version The version, e.g. '4.2'.
* @since 5.0.0
* @private
*/
setCompatVersion: function(packageName, version) {
Version.set(Ext.compatVersions, packageName, version);
},
/**
* Set version number for the given package name.
*
* @param {String} packageName The package name, e.g. 'core', 'touch', 'ext'.
* @param {String/Ext.Version} version The version, e.g. '1.2.3alpha', '2.4.0-dev'.
* @return {Ext}
*/
setVersion: function(packageName, version) {
if (packageName in toolkitNames) {
Ext.toolkit = packageName;
}
Ext.lastRegisteredVersion = Version.set(Ext.versions, packageName, version);
return this;
},
/**
* Get the version number of the supplied package name; will return the version of
* the framework.
*
* @param {String} [packageName] The package name, e.g., 'core', 'touch', 'ext'.
* @return {Ext.Version} The version.
*/
getVersion: function(packageName) {
var versions = Ext.versions;
if (!packageName) {
return versions.ext || versions.touch || versions.core;
}
return versions[Version.aliases.from[packageName] || packageName];
},
/**
* This method checks the registered package versions against the provided version
* `specs`. A `spec` is either a string or an object indicating a boolean operator.
* This method accepts either form or an array of these as the first argument. The
* second argument applies only when the first is an array and indicates whether
* all `specs` must match or just one.
*
* ## Package Version Specifications
* The string form of a `spec` is used to indicate a version or range of versions
* for a particular package. This form of `spec` consists of three (3) parts:
*
* * Package name followed by "@". If not provided, the framework is assumed.
* * Minimum version.
* * Maximum version.
*
* At least one version number must be provided. If both minimum and maximum are
* provided, these must be separated by a "-".
*
* Some examples of package version specifications:
*
* 4.2.2 (exactly version 4.2.2 of the framework)
* 4.2.2+ (version 4.2.2 or higher of the framework)
* 4.2.2- (version 4.2.2 or higher of the framework)
* 4.2.1 - 4.2.3 (versions from 4.2.1 up to 4.2.3 of the framework)
* - 4.2.2 (any version up to version 4.2.1 of the framework)
*
* foo@1.0 (exactly version 1.0 of package "foo")
* foo@1.0-1.3 (versions 1.0 up to 1.3 of package "foo")
*
* **NOTE:** This syntax is the same as that used in Sencha Cmd's package
* requirements declarations.
*
* ## Boolean Operator Specifications
* Instead of a string, an object can be used to describe a boolean operation to
* perform on one or more `specs`. The operator is either **`and`** or **`or`**
* and can contain an optional **`not`**.
*
* For example:
*
* {
* not: true, // negates boolean result
* and: [
* '4.2.2',
* 'foo@1.0.1 - 2.0.1'
* ]
* }
*
* Each element of the array can in turn be a string or object spec. In other
* words, the value is passed to this method (recursively) as the first argument
* so these two calls are equivalent:
*
* Ext.checkVersion({
* not: true, // negates boolean result
* and: [
* '4.2.2',
* 'foo@1.0.1 - 2.0.1'
* ]
* });
*
* !Ext.checkVersion([
* '4.2.2',
* 'foo@1.0.1 - 2.0.1'
* ], true);
*
* ## Examples
*
* // A specific framework version
* Ext.checkVersion('4.2.2');
*
* // A range of framework versions:
* Ext.checkVersion('4.2.1-4.2.3');
*
* // A specific version of a package:
* Ext.checkVersion('foo@1.0.1');
*
* // A single spec that requires both a framework version and package
* // version range to match:
* Ext.checkVersion({
* and: [
* '4.2.2',
* 'foo@1.0.1-1.0.2'
* ]
* });
*
* // These checks can be nested:
* Ext.checkVersion({
* and: [
* '4.2.2', // exactly version 4.2.2 of the framework *AND*
* {
* // either (or both) of these package specs:
* or: [
* 'foo@1.0.1-1.0.2',
* 'bar@3.0+'
* ]
* }
* ]
* });
*
* ## Version Comparisons
* Version comparsions are assumed to be "prefix" based. That is to say, `"foo@1.2"`
* matches any version of "foo" that has a major version 1 and a minor version of 2.
*
* This also applies to ranges. For example `"foo@1.2-2.2"` matches all versions
* of "foo" from 1.2 up to 2.2 regardless of the specific patch and build.
*
* ## Use in Overrides
* This methods primary use is in support of conditional overrides on an
* `Ext.define` declaration.
*
* @param {String/Array/Object} specs A version specification string, an object
* containing `or` or `and` with a value that is equivalent to `specs` or an array
* of either of these.
* @param {Boolean} [matchAll=false] Pass `true` to require all specs to match.
* @return {Boolean} True if `specs` matches the registered package versions.
*/
checkVersion: function(specs, matchAll) {
var isArray = Ext.isArray(specs),
aliases = Version.aliases.from,
compat = isArray ? specs : checkVerTemp,
length = compat.length,
versions = Ext.versions,
frameworkVer = versions.ext || versions.touch,
i, index, matches, minVer, maxVer, packageName, spec, range, ver;
if (!isArray) {
checkVerTemp[0] = specs;
}
for (i = 0; i < length; ++i) {
if (!Ext.isString(spec = compat[i])) {
matches = Ext.checkVersion(spec.and || spec.or, !spec.or);
if (spec.not) {
matches = !matches;
}
} else {
if (spec.indexOf(' ') >= 0) {
spec = spec.replace(stripRe, '');
}
// For "name@..." syntax, we need to find the package by the given name
// as a registered package.
index = spec.indexOf('@');
if (index < 0) {
range = spec;
ver = frameworkVer;
} else {
packageName = spec.substring(0, index);
if (!(ver = versions[aliases[packageName] || packageName])) {
// The package is not registered, so if we must matchAll then
// we are done - FAIL:
if (matchAll) {
return false;
}
// Otherwise this spec is not a match so we can move on to the
// next...
continue;
}
range = spec.substring(index + 1);
}
// Now look for a version, version range or partial range:
index = range.indexOf('-');
if (index < 0) {
// just a version or "1.0+"
if (range.charAt(index = range.length - 1) === '+') {
minVer = range.substring(0, index);
maxVer = null;
} else {
minVer = maxVer = range;
}
} else if (index > 0) {
// a range like "1.0-1.5" or "1.0-"
minVer = range.substring(0, index);
maxVer = range.substring(index + 1);
} else // may be empty
{
// an upper limit like "-1.5"
minVer = null;
maxVer = range.substring(index + 1);
}
matches = true;
if (minVer) {
minVer = new Version(minVer, '~');
// prefix matching
matches = minVer.ltEq(ver);
}
if (matches && maxVer) {
maxVer = new Version(maxVer, '~');
// prefix matching
matches = maxVer.gtEq(ver);
}
}
// string spec
if (matches) {
// spec matched and we are looking for any match, so we are GO!
if (!matchAll) {
return true;
}
} else if (matchAll) {
// spec does not match the registered package version
return false;
}
}
// In the loop above, for matchAll we return FALSE on mismatch, so getting
// here with matchAll means we had no mismatches. On the other hand, if we
// are !matchAll, we return TRUE on match and so we get here only if we found
// no matches.
return !!matchAll;
},
/**
* Create a closure for deprecated code.
*
* // This means Ext.oldMethod is only supported in 4.0.0beta and older.
* // If Ext.getVersion('extjs') returns a version that is later than '4.0.0beta', for example '4.0.0RC',
* // the closure will not be invoked
* Ext.deprecate('extjs', '4.0.0beta', function() {
* Ext.oldMethod = Ext.newMethod;
*
* ...
* });
*
* @param {String} packageName The package name
* @param {String} since The last version before it's deprecated
* @param {Function} closure The callback function to be executed with the specified version is less than the current version
* @param {Object} scope The execution scope (`this`) if the closure
* @private
*/
deprecate: function(packageName, since, closure, scope) {
if (Version.compare(Ext.getVersion(packageName), since) < 1) {
closure.call(scope);
}
}
});
}());
// End Versioning
// load the cmd-5 style app manifest metadata now, if available...
(function(manifest) {
var packages = (manifest && manifest.packages) || {},
compat = manifest && manifest.compatibility,
name, pkg;
for (name in packages) {
pkg = packages[name];
Ext.setVersion(name, pkg.version);
}
if (compat) {
if (Ext.isString(compat)) {
Ext.setCompatVersion('core', compat);
} else {
for (name in compat) {
Ext.setCompatVersion(name, compat[name]);
}
}
}
if (!packages.ext && !packages.touch) {
Ext.setVersion('ext', '6.2.0.981');
Ext.setVersion('core', '6.2.0.981');
}
})(Ext.manifest);
/**
* @class Ext.Config
* This class manages a config property. Instances of this type are created and cached as
* classes declare their config properties. One instance of this class is created per
* config property name.
*
* Ext.define('MyClass', {
* config: {
* foo: 42
* }
* });
*
* This uses the cached `Ext.Config` instance for the "foo" property.
*
* When config properties apply options to config properties a prototype chained object is
* created from the cached instance. For example:
*
* Ext.define('MyClass', {
* config: {
* foo: {
* $value: 42,
* lazy: true
* }
* }
* });
*
* This creates a prototype chain to the cached "foo" instance of `Ext.Config` and applies
* the `lazy` option to that new instance. This chained instance is then kept by the
* `Ext.Configurator` for that class.
* @private
*/
Ext.Config = function(name) {
// @define Ext.class.Config
// @define Ext.Config
var me = this,
capitalizedName = name.charAt(0).toUpperCase() + name.substr(1);
/**
* @property {String} name
* The name of this config property.
* @readonly
* @private
* @since 5.0.0
*/
me.name = name;
/**
* @property {Object} names
* This object holds the cached names used to lookup properties or methods for this
* config property. The properties of this object are explained in the context of an
* example property named "foo".
*
* @property {String} names.internal The default backing property ("_foo").
*
* @property {String} names.initializing The property that is `true` when the config
* is being initialized ("isFooInitializing").
*
* @property {String} names.apply The name of the applier method ("applyFoo").
*
* @property {String} names.update The name of the updater method ("updateFoo").
*
* @property {String} names.get The name of the getter method ("getFoo").
*
* @property {String} names.set The name of the setter method ("setFoo").
*
* @property {String} names.initGet The name of the initializing getter ("initGetFoo").
*
* @property {String} names.changeEvent The name of the change event ("foochange").
*
* @readonly
* @private
* @since 5.0.0
*/
me.names = {
internal: '_' + name,
initializing: 'is' + capitalizedName + 'Initializing',
apply: 'apply' + capitalizedName,
update: 'update' + capitalizedName,
get: 'get' + capitalizedName,
set: 'set' + capitalizedName,
initGet: 'initGet' + capitalizedName,
changeEvent: name.toLowerCase() + 'change'
};
// This allows folks to prototype chain on top of these objects and yet still cache
// generated methods at the bottom of the chain.
me.root = me;
};
Ext.Config.map = {};
Ext.Config.get = function(name) {
var map = Ext.Config.map,
ret = map[name] || (map[name] = new Ext.Config(name));
return ret;
};
Ext.Config.prototype = {
self: Ext.Config,
isConfig: true,
/**
* @cfg {Boolean} [cached=false]
* When set as `true` the config property will be stored on the class prototype once
* the first instance has had a chance to process the default value.
* @private
* @since 5.0.0
*/
/**
* @cfg {Boolean} [lazy=false]
* When set as `true` the config property will not be immediately initialized during
* the `initConfig` call.
* @private
* @since 5.0.0
*/
/**
* @cfg {Boolean} [evented=false]
* When set as `true` the config property will be treated as a {@link Ext.Evented Evented Config}.
* @private
* @since 6.0.0
*/
/**
* @cfg {Function} [merge]
* This function if supplied will be called as classes or instances provide values
* that need to be combined with inherited values. The function should return the
* value that will be the config value. Further calls may receive such returned
* values as `oldValue`.
*
* @cfg {Mixed} merge.newValue The new value to merge with the old.
*
* @cfg {Mixed} merge.oldValue The current value prior to `newValue` being merged.
*
* @cfg {Mixed} merge.target The class or instance to which the merged config value
* will be applied.
*
* @cfg {Ext.Class} merge.mixinClass The mixin providing the `newValue` or `null` if
* the `newValue` is not being provided by a mixin.
*/
getGetter: function() {
return this.getter || (this.root.getter = this.makeGetter());
},
getInitGetter: function() {
return this.initGetter || (this.root.initGetter = this.makeInitGetter());
},
getSetter: function() {
return this.setter || (this.root.setter = this.makeSetter());
},
getEventedSetter: function() {
return this.eventedSetter || (this.root.eventedSetter = this.makeEventedSetter());
},
/**
* Returns the name of the property that stores this config on the given instance or
* class prototype.
* @param {Object} target
* @return {String}
*/
getInternalName: function(target) {
return target.$configPrefixed ? this.names.internal : this.name;
},
mergeNew: function(newValue, oldValue, target, mixinClass) {
var ret, key;
if (!oldValue) {
ret = newValue;
} else if (!newValue) {
ret = oldValue;
} else {
ret = Ext.Object.chain(oldValue);
for (key in newValue) {
if (!mixinClass || !(key in ret)) {
ret[key] = newValue[key];
}
}
}
return ret;
},
/**
* Merges the `newValue` and the `oldValue` assuming that these are basically objects
* the represent sets. For example something like:
*
* {
* foo: true,
* bar: true
* }
*
* The merge process converts arrays like the following into the above:
*
* [ 'foo', 'bar' ]
*
* @param {String/String[]/Object} newValue
* @param {Object} oldValue
* @param {Boolean} [preserveExisting=false]
* @return {Object}
* @private
* @since 5.0.0
*/
mergeSets: function(newValue, oldValue, preserveExisting) {
var ret = oldValue ? Ext.Object.chain(oldValue) : {},
i, val;
if (newValue instanceof Array) {
for (i = newValue.length; i--; ) {
val = newValue[i];
if (!preserveExisting || !(val in ret)) {
ret[val] = true;
}
}
} else if (newValue) {
if (newValue.constructor === Object) {
for (i in newValue) {
val = newValue[i];
if (!preserveExisting || !(i in ret)) {
ret[i] = val;
}
}
} else if (!preserveExisting || !(newValue in ret)) {
ret[newValue] = true;
}
}
return ret;
},
//--------------------------------------------------
// Factories
makeGetter: function() {
var name = this.name,
prefixedName = this.names.internal;
return function() {
var internalName = this.$configPrefixed ? prefixedName : name;
return this[internalName];
};
},
makeInitGetter: function() {
var name = this.name,
names = this.names,
setName = names.set,
getName = names.get,
initializingName = names.initializing;
return function() {
var me = this;
me[initializingName] = true;
// Remove the initGetter from the instance now that the value has been set.
delete me[getName];
me[setName](me.config[name]);
delete me[initializingName];
return me[getName].apply(me, arguments);
};
},
makeSetter: function() {
var name = this.name,
names = this.names,
prefixedName = names.internal,
getName = names.get,
applyName = names.apply,
updateName = names.update,
setter;
// http://jsperf.com/method-call-apply-or-direct
// http://jsperf.com/method-detect-invoke
setter = function(value) {
var me = this,
internalName = me.$configPrefixed ? prefixedName : name,
oldValue = me[internalName];
// Remove the initGetter from the instance now that the value has been set.
delete me[getName];
if (!me[applyName] || (value = me[applyName](value, oldValue)) !== undefined) {
// The old value might have been changed at this point
// (after the apply call chain) so it should be read again
if (value !== (oldValue = me[internalName])) {
me[internalName] = value;
if (me[updateName]) {
me[updateName](value, oldValue);
}
}
}
return me;
};
setter.$isDefault = true;
return setter;
},
makeEventedSetter: function() {
var name = this.name,
names = this.names,
prefixedName = names.internal,
getName = names.get,
applyName = names.apply,
updateName = names.update,
changeEventName = names.changeEvent,
updateFn = function(me, value, oldValue, internalName) {
me[internalName] = value;
if (me[updateName]) {
me[updateName](value, oldValue);
}
},
setter;
// http://jsperf.com/method-call-apply-or-direct
// http://jsperf.com/method-detect-invoke
setter = function(value) {
var me = this,
internalName = me.$configPrefixed ? prefixedName : name,
oldValue = me[internalName];
// Remove the initGetter from the instance now that the value has been set.
delete me[getName];
if (!me[applyName] || (value = me[applyName](value, oldValue)) !== undefined) {
// The old value might have been changed at this point
// (after the apply call chain) so it should be read again
if (value !== (oldValue = me[internalName])) {
if (me.isConfiguring) {
me[internalName] = value;
if (me[updateName]) {
me[updateName](value, oldValue);
}
} else {
me.fireEventedAction(changeEventName, [
me,
value,
oldValue
], updateFn, me, [
me,
value,
oldValue,
internalName
]);
}
}
}
return me;
};
setter.$isDefault = true;
return setter;
}
};
/**
* @class Ext.Configurator
* This class manages the config properties for a class.
* @private
*/
(function() {
// see end of file (and please don't indent the whole file)
var ExtConfig = Ext.Config,
configPropMap = ExtConfig.map,
ExtObject = Ext.Object;
Ext.Configurator = function(cls) {
// @define Ext.class.Configurator
// @define Ext.Configurator
// @require Ext.Config
var me = this,
prototype = cls.prototype,
superCfg = cls.superclass ? cls.superclass.self.$config : null;
/**
* @property {Ext.Class} cls The class to which this instance is associated.
* @private
* @readonly
*/
me.cls = cls;
/**
* The super class `Configurator` instance or `null` if there is no super class.
*
* @property {Ext.Configurator} superCfg
* @private
* @readonly
*/
me.superCfg = superCfg;
if (superCfg) {
/**
* This object holds an `Ext.Config` value for each config property keyed by name.
* This object has as its prototype object the `configs` of its super class.
*
* This map is maintained as each property is added via the `add` method.
*
* @property {Object} configs
* @private
* @readonly
*/
me.configs = ExtObject.chain(superCfg.configs);
/**
* This object holds a bool value for each cachedConfig property keyed by name.
*
* This map is maintained as each property is added via the `add` method.
*
* @property {Object} cachedConfigs
* @private
* @readonly
*/
me.cachedConfigs = ExtObject.chain(superCfg.cachedConfigs);
/**
* This object holds a `Number` for each config property keyed by name. This object has
* as its prototype object the `initMap` of its super class. The value of each property
* has the following meaning:
*
* * `0` - initial value is `null` and requires no processing.
* * `1` - initial value must be set on each instance.
* * `2` - initial value can be cached on the prototype by the first instance.
*
* Any `null` values will either never be added to this map or (if added by a base
* class and set to `null` by a derived class) will cause the entry to be 0.
*
* This map is maintained as each property is added via the `add` method.
*
* @property {Object} initMap
* @private
* @readonly
*/
me.initMap = ExtObject.chain(superCfg.initMap);
/**
* This object holds the default value for each config property keyed by name. This
* object has as its prototype object the `values` of its super class.
*
* This map is maintained as each property is added via the `add` method.
*
* @property {Object} values
* @private
* @readonly
*/
me.values = ExtObject.chain(superCfg.values);
me.needsFork = superCfg.needsFork;
// The reason this feature is debug only is that we would have to create this
// map for all classes because deprecations could be added to bases after the
// derived class had created its Configurator.
me.deprecations = ExtObject.chain(superCfg.deprecations);
} else {
me.configs = {};
me.cachedConfigs = {};
me.initMap = {};
me.values = {};
me.deprecations = {};
}
prototype.config = prototype.defaultConfig = me.values;
cls.$config = me;
};
Ext.Configurator.prototype = {
self: Ext.Configurator,
needsFork: false,
/**
* This array holds the properties that need to be set on new instances.
*
* This array is populated when the first instance is passed to `configure` (basically
* when the first instance is created). The entries in `initMap` are iterated to find
* those configs needing per-instance processing.
*
* @property {Ext.Config[]} initList
* @private
*/
initList: null,
/**
* This method adds new config properties. This is called for classes when they are
* declared, then for any mixins that class may define and finally for any overrides
* defined that target the class.
*
* @param {Object} config The config object containing the new config properties.
* @param {Ext.Class} [mixinClass] The mixin class if the configs are from a mixin.
* @private
*/
add: function(config, mixinClass) {
var me = this,
Cls = me.cls,
configs = me.configs,
cachedConfigs = me.cachedConfigs,
initMap = me.initMap,
prototype = Cls.prototype,
mixinConfigs = mixinClass && mixinClass.$config.configs,
values = me.values,
isObject, meta, isCached, merge, cfg, currentValue, name, names, s, value;
for (name in config) {
value = config[name];
isObject = value && value.constructor === Object;
meta = isObject && '$value' in value ? value : null;
isCached = false;
if (meta) {
isCached = !!meta.cached;
value = meta.$value;
isObject = value && value.constructor === Object;
}
merge = meta && meta.merge;
cfg = configs[name];
if (cfg) {
// Only proceed with a mixin if we have a custom merge.
if (mixinClass) {
merge = cfg.merge;
if (!merge) {
continue;
}
// Don't want the mixin meta modifying our own
meta = null;
} else {
merge = merge || cfg.merge;
}
// This means that we've already declared this as a config in a superclass
// Let's not allow us to change it here.
if (!mixinClass && isCached && !cachedConfigs[name]) {
Ext.raise('Redefining config as cached: ' + name + ' in class: ' + Cls.$className);
}
// There is already a value for this config and we are not allowed to
// modify it. So, if it is an object and the new value is also an object,
// the result is a merge so we have to merge both on to a new object.
currentValue = values[name];
if (merge) {
value = merge.call(cfg, value, currentValue, Cls, mixinClass);
} else if (isObject) {
if (currentValue && currentValue.constructor === Object) {
// We favor moving the cost of an "extra" copy here because this
// is likely to be a rare thing two object values for the same
// property. The alternative would be to clone the initial value
// to make it safely modifiable even though it is likely to never
// need to be modified.
value = ExtObject.merge({}, currentValue, value);
}
}
} else // else "currentValue" is a primitive so "value" can just replace it
// else "value" is a primitive and it can just replace currentValue
{
// This is a new property value, so add it to the various maps "as is".
// In the majority of cases this value will not be overridden or need to
// be forked.
if (mixinConfigs) {
// Since this is a config from a mixin, we don't want to apply its
// meta-ness because it already has. Instead we want to use its cfg
// instance:
cfg = mixinConfigs[name];
meta = null;
} else {
cfg = ExtConfig.get(name);
}
configs[name] = cfg;
if (cfg.cached || isCached) {
cachedConfigs[name] = true;
}
// Ensure that the new config has a getter and setter. Because this method
// is called during class creation as the "config" (or "cachedConfig") is
// being processed, the user's methods will not be on the prototype yet.
//
// This has the following trade-offs:
//
// - Custom getters are rare so there is minimal waste generated by them.
//
// - Custom setters are more common but, by putting the default setter on
// the prototype prior to addMembers, when the user methods are added
// callParent can be used to call the generated setter. This is almost
// certainly desirable as the setter has some very important semantics
// that a custom setter would probably want to preserve by just adding
// logic before and/or after the callParent.
//
// - By not adding these to the class body we avoid all the "is function"
// tests that get applied to each class member thereby streamlining the
// downstream class creation process.
//
// We still check for getter and/or setter but primarily for reasons of
// backwards compatibility and "just in case" someone relied on inherited
// getter/setter even though the base did not have the property listed as
// a "config" (obscure case certainly).
//
names = cfg.names;
if (!prototype[s = names.get]) {
prototype[s] = cfg.getter || cfg.getGetter();
}
if (!prototype[s = names.set]) {
prototype[s] = (meta && meta.evented) ? (cfg.eventedSetter || cfg.getEventedSetter()) : (cfg.setter || cfg.getSetter());
}
}
if (meta) {
if (cfg.owner !== Cls) {
configs[name] = cfg = Ext.Object.chain(cfg);
cfg.owner = Cls;
}
Ext.apply(cfg, meta);
delete cfg.$value;
}
// Fork checks all the default values to see if they are arrays or objects
// Do this to save us from doing it on each run
if (!me.needsFork && value && (value.constructor === Object || value instanceof Array)) {
me.needsFork = true;
}
// If the value is non-null, we need to initialize it.
if (value !== null) {
initMap[name] = true;
} else {
if (prototype.$configPrefixed) {
prototype[configs[name].names.internal] = null;
} else {
prototype[configs[name].name] = null;
}
if (name in initMap) {
// Only set this to false if we already have it in the map, otherwise, just leave it out!
initMap[name] = false;
}
}
values[name] = value;
}
},
addDeprecations: function(configs) {
var me = this,
deprecations = me.deprecations,
className = (me.cls.$className || '') + '#',
message, newName, oldName;
for (oldName in configs) {
newName = configs[oldName];
// configs: {
// dead: null,
//
// renamed: 'newName',
//
// removed: {
// message: 'This config was replaced by pixie dust'
// }
// }
if (!newName) {
message = 'This config has been removed.';
} else if (!(message = newName.message)) {
message = 'This config has been renamed to "' + newName + '"';
}
deprecations[oldName] = className + oldName + ': ' + message;
}
},
/**
* This method configures the given `instance` using the specified `instanceConfig`.
* The given `instance` should have been created by this object's `cls`.
*
* @param {Object} instance The instance to configure.
* @param {Object} instanceConfig The configuration properties to apply to `instance`.
* @private
*/
configure: function(instance, instanceConfig) {
var me = this,
configs = me.configs,
deprecations = me.deprecations,
initMap = me.initMap,
initListMap = me.initListMap,
initList = me.initList,
prototype = me.cls.prototype,
values = me.values,
remaining = 0,
firstInstance = !initList,
cachedInitList, cfg, getter, i, internalName, ln, names, name, value, isCached, valuesKey, field;
values = me.needsFork ? ExtObject.fork(values) : ExtObject.chain(values);
// Let apply/update methods know that the initConfig is currently running.
instance.isConfiguring = true;
if (firstInstance) {
// When called to configure the first instance of the class to which we are
// bound we take a bit to plan for instance 2+.
me.initList = initList = [];
me.initListMap = initListMap = {};
instance.isFirstInstance = true;
for (name in initMap) {
cfg = configs[name];
isCached = cfg.cached;
if (initMap[name]) {
names = cfg.names;
value = values[name];
if (!prototype[names.set].$isDefault || prototype[names.apply] || prototype[names.update] || typeof value === 'object') {
if (isCached) {
// This is a cachedConfig, so it needs to be initialized with
// the default value and placed on the prototype... but the
// instanceConfig may have a different value so the value may
// need resetting. We have to defer the call to the setter so
// that all of the initGetters are set up first.
(cachedInitList || (cachedInitList = [])).push(cfg);
} else {
// Remember this config so that all instances (including this
// one) can invoke the setter to properly initialize it.
initList.push(cfg);
initListMap[name] = true;
}
// Point all getters to the initGetters. By doing this here we
// avoid creating initGetters for configs that don't need them
// and we can easily pick up the cached fn to save the call.
instance[names.get] = cfg.initGetter || cfg.getInitGetter();
} else {
// Non-object configs w/o custom setter, applier or updater can
// be simply stored on the prototype.
prototype[cfg.getInternalName(prototype)] = value;
}
} else if (isCached) {
prototype[cfg.getInternalName(prototype)] = undefined;
}
}
}
// TODO - we need to combine the cached loop with the instanceConfig loop to
// avoid duplication of init getter setups (for correctness if a cached cfg
// calls on a non-cached cfg)
ln = cachedInitList && cachedInitList.length;
if (ln) {
// This is only ever done on the first instance we configure. Any config in
// cachedInitList has to be set to the default value to allow any side-effects
// or transformations to occur. The resulting values can then be elevated to
// the prototype and this property need not be initialized on each instance.
for (i = 0; i < ln; ++i) {
internalName = cachedInitList[i].getInternalName(prototype);
// Since these are cached configs the base class will potentially have put
// its cached values on the prototype so we need to hide these while we
// run the inits for our cached configs.
instance[internalName] = null;
}
for (i = 0; i < ln; ++i) {
names = (cfg = cachedInitList[i]).names;
getter = names.get;
if (instance.hasOwnProperty(getter)) {
instance[names.set](values[cfg.name]);
delete instance[getter];
}
}
for (i = 0; i < ln; ++i) {
internalName = cachedInitList[i].getInternalName(prototype);
prototype[internalName] = instance[internalName];
delete instance[internalName];
}
}
// The cachedConfigs have all been set to the default values including any of
// those that may have been triggered by their getter.
// If the instanceConfig has a platformConfig in it, we need to merge the active
// rules of that object to make the actual instanceConfig.
if (instanceConfig && instanceConfig.platformConfig) {
instanceConfig = me.resolvePlatformConfig(instance, instanceConfig);
}
if (firstInstance) {
// Allow the class to do things once the cachedConfig has been processed.
// We need to call this method always when the first instance is configured
// whether or not it actually has cached configs
if (instance.afterCachedConfig && !instance.afterCachedConfig.$nullFn) {
instance.afterCachedConfig(instanceConfig);
}
}
// Now that the cachedConfigs have been processed we can apply the instanceConfig
// and hide the "configs" on the prototype. This will serve as the source for any
// configs that need to initialize from their initial getter call.
instance.config = values;
// There are 2 possibilities here:
// 1) If it's the first time in this function, we may have had cachedConfigs running.
// these configs may have called the getters for any of the normal getters, which
// means the initial getters have been clobbered on the instance and won't be able
// to be called below when we iterate over the initList. As such, we need to
// reinitialize them here, even though we've done it up above.
//
// 2) If this the second time in this function, the cachedConfigs won't be processed,
// so we don't need to worry about them clobbering config values. However, since
// we've already done all our setup, we won't enter into the block that sets the
// initGetter, so we need to do it here anyway.
//
// Also note, that lazy configs will appear in the initList because we need
// to spin up the initGetter.
for (i = 0 , ln = initList.length; i < ln; ++i) {
cfg = initList[i];
instance[cfg.names.get] = cfg.initGetter || cfg.getInitGetter();
}
// Give the class a chance to transform the configs.
if (instance.transformInstanceConfig) {
instanceConfig = instance.transformInstanceConfig(instanceConfig);
}
// Important: We are looping here twice on purpose. This first loop serves 2 purposes:
//
// 1) Ensure the values collection is fully populated before we call any setters. Since
// a setter may have an updater/applier, it could potentially call another getter() to grab
// the value for some other property, so this ensures they are all set on the config object.
//
// 2) Ensure that the initGetter is set as the getter for any config that doesn't appear in
// the initList. We need to ensure that the initGetter is pushed on for everything that we will
// be setting during init time.
//
// The merging in this loop cannot be completed by Ext.merge(), since we do NOT want to merge
// non-strict values, they should always just be assigned across without modification.
if (instanceConfig) {
for (name in instanceConfig) {
value = instanceConfig[name];
cfg = configs[name];
if (deprecations[name]) {
Ext.log.warn(deprecations[name]);
if (!cfg) {
// If there is a Config for this, perhaps the class is emulating
// the old config... If there is not a Config we don't want to
// proceed and put the property on the instance. That will likely
// hide the bug during development.
continue;
}
}
if (!cfg) {
field = instance.self.prototype[name];
if (instance.$configStrict && (typeof field === 'function') && !field.$nullFn) {
// In strict mode you cannot override functions
Ext.raise('Cannot override method ' + name + ' on ' + instance.$className + ' instance.');
}
// Not all "configs" use the config system so in this case simply put
// the value on the instance:
instance[name] = value;
} else {
// However we still need to create the initial value that needs
// to be used. We also need to spin up the initGetter.
if (!cfg.lazy) {
++remaining;
}
if (!initListMap[name]) {
instance[cfg.names.get] = cfg.initGetter || cfg.getInitGetter();
}
if (cfg.merge) {
value = cfg.merge(value, values[name], instance);
} else if (value && value.constructor === Object) {
valuesKey = values[name];
if (valuesKey && valuesKey.constructor === Object) {
value = ExtObject.merge(values[name], value);
} else {
value = Ext.clone(value, false);
}
}
}
values[name] = value;
}
}
// Give the class a chance to hook in prior to initializing the configs.
if (instance.beforeInitConfig && !instance.beforeInitConfig.$nullFn) {
if (instance.beforeInitConfig(instanceConfig) === false) {
return;
}
}
if (instanceConfig) {
for (name in instanceConfig) {
if (!remaining) {
// For classes that have few proper Config properties, this saves us
// from making the full 2 passes over the instanceConfig.
break;
}
// We can ignore deprecated configs here because we warned about them
// above. Further, since we only process proper Config's here we would
// not be skipping them anyway.
cfg = configs[name];
if (cfg && !cfg.lazy) {
--remaining;
// A proper "config" property so call the setter to set the value.
names = cfg.names;
getter = names.get;
// At this point the initGetter may have already been called and
// cleared if the getter was called from the applier or updater of a
// previously processed instance config. checking if the instance has
// its own getter ensures the setter does not get called twice.
if (instance.hasOwnProperty(getter)) {
instance[names.set](values[name]);
// The generated setter will remove the initGetter from the instance
// but the user may have provided their own setter so we have to do
// this here as well:
delete instance[names.get];
}
}
}
}
// Process configs declared on the class that need per-instance initialization.
for (i = 0 , ln = initList.length; i < ln; ++i) {
cfg = initList[i];
names = cfg.names;
getter = names.get;
if (!cfg.lazy && instance.hasOwnProperty(getter)) {
// Since the instance still hasOwn the getter, that means we've set an initGetter
// and it hasn't been cleared by calling any setter. Since we've never set the value
// because it wasn't passed in the instance, we go and set it here, taking the value
// from our definition config and passing it through finally clear off the getter.
instance[names.set](values[cfg.name]);
delete instance[getter];
}
}
// Expose the value from the prototype chain (false):
delete instance.isConfiguring;
},
getCurrentConfig: function(instance) {
var defaultConfig = instance.defaultConfig,
config = {},
name;
for (name in defaultConfig) {
config[name] = instance[configPropMap[name].names.get]();
}
return config;
},
/**
* Merges the values of a config object onto a base config.
* @param {Ext.Base} instance
* @param {Object} baseConfig
* @param {Object} config
* @return {Object} the merged config
* @private
*/
merge: function(instance, baseConfig, config) {
// Although this is a "private" method. It is used by Sencha Architect and so
// its api should remain stable.
var configs = this.configs,
name, value, baseValue, cfg;
for (name in config) {
value = config[name];
cfg = configs[name];
if (cfg) {
if (cfg.merge) {
value = cfg.merge(value, baseConfig[name], instance);
} else if (value && value.constructor === Object) {
baseValue = baseConfig[name];
if (baseValue && baseValue.constructor === Object) {
value = Ext.Object.merge(baseValue, value);
} else {
value = Ext.clone(value, false);
}
}
}
baseConfig[name] = value;
}
return baseConfig;
},
/**
* @private
*/
reconfigure: function(instance, instanceConfig, options) {
var currentConfig = instance.config,
configList = [],
strict = instance.$configStrict && !(options && options.strict === false),
configs = this.configs,
defaults = options && options.defaults,
cfg, getter, i, len, name, names, prop;
for (name in instanceConfig) {
if (defaults && instance.hasOwnProperty(name)) {
continue;
}
currentConfig[name] = instanceConfig[name];
cfg = configs[name];
if (this.deprecations[name]) {
// See similar logic doc in configure() method.
Ext.log.warn(this.deprecations[name]);
if (!cfg) {
continue;
}
}
if (cfg) {
// To ensure that configs being set here get processed in the proper order
// we must give them init getters just in case they depend upon each other
instance[cfg.names.get] = cfg.initGetter || cfg.getInitGetter();
} else {
// Check for existence of the property on the prototype before proceeding.
// If present on the prototype, and if the property is a function we
// do not allow it to be overridden by a property in the config object
// in strict mode (unless the function on the prototype is a emptyFn or
// identityFn). Note that we always check the prototype, not the instance
// because calling setConfig a second time should have the same results -
// the first call may have set a function on the instance.
prop = instance.self.prototype[name];
if (strict) {
if ((typeof prop === 'function') && !prop.$nullFn) {
Ext.Error.raise("Cannot override method " + name + " on " + instance.$className + " instance.");
continue;
} else {
if (name !== 'type') {
Ext.log.warn('No such config "' + name + '" for class ' + instance.$className);
}
}
}
}
configList.push(name);
}
for (i = 0 , len = configList.length; i < len; i++) {
name = configList[i];
cfg = configs[name];
if (cfg) {
names = cfg.names;
getter = names.get;
if (instance.hasOwnProperty(getter)) {
// Since the instance still hasOwn the getter, that means we've set an initGetter
// and it hasn't been cleared by calling any setter. Since we've never set the value
// because it wasn't passed in the instance, we go and set it here, taking the value
// from our definition config and passing it through finally clear off the getter.
instance[names.set](instanceConfig[name]);
delete instance[getter];
}
} else {
cfg = configPropMap[name] || Ext.Config.get(name);
names = cfg.names;
if (instance[names.set]) {
instance[names.set](instanceConfig[name]);
} else {
// apply non-config props directly to the instance
instance[name] = instanceConfig[name];
}
}
}
},
/**
* This method accepts an instance config object containing a `platformConfig`
* property and merges the appropriate rules from that sub-object with the root object
* to create the final config object that should be used. This is method called by
* `{@link #configure}` when it receives an `instanceConfig` containing a
* `platformConfig` property.
*
* @param {Object} instanceConfig The instance config parameter.
* @return {Object} The new instance config object with platformConfig results applied.
* @private
* @since 5.1.0
*/
resolvePlatformConfig: function(instance, instanceConfig) {
var platformConfig = instanceConfig && instanceConfig.platformConfig,
ret = instanceConfig,
i, keys, n;
if (platformConfig) {
keys = Ext.getPlatformConfigKeys(platformConfig);
n = keys.length;
if (n) {
ret = Ext.merge({}, ret);
// this deep copies sub-objects
for (i = 0 , n = keys.length; i < n; ++i) {
this.merge(instance, ret, platformConfig[keys[i]]);
}
}
}
return ret;
}
};
}());
// prototype
// closure on whole file
// @tag class
/**
* @class Ext.Base
*
* The root of all classes created with {@link Ext#define}.
*
* Ext.Base is the building block of all Ext classes. All classes in Ext inherit from Ext.Base.
* All prototype and static members of this class are inherited by all other classes.
*/
Ext.Base = (function(flexSetter) {
// @define Ext.Base
// @require Ext.Util
// @require Ext.Version
// @require Ext.Configurator
// @uses Ext.ClassManager
var noArgs = [],
baseStaticMember,
baseStaticMembers = [],
getConfig = function(name, peek) {
var me = this,
ret, cfg, getterName;
if (name) {
cfg = Ext.Config.map[name];
if (!cfg) {
Ext.Logger.error("Invalid property name for getter: '" + name + "' for '" + me.$className + "'.");
}
getterName = cfg.names.get;
if (peek && me.hasOwnProperty(getterName)) {
ret = me.config[name];
} else {
ret = me[getterName]();
}
} else {
ret = me.getCurrentConfig();
}
return ret;
},
makeDeprecatedMethod = function(oldName, newName, msg) {
var message = '"' + oldName + '" is deprecated.';
if (msg) {
message += ' ' + msg;
} else if (newName) {
message += ' Please use "' + newName + '" instead.';
}
return function() {
Ext.raise(message);
};
},
addDeprecatedProperty = function(object, oldName, newName, message) {
if (!message) {
message = '"' + oldName + '" is deprecated.';
}
if (newName) {
message += ' Please use "' + newName + '" instead.';
}
if (message) {
Ext.Object.defineProperty(object, oldName, {
get: function() {
Ext.raise(message);
},
set: function(value) {
Ext.raise(message);
},
configurable: true
});
}
},
makeAliasFn = function(name) {
return function() {
return this[name].apply(this, arguments);
};
},
Version = Ext.Version,
leadingDigitRe = /^\d/,
oneMember = {},
aliasOneMember = {},
Base = function() {},
BasePrototype = Base.prototype,
Reaper;
Ext.Reaper = Reaper = {
delay: 100,
queue: [],
timer: null,
add: function(obj) {
if (!Reaper.timer) {
Reaper.timer = Ext.defer(Reaper.tick, Reaper.delay);
}
Reaper.queue.push(obj);
},
flush: function() {
if (Reaper.timer) {
clearTimeout(Reaper.timer);
Reaper.timer = null;
}
var queue = Reaper.queue,
n = queue.length,
i, obj;
Reaper.queue = [];
for (i = 0; i < n; ++i) {
obj = queue[i];
if (obj && obj.$reap) {
obj.$reap();
}
}
},
tick: function() {
Reaper.timer = null;
Reaper.flush();
}
};
// These static properties will be copied to every newly created class with {@link Ext#define}
Ext.apply(Base, {
$className: 'Ext.Base',
$isClass: true,
/**
* Create a new instance of this Class.
*
* Ext.define('My.cool.Class', {
* ...
* });
*
* My.cool.Class.create({
* someConfig: true
* });
*
* All parameters are passed to the constructor of the class.
*
* @return {Object} the created instance.
* @static
* @inheritable
*/
create: function() {
return Ext.create.apply(Ext, [
this
].concat(Array.prototype.slice.call(arguments, 0)));
},
/**
* This method applies a versioned, deprecation declaration to this class. This
* is typically called by the `deprecated` config.
* @private
*/
addDeprecations: function(deprecations) {
var me = this,
all = [],
compatVersion = Ext.getCompatVersion(deprecations.name),
configurator = me.getConfigurator(),
displayName = (me.$className || '') + '#',
deprecate, versionSpec, index, message, target, enabled, existing, fn, names, oldName, newName, member, statics, version;
for (versionSpec in deprecations) {
if (leadingDigitRe.test(versionSpec)) {
version = new Ext.Version(versionSpec);
version.deprecations = deprecations[versionSpec];
all.push(version);
}
}
all.sort(Version.compare);
for (index = all.length; index--; ) {
deprecate = (version = all[index]).deprecations;
target = me.prototype;
statics = deprecate.statics;
// If user specifies, say 4.2 compatibility and we have a 5.0 deprecation
// then that block needs to be "enabled" to "revert" to behaviors prior
// to 5.0. By default, compatVersion === currentVersion, so there are no
// enabled blocks. In dev mode we still want to visit all the blocks and
// possibly add shims to detect use of deprecated methods, but in a build
// (if the deprecated block remains somehow) we just break the loop.
enabled = compatVersion && compatVersion.lt(version);
if (!enabled) {} else if (!enabled) {
// we won't get here in dev mode when !enabled
break;
}
while (deprecate) {
names = deprecate.methods;
if (names) {
for (oldName in names) {
member = names[oldName];
fn = null;
if (!member) {
/*
* Something like:
*
* '5.1': {
* methods: {
* removedMethod: null
* }
* }
*
* Since there is no recovering the method, we always put
* on a shim to catch abuse.
*/
// The class should not already have a method by the oldName
Ext.Assert.isNotDefinedProp(target, oldName);
fn = makeDeprecatedMethod(displayName + oldName);
} else if (Ext.isString(member)) {
/*
* Something like:
*
* '5.1': {
* methods: {
* oldName: 'newName'
* }
* }
*
* If this block is enabled, we just put an alias in place.
* Otherwise we need to inject a
*/
// The class should not already have a method by the oldName
Ext.Assert.isNotDefinedProp(target, oldName);
Ext.Assert.isDefinedProp(target, member);
if (enabled) {
// This call to the real method name must be late
// bound if it is to pick up overrides and such.
fn = makeAliasFn(member);
} else {
fn = makeDeprecatedMethod(displayName + oldName, member);
}
} else {
/*
* Something like:
*
* '5.1': {
* methods: {
* foo: function () { ... }
* }
* }
*
* Or this:
*
* '5.1': {
* methods: {
* foo: {
* fn: function () { ... },
* message: 'Please use "bar" instead.'
* }
* }
* }
*
* Or just this:
*
* '5.1': {
* methods: {
* foo: {
* message: 'Use something else instead.'
* }
* }
* }
*
* If this block is enabled, and "foo" is an existing
* method, than we apply the given method as an override.
* If "foo" is not existing, we simply add the method.
*
* If the block is not enabled and there is no existing
* method by that name, than we add a shim to prevent
* abuse.
*/
message = '';
if (member.message || member.fn) {
message = member.message;
member = member.fn;
}
existing = target.hasOwnProperty(oldName) && target[oldName];
if (enabled && member) {
member.$owner = me;
member.$name = oldName;
member.name = displayName + oldName;
if (existing) {
member.$previous = existing;
}
fn = member;
} else if (!existing) {
fn = makeDeprecatedMethod(displayName + oldName, null, message);
}
}
if (fn) {
target[oldName] = fn;
}
}
}
// for oldName
//-------------------------------------
// Debug only
names = deprecate.configs;
if (names) {
//
// '6.0': {
// configs: {
// dead: null,
//
// renamed: 'newName',
//
// removed: {
// message: 'This config was replaced by pixie dust'
// }
// }
// }
//
configurator.addDeprecations(names);
}
names = deprecate.properties;
if (names && !enabled) {
// For properties about the only thing we can do is (on Good
// Browsers), add warning shims for accessing them. So if the
// block is enabled, we don't want those.
for (oldName in names) {
newName = names[oldName];
if (Ext.isString(newName)) {
addDeprecatedProperty(target, displayName + oldName, newName);
} else if (newName && newName.message) {
addDeprecatedProperty(target, displayName + oldName, null, newName.message);
} else {
addDeprecatedProperty(target, displayName + oldName);
}
}
}
//-------------------------------------
// reset to handle statics and apply them to the class
deprecate = statics;
statics = null;
target = me;
}
}
},
/**
* @private
* @static
* @inheritable
* @param config
*/
extend: function(parent) {
var me = this,
parentPrototype = parent.prototype,
prototype, name, statics;
prototype = me.prototype = Ext.Object.chain(parentPrototype);
prototype.self = me;
me.superclass = prototype.superclass = parentPrototype;
if (!parent.$isClass) {
for (name in BasePrototype) {
if (name in prototype) {
prototype[name] = BasePrototype[name];
}
}
}
// Statics inheritance
statics = parentPrototype.$inheritableStatics;
if (statics) {
for (name in statics) {
if (!me.hasOwnProperty(name)) {
me[name] = parent[name];
}
}
}
if (parent.$onExtended) {
me.$onExtended = parent.$onExtended.slice();
}
me.getConfigurator();
},
/**
* @private
* @static
* @inheritable
*/
$onExtended: [],
/**
* @private
* @static
* @inheritable
*/
triggerExtended: function() {
Ext.classSystemMonitor && Ext.classSystemMonitor(this, 'Ext.Base#triggerExtended', arguments);
var callbacks = this.$onExtended,
ln = callbacks.length,
i, callback;
if (ln > 0) {
for (i = 0; i < ln; i++) {
callback = callbacks[i];
callback.fn.apply(callback.scope || this, arguments);
}
}
},
/**
* @private
* @static
* @inheritable
*/
onExtended: function(fn, scope) {
this.$onExtended.push({
fn: fn,
scope: scope
});
return this;
},
/**
* Add / override static properties of this class.
*
* Ext.define('My.cool.Class', {
* ...
* });
*
* My.cool.Class.addStatics({
* someProperty: 'someValue', // My.cool.Class.someProperty = 'someValue'
* method1: function() { ... }, // My.cool.Class.method1 = function() { ... };
* method2: function() { ... } // My.cool.Class.method2 = function() { ... };
* });
*
* @param {Object} members
* @return {Ext.Base} this
* @static
* @inheritable
*/
addStatics: function(members) {
this.addMembers(members, true);
return this;
},
/**
* @private
* @static
* @inheritable
* @param {Object} members
*/
addInheritableStatics: function(members) {
var me = this,
proto = me.prototype,
inheritableStatics = me.$inheritableStatics,
name, member, current;
if (!inheritableStatics) {
inheritableStatics = Ext.apply({}, proto.$inheritableStatics);
me.$inheritableStatics = proto.$inheritableStatics = inheritableStatics;
}
var className = Ext.getClassName(me) + '.';
for (name in members) {
if (members.hasOwnProperty(name)) {
member = members[name];
current = me[name];
if (typeof member == 'function') {
member.name = className + name;
}
if (typeof current === 'function' && !current.$isClass && !current.$nullFn) {
member.$previous = current;
}
me[name] = member;
inheritableStatics[name] = true;
}
}
return me;
},
/**
* Add methods / properties to the prototype of this class.
*
* Ext.define('My.awesome.Cat', {
* constructor: function() {
* ...
* }
* });
*
* My.awesome.Cat.addMembers({
* meow: function() {
* alert('Meowww...');
* }
* });
*
* var kitty = new My.awesome.Cat();
* kitty.meow();
*
* @param {Object} members The members to add to this class.
* @param {Boolean} [isStatic=false] Pass `true` if the members are static.
* @param {Boolean} [privacy=false] Pass `true` if the members are private. This
* only has meaning in debug mode and only for methods.
* @static
* @inheritable
*/
addMembers: function(members, isStatic, privacy) {
var me = this,
// this class
cloneFunction = Ext.Function.clone,
target = isStatic ? me : me.prototype,
defaultConfig = !isStatic && target.defaultConfig,
enumerables = Ext.enumerables,
privates = members.privates,
configs, i, ln, member, name, subPrivacy, privateStatics;
var displayName = (me.$className || '') + '#';
if (privates) {
// This won't run for normal class private members but will pick up all
// others (statics, overrides, etc).
delete members.privates;
if (!isStatic) {
privateStatics = privates.statics;
delete privates.statics;
}
subPrivacy = privates.privacy || privacy || 'framework';
me.addMembers(privates, isStatic, subPrivacy);
if (privateStatics) {
me.addMembers(privateStatics, true, subPrivacy);
}
}
for (name in members) {
if (members.hasOwnProperty(name)) {
member = members[name];
if (privacy === true) {
privacy = 'framework';
}
if (member && member.$nullFn && privacy !== member.$privacy) {
Ext.raise('Cannot use stock function for private method ' + (me.$className ? me.$className + '#' : '') + name);
}
if (typeof member === 'function' && !member.$isClass && !member.$nullFn) {
if (member.$owner) {
member = cloneFunction(member);
}
if (target.hasOwnProperty(name)) {
member.$previous = target[name];
}
// This information is needed by callParent() and callSuper() as
// well as statics() and even Ext.fly().
member.$owner = me;
member.$name = name;
member.name = displayName + name;
var existing = target[name];
if (privacy) {
member.$privacy = privacy;
// The general idea here is that an existing, non-private
// method can be marked private. This is because the other
// way is strictly forbidden (private method going public)
// so if a method is in that gray area it can only be made
// private in doc form which allows a derived class to make
// it public.
if (existing && existing.$privacy && existing.$privacy !== privacy) {
Ext.privacyViolation(me, existing, member, isStatic);
}
} else if (existing && existing.$privacy) {
Ext.privacyViolation(me, existing, member, isStatic);
}
}
// The last part of the check here resolves a conflict if we have the same property
// declared as both a config and a member on the class so that the config wins.
else if (defaultConfig && (name in defaultConfig) && !target.config.hasOwnProperty(name)) {
// This is a config property so it must be added to the configs
// collection not just smashed on the prototype...
(configs || (configs = {}))[name] = member;
continue;
}
target[name] = member;
}
}
if (configs) {
// Add any configs found in the normal members arena:
me.addConfig(configs);
}
if (enumerables) {
for (i = 0 , ln = enumerables.length; i < ln; ++i) {
if (members.hasOwnProperty(name = enumerables[i])) {
member = members[name];
// The enumerables are all functions...
if (member && !member.$nullFn) {
if (member.$owner) {
member = cloneFunction(member);
}
member.$owner = me;
member.$name = name;
member.name = displayName + name;
if (target.hasOwnProperty(name)) {
member.$previous = target[name];
}
}
target[name] = member;
}
}
}
return this;
},
/**
* @private
* @static
* @inheritable
* @param name
* @param member
*/
addMember: function(name, member) {
oneMember[name] = member;
this.addMembers(oneMember);
delete oneMember[name];
return this;
},
/**
* Borrow another class' members to the prototype of this class.
*
* Ext.define('Bank', {
* money: '$$$',
* printMoney: function() {
* alert('$$$$$$$');
* }
* });
*
* Ext.define('Thief', {
* ...
* });
*
* Thief.borrow(Bank, ['money', 'printMoney']);
*
* var steve = new Thief();
*
* alert(steve.money); // alerts '$$$'
* steve.printMoney(); // alerts '$$$$$$$'
*
* @param {Ext.Base} fromClass The class to borrow members from
* @param {Array/String} members The names of the members to borrow
* @return {Ext.Base} this
* @static
* @inheritable
* @private
*/
borrow: function(fromClass, members) {
Ext.classSystemMonitor && Ext.classSystemMonitor(this, 'Ext.Base#borrow', arguments);
var prototype = fromClass.prototype,
membersObj = {},
i, ln, name;
members = Ext.Array.from(members);
for (i = 0 , ln = members.length; i < ln; i++) {
name = members[i];
membersObj[name] = prototype[name];
}
return this.addMembers(membersObj);
},
/**
* Override members of this class. Overridden methods can be invoked via
* {@link Ext.Base#callParent}.
*
* Ext.define('My.Cat', {
* constructor: function() {
* alert("I'm a cat!");
* }
* });
*
* My.Cat.override({
* constructor: function() {
* alert("I'm going to be a cat!");
*
* this.callParent(arguments);
*
* alert("Meeeeoooowwww");
* }
* });
*
* var kitty = new My.Cat(); // alerts "I'm going to be a cat!"
* // alerts "I'm a cat!"
* // alerts "Meeeeoooowwww"
*
* Direct use of this method should be rare. Use {@link Ext#define Ext.define}
* instead:
*
* Ext.define('My.CatOverride', {
* override: 'My.Cat',
* constructor: function() {
* alert("I'm going to be a cat!");
*
* this.callParent(arguments);
*
* alert("Meeeeoooowwww");
* }
* });
*
* The above accomplishes the same result but can be managed by the {@link Ext.Loader}
* which can properly order the override and its target class and the build process
* can determine whether the override is needed based on the required state of the
* target class (My.Cat).
*
* @param {Object} members The properties to add to this class. This should be
* specified as an object literal containing one or more properties.
* @return {Ext.Base} this class
* @static
* @inheritable
*/
override: function(members) {
var me = this,
statics = members.statics,
inheritableStatics = members.inheritableStatics,
config = members.config,
mixins = members.mixins,
cachedConfig = members.cachedConfig;
if (statics || inheritableStatics || config) {
members = Ext.apply({}, members);
}
if (statics) {
me.addMembers(statics, true);
delete members.statics;
}
if (inheritableStatics) {
me.addInheritableStatics(inheritableStatics);
delete members.inheritableStatics;
}
if (members.platformConfig) {
me.addPlatformConfig(members);
}
if (config) {
me.addConfig(config);
delete members.config;
}
if (cachedConfig) {
me.addCachedConfig(cachedConfig);
delete members.cachedConfig;
}
delete members.mixins;
me.addMembers(members);
if (mixins) {
me.mixin(mixins);
}
return me;
},
addPlatformConfig: function(data) {
var me = this,
platformConfigs = data.platformConfig,
config = data.config,
added, classConfigs, configs, configurator, hoisted, keys, name, value, i, ln;
delete data.platformConfig;
if (platformConfigs instanceof Array) {
throw new Error('platformConfigs must be specified as an object.');
}
configurator = me.getConfigurator();
classConfigs = configurator.configs;
// Get the keys shortest to longest (ish).
keys = Ext.getPlatformConfigKeys(platformConfigs);
// To leverage the Configurator#add method, we want to generate potentially
// two objects to pass in: "added" and "hoisted". For any properties in an
// active platformConfig rule that set proper Configs in the base class, we
// need to put them in "added". If instead of the proper Config coming from
// a base class, it comes from this class's config block, we still need to
// put that config in "added" but we also need move the class-level config
// out of "config" and into "hoisted".
//
// This will ensure that the config defined at the class level is added to
// the Configurator first.
for (i = 0 , ln = keys.length; i < ln; ++i) {
configs = platformConfigs[keys[i]];
hoisted = added = null;
for (name in configs) {
value = configs[name];
// We have a few possibilities for each config name:
if (config && name in config) {
// It is a proper Config defined by this class.
(added || (added = {}))[name] = value;
(hoisted || (hoisted = {}))[name] = config[name];
delete config[name];
} else if (name in classConfigs) {
// It is a proper Config defined by a base class.
(added || (added = {}))[name] = value;
} else {
// It is just a property to put on the prototype.
data[name] = value;
}
}
if (hoisted) {
configurator.add(hoisted);
}
if (added) {
configurator.add(added);
}
}
},
/**
* @protected
* @static
* @inheritable
*/
callParent: function(args) {
var method;
// This code is intentionally inlined for the least amount of debugger stepping
return (method = this.callParent.caller) && (method.$previous || ((method = method.$owner ? method : method.caller) && method.$owner.superclass.self[method.$name])).apply(this, args || noArgs);
},
/**
* @protected
* @static
* @inheritable
*/
callSuper: function(args) {
var method;
// This code is intentionally inlined for the least amount of debugger stepping
return (method = this.callSuper.caller) && ((method = method.$owner ? method : method.caller) && method.$owner.superclass.self[method.$name]).apply(this, args || noArgs);
},
/**
* Used internally by the mixins pre-processor
* @private
* @static
* @inheritable
*/
mixin: function(name, mixinClass) {
var me = this,
mixin, prototype, key, statics, i, ln, mixinName, name, mixinValue, mixins, mixinStatics;
if (typeof name !== 'string') {
mixins = name;
if (mixins instanceof Array) {
for (i = 0 , ln = mixins.length; i < ln; i++) {
mixin = mixins[i];
me.mixin(mixin.prototype.mixinId || mixin.$className, mixin);
}
} else {
// Not a string or array - process the object form:
// mixins: {
// foo: ...
// }
for (mixinName in mixins) {
me.mixin(mixinName, mixins[mixinName]);
}
}
return;
}
mixin = mixinClass.prototype;
prototype = me.prototype;
if (mixin.onClassMixedIn) {
mixin.onClassMixedIn.call(mixinClass, me);
}
if (!prototype.hasOwnProperty('mixins')) {
if ('mixins' in prototype) {
prototype.mixins = Ext.Object.chain(prototype.mixins);
} else {
prototype.mixins = {};
}
}
for (key in mixin) {
mixinValue = mixin[key];
if (key === 'mixins') {
// if 2 superclasses (e.g. a base class and a mixin) of this class both
// have a mixin with the same id, the first one wins, that is to say,
// the first mixin's methods to be applied to the prototype will not
// be overwritten by the second one. Since this is the case we also
// want to make sure we use the first mixin's prototype as the mixin
// reference, hence the "applyIf" below. A real world example of this
// is Ext.Widget which mixes in Ext.mixin.Observable. Ext.Widget can
// be mixed into subclasses of Ext.Component, which mixes in
// Ext.util.Observable. In this example, since the first "observable"
// mixin's methods win, we also want its reference to be preserved.
Ext.applyIf(prototype.mixins, mixinValue);
} else if (!(key === 'mixinId' || key === 'config' || key === '$inheritableStatics') && (prototype[key] === undefined)) {
prototype[key] = mixinValue;
}
}
// Mixin statics inheritance
statics = mixin.$inheritableStatics;
if (statics) {
mixinStatics = {};
for (name in statics) {
if (!me.hasOwnProperty(name)) {
mixinStatics[name] = mixinClass[name];
}
}
me.addInheritableStatics(mixinStatics);
}
if ('config' in mixin) {
me.addConfig(mixin.config, mixinClass);
}
prototype.mixins[name] = mixin;
if (mixin.afterClassMixedIn) {
mixin.afterClassMixedIn.call(mixinClass, me);
}
return me;
},
/**
* Adds new config properties to this class. This is called for classes when they
* are declared, then for any mixins that class may define and finally for any
* overrides defined that target the class.
*
* @param {Object} config
* @param {Ext.Class} [mixinClass] The mixin class if the configs are from a mixin.
* @private
* @static
* @inheritable
*/
addConfig: function(config, mixinClass) {
var cfg = this.$config || this.getConfigurator();
cfg.add(config, mixinClass);
},
addCachedConfig: function(config, isMixin) {
var cached = {},
key;
for (key in config) {
cached[key] = {
cached: true,
$value: config[key]
};
}
this.addConfig(cached, isMixin);
},
/**
* Returns the `Ext.Configurator` for this class.
*
* @return {Ext.Configurator}
* @private
* @static
* @inheritable
*/
getConfigurator: function() {
// the Ext.Configurator ctor will set $config so micro-opt out fn call:
return this.$config || new Ext.Configurator(this);
},
/**
* Get the current class' name in string format.
*
* Ext.define('My.cool.Class', {
* constructor: function() {
* alert(this.self.getName()); // alerts 'My.cool.Class'
* }
* });
*
* My.cool.Class.getName(); // 'My.cool.Class'
*
* @return {String} className
* @static
* @inheritable
*/
getName: function() {
return Ext.getClassName(this);
},
/**
* Create aliases for existing prototype methods. Example:
*
* Ext.define('My.cool.Class', {
* method1: function() { ... },
* method2: function() { ... }
* });
*
* var test = new My.cool.Class();
*
* My.cool.Class.createAlias({
* method3: 'method1',
* method4: 'method2'
* });
*
* test.method3(); // test.method1()
*
* My.cool.Class.createAlias('method5', 'method3');
*
* test.method5(); // test.method3() -> test.method1()
*
* @param {String/Object} alias The new method name, or an object to set multiple aliases. See
* {@link Ext.Function#flexSetter flexSetter}
* @param {String/Object} origin The original method name
* @static
* @inheritable
* @method
*/
createAlias: flexSetter(function(alias, origin) {
aliasOneMember[alias] = function() {
return this[origin].apply(this, arguments);
};
this.override(aliasOneMember);
delete aliasOneMember[alias];
})
});
// Capture the set of static members on Ext.Base that we want to copy to all
// derived classes. This array is used by Ext.Class as well as the optimizer.
for (baseStaticMember in Base) {
if (Base.hasOwnProperty(baseStaticMember)) {
baseStaticMembers.push(baseStaticMember);
}
}
Base.$staticMembers = baseStaticMembers;
Base.getConfigurator();
// lazily create now so as not capture in $staticMembers
Base.addMembers({
/** @private */
$className: 'Ext.Base',
/**
* @property {Boolean} isInstance
* This value is `true` and is used to identify plain objects from instances of
* a defined class.
* @protected
* @readonly
*/
isInstance: true,
/**
* @property {Boolean} [$configPrefixed]
* The value `true` causes `config` values to be stored on instances using a
* property name prefixed with an underscore ("_") character. A value of `false`
* stores `config` values as properties using their exact name (no prefix).
* @private
* @since 5.0.0
*/
$configPrefixed: true,
/**
* @property {Boolean} [$configStrict]
* The value `true` instructs the `initConfig` method to only honor values for
* properties declared in the `config` block of a class. When `false`, properties
* that are not declared in a `config` block will be placed on the instance.
* @private
* @since 5.0.0
*/
$configStrict: true,
/**
* @property {Boolean} isConfiguring
* This property is set to `true` during the call to `initConfig`.
* @protected
* @readonly
* @since 5.0.0
*/
isConfiguring: false,
/**
* @property {Boolean} isFirstInstance
* This property is set to `true` if this instance is the first of its class.
* @protected
* @readonly
* @since 5.0.0
*/
isFirstInstance: false,
/**
* @property {Boolean} destroyed
* This property is set to `true` after the `destroy` method is called.
* @protected
*/
destroyed: false,
/**
* @property {Boolean/"async"} [clearPropertiesOnDestroy=true]
* Setting this property to `false` will prevent nulling object references
* on a Class instance after destruction. Setting this to `"async"` will delay
* the clearing for approx 50ms.
* @protected
* @since 6.2.0
*/
clearPropertiesOnDestroy: true,
/**
* @property {Boolean} [clearPrototypeOnDestroy=false]
* Setting this property to `true` will result in setting the object's
* prototype to `null` after the destruction sequence is fully completed.
* After that, most attempts at calling methods on the object instance
* will result in "method not defined" exception. This can be very helpful
* with tracking down otherwise hard to find bugs like runaway Ajax requests,
* timed functions not cleared on destruction, etc.
*
* Note that this option can only work in browsers that support `Object.setPrototypeOf`
* method, and is only available in debugging mode.
* @private
* @since 6.2.0
*/
clearPrototypeOnDestroy: false,
/**
* Get the reference to the class from which this object was instantiated. Note that unlike {@link Ext.Base#self},
* `this.statics()` is scope-independent and it always returns the class from which it was called, regardless of what
* `this` points to during run-time
*
* Ext.define('My.Cat', {
* statics: {
* totalCreated: 0,
* speciesName: 'Cat' // My.Cat.speciesName = 'Cat'
* },
*
* constructor: function() {
* var statics = this.statics();
*
* alert(statics.speciesName); // always equals to 'Cat' no matter what 'this' refers to
* // equivalent to: My.Cat.speciesName
*
* alert(this.self.speciesName); // dependent on 'this'
*
* statics.totalCreated++;
* },
*
* clone: function() {
* var cloned = new this.self(); // dependent on 'this'
*
* cloned.groupName = this.statics().speciesName; // equivalent to: My.Cat.speciesName
*
* return cloned;
* }
* });
*
*
* Ext.define('My.SnowLeopard', {
* extend: 'My.Cat',
*
* statics: {
* speciesName: 'Snow Leopard' // My.SnowLeopard.speciesName = 'Snow Leopard'
* },
*
* constructor: function() {
* this.callParent();
* }
* });
*
* var cat = new My.Cat(); // alerts 'Cat', then alerts 'Cat'
*
* var snowLeopard = new My.SnowLeopard(); // alerts 'Cat', then alerts 'Snow Leopard'
*
* var clone = snowLeopard.clone();
* alert(Ext.getClassName(clone)); // alerts 'My.SnowLeopard'
* alert(clone.groupName); // alerts 'Cat'
*
* alert(My.Cat.totalCreated); // alerts 3
*
* @protected
* @return {Ext.Class}
*/
statics: function() {
var method = this.statics.caller,
self = this.self;
if (!method) {
return self;
}
return method.$owner;
},
/**
* Call the "parent" method of the current method. That is the method previously
* overridden by derivation or by an override (see {@link Ext#define}).
*
* Ext.define('My.Base', {
* constructor: function (x) {
* this.x = x;
* },
*
* statics: {
* method: function (x) {
* return x;
* }
* }
* });
*
* Ext.define('My.Derived', {
* extend: 'My.Base',
*
* constructor: function () {
* this.callParent([21]);
* }
* });
*
* var obj = new My.Derived();
*
* alert(obj.x); // alerts 21
*
* This can be used with an override as follows:
*
* Ext.define('My.DerivedOverride', {
* override: 'My.Derived',
*
* constructor: function (x) {
* this.callParent([x*2]); // calls original My.Derived constructor
* }
* });
*
* var obj = new My.Derived();
*
* alert(obj.x); // now alerts 42
*
* This also works with static and private methods.
*
* Ext.define('My.Derived2', {
* extend: 'My.Base',
*
* // privates: {
* statics: {
* method: function (x) {
* return this.callParent([x*2]); // calls My.Base.method
* }
* }
* });
*
* alert(My.Base.method(10)); // alerts 10
* alert(My.Derived2.method(10)); // alerts 20
*
* Lastly, it also works with overridden static methods.
*
* Ext.define('My.Derived2Override', {
* override: 'My.Derived2',
*
* // privates: {
* statics: {
* method: function (x) {
* return this.callParent([x*2]); // calls My.Derived2.method
* }
* }
* });
*
* alert(My.Derived2.method(10); // now alerts 40
*
* To override a method and replace it and also call the superclass method, use
* {@link #method-callSuper}. This is often done to patch a method to fix a bug.
*
* @protected
* @param {Array/Arguments} args The arguments, either an array or the `arguments` object
* from the current method, for example: `this.callParent(arguments)`
* @return {Object} Returns the result of calling the parent method
*/
callParent: function(args) {
// NOTE: this code is deliberately as few expressions (and no function calls)
// as possible so that a debugger can skip over this noise with the minimum number
// of steps. Basically, just hit Step Into until you are where you really wanted
// to be.
var method,
superMethod = (method = this.callParent.caller) && (method.$previous || ((method = method.$owner ? method : method.caller) && method.$owner.superclass[method.$name]));
if (!superMethod) {
method = this.callParent.caller;
var parentClass, methodName;
if (!method.$owner) {
if (!method.caller) {
throw new Error("Attempting to call a protected method from the public scope, which is not allowed");
}
method = method.caller;
}
parentClass = method.$owner.superclass;
methodName = method.$name;
if (!(methodName in parentClass)) {
throw new Error("this.callParent() was called but there's no such method (" + methodName + ") found in the parent class (" + (Ext.getClassName(parentClass) || 'Object') + ")");
}
}
return superMethod.apply(this, args || noArgs);
},
/**
* This method is used by an **override** to call the superclass method but
* bypass any overridden method. This is often done to "patch" a method that
* contains a bug but for whatever reason cannot be fixed directly.
*
* Consider:
*
* Ext.define('Ext.some.Class', {
* method: function () {
* console.log('Good');
* }
* });
*
* Ext.define('Ext.some.DerivedClass', {
* extend: 'Ext.some.Class',
*
* method: function () {
* console.log('Bad');
*
* // ... logic but with a bug ...
*
* this.callParent();
* }
* });
*
* To patch the bug in `Ext.some.DerivedClass.method`, the typical solution is to create an
* override:
*
* Ext.define('App.patches.DerivedClass', {
* override: 'Ext.some.DerivedClass',
*
* method: function () {
* console.log('Fixed');
*
* // ... logic but with bug fixed ...
*
* this.callSuper();
* }
* });
*
* The patch method cannot use {@link #method-callParent} to call the superclass
* `method` since that would call the overridden method containing the bug. In
* other words, the above patch would only produce "Fixed" then "Good" in the
* console log, whereas, using `callParent` would produce "Fixed" then "Bad"
* then "Good".
*
* @protected
* @param {Array/Arguments} args The arguments, either an array or the `arguments` object
* from the current method, for example: `this.callSuper(arguments)`
* @return {Object} Returns the result of calling the superclass method
*/
callSuper: function(args) {
// NOTE: this code is deliberately as few expressions (and no function calls)
// as possible so that a debugger can skip over this noise with the minimum number
// of steps. Basically, just hit Step Into until you are where you really wanted
// to be.
var method,
superMethod = (method = this.callSuper.caller) && ((method = method.$owner ? method : method.caller) && method.$owner.superclass[method.$name]);
if (!superMethod) {
method = this.callSuper.caller;
var parentClass, methodName;
if (!method.$owner) {
if (!method.caller) {
throw new Error("Attempting to call a protected method from the public scope, which is not allowed");
}
method = method.caller;
}
parentClass = method.$owner.superclass;
methodName = method.$name;
if (!(methodName in parentClass)) {
throw new Error("this.callSuper() was called but there's no such method (" + methodName + ") found in the parent class (" + (Ext.getClassName(parentClass) || 'Object') + ")");
}
}
return superMethod.apply(this, args || noArgs);
},
/**
* @property {Ext.Class} self
*
* Get the reference to the current class from which this object was instantiated. Unlike {@link Ext.Base#statics},
* `this.self` is scope-dependent and it's meant to be used for dynamic inheritance. See {@link Ext.Base#statics}
* for a detailed comparison
*
* Ext.define('My.Cat', {
* statics: {
* speciesName: 'Cat' // My.Cat.speciesName = 'Cat'
* },
*
* constructor: function() {
* alert(this.self.speciesName); // dependent on 'this'
* },
*
* clone: function() {
* return new this.self();
* }
* });
*
*
* Ext.define('My.SnowLeopard', {
* extend: 'My.Cat',
* statics: {
* speciesName: 'Snow Leopard' // My.SnowLeopard.speciesName = 'Snow Leopard'
* }
* });
*
* var cat = new My.Cat(); // alerts 'Cat'
* var snowLeopard = new My.SnowLeopard(); // alerts 'Snow Leopard'
*
* var clone = snowLeopard.clone();
* alert(Ext.getClassName(clone)); // alerts 'My.SnowLeopard'
*
* @protected
*/
self: Base,
// Default constructor, simply returns `this`
constructor: function() {
return this;
},
/**
* Initialize configuration for this class. a typical example:
*
* Ext.define('My.awesome.Class', {
* // The default config
* config: {
* name: 'Awesome',
* isAwesome: true
* },
*
* constructor: function(config) {
* this.initConfig(config);
* }
* });
*
* var awesome = new My.awesome.Class({
* name: 'Super Awesome'
* });
*
* alert(awesome.getName()); // 'Super Awesome'
*
* @protected
* @param {Object} config
* @return {Ext.Base} this
*/
initConfig: function(instanceConfig) {
var me = this,
cfg = me.self.getConfigurator();
me.initConfig = Ext.emptyFn;
// ignore subsequent calls to initConfig
me.initialConfig = instanceConfig || {};
cfg.configure(me, instanceConfig);
return me;
},
beforeInitConfig: Ext.emptyFn,
/**
* Returns a specified config property value. If the name parameter is not passed,
* all current configuration options will be returned as key value pairs.
* @method
* @param {String} [name] The name of the config property to get.
* @param {Boolean} [peek=false] `true` to peek at the raw value without calling the getter.
* @return {Object} The config property value.
*/
getConfig: getConfig,
/**
* Sets a single/multiple configuration options.
* @method
* @param {String/Object} name The name of the property to set, or a set of key value pairs to set.
* @param {Object} [value] The value to set for the name parameter.
* @return {Ext.Base} this
*/
setConfig: function(name, value, /* private */
options) {
// options can have the following properties:
// - defaults `true` to only set the config(s) that have not been already set on
// this instance.
// - strict `false` to apply properties to the instance that are not configs,
// and do not have setters.
var me = this,
config;
if (name) {
if (typeof name === 'string') {
config = {};
config[name] = value;
} else {
config = name;
}
me.self.getConfigurator().reconfigure(me, config, options);
}
return me;
},
/**
* @private
*/
getCurrentConfig: function() {
var cfg = this.self.getConfigurator();
return cfg.getCurrentConfig(this);
},
/**
* @private
* @param config
*/
hasConfig: function(name) {
return name in this.defaultConfig;
},
/**
* Returns the initial configuration passed to the constructor when
* instantiating this class.
*
* Given this example Ext.button.Button definition and instance:
*
* Ext.define('MyApp.view.Button', {
* extend: 'Ext.button.Button',
* xtype: 'mybutton',
*
* scale: 'large',
* enableToggle: true
* });
*
* var btn = Ext.create({
* xtype: 'mybutton',
* renderTo: Ext.getBody(),
* text: 'Test Button'
* });
*
* Calling `btn.getInitialConfig()` would return an object including the config
* options passed to the `create` method:
*
* xtype: 'mybutton',
* renderTo: // The document body itself
* text: 'Test Button'
*
* Calling `btn.getInitialConfig('text')`returns **'Test Button'**.
*
* @param {String} [name] Name of the config option to return.
* @return {Object/Mixed} The full config object or a single config value
* when `name` parameter specified.
*/
getInitialConfig: function(name) {
var config = this.config;
if (!name) {
return config;
}
return config[name];
},
$links: null,
/**
* Adds a "destroyable" object to an internal list of objects that will be destroyed
* when this instance is destroyed (via `{@link #destroy}`).
* @param {String} name
* @param {Object} value
* @return {Object} The `value` passed.
* @private
*/
link: function(name, value) {
var me = this,
links = me.$links || (me.$links = {});
links[name] = true;
me[name] = value;
return value;
},
/**
* Destroys a given set of `{@link #link linked}` objects. This is only needed if
* the linked object is being destroyed before this instance.
* @param {String[]} names The names of the linked objects to destroy.
* @return {Ext.Base} this
* @private
*/
unlink: function(names) {
var me = this,
i, ln, link, value;
if (!Ext.isArray(names)) {
Ext.raise('Invalid argument - expected array of strings');
}
for (i = 0 , ln = names.length; i < ln; i++) {
link = names[i];
value = me[link];
if (value) {
if (value.isInstance && !value.destroyed) {
value.destroy();
} else if (value.parentNode && 'nodeType' in value) {
value.parentNode.removeChild(value);
}
}
me[link] = null;
}
return me;
},
$reap: function() {
var me = this,
protectedProps = me.$noClearOnDestroy,
prop, value, type;
for (prop in me) {
if ((!protectedProps || !protectedProps[prop]) && me.hasOwnProperty(prop)) {
value = me[prop];
type = typeof value;
// Object may retain references to other objects. Functions can do too
// if they are closures, and most of the *own* function properties
// are closures indeed. We skip Ext.emptyFn and the like though,
// they're mostly harmless.
if (type === 'object' || (type === 'function' && !value.$noClearOnDestroy)) {
me[prop] = null;
}
}
}
// We also want to make sure no methods are called on the destroyed object,
// because that may lead to accessing nulled properties and resulting exceptions.
if (me.clearPrototypeOnDestroy && !me.$vetoClearingPrototypeOnDestroy && Object.setPrototypeOf) {
Object.setPrototypeOf(me, null);
}
},
/**
* This method is called to cleanup an object and its resources. After calling
* this method, the object should not be used any further in any way, including
* access to its methods and properties.
*
* To prevent potential memory leaks, all object references will be nulled
* at the end of destruction sequence, unless {@link #clearPropertiesOnDestroy}
* is set to `false`.
*/
destroy: function() {
var me = this,
links = me.$links,
clearPropertiesOnDestroy = me.clearPropertiesOnDestroy;
if (links) {
me.$links = null;
me.unlink(Ext.Object.getKeys(links));
}
me.destroy = Ext.emptyFn;
// isDestroyed added for compat reasons
me.isDestroyed = me.destroyed = true;
// By this time the destruction is complete. Now we can make sure
// no objects are retained by the husk of this ex-Instance.
if (clearPropertiesOnDestroy === true) {
me.$reap();
} else if (clearPropertiesOnDestroy) {
if (clearPropertiesOnDestroy !== 'async') {
Ext.raise('Invalid value for clearPropertiesOnDestroy');
}
Reaper.add(me);
}
}
});
/**
* Call the original method that was previously overridden with {@link Ext.Base#override}
*
* Ext.define('My.Cat', {
* constructor: function() {
* alert("I'm a cat!");
* }
* });
*
* My.Cat.override({
* constructor: function() {
* alert("I'm going to be a cat!");
*
* this.callOverridden();
*
* alert("Meeeeoooowwww");
* }
* });
*
* var kitty = new My.Cat(); // alerts "I'm going to be a cat!"
* // alerts "I'm a cat!"
* // alerts "Meeeeoooowwww"
*
* @param {Array/Arguments} args The arguments, either an array or the `arguments` object
* from the current method, for example: `this.callOverridden(arguments)`
* @return {Object} Returns the result of calling the overridden method
* @protected
* @deprecated Use {@link #callParent} instead.
*/
BasePrototype.callOverridden = BasePrototype.callParent;
Ext.privacyViolation = function(cls, existing, member, isStatic) {
var name = member.$name,
conflictCls = existing.$owner && existing.$owner.$className,
s = isStatic ? 'static ' : '',
msg = member.$privacy ? 'Private ' + s + member.$privacy + ' method "' + name + '"' : 'Public ' + s + 'method "' + name + '"';
if (cls.$className) {
msg = cls.$className + ': ' + msg;
}
if (!existing.$privacy) {
msg += conflictCls ? ' hides public method inherited from ' + conflictCls : ' hides inherited public method.';
} else {
msg += conflictCls ? ' conflicts with private ' + existing.$privacy + ' method declared by ' + conflictCls : ' conflicts with inherited private ' + existing.$privacy + ' method.';
}
var compat = Ext.getCompatVersion();
var ver = Ext.getVersion();
// When compatibility is enabled, log problems instead of throwing errors.
if (ver && compat && compat.lt(ver)) {
Ext.log.error(msg);
} else {
Ext.raise(msg);
}
};
return Base;
}(Ext.Function.flexSetter));
/**
* This class is used to manage simple, LRU caches. It provides an absolutely minimal
* container interface. It is created like this:
*
* this.itemCache = new Ext.util.Cache({
* miss: function (key) {
* return new CacheItem(key);
* }
* });
*
* The `{@link #miss}` abstract method must be implemented by either a derived class or
* at the instance level as shown above.
*
* Once the cache exists and it can handle cache misses, the cache is used like so:
*
* var item = this.itemCache.get(key);
*
* The `key` is some value that uniquely identifies the cached item.
*
* In some cases, creating the cache item may require more than just the lookup key. In
* that case, any extra arguments passed to `get` will be passed to `miss`.
*
* this.otherCache = new Ext.util.Cache({
* miss: function (key, extra) {
* return new CacheItem(key, extra);
* }
* });
*
* var item = this.otherCache.get(key, extra);
*
* To process items as they are removed, you can provide an `{@link #evict}` method. The
* stock method is `Ext.emptyFn` and so does nothing.
*
* For example:
*
* this.itemCache = new Ext.util.Cache({
* miss: function (key) {
* return new CacheItem(key);
* },
*
* evict: function (key, cacheItem) {
* cacheItem.destroy();
* }
* });
*
* @class Ext.util.Cache
* @private
* @since 5.1.0
*/
(function(Cache, prototype) {
// @define Ext.util.Cache
// NOTE: We have to implement this class old-school because it is used by the
// platformConfig class processor (so Ext.define is not yet ready for action).
(Ext.util || (Ext.util = {})).Cache = Cache = function(config) {
var me = this,
head;
if (config) {
Ext.apply(me, config);
}
// Give all entries the same object shape.
me.head = head = {
id: (me.seed = 0),
key: null,
value: null
};
me.map = {};
head.next = head.prev = head;
};
Cache.prototype = prototype = {
/**
* @cfg {Number} maxSize The maximum size the cache is allowed to grow to before
* further additions cause removal of the least recently used entry.
*/
maxSize: 100,
/**
* @property {Number} count
* The number of items in this cache.
* @readonly
*/
count: 0,
/**
* This method is called by `{@link #get}` when the key is not found in the cache.
* The implementation of this method should create the (expensive) value and return
* it. Whatever arguments were passed to `{@link #get}` will be passed on to this
* method.
*
* @param {String} key The cache lookup key for the item.
* @param {Object...} args Any other arguments originally passed to `{@link #get}`.
* @method miss
* @abstract
* @protected
*/
/**
* Removes all items from this cache.
*/
clear: function() {
var me = this,
head = me.head,
entry = head.next;
head.next = head.prev = head;
if (!me.evict.$nullFn) {
for (; entry !== head; entry = entry.next) {
me.evict(entry.key, entry.value);
}
}
me.count = 0;
},
/**
* Calls the given function `fn` for each item in the cache. The items will be passed
* to `fn` from most-to-least recently used.
* @param {Function} fn The function to call for each cache item.
* @param {String} fn.key The cache key for the item.
* @param {Object} fn.value The value in the cache for the item.
* @param {Object} [scope] The `this` pointer to use for `fn`.
*/
each: function(fn, scope) {
scope = scope || this;
for (var head = this.head,
ent = head.next; ent !== head; ent = ent.next) {
if (fn.call(scope, ent.key, ent.value)) {
break;
}
}
},
/**
* Finds an item in this cache and returns its value. If the item is present, it is
* shuffled into the MRU (most-recently-used) position as necessary. If the item is
* missing, the `{@link #miss}` method is called to create the item.
*
* @param {String} key The cache key of the item.
* @param {Object...} args Arguments for the `miss` method should it be needed.
* @return {Object} The cached object.
*/
get: function(key) {
var me = this,
head = me.head,
map = me.map,
entry = map[key];
if (entry) {
if (entry.prev !== head) {
// The entry is not at the front, so remove it and insert it at the front
// (to make it the MRU - Most Recently Used).
me.unlinkEntry(entry);
me.linkEntry(entry);
}
} else {
map[key] = entry = {
id: ++me.seed,
key: key,
value: me.miss.apply(me, arguments)
};
me.linkEntry(entry);
++me.count;
while (me.count > me.maxSize) {
me.unlinkEntry(head.prev, true);
--me.count;
}
}
return entry.value;
},
//-------------------------------------------------------------------------
// Internals
/**
* This method is called internally from `{@link #get}` when the cache is full and
* the least-recently-used (LRU) item has been removed.
*
* @param {String} key The cache lookup key for the item being removed.
* @param {Object} value The cache value (returned by `{@link #miss}`) for the item
* being removed.
* @method evict
* @template
* @protected
*/
evict: Ext.emptyFn,
/**
* Inserts the given entry at the front (MRU) end of the entry list.
* @param {Object} entry The cache item entry.
* @private
*/
linkEntry: function(entry) {
var head = this.head,
first = head.next;
entry.next = first;
entry.prev = head;
head.next = entry;
first.prev = entry;
},
/**
* Removes the given entry from the entry list.
* @param {Object} entry The cache item entry.
* @param {Boolean} evicted Pass `true` if `{@link #evict}` should be called.
* @private
*/
unlinkEntry: function(entry, evicted) {
var next = entry.next,
prev = entry.prev;
prev.next = next;
next.prev = prev;
if (evicted) {
this.evict(entry.key, entry.value);
}
}
};
prototype.destroy = prototype.clear;
}());
/**
* @class Ext.Class
*
* This is a low level factory that is used by {@link Ext#define Ext.define} and should not be used
* directly in application code.
*
* The configs of this class are intended to be used in `Ext.define` calls to describe the class you
* are declaring. For example:
*
* Ext.define('App.util.Thing', {
* extend: 'App.util.Other',
*
* alias: 'util.thing',
*
* config: {
* foo: 42
* }
* });
*
* Ext.Class is the factory and **not** the superclass of everything. For the base class that **all**
* classes inherit from, see {@link Ext.Base}.
*/
(function() {
// @tag class
// @define Ext.Class
// @require Ext.Base
// @require Ext.Util
// @require Ext.util.Cache
var ExtClass,
Base = Ext.Base,
baseStaticMembers = Base.$staticMembers,
ruleKeySortFn = function(a, b) {
// longest to shortest, by text if names are equal
return (a.length - b.length) || ((a < b) ? -1 : ((a > b) ? 1 : 0));
};
// Creates a constructor that has nothing extra in its scope chain.
function makeCtor(className) {
function constructor() {
// Opera has some problems returning from a constructor when Dragonfly isn't running. The || null seems to
// be sufficient to stop it misbehaving. Known to be required against 10.53, 11.51 and 11.61.
return this.constructor.apply(this, arguments) || null;
}
if (className) {
constructor.name = className;
}
return constructor;
}
/**
* @method constructor
* Create a new anonymous class.
*
* @param {Object} data An object represent the properties of this class
* @param {Function} onCreated Optional, the callback function to be executed when this class is fully created.
* Note that the creation process can be asynchronous depending on the pre-processors used.
*
* @return {Ext.Base} The newly created class
*/
Ext.Class = ExtClass = function(Class, data, onCreated) {
if (typeof Class != 'function') {
onCreated = data;
data = Class;
Class = null;
}
if (!data) {
data = {};
}
Class = ExtClass.create(Class, data);
ExtClass.process(Class, data, onCreated);
return Class;
};
Ext.apply(ExtClass, {
makeCtor: makeCtor,
/**
* @private
*/
onBeforeCreated: function(Class, data, hooks) {
Ext.classSystemMonitor && Ext.classSystemMonitor(Class, '>> Ext.Class#onBeforeCreated', arguments);
Class.addMembers(data);
hooks.onCreated.call(Class, Class);
Ext.classSystemMonitor && Ext.classSystemMonitor(Class, '<< Ext.Class#onBeforeCreated', arguments);
},
/**
* @private
*/
create: function(Class, data) {
var i = baseStaticMembers.length,
name;
if (!Class) {
Class = makeCtor(data.$className);
}
while (i--) {
name = baseStaticMembers[i];
Class[name] = Base[name];
}
return Class;
},
/**
* @private
*/
process: function(Class, data, onCreated) {
var preprocessorStack = data.preprocessors || ExtClass.defaultPreprocessors,
registeredPreprocessors = this.preprocessors,
hooks = {
onBeforeCreated: this.onBeforeCreated
},
preprocessors = [],
preprocessor, preprocessorsProperties, i, ln, j, subLn, preprocessorProperty;
delete data.preprocessors;
Class._classHooks = hooks;
for (i = 0 , ln = preprocessorStack.length; i < ln; i++) {
preprocessor = preprocessorStack[i];
if (typeof preprocessor == 'string') {
preprocessor = registeredPreprocessors[preprocessor];
preprocessorsProperties = preprocessor.properties;
if (preprocessorsProperties === true) {
preprocessors.push(preprocessor.fn);
} else if (preprocessorsProperties) {
for (j = 0 , subLn = preprocessorsProperties.length; j < subLn; j++) {
preprocessorProperty = preprocessorsProperties[j];
if (data.hasOwnProperty(preprocessorProperty)) {
preprocessors.push(preprocessor.fn);
break;
}
}
}
} else {
preprocessors.push(preprocessor);
}
}
hooks.onCreated = onCreated ? onCreated : Ext.emptyFn;
hooks.preprocessors = preprocessors;
this.doProcess(Class, data, hooks);
},
doProcess: function(Class, data, hooks) {
var me = this,
preprocessors = hooks.preprocessors,
preprocessor = preprocessors.shift(),
doProcess = me.doProcess;
for (; preprocessor; preprocessor = preprocessors.shift()) {
// Returning false signifies an asynchronous preprocessor - it will call doProcess when we can continue
if (preprocessor.call(me, Class, data, hooks, doProcess) === false) {
return;
}
}
hooks.onBeforeCreated.apply(me, arguments);
},
/**
* @private
* */
preprocessors: {},
/**
* Register a new pre-processor to be used during the class creation process
*
* @param {String} name The pre-processor's name
* @param {Function} fn The callback function to be executed. Typical format:
*
* function(cls, data, fn) {
* // Your code here
*
* // Execute this when the processing is finished.
* // Asynchronous processing is perfectly ok
* if (fn) {
* fn.call(this, cls, data);
* }
* });
*
* @param {Function} fn.cls The created class
* @param {Object} fn.data The set of properties passed in {@link Ext.Class} constructor
* @param {Function} fn.fn The callback function that **must** to be executed when this
* pre-processor finishes, regardless of whether the processing is synchronous or asynchronous.
* @return {Ext.Class} this
* @private
* @static
*/
registerPreprocessor: function(name, fn, properties, position, relativeTo) {
if (!position) {
position = 'last';
}
if (!properties) {
properties = [
name
];
}
this.preprocessors[name] = {
name: name,
properties: properties || false,
fn: fn
};
this.setDefaultPreprocessorPosition(name, position, relativeTo);
return this;
},
/**
* Retrieve a pre-processor callback function by its name, which has been registered before
*
* @param {String} name
* @return {Function} preprocessor
* @private
* @static
*/
getPreprocessor: function(name) {
return this.preprocessors[name];
},
/**
* @private
*/
getPreprocessors: function() {
return this.preprocessors;
},
/**
* @private
*/
defaultPreprocessors: [],
/**
* Retrieve the array stack of default pre-processors
* @return {Function[]} defaultPreprocessors
* @private
* @static
*/
getDefaultPreprocessors: function() {
return this.defaultPreprocessors;
},
/**
* Set the default array stack of default pre-processors
*
* @private
* @param {Array} preprocessors
* @return {Ext.Class} this
* @static
*/
setDefaultPreprocessors: function(preprocessors) {
this.defaultPreprocessors = Ext.Array.from(preprocessors);
return this;
},
/**
* Insert this pre-processor at a specific position in the stack, optionally relative to
* any existing pre-processor. For example:
*
* Ext.Class.registerPreprocessor('debug', function(cls, data, fn) {
* // Your code here
*
* if (fn) {
* fn.call(this, cls, data);
* }
* }).setDefaultPreprocessorPosition('debug', 'last');
*
* @private
* @param {String} name The pre-processor name. Note that it needs to be registered with
* {@link Ext.Class#registerPreprocessor registerPreprocessor} before this
* @param {String} offset The insertion position. Four possible values are:
* 'first', 'last', or: 'before', 'after' (relative to the name provided in the third argument)
* @param {String} relativeName
* @return {Ext.Class} this
* @static
*/
setDefaultPreprocessorPosition: function(name, offset, relativeName) {
var defaultPreprocessors = this.defaultPreprocessors,
index;
if (typeof offset == 'string') {
if (offset === 'first') {
defaultPreprocessors.unshift(name);
return this;
} else if (offset === 'last') {
defaultPreprocessors.push(name);
return this;
}
offset = (offset === 'after') ? 1 : -1;
}
index = Ext.Array.indexOf(defaultPreprocessors, relativeName);
if (index !== -1) {
Ext.Array.splice(defaultPreprocessors, Math.max(0, index + offset), 0, name);
}
return this;
}
});
/**
* @cfg {String} extend
* The parent class that this class extends. For example:
*
* Ext.define('Person', {
* say: function(text) { alert(text); }
* });
*
* Ext.define('Developer', {
* extend: 'Person',
* say: function(text) { this.callParent(["print "+text]); }
* });
*/
ExtClass.registerPreprocessor('extend', function(Class, data, hooks) {
Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#extendPreProcessor', arguments);
var Base = Ext.Base,
basePrototype = Base.prototype,
extend = data.extend,
Parent, parentPrototype, i;
delete data.extend;
if (extend && extend !== Object) {
Parent = extend;
} else {
Parent = Base;
}
parentPrototype = Parent.prototype;
if (!Parent.$isClass) {
for (i in basePrototype) {
if (!parentPrototype[i]) {
parentPrototype[i] = basePrototype[i];
}
}
}
Class.extend(Parent);
Class.triggerExtended.apply(Class, arguments);
if (data.onClassExtended) {
Class.onExtended(data.onClassExtended, Class);
delete data.onClassExtended;
}
}, true);
// true to always run this preprocessor even w/o "extend" keyword
/**
* @cfg {Object} privates
* The `privates` config is a list of methods intended to be used internally by the
* framework. Methods are placed in a `privates` block to prevent developers from
* accidentally overriding framework methods in custom classes.
*
* Ext.define('Computer', {
* privates: {
* runFactory: function(brand) {
* // internal only processing of brand passed to factory
* this.factory(brand);
* }
* },
*
* factory: function (brand) {}
* });
*
* In order to override a method from a `privates` block, the overridden method must
* also be placed in a `privates` block within the override class.
*
* Ext.define('Override.Computer', {
* override: 'Computer',
* privates: {
* runFactory: function() {
* // overriding logic
* }
* }
* });
*/
ExtClass.registerPreprocessor('privates', function(Class, data) {
Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#privatePreprocessor', arguments);
var privates = data.privates,
statics = privates.statics,
privacy = privates.privacy || true;
delete data.privates;
delete privates.statics;
// We have to add this preprocessor so that private getters/setters are picked up
// by the config system. This also catches duplication in the public part of the
// class since it is an error to override a private method with a public one.
Class.addMembers(privates, false, privacy);
if (statics) {
Class.addMembers(statics, true, privacy);
}
});
/**
* @cfg {Object} statics
* List of static methods for this class. For example:
*
* Ext.define('Computer', {
* statics: {
* factory: function(brand) {
* // 'this' in static methods refer to the class itself
* return new this(brand);
* }
* },
*
* constructor: function() { ... }
* });
*
* var dellComputer = Computer.factory('Dell');
*/
ExtClass.registerPreprocessor('statics', function(Class, data) {
Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#staticsPreprocessor', arguments);
Class.addStatics(data.statics);
delete data.statics;
});
/**
* @cfg {Object} inheritableStatics
* List of inheritable static methods for this class.
* Otherwise just like {@link #statics} but subclasses inherit these methods.
*/
ExtClass.registerPreprocessor('inheritableStatics', function(Class, data) {
Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#inheritableStaticsPreprocessor', arguments);
Class.addInheritableStatics(data.inheritableStatics);
delete data.inheritableStatics;
});
Ext.createRuleFn = function(code) {
return new Function('$c', 'with($c) { try { return (' + code + '); } catch(e) { return false;}}');
};
Ext.expressionCache = new Ext.util.Cache({
miss: Ext.createRuleFn
});
Ext.ruleKeySortFn = ruleKeySortFn;
Ext.getPlatformConfigKeys = function(platformConfig) {
var ret = [],
platform, rule;
for (platform in platformConfig) {
rule = Ext.expressionCache.get(platform);
if (rule(Ext.platformTags)) {
ret.push(platform);
}
}
ret.sort(ruleKeySortFn);
return ret;
};
/**
* @cfg {Object} platformConfig
* Allows setting config values for a class based on specific platforms. The value
* of this config is an object whose properties are "rules" and whose values are
* objects containing config values.
*
* For example:
*
* Ext.define('App.view.Foo', {
* extend: 'Ext.panel.Panel',
*
* platformConfig: {
* desktop: {
* title: 'Some Rather Descriptive Title'
* },
*
* '!desktop': {
* title: 'Short Title'
* }
* }
* });
*
* In the above, "desktop" and "!desktop" are (mutually exclusive) rules. Whichever
* evaluates to `true` will have its configs applied to the class. In this case, only
* the "title" property, but the object can contain any number of config properties.
* In this case, the `platformConfig` is evaluated as part of the class and there is
* no cost for each instance created.
*
* The rules are evaluated expressions in the context of the platform tags contained
* in `{@link Ext#platformTags Ext.platformTags}`. Any properties of that object are
* implicitly usable (as shown above).
*
* If a `platformConfig` specifies a config value, it will replace any values declared
* on the class itself.
*
* Use of `platformConfig` on instances is handled by the config system when classes
* call `{@link Ext.Base#initConfig initConfig}`. For example:
*
* Ext.create({
* xtype: 'panel',
*
* platformConfig: {
* desktop: {
* title: 'Some Rather Descriptive Title'
* },
*
* '!desktop': {
* title: 'Short Title'
* }
* }
* });
*
* The following is equivalent to the above:
*
* if (Ext.platformTags.desktop) {
* Ext.create({
* xtype: 'panel',
* title: 'Some Rather Descriptive Title'
* });
* } else {
* Ext.create({
* xtype: 'panel',
* title: 'Short Title'
* });
* }
*
* To adjust configs based on dynamic conditions, see `{@link Ext.mixin.Responsive}`.
*/
ExtClass.registerPreprocessor('platformConfig', function(Class, data, hooks) {
Class.addPlatformConfig(data);
});
/**
* @cfg {Object} config
*
* List of configuration options with their default values.
*
* __Note:__ You need to make sure {@link Ext.Base#initConfig} is called from your constructor if you are defining
* your own class or singleton, unless you are extending a Component. Otherwise the generated getter and setter
* methods will not be initialized.
*
* Each config item will have its own setter and getter method automatically generated inside the class prototype
* during class creation time, if the class does not have those methods explicitly defined.
*
* As an example, let's convert the name property of a Person class to be a config item, then add extra age and
* gender items.
*
* Ext.define('My.sample.Person', {
* config: {
* name: 'Mr. Unknown',
* age: 0,
* gender: 'Male'
* },
*
* constructor: function(config) {
* this.initConfig(config);
*
* return this;
* }
*
* // ...
* });
*
* Within the class, this.name still has the default value of "Mr. Unknown". However, it's now publicly accessible
* without sacrificing encapsulation, via setter and getter methods.
*
* var jacky = new Person({
* name: "Jacky",
* age: 35
* });
*
* alert(jacky.getAge()); // alerts 35
* alert(jacky.getGender()); // alerts "Male"
*
* jacky.walk(10); // alerts "Jacky is walking 10 steps"
*
* jacky.setName("Mr. Nguyen");
* alert(jacky.getName()); // alerts "Mr. Nguyen"
*
* jacky.walk(10); // alerts "Mr. Nguyen is walking 10 steps"
*
* Notice that we changed the class constructor to invoke this.initConfig() and pass in the provided config object.
* Two key things happened:
*
* - The provided config object when the class is instantiated is recursively merged with the default config object.
* - All corresponding setter methods are called with the merged values.
*
* Beside storing the given values, throughout the frameworks, setters generally have two key responsibilities:
*
* - Filtering / validation / transformation of the given value before it's actually stored within the instance.
* - Notification (such as firing events) / post-processing after the value has been set, or changed from a
* previous value.
*
* By standardize this common pattern, the default generated setters provide two extra template methods that you
* can put your own custom logics into, i.e: an "applyFoo" and "updateFoo" method for a "foo" config item, which are
* executed before and after the value is actually set, respectively. Back to the example class, let's validate that
* age must be a valid positive number, and fire an 'agechange' if the value is modified.
*
* Ext.define('My.sample.Person', {
* config: {
* // ...
* },
*
* constructor: {
* // ...
* },
*
* applyAge: function(age) {
* if (typeof age !== 'number' || age < 0) {
* console.warn("Invalid age, must be a positive number");
* return;
* }
*
* return age;
* },
*
* updateAge: function(newAge, oldAge) {
* // age has changed from "oldAge" to "newAge"
* this.fireEvent('agechange', this, newAge, oldAge);
* }
*
* // ...
* });
*
* var jacky = new Person({
* name: "Jacky",
* age: 'invalid'
* });
*
* alert(jacky.getAge()); // alerts 0
*
* alert(jacky.setAge(-100)); // alerts 0
* alert(jacky.getAge()); // alerts 0
*
* alert(jacky.setAge(35)); // alerts 0
* alert(jacky.getAge()); // alerts 35
*
* In other words, when leveraging the config feature, you mostly never need to define setter and getter methods
* explicitly. Instead, "apply*" and "update*" methods should be implemented where necessary. Your code will be
* consistent throughout and only contain the minimal logic that you actually care about.
*
* When it comes to inheritance, the default config of the parent class is automatically, recursively merged with
* the child's default config. The same applies for mixins.
*/
ExtClass.registerPreprocessor('config', function(Class, data) {
// Need to copy to the prototype here because that happens after preprocessors
if (data.hasOwnProperty('$configPrefixed')) {
Class.prototype.$configPrefixed = data.$configPrefixed;
}
Class.addConfig(data.config);
// We need to remove this or it will be applied by addMembers and smash the
// "config" placed on the prototype by Configurator (which includes *all* configs
// such as cachedConfigs).
delete data.config;
});
/**
* @cfg {Object} cachedConfig
*
* This configuration works in a very similar manner to the {@link #config} option.
* The difference is that the configurations are only ever processed when the first instance
* of that class is created. The processed value is then stored on the class prototype and
* will not be processed on subsequent instances of the class. Getters/setters will be generated
* in exactly the same way as {@link #config}.
*
* This option is useful for expensive objects that can be shared across class instances.
* The class itself ensures that the creation only occurs once.
*/
ExtClass.registerPreprocessor('cachedConfig', function(Class, data) {
// Need to copy to the prototype here because that happens after preprocessors
if (data.hasOwnProperty('$configPrefixed')) {
Class.prototype.$configPrefixed = data.$configPrefixed;
}
Class.addCachedConfig(data.cachedConfig);
// Remove this so it won't be placed on the prototype.
delete data.cachedConfig;
});
/**
* @cfg {String[]/Object} mixins
* List of classes to mix into this class. For example:
*
* Ext.define('CanSing', {
* sing: function() {
* alert("For he's a jolly good fellow...")
* }
* });
*
* Ext.define('Musician', {
* mixins: ['CanSing']
* })
*
* In this case the Musician class will get a `sing` method from CanSing mixin.
*
* But what if the Musician already has a `sing` method? Or you want to mix
* in two classes, both of which define `sing`? In such a cases it's good
* to define mixins as an object, where you assign a name to each mixin:
*
* Ext.define('Musician', {
* mixins: {
* canSing: 'CanSing'
* },
*
* sing: function() {
* // delegate singing operation to mixin
* this.mixins.canSing.sing.call(this);
* }
* })
*
* In this case the `sing` method of Musician will overwrite the
* mixed in `sing` method. But you can access the original mixed in method
* through special `mixins` property.
*/
ExtClass.registerPreprocessor('mixins', function(Class, data, hooks) {
Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#mixinsPreprocessor', arguments);
var mixins = data.mixins,
onCreated = hooks.onCreated;
delete data.mixins;
hooks.onCreated = function() {
Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#mixinsPreprocessor#beforeCreated', arguments);
// Put back the original onCreated before processing mixins. This allows a
// mixin to hook onCreated by access Class._classHooks.
hooks.onCreated = onCreated;
Class.mixin(mixins);
// We must go back to hooks.onCreated here because it may have changed during
// calls to onClassMixedIn.
return hooks.onCreated.apply(this, arguments);
};
});
// Backwards compatible
Ext.extend = function(Class, Parent, members) {
Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#extend-backwards-compatible', arguments);
if (arguments.length === 2 && Ext.isObject(Parent)) {
members = Parent;
Parent = Class;
Class = null;
}
var cls;
if (!Parent) {
throw new Error("[Ext.extend] Attempting to extend from a class which has not been loaded on the page.");
}
members.extend = Parent;
members.preprocessors = [
'extend',
'statics',
'inheritableStatics',
'mixins',
'platformConfig',
'config'
];
if (Class) {
cls = new ExtClass(Class, members);
// The 'constructor' is given as 'Class' but also needs to be on prototype
cls.prototype.constructor = Class;
} else {
cls = new ExtClass(members);
}
cls.prototype.override = function(o) {
for (var m in o) {
if (o.hasOwnProperty(m)) {
this[m] = o[m];
}
}
};
return cls;
};
}());
/**
* This object contains properties that describe the current device or platform. These
* values can be used in `{@link Ext.Class#platformConfig platformConfig}` as well as
* `{@link Ext.mixin.Responsive#responsiveConfig responsiveConfig}` statements.
*
* This object can be modified to include tags that are useful for the application. To
* add custom properties, it is advisable to use a sub-object. For example:
*
* Ext.platformTags.app = {
* mobile: true
* };
*
* @property {Object} platformTags
* @property {Boolean} platformTags.phone
* @property {Boolean} platformTags.tablet
* @property {Boolean} platformTags.desktop
* @property {Boolean} platformTags.touch Indicates touch inputs are available.
* @property {Boolean} platformTags.safari
* @property {Boolean} platformTags.chrome
* @property {Boolean} platformTags.windows
* @property {Boolean} platformTags.firefox
* @property {Boolean} platformTags.ios True for iPad, iPhone and iPod.
* @property {Boolean} platformTags.android
* @property {Boolean} platformTags.blackberry
* @property {Boolean} platformTags.tizen
* @member Ext
*/
// @tag class
/**
* @class Ext.Inventory
* @private
*/
Ext.Inventory = function() {
// @define Ext.Script
// @define Ext.Inventory
// @require Ext.Function
var me = this;
me.names = [];
me.paths = {};
me.alternateToName = {};
me.aliasToName = {};
me.nameToAliases = {};
me.nameToAlternates = {};
me.nameToPrefix = {};
};
Ext.Inventory.prototype = {
_array1: [
0
],
prefixes: null,
dotRe: /\./g,
wildcardRe: /\*/g,
addAlias: function(className, alias, update) {
return this.addMapping(className, alias, this.aliasToName, this.nameToAliases, update);
},
addAlternate: function(className, alternate) {
return this.addMapping(className, alternate, this.alternateToName, this.nameToAlternates);
},
addMapping: function(className, alternate, toName, nameTo, update) {
var name = className.$className || className,
mappings = name,
array = this._array1,
a, aliases, cls, i, length, nameMapping;
if (Ext.isString(name)) {
mappings = {};
mappings[name] = alternate;
}
for (cls in mappings) {
aliases = mappings[cls];
if (Ext.isString(aliases)) {
array[0] = aliases;
aliases = array;
}
length = aliases.length;
nameMapping = nameTo[cls] || (nameTo[cls] = []);
for (i = 0; i < length; ++i) {
if (!(a = aliases[i])) {
continue;
}
if (toName[a] !== cls) {
if (!update && toName[a]) {
Ext.log.warn("Overriding existing mapping: '" + a + "' From '" + toName[a] + "' to '" + cls + "'. Is this intentional?");
}
toName[a] = cls;
nameMapping.push(a);
}
}
}
},
/**
* Get the aliases of a class by the class name
*
* @param {String} name
* @return {Array} aliases
*/
getAliasesByName: function(name) {
return this.nameToAliases[name] || null;
},
getAlternatesByName: function(name) {
return this.nameToAlternates[name] || null;
},
/**
* Get the name of a class by its alias.
*
* @param {String} alias
* @return {String} className
*/
getNameByAlias: function(alias) {
return this.aliasToName[alias] || '';
},
/**
* Get the name of a class by its alternate name.
*
* @param {String} alternate
* @return {String} className
*/
getNameByAlternate: function(alternate) {
return this.alternateToName[alternate] || '';
},
/**
* Converts a string expression to an array of matching class names. An expression can
* either refers to class aliases or class names. Expressions support wildcards:
*
* // returns ['Ext.window.Window']
* var window = Ext.ClassManager.getNamesByExpression('widget.window');
*
* // returns ['widget.panel', 'widget.window', ...]
* var allWidgets = Ext.ClassManager.getNamesByExpression('widget.*');
*
* // returns ['Ext.data.Store', 'Ext.data.ArrayProxy', ...]
* var allData = Ext.ClassManager.getNamesByExpression('Ext.data.*');
*
* @param {String/String[]} expression
* @param {Object} [exclude=null] An object keyed by class name containing classes to
* exclude from the returned classes. This must be provided if `accumulate` is set to
* `true`.
* @param {Boolean} [accumulate=false] Pass `true` to add matching classes to the
* specified `exclude` object.
* @return {String[]} An array of class names.
*/
getNamesByExpression: function(expression, exclude, accumulate) {
var me = this,
aliasToName = me.aliasToName,
alternateToName = me.alternateToName,
nameToAliases = me.nameToAliases,
nameToAlternates = me.nameToAlternates,
map = accumulate ? exclude : {},
names = [],
expressions = Ext.isString(expression) ? [
expression
] : expression,
length = expressions.length,
wildcardRe = me.wildcardRe,
expr, i, list, match, n, name, regex;
for (i = 0; i < length; ++i) {
if ((expr = expressions[i]).indexOf('*') < 0) {
// No wildcard
if (!(name = aliasToName[expr])) {
if (!(name = alternateToName[expr])) {
name = expr;
}
}
if (!(name in map) && !(exclude && (name in exclude))) {
map[name] = 1;
names.push(name);
}
} else {
regex = new RegExp('^' + expr.replace(wildcardRe, '(.*?)') + '$');
for (name in nameToAliases) {
if (!(name in map) && !(exclude && (name in exclude))) {
if (!(match = regex.test(name))) {
n = (list = nameToAliases[name]).length;
while (!match && n-- > 0) {
match = regex.test(list[n]);
}
list = nameToAlternates[name];
if (list && !match) {
n = list.length;
while (!match && n-- > 0) {
match = regex.test(list[n]);
}
}
}
if (match) {
map[name] = 1;
names.push(name);
}
}
}
}
}
return names;
},
getPath: function(className) {
var me = this,
paths = me.paths,
ret = '',
prefix;
if (className in paths) {
ret = paths[className];
} else {
prefix = me.nameToPrefix[className] || (me.nameToPrefix[className] = me.getPrefix(className));
if (prefix) {
className = className.substring(prefix.length + 1);
ret = paths[prefix];
if (ret) {
ret += '/';
}
}
ret += className.replace(me.dotRe, '/') + '.js';
}
return ret;
},
getPrefix: function(className) {
if (className in this.paths) {
return className;
} else if (className in this.nameToPrefix) {
return this.nameToPrefix[className];
}
var prefixes = this.getPrefixes(),
length = className.length,
items, currChar, currSubstr, prefix, j, jlen;
// Walk the prefixes backwards so we consider the longest ones first.
// Prefixes are kept in a sparse array grouped by length so we don't have to
// iterate over all of them, just the ones we need.
while (length-- > 0) {
items = prefixes[length];
if (items) {
currChar = className.charAt(length);
if (currChar !== '.') {
continue;
}
currSubstr = className.substring(0, length);
for (j = 0 , jlen = items.length; j < jlen; j++) {
prefix = items[j];
if (prefix === className.substring(0, length)) {
return prefix;
}
}
}
}
return '';
},
getPrefixes: function() {
var me = this,
prefixes = me.prefixes,
names, name, nameLength, items, i, len;
if (!prefixes) {
names = me.names.slice(0);
me.prefixes = prefixes = [];
for (i = 0 , len = names.length; i < len; i++) {
name = names[i];
nameLength = name.length;
items = prefixes[nameLength] || (prefixes[nameLength] = []);
items.push(name);
}
}
return prefixes;
},
removeName: function(name) {
var me = this,
aliasToName = me.aliasToName,
alternateToName = me.alternateToName,
nameToAliases = me.nameToAliases,
nameToAlternates = me.nameToAlternates,
aliases = nameToAliases[name],
alternates = nameToAlternates[name],
i, a;
delete nameToAliases[name];
delete nameToAlternates[name];
delete me.nameToPrefix[name];
if (aliases) {
for (i = aliases.length; i--; ) {
// Aliases can be reassigned so if this class is the current mapping of
// the alias, remove it. Since there is no chain to restore what was
// removed this is not perfect.
if (name === (a = aliases[i])) {
delete aliasToName[a];
}
}
}
if (alternates) {
for (i = alternates.length; i--; ) {
// Like aliases, alternate class names can also be remapped.
if (name === (a = alternates[i])) {
delete alternateToName[a];
}
}
}
},
resolveName: function(name) {
var me = this,
trueName;
// If the name has a registered alias, it is a true className (not an alternate)
// so we can stop now.
if (!(name in me.nameToAliases)) {
// The name is not a known class name, so check to see if it is a known alias:
if (!(trueName = me.aliasToName[name])) {
// The name does not correspond to a known alias, so check if it is a known
// alternateClassName:
trueName = me.alternateToName[name];
}
}
return trueName || name;
},
/**
* This method returns a selector object that produces a selection of classes and
* delivers them to the desired `receiver`.
*
* The returned selector object has the same methods as the given `receiver` object
* but these methods on the selector accept a first argument that expects a pattern
* or array of patterns. The actual method on the `receiver` will be called with an
* array of classes that match these patterns but with any patterns passed to an
* `exclude` call removed.
*
* For example:
*
* var sel = inventory.select({
* require: function (classes) {
* console.log('Classes: ' + classes.join(','));
* }
* });
*
* sel.exclude('Ext.chart.*').exclude('Ext.draw.*').require('*');
*
* // Logs all classes except those in the Ext.chart and Ext.draw namespaces.
*
* @param {Object} receiver
* @param {Object} [scope] Optional scope to use when calling `receiver` methods.
* @return {Object} An object with the same methods as `receiver` plus `exclude`.
*/
select: function(receiver, scope) {
var me = this,
excludes = {},
ret = {
excludes: excludes,
exclude: function() {
me.getNamesByExpression(arguments, excludes, true);
return this;
}
},
name;
for (name in receiver) {
ret[name] = me.selectMethod(excludes, receiver[name], scope || receiver);
}
return ret;
},
selectMethod: function(excludes, fn, scope) {
var me = this;
return function(include) {
var args = Ext.Array.slice(arguments, 1);
args.unshift(me.getNamesByExpression(include, excludes));
return fn.apply(scope, args);
};
},
/**
* Sets the path of a namespace.
* For Example:
*
* inventory.setPath('Ext', '.');
* inventory.setPath({
* Ext: '.'
* });
*
* @param {String/Object} name The name of a single mapping or an object of mappings.
* @param {String} [path] If `name` is a String, then this is the path for that name.
* Otherwise this parameter is ignored.
* @return {Ext.Inventory} this
* @method
*/
setPath: Ext.Function.flexSetter(function(name, path) {
var me = this;
me.paths[name] = path;
me.names.push(name);
me.prefixes = null;
me.nameToPrefix = {};
return me;
})
};
// @tag class
/**
* @class Ext.ClassManager
*
* Ext.ClassManager manages all classes and handles mapping from string class name to
* actual class objects throughout the whole framework. It is not generally accessed directly, rather through
* these convenient shorthands:
*
* - {@link Ext#define Ext.define}
* - {@link Ext#create Ext.create}
* - {@link Ext#widget Ext.widget}
* - {@link Ext#getClass Ext.getClass}
* - {@link Ext#getClassName Ext.getClassName}
*
* # Basic syntax:
*
* Ext.define(className, properties);
*
* in which `properties` is an object represent a collection of properties that apply to the class. See
* {@link Ext.ClassManager#create} for more detailed instructions.
*
* Ext.define('Person', {
* name: 'Unknown',
*
* constructor: function(name) {
* if (name) {
* this.name = name;
* }
* },
*
* eat: function(foodType) {
* alert("I'm eating: " + foodType);
*
* return this;
* }
* });
*
* var aaron = new Person("Aaron");
* aaron.eat("Sandwich"); // alert("I'm eating: Sandwich");
*
* Ext.Class has a powerful set of extensible {@link Ext.Class#registerPreprocessor pre-processors} which takes care of
* everything related to class creation, including but not limited to inheritance, mixins, configuration, statics, etc.
*
* # Inheritance:
*
* Ext.define('Developer', {
* extend: 'Person',
*
* constructor: function(name, isGeek) {
* this.isGeek = isGeek;
*
* // Apply a method from the parent class' prototype
* this.callParent([name]);
* },
*
* code: function(language) {
* alert("I'm coding in: " + language);
*
* this.eat("Bugs");
*
* return this;
* }
* });
*
* var jacky = new Developer("Jacky", true);
* jacky.code("JavaScript"); // alert("I'm coding in: JavaScript");
* // alert("I'm eating: Bugs");
*
* See {@link Ext.Base#callParent} for more details on calling superclass' methods
*
* # Mixins:
*
* Ext.define('CanPlayGuitar', {
* playGuitar: function() {
* alert("F#...G...D...A");
* }
* });
*
* Ext.define('CanComposeSongs', {
* composeSongs: function() { ... }
* });
*
* Ext.define('CanSing', {
* sing: function() {
* alert("For he's a jolly good fellow...")
* }
* });
*
* Ext.define('Musician', {
* extend: 'Person',
*
* mixins: {
* canPlayGuitar: 'CanPlayGuitar',
* canComposeSongs: 'CanComposeSongs',
* canSing: 'CanSing'
* }
* })
*
* Ext.define('CoolPerson', {
* extend: 'Person',
*
* mixins: {
* canPlayGuitar: 'CanPlayGuitar',
* canSing: 'CanSing'
* },
*
* sing: function() {
* alert("Ahem....");
*
* this.mixins.canSing.sing.call(this);
*
* alert("[Playing guitar at the same time...]");
*
* this.playGuitar();
* }
* });
*
* var me = new CoolPerson("Jacky");
*
* me.sing(); // alert("Ahem...");
* // alert("For he's a jolly good fellow...");
* // alert("[Playing guitar at the same time...]");
* // alert("F#...G...D...A");
*
* # Config:
*
* Ext.define('SmartPhone', {
* config: {
* hasTouchScreen: false,
* operatingSystem: 'Other',
* price: 500
* },
*
* isExpensive: false,
*
* constructor: function(config) {
* this.initConfig(config);
* },
*
* applyPrice: function(price) {
* this.isExpensive = (price > 500);
*
* return price;
* },
*
* applyOperatingSystem: function(operatingSystem) {
* if (!(/^(iOS|Android|BlackBerry)$/i).test(operatingSystem)) {
* return 'Other';
* }
*
* return operatingSystem;
* }
* });
*
* var iPhone = new SmartPhone({
* hasTouchScreen: true,
* operatingSystem: 'iOS'
* });
*
* iPhone.getPrice(); // 500;
* iPhone.getOperatingSystem(); // 'iOS'
* iPhone.getHasTouchScreen(); // true;
*
* iPhone.isExpensive; // false;
* iPhone.setPrice(600);
* iPhone.getPrice(); // 600
* iPhone.isExpensive; // true;
*
* iPhone.setOperatingSystem('AlienOS');
* iPhone.getOperatingSystem(); // 'Other'
*
* # Statics:
*
* Ext.define('Computer', {
* statics: {
* factory: function(brand) {
* // 'this' in static methods refer to the class itself
* return new this(brand);
* }
* },
*
* constructor: function() { ... }
* });
*
* var dellComputer = Computer.factory('Dell');
*
* Also see {@link Ext.Base#statics} and {@link Ext.Base#self} for more details on accessing
* static properties within class methods
*
* @singleton
*/
Ext.ClassManager = (function(Class, alias, arraySlice, arrayFrom, global) {
// @define Ext.ClassManager
// @require Ext.Inventory
// @require Ext.Class
// @require Ext.Function
// @require Ext.Array
var makeCtor = Ext.Class.makeCtor,
nameLookupStack = [],
namespaceCache = {
Ext: {
name: 'Ext',
value: Ext
}
},
// specially added for sandbox (Ext === global.Ext6)
/*
'Ext.grid': {
name: 'grid',
parent: namespaceCache['Ext']
},
'Ext.grid.Panel': {
name: 'Panel',
parent: namespaceCache['Ext.grid']
},
...
Also,
'MyApp': {
name: 'MyApp',
value: MyApp
}
*/
Manager = Ext.apply(new Ext.Inventory(), {
/**
* @property {Object} classes
* All classes which were defined through the ClassManager. Keys are the
* name of the classes and the values are references to the classes.
* @private
*/
classes: {},
classState: {},
/*
* 'Ext.foo.Bar': <state enum>
*
* 10 = Ext.define called
* 20 = Ext.define/override called
* 30 = Manager.existCache[<name>] == true for define
* 40 = Manager.existCache[<name>] == true for define/override
* 50 = Manager.isCreated(<name>) == true for define
* 60 = Manager.isCreated(<name>) == true for define/override
*
*/
/**
* @private
*/
existCache: {},
/** @private */
instantiators: [],
/**
* Checks if a class has already been created.
*
* @param {String} className
* @return {Boolean} exist
*/
isCreated: function(className) {
if (typeof className !== 'string' || className.length < 1) {
throw new Error("[Ext.ClassManager] Invalid classname, must be a string and must not be empty");
}
if (Manager.classes[className] || Manager.existCache[className]) {
return true;
}
if (!Manager.lookupName(className, false)) {
return false;
}
Manager.triggerCreated(className);
return true;
},
/**
* @private
*/
createdListeners: [],
/**
* @private
*/
nameCreatedListeners: {},
/**
* @private
*/
existsListeners: [],
/**
* @private
*/
nameExistsListeners: {},
/**
* @private
*/
overrideMap: {},
/**
* @private
*/
triggerCreated: function(className, state) {
Manager.existCache[className] = state || 1;
Manager.classState[className] += 40;
Manager.notify(className, Manager.createdListeners, Manager.nameCreatedListeners);
},
/**
* @private
*/
onCreated: function(fn, scope, className) {
Manager.addListener(fn, scope, className, Manager.createdListeners, Manager.nameCreatedListeners);
},
/**
* @private
*/
notify: function(className, listeners, nameListeners) {
var alternateNames = Manager.getAlternatesByName(className),
names = [
className
],
i, ln, j, subLn, listener, name;
for (i = 0 , ln = listeners.length; i < ln; i++) {
listener = listeners[i];
listener.fn.call(listener.scope, className);
}
while (names) {
for (i = 0 , ln = names.length; i < ln; i++) {
name = names[i];
listeners = nameListeners[name];
if (listeners) {
for (j = 0 , subLn = listeners.length; j < subLn; j++) {
listener = listeners[j];
listener.fn.call(listener.scope, name);
}
delete nameListeners[name];
}
}
names = alternateNames;
// for 2nd pass (if needed)
alternateNames = null;
}
},
// no 3rd pass
/**
* @private
*/
addListener: function(fn, scope, className, listeners, nameListeners) {
if (Ext.isArray(className)) {
fn = Ext.Function.createBarrier(className.length, fn, scope);
for (i = 0; i < className.length; i++) {
this.addListener(fn, null, className[i], listeners, nameListeners);
}
return;
}
var i,
listener = {
fn: fn,
scope: scope
};
if (className) {
if (this.isCreated(className)) {
fn.call(scope, className);
return;
}
if (!nameListeners[className]) {
nameListeners[className] = [];
}
nameListeners[className].push(listener);
} else {
listeners.push(listener);
}
},
/**
* Supports namespace rewriting.
* @private
*/
$namespaceCache: namespaceCache,
/**
* See `{@link Ext#addRootNamespaces Ext.addRootNamespaces}`.
* @since 6.0.0
* @private
*/
addRootNamespaces: function(namespaces) {
for (var name in namespaces) {
namespaceCache[name] = {
name: name,
value: namespaces[name]
};
}
},
/**
* Clears the namespace lookup cache. After application launch, this cache can
* often contain several hundred entries that are unlikely to be needed again.
* These will be rebuilt as needed, so it is harmless to clear this cache even
* if its results will be used again.
* @since 6.0.0
* @private
*/
clearNamespaceCache: function() {
nameLookupStack.length = 0;
for (var name in namespaceCache) {
if (!namespaceCache[name].value) {
delete namespaceCache[name];
}
}
},
/**
* Return the namespace cache entry for the given a class name or namespace (e.g.,
* "Ext.grid.Panel").
*
* @param {String} namespace The namespace or class name to lookup.
* @return {Object} The cache entry.
* @return {String} return.name The leaf name ("Panel" for "Ext.grid.Panel").
* @return {Object} return.parent The entry of the parent namespace (i.e., "Ext.grid").
* @return {Object} return.value The namespace object. This is only set for
* top-level namespace entries to support renaming them for sandboxing ("Ext6" vs
* "Ext").
* @since 6.0.0
* @private
*/
getNamespaceEntry: function(namespace) {
if (typeof namespace !== 'string') {
return namespace;
}
// assume we've been given an entry object
var entry = namespaceCache[namespace],
i;
if (!entry) {
i = namespace.lastIndexOf('.');
if (i < 0) {
entry = {
name: namespace
};
} else {
entry = {
name: namespace.substring(i + 1),
parent: Manager.getNamespaceEntry(namespace.substring(0, i))
};
}
namespaceCache[namespace] = entry;
}
return entry;
},
/**
* Return the value of the given "dot path" name. This supports remapping (for use
* in sandbox builds) as well as auto-creating of namespaces.
*
* @param {String} namespace The name of the namespace or class.
* @param {Boolean} [autoCreate] Pass `true` to create objects for undefined names.
* @return {Object} The object that is the namespace or class name.
* @since 6.0.0
* @private
*/
lookupName: function(namespace, autoCreate) {
var entry = Manager.getNamespaceEntry(namespace),
scope = Ext.global,
i = 0,
e, parent;
// Put entries on the stack in reverse order: [ 'Panel', 'grid', 'Ext' ]
for (e = entry; e; e = e.parent) {
// since we process only what we add to the array, and that always
// starts at index=0, we don't need to clean up the array (that would
// just encourage the GC to do something pointless).
nameLookupStack[i++] = e;
}
while (scope && i-- > 0) {
// We'll process entries in top-down order ('Ext', 'grid' then 'Panel').
e = nameLookupStack[i];
parent = scope;
scope = e.value || scope[e.name];
if (!scope && autoCreate) {
parent[e.name] = scope = {};
}
}
return scope;
},
/**
* Creates a namespace and assign the `value` to the created object.
*
* Ext.ClassManager.setNamespace('MyCompany.pkg.Example', someObject);
*
* alert(MyCompany.pkg.Example === someObject); // alerts true
*
* @param {String} name
* @param {Object} value
*/
setNamespace: function(namespace, value) {
var entry = Manager.getNamespaceEntry(namespace),
scope = Ext.global;
if (entry.parent) {
scope = Manager.lookupName(entry.parent, true);
}
scope[entry.name] = value;
return value;
},
/**
* Changes the mapping of an `xtype` to map to the specified component class.
* @param {String/Ext.Class} cls The class or class name to which `xtype` is mapped.
* @param {String} xtype The `xtype` to map or redefine as `cls`.
* @since 6.0.1
* @private
*/
setXType: function(cls, xtype) {
var className = cls.$className,
C = className ? cls : Manager.get(className = cls),
proto = C.prototype,
xtypes = proto.xtypes,
xtypesChain = proto.xtypesChain,
xtypesMap = proto.xtypesMap;
if (!proto.hasOwnProperty('xtypes')) {
proto.xtypes = xtypes = [];
proto.xtypesChain = xtypesChain = xtypesChain ? xtypesChain.slice(0) : [];
proto.xtypesMap = xtypesMap = Ext.apply({}, xtypesMap);
}
Manager.addAlias(className, 'widget.' + xtype, true);
xtypes.push(xtype);
xtypesChain.push(xtype);
xtypesMap[xtype] = true;
},
//TODO consider updating derived class xtypesChain / xtypesMap
/**
* Sets a name reference to a class.
*
* @param {String} name
* @param {Object} value
* @return {Ext.ClassManager} this
*/
set: function(name, value) {
var targetName = Manager.getName(value);
Manager.classes[name] = Manager.setNamespace(name, value);
if (targetName && targetName !== name) {
Manager.addAlternate(targetName, name);
}
return Manager;
},
/**
* Retrieve a class by its name.
*
* @param {String} name
* @return {Ext.Class} class
*/
get: function(name) {
return Manager.classes[name] || Manager.lookupName(name, false);
},
/**
* Adds a batch of class name to alias mappings.
* @param {Object} aliases The set of mappings of the form.
* className : [values...]
*/
addNameAliasMappings: function(aliases) {
Manager.addAlias(aliases);
},
/**
*
* @param {Object} alternates The set of mappings of the form
* className : [values...]
*/
addNameAlternateMappings: function(alternates) {
Manager.addAlternate(alternates);
},
/**
* Get a reference to the class by its alias.
*
* @param {String} alias
* @return {Ext.Class} class
*/
getByAlias: function(alias) {
return Manager.get(Manager.getNameByAlias(alias));
},
/**
* Get a component class name from a config object.
* @param {Object} config The config object.
* @param {String} [aliasPrefix] A prefix to use when getting
* a class name by alias.
* @return {Ext.Class} The class.
*
* @private
*/
getByConfig: function(config, aliasPrefix) {
var xclass = config.xclass,
name;
if (xclass) {
name = xclass;
} else {
name = config.xtype;
if (name) {
aliasPrefix = 'widget.';
} else {
name = config.type;
}
name = Manager.getNameByAlias(aliasPrefix + name);
}
return Manager.get(name);
},
/**
* Get the name of the class by its reference or its instance. This is
* usually invoked by the shorthand {@link Ext#getClassName}.
*
* Ext.ClassManager.getName(Ext.Action); // returns "Ext.Action"
*
* @param {Ext.Class/Object} object
* @return {String} className
*/
getName: function(object) {
return object && object.$className || '';
},
/**
* Get the class of the provided object; returns null if it's not an instance
* of any class created with Ext.define. This is usually invoked by the
* shorthand {@link Ext#getClass}.
*
* var component = new Ext.Component();
*
* Ext.getClass(component); // returns Ext.Component
*
* @param {Object} object
* @return {Ext.Class} class
*/
getClass: function(object) {
return object && object.self || null;
},
/**
* Defines a class.
* @deprecated Use {@link Ext#define} instead, as that also supports creating overrides.
* @private
*/
create: function(className, data, createdFn) {
if (className != null && typeof className !== 'string') {
throw new Error("[Ext.define] Invalid class name '" + className + "' specified, must be a non-empty string");
}
var ctor = makeCtor(className);
if (typeof data === 'function') {
data = data(ctor);
}
if (className) {
if (Manager.classes[className]) {
Ext.log.warn("[Ext.define] Duplicate class name '" + className + "' specified, must be a non-empty string");
}
ctor.name = className;
}
data.$className = className;
return new Class(ctor, data, function() {
var postprocessorStack = data.postprocessors || Manager.defaultPostprocessors,
registeredPostprocessors = Manager.postprocessors,
postprocessors = [],
postprocessor, i, ln, j, subLn, postprocessorProperties, postprocessorProperty;
delete data.postprocessors;
for (i = 0 , ln = postprocessorStack.length; i < ln; i++) {
postprocessor = postprocessorStack[i];
if (typeof postprocessor === 'string') {
postprocessor = registeredPostprocessors[postprocessor];
postprocessorProperties = postprocessor.properties;
if (postprocessorProperties === true) {
postprocessors.push(postprocessor.fn);
} else if (postprocessorProperties) {
for (j = 0 , subLn = postprocessorProperties.length; j < subLn; j++) {
postprocessorProperty = postprocessorProperties[j];
if (data.hasOwnProperty(postprocessorProperty)) {
postprocessors.push(postprocessor.fn);
break;
}
}
}
} else {
postprocessors.push(postprocessor);
}
}
data.postprocessors = postprocessors;
data.createdFn = createdFn;
Manager.processCreate(className, this, data);
});
},
processCreate: function(className, cls, clsData) {
var me = this,
postprocessor = clsData.postprocessors.shift(),
createdFn = clsData.createdFn;
if (!postprocessor) {
Ext.classSystemMonitor && Ext.classSystemMonitor(className, 'Ext.ClassManager#classCreated', arguments);
if (className) {
me.set(className, cls);
}
delete cls._classHooks;
if (createdFn) {
createdFn.call(cls, cls);
}
if (className) {
me.triggerCreated(className);
}
return;
}
if (postprocessor.call(me, className, cls, clsData, me.processCreate) !== false) {
me.processCreate(className, cls, clsData);
}
},
createOverride: function(className, data, createdFn) {
var me = this,
overriddenClassName = data.override,
requires = data.requires,
uses = data.uses,
mixins = data.mixins,
mixinsIsArray,
compat = 1,
// default if 'compatibility' is not specified
dependenciesLoaded,
classReady = function() {
var cls, dependencies, i, key, temp;
if (!dependenciesLoaded) {
dependencies = requires ? requires.slice(0) : [];
if (mixins) {
if (!(mixinsIsArray = mixins instanceof Array)) {
for (key in mixins) {
if (Ext.isString(cls = mixins[key])) {
dependencies.push(cls);
}
}
} else {
for (i = 0 , temp = mixins.length; i < temp; ++i) {
if (Ext.isString(cls = mixins[i])) {
dependencies.push(cls);
}
}
}
}
dependenciesLoaded = true;
if (dependencies.length) {
// Since the override is going to be used (its target class is
// now created), we need to fetch the required classes for the
// override and call us back once they are loaded:
Ext.require(dependencies, classReady);
return;
}
}
// else we have no dependencies, so proceed
// transform mixin class names into class references, This
// loop can handle both the array and object forms of
// mixin definitions
if (mixinsIsArray) {
for (i = 0 , temp = mixins.length; i < temp; ++i) {
if (Ext.isString(cls = mixins[i])) {
mixins[i] = Ext.ClassManager.get(cls);
}
}
} else if (mixins) {
for (key in mixins) {
if (Ext.isString(cls = mixins[key])) {
mixins[key] = Ext.ClassManager.get(cls);
}
}
}
// The target class and the required classes for this override are
// ready, so we can apply the override now:
cls = overriddenClassName.$isClass ? overriddenClassName : me.get(overriddenClassName);
// We don't want to apply these:
delete data.override;
delete data.compatibility;
delete data.requires;
delete data.uses;
Ext.override(cls, data);
// This pushes the overriding file itself into Ext.Loader.history
// Hence if the target class never exists, the overriding file will
// never be included in the build.
Ext.Loader.history.push(className);
if (uses) {
// This "hides" from the Cmd auto-dependency scanner since
// the reference is circular (Loader requires us).
Ext['Loader'].addUsedClasses(uses);
}
// get these classes too!
if (createdFn) {
createdFn.call(cls, cls);
}
};
// last but not least!
if (className) {
Manager.overrideMap[className] = true;
}
// If specified, parse strings as versions, but otherwise treat as a
// boolean (maybe "compatibility: Ext.isIE8" or something).
//
if ('compatibility' in data) {
compat = data.compatibility;
if (!compat) {
// Cast '', null, undefined, 0 to false.
compat = false;
} else if (typeof compat === 'number') {
// By virtue of the condition above we must be a nonzero number.
compat = true;
} else if (typeof compat !== 'boolean') {
compat = Ext.checkVersion(compat);
}
}
if (compat) {
// override the target class right after it's created
if (overriddenClassName.$isClass) {
classReady();
} else {
me.onCreated(classReady, me, overriddenClassName);
}
}
me.triggerCreated(className, 2);
return me;
},
/**
* Instantiate a class by its alias. This is usually invoked by the
* shorthand {@link Ext#createByAlias}.
*
* If {@link Ext.Loader} is {@link Ext.Loader#setConfig enabled} and the class
* has not been defined yet, it will attempt to load the class via synchronous
* loading.
*
* var window = Ext.createByAlias('widget.window', { width: 600, height: 800 });
*
* @param {String} alias
* @param {Object...} args Additional arguments after the alias will be passed to the
* class constructor.
* @return {Object} instance
*/
instantiateByAlias: function() {
var alias = arguments[0],
args = arraySlice.call(arguments),
className = this.getNameByAlias(alias);
if (!className) {
throw new Error("[Ext.createByAlias] Unrecognized alias: " + alias);
}
args[0] = className;
return Ext.create.apply(Ext, args);
},
/**
* Instantiate a class by either full name, alias or alternate name
* @param {String} name
* @param {Mixed} args Additional arguments after the name will be passed to the class' constructor.
* @return {Object} instance
* @deprecated 5.0 Use Ext.create() instead.
*/
instantiate: function() {
Ext.log.warn('Ext.ClassManager.instantiate() is deprecated. Use Ext.create() instead.');
return Ext.create.apply(Ext, arguments);
},
/**
* @private
* @param name
* @param args
*/
dynInstantiate: function(name, args) {
args = arrayFrom(args, true);
args.unshift(name);
return Ext.create.apply(Ext, args);
},
/**
* @private
* @param length
*/
getInstantiator: function(length) {
var instantiators = this.instantiators,
instantiator, i, args;
instantiator = instantiators[length];
if (!instantiator) {
i = length;
args = [];
for (i = 0; i < length; i++) {
args.push('a[' + i + ']');
}
instantiator = instantiators[length] = new Function('c', 'a', 'return new c(' + args.join(',') + ')');
instantiator.name = "Ext.create" + length;
}
return instantiator;
},
/**
* @private
*/
postprocessors: {},
/**
* @private
*/
defaultPostprocessors: [],
/**
* Register a post-processor function.
*
* @private
* @param {String} name
* @param {Function} postprocessor
*/
registerPostprocessor: function(name, fn, properties, position, relativeTo) {
if (!position) {
position = 'last';
}
if (!properties) {
properties = [
name
];
}
this.postprocessors[name] = {
name: name,
properties: properties || false,
fn: fn
};
this.setDefaultPostprocessorPosition(name, position, relativeTo);
return this;
},
/**
* Set the default post processors array stack which are applied to every class.
*
* @private
* @param {String/Array} postprocessors The name of a registered post processor or an array of registered names.
* @return {Ext.ClassManager} this
*/
setDefaultPostprocessors: function(postprocessors) {
this.defaultPostprocessors = arrayFrom(postprocessors);
return this;
},
/**
* Insert this post-processor at a specific position in the stack, optionally relative to
* any existing post-processor
*
* @private
* @param {String} name The post-processor name. Note that it needs to be registered with
* {@link Ext.ClassManager#registerPostprocessor} before this
* @param {String} offset The insertion position. Four possible values are:
* 'first', 'last', or: 'before', 'after' (relative to the name provided in the third argument)
* @param {String} relativeName
* @return {Ext.ClassManager} this
*/
setDefaultPostprocessorPosition: function(name, offset, relativeName) {
var defaultPostprocessors = this.defaultPostprocessors,
index;
if (typeof offset === 'string') {
if (offset === 'first') {
defaultPostprocessors.unshift(name);
return this;
} else if (offset === 'last') {
defaultPostprocessors.push(name);
return this;
}
offset = (offset === 'after') ? 1 : -1;
}
index = Ext.Array.indexOf(defaultPostprocessors, relativeName);
if (index !== -1) {
Ext.Array.splice(defaultPostprocessors, Math.max(0, index + offset), 0, name);
}
return this;
}
});
/**
* @cfg xtype
* @member Ext.Class
* @inheritdoc Ext.Component#cfg-xtype
*/
/**
* @cfg {String} override
* @member Ext.Class
* Overrides members of the specified `target` class.
*
* **NOTE:** the overridden class must have been defined using
* {@link Ext#define Ext.define} in order to use the `override` config.
*
* Methods defined on the overriding class will not automatically call the methods of
* the same name in the ancestor class chain. To call the parent's method of the
* same name you must call {@link Ext.Base#callParent callParent}. To skip the
* method of the overridden class and call its parent you will instead call
* {@link Ext.Base#callSuper callSuper}.
*
* See {@link Ext#define Ext.define} for additional usage examples.
*/
/**
* @cfg {String/String[]} alias
* @member Ext.Class
* List of short aliases for class names. An alias consists of a namespace and a name
* concatenated by a period as &#60;namespace&#62;.&#60;name&#62;
*
* - **namespace** - The namespace describes what kind of alias this is and must be
* all lowercase.
* - **name** - The name of the alias which allows the lazy-instantiation via the
* alias. The name shouldn't contain any periods.
*
* A list of namespaces and the usages are:
*
* - **feature** - {@link Ext.grid.Panel Grid} features
* - **plugin** - Plugins
* - **store** - {@link Ext.data.Store}
* - **widget** - Components
*
* Most useful for defining xtypes for widgets:
*
* Ext.define('MyApp.CoolPanel', {
* extend: 'Ext.panel.Panel',
* alias: ['widget.coolpanel'],
* title: 'Yeah!'
* });
*
* // Using Ext.create
* Ext.create('widget.coolpanel');
*
* // Using the shorthand for defining widgets by xtype
* Ext.widget('panel', {
* items: [
* {xtype: 'coolpanel', html: 'Foo'},
* {xtype: 'coolpanel', html: 'Bar'}
* ]
* });
*/
Manager.registerPostprocessor('alias', function(name, cls, data) {
Ext.classSystemMonitor && Ext.classSystemMonitor(name, 'Ext.ClassManager#aliasPostProcessor', arguments);
var aliases = Ext.Array.from(data.alias),
i, ln;
for (i = 0 , ln = aliases.length; i < ln; i++) {
alias = aliases[i];
this.addAlias(cls, alias);
}
}, [
'xtype',
'alias'
]);
/**
* @cfg {Boolean} singleton
* @member Ext.Class
* When set to true, the class will be instantiated as singleton. For example:
*
* Ext.define('Logger', {
* singleton: true,
* log: function(msg) {
* console.log(msg);
* }
* });
*
* Logger.log('Hello');
*/
Manager.registerPostprocessor('singleton', function(name, cls, data, fn) {
Ext.classSystemMonitor && Ext.classSystemMonitor(name, 'Ext.ClassManager#singletonPostProcessor', arguments);
if (data.singleton) {
fn.call(this, name, new cls(), data);
} else {
return true;
}
return false;
});
/**
* @cfg {String/String[]} alternateClassName
* @member Ext.Class
* Defines alternate names for this class. For example:
*
* Ext.define('Developer', {
* alternateClassName: ['Coder', 'Hacker'],
* code: function(msg) {
* alert('Typing... ' + msg);
* }
* });
*
* var joe = Ext.create('Developer');
* joe.code('stackoverflow');
*
* var rms = Ext.create('Hacker');
* rms.code('hack hack');
*/
Manager.registerPostprocessor('alternateClassName', function(name, cls, data) {
Ext.classSystemMonitor && Ext.classSystemMonitor(name, 'Ext.ClassManager#alternateClassNamePostprocessor', arguments);
var alternates = data.alternateClassName,
i, ln, alternate;
if (!(alternates instanceof Array)) {
alternates = [
alternates
];
}
for (i = 0 , ln = alternates.length; i < ln; i++) {
alternate = alternates[i];
if (typeof alternate !== 'string') {
throw new Error("[Ext.define] Invalid alternate of: '" + alternate + "' for class: '" + name + "'; must be a valid string");
}
this.set(alternate, cls);
}
});
/**
* @cfg {Object} debugHooks
* A collection of diagnostic methods to decorate the real methods of the class. These
* methods are applied as an `override` if this class has debug enabled as defined by
* `Ext.isDebugEnabled`.
*
* These will be automatically removed by the Sencha Cmd compiler for production builds.
*
* Example usage:
*
* Ext.define('Foo.bar.Class', {
* foo: function (a, b, c) {
* ...
* },
*
* bar: function (a, b) {
* ...
* return 42;
* },
*
* debugHooks: {
* foo: function (a, b, c) {
* // check arguments...
* return this.callParent(arguments);
* }
* }
* });
*
* If you specify a `$enabled` property in the `debugHooks` object that will be used
* as the default enabled state for the hooks. If the `{@link Ext#manifest}` contains
* a `debug` object of if `{@link Ext#debugConfig}` is specified, the `$enabled` flag
* will override its "*" value.
*/
Manager.registerPostprocessor('debugHooks', function(name, Class, data) {
Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#debugHooks', arguments);
if (Ext.isDebugEnabled(Class.$className, data.debugHooks.$enabled)) {
delete data.debugHooks.$enabled;
Ext.override(Class, data.debugHooks);
}
// may already have an instance here in the case of singleton
var target = Class.isInstance ? Class.self : Class;
delete target.prototype.debugHooks;
});
/**
* @cfg {Object} deprecated
* The object given has properties that describe the versions at which the deprecations
* apply.
*
* The purpose of the `deprecated` declaration is to enable development mode to give
* suitable error messages when deprecated methods or properties are used. Methods can
* always be injected to provide this feedback, but properties can only be handled on
* some browsers (those that support `Object.defineProperty`).
*
* In some cases, deprecated methods can be restored to their previous behavior or
* added back if they have been removed.
*
* The structure of a `deprecated` declaration is this:
*
* Ext.define('Foo.bar.Class', {
* ...
*
* deprecated: {
* // Optional package name - default is the framework (ext or touch)
* name: 'foobar',
*
* '5.0': {
* methods: {
* // Throws: '"removedMethod" is deprecated.'
* removedMethod: null,
*
* // Throws: '"oldMethod" is deprecated. Please use "newMethod" instead.'
* oldMethod: 'newMethod',
*
* // When this block is enabled, this method is applied as an
* // override. Otherwise you get same as "removeMethod".
* method: function () {
* // Do what v5 "method" did. If "method" exists in newer
* // versions callParent can call it. If 5.1 has "method"
* // then it would be next in line, otherwise 5.2 and last
* // would be the current class.
* },
*
* moreHelpful: {
* message: 'Something helpful to do instead.',
* fn: function () {
* // The v5 "moreHelpful" method to use when enabled.
* }
* }
* },
* properties: {
* // Throws: '"removedProp" is deprecated.'
* removedProp: null,
*
* // Throws: '"oldProp" is deprecated. Please use "newProp" instead.'
* oldProp: 'newProp',
*
* helpful: {
* message: 'Something helpful message about what to do.'
* }
* ...
* },
* statics: {
* methods: {
* ...
* },
* properties: {
* ...
* },
* }
* },
*
* '5.1': {
* ...
* },
*
* '5.2': {
* ...
* }
* }
* });
*
* The primary content of `deprecated` are the version number keys. These indicate
* a version number where methods or properties were deprecated. These versions are
* compared to the version reported by `Ext.getCompatVersion` to determine the action
* to take for each "block".
*
* When the compatibility version is set to a value less than a version number key,
* that block is said to be "enabled". For example, if a method was deprecated in
* version 5.0 but the desired compatibility level is 4.2 then the block is used to
* patch methods and (to some degree) restore pre-5.0 compatibility.
*
* When multiple active blocks have the same method name, each method is applied as
* an override in reverse order of version. In the above example, if a method appears
* in the "5.0", "5.1" and "5.2" blocks then the "5.2" method is applied as an override
* first, followed by the "5.1" method and finally the "5.0" method. This means that
* the `callParent` from the "5.0" method calls the "5.1" method which calls the
* "5.2" method which can (if applicable) call the current version.
*/
Manager.registerPostprocessor('deprecated', function(name, Class, data) {
Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#deprecated', arguments);
// may already have an instance here in the case of singleton
var target = Class.isInstance ? Class.self : Class;
target.addDeprecations(data.deprecated);
delete target.prototype.deprecated;
});
Ext.apply(Ext, {
/**
* Instantiate a class by either full name, alias or alternate name.
*
* If {@link Ext.Loader} is {@link Ext.Loader#setConfig enabled} and the class has
* not been defined yet, it will attempt to load the class via synchronous loading.
*
* For example, all these three lines return the same result:
*
* // xtype
* var window = Ext.create({
* xtype: 'window',
* width: 600,
* height: 800,
* ...
* });
*
* // alias
* var window = Ext.create('widget.window', {
* width: 600,
* height: 800,
* ...
* });
*
* // alternate name
* var window = Ext.create('Ext.Window', {
* width: 600,
* height: 800,
* ...
* });
*
* // full class name
* var window = Ext.create('Ext.window.Window', {
* width: 600,
* height: 800,
* ...
* });
*
* // single object with xclass property:
* var window = Ext.create({
* xclass: 'Ext.window.Window', // any valid value for 'name' (above)
* width: 600,
* height: 800,
* ...
* });
*
* @param {String} [name] The class name or alias. Can be specified as `xclass`
* property if only one object parameter is specified.
* @param {Object...} [args] Additional arguments after the name will be passed to
* the class' constructor.
* @return {Object} instance
* @member Ext
* @method create
*/
create: function() {
var name = arguments[0],
nameType = typeof name,
args = arraySlice.call(arguments, 1),
cls;
if (nameType === 'function') {
cls = name;
} else {
if (nameType !== 'string' && args.length === 0) {
args = [
name
];
if (!(name = name.xclass)) {
name = args[0].xtype;
if (name) {
name = 'widget.' + name;
}
}
}
if (typeof name !== 'string' || name.length < 1) {
throw new Error("[Ext.create] Invalid class name or alias '" + name + "' specified, must be a non-empty string");
}
name = Manager.resolveName(name);
cls = Manager.get(name);
}
// Still not existing at this point, try to load it via synchronous mode as the last resort
if (!cls) {
Ext.log.warn("[Ext.Loader] Synchronously loading '" + name + "'; consider adding " + "Ext.require('" + name + "') above Ext.onReady");
Ext.syncRequire(name);
cls = Manager.get(name);
}
if (!cls) {
throw new Error("[Ext.create] Unrecognized class name / alias: " + name);
}
if (typeof cls !== 'function') {
throw new Error("[Ext.create] Singleton '" + name + "' cannot be instantiated.");
}
return Manager.getInstantiator(args.length)(cls, args);
},
/**
* Convenient shorthand to create a widget by its xtype or a config object.
*
* var button = Ext.widget('button'); // Equivalent to Ext.create('widget.button');
*
* var panel = Ext.widget('panel', { // Equivalent to Ext.create('widget.panel')
* title: 'Panel'
* });
*
* var grid = Ext.widget({
* xtype: 'grid',
* ...
* });
*
* If a {@link Ext.Component component} instance is passed, it is simply returned.
*
* @member Ext
* @param {String} [name] The xtype of the widget to create.
* @param {Object} [config] The configuration object for the widget constructor.
* @return {Object} The widget instance
*/
widget: function(name, config) {
// forms:
// 1: (xtype)
// 2: (xtype, config)
// 3: (config)
// 4: (xtype, component)
// 5: (component)
//
var xtype = name,
alias, className, T;
if (typeof xtype !== 'string') {
// if (form 3 or 5)
// first arg is config or component
config = name;
// arguments[0]
xtype = config.xtype;
className = config.xclass;
} else {
config = config || {};
}
if (config.isComponent) {
return config;
}
if (!className) {
alias = 'widget.' + xtype;
className = Manager.getNameByAlias(alias);
}
// this is needed to support demand loading of the class
if (className) {
T = Manager.get(className);
}
if (!T) {
return Ext.create(className || alias, config);
}
return new T(config);
},
/**
* @inheritdoc Ext.ClassManager#instantiateByAlias
* @member Ext
* @method createByAlias
*/
createByAlias: alias(Manager, 'instantiateByAlias'),
/**
* Defines a class or override. A basic class is defined like this:
*
* Ext.define('My.awesome.Class', {
* someProperty: 'something',
*
* someMethod: function(s) {
* alert(s + this.someProperty);
* }
*
* ...
* });
*
* var obj = new My.awesome.Class();
*
* obj.someMethod('Say '); // alerts 'Say something'
*
* To create an anonymous class, pass `null` for the `className`:
*
* Ext.define(null, {
* constructor: function () {
* // ...
* }
* });
*
* In some cases, it is helpful to create a nested scope to contain some private
* properties. The best way to do this is to pass a function instead of an object
* as the second parameter. This function will be called to produce the class
* body:
*
* Ext.define('MyApp.foo.Bar', function () {
* var id = 0;
*
* return {
* nextId: function () {
* return ++id;
* }
* };
* });
*
* _Note_ that when using override, the above syntax will not override successfully, because
* the passed function would need to be executed first to determine whether or not the result
* is an override or defining a new object. As such, an alternative syntax that immediately
* invokes the function can be used:
*
* Ext.define('MyApp.override.BaseOverride', function () {
* var counter = 0;
*
* return {
* override: 'Ext.Component',
* logId: function () {
* console.log(++counter, this.id);
* }
* };
* }());
*
*
* When using this form of `Ext.define`, the function is passed a reference to its
* class. This can be used as an efficient way to access any static properties you
* may have:
*
* Ext.define('MyApp.foo.Bar', function (Bar) {
* return {
* statics: {
* staticMethod: function () {
* // ...
* }
* },
*
* method: function () {
* return Bar.staticMethod();
* }
* };
* });
*
* To define an override, include the `override` property. The content of an
* override is aggregated with the specified class in order to extend or modify
* that class. This can be as simple as setting default property values or it can
* extend and/or replace methods. This can also extend the statics of the class.
*
* One use for an override is to break a large class into manageable pieces.
*
* // File: /src/app/Panel.js
*
* Ext.define('My.app.Panel', {
* extend: 'Ext.panel.Panel',
* requires: [
* 'My.app.PanelPart2',
* 'My.app.PanelPart3'
* ]
*
* constructor: function (config) {
* this.callParent(arguments); // calls Ext.panel.Panel's constructor
* //...
* },
*
* statics: {
* method: function () {
* return 'abc';
* }
* }
* });
*
* // File: /src/app/PanelPart2.js
* Ext.define('My.app.PanelPart2', {
* override: 'My.app.Panel',
*
* constructor: function (config) {
* this.callParent(arguments); // calls My.app.Panel's constructor
* //...
* }
* });
*
* Another use of overrides is to provide optional parts of classes that can be
* independently required. In this case, the class may even be unaware of the
* override altogether.
*
* Ext.define('My.ux.CoolTip', {
* override: 'Ext.tip.ToolTip',
*
* constructor: function (config) {
* this.callParent(arguments); // calls Ext.tip.ToolTip's constructor
* //...
* }
* });
*
* The above override can now be required as normal.
*
* Ext.define('My.app.App', {
* requires: [
* 'My.ux.CoolTip'
* ]
* });
*
* Overrides can also contain statics, inheritableStatics, or privates:
*
* Ext.define('My.app.BarMod', {
* override: 'Ext.foo.Bar',
*
* statics: {
* method: function (x) {
* return this.callParent([x * 2]); // call Ext.foo.Bar.method
* }
* }
* });
*
* Starting in version 4.2.2, overrides can declare their `compatibility` based
* on the framework version or on versions of other packages. For details on the
* syntax and options for these checks, see `Ext.checkVersion`.
*
* The simplest use case is to test framework version for compatibility:
*
* Ext.define('App.overrides.grid.Panel', {
* override: 'Ext.grid.Panel',
*
* compatibility: '4.2.2', // only if framework version is 4.2.2
*
* //...
* });
*
* An array is treated as an OR, so if any specs match, the override is
* compatible.
*
* Ext.define('App.overrides.some.Thing', {
* override: 'Foo.some.Thing',
*
* compatibility: [
* '4.2.2',
* 'foo@1.0.1-1.0.2'
* ],
*
* //...
* });
*
* To require that all specifications match, an object can be provided:
*
* Ext.define('App.overrides.some.Thing', {
* override: 'Foo.some.Thing',
*
* compatibility: {
* and: [
* '4.2.2',
* 'foo@1.0.1-1.0.2'
* ]
* },
*
* //...
* });
*
* Because the object form is just a recursive check, these can be nested:
*
* Ext.define('App.overrides.some.Thing', {
* override: 'Foo.some.Thing',
*
* compatibility: {
* and: [
* '4.2.2', // exactly version 4.2.2 of the framework *AND*
* {
* // either (or both) of these package specs:
* or: [
* 'foo@1.0.1-1.0.2',
* 'bar@3.0+'
* ]
* }
* ]
* },
*
* //...
* });
*
* IMPORTANT: An override is only included in a build if the class it overrides is
* required. Otherwise, the override, like the target class, is not included. In
* Sencha Cmd v4, the `compatibility` declaration can likewise be used to remove
* incompatible overrides from a build.
*
* @param {String} className The class name to create in string dot-namespaced format, for example:
* 'My.very.awesome.Class', 'FeedViewer.plugin.CoolPager'
* It is highly recommended to follow this simple convention:
* - The root and the class name are 'CamelCased'
* - Everything else is lower-cased
* Pass `null` to create an anonymous class.
* @param {Object} data The key - value pairs of properties to apply to this class. Property names can be of any valid
* strings, except those in the reserved listed below:
*
* - {@link Ext.Class#cfg-alias alias}
* - {@link Ext.Class#cfg-alternateClassName alternateClassName}
* - {@link Ext.Class#cfg-cachedConfig cachedConfig}
* - {@link Ext.Class#cfg-config config}
* - {@link Ext.Class#cfg-extend extend}
* - {@link Ext.Class#cfg-inheritableStatics inheritableStatics}
* - {@link Ext.Class#cfg-mixins mixins}
* - {@link Ext.Class#cfg-override override}
* - {@link Ext.Class#cfg-platformConfig platformConfig}
* - {@link Ext.Class#cfg-privates privates}
* - {@link Ext.Class#cfg-requires requires}
* - `self`
* - {@link Ext.Class#cfg-singleton singleton}
* - {@link Ext.Class#cfg-statics statics}
* - {@link Ext.Class#cfg-uses uses}
* - {@link Ext.Class#cfg-xtype xtype} (for {@link Ext.Component Components} only)
*
* @param {Function} [createdFn] Callback to execute after the class is created, the execution scope of which
* (`this`) will be the newly created class itself.
* @return {Ext.Base}
* @member Ext
*/
define: function(className, data, createdFn) {
Ext.classSystemMonitor && Ext.classSystemMonitor(className, 'ClassManager#define', arguments);
if (data.override) {
Manager.classState[className] = 20;
return Manager.createOverride.apply(Manager, arguments);
}
Manager.classState[className] = 10;
return Manager.create.apply(Manager, arguments);
},
/**
* Undefines a class defined using the #define method. Typically used
* for unit testing where setting up and tearing down a class multiple
* times is required. For example:
*
* // define a class
* Ext.define('Foo', {
* ...
* });
*
* // run test
*
* // undefine the class
* Ext.undefine('Foo');
* @param {String} className The class name to undefine in string dot-namespaced format.
* @private
*/
undefine: function(className) {
Ext.classSystemMonitor && Ext.classSystemMonitor(className, 'Ext.ClassManager#undefine', arguments);
var classes = Manager.classes;
delete classes[className];
delete Manager.existCache[className];
delete Manager.classState[className];
Manager.removeName(className);
var entry = Manager.getNamespaceEntry(className),
scope = entry.parent ? Manager.lookupName(entry.parent, false) : Ext.global;
if (scope) {
// Old IE blows up on attempt to delete window property
try {
delete scope[entry.name];
} catch (e) {
scope[entry.name] = undefined;
}
}
},
/**
* @inheritdoc Ext.ClassManager#getName
* @member Ext
* @method getClassName
*/
getClassName: alias(Manager, 'getName'),
/**
* Returns the displayName property or className or object. When all else fails, returns "Anonymous".
* @param {Object} object
* @return {String}
*/
getDisplayName: function(object) {
if (object) {
if (object.displayName) {
return object.displayName;
}
if (object.$name && object.$class) {
return Ext.getClassName(object.$class) + '#' + object.$name;
}
if (object.$className) {
return object.$className;
}
}
return 'Anonymous';
},
/**
* @inheritdoc Ext.ClassManager#getClass
* @member Ext
* @method getClass
*/
getClass: alias(Manager, 'getClass'),
/**
* Creates namespaces to be used for scoping variables and classes so that they are not global.
* Specifying the last node of a namespace implicitly creates all other nodes. Usage:
*
* Ext.namespace('Company', 'Company.data');
*
* // equivalent and preferable to the above syntax
* Ext.ns('Company.data');
*
* Company.Widget = function() { ... };
*
* Company.data.CustomStore = function(config) { ... };
*
* @param {String...} namespaces
* @return {Object} The (last) namespace object created.
* @member Ext
* @method namespace
*/
namespace: function() {
var root = global,
i;
for (i = arguments.length; i-- > 0; ) {
root = Manager.lookupName(arguments[i], true);
}
return root;
}
});
/**
* This function registers top-level (root) namespaces. This is needed for "sandbox"
* builds.
*
* Ext.addRootNamespaces({
* MyApp: MyApp,
* Common: Common
* });
*
* In the above example, `MyApp` and `Common` are top-level namespaces that happen
* to also be included in the sandbox closure. Something like this:
*
* (function(Ext) {
*
* Ext.sandboxName = 'Ext6';
* Ext.isSandboxed = true;
* Ext.buildSettings = { baseCSSPrefix: "x6-", scopeResetCSS: true };
*
* var MyApp = MyApp || {};
* Ext.addRootNamespaces({ MyApp: MyApp );
*
* ... normal app.js goes here ...
*
* })(this.Ext6 || (this.Ext6 = {}));
*
* The sandbox wrapper around the normally built `app.js` content has to take care
* of introducing top-level namespaces as well as call this method.
*
* @param {Object} namespaces
* @method addRootNamespaces
* @member Ext
* @since 6.0.0
* @private
*/
Ext.addRootNamespaces = Manager.addRootNamespaces;
/**
* Old name for {@link Ext#widget}.
* @deprecated Use {@link Ext#widget} instead.
* @method createWidget
* @member Ext
* @private
*/
Ext.createWidget = Ext.widget;
/**
* Convenient alias for {@link Ext#namespace Ext.namespace}.
* @inheritdoc Ext#namespace
* @member Ext
* @method ns
*/
Ext.ns = Ext.namespace;
Class.registerPreprocessor('className', function(cls, data) {
if ('$className' in data) {
cls.$className = data.$className;
cls.displayName = cls.$className;
}
Ext.classSystemMonitor && Ext.classSystemMonitor(cls, 'Ext.ClassManager#classNamePreprocessor', arguments);
}, true, 'first');
Class.registerPreprocessor('alias', function(cls, data) {
Ext.classSystemMonitor && Ext.classSystemMonitor(cls, 'Ext.ClassManager#aliasPreprocessor', arguments);
var prototype = cls.prototype,
xtypes = arrayFrom(data.xtype),
aliases = arrayFrom(data.alias),
widgetPrefix = 'widget.',
widgetPrefixLength = widgetPrefix.length,
xtypesChain = Array.prototype.slice.call(prototype.xtypesChain || []),
xtypesMap = Ext.merge({}, prototype.xtypesMap || {}),
i, ln, alias, xtype;
for (i = 0 , ln = aliases.length; i < ln; i++) {
alias = aliases[i];
if (typeof alias !== 'string' || alias.length < 1) {
throw new Error("[Ext.define] Invalid alias of: '" + alias + "' for class: '" + name + "'; must be a valid string");
}
if (alias.substring(0, widgetPrefixLength) === widgetPrefix) {
xtype = alias.substring(widgetPrefixLength);
Ext.Array.include(xtypes, xtype);
}
}
cls.xtype = data.xtype = xtypes[0];
data.xtypes = xtypes;
for (i = 0 , ln = xtypes.length; i < ln; i++) {
xtype = xtypes[i];
if (!xtypesMap[xtype]) {
xtypesMap[xtype] = true;
xtypesChain.push(xtype);
}
}
data.xtypesChain = xtypesChain;
data.xtypesMap = xtypesMap;
// Class is already extended at this point
Ext.Function.interceptAfterOnce(cls, 'onClassCreated', function() {
var cls = this,
prototype = cls.prototype,
mixins = prototype.mixins,
key, mixin;
Ext.classSystemMonitor && Ext.classSystemMonitor(cls, 'Ext.ClassManager#aliasPreprocessor#afterClassCreated', arguments);
for (key in mixins) {
if (mixins.hasOwnProperty(key)) {
mixin = mixins[key];
xtypes = mixin.xtypes;
if (xtypes) {
for (i = 0 , ln = xtypes.length; i < ln; i++) {
xtype = xtypes[i];
if (!xtypesMap[xtype]) {
xtypesMap[xtype] = true;
xtypesChain.push(xtype);
}
}
}
}
}
});
for (i = 0 , ln = xtypes.length; i < ln; i++) {
xtype = xtypes[i];
if (typeof xtype !== 'string' || xtype.length < 1) {
throw new Error("[Ext.define] Invalid xtype of: '" + xtype + "' for class: '" + name + "'; must be a valid non-empty string");
}
Ext.Array.include(aliases, widgetPrefix + xtype);
}
data.alias = aliases;
}, [
'xtype',
'alias'
]);
// load the cmd-5 style app manifest metadata now, if available...
if (Ext.manifest) {
var manifest = Ext.manifest,
classes = manifest.classes,
paths = manifest.paths,
aliases = {},
alternates = {},
className, obj, name, path, baseUrl;
if (paths) {
// if the manifest paths were calculated as relative to the
// bootstrap file, then we need to prepend Boot.baseUrl to the
// paths before processing
if (manifest.bootRelative) {
baseUrl = Ext.Boot.baseUrl;
for (path in paths) {
if (paths.hasOwnProperty(path)) {
paths[path] = baseUrl + paths[path];
}
}
}
Manager.setPath(paths);
}
if (classes) {
for (className in classes) {
alternates[className] = [];
aliases[className] = [];
obj = classes[className];
if (obj.alias) {
aliases[className] = obj.alias;
}
if (obj.alternates) {
alternates[className] = obj.alternates;
}
}
}
Manager.addAlias(aliases);
Manager.addAlternate(alternates);
}
return Manager;
}(Ext.Class, Ext.Function.alias, Array.prototype.slice, Ext.Array.from, Ext.global));
/**
* @class Ext.env.Browser
* Provides information about browser.
*
* Should not be manually instantiated unless for unit-testing.
* Access the global instance stored in {@link Ext.browser} instead.
* @private
*/
(Ext.env || (Ext.env = {})).Browser = function(userAgent, publish) {
// @define Ext.env.Browser
// @define Ext.browser
// @require Ext.Object
// @require Ext.Version
var me = this,
browserPrefixes = Ext.Boot.browserPrefixes,
browserNames = Ext.Boot.browserNames,
enginePrefixes = me.enginePrefixes,
engineNames = me.engineNames,
browserMatch = userAgent.match(new RegExp('((?:' + Ext.Object.getValues(browserPrefixes).join(')|(?:') + '))([\\w\\._]+)')),
engineMatch = userAgent.match(new RegExp('((?:' + Ext.Object.getValues(enginePrefixes).join(')|(?:') + '))([\\w\\._]+)')),
browserName = browserNames.other,
engineName = engineNames.other,
browserVersion = '',
engineVersion = '',
majorVer = '',
isWebView = false,
i, prefix, mode, name, maxIEVersion;
/**
* @property {String}
* Browser User Agent string.
*/
me.userAgent = userAgent;
/**
* A "hybrid" property, can be either accessed as a method call, for example:
*
* if (Ext.browser.is('IE')) {
* // ...
* }
*
* Or as an object with Boolean properties, for example:
*
* if (Ext.browser.is.IE) {
* // ...
* }
*
* Versions can be conveniently checked as well. For example:
*
* if (Ext.browser.is.IE10) {
* // Equivalent to (Ext.browser.is.IE && Ext.browser.version.equals(10))
* }
*
* __Note:__ Only {@link Ext.Version#getMajor major component} and {@link Ext.Version#getShortVersion simplified}
* value of the version are available via direct property checking.
*
* Supported values are:
*
* - IE
* - Firefox
* - Safari
* - Chrome
* - Opera
* - WebKit
* - Gecko
* - Presto
* - Trident
* - WebView
* - Other
*
* @param {String} name The OS name to check.
* @return {Boolean}
*/
this.is = function(name) {
// Since this function reference also acts as a map, we do not want it to be
// shared between instances, so it is defined here, not on the prototype.
return !!this.is[name];
};
// Edge has a userAgent with All browsers so we manage it separately
// "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10240"
if (/Edge\//.test(userAgent)) {
browserMatch = userAgent.match(/(Edge\/)([\w.]+)/);
}
if (browserMatch) {
browserName = browserNames[Ext.Object.getKey(browserPrefixes, browserMatch[1])];
if (browserName === 'Safari' && /^Opera/.test(userAgent)) {
// Prevent Opera 12 and earlier from being incorrectly reported as Safari
browserName = 'Opera';
}
browserVersion = new Ext.Version(browserMatch[2]);
}
if (engineMatch) {
engineName = engineNames[Ext.Object.getKey(enginePrefixes, engineMatch[1])];
engineVersion = new Ext.Version(engineMatch[2]);
}
if (engineName === 'Trident' && browserName !== 'IE') {
browserName = 'IE';
var version = userAgent.match(/.*rv:(\d+.\d+)/);
if (version && version.length) {
version = version[1];
browserVersion = new Ext.Version(version);
}
}
if (browserName && browserVersion) {
Ext.setVersion(browserName, browserVersion);
}
/**
* @property chromeVersion
* The current version of Chrome (0 if the browser is not Chrome).
* @readonly
* @type Number
* @member Ext
*/
/**
* @property firefoxVersion
* The current version of Firefox (0 if the browser is not Firefox).
* @readonly
* @type Number
* @member Ext
*/
/**
* @property ieVersion
* The current version of IE (0 if the browser is not IE). This does not account
* for the documentMode of the current page, which is factored into {@link #isIE8},
* and {@link #isIE9}. Thus this is not always true:
*
* Ext.isIE8 == (Ext.ieVersion == 8)
*
* @readonly
* @type Number
* @member Ext
*/
/**
* @property isChrome
* True if the detected browser is Chrome.
* @readonly
* @type Boolean
* @member Ext
*/
/**
* @property isGecko
* True if the detected browser uses the Gecko layout engine (e.g. Mozilla, Firefox).
* @readonly
* @type Boolean
* @member Ext
*/
/**
* @property isIE
* True if the detected browser is Internet Explorer.
* @readonly
* @type Boolean
* @member Ext
*/
/**
* @property isIE8
* True if the detected browser is Internet Explorer 8.x.
* @readonly
* @type Boolean
* @member Ext
*/
/**
* @property isIE8m
* True if the detected browser is Internet Explorer 8.x or lower.
* @readonly
* @type Boolean
* @member Ext
*/
/**
* @property isIE8p
* True if the detected browser is Internet Explorer 8.x or higher.
* @readonly
* @type Boolean
* @member Ext
*/
/**
* @property isIE9
* True if the detected browser is Internet Explorer 9.x.
* @readonly
* @type Boolean
* @member Ext
*/
/**
* @property isIE9m
* True if the detected browser is Internet Explorer 9.x or lower.
* @readonly
* @type Boolean
* @member Ext
*/
/**
* @property isIE9p
* True if the detected browser is Internet Explorer 9.x or higher.
* @readonly
* @type Boolean
* @member Ext
*/
/**
* @property isIE10
* True if the detected browser is Internet Explorer 10.x.
* @readonly
* @type Boolean
* @member Ext
*/
/**
* @property isIE10m
* True if the detected browser is Internet Explorer 10.x or lower.
* @readonly
* @type Boolean
* @member Ext
*/
/**
* @property isIE10p
* True if the detected browser is Internet Explorer 10.x or higher.
* @readonly
* @type Boolean
* @member Ext
*/
/**
* @property isIE11
* True if the detected browser is Internet Explorer 11.x.
* @readonly
* @type Boolean
* @member Ext
*/
/**
* @property isIE11m
* True if the detected browser is Internet Explorer 11.x or lower.
* @readonly
* @type Boolean
* @member Ext
*/
/**
* @property isIE11p
* True if the detected browser is Internet Explorer 11.x or higher.
* @readonly
* @type Boolean
* @member Ext
*/
/**
* @property isEdge
* True if the detected browser is Edge.
* @readonly
* @type Boolean
* @member Ext
*/
/**
* @property isLinux
* True if the detected platform is Linux.
* @readonly
* @type Boolean
* @member Ext
*/
/**
* @property isMac
* True if the detected platform is Mac OS.
* @readonly
* @type Boolean
* @member Ext
*/
/**
* @property isOpera
* True if the detected browser is Opera.
* @readonly
* @type Boolean
* @member Ext
*/
/**
* @property isSafari
* True if the detected browser is Safari.
* @readonly
* @type Boolean
* @member Ext
*/
/**
* @property isWebKit
* True if the detected browser uses WebKit.
* @readonly
* @type Boolean
* @member Ext
*/
/**
* @property isWindows
* True if the detected platform is Windows.
* @readonly
* @type Boolean
* @member Ext
*/
/**
* @property operaVersion
* The current version of Opera (0 if the browser is not Opera).
* @readonly
* @type Number
* @member Ext
*/
/**
* @property safariVersion
* The current version of Safari (0 if the browser is not Safari).
* @readonly
* @type Number
* @member Ext
*/
/**
* @property webKitVersion
* The current version of WebKit (0 if the browser does not use WebKit).
* @readonly
* @type Number
* @member Ext
*/
// Facebook changes the userAgent when you view a website within their iOS app. For some reason, the strip out information
// about the browser, so we have to detect that and fake it...
if (userAgent.match(/FB/) && browserName === "Other") {
browserName = browserNames.safari;
engineName = engineNames.webkit;
}
if (userAgent.match(/Android.*Chrome/g)) {
browserName = 'ChromeMobile';
}
if (userAgent.match(/OPR/)) {
browserName = 'Opera';
browserMatch = userAgent.match(/OPR\/(\d+.\d+)/);
browserVersion = new Ext.Version(browserMatch[1]);
}
Ext.apply(this, {
engineName: engineName,
engineVersion: engineVersion,
name: browserName,
version: browserVersion
});
this.setFlag(browserName, true, publish);
// e.g., Ext.isIE
if (browserVersion) {
majorVer = browserVersion.getMajor() || '';
if (me.is.IE) {
majorVer = parseInt(majorVer, 10);
mode = document.documentMode;
// IE's Developer Tools allows switching of Browser Mode (userAgent) and
// Document Mode (actual behavior) independently. While this makes no real
// sense, the bottom line is that document.documentMode holds the key to
// getting the proper "version" determined. That value is always 5 when in
// Quirks Mode.
if (mode === 7 || (majorVer === 7 && mode !== 8 && mode !== 9 && mode !== 10)) {
majorVer = 7;
} else if (mode === 8 || (majorVer === 8 && mode !== 8 && mode !== 9 && mode !== 10)) {
majorVer = 8;
} else if (mode === 9 || (majorVer === 9 && mode !== 7 && mode !== 8 && mode !== 10)) {
majorVer = 9;
} else if (mode === 10 || (majorVer === 10 && mode !== 7 && mode !== 8 && mode !== 9)) {
majorVer = 10;
} else if (mode === 11 || (majorVer === 11 && mode !== 7 && mode !== 8 && mode !== 9 && mode !== 10)) {
majorVer = 11;
}
maxIEVersion = Math.max(majorVer, Ext.Boot.maxIEVersion);
for (i = 7; i <= maxIEVersion; ++i) {
prefix = 'isIE' + i;
if (majorVer <= i) {
Ext[prefix + 'm'] = true;
}
if (majorVer === i) {
Ext[prefix] = true;
}
if (majorVer >= i) {
Ext[prefix + 'p'] = true;
}
}
}
if (me.is.Opera && parseInt(majorVer, 10) <= 12) {
Ext.isOpera12m = true;
}
Ext.chromeVersion = Ext.isChrome ? majorVer : 0;
Ext.firefoxVersion = Ext.isFirefox ? majorVer : 0;
Ext.ieVersion = Ext.isIE ? majorVer : 0;
Ext.operaVersion = Ext.isOpera ? majorVer : 0;
Ext.safariVersion = Ext.isSafari ? majorVer : 0;
Ext.webKitVersion = Ext.isWebKit ? majorVer : 0;
this.setFlag(browserName + majorVer, true, publish);
// Ext.isIE10
this.setFlag(browserName + browserVersion.getShortVersion());
}
for (i in browserNames) {
if (browserNames.hasOwnProperty(i)) {
name = browserNames[i];
this.setFlag(name, browserName === name);
}
}
this.setFlag(name);
if (engineVersion) {
this.setFlag(engineName + (engineVersion.getMajor() || ''));
this.setFlag(engineName + engineVersion.getShortVersion());
}
for (i in engineNames) {
if (engineNames.hasOwnProperty(i)) {
name = engineNames[i];
this.setFlag(name, engineName === name, publish);
}
}
this.setFlag('Standalone', !!navigator.standalone);
this.setFlag('Ripple', !!document.getElementById("tinyhippos-injected") && !Ext.isEmpty(window.top.ripple));
this.setFlag('WebWorks', !!window.blackberry);
if (window.PhoneGap !== undefined || window.Cordova !== undefined || window.cordova !== undefined) {
isWebView = true;
this.setFlag('PhoneGap');
this.setFlag('Cordova');
}
// Check if running in UIWebView
if (/(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)(?!.*FBAN)/i.test(userAgent)) {
isWebView = true;
}
// Flag to check if it we are in the WebView
this.setFlag('WebView', isWebView);
/**
* @property {Boolean}
* `true` if browser is using strict mode.
*/
this.isStrict = Ext.isStrict = document.compatMode === "CSS1Compat";
/**
* @property {Boolean}
* `true` if page is running over SSL.
*/
this.isSecure = Ext.isSecure;
// IE10Quirks, Chrome26Strict, etc.
this.identity = browserName + majorVer + (this.isStrict ? 'Strict' : 'Quirks');
};
Ext.env.Browser.prototype = {
constructor: Ext.env.Browser,
engineNames: {
webkit: 'WebKit',
gecko: 'Gecko',
presto: 'Presto',
trident: 'Trident',
other: 'Other'
},
enginePrefixes: {
webkit: 'AppleWebKit/',
gecko: 'Gecko/',
presto: 'Presto/',
trident: 'Trident/'
},
styleDashPrefixes: {
WebKit: '-webkit-',
Gecko: '-moz-',
Trident: '-ms-',
Presto: '-o-',
Other: ''
},
stylePrefixes: {
WebKit: 'Webkit',
Gecko: 'Moz',
Trident: 'ms',
Presto: 'O',
Other: ''
},
propertyPrefixes: {
WebKit: 'webkit',
Gecko: 'moz',
Trident: 'ms',
Presto: 'o',
Other: ''
},
// scope: Ext.env.Browser.prototype
/**
* The full name of the current browser.
* Possible values are:
*
* - IE
* - Firefox
* - Safari
* - Chrome
* - Opera
* - Other
* @type String
* @readonly
*/
name: null,
/**
* Refer to {@link Ext.Version}.
* @type Ext.Version
* @readonly
*/
version: null,
/**
* The full name of the current browser's engine.
* Possible values are:
*
* - WebKit
* - Gecko
* - Presto
* - Trident
* - Other
* @type String
* @readonly
*/
engineName: null,
/**
* Refer to {@link Ext.Version}.
* @type Ext.Version
* @readonly
*/
engineVersion: null,
setFlag: function(name, value, publish) {
if (value === undefined) {
value = true;
}
this.is[name] = value;
this.is[name.toLowerCase()] = value;
if (publish) {
Ext['is' + name] = value;
}
return this;
},
getStyleDashPrefix: function() {
return this.styleDashPrefixes[this.engineName];
},
getStylePrefix: function() {
return this.stylePrefixes[this.engineName];
},
getVendorProperyName: function(name) {
var prefix = this.propertyPrefixes[this.engineName];
if (prefix.length > 0) {
return prefix + Ext.String.capitalize(name);
}
return name;
},
getPreferredTranslationMethod: function(config) {
if (typeof config === 'object' && 'translationMethod' in config && config.translationMethod !== 'auto') {
return config.translationMethod;
} else {
return 'csstransform';
}
}
};
/**
* @class Ext.browser
* @extends Ext.env.Browser
* @singleton
* Provides useful information about the current browser.
*
* Example:
*
* if (Ext.browser.is.IE) {
* // IE specific code here
* }
*
* if (Ext.browser.is.WebKit) {
* // WebKit specific code here
* }
*
* console.log("Version " + Ext.browser.version);
*
* For a full list of supported values, refer to {@link #is} property/method.
*
*/
(function(userAgent) {
Ext.browser = new Ext.env.Browser(userAgent, true);
Ext.userAgent = userAgent.toLowerCase();
/**
* @property {String} SSL_SECURE_URL
* URL to a blank file used by Ext when in secure mode for iframe src and onReady src
* to prevent the IE insecure content warning (`'about:blank'`, except for IE
* in secure mode, which is `'javascript:""'`).
* @member Ext
*/
Ext.SSL_SECURE_URL = Ext.isSecure && Ext.isIE ? 'javascript:\'\'' : 'about:blank';
}(// jshint ignore:line
Ext.global.navigator.userAgent));
/**
* @class Ext.env.OS
*
* Provides information about operating system environment.
*
* Should not be manually instantiated unless for unit-testing.
* Access the global instance stored in {@link Ext.os} instead.
* @private
*/
Ext.env.OS = function(userAgent, platform, browserScope) {
// @define Ext.env.OS
// @define Ext.os
// @require Ext.Version
// @require Ext.env.Browser
var me = this,
names = Ext.Boot.osNames,
prefixes = Ext.Boot.osPrefixes,
name,
version = '',
is = me.is,
i, prefix, match, item, match1;
browserScope = browserScope || Ext.browser;
for (i in prefixes) {
if (prefixes.hasOwnProperty(i)) {
prefix = prefixes[i];
match = userAgent.match(new RegExp('(?:' + prefix + ')([^\\s;]+)'));
if (match) {
name = names[i];
match1 = match[1];
// This is here because some HTC android devices show an OSX Snow Leopard userAgent by default.
// And the Kindle Fire doesn't have any indicator of Android as the OS in its User Agent
if (match1 && match1 === "HTC_") {
version = new Ext.Version("2.3");
} else if (match1 && match1 === "Silk/") {
version = new Ext.Version("2.3");
} else {
version = new Ext.Version(match[match.length - 1]);
}
break;
}
}
}
if (!name) {
name = names[(userAgent.toLowerCase().match(/mac|win|linux/) || [
'other'
])[0]];
version = new Ext.Version('');
}
this.name = name;
this.version = version;
// This is added as a workaround for Chrome iPad emulation mode
// it will report the platform of the machine (MacIntel, Win32, etc) instead
// of an emulated platform
if (userAgent.match(/ipad/i)) {
platform = 'iPad';
}
if (platform) {
this.setFlag(platform.replace(/ simulator$/i, ''));
}
this.setFlag(name);
if (version) {
this.setFlag(name + (version.getMajor() || ''));
this.setFlag(name + version.getShortVersion());
}
for (i in names) {
if (names.hasOwnProperty(i)) {
item = names[i];
if (!is.hasOwnProperty(name)) {
this.setFlag(item, (name === item));
}
}
}
// Detect if the device is the iPhone 5.
if (this.name === "iOS" && window.screen.height === 568) {
this.setFlag('iPhone5');
}
if (browserScope.is.Safari || browserScope.is.Silk) {
// Ext.browser.version.shortVersion == 501 is for debugging off device
if (this.is.Android2 || this.is.Android3 || browserScope.version.shortVersion === 501) {
browserScope.setFlag("AndroidStock");
}
if (this.is.Android4) {
browserScope.setFlag("AndroidStock");
browserScope.setFlag("AndroidStock4");
}
}
};
Ext.env.OS.prototype = {
constructor: Ext.env.OS,
/**
* A "hybrid" property, can be either accessed as a method call, i.e:
*
* if (Ext.os.is('Android')) {
* // ...
* }
*
* or as an object with boolean properties, i.e:
*
* if (Ext.os.is.Android) {
* // ...
* }
*
* Versions can be conveniently checked as well. For example:
*
* if (Ext.os.is.Android2) {
* // Equivalent to (Ext.os.is.Android && Ext.os.version.equals(2))
* }
*
* if (Ext.os.is.iOS32) {
* // Equivalent to (Ext.os.is.iOS && Ext.os.version.equals(3.2))
* }
*
* Note that only {@link Ext.Version#getMajor major component} and {@link Ext.Version#getShortVersion simplified}
* value of the version are available via direct property checking. Supported values are:
*
* - iOS
* - iPad
* - iPhone
* - iPhone5 (also true for 4in iPods).
* - iPod
* - Android
* - WebOS
* - BlackBerry
* - Bada
* - MacOS
* - Windows
* - Linux
* - Other
* @member Ext.os
* @param {String} name The OS name to check.
* @return {Boolean}
*/
is: function(name) {
return !!this[name];
},
/**
* @property {String} [name=null]
* @readonly
* @member Ext.os
* The full name of the current operating system. Possible values are:
*
* - iOS
* - Android
* - WebOS
* - BlackBerry,
* - MacOS
* - Windows
* - Linux
* - Other
*/
name: null,
/**
* @property {Ext.Version} [version=null]
* Refer to {@link Ext.Version}
* @member Ext.os
* @readonly
*/
version: null,
setFlag: function(name, value) {
if (value === undefined) {
value = true;
}
if (this.flags) {
this.flags[name] = value;
}
this.is[name] = value;
this.is[name.toLowerCase()] = value;
return this;
}
};
(function() {
var navigation = Ext.global.navigator,
userAgent = navigation.userAgent,
OS = Ext.env.OS,
is = (Ext.is || (Ext.is = {})),
osEnv, osName, deviceType;
OS.prototype.flags = is;
/**
* @class Ext.os
* @extends Ext.env.OS
* @singleton
* Provides useful information about the current operating system environment.
*
* Example:
*
* if (Ext.os.is.Windows) {
* // Windows specific code here
* }
*
* if (Ext.os.is.iOS) {
* // iPad, iPod, iPhone, etc.
* }
*
* console.log("Version " + Ext.os.version);
*
* For a full list of supported values, refer to the {@link #is} property/method.
*
*/
Ext.os = osEnv = new OS(userAgent, navigation.platform);
osName = osEnv.name;
// A couple compatible flavors:
Ext['is' + osName] = true;
// e.g., Ext.isWindows
Ext.isMac = is.Mac = is.MacOS;
var search = window.location.search.match(/deviceType=(Tablet|Phone)/),
nativeDeviceType = window.deviceType;
// Override deviceType by adding a get variable of deviceType. NEEDED FOR DOCS APP.
// E.g: example/kitchen-sink.html?deviceType=Phone
if (search && search[1]) {
deviceType = search[1];
} else if (nativeDeviceType === 'iPhone') {
deviceType = 'Phone';
} else if (nativeDeviceType === 'iPad') {
deviceType = 'Tablet';
} else {
if (!osEnv.is.Android && !osEnv.is.iOS && !osEnv.is.WindowsPhone && /Windows|Linux|MacOS/.test(osName)) {
deviceType = 'Desktop';
// always set it to false when you are on a desktop not using Ripple Emulation
Ext.browser.is.WebView = !!Ext.browser.is.Ripple;
} else if (osEnv.is.iPad || osEnv.is.RIMTablet || osEnv.is.Android3 || Ext.browser.is.Silk || (osEnv.is.Android && userAgent.search(/mobile/i) === -1)) {
deviceType = 'Tablet';
} else {
deviceType = 'Phone';
}
}
/**
* @property {String} deviceType
* The generic type of the current device.
*
* Possible values:
*
* - Phone
* - Tablet
* - Desktop
*
* For testing purposes the deviceType can be overridden by adding
* a deviceType parameter to the URL of the page, like so:
*
* http://localhost/mypage.html?deviceType=Tablet
*
* @member Ext.os
*/
osEnv.setFlag(deviceType, true);
osEnv.deviceType = deviceType;
delete OS.prototype.flags;
}());
/**
* @class Ext.feature
* @singleton
*
* A simple class to verify if a browser feature exists or not on the current device.
*
* if (Ext.feature.has.Canvas) {
* // do some cool things with canvas here
* }
*
* See the {@link #has} property/method for details of the features that can be detected.
*
*/
Ext.feature = {
// @define Ext.env.Feature
// @define Ext.feature
// @define Ext.supports
// @require Ext.String
// @require Ext.env.Browser
// @require Ext.env.OS
/**
* @method has
* @member Ext.feature
* Verifies if a browser feature exists or not on the current device.
*
* A "hybrid" property, can be either accessed as a method call, i.e:
*
* if (Ext.feature.has('Canvas')) {
* // ...
* }
*
* or as an object with boolean properties, i.e:
*
* if (Ext.feature.has.Canvas) {
* // ...
* }
*
* For possible properties/parameter values see `Ext.supports`.
*
* @param {String} name The feature name to check.
* @return {Boolean}
*/
has: function(name) {
return !!this.has[name];
},
testElements: {},
getTestElement: function(tag, createNew) {
if (tag === undefined) {
tag = 'div';
} else if (typeof tag !== 'string') {
return tag;
}
if (createNew) {
return document.createElement(tag);
}
if (!this.testElements[tag]) {
this.testElements[tag] = document.createElement(tag);
}
return this.testElements[tag];
},
isStyleSupported: function(name, tag) {
var elementStyle = this.getTestElement(tag).style,
cName = Ext.String.capitalize(name);
if (typeof elementStyle[name] !== 'undefined' || typeof elementStyle[Ext.browser.getStylePrefix(name) + cName] !== 'undefined') {
return true;
}
return false;
},
isStyleSupportedWithoutPrefix: function(name, tag) {
var elementStyle = this.getTestElement(tag).style;
if (typeof elementStyle[name] !== 'undefined') {
return true;
}
return false;
},
isEventSupported: function(name, tag) {
if (tag === undefined) {
tag = window;
}
var element = this.getTestElement(tag),
eventName = 'on' + name.toLowerCase(),
isSupported = (eventName in element);
if (!isSupported) {
if (element.setAttribute && element.removeAttribute) {
element.setAttribute(eventName, '');
isSupported = typeof element[eventName] === 'function';
if (typeof element[eventName] !== 'undefined') {
element[eventName] = undefined;
}
element.removeAttribute(eventName);
}
}
return isSupported;
},
// This is a local copy of certain logic from Element.getStyle
// to break a dependancy between the supports mechanism and Element
// use this instead of element references to check for styling info
getStyle: function(element, styleName) {
var view = element.ownerDocument.defaultView,
style = (view ? view.getComputedStyle(element, null) : element.currentStyle);
return (style || element.style)[styleName];
},
getSupportedPropertyName: function(object, name) {
var vendorName = Ext.browser.getVendorProperyName(name);
if (vendorName in object) {
return vendorName;
} else if (name in object) {
return name;
}
return null;
},
/**
* Runs feature detection routines and sets the various flags. This is called when
* the scripts loads (very early) and again at {@link Ext#onReady}. Some detections
* can be run immediately. Others that require the document body will not run until
* domready (these have the `ready` flag set).
*
* Each test is run only once, so calling this method from an onReady function is safe
* and ensures that all flags have been set.
* @private
*/
detect: function(isReady) {
var me = this,
doc = document,
toRun = me.toRun || me.tests,
n = toRun.length,
div = doc.createElement('div'),
notRun = [],
supports = Ext.supports,
has = me.has,
name, names, test, vector, value;
// Only the legacy browser tests use this div so clip this out if we don't need
// to use it.
div.innerHTML = '<div style="height:30px;width:50px;">' + '<div style="height:20px;width:20px;"></div>' + '</div>' + '<div style="width: 200px; height: 200px; position: relative; padding: 5px;">' + '<div style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"></div>' + '</div>' + '<div style="position: absolute; left: 10%; top: 10%;"></div>' + '<div style="float:left; background-color:transparent;"></div>';
if (isReady) {
doc.body.appendChild(div);
}
vector = me.preDetected[Ext.browser.identity] || [];
while (n--) {
test = toRun[n];
value = vector[n];
name = test.name;
names = test.names;
if (value === undefined) {
if (!isReady && test.ready) {
// test requires domready state
notRun.push(test);
continue;
}
value = test.fn.call(me, doc, div);
}
// Store test results on Ext.supports and Ext.feature.has
if (name) {
supports[name] = has[name] = value;
} else if (names) {
while (names.length) {
name = names.pop();
supports[name] = has[name] = value;
}
}
}
if (isReady) {
doc.body.removeChild(div);
}
me.toRun = notRun;
},
//</debug>
report: function() {
var values = [],
len = this.tests.length,
i;
for (i = 0; i < len; ++i) {
values.push(this.has[this.tests[i].name] ? 1 : 0);
}
Ext.log(Ext.browser.identity + ': [' + values.join(',') + ']');
},
//</debug>
preDetected: {},
// TODO
/**
* @class Ext.supports
*
* Contains information about features supported in the current environment as well
* as bugs detected.
*
* @singleton
*/
tests: [
{
/**
* @property CloneNodeCopiesExpando `true` if the native DOM cloneNode method copies
* expando properties to the newly cloned node.
*
* This property is available at application boot time, before document ready.
* @type {Boolean}
*/
name: 'CloneNodeCopiesExpando',
fn: function() {
var el = document.createElement('div');
el.expandoProp = {};
return el.cloneNode().expandoProp === el.expandoProp;
}
},
{
/**
* @property CSSPointerEvents `true` if document environment supports the CSS3
* pointer-events style.
*
* This property is available at application boot time, before document ready.
* @type {Boolean}
*/
name: 'CSSPointerEvents',
fn: function(doc) {
return 'pointerEvents' in doc.documentElement.style;
}
},
{
/**
* @property CSS3BoxShadow `true` if document environment supports the CSS3
* box-shadow style.
*
* This property is available at application boot time, before document ready.
* @type {Boolean}
*/
name: 'CSS3BoxShadow',
fn: function(doc) {
return 'boxShadow' in doc.documentElement.style || 'WebkitBoxShadow' in doc.documentElement.style || 'MozBoxShadow' in doc.documentElement.style;
}
},
{
name: 'CSS3NegationSelector',
fn: function(doc) {
try {
doc.querySelectorAll("foo:not(bar)");
} catch (e) {
return false;
}
return true;
}
},
{
/**
* @property ClassList `true` if document environment supports the HTML5
* classList API.
*
* This property is available at application boot time, before document ready.
* @type {Boolean}
*/
name: 'ClassList',
fn: function(doc) {
return !!doc.documentElement.classList;
}
},
{
/**
* @property Canvas `true` if the device supports Canvas.
*
* This property is available at application boot time, before document ready.
* @type {Boolean}
*/
name: 'Canvas',
fn: function() {
var element = this.getTestElement('canvas');
return !!(element && element.getContext && element.getContext('2d'));
}
},
{
/**
* @property Svg `true` if the device supports SVG.
*
* This property is available at application boot time, before document ready.
* @type {Boolean}
*/
name: 'Svg',
fn: function(doc) {
return !!(doc.createElementNS && !!doc.createElementNS("http:/" + "/www.w3.org/2000/svg", "svg").createSVGRect);
}
},
{
/**
* @property Vml `true` if the device supports VML.
* @type {Boolean}
*
* This property is available at application boot time, before document ready.
*/
name: 'Vml',
fn: function() {
var element = this.getTestElement(),
ret = false;
element.innerHTML = "<!--[if vml]><br><![endif]-->";
ret = (element.childNodes.length === 1);
element.innerHTML = "";
return ret;
}
},
{
/**
* @property Touch `true` if the browser supports touch input.
*
* This property is available at application boot time, before document ready.
* @type {Boolean}
*/
name: 'Touch',
fn: function() {
// IE10 uses a vendor-prefixed maxTouchPoints property
var maxTouchPoints = navigator.msMaxTouchPoints || navigator.maxTouchPoints;
// if the browser has touch events we can be reasonably sure the device has
// a touch screen
// browsers that use pointer event have maxTouchPoints > 1 if the
// device supports touch input
// Chrome Desktop < 39 reports maxTouchPoints === 1 even if there is no
// touch support on the device
// http://www.w3.org/TR/pointerevents/#widl-Navigator-maxTouchPoints
// Chrome Desktop > 39 properly reports maxTouchPoints === 0 and
// Chrome Desktop Device Emulation mode reports maxTouchPoints === 1
if (Ext.browser.is.Chrome && Ext.browser.version.isLessThanOrEqual(39)) {
return (Ext.supports.TouchEvents && maxTouchPoints !== 1) || maxTouchPoints > 1;
} else {
return Ext.supports.TouchEvents || maxTouchPoints > 0;
}
}
},
{
/**
* @property TouchEvents `true` if the device supports touch events (`touchstart`,
* `touchmove`, `touchend`).
*
* This property is available at application boot time, before document ready.
* @type {Boolean}
*/
name: 'TouchEvents',
fn: function() {
return this.isEventSupported('touchend');
}
},
{
name: 'PointerEvents',
fn: function() {
return navigator.pointerEnabled;
}
},
{
name: 'MSPointerEvents',
fn: function() {
return navigator.msPointerEnabled;
}
},
{
/**
* @property Orientation `true` if the device supports different orientations.
* @type {Boolean}
*
* This property is available at application boot time, before document ready.
*/
name: 'Orientation',
fn: function() {
return ('orientation' in window) && this.isEventSupported('orientationchange');
}
},
{
/**
* @property OrientationChange `true` if the device supports the `orientationchange`
* event.
*
* This property is available at application boot time, before document ready.
* @type {Boolean}
*/
name: 'OrientationChange',
fn: function() {
return this.isEventSupported('orientationchange');
}
},
{
/**
* @property DeviceMotion `true` if the device supports device motion (acceleration
* and rotation rate).
*
* This property is available at application boot time, before document ready.
* @type {Boolean}
*/
name: 'DeviceMotion',
fn: function() {
return this.isEventSupported('devicemotion');
}
},
{
/**
* @property Geolocation `true` if the device supports GeoLocation.
* @type {Boolean}
*
* This property is available at application boot time, before document ready.
*/
/**
* @property GeoLocation `true` if the device supports Geo-location.
* @type {Boolean}
* @deprecated Use `Geolocation` instead (notice the lower-casing of 'L').
*/
names: [
'Geolocation',
'GeoLocation'
],
fn: function() {
return 'geolocation' in window.navigator;
}
},
{
name: 'SqlDatabase',
fn: function() {
return 'openDatabase' in window;
}
},
{
name: 'WebSockets',
fn: function() {
return 'WebSocket' in window;
}
},
{
/**
* @property Range `true` if browser support document.createRange native method.
* See https://developer.mozilla.org/en/DOM/range.
*
* This property is available at application boot time, before document ready.
* @type {Boolean}
*/
name: 'Range',
fn: function() {
return !!document.createRange;
}
},
{
/**
* @property CreateContextualFragment `true` if browser support CreateContextualFragment
* range native methods.
* See https://developer.mozilla.org/en/DOM/range.createContextualFragment
*
* This property is available at application boot time, before document ready.
* @type {Boolean}
*/
name: 'CreateContextualFragment',
fn: function() {
var range = !!document.createRange ? document.createRange() : false;
return range && !!range.createContextualFragment;
}
},
{
/**
* @property History `true` if the device supports HTML5 history. See
* https://developer.mozilla.org/en/DOM/Manipulating_the_browser_history
*
* This property is available at application boot time, before document ready.
* @type {Boolean}
*/
name: 'History',
fn: function() {
return ('history' in window && 'pushState' in window.history);
}
},
{
/**
* @property Css3DTransforms `true` if the device supports CSS3DTransform.
* @type {Boolean}
*
* This property is available at application boot time, before document ready.
*/
name: 'Css3dTransforms',
fn: function() {
// See https://sencha.jira.com/browse/TOUCH-1544
return this.has('CssTransforms') && this.isStyleSupported('perspective');
}
},
// TODO - double check vs Ext JS flavor:
//return (typeof WebKitCSSMatrix != 'undefined' && new WebKitCSSMatrix().hasOwnProperty('m41'));
{
// Important that this goes after Css3dTransforms, since tests are run in reverse order
name: 'CssTransforms',
fn: function() {
return this.isStyleSupported('transform');
}
},
{
name: 'CssTransformNoPrefix',
fn: function() {
return this.isStyleSupportedWithoutPrefix('transform');
}
},
{
name: 'CssAnimations',
fn: function() {
return this.isStyleSupported('animationName');
}
},
{
/**
* @property Transitions `true` if the device supports CSS3 Transitions.
*
* This property is available at application boot time, before document ready.
* @type {Boolean}
*/
names: [
'CssTransitions',
'Transitions'
],
fn: function() {
return this.isStyleSupported('transitionProperty');
}
},
{
/**
* @property Audio `true` if the device supports the HTML5 `audio` tag.
*
* This property is available at application boot time, before document ready.
* @type {Boolean}
*/
/**
* @property AudioTag `true` if the device supports the HTML5 `audio` tag.
* @type {Boolean}
* @deprecated Use `Audio` instead.
*/
names: [
'Audio',
'AudioTag'
],
fn: function() {
return !!this.getTestElement('audio').canPlayType;
}
},
{
/**
* @property Video `true` if the device supports the HTML5 `video` tag.
*
* This property is available at application boot time, before document ready.
* @type {Boolean}
*/
name: 'Video',
fn: function() {
return !!this.getTestElement('video').canPlayType;
}
},
{
/**
* @property LocalStorage `true` if localStorage is supported.
*
* This property is available at application boot time, before document ready.
* @type {Boolean}
*/
name: 'LocalStorage',
fn: function() {
try {
// IE10/Win8 throws "Access Denied" accessing window.localStorage, so
// this test needs to have a try/catch
if ('localStorage' in window && window['localStorage'] !== null) {
// jshint ignore:line
//this should throw an error in private browsing mode in iOS as well
localStorage.setItem('sencha-localstorage-test', 'test success');
//clean up if setItem worked
localStorage.removeItem('sencha-localstorage-test');
return true;
}
} catch (e) {}
// ignore
return false;
}
},
{
/**
* @property XHR2 `true` if the browser supports XMLHttpRequest
*
* This property is available at application boot time, before document ready.
* @type {Boolean}
*/
name: 'XHR2',
fn: function() {
return window.ProgressEvent && window.FormData && window.XMLHttpRequest && ('withCredentials' in new XMLHttpRequest());
}
},
{
/**
* @property XHRUploadProgress `true` if the browser supports XMLHttpRequest
* upload progress info
*
* This property is available at application boot time, before document ready.
* @type {Boolean}
*/
name: 'XHRUploadProgress',
fn: function() {
if (window.XMLHttpRequest && !Ext.browser.is.AndroidStock) {
var xhr = new XMLHttpRequest();
return xhr && ('upload' in xhr) && ('onprogress' in xhr.upload);
}
return false;
}
},
{
/**
* @property NumericInputPlaceHolder `true` if the browser supports placeholders
* on numeric input fields
*
* This property is available at application boot time, before document ready.
* @type {Boolean}
*/
name: 'NumericInputPlaceHolder',
fn: function() {
return !(Ext.browser.is.AndroidStock4 && Ext.os.version.getMinor() < 2);
}
},
/**
* @property {String} matchesSelector
* The method name which matches an element against a selector if implemented in this environment.
*
* This property is available at application boot time, before document ready.
*/
{
name: 'matchesSelector',
fn: function() {
var el = document.documentElement,
w3 = 'matches',
wk = 'webkitMatchesSelector',
ms = 'msMatchesSelector',
mz = 'mozMatchesSelector';
return el[w3] ? w3 : el[wk] ? wk : el[ms] ? ms : el[mz] ? mz : null;
}
},
/**
* @property RightMargin `true` if the device supports right margin.
* See https://bugs.webkit.org/show_bug.cgi?id=13343 for why this is needed.
*
* This property is *NOT* available at application boot time. Only after the document ready event.
* @type {Boolean}
*/
{
name: 'RightMargin',
ready: true,
fn: function(doc, div) {
var view = doc.defaultView;
return !(view && view.getComputedStyle(div.firstChild.firstChild, null).marginRight !== '0px');
}
},
/**
* @property DisplayChangeInputSelectionBug `true` if INPUT elements lose their
* selection when their display style is changed. Essentially, if a text input
* has focus and its display style is changed, the I-beam disappears.
*
* This bug is encountered due to the work around in place for the {@link #RightMargin}
* bug. This has been observed in Safari 4.0.4 and older, and appears to be fixed
* in Safari 5. It's not clear if Safari 4.1 has the bug, but it has the same WebKit
* version number as Safari 5 (according to http://unixpapa.com/js/gecko.html).
*
* This property is available at application boot time, before document ready.
*/
{
name: 'DisplayChangeInputSelectionBug',
fn: function() {
var webKitVersion = Ext.webKitVersion;
// WebKit but older than Safari 5 or Chrome 6:
return 0 < webKitVersion && webKitVersion < 533;
}
},
/**
* @property DisplayChangeTextAreaSelectionBug `true` if TEXTAREA elements lose their
* selection when their display style is changed. Essentially, if a text area has
* focus and its display style is changed, the I-beam disappears.
*
* This bug is encountered due to the work around in place for the {@link #RightMargin}
* bug. This has been observed in Chrome 10 and Safari 5 and older, and appears to
* be fixed in Chrome 11.
*
* This property is available at application boot time, before document ready.
*/
{
name: 'DisplayChangeTextAreaSelectionBug',
fn: function() {
var webKitVersion = Ext.webKitVersion;
/*
Has bug w/textarea:
(Chrome) Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-US)
AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.127
Safari/534.16
(Safari) Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-us)
AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5
Safari/533.21.1
No bug:
(Chrome) Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_7)
AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.57
Safari/534.24
*/
return 0 < webKitVersion && webKitVersion < 534.24;
}
},
/**
* @property TransparentColor `true` if the device supports transparent color.
* @type {Boolean}
*
* This property is *NOT* available at application boot time. Only after the document ready event.
*/
{
name: 'TransparentColor',
ready: true,
fn: function(doc, div, view) {
view = doc.defaultView;
return !(view && view.getComputedStyle(div.lastChild, null).backgroundColor !== 'transparent');
}
},
/**
* @property ComputedStyle `true` if the browser supports document.defaultView.getComputedStyle().
* @type {Boolean}
*
* This property is *NOT* available at application boot time. Only after the document ready event.
*/
{
name: 'ComputedStyle',
ready: true,
fn: function(doc, div, view) {
view = doc.defaultView;
return view && view.getComputedStyle;
}
},
/**
* @property Float `true` if the device supports CSS float.
* @type {Boolean}
*
* This property is available at application boot time, before document ready.
*/
{
name: 'Float',
fn: function(doc) {
return 'cssFloat' in doc.documentElement.style;
}
},
/**
* @property CSS3BorderRadius `true` if the device supports CSS3 border radius.
* @type {Boolean}
*
* This property is *NOT* available at application boot time. Only after the document ready event.
*/
{
name: 'CSS3BorderRadius',
ready: true,
fn: function(doc) {
var domPrefixes = [
'borderRadius',
'BorderRadius',
'MozBorderRadius',
'WebkitBorderRadius',
'OBorderRadius',
'KhtmlBorderRadius'
],
pass = false,
i;
for (i = 0; i < domPrefixes.length; i++) {
if (doc.documentElement.style[domPrefixes[i]] !== undefined) {
pass = true;
}
}
return pass && !Ext.isIE9;
}
},
/**
* @property CSS3LinearGradient `true` if the device supports CSS3 linear gradients.
* @type {Boolean}
*
* This property is available at application boot time, before document ready.
*/
{
name: 'CSS3LinearGradient',
fn: function(doc, div) {
var property = 'background-image:',
webkit = '-webkit-gradient(linear, left top, right bottom, from(black), to(white))',
w3c = 'linear-gradient(left top, black, white)',
moz = '-moz-' + w3c,
ms = '-ms-' + w3c,
opera = '-o-' + w3c,
options = [
property + webkit,
property + w3c,
property + moz,
property + ms,
property + opera
];
div.style.cssText = options.join(';');
return (("" + div.style.backgroundImage).indexOf('gradient') !== -1) && !Ext.isIE9;
}
},
/**
* @property MouseEnterLeave `true` if the browser supports mouseenter and mouseleave events
* @type {Boolean}
*
* This property is available at application boot time, before document ready.
*/
{
name: 'MouseEnterLeave',
fn: function(doc) {
return ('onmouseenter' in doc.documentElement && 'onmouseleave' in doc.documentElement);
}
},
/**
* @property MouseWheel `true` if the browser supports the mousewheel event
* @type {Boolean}
*
* This property is available at application boot time, before document ready.
*/
{
name: 'MouseWheel',
fn: function(doc) {
return ('onmousewheel' in doc.documentElement);
}
},
/**
* @property Opacity `true` if the browser supports normal css opacity
* @type {Boolean}
*
* This property is available at application boot time, before document ready.
*/
{
name: 'Opacity',
fn: function(doc, div) {
// Not a strict equal comparison in case opacity can be converted to a number.
if (Ext.isIE8) {
return false;
}
div.firstChild.style.cssText = 'opacity:0.73';
return div.firstChild.style.opacity == '0.73';
}
},
// jshint ignore:line
/**
* @property Placeholder `true` if the browser supports the HTML5 placeholder attribute on inputs
* @type {Boolean}
*
* This property is available at application boot time, before document ready.
*/
{
name: 'Placeholder',
fn: function(doc) {
return 'placeholder' in doc.createElement('input');
}
},
/**
* @property Direct2DBug `true` if when asking for an element's dimension via offsetWidth or offsetHeight,
* getBoundingClientRect, etc. the browser returns the subpixel width rounded to the nearest pixel.
*
* This property is available at application boot time, before document ready.
* @type {Boolean}
*/
{
name: 'Direct2DBug',
fn: function(doc) {
return Ext.isString(doc.documentElement.style.msTransformOrigin) && Ext.isIE9m;
}
},
/**
* @property BoundingClientRect `true` if the browser supports the getBoundingClientRect method on elements
* @type {Boolean}
*
* This property is available at application boot time, before document ready.
*/
{
name: 'BoundingClientRect',
fn: function(doc) {
return 'getBoundingClientRect' in doc.documentElement;
}
},
/**
* @property RotatedBoundingClientRect `true` if the BoundingClientRect is
* rotated when the element is rotated using a CSS transform.
* @type {Boolean}
*
* This property is *NOT* available at application boot time. Only after the document ready event.
*/
{
name: 'RotatedBoundingClientRect',
ready: true,
fn: function(doc) {
var body = doc.body,
supports = false,
el = doc.createElement('div'),
style = el.style;
if (el.getBoundingClientRect) {
// If the document body already has child nodes (text nodes etc) we can end
// up with subpixel rounding errors in IE11 when measuring the height.
// Absolute positioning prevents this.
style.position = 'absolute';
style.top = "0";
style.WebkitTransform = style.MozTransform = style.msTransform = style.OTransform = style.transform = 'rotate(90deg)';
style.width = '100px';
style.height = '30px';
body.appendChild(el);
supports = el.getBoundingClientRect().height !== 100;
body.removeChild(el);
}
return supports;
}
},
/**
* @property ChildContentClearedWhenSettingInnerHTML `true` if created child elements
* lose their innerHTML when modifying the innerHTML of the parent element.
* @type {Boolean}
*
* This property is *NOT* available at application boot time. Only after the document ready event.
*/
{
name: 'ChildContentClearedWhenSettingInnerHTML',
ready: true,
fn: function() {
var el = this.getTestElement(),
child;
el.innerHTML = '<div>a</div>';
child = el.firstChild;
el.innerHTML = '<div>b</div>';
return child.innerHTML !== 'a';
}
},
{
name: 'IncludePaddingInWidthCalculation',
ready: true,
fn: function(doc, div) {
return div.childNodes[1].firstChild.offsetWidth === 210;
}
},
{
name: 'IncludePaddingInHeightCalculation',
ready: true,
fn: function(doc, div) {
return div.childNodes[1].firstChild.offsetHeight === 210;
}
},
/**
* @property TextAreaMaxLength `true` if the browser supports maxlength on textareas.
* @type {Boolean}
*
* This property is available at application boot time, before document ready.
*/
{
name: 'TextAreaMaxLength',
fn: function(doc) {
return ('maxlength' in doc.createElement('textarea'));
}
},
/**
* @property GetPositionPercentage `true` if the browser will return the left/top/right/bottom
* position as a percentage when explicitly set as a percentage value.
*
* This property is *NOT* available at application boot time. Only after the document ready event.
* @type {Boolean}
*/
// Related bug: https://bugzilla.mozilla.org/show_bug.cgi?id=707691#c7
{
name: 'GetPositionPercentage',
ready: true,
fn: function(doc, div) {
return Ext.feature.getStyle(div.childNodes[2], 'left') === '10%';
}
},
/**
* @property {Boolean} PercentageHeightOverflowBug
* In some browsers (IE quirks, IE6, IE7, IE9, chrome, safari and opera at the time
* of this writing) a percentage-height element ignores the horizontal scrollbar
* of its parent element. This method returns true if the browser is affected
* by this bug.
*
* This property is *NOT* available at application boot time. Only after the document ready event.
* @private
*/
{
name: 'PercentageHeightOverflowBug',
ready: true,
fn: function(doc) {
var hasBug = false,
style, el;
if (Ext.getScrollbarSize().height) {
// must have space-consuming scrollbars for bug to be possible
el = this.getTestElement();
style = el.style;
style.height = '50px';
style.width = '50px';
style.overflow = 'auto';
style.position = 'absolute';
el.innerHTML = [
'<div style="display:table;height:100%;">',
// The element that causes the horizontal overflow must be
// a child of the element with the 100% height, otherwise
// horizontal overflow is not triggered in webkit quirks mode
'<div style="width:51px;"></div>',
'</div>'
].join('');
doc.body.appendChild(el);
if (el.firstChild.offsetHeight === 50) {
hasBug = true;
}
doc.body.removeChild(el);
}
return hasBug;
}
},
/**
* @property {Boolean} xOriginBug
* In Chrome 24.0, an RTL element which has vertical overflow positions its right X origin incorrectly.
* It skips a non-existent scrollbar which has been moved to the left edge due to the RTL setting.
*
* http://code.google.com/p/chromium/issues/detail?id=174656
*
* This method returns true if the browser is affected by this bug.
*
* This property is *NOT* available at application boot time. Only after the document ready event.
* @private
*/
{
name: 'xOriginBug',
ready: true,
fn: function(doc, div) {
div.innerHTML = '<div id="b1" style="height:100px;width:100px;direction:rtl;position:relative;overflow:scroll">' + '<div id="b2" style="position:relative;width:100%;height:20px;"></div>' + '<div id="b3" style="position:absolute;width:20px;height:20px;top:0px;right:0px"></div>' + '</div>';
var outerBox = document.getElementById('b1').getBoundingClientRect(),
b2 = document.getElementById('b2').getBoundingClientRect(),
b3 = document.getElementById('b3').getBoundingClientRect();
return (b2.left !== outerBox.left && b3.right !== outerBox.right);
}
},
/**
* @property {Boolean} ScrollWidthInlinePaddingBug
* In some browsers the right padding of an overflowing element is not accounted
* for in its scrollWidth. The result can vary depending on whether or not
* The element contains block-level children. This method tests the effect
* of padding on scrollWidth when there are no block-level children inside the
* overflowing element.
*
* This method returns true if the browser is affected by this bug.
*
* This property is *NOT* available at application boot time. Only after the document ready event.
*/
{
name: 'ScrollWidthInlinePaddingBug',
ready: true,
fn: function(doc) {
var hasBug = false,
style, el;
el = doc.createElement('div');
style = el.style;
style.height = '50px';
style.width = '50px';
style.padding = '10px';
style.overflow = 'hidden';
style.position = 'absolute';
el.innerHTML = '<span style="display:inline-block;zoom:1;height:60px;width:60px;"></span>';
doc.body.appendChild(el);
if (el.scrollWidth === 70) {
hasBug = true;
}
doc.body.removeChild(el);
return hasBug;
}
},
/**
* @property {Boolean} rtlVertScrollbarOnRight
* Safari, in RTL mode keeps the scrollbar at the right side.
* This means that when two elements must keep their left/right positions synched, if one has no vert
* scrollbar, it must have some extra padding.
* See https://sencha.jira.com/browse/EXTJSIV-11245
*
* This property is *NOT* available at application boot time. Only after the document ready event.
* @private
*/
{
name: 'rtlVertScrollbarOnRight',
ready: true,
fn: function(doc, div) {
div.innerHTML = '<div style="height:100px;width:100px;direction:rtl;overflow:scroll">' + '<div style="width:20px;height:200px;"></div>' + '</div>';
var outerBox = div.firstChild,
innerBox = outerBox.firstChild;
return (innerBox.offsetLeft + innerBox.offsetWidth !== outerBox.offsetLeft + outerBox.offsetWidth);
}
},
/**
* @property {Boolean} rtlVertScrollbarOverflowBug
* In Chrome, in RTL mode, horizontal overflow only into the vertical scrollbar does NOT trigger horizontal scrollability.
* See https://code.google.com/p/chromium/issues/detail?id=179332
* We need to detect this for when a grid header needs to have exactly the same horizontal scrolling range as its table view.
* See {@link Ext.grid.ColumnLayout#publishInnerCtSize}
* TODO: Remove this when all supported Chrome versions are fixed.
*
* This property is *NOT* available at application boot time. Only after the document ready event.
* @private
*/
{
name: 'rtlVertScrollbarOverflowBug',
ready: true,
fn: function(doc, div) {
div.innerHTML = '<div style="height:100px;width:100px;direction:rtl;overflow:auto">' + '<div style="width:95px;height:200px;"></div>' + '</div>';
// If the bug is present, the 95 pixel wide inner div, encroaches into the
// vertical scrollbar, but does NOT trigger horizontal overflow, so the clientHeight remains
// equal to the offset height.
var outerBox = div.firstChild;
return outerBox.clientHeight === outerBox.offsetHeight;
}
},
{
identity: 'defineProperty',
fn: function() {
if (Ext.isIE8m) {
Ext.Object.defineProperty = Ext.emptyFn;
return false;
}
return true;
}
},
{
identify: 'nativeXhr',
fn: function() {
if (typeof XMLHttpRequest !== 'undefined') {
return true;
}
// Apply a polyfill:
XMLHttpRequest = function() {
// jshint ignore:line
try {
return new ActiveXObject('MSXML2.XMLHTTP.3.0');
} // jshint ignore:line
catch (ex) {
return null;
}
};
return false;
}
},
/**
* @property {Boolean} SpecialKeyDownRepeat
* True if the browser fires the keydown event on specialkey autorepeat
*
* note 1: IE fires ONLY the keydown event on specialkey autorepeat
* note 2: Safari < 3.1, Gecko (Mac/Linux) & Opera fire only the keypress event on
* specialkey autorepeat (research done by Jan Wolter at
* http://unixpapa.com/js/key.html)
* note 3: Opera 12 behaves like other modern browsers so this workaround does not
* work anymore
*
* This property is available at application boot time, before document ready.
*/
{
name: 'SpecialKeyDownRepeat',
fn: function() {
return Ext.isWebKit ? parseInt(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1], 10) >= 525 : !(!(Ext.isGecko || Ext.isIE) || (Ext.isOpera && Ext.operaVersion < 12));
}
},
/**
* @property {Boolean} EmulatedMouseOver
* True if the browser emulates a mouseover event on tap (mobile safari)
*
* This property is available at application boot time, before document ready.
*/
{
name: 'EmulatedMouseOver',
fn: function() {
// TODO: is it possible to feature detect this?
return Ext.os.is.iOS;
}
},
/**
* @property Hashchange True if the user agent supports the hashchange event
*
* This property is available at application boot time, before document ready.
* @type {Boolean}
*/
{
// support Vector 12
name: 'Hashchange',
fn: function() {
// Note that IE8 in IE7 compatibility mode reports true for 'onhashchange' in window, so also test documentMode
var docMode = document.documentMode;
return 'onhashchange' in window && (docMode === undefined || docMode > 7);
}
},
/**
* @property FixedTableWidthBug
* @private
* @type {Boolean}
* `true` if the browser has this bug: https://bugs.webkit.org/show_bug.cgi?id=130239
*
* This property is *NOT* available at application boot time. Only after the document ready event.
*/
{
name: 'FixedTableWidthBug',
ready: true,
fn: function() {
if (Ext.isIE8) {
// IE8 incorrectly detects that we have this bug.
return false;
}
var outer = document.createElement('div'),
inner = document.createElement('div'),
width;
outer.setAttribute('style', 'display:table;table-layout:fixed;');
inner.setAttribute('style', 'display:table-cell;min-width:50px;');
outer.appendChild(inner);
document.body.appendChild(outer);
// must poke offsetWidth to trigger a reflow before setting width
outer.offsetWidth;
// jshint ignore:line
outer.style.width = '25px';
width = outer.offsetWidth;
document.body.removeChild(outer);
return width === 50;
}
},
/**
* @property FocusinFocusoutEvents
* @private
* @type {Boolean}
* `true` if the browser supports focusin and focusout events:
* https://developer.mozilla.org/en-US/docs/Web/Events/focusin
* At this point, only Firefox does not, see this bug:
* https://bugzilla.mozilla.org/show_bug.cgi?id=687787
*
* This property is available at application boot time, before document ready.
*/
{
name: 'FocusinFocusoutEvents',
fn: function() {
// There is no reliable way to feature detect focusin/focusout event support.
// window.onfocusin will return undefined both in Chrome (where it is supported)
// and in Firefox (where it is not supported); adding an element and trying to
// focus it will fail when the browser window itself is not focused.
return !Ext.isGecko;
}
},
/**
* @property {Boolean} AsyncFocusEvents
* `true` if the browser fires focus events (focus, blur, focusin, focusout)
* asynchronously, i.e. in a separate event loop invocation. This is only true
* for all versions Internet Explorer; Microsoft Edge and other browsers fire
* focus events synchronously.
*/
{
name: 'AsyncFocusEvents',
fn: function() {
// The sad part is that we can't feature detect this because the focus
// event won't be fired when the browser window itself is not focused.
// Private shortcut for brevity
return Ext.asyncFocus = !!Ext.isIE;
}
},
/**
* @property {Boolean} HighContrastMode `true` if the browser is currently
* running in Windows High Contrast accessibility mode.
*
* @property {Object} accessibility Accessibility features.
*
* @property {Boolean} accessibility.Images `true` if the browser is configured
* to display images.
*
* @property {Boolean} accessibility.BackgroundImages `true` if the browser
* is configured to display background images.
*
* @property {Boolean} accessibility.BorderColors `true` if the browser
* is configured to honor CSS styling for border colors.
*
* @property {Boolean} accessibility.LightOnDark `true` if the browser
* is currently using reverse colors in light-on-dark accessibility mode.
*/
{
name: 'accessibility',
ready: true,
fn: function(doc) {
var body = doc.body,
div, img, style, supports, bgImg;
function getColor(colorTxt) {
var values = [],
colorValue = 0,
regex, match;
if (colorTxt.indexOf('rgb(') !== -1) {
values = colorTxt.replace('rgb(', '').replace(')', '').split(', ');
} else if (colorTxt.indexOf('#') !== -1) {
regex = colorTxt.length === 7 ? /^#(\S\S)(\S\S)(\S\S)$/ : /^#(\S)(\S)(\S)$/;
match = colorTxt.match(regex);
if (match) {
values = [
'0x' + match[1],
'0x' + match[2],
'0x' + match[3]
];
}
}
for (var i = 0; i < values.length; i++) {
colorValue += parseInt(values[i]);
}
return colorValue;
}
div = doc.createElement('div');
img = doc.createElement('img');
style = div.style;
Ext.apply(style, {
width: '2px',
position: 'absolute',
clip: 'rect(1px,1px,1px,1px)',
borderWidth: '1px',
borderStyle: 'solid',
borderTopTolor: '#f00',
borderRightColor: '#ff0',
backgroundColor: '#fff',
backgroundImage: 'url(' + Ext.BLANK_IMAGE_URL + ')'
});
img.alt = '';
img.src = Ext.BLANK_IMAGE_URL;
div.appendChild(img);
body.appendChild(div);
// Now check if the styles were indeed honored
style = div.currentStyle || div.style;
bgImg = style.backgroundImage;
supports = {
// In IE it is possible to untick "Show pictures" option in Advanced
// settings; this will result in img element reporting its readyState
// as 'uninitialized'.
// See http://www.paciellogroup.com/blog/2011/10/detecting-if-images-are-disabled-in-browsers/
Images: img.offsetWidth === 1 && img.readyState !== 'uninitialized',
BackgroundImages: !(bgImg !== null && (bgImg === "none" || bgImg === "url(invalid-url:)")),
BorderColors: style.borderTopColor !== style.borderRightColor,
LightOnDark: getColor(style.color) - getColor(style.backgroundColor) > 0
};
Ext.supports.HighContrastMode = !supports.BackgroundImages;
body.removeChild(div);
div = img = null;
return supports;
}
},
{
/**
* @property ViewportUnits `true` if the device supports ViewportUnits.
* @type {Boolean}
*
*/
name: 'ViewportUnits',
ready: true,
fn: function(doc) {
// Even attempting to detect the feature throws a fatal error on IE8
if (Ext.isIE8) {
return false;
}
var body = doc.body,
div = document.createElement('div'),
style = div.currentStyle || div.style,
width, divWidth;
body.appendChild(div);
Ext.apply(style, {
width: '50vw'
});
width = parseInt(window.innerWidth / 2, 10);
divWidth = parseInt((window.getComputedStyle ? getComputedStyle(div, null) : div.currentStyle).width, 10);
body.removeChild(div);
div = null;
return width === divWidth;
}
},
{
name: 'CSSVariables',
ready: false,
fn: function(doc) {
// Legacy browsers do not have this method.
if (!window.getComputedStyle) {
return false;
}
var style = window.getComputedStyle(doc.documentElement);
return style.getPropertyValue && !!style.getPropertyValue('--x-supports-variables');
}
},
{
/**
* @property Selectors2 `true` if the browser supports the CSS selector API level 2.
* https://dev.w3.org/2006/webapi/selectors-api2/
* @type {Boolean}
*
*/
name: 'Selectors2',
ready: false,
fn: function(doc) {
try {
return !!doc.querySelectorAll(':scope');
} catch (e) {
return false;
}
}
},
{
/**
* @property CSSScrollSnap
* @private
* @type {Boolean}
*/
name: 'CSSScrollSnap',
ready: false,
fn: function(doc) {
var style = doc.documentElement.style;
return 'scrollSnapType' in style || 'webkitScrollSnapType' in style || 'msScrollSnapType' in style;
}
},
0
]
};
// placeholder so legacy browser detectors can come/go cleanly
Ext.feature.tests.pop();
// remove the placeholder
Ext.supports = {};
Ext.feature.detect();
/**
* This class manages ready detection and handling. Direct use of this class is not
* recommended. Instead use `Ext.onReady`:
*
* Ext.onReady(function () {
* // DOM and Framework are ready...
* });
*
* ## DOM Ready
*
* The lowest-level of readiness is DOM readiness. This level implies only that the document
* body exists. Many things require the DOM to be ready for manipulation. If that is all
* that is required, the `Ext.onDocumentReady` method can be called to register a callback
* to be called as soon as the DOM is ready:
*
* Ext.onDocumentReady(function () {
* // the document body is ready
* });
*
* ## Framework Ready
*
* In production builds of applications it is common to have all of the code loaded before
* DOM ready, so the need to wait for "onReady" is often confused with only that concern.
* This is easy to understand, at least in part because historically `Ext.onReady` only
* waited for DOM ready.
*
* With the introduction of `Ext.Loader`, however, it became common for DOM ready to occur
* in the middle of dynamically loading code. If application code were executed at that
* time, any use of the yet-to-be-loaded classes would throw errors. As a consequence of
* this, the `Ext.onReady` mechanism was extended to wait for both DOM ready *and* all of
* the required classes to be loaded.
*
* When the framework enters or leaves a state where it is not ready (for example, the
* first dynamic load is requested or last load completes), `Ext.env.Ready` is informed.
* For example:
*
* Ext.env.Ready.block();
*
* //...
*
* Ext.env.Ready.unblock();
*
* When there are no blocks and the DOM is ready, the Framework is ready and the "onReady"
* callbacks are called.
*
* Priority can be used to control the ordering of onReady listeners, for example:
*
* Ext.onReady(function() {
*
* }, null, {
* priority: 100
* });
*
* Ready listeners with higher priorities will run sooner than those with lower priorities,
* the default priority being `0`. Internally the framework reserves priorities of 1000
* or greater, and -1000 or lesser for onReady handlers that must run before or after
* any application code. Applications should stick to using priorities in the -999 - 999
* range. The following priorities are currently in use by the framework:
*
* - Element_scroll rtl override: `1001`
* - Event system initialization: `2000`
* - Ext.dom.Element: `1500`
*
* @class Ext.env.Ready
* @singleton
* @private
* @since 5.0.0
*/
Ext.env.Ready = {
// @define Ext.env.Ready
// @require Ext.env.Browser
// @require Ext.env.OS
// @require Ext.env.Feature
/**
* @property {Number} blocks The number of Framework readiness blocks.
* @private
*/
blocks: (location.search || '').indexOf('ext-pauseReadyFire') > 0 ? 1 : 0,
/**
* @property {Number} bound This property stores the state of event listeners bound
* to the document or window to detect ready state.
* @private
*/
bound: 0,
/**
* @property {Number} [delay=1]
* This allows the DOM listener thread to complete (usually desirable with mobWebkit,
* Gecko) before firing the entire onReady chain (high stack load on Loader). For mobile
* devices when running from Home Screen, the splash screen will not disappear until
* all external resource requests finish. This delay clears the splash screen.
* @private
*/
delay: 1,
/**
* @property {Event[]} events An array of events that have triggered ready state. This
* is for diagnostic purposes only and is only available in debug builds.
* An array
* @private
*/
events: [],
/**
* @property {Boolean} firing This property is `true` when we currently calling the
* listeners.
* @private
*/
firing: false,
/**
* @property {Number} generation A counter of the number of mutations of `listeners`.
* @private
*/
generation: 0,
/**
* @property {Object[]} listeners The set of listeners waiting for ready.
* @private
*/
listeners: [],
/**
* @property {Number} nextId A counter so we can assign listeners an `id` to keep
* them in FIFO order.
* @private
*/
nextId: 0,
/**
* @property {Number} sortGeneration A captured value of `generation` that indicates
* when the `listeners` were last sorted.
* @private
*/
sortGeneration: 0,
/**
* @property {Number} state
* Holds the current ready state as managed by this class. The values possible are:
*
* * 0 - Not ready.
* * 1 - Ready detected but listeners are not yet notified.
* * 2 - Ready detected and listeners are notified. See also `firing`.
*
* @private
*/
state: 0,
/**
* @property {Object} timer The handle from `setTimeout` for the delayed notification
* of ready.
* @private
*/
timer: null,
/**
* Binds the appropriate browser event for checking if the DOM has loaded.
* @private
*/
bind: function() {
var me = Ext.env.Ready,
doc = document;
if (!me.bound) {
// Test scenario where load is dynamic AFTER window.load
if (doc.readyState === 'complete') {
// Firefox4+ got support for this state, others already do.
me.onReadyEvent({
type: doc.readyState || 'body'
});
} else {
me.bound = 1;
if (Ext.browser.is.PhoneGap && !Ext.os.is.Desktop) {
me.bound = 2;
doc.addEventListener('deviceready', me.onReadyEvent, false);
}
doc.addEventListener('DOMContentLoaded', me.onReadyEvent, false);
window.addEventListener('load', me.onReadyEvent, false);
}
}
},
block: function() {
++this.blocks;
Ext.isReady = false;
},
/**
* This method starts the process of firing the ready event. This may be delayed based
* on the `delay` property.
* @private
*/
fireReady: function() {
var me = Ext.env.Ready;
if (!me.state) {
Ext._readyTime = Ext.ticks();
Ext.isDomReady = true;
me.state = 1;
// As soon as we transition to domready, complete the feature detection:
Ext.feature.detect(true);
if (!me.delay) {
me.handleReady();
} else if (navigator.standalone) {
// When running from Home Screen, the splash screen will not disappear
// until all external resource requests finish.
// The first timeout clears the splash screen
// The second timeout allows inital HTML content to be displayed
me.timer = Ext.defer(function() {
me.timer = null;
me.handleReadySoon();
}, 1);
} else {
me.handleReadySoon();
}
}
},
/**
* This method iterates over the `listeners` and invokes them. This advances the
* `state` from 1 to 2 and ensure the proper subset of `listeners` are invoked.
* @private
*/
handleReady: function() {
var me = this;
if (me.state === 1) {
me.state = 2;
Ext._beforeReadyTime = Ext.ticks();
me.invokeAll();
Ext._afterReadyTime = Ext.ticks();
}
},
/**
* This method is called to schedule a call to `handleReady` using a `setTimeout`. It
* ensures that only one timer is pending.
* @param {Number} [delay] If passed, this overrides the `delay` property.
* @private
*/
handleReadySoon: function(delay) {
var me = this;
if (!me.timer) {
me.timer = Ext.defer(function() {
me.timer = null;
me.handleReady();
}, delay || me.delay);
}
},
/**
* This method invokes the given `listener` instance based on its options.
* @param {Object} listener
*/
invoke: function(listener) {
var delay = listener.delay;
if (delay) {
Ext.defer(listener.fn, delay, listener.scope);
} else {
if (Ext.elevateFunction) {
Ext.elevateFunction(listener.fn, listener.scope);
} else {
listener.fn.call(listener.scope);
}
}
},
/**
* Invokes as many listeners as are appropriate given the current state. This should
* only be called when DOM ready is achieved. The remaining business of `blocks` is
* handled here.
*/
invokeAll: function() {
if (Ext.elevateFunction) {
Ext.elevateFunction(this.doInvokeAll, this);
} else {
this.doInvokeAll();
}
},
doInvokeAll: function() {
var me = this,
listeners = me.listeners,
listener;
if (!me.blocks) {
// Since DOM is ready and we have no blocks, we mark the framework as ready.
Ext.isReady = true;
}
me.firing = true;
// NOTE: We cannot cache this length because each time through the loop a callback
// may have added new callbacks.
while (listeners.length) {
if (me.sortGeneration !== me.generation) {
me.sortGeneration = me.generation;
// This will happen just once on the first pass... if nothing is being
// added as we call the callbacks. This sorts the listeners such that the
// highest priority listener is at the *end* of the array ... so we can
// use pop (as opposed to shift) to extract it.
listeners.sort(me.sortFn);
}
listener = listeners.pop();
if (me.blocks && !listener.dom) {
// If we are blocked (i.e., only DOM ready) and this listener is not a
// DOM-ready listener we have reached the end of the line. The remaining
// listeners are Framework ready listeners.
listeners.push(listener);
break;
}
me.invoke(listener);
}
me.firing = false;
},
/**
* This method wraps the given listener pieces in a proper object for the `listeners`
* array and `invoke` methods.
* @param {Function} fn The method to call.
* @param {Object} [scope] The scope (`this` reference) in which the `fn` executes.
* Defaults to the browser window.
* @param {Object} [options] An object with extra options.
* @param {Number} [options.delay=0] A number of milliseconds to delay.
* @param {Number} [options.priority=0] Relative priority of this callback. A larger
* number will result in the callback being sorted before the others. Priorities
* 1000 or greater and -1000 or lesser are reserved for internal framework use only.
* @param {Boolean} [options.dom=false] Pass `true` to only wait for DOM ready, `false`
* means full Framework and DOM readiness.
* @return {Object} The listener instance.
* @private
*/
makeListener: function(fn, scope, options) {
var ret = {
fn: fn,
id: ++this.nextId,
// so sortFn can respect FIFO
scope: scope,
dom: false,
priority: 0
};
if (options) {
Ext.apply(ret, options);
}
ret.phase = ret.dom ? 0 : 1;
// to simplify the sortFn
return ret;
},
/**
* Adds a listener to be notified when the document is ready (before onload and before
* images are loaded).
*
* @param {Function} fn The method to call.
* @param {Object} [scope] The scope (`this` reference) in which the `fn` executes.
* Defaults to the browser window.
* @param {Object} [options] An object with extra options.
* @param {Number} [options.delay=0] A number of milliseconds to delay.
* @param {Number} [options.priority=0] Relative priority of this callback. A larger
* number will result in the callback being sorted before the others. Priorities
* 1000 or greater and -1000 or lesser are reserved for internal framework use only.
* @param {Boolean} [options.dom=false] Pass `true` to only wait for DOM ready, `false`
* means full Framework and DOM readiness.
* @private
*/
on: function(fn, scope, options) {
var me = Ext.env.Ready,
listener = me.makeListener(fn, scope, options);
if (me.state === 2 && !me.firing && (listener.dom || !me.blocks)) {
// If we are DOM ready (state === 2) and not currently in invokeAll (!firing)
// and this listener is ready to call (either a DOM ready listener or there
// are no blocks), then we need to invoke the listener now.
//
// Otherwise we can fall to the else block and push the listener. The eventual
// (or currently executing) call to handleReady or unblock will trigger its
// delivery in proper priority order.
me.invoke(listener);
} else {
me.listeners.push(listener);
++me.generation;
if (!me.bound) {
// If we have never bound then bind the ready event now. If we have unbound
// the event then me.bound == -1 and we don't want to rebind it as the DOM
// is ready.
me.bind();
}
}
},
/**
* This is a generic event handler method attached to all of the various events that
* may indicate ready state. The first call to this method indicates ready state has
* been achieved.
* @param {Event} [ev] The event instance.
* @private
*/
onReadyEvent: function(ev) {
var me = Ext.env.Ready;
if (Ext.elevateFunction) {
Ext.elevateFunction(me.doReadyEvent, me, arguments);
} else {
me.doReadyEvent(ev);
}
},
doReadyEvent: function(ev) {
var me = this;
if (ev && ev.type) {
me.events.push(ev);
}
if (me.bound > 0) {
me.unbind();
me.bound = -1;
}
// NOTE: *not* 0 or false - we never want to rebind!
if (!me.state) {
me.fireReady();
}
},
/**
* Sorts the `listeners` array by `phase` and `priority` such that the first listener
* to fire can be determined using `pop` on the `listeners` array.
* @private
*/
sortFn: function(a, b) {
return -((a.phase - b.phase) || (b.priority - a.priority) || (a.id - b.id));
},
unblock: function() {
var me = this;
if (me.blocks) {
if (!--me.blocks) {
if (me.state === 2 && !me.firing) {
// We have already finished handleReady (the DOM ready handler) so
// this trigger just needs to dispatch all the remaining listeners.
me.invokeAll();
}
}
}
},
// if we are currently firing then invokeAll will pick up the Framework
// ready listeners automatically.
//
// If me.state < 2 then we are waiting for DOM ready so it will eventually
// call handleReady and invokeAll when everything is ready.
/**
* This method is called to remove all event listeners that may have been set up to
* detect ready state.
* @private
*/
unbind: function() {
var me = this,
doc = document;
if (me.bound > 1) {
doc.removeEventListener('deviceready', me.onReadyEvent, false);
}
doc.removeEventListener('DOMContentLoaded', me.onReadyEvent, false);
window.removeEventListener('load', me.onReadyEvent, false);
}
};
(function() {
var Ready = Ext.env.Ready;
/*
* EXTJS-13522
* Although IE 9 has the DOMContentLoaded event available, usage of that causes
* timing issues when attempting to access document.namespaces (VmlCanvas.js).
* Consequently, even in IE 9 we need to use the legacy bind override for ready
* detection. This defers ready firing enough to allow access to the
* document.namespaces property.
*
* NOTE: this issue is very timing sensitive, and typically only displays itself
* when there is a large amount of latency between the browser and the server, and
* when testing against a built page (ext-all.js) and not a dev mode page.
*/
if (Ext.isIE9m) {
/* Customized implementation for Legacy IE. The default implementation is
* configured for use with all other 'standards compliant' agents.
* References: http://javascript.nwbox.com/IEContentLoaded/
* licensed courtesy of http://developer.yahoo.com/yui/license.html
*/
Ext.apply(Ready, {
/**
* Timer for doScroll polling
* @private
*/
scrollTimer: null,
/**
* @private
*/
readyStatesRe: /complete/i,
/**
* This strategy has minimal benefits for Sencha solutions that build
* themselves (ie. minimal initial page markup). However, progressively-enhanced
* pages (with image content and/or embedded frames) will benefit the most
* from it. Browser timer resolution is too poor to ensure a doScroll check
* more than once on a page loaded with minimal assets (the readystatechange
* event 'complete' usually beats the doScroll timer on a 'lightly-loaded'
* initial document).
* @private
*/
pollScroll: function() {
var scrollable = true;
try {
document.documentElement.doScroll('left');
} catch (e) {
scrollable = false;
}
// on IE8, when running within an iFrame, document.body is not immediately
// available
if (scrollable && document.body) {
Ready.onReadyEvent({
type: 'doScroll'
});
} else {
// Minimize thrashing --
// adjusted for setTimeout's close-to-minimums (not too low),
// as this method SHOULD always be called once initially
Ready.scrollTimer = Ext.defer(Ready.pollScroll, 20);
}
return scrollable;
},
bind: function() {
if (Ready.bound) {
return;
}
var doc = document,
topContext;
// See if we are in an IFRAME? (doScroll ineffective here)
try {
topContext = window.frameElement === undefined;
} catch (e) {}
// If we throw an exception, it means we're probably getting access
// denied, which means we're in an iframe cross domain.
if (!topContext || !doc.documentElement.doScroll) {
Ready.pollScroll = Ext.emptyFn;
}
//then noop this test altogether
else if (Ready.pollScroll()) {
// starts scroll polling if necessary
return;
}
if (doc.readyState === 'complete') {
// Loaded AFTER initial document write/load...
Ready.onReadyEvent({
type: 'already ' + (doc.readyState || 'body')
});
} else {
doc.attachEvent('onreadystatechange', Ready.onReadyStateChange);
window.attachEvent('onload', Ready.onReadyEvent);
Ready.bound = 1;
}
},
unbind: function() {
document.detachEvent('onreadystatechange', Ready.onReadyStateChange);
window.detachEvent('onload', Ready.onReadyEvent);
if (Ext.isNumber(Ready.scrollTimer)) {
clearTimeout(Ready.scrollTimer);
Ready.scrollTimer = null;
}
},
/**
* This event handler is called when the readyState changes.
* @private
*/
onReadyStateChange: function() {
var state = document.readyState;
if (Ready.readyStatesRe.test(state)) {
Ready.onReadyEvent({
type: state
});
}
}
});
}
/**
* @property {Boolean} isDomReady
* `true` when the document body is ready for use.
* @member Ext
* @readonly
*/
/**
* @property {Boolean} isReady
* `true` when `isDomReady` is true and the Framework is ready for use.
* @member Ext
* @readonly
*/
/**
* @method onDocumentReady
* @member Ext
* Adds a listener to be notified when the document is ready (before onload and before
* images are loaded).
*
* @param {Function} fn The method to call.
* @param {Object} [scope] The scope (`this` reference) in which the handler function
* executes. Defaults to the browser window.
* @param {Object} [options] An object with extra options.
* @param {Number} [options.delay=0] A number of milliseconds to delay.
* @param {Number} [options.priority=0] Relative priority of this callback. A larger
* number will result in the callback being sorted before the others. Priorities
* 1000 or greater and -1000 or lesser are reserved for internal framework use only.
* @private
*/
Ext.onDocumentReady = function(fn, scope, options) {
var opt = {
dom: true
};
if (options) {
Ext.apply(opt, options);
}
Ready.on(fn, scope, opt);
};
/**
* @method onReady
* @member Ext
* Adds a listener to be notified when the document is ready (before onload and before
* images are loaded).
*
* @param {Function} fn The method to call.
* @param {Object} [scope] The scope (`this` reference) in which the handler function
* executes. Defaults to the browser window.
* @param {Object} [options] An object with extra options.
* @param {Number} [options.delay=0] A number of milliseconds to delay.
* @param {Number} [options.priority=0] Relative priority of this callback. A larger
* number will result in the callback being sorted before the others. Priorities
* 1000 or greater and -1000 or lesser are reserved for internal framework use only.
* @param {Boolean} [options.dom=false] Pass `true` to only wait for DOM ready, `false`
* means full Framework and DOM readiness.
* numbers are reserved.
*/
Ext.onReady = function(fn, scope, options) {
Ready.on(fn, scope, options);
};
// A shortcut method for onReady with a high priority
Ext.onInternalReady = function(fn, scope, options) {
Ready.on(fn, scope, Ext.apply({
priority: 1000
}, options));
};
Ready.bind();
}());
// @tag class
/**
* This class provides dynamic loading support for JavaScript classes. Application code
* does not typically need to call `Ext.Loader` except perhaps to configure path mappings
* when not using [Sencha Cmd](http://www.sencha.com/products/sencha-cmd/).
*
* Ext.Loader.setPath('MyApp', 'app');
*
* When using Sencha Cmd, this is handled by the "bootstrap" provided by the application
* build script and such configuration is not necessary.
*
* # Typical Usage
*
* The `Ext.Loader` is most often used behind the scenes to satisfy class references in
* class declarations. Like so:
*
* Ext.define('MyApp.view.Main', {
* extend: 'Ext.panel.Panel',
*
* mixins: [
* 'MyApp.util.Mixin'
* ],
*
* requires: [
* 'Ext.grid.Panel'
* ],
*
* uses: [
* 'MyApp.util.Stuff'
* ]
* });
*
* In all of these cases, `Ext.Loader` is used internally to resolve these class names
* and ensure that the necessary class files are loaded.
*
* During development, these files are loaded individually for optimal debugging. For a
* production use, [Sencha Cmd](http://www.sencha.com/products/sencha-cmd/) will replace
* all of these strings with the actual resolved class references because it ensures that
* the classes are all contained in the build in the correct order. In development, these
* files will not be loaded until the `MyApp.view.Main` class indicates they are needed
* as shown above.
*
* # Loading Classes
*
* You can also use `Ext.Loader` directly to load classes or files. The simplest form of
* use is `{@link Ext#require}`.
*
* For example:
*
* Ext.require('MyApp.view.Main', function () {
* // On callback, the MyApp.view.Main class is now loaded
*
* var view = new MyApp.view.Main();
* });
*
* You can alternatively require classes by alias or wildcard.
*
* Ext.require('widget.window');
*
* Ext.require(['widget.window', 'layout.border', 'Ext.data.Connection']);
*
* Ext.require(['widget.*', 'layout.*', 'Ext.data.*']);
*
* The callback function is optional.
*
* **Note** Using `Ext.require` at global scope will cause `{@link Ext#onReady}` and
* `{@link Ext.app.Application#launch}` methods to be deferred until the required classes
* are loaded. It is these cases where the callback function is most often unnecessary.
*
* ## Using Excludes
*
* Alternatively, you can exclude what you don't need:
*
* // Include everything except Ext.tree.*
* Ext.exclude('Ext.tree.*').require('*');
*
* // Include all widgets except widget.checkbox* (this will exclude
* // widget.checkbox, widget.checkboxfield, widget.checkboxgroup, etc.)
* Ext.exclude('widget.checkbox*').require('widget.*');
*
* # Dynamic Instantiation
*
* Another feature enabled by `Ext.Loader` is instantiation using class names or aliases.
*
* For example:
*
* var win = Ext.create({
* xtype: 'window',
*
* // or
* // xclass: 'Ext.window.Window'
*
* title: 'Hello'
* });
*
* This form of creation can be useful if the type to create (`window` in the above) is
* not known statically. Internally, `{@link Ext#create}` may need to *synchronously*
* load the desired class and its requirements. Doing this will generate a warning in
* the console:
*
* [Ext.Loader] Synchronously loading 'Ext.window.Window'...
*
* If you see these in your debug console, you should add the indicated class(es) to the
* appropriate `requires` array (as above) or make an `{@link Ext#require}` call.
*
*
* **Note** Using `{@link Ext#create}` has some performance overhead and is best reserved
* for cases where the target class is not known until run-time.
*
* @class Ext.Loader
* @singleton
*/
Ext.Loader = (new function() {
// jshint ignore:line
// @define Ext.Loader
// @require Ext.Base
// @require Ext.Class
// @require Ext.ClassManager
// @require Ext.Function
// @require Ext.Array
// @require Ext.env.Ready
var Loader = this,
Manager = Ext.ClassManager,
// this is an instance of Ext.Inventory
Boot = Ext.Boot,
Class = Ext.Class,
Ready = Ext.env.Ready,
alias = Ext.Function.alias,
dependencyProperties = [
'extend',
'mixins',
'requires'
],
isInHistory = {},
history = [],
readyListeners = [],
usedClasses = [],
_requiresMap = {},
_config = {
/**
* @cfg {Boolean} [enabled=true]
* Whether or not to enable the dynamic dependency loading feature.
*/
enabled: true,
/**
* @cfg {Boolean} [scriptChainDelay=false]
* millisecond delay between asynchronous script injection (prevents stack
* overflow on some user agents) 'false' disables delay but potentially
* increases stack load.
*/
scriptChainDelay: false,
/**
* @cfg {Boolean} [disableCaching=true]
* Appends current timestamp to script files to prevent caching.
*/
disableCaching: true,
/**
* @cfg {String} [disableCachingParam="_dc"]
* The get parameter name for the cache buster's timestamp.
*/
disableCachingParam: '_dc',
/**
* @cfg {Object} paths
* The mapping from namespaces to file paths
*
* {
* 'Ext': '.', // This is set by default, Ext.layout.container.Container will be
* // loaded from ./layout/Container.js
*
* 'My': './src/my_own_folder' // My.layout.Container will be loaded from
* // ./src/my_own_folder/layout/Container.js
* }
*
* Note that all relative paths are relative to the current HTML document.
* If not being specified, for example, `Other.awesome.Class` will simply be
* loaded from `"./Other/awesome/Class.js"`.
*/
paths: Manager.paths,
/**
* @cfg {Boolean} preserveScripts
* `false` to remove asynchronously loaded scripts, `true` to retain script
* element for browser debugger compatibility and improved load performance.
*/
preserveScripts: true,
/**
* @cfg {String} scriptCharset
* Optional charset to specify encoding of dynamic script content.
*/
scriptCharset: undefined
},
// These configs are delegated to Ext.Script and may need different names:
delegatedConfigs = {
disableCaching: true,
disableCachingParam: true,
preserveScripts: true,
scriptChainDelay: 'loadDelay'
};
Ext.apply(Loader, {
/**
* @private
*/
isInHistory: isInHistory,
/**
* Flag indicating whether there are still files being loaded
* @private
*/
isLoading: false,
/**
* An array of class names to keep track of the dependency loading order.
* This is not guaranteed to be the same everytime due to the asynchronous
* nature of the Loader.
*
* @property {Array} history
*/
history: history,
/**
* Configuration
* @private
*/
config: _config,
/**
* Maintain the list of listeners to execute when all required scripts are fully loaded
* @private
*/
readyListeners: readyListeners,
/**
* Contains classes referenced in `uses` properties.
* @private
*/
optionalRequires: usedClasses,
/**
* Map of fully qualified class names to an array of dependent classes.
* @private
*/
requiresMap: _requiresMap,
/** @private */
hasFileLoadError: false,
/**
* The number of scripts loading via loadScript.
* @private
*/
scriptsLoading: 0,
/**
* @private
*/
classesLoading: {},
missingCount: 0,
missingQueue: {},
/**
* @private
*/
syncModeEnabled: false,
init: function() {
// initalize the default path of the framework
var scripts = document.getElementsByTagName('script'),
src = scripts[scripts.length - 1].src,
path = src.substring(0, src.lastIndexOf('/') + 1),
meta = Ext._classPathMetadata,
microloader = Ext.Microloader,
manifest = Ext.manifest,
loadOrder, classes, className, idx, baseUrl, loadlen, l, loadItem;
if (src.indexOf("packages/core/src/") !== -1) {
path = path + "../../";
} else if (src.indexOf("/core/src/class/") !== -1) {
path = path + "../../../";
}
if (!Manager.getPath("Ext")) {
Manager.setPath('Ext', path + 'src');
}
// Pull in Cmd generated metadata if available.
if (meta) {
Ext._classPathMetadata = null;
Loader.addClassPathMappings(meta);
}
if (manifest) {
loadOrder = manifest.loadOrder;
// if the manifest paths were calculated as relative to the
// bootstrap file, then we need to prepend Boot.baseUrl to the
// paths before processing
baseUrl = Ext.Boot.baseUrl;
if (loadOrder && manifest.bootRelative) {
for (loadlen = loadOrder.length , l = 0; l < loadlen; l++) {
loadItem = loadOrder[l];
loadItem.path = baseUrl + loadItem.path;
loadItem.canonicalPath = true;
}
}
}
if (microloader) {
Ready.block();
microloader.onMicroloaderReady(function() {
Ready.unblock();
});
}
},
/**
* Set the configuration for the loader. This should be called right after ext-(debug).js
* is included in the page, and before Ext.onReady. i.e:
*
* <script type="text/javascript" src="ext-core-debug.js"></script>
* <script type="text/javascript">
* Ext.Loader.setConfig({
* enabled: true,
* paths: {
* 'My': 'my_own_path'
* }
* });
* </script>
* <script type="text/javascript">
* Ext.require(...);
*
* Ext.onReady(function() {
* // application code here
* });
* </script>
*
* Refer to config options of {@link Ext.Loader} for the list of possible properties
*
* @param {Object} config The config object to override the default values
* @return {Ext.Loader} this
*/
setConfig: Ext.Function.flexSetter(function(name, value) {
if (name === 'paths') {
Loader.setPath(value);
} else {
_config[name] = value;
var delegated = delegatedConfigs[name];
if (delegated) {
Boot.setConfig((delegated === true) ? name : delegated, value);
}
}
return Loader;
}),
/**
* Get the config value corresponding to the specified name. If no name is given,
* will return the config object
*
* @param {String} name The config property name
* @return {Object}
*/
getConfig: function(name) {
return name ? _config[name] : _config;
},
/**
* Sets the path of a namespace.
* For Example:
*
* Ext.Loader.setPath('Ext', '.');
*
* @param {String/Object} name See {@link Ext.Function#flexSetter flexSetter}
* @param {String} [path] See {@link Ext.Function#flexSetter flexSetter}
* @return {Ext.Loader} this
* @method
*/
setPath: function() {
// Paths are an Ext.Inventory thing and ClassManager is an instance of that:
Manager.setPath.apply(Manager, arguments);
return Loader;
},
/**
* Sets a batch of path entries
*
* @param {Object} paths a set of className: path mappings
* @return {Ext.Loader} this
*/
addClassPathMappings: function(paths) {
// Paths are an Ext.Inventory thing and ClassManager is an instance of that:
Manager.setPath(paths);
return Loader;
},
/**
* fixes up loader path configs by prepending Ext.Boot#baseUrl to the beginning
* of the path, then delegates to Ext.Loader#addClassPathMappings
* @param pathConfig
*/
addBaseUrlClassPathMappings: function(pathConfig) {
for (var name in pathConfig) {
pathConfig[name] = Boot.baseUrl + pathConfig[name];
}
Ext.Loader.addClassPathMappings(pathConfig);
},
/**
* Translates a className to a file path by adding the
* the proper prefix and converting the .'s to /'s. For example:
*
* Ext.Loader.setPath('My', '/path/to/My');
*
* alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/path/to/My/awesome/Class.js'
*
* Note that the deeper namespace levels, if explicitly set, are always resolved first.
* For example:
*
* Ext.Loader.setPath({
* 'My': '/path/to/lib',
* 'My.awesome': '/other/path/for/awesome/stuff',
* 'My.awesome.more': '/more/awesome/path'
* });
*
* alert(Ext.Loader.getPath('My.awesome.Class')); // alerts '/other/path/for/awesome/stuff/Class.js'
*
* alert(Ext.Loader.getPath('My.awesome.more.Class')); // alerts '/more/awesome/path/Class.js'
*
* alert(Ext.Loader.getPath('My.cool.Class')); // alerts '/path/to/lib/cool/Class.js'
*
* alert(Ext.Loader.getPath('Unknown.strange.Stuff')); // alerts 'Unknown/strange/Stuff.js'
*
* @param {String} className
* @return {String} path
*/
getPath: function(className) {
// Paths are an Ext.Inventory thing and ClassManager is an instance of that:
return Manager.getPath(className);
},
require: function(expressions, fn, scope, excludes) {
if (excludes) {
return Loader.exclude(excludes).require(expressions, fn, scope);
}
var classNames = Manager.getNamesByExpression(expressions);
return Loader.load(classNames, fn, scope);
},
syncRequire: function() {
var wasEnabled = Loader.syncModeEnabled;
Loader.syncModeEnabled = true;
var ret = Loader.require.apply(Loader, arguments);
Loader.syncModeEnabled = wasEnabled;
return ret;
},
exclude: function(excludes) {
var selector = Manager.select({
require: function(classNames, fn, scope) {
return Loader.load(classNames, fn, scope);
},
syncRequire: function(classNames, fn, scope) {
var wasEnabled = Loader.syncModeEnabled;
Loader.syncModeEnabled = true;
var ret = Loader.load(classNames, fn, scope);
Loader.syncModeEnabled = wasEnabled;
return ret;
}
});
selector.exclude(excludes);
return selector;
},
load: function(classNames, callback, scope) {
if (callback) {
if (callback.length) {
// If callback expects arguments, shim it with a function that will map
// the requires class(es) from the names we are given.
callback = Loader.makeLoadCallback(classNames, callback);
}
callback = callback.bind(scope || Ext.global);
}
var state = Manager.classState,
missingClassNames = [],
urls = [],
urlByClass = {},
numClasses = classNames.length,
url, className, i, numMissing;
for (i = 0; i < numClasses; ++i) {
className = Manager.resolveName(classNames[i]);
if (!Manager.isCreated(className)) {
missingClassNames.push(className);
if (!state[className]) {
urlByClass[className] = Loader.getPath(className);
urls.push(urlByClass[className]);
}
}
}
// If the dynamic dependency feature is not being used, throw an error
// if the dependencies are not defined
numMissing = missingClassNames.length;
if (numMissing) {
Loader.missingCount += numMissing;
Manager.onCreated(function() {
if (callback) {
Ext.callback(callback, scope, arguments);
}
Loader.checkReady();
}, Loader, missingClassNames);
if (!_config.enabled) {
Ext.raise("Ext.Loader is not enabled, so dependencies cannot be resolved dynamically. " + "Missing required class" + ((missingClassNames.length > 1) ? "es" : "") + ": " + missingClassNames.join(', '));
}
if (urls.length) {
Loader.loadScripts({
url: urls,
// scope will be this options object so we can pass these along:
_classNames: missingClassNames,
_urlByClass: urlByClass
});
} else {
// need to call checkReady here, as the _missingCoun
// may have transitioned from 0 to > 0, meaning we
// need to block ready
Loader.checkReady();
}
} else {
if (callback) {
callback.call(scope);
}
// need to call checkReady here, as the _missingCoun
// may have transitioned from 0 to > 0, meaning we
// need to block ready
Loader.checkReady();
}
if (Loader.syncModeEnabled) {
// Class may have been just loaded or was already loaded
if (numClasses === 1) {
return Manager.get(classNames[0]);
}
}
return Loader;
},
makeLoadCallback: function(classNames, callback) {
return function() {
var classes = [],
i = classNames.length;
while (i-- > 0) {
classes[i] = Manager.get(classNames[i]);
}
return callback.apply(this, classes);
};
},
onLoadFailure: function() {
var options = this,
onError = options.onError;
Loader.hasFileLoadError = true;
--Loader.scriptsLoading;
if (onError) {
//TODO: need an adapter to convert to v4 onError signatures
onError.call(options.userScope, options);
} else {
Ext.log.error("[Ext.Loader] Some requested files failed to load.");
}
Loader.checkReady();
},
onLoadSuccess: function() {
var options = this,
onLoad = options.onLoad,
classNames = options._classNames,
urlByClass = options._urlByClass,
state = Manager.classState,
missingQueue = Loader.missingQueue,
className, i, len;
--Loader.scriptsLoading;
if (onLoad) {
//TODO: need an adapter to convert to v4 onLoad signatures
onLoad.call(options.userScope, options);
}
// onLoad can cause more loads to start, so it must run first
// classNames is the array of *all* classes that load() was asked to load,
// including those that might have been already loaded but not yet created.
// urlByClass is a map of only those classes that we asked Boot to load.
for (i = 0 , len = classNames.length; i < len; i++) {
className = classNames[i];
// When a script is loaded and executed, we should have Ext.define() called
// for at least one of the classes in the list, which will set the state
// for that class. That by itself does not mean that the class is available
// *now* but it means that ClassManager is tracking it and will fire the
// onCreated callback that we set back in load().
// However if there is no state for the class, that may mean two things:
// either it is not a Ext class, or it is truly missing. In any case we need
// to watch for that thing ourselves, which we will do every checkReady().
if (!state[className]) {
missingQueue[className] = urlByClass[className];
}
}
Loader.checkReady();
},
// TODO: this timing of this needs to be deferred until all classes have had
// a chance to be created
reportMissingClasses: function() {
if (!Loader.syncModeEnabled && !Loader.scriptsLoading && Loader.isLoading && !Loader.hasFileLoadError) {
var missingQueue = Loader.missingQueue,
missingClasses = [],
missingPaths = [];
for (var missingClassName in missingQueue) {
missingClasses.push(missingClassName);
missingPaths.push(missingQueue[missingClassName]);
}
if (missingClasses.length) {
throw new Error("The following classes are not declared even if their files have been " + "loaded: '" + missingClasses.join("', '") + "'. Please check the source code of their " + "corresponding files for possible typos: '" + missingPaths.join("', '"));
}
}
},
/**
* Add a new listener to be executed when all required scripts are fully loaded
*
* @param {Function} fn The function callback to be executed
* @param {Object} scope The execution scope (`this`) of the callback function.
* @param {Boolean} [withDomReady=true] Pass `false` to not also wait for document
* dom ready.
* @param {Object} [options] Additional callback options.
* @param {Number} [options.delay=0] A number of milliseconds to delay.
* @param {Number} [options.priority=0] Relative priority of this callback. Negative
* numbers are reserved.
*/
onReady: function(fn, scope, withDomReady, options) {
if (withDomReady) {
Ready.on(fn, scope, options);
} else {
var listener = Ready.makeListener(fn, scope, options);
if (Loader.isLoading) {
readyListeners.push(listener);
} else {
Ready.invoke(listener);
}
}
},
/**
* @private
* Ensure that any classes referenced in the `uses` property are loaded.
*/
addUsedClasses: function(classes) {
var cls, i, ln;
if (classes) {
classes = (typeof classes === 'string') ? [
classes
] : classes;
for (i = 0 , ln = classes.length; i < ln; i++) {
cls = classes[i];
if (typeof cls === 'string' && !Ext.Array.contains(usedClasses, cls)) {
usedClasses.push(cls);
}
}
}
return Loader;
},
/**
* @private
*/
triggerReady: function() {
var listener,
refClasses = usedClasses;
if (Loader.isLoading && refClasses.length) {
// Empty the array to eliminate potential recursive loop issue
usedClasses = [];
// this may immediately call us back if all 'uses' classes
// have been loaded
Loader.require(refClasses);
} else {
// Must clear this before calling callbacks. This will cause any new loads
// to call Ready.block() again. See below for more on this.
Loader.isLoading = false;
// These listeners are just those attached directly to Loader to wait for
// class loading only.
readyListeners.sort(Ready.sortFn);
// this method can be called with Loader.isLoading either true or false
// (can be called with false when all 'uses' classes are already loaded)
// this may bypass the above if condition
while (readyListeners.length && !Loader.isLoading) {
// we may re-enter triggerReady so we cannot necessarily iterate the
// readyListeners array
listener = readyListeners.pop();
Ready.invoke(listener);
}
// If the DOM is also ready, this will fire the normal onReady listeners.
// An astute observer would note that we may now be back to isLoading and
// so ask "Why you call unblock?". The reason is that we must match the
// calls to block and since we transitioned from isLoading to !isLoading
// here we must call unblock. If we have transitioned back to isLoading in
// the above loop it will have called block again so the counter will be
// increased and this call will not reduce the block count to 0. This is
// done by loadScripts.
Ready.unblock();
}
},
/**
* @private
* @param {String} className
*/
historyPush: function(className) {
if (className && !isInHistory[className] && !Manager.overrideMap[className]) {
isInHistory[className] = true;
history.push(className);
}
return Loader;
},
/**
* This is an internal method that delegate content loading to the
* bootstrap layer.
* @private
* @param params
*/
loadScripts: function(params) {
var manifest = Ext.manifest,
loadOrder = manifest && manifest.loadOrder,
loadOrderMap = manifest && manifest.loadOrderMap,
options;
++Loader.scriptsLoading;
// if the load order map hasn't been created, create it now
// and cache on the manifest
if (loadOrder && !loadOrderMap) {
manifest.loadOrderMap = loadOrderMap = Boot.createLoadOrderMap(loadOrder);
}
// verify the loading state, as this may have transitioned us from
// not loading to loading
Loader.checkReady();
options = Ext.apply({
loadOrder: loadOrder,
loadOrderMap: loadOrderMap,
charset: _config.scriptCharset,
success: Loader.onLoadSuccess,
failure: Loader.onLoadFailure,
sync: Loader.syncModeEnabled,
_classNames: []
}, params);
options.userScope = options.scope;
options.scope = options;
Boot.load(options);
},
/**
* This method is provide for use by the bootstrap layer.
* @private
* @param {String[]} urls
*/
loadScriptsSync: function(urls) {
var syncwas = Loader.syncModeEnabled;
Loader.syncModeEnabled = true;
Loader.loadScripts({
url: urls
});
Loader.syncModeEnabled = syncwas;
},
/**
* This method is provide for use by the bootstrap layer.
* @private
* @param {String[]} urls
*/
loadScriptsSyncBasePrefix: function(urls) {
var syncwas = Loader.syncModeEnabled;
Loader.syncModeEnabled = true;
Loader.loadScripts({
url: urls,
prependBaseUrl: true
});
Loader.syncModeEnabled = syncwas;
},
/**
* Loads the specified script URL and calls the supplied callbacks. If this method
* is called before {@link Ext#isReady}, the script's load will delay the transition
* to ready. This can be used to load arbitrary scripts that may contain further
* {@link Ext#require Ext.require} calls.
*
* @param {Object/String/String[]} options The options object or simply the URL(s) to load.
* @param {String} options.url The URL from which to load the script.
* @param {Function} [options.onLoad] The callback to call on successful load.
* @param {Function} [options.onError] The callback to call on failure to load.
* @param {Object} [options.scope] The scope (`this`) for the supplied callbacks.
*/
loadScript: function(options) {
var isString = typeof options === 'string',
isArray = options instanceof Array,
isObject = !isArray && !isString,
url = isObject ? options.url : options,
onError = isObject && options.onError,
onLoad = isObject && options.onLoad,
scope = isObject && options.scope,
request = {
url: url,
scope: scope,
onLoad: onLoad,
onError: onError,
_classNames: []
};
Loader.loadScripts(request);
},
/**
* @private
*/
checkMissingQueue: function() {
var missingQueue = Loader.missingQueue,
newQueue = {},
name,
missing = 0;
for (name in missingQueue) {
// If class state is available for the name, that means ClassManager
// is tracking it and will fire callback when it is created.
// We only need to track non-class things in the Loader.
if (!(Manager.classState[name] || Manager.isCreated(name))) {
newQueue[name] = missingQueue[name];
missing++;
}
}
Loader.missingCount = missing;
Loader.missingQueue = newQueue;
},
/**
* @private
*/
checkReady: function() {
var wasLoading = Loader.isLoading,
isLoading;
Loader.checkMissingQueue();
isLoading = Loader.missingCount + Loader.scriptsLoading;
if (isLoading && !wasLoading) {
Ready.block();
Loader.isLoading = !!isLoading;
} else if (!isLoading && wasLoading) {
Loader.triggerReady();
}
if (!Loader.scriptsLoading && Loader.missingCount) {
// Things look bad, but since load requests may come later, defer this
// for a bit then check if things are still stuck.
Ext.defer(function() {
if (!Loader.scriptsLoading && Loader.missingCount) {
Ext.log.error('[Loader] The following classes failed to load:');
for (var name in Loader.missingQueue) {
Ext.log.error('[Loader] ' + name + ' from ' + Loader.missingQueue[name]);
}
}
}, 1000);
}
}
});
/**
* Loads all classes by the given names and all their direct dependencies; optionally
* executes the given callback function when finishes, within the optional scope.
*
* @param {String/String[]} expressions The class, classes or wildcards to load.
* @param {Function} [fn] The callback function.
* @param {Object} [scope] The execution scope (`this`) of the callback function.
* @member Ext
* @method require
*/
Ext.require = alias(Loader, 'require');
/**
* Synchronously loads all classes by the given names and all their direct dependencies; optionally
* executes the given callback function when finishes, within the optional scope.
*
* @param {String/String[]} expressions The class, classes or wildcards to load.
* @param {Function} [fn] The callback function.
* @param {Object} [scope] The execution scope (`this`) of the callback function.
* @member Ext
* @method syncRequire
*/
Ext.syncRequire = alias(Loader, 'syncRequire');
/**
* Explicitly exclude files from being loaded. Useful when used in conjunction with a
* broad include expression. Can be chained with more `require` and `exclude` methods,
* for example:
*
* Ext.exclude('Ext.data.*').require('*');
*
* Ext.exclude('widget.button*').require('widget.*');
*
* @param {String/String[]} excludes
* @return {Object} Contains `exclude`, `require` and `syncRequire` methods for chaining.
* @member Ext
* @method exclude
*/
Ext.exclude = alias(Loader, 'exclude');
/**
* @cfg {String[]} requires
* @member Ext.Class
* List of classes that have to be loaded before instantiating this class.
* For example:
*
* Ext.define('Mother', {
* requires: ['Child'],
* giveBirth: function() {
* // we can be sure that child class is available.
* return new Child();
* }
* });
*/
Class.registerPreprocessor('loader', function(cls, data, hooks, continueFn) {
Ext.classSystemMonitor && Ext.classSystemMonitor(cls, 'Ext.Loader#loaderPreprocessor', arguments);
// jshint ignore:line
var me = this,
dependencies = [],
dependency,
className = Manager.getName(cls),
i, j, ln, subLn, value, propertyName, propertyValue, requiredMap;
/*
Loop through the dependencyProperties, look for string class names and push
them into a stack, regardless of whether the property's value is a string, array or object. For example:
{
extend: 'Ext.MyClass',
requires: ['Ext.some.OtherClass'],
mixins: {
thing: 'Foo.bar.Thing';
}
}
which will later be transformed into:
{
extend: Ext.MyClass,
requires: [Ext.some.OtherClass],
mixins: {
thing: Foo.bar.Thing;
}
}
*/
for (i = 0 , ln = dependencyProperties.length; i < ln; i++) {
propertyName = dependencyProperties[i];
if (data.hasOwnProperty(propertyName)) {
propertyValue = data[propertyName];
if (typeof propertyValue === 'string') {
dependencies.push(propertyValue);
} else if (propertyValue instanceof Array) {
for (j = 0 , subLn = propertyValue.length; j < subLn; j++) {
value = propertyValue[j];
if (typeof value === 'string') {
dependencies.push(value);
}
}
} else if (typeof propertyValue !== 'function') {
for (j in propertyValue) {
if (propertyValue.hasOwnProperty(j)) {
value = propertyValue[j];
if (typeof value === 'string') {
dependencies.push(value);
}
}
}
}
}
}
if (dependencies.length === 0) {
return;
}
if (className) {
_requiresMap[className] = dependencies;
}
var manifestClasses = Ext.manifest && Ext.manifest.classes,
deadlockPath = [],
detectDeadlock;
/*
* Automatically detect deadlocks before-hand,
* will throw an error with detailed path for ease of debugging. Examples
* of deadlock cases:
*
* - A extends B, then B extends A
* - A requires B, B requires C, then C requires A
*
* The detectDeadlock function will recursively transverse till the leaf, hence
* it can detect deadlocks no matter how deep the path is. However we don't need
* to run this check if the class name is in the manifest: that means Cmd has
* already resolved all dependencies for this class with no deadlocks.
*/
if (className && (!manifestClasses || !manifestClasses[className])) {
requiredMap = Loader.requiredByMap || (Loader.requiredByMap = {});
for (i = 0 , ln = dependencies.length; i < ln; i++) {
dependency = dependencies[i];
(requiredMap[dependency] || (requiredMap[dependency] = [])).push(className);
}
detectDeadlock = function(cls) {
deadlockPath.push(cls);
var requires = _requiresMap[cls],
dep, i, ln;
if (requires) {
if (Ext.Array.contains(requires, className)) {
Ext.Error.raise("Circular requirement detected! '" + className + "' and '" + deadlockPath[1] + "' mutually require each other. Path: " + deadlockPath.join(' -> ') + " -> " + deadlockPath[0]);
}
for (i = 0 , ln = requires.length; i < ln; i++) {
dep = requires[i];
if (!isInHistory[dep]) {
detectDeadlock(requires[i]);
}
}
}
};
detectDeadlock(className);
}
(className ? Loader.exclude(className) : Loader).require(dependencies, function() {
for (i = 0 , ln = dependencyProperties.length; i < ln; i++) {
propertyName = dependencyProperties[i];
if (data.hasOwnProperty(propertyName)) {
propertyValue = data[propertyName];
if (typeof propertyValue === 'string') {
data[propertyName] = Manager.get(propertyValue);
} else if (propertyValue instanceof Array) {
for (j = 0 , subLn = propertyValue.length; j < subLn; j++) {
value = propertyValue[j];
if (typeof value === 'string') {
data[propertyName][j] = Manager.get(value);
}
}
} else if (typeof propertyValue !== 'function') {
for (var k in propertyValue) {
if (propertyValue.hasOwnProperty(k)) {
value = propertyValue[k];
if (typeof value === 'string') {
data[propertyName][k] = Manager.get(value);
}
}
}
}
}
}
continueFn.call(me, cls, data, hooks);
});
return false;
}, true, 'after', 'className');
/**
* @cfg {String[]} uses
* @member Ext.Class
* List of optional classes to load together with this class. These aren't neccessarily loaded before
* this class is created, but are guaranteed to be available before Ext.onReady listeners are
* invoked. For example:
*
* Ext.define('Mother', {
* uses: ['Child'],
* giveBirth: function() {
* // This code might, or might not work:
* // return new Child();
*
* // Instead use Ext.create() to load the class at the spot if not loaded already:
* return Ext.create('Child');
* }
* });
*/
Manager.registerPostprocessor('uses', function(name, cls, data) {
Ext.classSystemMonitor && Ext.classSystemMonitor(cls, 'Ext.Loader#usesPostprocessor', arguments);
// jshint ignore:line
var uses = data.uses,
classNames;
if (uses) {
classNames = Manager.getNamesByExpression(data.uses);
Loader.addUsedClasses(classNames);
}
});
Manager.onCreated(Loader.historyPush);
Loader.init();
}());
//-----------------------------------------------------------------------------
// Use performance.now when available to keep timestamps consistent.
Ext._endTime = Ext.ticks();
// This hook is to allow tools like DynaTrace to deterministically detect the availability
// of Ext.onReady. Since Loader takes over Ext.onReady this must be done here and not in
// Ext.env.Ready.
if (Ext._beforereadyhandler) {
Ext._beforereadyhandler();
}
/**
* @class Ext.util.Positionable
*/
Ext.define('Ext.overrides.util.Positionable', {
override: 'Ext.util.Positionable',
/**
* @method alignTo
* @param {Ext.util.Positionable/HTMLElement/String} anchorToEl The Positionable,
* HTMLElement, or id of the element to align to.
* @param {String} [alignment="tl-bl?"] The position to align to
* @param {Number[]} [offsets] Offset the positioning by [x, y]
* @param {Boolean/Object} [animate] true for the default animation or a standard
* Element animation config object
* @return {Ext.util.Positionable} this
*/
/**
* @method anchorTo
* Anchors an element to another element and realigns it when the window is resized.
* @param {Ext.util.Positionable/HTMLElement/String} anchorToEl The Positionable,
* HTMLElement, or id of the element to align to.
* @param {String} [alignment="tl-bl?"] The position to align to
* @param {Number[]} [offsets] Offset the positioning by [x, y]
* @param {Boolean/Object} [animate] true for the default animation or a standard
* Element animation config object
* @param {Boolean/Number} [monitorScroll=50] True to monitor body scroll and
* reposition. If this parameter is a number, it is used as the buffer delay in
* milliseconds.
* @param {Function} [callback] The function to call after the animation finishes
* @return {Ext.util.Positionable} this
*/
anchorTo: function(anchorToEl, alignment, offsets, animate, monitorScroll, callback) {
var me = this,
scroll = !Ext.isEmpty(monitorScroll),
action = function() {
me.mixins.positionable.alignTo.call(me, anchorToEl, alignment, offsets, animate);
Ext.callback(callback, me);
},
anchor = me.getAnchor();
// previous listener anchor, remove it
me.removeAnchor();
Ext.apply(anchor, {
fn: action,
scroll: scroll
});
Ext.on('resize', action, null);
if (scroll) {
Ext.getWin().on('scroll', action, null, {
buffer: !isNaN(monitorScroll) ? monitorScroll : 50
});
}
action();
// align immediately
return me;
},
getAnchor: function() {
var el = this.el,
data, anchor;
if (!el || !el.dom) {
return;
}
data = el.getData();
anchor = data._anchor;
if (!anchor) {
anchor = data._anchor = {};
}
return anchor;
},
alignTo: function(element, position, offsets, /* private (documented in ext) */
animate) {
var me = this,
el = me.el,
newMaxHeight, newRegion;
// Release any height constraint prior to aligning if we are shrinkwrap height.
if (me.isComponent && me.getSizeModel().height.shrinkWrap) {
if (me.maxHeight) {
me.setMaxHeight(null);
}
newMaxHeight = true;
}
newRegion = me.getAlignToRegion(element, position, offsets, me.minHeight || 150);
me.setXY([
newRegion.x,
newRegion.y
], el.anim && !!animate ? el.anim(animate) : false);
// Impose calculated height constraint.
if (newMaxHeight && (newMaxHeight = newRegion.getHeight()) !== me.getHeight()) {
me.setMaxHeight(newMaxHeight);
}
return me;
},
/**
* @method move
* Move the element relative to its current position.
* @param {String} direction Possible values are:
*
* - `"l"` (or `"left"`)
* - `"r"` (or `"right"`)
* - `"t"` (or `"top"`, or `"up"`)
* - `"b"` (or `"bottom"`, or `"down"`)
*
* @param {Number} distance How far to move the element in pixels
* @param {Boolean/Object} [animate] true for the default animation or a standard
* Element animation config object
*/
/**
* Remove any anchor to this element. See {@link #anchorTo}.
* @return {Ext.util.Positionable} this
*/
removeAnchor: function() {
var anchor = this.getAnchor();
if (anchor && anchor.fn) {
Ext.un('resize', anchor.fn);
if (anchor.scroll) {
Ext.getWin().on('scroll', anchor.fn);
}
delete anchor.fn;
}
return this;
},
/**
* @method setBox
* Sets the element's box. If animate is true then x, y, width, and height will be
* animated concurrently.
* @param {Object} box The box to fill {x, y, width, height}
* @param {Boolean/Object} [animate] true for the default animation or a standard
* Element animation config object
* @return {Ext.util.Positionable} this
*/
setBox: function(box, animate) {
var me = this;
if (box.isRegion) {
box = {
x: box.left,
y: box.top,
width: box.right - box.left,
height: box.bottom - box.top
};
}
if (animate) {
me.constrainBox(box);
me.animate(Ext.applyIf({
to: box,
listeners: {
afteranimate: Ext.Function.bind(me.afterSetPosition, me, [
box.x,
box.y
])
}
}, animate));
} else {
me.callParent([
box
]);
}
return me;
}
});
/**
* @method setX
* Sets the X position of the DOM element based on page coordinates.
* @param {Number} x The X position
* @param {Boolean/Object} [animate] True for the default animation, or a standard
* Element animation config object
* @return {Ext.util.Positionable} this
*/
/**
* @method setXY
* Sets the position of the DOM element in page coordinates.
* @param {Number[]} pos Contains X & Y [x, y] values for new position (coordinates
* are page-based)
* @param {Boolean/Object} [animate] True for the default animation, or a standard
* Element animation config object
* @return {Ext.util.Positionable} this
*/
/**
* @method setY
* Sets the Y position of the DOM element based on page coordinates.
* @param {Number} y The Y position
* @param {Boolean/Object} [animate] True for the default animation, or a standard
* Element animation config object
* @return {Ext.util.Positionable} this
*/
/**
* @class Ext.event.Event
*/
Ext.define('Ext.overrides.event.Event', {
override: 'Ext.event.Event',
// map of events that should fire global mousedown even if stopped
mousedownEvents: {
mousedown: 1,
pointerdown: 1,
touchstart: 1
},
/**
* @method injectEvent
* @member Ext.event.Event
* Injects a DOM event using the data in this object and (optionally) a new target.
* This is a low-level technique and not likely to be used by application code. The
* currently supported event types are:
* <p><b>HTMLEvents</b></p>
* <ul>
* <li>load</li>
* <li>unload</li>
* <li>select</li>
* <li>change</li>
* <li>submit</li>
* <li>reset</li>
* <li>resize</li>
* <li>scroll</li>
* </ul>
* <p><b>MouseEvents</b></p>
* <ul>
* <li>click</li>
* <li>dblclick</li>
* <li>mousedown</li>
* <li>mouseup</li>
* <li>mouseover</li>
* <li>mousemove</li>
* <li>mouseout</li>
* </ul>
* <p><b>UIEvents</b></p>
* <ul>
* <li>focusin</li>
* <li>focusout</li>
* <li>activate</li>
* <li>focus</li>
* <li>blur</li>
* </ul>
* @param {Ext.Element/HTMLElement} target (optional) If specified, the target for the event. This
* is likely to be used when relaying a DOM event. If not specified, {@link #getTarget}
* is used to determine the target.
*/
injectEvent: (function() {
var API,
dispatchers = {},
// keyed by event type (e.g., 'mousedown')
crazyIEButtons;
// Good reference: http://developer.yahoo.com/yui/docs/UserAction.js.html
// IE9 has createEvent, but this code causes major problems with htmleditor (it
// blocks all mouse events and maybe more). TODO
if (!Ext.isIE9m && document.createEvent) {
// if (DOM compliant)
API = {
createHtmlEvent: function(doc, type, bubbles, cancelable) {
var event = doc.createEvent('HTMLEvents');
event.initEvent(type, bubbles, cancelable);
return event;
},
createMouseEvent: function(doc, type, bubbles, cancelable, detail, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget) {
var event = doc.createEvent('MouseEvents'),
view = doc.defaultView || window;
if (event.initMouseEvent) {
event.initMouseEvent(type, bubbles, cancelable, view, detail, clientX, clientY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget);
} else {
// old Safari
event = doc.createEvent('UIEvents');
event.initEvent(type, bubbles, cancelable);
event.view = view;
event.detail = detail;
event.screenX = clientX;
event.screenY = clientY;
event.clientX = clientX;
event.clientY = clientY;
event.ctrlKey = ctrlKey;
event.altKey = altKey;
event.metaKey = metaKey;
event.shiftKey = shiftKey;
event.button = button;
event.relatedTarget = relatedTarget;
}
return event;
},
createUIEvent: function(doc, type, bubbles, cancelable, detail) {
var event = doc.createEvent('UIEvents'),
view = doc.defaultView || window;
event.initUIEvent(type, bubbles, cancelable, view, detail);
return event;
},
fireEvent: function(target, type, event) {
target.dispatchEvent(event);
}
};
} else if (document.createEventObject) {
// else if (IE)
crazyIEButtons = {
0: 1,
1: 4,
2: 2
};
API = {
createHtmlEvent: function(doc, type, bubbles, cancelable) {
var event = doc.createEventObject();
event.bubbles = bubbles;
event.cancelable = cancelable;
return event;
},
createMouseEvent: function(doc, type, bubbles, cancelable, detail, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget) {
var event = doc.createEventObject();
event.bubbles = bubbles;
event.cancelable = cancelable;
event.detail = detail;
event.screenX = clientX;
event.screenY = clientY;
event.clientX = clientX;
event.clientY = clientY;
event.ctrlKey = ctrlKey;
event.altKey = altKey;
event.shiftKey = shiftKey;
event.metaKey = metaKey;
event.button = crazyIEButtons[button] || button;
event.relatedTarget = relatedTarget;
// cannot assign to/fromElement
return event;
},
createUIEvent: function(doc, type, bubbles, cancelable, detail) {
var event = doc.createEventObject();
event.bubbles = bubbles;
event.cancelable = cancelable;
return event;
},
fireEvent: function(target, type, event) {
target.fireEvent('on' + type, event);
}
};
}
//----------------
// HTMLEvents
Ext.Object.each({
load: [
false,
false
],
unload: [
false,
false
],
select: [
true,
false
],
change: [
true,
false
],
submit: [
true,
true
],
reset: [
true,
false
],
resize: [
true,
false
],
scroll: [
true,
false
]
}, function(name, value) {
var bubbles = value[0],
cancelable = value[1];
dispatchers[name] = function(targetEl, srcEvent) {
var e = API.createHtmlEvent(name, bubbles, cancelable);
API.fireEvent(targetEl, name, e);
};
});
//----------------
// MouseEvents
function createMouseEventDispatcher(type, detail) {
var cancelable = (type !== 'mousemove');
return function(targetEl, srcEvent) {
var xy = srcEvent.getXY(),
e = API.createMouseEvent(targetEl.ownerDocument, type, true, cancelable, detail, xy[0], xy[1], srcEvent.ctrlKey, srcEvent.altKey, srcEvent.shiftKey, srcEvent.metaKey, srcEvent.button, srcEvent.relatedTarget);
API.fireEvent(targetEl, type, e);
};
}
Ext.each([
'click',
'dblclick',
'mousedown',
'mouseup',
'mouseover',
'mousemove',
'mouseout'
], function(eventName) {
dispatchers[eventName] = createMouseEventDispatcher(eventName, 1);
});
//----------------
// UIEvents
Ext.Object.each({
focusin: [
true,
false
],
focusout: [
true,
false
],
activate: [
true,
true
],
focus: [
false,
false
],
blur: [
false,
false
]
}, function(name, value) {
var bubbles = value[0],
cancelable = value[1];
dispatchers[name] = function(targetEl, srcEvent) {
var e = API.createUIEvent(targetEl.ownerDocument, name, bubbles, cancelable, 1);
API.fireEvent(targetEl, name, e);
};
});
//---------
if (!API) {
// not even sure what ancient browsers fall into this category...
dispatchers = {};
// never mind all those we just built :P
API = {};
}
function cannotInject(target, srcEvent) {}
// TODO log something
return function(target) {
var me = this,
dispatcher = dispatchers[me.type] || cannotInject,
t = target ? (target.dom || target) : me.getTarget();
dispatcher(t, me);
};
}()),
// call to produce method
preventDefault: function(browserOnly) {
var me = this,
event = me.browserEvent,
parentEvent = me.parentEvent,
unselectable, target;
// This check is for IE8/9. The event object may have been
// invalidated, so we can't delve into the details of it. If so,
// just fall out gracefully and don't attempt to do anything.
if (typeof event.type !== 'unknown') {
// In some cases we want to prevent default on the browser event
// but keep propagating it through our event system. For example,
// in Checkbox selection where the cells with checkboxes should
// prevent focusing on mousedown but still fire the click event.
if (!browserOnly) {
me.defaultPrevented = true;
}
// if the event was created by prototype-chaining a new object to an existing event
// instance, we need to make sure the parent event is defaultPrevented as well.
if (parentEvent) {
parentEvent.defaultPrevented = true;
}
if (event.preventDefault) {
event.preventDefault();
} else {
// The purpose of the code below is for preventDefault to stop focus from
// occurring like it does in other modern browsers. This only happens in
// IE8/9 when using attachEvent. The use of unselectable seems the most reliable
// way to prevent this from happening. We need to use a timeout to restore the
// unselectable state because if we don't setting it has no effect. It's important
// to set the atrribute to 'on' as opposed to just setting the property on the DOM element.
// See the link below for a discussion on the issue:
// http://bugs.jquery.com/ticket/10345
if (event.type === 'mousedown') {
target = event.target;
unselectable = target.getAttribute('unselectable');
if (unselectable !== 'on') {
target.setAttribute('unselectable', 'on');
Ext.defer(function() {
target.setAttribute('unselectable', unselectable);
}, 1);
}
}
// IE9 and earlier do not support preventDefault
event.returnValue = false;
// Some keys events require setting the keyCode to -1 to be prevented
// all ctrl + X and F1 -> F12
if (event.ctrlKey || event.keyCode > 111 && event.keyCode < 124) {
event.keyCode = -1;
}
}
}
return me;
},
stopPropagation: function() {
var me = this,
event = me.browserEvent;
// This check is for IE8/9. The event object may have been
// invalidated, so we can't delve into the details of it. If so,
// just fall out gracefully and don't attempt to do anything.
if (typeof event.type !== 'unknown') {
if (me.mousedownEvents[me.type]) {
// Fire the "unstoppable" global mousedown event
// (used for menu hiding, etc)
Ext.GlobalEvents.fireMouseDown(me);
}
me.callParent();
}
return me;
},
deprecated: {
'5.0': {
methods: {
/**
* @method clone
* @member Ext.event.Event
* Clones this event.
* @return {Ext.event.Event} The cloned copy
* @deprecated 5.0.0
*/
clone: function() {
return new this.self(this.browserEvent, this);
}
}
}
}
}, function() {
var Event = this,
btnMap,
onKeyDown = function(e) {
if (e.keyCode === 9) {
Event.forwardTab = !e.shiftKey;
}
},
onKeyUp = function(e) {
if (e.keyCode === 9) {
delete Event.forwardTab;
}
};
if (Ext.isIE9m) {
btnMap = {
0: 0,
1: 0,
4: 1,
2: 2
};
Event.override({
statics: {
/**
* @member Ext.event.Event
* When events are attached using IE's attachEvent API instead of
* addEventListener accessing any members of an event object asynchronously
* results in "Member not found" error. To work around this we fabricate
* our own event object by copying all of its members to a new object.
* @param {Event} browserEvent The native browser event object
* @private
* @static
*/
enableIEAsync: function(browserEvent) {
var name,
fakeEvent = {};
for (name in browserEvent) {
fakeEvent[name] = browserEvent[name];
}
return fakeEvent;
}
},
constructor: function(event, info, touchesMap, identifiers) {
var me = this;
me.callParent([
event,
info,
touchesMap,
identifiers
]);
me.button = btnMap[event.button];
if (event.type === 'contextmenu') {
me.button = 2;
}
// IE8/9 reports click as 0, so we can at least attempt to infer here
// IE8 can throw an error when trying to access properties on a browserEvent
// object when the event has been buffered or delayed. Cache them here
// so we can access them later.
me.toElement = event.toElement;
me.fromElement = event.fromElement;
},
mouseLeaveRe: /(mouseout|mouseleave)/,
mouseEnterRe: /(mouseover|mouseenter)/,
/**
* @member Ext.event.Event
* @inheritdoc Ext.event.Event#static-enableIEAsync
* @private
*/
enableIEAsync: function(browserEvent) {
this.browserEvent = this.self.enableIEAsync(browserEvent);
},
getRelatedTarget: function(selector, maxDepth, returnEl) {
var me = this,
type, target;
if (!me.relatedTarget) {
type = me.type;
if (me.mouseLeaveRe.test(type)) {
target = me.toElement;
} else if (me.mouseEnterRe.test(type)) {
target = me.fromElement;
}
if (target) {
me.relatedTarget = me.self.resolveTextNode(target);
}
}
return me.callParent([
selector,
maxDepth,
returnEl
]);
}
});
// We place these listeners to capture Tab and Shift-Tab key strokes
// and pass this information in the focus/blur event if it happens
// between keydown/keyup pair.
document.attachEvent('onkeydown', onKeyDown);
document.attachEvent('onkeyup', onKeyUp);
window.attachEvent('onunload', function() {
document.detachEvent('onkeydown', onKeyDown);
document.detachEvent('onkeyup', onKeyUp);
});
} else if (document.addEventListener) {
document.addEventListener('keydown', onKeyDown, true);
document.addEventListener('keyup', onKeyUp, true);
}
});
Ext.define('Ext.overrides.event.publisher.Dom', {
override: 'Ext.event.publisher.Dom'
}, function(DomPublisher) {
if (Ext.isIE9m) {
var docElement = document.documentElement,
docBody = document.body,
prototype = DomPublisher.prototype,
onDirectEvent, onDirectCaptureEvent;
prototype.target = document;
prototype.directBoundListeners = {};
// This method gets bound to the element scope in addDirectListener so that
// the currentTarget can be captured using "this".
onDirectEvent = function(e, publisher, capture) {
e.target = e.srcElement || window;
e.currentTarget = this;
if (capture) {
// Although directly attached capture listeners are not supported in IE9m
// we still need to call the handler so at least the event fires.
publisher.onDirectCaptureEvent(e);
} else {
publisher.onDirectEvent(e);
}
};
onDirectCaptureEvent = function(e, publisher) {
e.target = e.srcElement || window;
e.currentTarget = this;
// this, not DomPublisher
publisher.onDirectCaptureEvent(e);
};
DomPublisher.override({
addDelegatedListener: function(eventName) {
this.delegatedListeners[eventName] = 1;
// Use attachEvent for IE9 and below. Even though IE9 strict supports
// addEventListener, it has issues with using synthetic events.
this.target.attachEvent('on' + eventName, this.onDelegatedEvent);
},
removeDelegatedListener: function(eventName) {
delete this.delegatedListeners[eventName];
this.target.detachEvent('on' + eventName, this.onDelegatedEvent);
},
addDirectListener: function(eventName, element, capture) {
var me = this,
dom = element.dom,
// binding the listener to the element allows us to capture the
// "currentTarget" (see onDirectEvent)
boundFn = Ext.Function.bind(onDirectEvent, dom, [
me,
capture
], true),
directBoundListeners = me.directBoundListeners,
handlers = directBoundListeners[eventName] || (directBoundListeners[eventName] = {});
handlers[dom.id] = boundFn;
// may be called with an SVG element here, which
// does not have the attachEvent method on IE 9 strict
if (dom.attachEvent) {
dom.attachEvent('on' + eventName, boundFn);
} else {
me.callParent([
eventName,
element,
capture
]);
}
},
removeDirectListener: function(eventName, element, capture) {
var dom = element.dom;
if (dom.detachEvent) {
dom.detachEvent('on' + eventName, this.directBoundListeners[eventName][dom.id]);
} else {
this.callParent([
eventName,
element,
capture
]);
}
},
doDelegatedEvent: function(e) {
e.target = e.srcElement || window;
if (e.type === 'focusin') {
// IE8 sometimes happen to focus <html> element instead of the body
e.relatedTarget = e.fromElement === docBody || e.fromElement === docElement ? null : e.fromElement;
} else if (e.type === 'focusout') {
e.relatedTarget = e.toElement === docBody || e.toElement === docElement ? null : e.toElement;
}
return this.callParent([
e
]);
}
});
// can't capture any events without addEventListener. Have to have direct
// listeners for every event that does not bubble.
Ext.apply(prototype.directEvents, prototype.captureEvents);
// These do not bubble in IE9m so have to attach direct listeners as well.
Ext.apply(prototype.directEvents, {
change: 1,
input: 1,
paste: 1
});
prototype.captureEvents = {};
}
});
Ext.define('Ext.overrides.event.publisher.Gesture', {
override: 'Ext.event.publisher.Gesture'
}, function() {
if (Ext.isIE9m) {
this.override({
updateTouches: function(e, isEnd) {
var browserEvent = e.browserEvent,
xy = e.getXY();
// I don't always set pageX and pageY on the event object, but when I do
// it's because the Gesture publisher expects an event object that has them.
browserEvent.pageX = xy[0];
browserEvent.pageY = xy[1];
this.callParent([
e,
isEnd
]);
},
doDelegatedEvent: function(e) {
// Workaround IE's "Member not found" errors when accessing an event
// object asynchronously. Needed for all gesture handlers because
// they use requestAnimationFrame (see enableIEAsync for more details)
this.callParent([
Ext.event.Event.enableIEAsync(e)
]);
}
});
}
});
/**
* @class Ext.dom.Element
* @override Ext.dom.Element
*/
Ext.define('Ext.overrides.dom.Element', (function() {
var Element,
// we cannot do this yet "= Ext.dom.Element"
WIN = window,
DOC = document,
HIDDEN = 'hidden',
ISCLIPPED = 'isClipped',
OVERFLOW = 'overflow',
OVERFLOWX = 'overflow-x',
OVERFLOWY = 'overflow-y',
ORIGINALCLIP = 'originalClip',
HEIGHT = 'height',
WIDTH = 'width',
VISIBILITY = 'visibility',
DISPLAY = 'display',
NONE = 'none',
OFFSETS = 'offsets',
CLIP = 'clip',
ORIGINALDISPLAY = 'originalDisplay',
VISMODE = 'visibilityMode',
ISVISIBLE = 'isVisible',
OFFSETCLASS = Ext.baseCSSPrefix + 'hidden-offsets',
CLIPCLASS = Ext.baseCSSPrefix + 'hidden-clip',
boxMarkup = [
'<div class="{0}-tl" role="presentation">',
'<div class="{0}-tr" role="presentation">',
'<div class="{0}-tc" role="presentation"></div>',
'</div>',
'</div>',
'<div class="{0}-ml" role="presentation">',
'<div class="{0}-mr" role="presentation">',
'<div class="{0}-mc" role="presentation"></div>',
'</div>',
'</div>',
'<div class="{0}-bl" role="presentation">',
'<div class="{0}-br" role="presentation">',
'<div class="{0}-bc" role="presentation"></div>',
'</div>',
'</div>'
].join(''),
scriptTagRe = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
replaceScriptTagRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
srcRe = /\ssrc=([\'\"])(.*?)\1/i,
nonSpaceRe = /\S/,
typeRe = /\stype=([\'\"])(.*?)\1/i,
msRe = /^-ms-/,
camelRe = /(-[a-z])/gi,
camelReplaceFn = function(m, a) {
return a.charAt(1).toUpperCase();
},
XMASKED = Ext.baseCSSPrefix + "masked",
XMASKEDRELATIVE = Ext.baseCSSPrefix + "masked-relative",
EXTELMASKMSG = Ext.baseCSSPrefix + "mask-msg",
bodyRe = /^body/i,
propertyCache = {},
getVisMode = function(el) {
var data = el.getData(),
visMode = data[VISMODE];
if (visMode === undefined) {
data[VISMODE] = visMode = Element.VISIBILITY;
}
return visMode;
},
emptyRange = DOC.createRange ? DOC.createRange() : null,
inputTags = {
INPUT: true,
TEXTAREA: true
};
if (Ext.isIE8) {
var garbageBin = DOC.createElement('div'),
destroyQueue = [],
// prevent memory leaks in IE8
// see http://social.msdn.microsoft.com/Forums/ie/en-US/c76967f0-dcf8-47d0-8984-8fe1282a94f5/ie-appendchildremovechild-memory-problem?forum=iewebdevelopment
// This function is called to fully destroy an element on a timer so that code following the
// remove call can still access the element.
clearGarbage = Ext.Function.createBuffered(function() {
var len = destroyQueue.length,
i;
for (i = 0; i < len; i++) {
garbageBin.appendChild(destroyQueue[i]);
}
garbageBin.innerHTML = '';
destroyQueue.length = 0;
}, 10);
}
return {
override: 'Ext.dom.Element',
mixins: [
'Ext.util.Animate'
],
uses: [
'Ext.dom.GarbageCollector',
'Ext.dom.Fly',
'Ext.event.publisher.MouseEnterLeave',
'Ext.fx.Manager',
'Ext.fx.Anim'
],
skipGarbageCollection: false,
_init: function(E) {
Element = E;
// now we can poke this into closure scope
// We want to expose destroyQueue on the prototype for testing purposes
if (WIN.__UNIT_TESTING__) {
E.destroyQueue = destroyQueue;
}
// Allow overriding the attribute name and/or selector; this is
// done only once for performance reasons
E.tabbableSelector += ',[' + E.tabbableSavedCounterAttribute + ']';
},
statics: {
selectableCls: Ext.baseCSSPrefix + 'selectable',
unselectableCls: Ext.baseCSSPrefix + 'unselectable',
// This selector will be modified at runtime in the _init() method above
// to include the elements with saved tabindex in the returned set
tabbableSelector: Ext.supports.CSS3NegationSelector ? 'a[href],button,iframe,input,select,textarea,[tabindex]:not([tabindex="-1"]),[contenteditable="true"]' : 'a[href],button,iframe,input,select,textarea,[tabindex],[contenteditable="true"]',
// Anchor and link tags are special; they are only naturally focusable (and tabbable)
// if they have href attribute, and tabbabledness is further platform/browser specific.
// Thus we check it separately in the code.
naturallyFocusableTags: {
BUTTON: true,
IFRAME: true,
EMBED: true,
INPUT: true,
OBJECT: true,
SELECT: true,
TEXTAREA: true,
HTML: Ext.isIE ? true : false,
BODY: Ext.isIE ? false : true
},
// <object> element is naturally tabbable only in IE8 and below
naturallyTabbableTags: {
BUTTON: true,
IFRAME: true,
INPUT: true,
SELECT: true,
TEXTAREA: true,
OBJECT: Ext.isIE8m ? true : false
},
tabbableSavedCounterAttribute: 'data-tabindex-counter',
tabbableSavedValueAttribute: 'data-tabindex-value',
normalize: function(prop) {
if (prop === 'float') {
prop = Ext.supports.Float ? 'cssFloat' : 'styleFloat';
}
// For '-ms-foo' we need msFoo
return propertyCache[prop] || (propertyCache[prop] = prop.replace(msRe, 'ms-').replace(camelRe, camelReplaceFn));
}
},
/**
* Convenience method for constructing a KeyMap
* @param {String/Number/Number[]/Object} key Either a string with the keys to listen for, the numeric key code,
* array of key codes or an object with the following options:
* @param {Number/Array} key.key
* @param {Boolean} key.shift
* @param {Boolean} key.ctrl
* @param {Boolean} key.alt
* @param {Function} fn The function to call
* @param {Object} [scope] The scope (`this` reference) in which the specified function is executed. Defaults to this Element.
* @return {Ext.util.KeyMap} The KeyMap created
*/
addKeyListener: function(key, fn, scope) {
var config;
if (typeof key !== 'object' || Ext.isArray(key)) {
config = {
target: this,
key: key,
fn: fn,
scope: scope
};
} else {
config = {
target: this,
key: key.key,
shift: key.shift,
ctrl: key.ctrl,
alt: key.alt,
fn: fn,
scope: scope
};
}
return new Ext.util.KeyMap(config);
},
/**
* Creates a KeyMap for this element
* @param {Object} config The KeyMap config. See {@link Ext.util.KeyMap} for more details
* @return {Ext.util.KeyMap} The KeyMap created
*/
addKeyMap: function(config) {
return new Ext.util.KeyMap(Ext.apply({
target: this
}, config));
},
/**
* @private
*/
afterAnimate: function() {
var shadow = this.shadow;
if (shadow && !shadow.disabled && !shadow.animate) {
shadow.show();
}
},
/**
* @private
*/
anchorAnimX: function(anchor) {
var xName = (anchor === 'l') ? 'right' : 'left';
this.dom.style[xName] = '0px';
},
/**
* @private
* process the passed fx configuration.
*/
anim: function(config) {
if (!Ext.isObject(config)) {
return (config) ? {} : false;
}
var me = this,
duration = config.duration || Ext.fx.Anim.prototype.duration,
easing = config.easing || 'ease',
animConfig;
if (config.stopAnimation) {
me.stopAnimation();
}
Ext.applyIf(config, Ext.fx.Manager.getFxDefaults(me.id));
// Clear any 'paused' defaults.
Ext.fx.Manager.setFxDefaults(me.id, {
delay: 0
});
animConfig = {
// Pass the DOM reference. That's tested first so will be converted to an Ext.fx.Target fastest.
target: me.dom,
remove: config.remove,
alternate: config.alternate || false,
duration: duration,
easing: easing,
callback: config.callback,
listeners: config.listeners,
iterations: config.iterations || 1,
scope: config.scope,
block: config.block,
concurrent: config.concurrent,
delay: config.delay || 0,
paused: true,
keyframes: config.keyframes,
from: config.from || {},
to: Ext.apply({}, config),
userConfig: config
};
Ext.apply(animConfig.to, config.to);
// Anim API properties - backward compat
delete animConfig.to.to;
delete animConfig.to.from;
delete animConfig.to.remove;
delete animConfig.to.alternate;
delete animConfig.to.keyframes;
delete animConfig.to.iterations;
delete animConfig.to.listeners;
delete animConfig.to.target;
delete animConfig.to.paused;
delete animConfig.to.callback;
delete animConfig.to.scope;
delete animConfig.to.duration;
delete animConfig.to.easing;
delete animConfig.to.concurrent;
delete animConfig.to.block;
delete animConfig.to.stopAnimation;
delete animConfig.to.delay;
return animConfig;
},
/**
* Calls `{@link #addAnimation}` and returns this Element (for call chaining). For
* details, see `{@link #addAnimation}`.
*
* @param {Object} config Configuration for {@link Ext.fx.Anim}.
* Note that the {@link Ext.fx.Anim#to to} config is required.
* @return {Ext.dom.Element} this
*/
animate: function(config) {
this.addAnimation(config);
return this;
},
/**
* Starts a custom animation on this Element.
*
* The following properties may be specified in `from`, `to`, and `keyframe` objects:
*
* - `x` - The page X position in pixels.
* - `y` - The page Y position in pixels
* - `left` - The element's CSS `left` value. Units must be supplied.
* - `top` - The element's CSS `top` value. Units must be supplied.
* - `width` - The element's CSS `width` value. Units must be supplied.
* - `height` - The element's CSS `height` value. Units must be supplied.
* - `scrollLeft` - The element's `scrollLeft` value.
* - `scrollTop` - The element's `scrollTop` value.
* - `opacity` - The element's `opacity` value (between `0` and `1`).
*
* **Be aware** that animating an Element which is being used by an Ext Component
* without in some way informing the Component about the changed element state will
* result in incorrect Component behaviour. This is because the Component will be
* using the old state of the element. To avoid this problem, it is now possible
* to directly animate certain properties of Components.
*
* @param {Object} config Configuration for {@link Ext.fx.Anim}.
* Note that the {@link Ext.fx.Anim#to to} config is required.
* @return {Ext.fx.Anim} The new animation.
*/
addAnimation: function(config) {
var me = this,
animId = me.dom.id || Ext.id(me.dom),
listeners, anim, end;
if (!Ext.fx.Manager.hasFxBlock(animId)) {
// Bit of gymnastics here to ensure our internal listeners get bound first
if (config.listeners) {
listeners = config.listeners;
delete config.listeners;
}
if (config.internalListeners) {
config.listeners = config.internalListeners;
delete config.internalListeners;
}
end = config.autoEnd;
delete config.autoEnd;
anim = new Ext.fx.Anim(me.anim(config));
anim.on({
afteranimate: 'afterAnimate',
beforeanimate: 'beforeAnimate',
scope: me,
single: true
});
if (listeners) {
anim.on(listeners);
}
Ext.fx.Manager.queueFx(anim);
if (end) {
anim.jumpToEnd();
}
}
return anim;
},
/**
* @private
*/
beforeAnimate: function() {
var shadow = this.shadow;
if (shadow && !shadow.disabled && !shadow.animate) {
shadow.hide();
}
},
/**
* Wraps the specified element with a special 9 element markup/CSS block that renders by default as
* a gray container with a gradient background, rounded corners and a 4-way shadow.
*
* This special markup is used throughout Ext when box wrapping elements ({@link Ext.button.Button},
* {@link Ext.panel.Panel} when {@link Ext.panel.Panel#frame frame=true}, {@link Ext.window.Window}).
* The markup is of this form:
*
* <div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div>
* <div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div>
* <div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>
*
* Example usage:
*
* // Basic box wrap
* Ext.get("foo").boxWrap();
*
* // You can also add a custom class and use CSS inheritance rules to customize the box look.
* // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example
* // for how to create a custom box wrap style.
* Ext.get("foo").boxWrap().addCls("x-box-blue");
*
* @param {String} [class='x-box'] A base CSS class to apply to the containing wrapper element.
* Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
* so if you supply an alternate base class, make sure you also supply all of the necessary rules.
* @return {Ext.dom.Element} The outermost wrapping element of the created box structure.
*/
boxWrap: function(cls) {
cls = cls || Ext.baseCSSPrefix + 'box';
var el = Ext.get(this.insertHtml("beforeBegin", "<div class='" + cls + "' role='presentation'>" + Ext.String.format(boxMarkup, cls) + "</div>"));
el.selectNode('.' + cls + '-mc').appendChild(this.dom);
return el;
},
/**
* Removes Empty, or whitespace filled text nodes. Combines adjacent text nodes.
* @param {Boolean} [forceReclean=false] By default the element keeps track if it has been cleaned already
* so you can call this over and over. However, if you update the element and need to force a re-clean, you
* can pass true.
*/
clean: function(forceReclean) {
var me = this,
dom = me.dom,
data = me.getData(),
n = dom.firstChild,
ni = -1,
nx;
if (data.isCleaned && forceReclean !== true) {
return me;
}
while (n) {
nx = n.nextSibling;
if (n.nodeType === 3) {
// Remove empty/whitespace text nodes
if (!(nonSpaceRe.test(n.nodeValue))) {
dom.removeChild(n);
}
// Combine adjacent text nodes
else if (nx && nx.nodeType === 3) {
n.appendData(Ext.String.trim(nx.data));
dom.removeChild(nx);
nx = n.nextSibling;
n.nodeIndex = ++ni;
}
} else {
// Recursively clean
Ext.fly(n, '_clean').clean();
n.nodeIndex = ++ni;
}
n = nx;
}
data.isCleaned = true;
return me;
},
/**
* Empties this element. Removes all child nodes.
*/
empty: emptyRange ? function() {
var dom = this.dom;
if (dom.firstChild) {
emptyRange.setStartBefore(dom.firstChild);
emptyRange.setEndAfter(dom.lastChild);
emptyRange.deleteContents();
}
} : function() {
var dom = this.dom;
while (dom.lastChild) {
dom.removeChild(dom.lastChild);
}
},
clearListeners: function() {
this.removeAnchor();
this.callParent();
},
/**
* Clears positioning back to the default when the document was loaded.
* @param {String} [value=''] The value to use for the left, right, top, bottom.
* You could use 'auto'.
* @return {Ext.dom.Element} this
*/
clearPositioning: function(value) {
value = value || '';
return this.setStyle({
left: value,
right: value,
top: value,
bottom: value,
'z-index': '',
position: 'static'
});
},
/**
* Creates a proxy element of this element
* @param {String/Object} config The class name of the proxy element or a DomHelper config object
* @param {String/HTMLElement} [renderTo] The element or element id to render the proxy to. Defaults to: document.body.
* @param {Boolean} [matchBox=false] True to align and size the proxy to this element now.
* @return {Ext.dom.Element} The new proxy element
*/
createProxy: function(config, renderTo, matchBox) {
config = (typeof config === 'object') ? config : {
tag: "div",
role: 'presentation',
cls: config
};
var me = this,
proxy = renderTo ? Ext.DomHelper.append(renderTo, config, true) : Ext.DomHelper.insertBefore(me.dom, config, true);
proxy.setVisibilityMode(Element.DISPLAY);
proxy.hide();
if (matchBox && me.setBox && me.getBox) {
// check to make sure Element_position.js is loaded
proxy.setBox(me.getBox());
}
return proxy;
},
/**
* Clears any opacity settings from this element. Required in some cases for IE.
* @return {Ext.dom.Element} this
*/
clearOpacity: function() {
return this.setOpacity('');
},
/**
* Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
* @return {Ext.dom.Element} this
*/
clip: function() {
var me = this,
data = me.getData(),
style;
if (!data[ISCLIPPED]) {
data[ISCLIPPED] = true;
style = me.getStyle([
OVERFLOW,
OVERFLOWX,
OVERFLOWY
]);
data[ORIGINALCLIP] = {
o: style[OVERFLOW],
x: style[OVERFLOWX],
y: style[OVERFLOWY]
};
me.setStyle(OVERFLOW, HIDDEN);
me.setStyle(OVERFLOWX, HIDDEN);
me.setStyle(OVERFLOWY, HIDDEN);
}
return me;
},
destroy: function() {
var me = this,
dom = me.dom,
data = me.getData(),
maskEl, maskMsg;
if (dom) {
if (me.isAnimate) {
me.stopAnimation();
}
me.removeAnchor();
}
me.callParent();
// prevent memory leaks in IE8
// see http://social.msdn.microsoft.com/Forums/ie/en-US/c76967f0-dcf8-47d0-8984-8fe1282a94f5/ie-appendchildremovechild-memory-problem?forum=iewebdevelopment
// must not be document, documentElement, body or window object
// Have to use != instead of !== for IE8 or it will not recognize that the window
// objects are equal
if (dom && Ext.isIE8 && (dom.window != dom) && (dom.nodeType !== 9) && (dom.tagName !== 'BODY') && (dom.tagName !== 'HTML')) {
destroyQueue[destroyQueue.length] = dom;
// Will perform extra IE8 cleanup in 10 milliseconds
// see http://social.msdn.microsoft.com/Forums/ie/en-US/c76967f0-dcf8-47d0-8984-8fe1282a94f5/ie-appendchildremovechild-memory-problem?forum=iewebdevelopment
clearGarbage();
}
if (data) {
maskEl = data.maskEl;
maskMsg = data.maskMsg;
if (maskEl) {
maskEl.destroy();
}
if (maskMsg) {
maskMsg.destroy();
}
}
},
/**
* Convenience method for setVisibilityMode(Element.DISPLAY).
* @param {String} [display] What to set display to when visible
* @return {Ext.dom.Element} this
*/
enableDisplayMode: function(display) {
var me = this;
me.setVisibilityMode(Element.DISPLAY);
if (display !== undefined) {
me.getData()[ORIGINALDISPLAY] = display;
}
return me;
},
/**
* Fade an element in (from transparent to opaque). The ending opacity can be specified using the `opacity`
* config option. Usage:
*
* // default: fade in from opacity 0 to 100%
* el.fadeIn();
*
* // custom: fade in from opacity 0 to 75% over 2 seconds
* el.fadeIn({ opacity: .75, duration: 2000});
*
* // common config options shown with default values
* el.fadeIn({
* opacity: 1, //can be any value between 0 and 1 (e.g. .5)
* easing: 'easeOut',
* duration: 500
* });
*
* @param {Object} options (optional) Object literal with any of the {@link Ext.fx.Anim} config options
* @return {Ext.dom.Element} The Element
*/
fadeIn: function(o) {
var me = this,
dom = me.dom;
me.animate(Ext.apply({}, o, {
opacity: 1,
internalListeners: {
beforeanimate: function(anim) {
// restore any visibility/display that may have
// been applied by a fadeout animation
var el = Ext.fly(dom, '_anim');
if (el.isStyle('display', 'none')) {
el.setDisplayed('');
} else {
el.show();
}
}
}
}));
return this;
},
/**
* Fade an element out (from opaque to transparent). The ending opacity can be specified using the `opacity`
* config option. Note that IE may require `useDisplay:true` in order to redisplay correctly.
* Usage:
*
* // default: fade out from the element's current opacity to 0
* el.fadeOut();
*
* // custom: fade out from the element's current opacity to 25% over 2 seconds
* el.fadeOut({ opacity: .25, duration: 2000});
*
* // common config options shown with default values
* el.fadeOut({
* opacity: 0, //can be any value between 0 and 1 (e.g. .5)
* easing: 'easeOut',
* duration: 500,
* remove: false,
* useDisplay: false
* });
*
* @param {Object} options (optional) Object literal with any of the {@link Ext.fx.Anim} config options
* @return {Ext.dom.Element} The Element
*/
fadeOut: function(o) {
var me = this,
dom = me.dom;
o = Ext.apply({
opacity: 0,
internalListeners: {
afteranimate: function(anim) {
if (dom && anim.to.opacity === 0) {
var el = Ext.fly(dom, '_anim');
if (o.useDisplay) {
el.setDisplayed(false);
} else {
el.hide();
}
}
}
}
}, o);
me.animate(o);
return me;
},
/**
* @private
*/
fixDisplay: function() {
var me = this;
if (me.isStyle(DISPLAY, NONE)) {
me.setStyle(VISIBILITY, HIDDEN);
me.setStyle(DISPLAY, me._getDisplay());
// first try reverting to default
if (me.isStyle(DISPLAY, NONE)) {
// if that fails, default to block
me.setStyle(DISPLAY, "block");
}
}
},
/**
* Shows a ripple of exploding, attenuating borders to draw attention to an Element. Usage:
*
* // default: a single light blue ripple
* el.frame();
*
* // custom: 3 red ripples lasting 3 seconds total
* el.frame("#ff0000", 3, { duration: 3000 });
*
* // common config options shown with default values
* el.frame("#C3DAF9", 1, {
* duration: 1000 // duration of each individual ripple.
* // Note: Easing is not configurable and will be ignored if included
* });
*
* @param {String} [color='#C3DAF9'] The hex color value for the border.
* @param {Number} [count=1] The number of ripples to display.
* @param {Object} [options] Object literal with any of the {@link Ext.fx.Anim} config options
* @return {Ext.dom.Element} The Element
*/
frame: function(color, count, obj) {
var me = this,
dom = me.dom,
beforeAnim;
color = color || '#C3DAF9';
count = count || 1;
obj = obj || {};
beforeAnim = function() {
var el = Ext.fly(dom, '_anim'),
animScope = this,
box, proxy, proxyAnim;
el.show();
box = el.getBox();
proxy = Ext.getBody().createChild({
role: 'presentation',
id: el.dom.id + '-anim-proxy',
style: {
position: 'absolute',
'pointer-events': 'none',
'z-index': 35000,
border: '0px solid ' + color
}
});
proxyAnim = new Ext.fx.Anim({
target: proxy,
duration: obj.duration || 1000,
iterations: count,
from: {
top: box.y,
left: box.x,
borderWidth: 0,
opacity: 1,
height: box.height,
width: box.width
},
to: {
top: box.y - 20,
left: box.x - 20,
borderWidth: 10,
opacity: 0,
height: box.height + 40,
width: box.width + 40
}
});
proxyAnim.on('afteranimate', function() {
proxy.destroy();
// kill the no-op element animation created below
animScope.end();
});
};
me.animate({
// See "A Note About Wrapped Animations" at the top of this class:
duration: (Math.max(obj.duration, 500) * 2) || 2000,
listeners: {
beforeanimate: {
fn: beforeAnim
}
},
callback: obj.callback,
scope: obj.scope
});
return me;
},
/**
* Return the CSS color for the specified CSS attribute. rgb, 3 digit (like `#fff`)
* and valid values are convert to standard 6 digit hex color.
* @param {String} attr The css attribute
* @param {String} defaultValue The default value to use when a valid color isn't found
* @param {String} [prefix] defaults to #. Use an empty string when working with
* color anims.
* @private
*/
getColor: function(attr, defaultValue, prefix) {
var v = this.getStyle(attr),
color = prefix || prefix === '' ? prefix : '#',
h, len,
i = 0;
if (!v || (/transparent|inherit/.test(v))) {
return defaultValue;
}
if (/^r/.test(v)) {
v = v.slice(4, v.length - 1).split(',');
len = v.length;
for (; i < len; i++) {
h = parseInt(v[i], 10);
color += (h < 16 ? '0' : '') + h.toString(16);
}
} else {
v = v.replace('#', '');
color += v.length === 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;
}
return (color.length > 5 ? color.toLowerCase() : defaultValue);
},
/**
* Gets this element's {@link Ext.ElementLoader ElementLoader}
* @return {Ext.ElementLoader} The loader
*/
getLoader: function() {
var me = this,
data = me.getData(),
loader = data.loader;
if (!loader) {
data.loader = loader = new Ext.ElementLoader({
target: me
});
}
return loader;
},
/**
* Gets an object with all CSS positioning properties. Useful along with
* `setPostioning` to get snapshot before performing an update and then restoring
* the element.
* @param {Boolean} [autoPx=false] true to return pixel values for "auto" styles.
* @return {Object}
*/
getPositioning: function(autoPx) {
var styles = this.getStyle([
'left',
'top',
'position',
'z-index'
]),
dom = this.dom;
if (autoPx) {
if (styles.left === 'auto') {
styles.left = dom.offsetLeft + 'px';
}
if (styles.top === 'auto') {
styles.top = dom.offsetTop + 'px';
}
}
return styles;
},
/**
* Slides the element while fading it out of view. An anchor point can be optionally passed to set the ending point
* of the effect. Usage:
*
* // default: slide the element downward while fading out
* el.ghost();
*
* // custom: slide the element out to the right with a 2-second duration
* el.ghost('r', { duration: 2000 });
*
* // common config options shown with default values
* el.ghost('b', {
* easing: 'easeOut',
* duration: 500
* });
*
* @param {String} anchor (optional) One of the valid {@link Ext.fx.Anim} anchor positions (defaults to bottom: 'b')
* @param {Object} options (optional) Object literal with any of the {@link Ext.fx.Anim} config options
* @return {Ext.dom.Element} The Element
*/
ghost: function(anchor, obj) {
var me = this,
dom = me.dom,
beforeAnim;
anchor = anchor || "b";
beforeAnim = function() {
var el = Ext.fly(dom, '_anim'),
width = el.getWidth(),
height = el.getHeight(),
xy = el.getXY(),
position = el.getPositioning(),
to = {
opacity: 0
};
switch (anchor) {
case 't':
to.y = xy[1] - height;
break;
case 'l':
to.x = xy[0] - width;
break;
case 'r':
to.x = xy[0] + width;
break;
case 'b':
to.y = xy[1] + height;
break;
case 'tl':
to.x = xy[0] - width;
to.y = xy[1] - height;
break;
case 'bl':
to.x = xy[0] - width;
to.y = xy[1] + height;
break;
case 'br':
to.x = xy[0] + width;
to.y = xy[1] + height;
break;
case 'tr':
to.x = xy[0] + width;
to.y = xy[1] - height;
break;
}
this.to = to;
this.on('afteranimate', function() {
var el = Ext.fly(dom, '_anim');
if (el) {
el.hide();
el.clearOpacity();
el.setPositioning(position);
}
});
};
me.animate(Ext.applyIf(obj || {}, {
duration: 500,
easing: 'ease-out',
listeners: {
beforeanimate: beforeAnim
}
}));
return me;
},
/**
* @override
* Hide this element - Uses display mode to determine whether to use "display",
* "visibility", "offsets", or "clip". See {@link #setVisible}.
* @param {Boolean/Object} [animate] true for the default animation or a standard
* Element animation config object
* @return {Ext.dom.Element} this
*/
hide: function(animate) {
// hideMode override
if (typeof animate === 'string') {
this.setVisible(false, animate);
return this;
}
this.setVisible(false, this.anim(animate));
return this;
},
/**
* Highlights the Element by setting a color (applies to the background-color by default, but can be changed using
* the "attr" config option) and then fading back to the original color. If no original color is available, you
* should provide the "endColor" config option which will be cleared after the animation. Usage:
*
* // default: highlight background to yellow
* el.highlight();
*
* // custom: highlight foreground text to blue for 2 seconds
* el.highlight("0000ff", { attr: 'color', duration: 2000 });
*
* // common config options shown with default values
* el.highlight("ffff9c", {
* attr: "backgroundColor", //can be any valid CSS property (attribute) that supports a color value
* endColor: (current color) or "ffffff",
* easing: 'easeIn',
* duration: 1000
* });
*
* @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading #
* (defaults to yellow: 'ffff9c')
* @param {Object} options (optional) Object literal with any of the {@link Ext.fx.Anim} config options
* @return {Ext.dom.Element} The Element
*/
highlight: function(color, o) {
var me = this,
dom = me.dom,
from = {},
restore, to, attr, lns, event, fn;
o = o || {};
lns = o.listeners || {};
attr = o.attr || 'backgroundColor';
from[attr] = color || 'ffff9c';
if (!o.to) {
to = {};
to[attr] = o.endColor || me.getColor(attr, 'ffffff', '');
} else {
to = o.to;
}
// Don't apply directly on lns, since we reference it in our own callbacks below
o.listeners = Ext.apply(Ext.apply({}, lns), {
beforeanimate: function() {
restore = dom.style[attr];
var el = Ext.fly(dom, '_anim');
el.clearOpacity();
el.show();
event = lns.beforeanimate;
if (event) {
fn = event.fn || event;
return fn.apply(event.scope || lns.scope || WIN, arguments);
}
},
afteranimate: function() {
if (dom) {
dom.style[attr] = restore;
}
event = lns.afteranimate;
if (event) {
fn = event.fn || event;
fn.apply(event.scope || lns.scope || WIN, arguments);
}
}
});
me.animate(Ext.apply({}, o, {
duration: 1000,
easing: 'ease-in',
from: from,
to: to
}));
return me;
},
/**
* Sets up event handlers to call the passed functions when the mouse is moved into and out of the Element.
* @param {Function} overFn The function to call when the mouse enters the Element.
* @param {Function} outFn The function to call when the mouse leaves the Element.
* @param {Object} [scope] The scope (`this` reference) in which the functions are executed. Defaults
* to the Element's DOM element.
* @param {Object} [options] Options for the listener. See {@link Ext.util.Observable#addListener the
* options parameter}.
* @return {Ext.dom.Element} this
*/
hover: function(overFn, outFn, scope, options) {
var me = this;
me.on('mouseenter', overFn, scope || me.dom, options);
me.on('mouseleave', outFn, scope || me.dom, options);
return me;
},
/**
* Initializes a {@link Ext.dd.DD} drag drop object for this element.
* @param {String} group The group the DD object is member of
* @param {Object} config The DD config object
* @param {Object} overrides An object containing methods to override/implement on the DD object
* @return {Ext.dd.DD} The DD object
*/
initDD: function(group, config, overrides) {
var dd = new Ext.dd.DD(Ext.id(this.dom), group, config);
return Ext.apply(dd, overrides);
},
/**
* Initializes a {@link Ext.dd.DDProxy} object for this element.
* @param {String} group The group the DDProxy object is member of
* @param {Object} config The DDProxy config object
* @param {Object} overrides An object containing methods to override/implement on the DDProxy object
* @return {Ext.dd.DDProxy} The DDProxy object
*/
initDDProxy: function(group, config, overrides) {
var dd = new Ext.dd.DDProxy(Ext.id(this.dom), group, config);
return Ext.apply(dd, overrides);
},
/**
* Initializes a {@link Ext.dd.DDTarget} object for this element.
* @param {String} group The group the DDTarget object is member of
* @param {Object} config The DDTarget config object
* @param {Object} overrides An object containing methods to override/implement on the DDTarget object
* @return {Ext.dd.DDTarget} The DDTarget object
*/
initDDTarget: function(group, config, overrides) {
var dd = new Ext.dd.DDTarget(Ext.id(this.dom), group, config);
return Ext.apply(dd, overrides);
},
/**
* Checks whether this element can be focused programmatically or by clicking.
* To check if an element is in the document tab flow, use {@link #isTabbable}.
*
* @return {Boolean} True if the element is focusable
*/
isFocusable: function() {
var dom = this.dom,
focusable = false,
nodeName;
if (dom && !dom.disabled) {
nodeName = dom.nodeName;
/*
* An element is focusable if:
* - It is naturally focusable, or
* - It is an anchor or link with href attribute, or
* - It has a tabIndex, or
* - It is an editing host (contenteditable="true")
*
* Also note that we can't check dom.tabIndex because IE will return 0
* for elements that have no tabIndex attribute defined, regardless of
* whether they are naturally focusable or not.
*/
focusable = !!Ext.Element.naturallyFocusableTags[nodeName] || ((nodeName === 'A' || nodeName === 'LINK') && !!dom.href) || dom.getAttribute('tabIndex') != null || dom.contentEditable === 'true';
// In IE8, <input type="hidden"> does not have a corresponding style
// so isVisible() will assume that it's not hidden.
if (Ext.isIE8 && nodeName === 'INPUT' && dom.type === 'hidden') {
focusable = false;
}
// Invisible elements cannot be focused, so check that as well
focusable = focusable && this.isVisible(true);
}
return focusable;
},
/**
* Returns `true` if this Element is an input field, or is editable in any way.
* @return {Boolean} `true` if this Element is an input field, or is editable in any way.
*/
isInputField: function() {
var dom = this.dom,
contentEditable = dom.contentEditable;
// contentEditable will default to inherit if not specified, only check if the
// attribute has been set or explicitly set to true
// http://html5doctor.com/the-contenteditable-attribute/
// Also skip <input> tags of type="button", we use them for checkboxes
// and radio buttons
if ((inputTags[dom.tagName] && dom.type !== 'button') || (contentEditable === '' || contentEditable === 'true')) {
return true;
}
return false;
},
/**
* Checks whether this element participates in the sequential focus navigation,
* and can be reached by using Tab key.
*
* @param {Boolean} [includeHidden=false] pass `true` if hidden, or unattached elements should be returned.
* @return {Boolean} True if the element is tabbable.
*/
isTabbable: function(includeHidden) {
var dom = this.dom,
tabbable = false,
nodeName, hasIndex, tabIndex;
if (dom && !dom.disabled) {
nodeName = dom.nodeName;
// Can't use dom.tabIndex here because IE will return 0 for elements
// that have no tabindex attribute defined, regardless of whether they are
// naturally tabbable or not.
tabIndex = dom.getAttribute('tabIndex');
hasIndex = tabIndex != null;
tabIndex -= 0;
// Anchors and links are only naturally tabbable if they have href attribute
// See http://www.w3.org/TR/html5/editing.html#specially-focusable
if (nodeName === 'A' || nodeName === 'LINK') {
if (dom.href) {
// It is also possible to make an anchor untabbable by setting
// tabIndex < 0 on it
tabbable = hasIndex && tabIndex < 0 ? false : true;
} else // Anchor w/o href is tabbable if it has tabIndex >= 0,
// or if it's editable
{
if (dom.contentEditable === 'true') {
tabbable = !hasIndex || (hasIndex && tabIndex >= 0) ? true : false;
} else {
tabbable = hasIndex && tabIndex >= 0 ? true : false;
}
}
}
// If an element has contenteditable="true" or is naturally tabbable,
// then it is a potential candidate unless its tabIndex is < 0.
else if (dom.contentEditable === 'true' || Ext.Element.naturallyTabbableTags[nodeName]) {
tabbable = hasIndex && tabIndex < 0 ? false : true;
} else // That leaves non-editable elements that can only be made tabbable
// by slapping tabIndex >= 0 on them
{
if (hasIndex && tabIndex >= 0) {
tabbable = true;
}
}
// In IE8, <input type="hidden"> does not have a corresponding style
// so isVisible() will assume that it's not hidden.
if (Ext.isIE8 && nodeName === 'INPUT' && dom.type === 'hidden') {
tabbable = false;
}
// Invisible elements can't be tabbed into. If we have a component ref
// we'll also check if the component itself is visible before incurring
// the expense of DOM style reads.
// Allow caller to specify that hiddens should be included.
tabbable = tabbable && (includeHidden || ((!this.component || this.component.isVisible(true)) && this.isVisible(true)));
}
return tabbable;
},
/**
* Returns true if this element is masked. Also re-centers any displayed message
* within the mask.
*
* @param {Boolean} [deep] Go up the DOM hierarchy to determine if any parent
* element is masked.
*
* @return {Boolean}
*/
isMasked: function(deep) {
var me = this,
data = me.getData(),
maskEl = data.maskEl,
maskMsg = data.maskMsg,
hasMask = false,
parent;
if (maskEl && maskEl.isVisible()) {
if (maskMsg) {
maskMsg.center(me);
}
hasMask = true;
} else if (deep) {
parent = me.findParentNode();
if (parent) {
return Ext.fly(parent).isMasked(deep);
}
}
return hasMask;
},
/**
* Direct access to the Ext.ElementLoader {@link Ext.ElementLoader#method-load} method.
* The method takes the same object parameter as {@link Ext.ElementLoader#method-load}
* @param {Object} options a options object for Ext.ElementLoader {@link Ext.ElementLoader#method-load}
* @return {Ext.dom.Element} this
*/
load: function(options) {
this.getLoader().load(options);
return this;
},
/**
* Puts a mask over this element to disable user interaction.
* This method can only be applied to elements which accept child nodes. Use
* {@link #unmask} to remove the mask.
*
* @param {String} [msg] A message to display in the mask
* @param {String} [msgCls] A css class to apply to the msg element
* @return {Ext.dom.Element} The mask element
*/
mask: function(msg, msgCls, /* private - passed by AbstractComponent.mask to avoid the need to interrogate the DOM to get the height*/
elHeight) {
var me = this,
dom = me.dom,
data = me.getData(),
maskEl = data.maskEl,
maskMsg;
if (!(bodyRe.test(dom.tagName) && me.getStyle('position') === 'static')) {
me.addCls(XMASKEDRELATIVE);
}
// We always needs to recreate the mask since the DOM element may have been re-created
if (maskEl) {
maskEl.destroy();
}
maskEl = Ext.DomHelper.append(dom, {
role: 'presentation',
cls: Ext.baseCSSPrefix + "mask " + Ext.baseCSSPrefix + "border-box",
children: {
role: 'presentation',
cls: msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG,
cn: {
tag: 'div',
role: 'presentation',
cls: Ext.baseCSSPrefix + 'mask-msg-inner',
cn: {
tag: 'div',
role: 'presentation',
cls: Ext.baseCSSPrefix + 'mask-msg-text',
html: msg || ''
}
}
}
}, true);
maskMsg = Ext.get(maskEl.dom.firstChild);
data.maskEl = maskEl;
me.addCls(XMASKED);
maskEl.setDisplayed(true);
if (typeof msg === 'string') {
maskMsg.setDisplayed(true);
maskMsg.center(me);
} else {
maskMsg.setDisplayed(false);
}
if (dom === DOC.body) {
maskEl.addCls(Ext.baseCSSPrefix + 'mask-fixed');
}
// When masking the body, don't touch its tabbable state
me.saveTabbableState({
skipSelf: dom === DOC.body
});
// ie will not expand full height automatically
if (Ext.isIE9m && dom !== DOC.body && me.isStyle('height', 'auto')) {
maskEl.setSize(undefined, elHeight || me.getHeight());
}
return maskEl;
},
/**
* Fades the element out while slowly expanding it in all directions. When the effect is completed, the element will
* be hidden (visibility = 'hidden') but block elements will still take up space in the document. Usage:
*
* // default
* el.puff();
*
* // common config options shown with default values
* el.puff({
* easing: 'easeOut',
* duration: 500,
* useDisplay: false
* });
*
* @param {Object} options (optional) Object literal with any of the {@link Ext.fx.Anim} config options
* @return {Ext.dom.Element} The Element
*/
puff: function(obj) {
var me = this,
dom = me.dom,
beforeAnim,
box = me.getBox(),
originalStyles = me.getStyle([
'width',
'height',
'left',
'right',
'top',
'bottom',
'position',
'z-index',
'font-size',
'opacity'
], true);
obj = Ext.applyIf(obj || {}, {
easing: 'ease-out',
duration: 500,
useDisplay: false
});
beforeAnim = function() {
var el = Ext.fly(dom, '_anim');
el.clearOpacity();
el.show();
this.to = {
width: box.width * 2,
height: box.height * 2,
x: box.x - (box.width / 2),
y: box.y - (box.height / 2),
opacity: 0,
fontSize: '200%'
};
this.on('afteranimate', function() {
var el = Ext.fly(dom, '_anim');
if (el) {
if (obj.useDisplay) {
el.setDisplayed(false);
} else {
el.hide();
}
el.setStyle(originalStyles);
Ext.callback(obj.callback, obj.scope);
}
});
};
me.animate({
duration: obj.duration,
easing: obj.easing,
listeners: {
beforeanimate: {
fn: beforeAnim
}
}
});
return me;
},
/**
* Enable text selection for this element (normalized across browsers)
* @return {Ext.dom.Element} this
*/
selectable: function() {
var me = this;
// We clear this property for all browsers, not just Opera. This is so that rendering templates don't need to
// condition on Opera when making elements unselectable.
me.dom.unselectable = '';
me.removeCls(Element.unselectableCls);
me.addCls(Element.selectableCls);
return me;
},
// private
// used to ensure the mouseup event is captured if it occurs outside of the
// window in IE9m. The only reason this method exists, (vs just calling
// el.dom.setCapture() directly) is so that we can override it to emptyFn
// during testing because setCapture() can wreak havoc on emulated mouse events
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms646262(v=vs.85).aspx
setCapture: function() {
var dom = this.dom;
if (Ext.isIE9m && dom.setCapture) {
dom.setCapture();
}
},
/**
* Set the height of this Element.
*
* // change the height to 200px and animate with default configuration
* Ext.fly('elementId').setHeight(200, true);
*
* // change the height to 150px and animate with a custom configuration
* Ext.fly('elId').setHeight(150, {
* duration : 500, // animation will have a duration of .5 seconds
* // will change the content to "finished"
* callback: function(){ this.setHtml("finished"); }
* });
*
* @param {Number/String} height The new height. This may be one of:
*
* - A Number specifying the new height in pixels.
* - A String used to set the CSS height style. Animation may **not** be used.
*
* @param {Boolean/Object} [animate] a standard Element animation config object or `true` for
* the default animation (`{duration: 350, easing: 'ease-in'}`)
* @return {Ext.dom.Element} this
*/
setHeight: function(height, animate) {
var me = this;
if (!animate || !me.anim) {
me.callParent(arguments);
} else {
if (!Ext.isObject(animate)) {
animate = {};
}
me.animate(Ext.applyIf({
to: {
height: height
}
}, animate));
}
return me;
},
/**
* Removes "vertical" state from this element (reverses everything done
* by {@link #setVertical}).
* @private
*/
setHorizontal: function() {
var me = this,
cls = me.verticalCls;
delete me.vertical;
if (cls) {
delete me.verticalCls;
me.removeCls(cls);
}
// delete the inverted methods and revert to inheriting from the prototype
delete me.setWidth;
delete me.setHeight;
if (!Ext.isIE8) {
delete me.getWidth;
delete me.getHeight;
}
// revert to inheriting styleHooks from the prototype
delete me.styleHooks;
},
/**
* Updates the *text* value of this element.
* Replaces the content of this element with a *single text node* containing the passed text.
* @param {String} text The text to display in this Element.
*/
updateText: function(text) {
var me = this,
dom, textNode;
if (dom) {
textNode = dom.firstChild;
if (!textNode || (textNode.nodeType !== 3 || textNode.nextSibling)) {
textNode = DOC.createTextNode();
me.empty();
dom.appendChild(textNode);
}
if (text) {
textNode.data = text;
}
}
},
/**
* Updates the innerHTML of this element, optionally searching for and processing scripts.
* @param {String} html The new HTML
* @param {Boolean} [loadScripts] Pass `true` to look for and process scripts.
* @param {Function} [callback] For async script loading you can be notified when the update completes.
* @param {Object} [scope=`this`] The scope (`this` reference) in which to execute the callback.
*
* Also used as the scope for any *inline* script source if the `loadScripts` parameter is `true`.
* Scripts with a `src` attribute cannot be executed in this scope.
*
* Defaults to this Element.
* @return {Ext.dom.Element} this
*/
setHtml: function(html, loadScripts, callback, scope) {
var me = this,
id, dom, interval;
if (!me.dom) {
return me;
}
html = html || '';
dom = me.dom;
if (loadScripts !== true) {
dom.innerHTML = html;
Ext.callback(callback, me);
return me;
}
id = Ext.id();
html += '<span id="' + id + '" role="presentation"></span>';
interval = Ext.interval(function() {
var hd, match, attrs, srcMatch, typeMatch, el, s;
if (!(el = DOC.getElementById(id))) {
return false;
}
clearInterval(interval);
Ext.removeNode(el);
hd = Ext.getHead().dom;
while ((match = scriptTagRe.exec(html))) {
attrs = match[1];
srcMatch = attrs ? attrs.match(srcRe) : false;
if (srcMatch && srcMatch[2]) {
s = DOC.createElement("script");
s.src = srcMatch[2];
typeMatch = attrs.match(typeRe);
if (typeMatch && typeMatch[2]) {
s.type = typeMatch[2];
}
hd.appendChild(s);
} else if (match[2] && match[2].length > 0) {
if (scope) {
Ext.functionFactory(match[2]).call(scope);
} else {
Ext.globalEval(match[2]);
}
}
}
Ext.callback(callback, scope || me);
}, 20);
dom.innerHTML = html.replace(replaceScriptTagRe, '');
return me;
},
/**
* Set the opacity of the element
* @param {Number} opacity The new opacity. 0 = transparent, .5 = 50% visible, 1 = fully visible, etc
* @param {Boolean/Object} [animate] a standard Element animation config object or `true` for
* the default animation (`{duration: 350, easing: 'ease-in'}`)
* @return {Ext.dom.Element} this
*/
setOpacity: function(opacity, animate) {
var me = this;
if (!me.dom) {
return me;
}
if (!animate || !me.anim) {
me.setStyle('opacity', opacity);
} else {
if (typeof animate != 'object') {
animate = {
duration: 350,
easing: 'ease-in'
};
}
me.animate(Ext.applyIf({
to: {
opacity: opacity
}
}, animate));
}
return me;
},
/**
* Set positioning with an object returned by `getPositioning`.
* @param {Object} posCfg
* @return {Ext.dom.Element} this
*/
setPositioning: function(pc) {
return this.setStyle(pc);
},
/**
* Changes this Element's state to "vertical" (rotated 90 or 270 degrees).
* This involves inverting the getters and setters for height and width,
* and applying hooks for rotating getters and setters for border/margin/padding.
* (getWidth becomes getHeight and vice versa), setStyle and getStyle will
* also return the inverse when height or width are being operated on.
*
* @param {Number} angle the angle of rotation - either 90 or 270
* @param {String} cls an optional css class that contains the required
* styles for switching the element to vertical orientation. Omit this if
* the element already contains vertical styling. If cls is provided,
* it will be removed from the element when {@link #setHorizontal} is called.
* @private
*/
setVertical: function(angle, cls) {
var me = this,
proto = Element.prototype;
me.vertical = true;
if (cls) {
me.addCls(me.verticalCls = cls);
}
me.setWidth = proto.setHeight;
me.setHeight = proto.setWidth;
if (!Ext.isIE8) {
// In browsers that use CSS3 transforms we must invert getHeight and
// get Width. In IE8 no adjustment is needed because we use
// a BasicImage filter to rotate the element and the element's
// offsetWidth and offsetHeight are automatically inverted.
me.getWidth = proto.getHeight;
me.getHeight = proto.getWidth;
}
// Switch to using the appropriate vertical style hooks
me.styleHooks = (angle === 270) ? proto.verticalStyleHooks270 : proto.verticalStyleHooks90;
},
/**
* Set the size of this Element. If animation is true, both width and height will be animated concurrently.
* @param {Number/String} width The new width. This may be one of:
*
* - A Number specifying the new width in pixels.
* - A String used to set the CSS width style. Animation may **not** be used.
* - A size object in the format `{width: widthValue, height: heightValue}`.
*
* @param {Number/String} height The new height. This may be one of:
*
* - A Number specifying the new height in pixels.
* - A String used to set the CSS height style. Animation may **not** be used.
*
* @param {Boolean/Object} [animate] a standard Element animation config object or `true` for
* the default animation (`{duration: 350, easing: 'ease-in'}`)
*
* @return {Ext.dom.Element} this
*/
setSize: function(width, height, animate) {
var me = this;
if (Ext.isObject(width)) {
// in case of object from getSize()
animate = height;
height = width.height;
width = width.width;
}
if (!animate || !me.anim) {
me.dom.style.width = Element.addUnits(width);
me.dom.style.height = Element.addUnits(height);
if (me.shadow || me.shim) {
me.syncUnderlays();
}
} else {
if (animate === true) {
animate = {};
}
me.animate(Ext.applyIf({
to: {
width: width,
height: height
}
}, animate));
}
return me;
},
/**
* Sets the visibility of the element (see details). If the visibilityMode is set
* to Element.DISPLAY, it will use the display property to hide the element,
* otherwise it uses visibility. The default is to hide and show using the
* visibility property.
*
* @param {Boolean} visible Whether the element is visible
* @param {Boolean/Object} [animate] True for the default animation,
* or a standard Element animation config object.
*
* @return {Ext.dom.Element} this
*/
setVisible: function(visible, animate) {
var me = this,
dom = me.dom,
visMode = getVisMode(me);
// hideMode string override
if (typeof animate === 'string') {
switch (animate) {
case DISPLAY:
visMode = Element.DISPLAY;
break;
case VISIBILITY:
visMode = Element.VISIBILITY;
break;
case OFFSETS:
visMode = Element.OFFSETS;
break;
case CLIP:
visMode = Element.CLIP;
break;
}
me.setVisibilityMode(visMode);
animate = false;
}
if (!animate || !me.anim) {
if (visMode === Element.DISPLAY) {
return me.setDisplayed(visible);
} else if (visMode === Element.OFFSETS) {
me[visible ? 'removeCls' : 'addCls'](OFFSETCLASS);
} else if (visMode === Element.CLIP) {
me[visible ? 'removeCls' : 'addCls'](CLIPCLASS);
} else if (visMode === Element.VISIBILITY) {
me.fixDisplay();
// Show by clearing visibility style. Explicitly setting to "visible" overrides parent visibility setting
dom.style.visibility = visible ? '' : HIDDEN;
}
} else {
// closure for composites
if (visible) {
me.setOpacity(0.01);
me.setVisible(true);
}
if (!Ext.isObject(animate)) {
animate = {
duration: 350,
easing: 'ease-in'
};
}
me.animate(Ext.applyIf({
callback: function() {
if (!visible) {
// Grab the dom again, since the reference may have changed if we use fly
Ext.fly(dom).setVisible(false).setOpacity(1);
}
},
to: {
opacity: (visible) ? 1 : 0
}
}, animate));
}
me.getData()[ISVISIBLE] = visible;
if (me.shadow || me.shim) {
me.setUnderlaysVisible(visible);
}
return me;
},
/**
* Set the width of this Element.
*
* // change the width to 200px and animate with default configuration
* Ext.fly('elementId').setWidth(200, true);
*
* // change the width to 150px and animate with a custom configuration
* Ext.fly('elId').setWidth(150, {
* duration : 500, // animation will have a duration of .5 seconds
* // will change the content to "finished"
* callback: function(){ this.setHtml("finished"); }
* });
*
* @param {Number/String} width The new width. This may be one of:
*
* - A Number specifying the new width in pixels.
* - A String used to set the CSS width style. Animation may **not** be used.
*
* @param {Boolean/Object} [animate] a standard Element animation config object or `true` for
* the default animation (`{duration: 350, easing: 'ease-in'}`)
* @return {Ext.dom.Element} this
*/
setWidth: function(width, animate) {
var me = this;
if (!animate || !me.anim) {
me.callParent(arguments);
} else {
if (!Ext.isObject(animate)) {
animate = {};
}
me.animate(Ext.applyIf({
to: {
width: width
}
}, animate));
}
return me;
},
setX: function(x, animate) {
return this.setXY([
x,
this.getY()
], animate);
},
setXY: function(xy, animate) {
var me = this;
if (!animate || !me.anim) {
me.callParent([
xy
]);
} else {
if (!Ext.isObject(animate)) {
animate = {};
}
me.animate(Ext.applyIf({
to: {
x: xy[0],
y: xy[1]
}
}, animate));
}
return this;
},
setY: function(y, animate) {
return this.setXY([
this.getX(),
y
], animate);
},
/**
* Show this element - Uses display mode to determine whether to use "display",
* "visibility", "offsets", or "clip". See {@link #setVisible}.
*
* @param {Boolean/Object} [animate] true for the default animation or a standard
* Element animation config object.
*
* @return {Ext.dom.Element} this
*/
show: function(animate) {
// hideMode override
if (typeof animate === 'string') {
this.setVisible(true, animate);
return this;
}
this.setVisible(true, this.anim(animate));
return this;
},
/**
* Slides the element into view. An anchor point can be optionally passed to set the point of origin for the slide
* effect. This function automatically handles wrapping the element with a fixed-size container if needed. See the
* {@link Ext.fx.Anim} class overview for valid anchor point options. Usage:
*
* // default: slide the element in from the top
* el.slideIn();
*
* // custom: slide the element in from the right with a 2-second duration
* el.slideIn('r', { duration: 2000 });
*
* // common config options shown with default values
* el.slideIn('t', {
* easing: 'easeOut',
* duration: 500
* });
*
* @param {String} anchor (optional) One of the valid {@link Ext.fx.Anim} anchor positions (defaults to top: 't')
* @param {Object} options (optional) Object literal with any of the {@link Ext.fx.Anim} config options
* @param {Boolean} options.preserveScroll Set to true if preservation of any descendant elements'
* `scrollTop` values is required. By default the DOM wrapping operation performed by `slideIn` and
* `slideOut` causes the browser to lose all scroll positions.
* @return {Ext.dom.Element} The Element
*/
slideIn: function(anchor, obj, slideOut) {
var me = this,
dom = me.dom,
elStyle = dom.style,
beforeAnim, wrapAnim, restoreScroll, wrapDomParentNode;
anchor = anchor || "t";
obj = obj || {};
beforeAnim = function() {
var animScope = this,
listeners = obj.listeners,
el = Ext.fly(dom, '_anim'),
box, originalStyles, anim, wrap;
if (!slideOut) {
el.fixDisplay();
}
box = el.getBox();
if ((anchor == 't' || anchor == 'b') && box.height === 0) {
box.height = dom.scrollHeight;
} else if ((anchor == 'l' || anchor == 'r') && box.width === 0) {
box.width = dom.scrollWidth;
}
originalStyles = el.getStyle([
'width',
'height',
'left',
'right',
'top',
'bottom',
'position',
'z-index'
], true);
el.setSize(box.width, box.height);
// Cache all descendants' scrollTop & scrollLeft values if configured to preserve scroll.
if (obj.preserveScroll) {
restoreScroll = el.cacheScrollValues();
}
wrap = el.wrap({
role: 'presentation',
id: Ext.id() + '-anim-wrap-for-' + el.dom.id,
style: {
visibility: slideOut ? 'visible' : 'hidden'
}
});
wrapDomParentNode = wrap.dom.parentNode;
wrap.setPositioning(el.getPositioning());
if (wrap.isStyle('position', 'static')) {
wrap.position('relative');
}
el.clearPositioning('auto');
wrap.clip();
// The wrap will have reset all descendant scrollTops. Restore them if we cached them.
if (restoreScroll) {
restoreScroll();
}
// This element is temporarily positioned absolute within its wrapper.
// Restore to its default, CSS-inherited visibility setting.
// We cannot explicitly poke visibility:visible into its style because that overrides the visibility of the wrap.
el.setStyle({
visibility: '',
position: 'absolute'
});
if (slideOut) {
wrap.setSize(box.width, box.height);
}
switch (anchor) {
case 't':
anim = {
from: {
width: box.width + 'px',
height: '0px'
},
to: {
width: box.width + 'px',
height: box.height + 'px'
}
};
elStyle.bottom = '0px';
break;
case 'l':
anim = {
from: {
width: '0px',
height: box.height + 'px'
},
to: {
width: box.width + 'px',
height: box.height + 'px'
}
};
me.anchorAnimX(anchor);
break;
case 'r':
anim = {
from: {
x: box.x + box.width,
width: '0px',
height: box.height + 'px'
},
to: {
x: box.x,
width: box.width + 'px',
height: box.height + 'px'
}
};
me.anchorAnimX(anchor);
break;
case 'b':
anim = {
from: {
y: box.y + box.height,
width: box.width + 'px',
height: '0px'
},
to: {
y: box.y,
width: box.width + 'px',
height: box.height + 'px'
}
};
break;
case 'tl':
anim = {
from: {
x: box.x,
y: box.y,
width: '0px',
height: '0px'
},
to: {
width: box.width + 'px',
height: box.height + 'px'
}
};
elStyle.bottom = '0px';
me.anchorAnimX('l');
break;
case 'bl':
anim = {
from: {
y: box.y + box.height,
width: '0px',
height: '0px'
},
to: {
y: box.y,
width: box.width + 'px',
height: box.height + 'px'
}
};
me.anchorAnimX('l');
break;
case 'br':
anim = {
from: {
x: box.x + box.width,
y: box.y + box.height,
width: '0px',
height: '0px'
},
to: {
x: box.x,
y: box.y,
width: box.width + 'px',
height: box.height + 'px'
}
};
me.anchorAnimX('r');
break;
case 'tr':
anim = {
from: {
x: box.x + box.width,
width: '0px',
height: '0px'
},
to: {
x: box.x,
width: box.width + 'px',
height: box.height + 'px'
}
};
elStyle.bottom = '0px';
me.anchorAnimX('r');
break;
}
wrap.show();
wrapAnim = Ext.apply({}, obj);
delete wrapAnim.listeners;
wrapAnim = new Ext.fx.Anim(Ext.applyIf(wrapAnim, {
target: wrap,
duration: 500,
easing: 'ease-out',
from: slideOut ? anim.to : anim.from,
to: slideOut ? anim.from : anim.to
}));
// In the absence of a callback, this listener MUST be added first
wrapAnim.on('afteranimate', function() {
var el = Ext.fly(dom, '_anim');
el.setStyle(originalStyles);
if (slideOut) {
if (obj.useDisplay) {
el.setDisplayed(false);
} else {
el.hide();
}
}
if (wrap.dom) {
if (wrap.dom.parentNode) {
wrap.dom.parentNode.insertBefore(el.dom, wrap.dom);
} else {
wrapDomParentNode.appendChild(el.dom);
}
wrap.destroy();
}
// The unwrap will have reset all descendant scrollTops. Restore them if we cached them.
if (restoreScroll) {
restoreScroll();
}
// kill the no-op element animation created below
animScope.end();
});
// Add configured listeners after
if (listeners) {
wrapAnim.on(listeners);
}
};
me.animate({
// See "A Note About Wrapped Animations" at the top of this class:
duration: obj.duration ? Math.max(obj.duration, 500) * 2 : 1000,
listeners: {
beforeanimate: beforeAnim
}
});
// kick off the wrap animation
return me;
},
/**
* Slides the element out of view. An anchor point can be optionally passed to set the end point for the slide
* effect. When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will
* still take up space in the document. The element must be removed from the DOM using the 'remove' config option if
* desired. This function automatically handles wrapping the element with a fixed-size container if needed. See the
* {@link Ext.fx.Anim} class overview for valid anchor point options. Usage:
*
* // default: slide the element out to the top
* el.slideOut();
*
* // custom: slide the element out to the right with a 2-second duration
* el.slideOut('r', { duration: 2000 });
*
* // common config options shown with default values
* el.slideOut('t', {
* easing: 'easeOut',
* duration: 500,
* remove: false,
* useDisplay: false
* });
*
* @param {String} anchor (optional) One of the valid {@link Ext.fx.Anim} anchor positions (defaults to top: 't')
* @param {Object} options (optional) Object literal with any of the {@link Ext.fx.Anim} config options
* @return {Ext.dom.Element} The Element
*/
slideOut: function(anchor, options) {
return this.slideIn(anchor, options, true);
},
/**
* Stops the specified event(s) from bubbling and optionally prevents the default action
*
* var store = Ext.create('Ext.data.Store', {
* fields: ['name', 'email'],
* data: [{
* 'name': 'Finn',
* "email": "finn@adventuretime.com"
* }]
* });
*
* Ext.create('Ext.grid.Panel', {
* title: 'Land of Ooo',
* store: store,
* columns: [{
* text: 'Name',
* dataIndex: 'name'
* }, {
* text: 'Email <img style="vertical-align:middle;" src="{some-help-image-src}" />',
* dataIndex: 'email',
* flex: 1,
* listeners: {
* render: function(col) {
* // Swallow the click event when the click occurs on the
* // help icon - preventing the sorting of data by that
* // column and instead performing an action specific to
* // the help icon
* var img = col.getEl().down('img');
* img.swallowEvent(['click', 'mousedown'], true);
* col.on('click', function() {
* // logic to show a help dialog
* console.log('image click handler');
* }, col);
* }
* }
* }],
* height: 200,
* width: 400,
* renderTo: document.body
* });
*
* @param {String/String[]} eventName an event / array of events to stop from bubbling
* @param {Boolean} [preventDefault] true to prevent the default action too
* @return {Ext.dom.Element} this
*/
swallowEvent: function(eventName, preventDefault) {
var me = this,
e, eLen,
fn = function(e) {
e.stopPropagation();
if (preventDefault) {
e.preventDefault();
}
};
if (Ext.isArray(eventName)) {
eLen = eventName.length;
for (e = 0; e < eLen; e++) {
me.on(eventName[e], fn);
}
return me;
}
me.on(eventName, fn);
return me;
},
/**
* Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
* When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still
* take up space in the document. The element must be removed from the DOM using the 'remove' config option if
* desired. Usage:
*
* // default
* el.switchOff();
*
* // all config options shown with default values
* el.switchOff({
* easing: 'easeIn',
* duration: .3,
* remove: false,
* useDisplay: false
* });
*
* @param {Object} options (optional) Object literal with any of the {@link Ext.fx.Anim} config options
* @return {Ext.dom.Element} The Element
*/
switchOff: function(options) {
var me = this,
dom = me.dom,
beforeAnim;
options = Ext.applyIf(options || {}, {
easing: 'ease-in',
duration: 500,
remove: false,
useDisplay: false
});
beforeAnim = function() {
var el = Ext.fly(dom, '_anim'),
animScope = this,
size = el.getSize(),
xy = el.getXY(),
keyframe, position;
el.clearOpacity();
el.clip();
position = el.getPositioning();
keyframe = new Ext.fx.Animator({
target: dom,
duration: options.duration,
easing: options.easing,
keyframes: {
33: {
opacity: 0.3
},
66: {
height: 1,
y: xy[1] + size.height / 2
},
100: {
width: 1,
x: xy[0] + size.width / 2
}
}
});
keyframe.on('afteranimate', function() {
var el = Ext.fly(dom, '_anim');
if (options.useDisplay) {
el.setDisplayed(false);
} else {
el.hide();
}
el.clearOpacity();
el.setPositioning(position);
el.setSize(size);
// kill the no-op element animation created below
animScope.end();
});
};
me.animate({
// See "A Note About Wrapped Animations" at the top of this class:
duration: (Math.max(options.duration, 500) * 2),
listeners: {
beforeanimate: {
fn: beforeAnim
}
},
callback: options.callback,
scope: options.scope
});
return me;
},
/**
* @private
* Currently used for updating grid cells without modifying DOM structure
*
* Synchronizes content of this Element with the content of the passed element.
*
* Style and CSS class are copied from source into this Element, and contents are synced
* recursively. If a child node is a text node, the textual data is copied.
*/
syncContent: function(source) {
source = Ext.getDom(source);
var sourceNodes = source.childNodes,
sourceLen = sourceNodes.length,
dest = this.dom,
destNodes = dest.childNodes,
destLen = destNodes.length,
i, destNode, sourceNode, nodeType, newAttrs, attLen, attName,
elData = dest._extData;
// Copy top node's attributes across. Use IE-specific method if possible.
// In IE10, there is a problem where the className will not get updated
// in the view, even though the className on the dom element is correct.
// See EXTJSIV-9462
if (Ext.isIE9m && dest.mergeAttributes) {
dest.mergeAttributes(source, true);
// EXTJSIV-6803. IE's mergeAttributes appears not to make the source's "src" value available until after the image is ready.
// So programmatically copy any src attribute.
dest.src = source.src;
} else {
newAttrs = source.attributes;
attLen = newAttrs.length;
for (i = 0; i < attLen; i++) {
attName = newAttrs[i].name;
if (attName !== 'id') {
dest.setAttribute(attName, newAttrs[i].value);
}
}
}
// The element's data is no longer synchronized. We just overwrite it in the DOM
if (elData) {
elData.isSynchronized = false;
}
// If the number of child nodes does not match, fall back to replacing innerHTML
if (sourceLen !== destLen) {
dest.innerHTML = source.innerHTML;
return;
}
// Loop through source nodes.
// If there are fewer, we must remove excess
for (i = 0; i < sourceLen; i++) {
sourceNode = sourceNodes[i];
destNode = destNodes[i];
nodeType = sourceNode.nodeType;
// If node structure is out of sync, just drop innerHTML in and return
if (nodeType !== destNode.nodeType || (nodeType === 1 && sourceNode.tagName !== destNode.tagName)) {
dest.innerHTML = source.innerHTML;
return;
}
// Update text node
if (nodeType === 3) {
destNode.data = sourceNode.data;
} else // Sync element content
{
if (sourceNode.id && destNode.id !== sourceNode.id) {
destNode.id = sourceNode.id;
}
destNode.style.cssText = sourceNode.style.cssText;
destNode.className = sourceNode.className;
Ext.fly(destNode, '_syncContent').syncContent(sourceNode);
}
}
},
/**
* Toggles the element's visibility, depending on visibility mode.
* @param {Boolean/Object} [animate] True for the default animation, or a standard Element animation config object
* @return {Ext.dom.Element} this
*/
toggle: function(animate) {
var me = this;
me.setVisible(!me.isVisible(), me.anim(animate));
return me;
},
/**
* Hides a previously applied mask.
*/
unmask: function() {
var me = this,
data = me.getData(),
maskEl = data.maskEl,
style;
if (maskEl) {
style = maskEl.dom.style;
// Remove resource-intensive CSS expressions as soon as they are not required.
if (style.clearExpression) {
style.clearExpression('width');
style.clearExpression('height');
}
if (maskEl) {
maskEl.destroy();
delete data.maskEl;
}
me.removeCls([
XMASKED,
XMASKEDRELATIVE
]);
}
me.restoreTabbableState(me.dom === DOC.body);
},
/**
* Return clipping (overflow) to original clipping before {@link #clip} was called
* @return {Ext.dom.Element} this
*/
unclip: function() {
var me = this,
data = me.getData(),
clip;
if (data[ISCLIPPED]) {
data[ISCLIPPED] = false;
clip = data[ORIGINALCLIP];
if (clip.o) {
me.setStyle(OVERFLOW, clip.o);
}
if (clip.x) {
me.setStyle(OVERFLOWX, clip.x);
}
if (clip.y) {
me.setStyle(OVERFLOWY, clip.y);
}
}
return me;
},
translate: function(x, y, z) {
if (Ext.supports.CssTransforms && !Ext.isIE9m) {
this.callParent(arguments);
} else {
if (x != null) {
this.dom.style.left = x + 'px';
}
if (y != null) {
this.dom.style.top = y + 'px';
}
}
},
/**
* Disables text selection for this element (normalized across browsers)
* @return {Ext.dom.Element} this
*/
unselectable: function() {
// The approach used to disable text selection combines CSS, HTML attributes and DOM events. Importantly the
// strategy is designed to be expressible in markup, so that elements can be rendered unselectable without
// needing modifications post-render. e.g.:
//
// <div class="x-unselectable" unselectable="on"></div>
//
// Changes to this method may need to be reflected elsewhere, e.g. ProtoElement.
var me = this;
// The unselectable property (or similar) is supported by various browsers but Opera is the only browser that
// doesn't support any of the other techniques. The problem with it is that it isn't inherited by child
// elements. Theoretically we could add it to all children but the performance would be terrible. In certain
// key locations (e.g. panel headers) we add unselectable="on" to extra elements during rendering just for
// Opera's benefit.
if (Ext.isOpera) {
me.dom.unselectable = 'on';
}
// In Mozilla and WebKit the CSS properties -moz-user-select and -webkit-user-select prevent a selection
// originating in an element. These are inherited, which is what we want.
//
// In IE we rely on a listener for the selectstart event instead. We don't need to register a listener on the
// individual element, instead we use a single listener and rely on event propagation to listen for the event at
// the document level. That listener will walk up the DOM looking for nodes that have either of the classes
// x-selectable or x-unselectable. This simulates the CSS inheritance approach.
//
// IE 10 is expected to support -ms-user-select so the listener may not be required.
me.removeCls(Element.selectableCls);
me.addCls(Element.unselectableCls);
return me;
},
privates: {
/**
* @private
*/
findTabbableElements: function(options) {
var skipSelf, skipChildren, excludeRoot, includeSaved, includeHidden,
dom = this.dom,
cAttr = Ext.Element.tabbableSavedCounterAttribute,
selection = [],
idx = 0,
nodes, node, fly, i, len, tabIndex;
if (!dom) {
return selection;
}
if (options) {
skipSelf = options.skipSelf;
skipChildren = options.skipChildren;
excludeRoot = options.excludeRoot;
includeSaved = options.includeSaved;
includeHidden = options.includeHidden;
}
excludeRoot = excludeRoot && Ext.getDom(excludeRoot);
if (excludeRoot && excludeRoot.contains(dom)) {
return selection;
}
if (!skipSelf && ((includeSaved && dom.hasAttribute(cAttr)) || this.isTabbable(includeHidden))) {
selection[idx++] = dom;
}
if (skipChildren) {
return selection;
}
nodes = dom.querySelectorAll(Ext.Element.tabbableSelector);
len = nodes.length;
if (!len) {
return selection;
}
fly = new Ext.dom.Fly();
// We're only interested in the elements that an user can *tab into*,
// not all programmatically focusable elements. So we have to filter
// these out.
for (i = 0; i < len; i++) {
node = nodes[i];
// A node with tabIndex < 0 absolutely can't be tabbable
// so we can save a function call if that is the case.
// Note that we can't use node.tabIndex here because IE
// will return 0 for elements that have no tabindex
// attribute defined, regardless of whether they are
// tabbable or not.
tabIndex = +node.getAttribute('tabIndex');
// quicker than parseInt
// tabIndex value may be null for nodes with no tabIndex defined;
// most of those may be naturally tabbable. We don't want to
// check this here, that's isTabbable()'s job and it's not trivial.
// We explicitly check that tabIndex is not negative. The expression
// below is purposeful if hairy; this is a very hot code path so care
// is taken to minimize the amount of DOM calls that could be avoided.
// A node may have its tabindex saved by previous calls to
// saveTabbableState(); in that case we need to return that node
// so that its saved counter could be properly incremented or
// decremented.
if (((includeSaved && node.hasAttribute(cAttr)) || (!(tabIndex < 0) && fly.attach(node).isTabbable(includeHidden))) && !(excludeRoot && (excludeRoot === node || excludeRoot.contains(node)))) {
selection[idx++] = node;
}
}
return selection;
},
/**
* @private
*/
saveTabbableState: function(options) {
var counterAttr = Ext.Element.tabbableSavedCounterAttribute,
savedAttr = Ext.Element.tabbableSavedValueAttribute,
counter, nodes, node, i, len;
// By default include already saved tabbables, and just increase their save counter.
// For example, if a View with saved tabbables is covered by a modal Window, saveTabbableState
// Must disable tabbability for the whole document. But upon unmask, the View must not
// be restored to tabbability. It must only have its save level decremented.
// AbstractView#toggleChildrenTabbability however pases this as false so that
// it may be called upon row add and it does not increment save levels on already saved tabbables.
if (!options || options.includeSaved == null) {
options = Ext.Object.chain(options || null);
options.includeSaved = true;
}
nodes = this.findTabbableElements(options);
for (i = 0 , len = nodes.length; i < len; i++) {
node = nodes[i];
counter = +node.getAttribute(counterAttr);
if (counter > 0) {
node.setAttribute(counterAttr, ++counter);
} else {
// tabIndex could be set on both naturally tabbable and generic elements.
// Either way we need to save it to restore later.
if (node.hasAttribute('tabIndex')) {
node.setAttribute(savedAttr, node.getAttribute('tabIndex'));
} else // When no tabIndex is specified, that means a naturally tabbable element.
{
node.setAttribute(savedAttr, 'none');
}
// We disable the tabbable state by setting tabIndex to -1.
// The element can still be focused programmatically though.
node.setAttribute('tabIndex', '-1');
node.setAttribute(counterAttr, '1');
}
}
return nodes;
},
/**
* @private
*/
restoreTabbableState: function(skipSelf, skipChildren) {
var dom = this.dom,
counterAttr = Ext.Element.tabbableSavedCounterAttribute,
savedAttr = Ext.Element.tabbableSavedValueAttribute,
nodes = [],
idx, counter, nodes, node, i, len;
if (!dom) {
return this;
}
if (!skipChildren) {
nodes = Ext.Array.from(dom.querySelectorAll('[' + counterAttr + ']'));
}
if (!skipSelf) {
nodes.unshift(dom);
}
for (i = 0 , len = nodes.length; i < len; i++) {
node = nodes[i];
if (!node.hasAttribute(counterAttr) || !node.hasAttribute(savedAttr)) {
continue;
}
counter = +node.getAttribute(counterAttr);
if (counter > 1) {
node.setAttribute(counterAttr, --counter);
continue;
}
idx = node.getAttribute(savedAttr);
// That is a naturally tabbable element
if (idx === 'none') {
node.removeAttribute('tabIndex');
} else {
node.setAttribute('tabIndex', idx);
}
node.removeAttribute(savedAttr);
node.removeAttribute(counterAttr);
}
return nodes;
}
},
deprecated: {
'4.0': {
methods: {
/**
* Creates a pause before any subsequent queued effects begin. If there are no effects queued after the pause it will
* have no effect. Usage:
*
* el.pause(1);
*
* @deprecated 4.0 Use the `delay` config to {@link #animate} instead.
* @param {Number} seconds The length of time to pause (in seconds)
* @return {Ext.dom.Element} The Element
*/
pause: function(ms) {
var me = this;
Ext.fx.Manager.setFxDefaults(me.id, {
delay: ms
});
return me;
},
/**
* Animates the transition of an element's dimensions from a starting height/width to an ending height/width. This
* method is a convenience implementation of {@link #shift}. Usage:
*
* // change height and width to 100x100 pixels
* el.scale(100, 100);
*
* // common config options shown with default values. The height and width will default to
* // the element's existing values if passed as null.
* el.scale(
* [element's width],
* [element's height], {
* easing: 'easeOut',
* duration: 350
* }
* );
*
* @deprecated 4.0 Just use {@link #animate} instead.
* @param {Number} width The new width (pass undefined to keep the original width)
* @param {Number} height The new height (pass undefined to keep the original height)
* @param {Object} options (optional) Object literal with any of the {@link Ext.fx.Anim} config options
* @return {Ext.dom.Element} The Element
*/
scale: function(w, h, o) {
this.animate(Ext.apply({}, o, {
width: w,
height: h
}));
return this;
},
/**
* Animates the transition of any combination of an element's dimensions, xy position and/or opacity. Any of these
* properties not specified in the config object will not be changed. This effect requires that at least one new
* dimension, position or opacity setting must be passed in on the config object in order for the function to have
* any effect. Usage:
*
* // slide the element horizontally to x position 200 while changing the height and opacity
* el.shift({ x: 200, height: 50, opacity: .8 });
*
* // common config options shown with default values.
* el.shift({
* width: [element's width],
* height: [element's height],
* x: [element's x position],
* y: [element's y position],
* opacity: [element's opacity],
* easing: 'easeOut',
* duration: 350
* });
*
* @deprecated 4.0 Just use {@link #animate} instead.
* @param {Object} options Object literal with any of the {@link Ext.fx.Anim} config options
* @return {Ext.dom.Element} The Element
*/
shift: function(config) {
this.animate(config);
return this;
}
}
},
'4.2': {
methods: {
/**
* Sets the position of the element in page coordinates.
* @param {Number} x X value for new position (coordinates are page-based)
* @param {Number} y Y value for new position (coordinates are page-based)
* @param {Boolean/Object} [animate] True for the default animation, or a standard
* Element animation config object
* @return {Ext.dom.Element} this
* @deprecated 4.2.0 Use {@link Ext.dom.Element#setXY} instead.
*/
moveTo: function(x, y, animate) {
return this.setXY([
x,
y
], animate);
},
/**
* Sets the element's position and size in one shot. If animation is true then
* width, height, x and y will be animated concurrently.
*
* @param {Number} x X value for new position (coordinates are page-based)
* @param {Number} y Y value for new position (coordinates are page-based)
* @param {Number/String} width The new width. This may be one of:
*
* - A Number specifying the new width in pixels
* - A String used to set the CSS width style. Animation may **not** be used.
*
* @param {Number/String} height The new height. This may be one of:
*
* - A Number specifying the new height in pixels
* - A String used to set the CSS height style. Animation may **not** be used.
*
* @param {Boolean/Object} [animate] true for the default animation or
* a standard Element animation config object
*
* @return {Ext.dom.Element} this
* @deprecated 4.2.0 Use {@link Ext.util.Positionable#setBox} instead.
*/
setBounds: function(x, y, width, height, animate) {
return this.setBox({
x: x,
y: y,
width: width,
height: height
}, animate);
},
/**
* Sets the element's left and top positions directly using CSS style
* @param {Number/String} left Number of pixels or CSS string value to
* set as the left CSS property value
* @param {Number/String} top Number of pixels or CSS string value to
* set as the top CSS property value
* @return {Ext.dom.Element} this
* @deprecated 4.2.0 Use {@link Ext.dom.Element#setLocalXY} instead
*/
setLeftTop: function(left, top) {
var me = this,
style = me.dom.style;
style.left = Element.addUnits(left);
style.top = Element.addUnits(top);
if (me.shadow || me.shim) {
me.syncUnderlays();
}
return me;
},
/**
* Sets the position of the element in page coordinates.
* @param {Number} x X value for new position
* @param {Number} y Y value for new position
* @param {Boolean/Object} [animate] True for the default animation, or a standard
* Element animation config object
* @return {Ext.dom.Element} this
* @deprecated 4.2.0 Use {@link Ext.dom.Element#setXY} instead.
*/
setLocation: function(x, y, animate) {
return this.setXY([
x,
y
], animate);
}
}
},
'5.0': {
methods: {
/**
* Returns the value of a namespaced attribute from the element's underlying DOM node.
* @param {String} namespace The namespace in which to look for the attribute
* @param {String} name The attribute name
* @return {String} The attribute value
* @deprecated 5.0.0 Please use {@link Ext.dom.Element#getAttribute} instead.
*/
getAttributeNS: function(namespace, name) {
return this.getAttribute(name, namespace);
},
/**
* Calculates the x, y to center this element on the screen
* @return {Number[]} The x, y values [x, y]
* @deprecated 5.0.0 Use {@link Ext.dom.Element#getAlignToXY} instead.
* el.getAlignToXY(document, 'c-c');
*/
getCenterXY: function() {
return this.getAlignToXY(DOC, 'c-c');
},
/**
* Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
* when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
* if a height has not been set using CSS.
* @return {Number}
* @deprecated 5.0.0 use {@link Ext.dom.Element#getHeight} instead
*/
getComputedHeight: function() {
return Math.max(this.dom.offsetHeight, this.dom.clientHeight) || parseFloat(this.getStyle(HEIGHT)) || 0;
},
/**
* Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
* when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
* if a width has not been set using CSS.
* @return {Number}
* @deprecated 5.0.0 use {@link Ext.dom.Element#getWidth} instead.
*/
getComputedWidth: function() {
return Math.max(this.dom.offsetWidth, this.dom.clientWidth) || parseFloat(this.getStyle(WIDTH)) || 0;
},
/**
* Returns the dimensions of the element available to lay content out in.
*
* getStyleSize utilizes prefers style sizing if present, otherwise it chooses the larger of offsetHeight/clientHeight and
* offsetWidth/clientWidth. To obtain the size excluding scrollbars, use getViewSize.
*
* Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
*
* @return {Object} Object describing width and height.
* @return {Number} return.width
* @return {Number} return.height
* @deprecated 5.0.0 Use {@link Ext.dom.Element#getSize} instead.
*/
getStyleSize: function() {
var me = this,
d = this.dom,
isDoc = (d === DOC || d === DOC.body),
s, w, h;
// If the body, use static methods
if (isDoc) {
return {
width: Element.getViewportWidth(),
height: Element.getViewportHeight()
};
}
s = me.getStyle([
'height',
'width'
], true);
//seek inline
// Use Styles if they are set
if (s.width && s.width !== 'auto') {
w = parseFloat(s.width);
}
// Use Styles if they are set
if (s.height && s.height !== 'auto') {
h = parseFloat(s.height);
}
// Use getWidth/getHeight if style not set.
return {
width: w || me.getWidth(true),
height: h || me.getHeight(true)
};
},
/**
* Returns true if this element uses the border-box-sizing model. This method is
* deprecated as of version 5.0 because border-box sizing is forced upon all elements
* via a style sheet rule, and the browsers that do not support border-box (IE6/7 strict
* mode) are no longer supported.
* @deprecated 5.0.0
* @return {Boolean}
*/
isBorderBox: function() {
return true;
},
/**
* Returns true if display is not "none"
* @return {Boolean}
* @deprecated 5.0.0 use element.isStyle('display', 'none');
*/
isDisplayed: function() {
return !this.isStyle('display', 'none');
},
/**
* Checks whether this element can be focused.
* @return {Boolean} True if the element is focusable
* @deprecated 5.0.0 use {@link #isFocusable} instead
*/
focusable: 'isFocusable'
}
}
}
};
})(), function() {
var Element = Ext.dom.Element,
proto = Element.prototype,
useDocForId = !Ext.isIE8,
DOC = document,
view = DOC.defaultView,
opacityRe = /alpha\(opacity=(.*)\)/i,
trimRe = /^\s+|\s+$/g,
styleHooks = proto.styleHooks,
supports = Ext.supports,
verticalStyleHooks90, verticalStyleHooks270, edges, k, edge, borderWidth, getBorderWidth;
proto._init(Element);
delete proto._init;
Ext.plainTableCls = Ext.baseCSSPrefix + 'table-plain';
Ext.plainListCls = Ext.baseCSSPrefix + 'list-plain';
// ensure that any methods added by this override are also added to Ext.CompositeElementLite
if (Ext.CompositeElementLite) {
Ext.CompositeElementLite.importElementMethods();
}
if (!supports.Opacity && Ext.isIE) {
Ext.apply(styleHooks.opacity, {
get: function(dom) {
var filter = dom.style.filter,
match, opacity;
if (filter.match) {
match = filter.match(opacityRe);
if (match) {
opacity = parseFloat(match[1]);
if (!isNaN(opacity)) {
return opacity ? opacity / 100 : 0;
}
}
}
return 1;
},
set: function(dom, value) {
var style = dom.style,
val = style.filter.replace(opacityRe, '').replace(trimRe, '');
style.zoom = 1;
// ensure dom.hasLayout
// value can be a number or '' or null... so treat falsey as no opacity
if (typeof (value) === 'number' && value >= 0 && value < 1) {
value *= 100;
style.filter = val + (val.length ? ' ' : '') + 'alpha(opacity=' + value + ')';
} else {
style.filter = val;
}
}
});
}
if (!supports.matchesSelector) {
// Match basic tagName.ClassName selector syntax for is implementation
var simpleSelectorRe = /^([a-z]+|\*)?(?:\.([a-z][a-z\-_0-9]*))?$/i,
dashRe = /\-/g,
fragment,
classMatcher = function(tag, cls) {
var classRe = new RegExp('(?:^|\\s+)' + cls.replace(dashRe, '\\-') + '(?:\\s+|$)');
if (tag && tag !== '*') {
tag = tag.toUpperCase();
return function(el) {
return el.tagName === tag && classRe.test(el.className);
};
}
return function(el) {
return classRe.test(el.className);
};
},
tagMatcher = function(tag) {
tag = tag.toUpperCase();
return function(el) {
return el.tagName === tag;
};
},
cache = {};
proto.matcherCache = cache;
proto.is = function(selector) {
// Empty selector always matches
if (!selector) {
return true;
}
var dom = this.dom,
cls, match, testFn, root, isOrphan, is, tag;
// Only Element node types can be matched.
if (dom.nodeType !== 1) {
return false;
}
if (!(testFn = Ext.isFunction(selector) ? selector : cache[selector])) {
if (!(match = selector.match(simpleSelectorRe))) {
// Not a simple tagName.className selector, do it the hard way
root = dom.parentNode;
if (!root) {
isOrphan = true;
root = fragment || (fragment = DOC.createDocumentFragment());
fragment.appendChild(dom);
}
is = Ext.Array.indexOf(Ext.fly(root, '_is').query(selector), dom) !== -1;
if (isOrphan) {
fragment.removeChild(dom);
}
return is;
}
tag = match[1];
cls = match[2];
cache[selector] = testFn = cls ? classMatcher(tag, cls) : tagMatcher(tag);
}
return testFn(dom);
};
}
// IE8 needs its own implementation of getStyle because it doesn't support getComputedStyle
if (!view || !view.getComputedStyle) {
proto.getStyle = function(property, inline) {
var me = this,
dom = me.dom,
multiple = typeof property !== 'string',
prop = property,
props = prop,
len = 1,
isInline = inline,
styleHooks = me.styleHooks,
camel, domStyle, values, hook, out, style, i;
if (multiple) {
values = {};
prop = props[0];
i = 0;
if (!(len = props.length)) {
return values;
}
}
if (!dom || dom.documentElement) {
return values || '';
}
domStyle = dom.style;
if (inline) {
style = domStyle;
} else {
style = dom.currentStyle;
// fallback to inline style if rendering context not available
if (!style) {
isInline = true;
style = domStyle;
}
}
do {
hook = styleHooks[prop];
if (!hook) {
styleHooks[prop] = hook = {
name: Element.normalize(prop)
};
}
if (hook.get) {
out = hook.get(dom, me, isInline, style);
} else {
camel = hook.name;
out = style[camel];
}
if (!multiple) {
return out;
}
values[prop] = out;
prop = props[++i];
} while (i < len);
return values;
};
}
// override getStyle for border-*-width
if (Ext.isIE8) {
getBorderWidth = function(dom, el, inline, style) {
if (style[this.styleName] === 'none') {
return '0px';
}
return style[this.name];
};
edges = [
'Top',
'Right',
'Bottom',
'Left'
];
k = edges.length;
while (k--) {
edge = edges[k];
borderWidth = 'border' + edge + 'Width';
styleHooks['border-' + edge.toLowerCase() + '-width'] = styleHooks[borderWidth] = {
name: borderWidth,
styleName: 'border' + edge + 'Style',
get: getBorderWidth
};
}
// IE8 has an odd bug with handling font icons in pseudo elements;
// it will render the icon once and not update it when something
// like text color is changed via style addition or removal.
// We have to force icon repaint by adding a style with forced empty
// pseudo element content, (x-sync-repaint) and removing it back to work
// around this issue.
// See this: https://github.com/FortAwesome/Font-Awesome/issues/954
// and this: https://github.com/twbs/bootstrap/issues/13863
var syncRepaintCls = Ext.baseCSSPrefix + 'sync-repaint';
proto.syncRepaint = function() {
this.addCls(syncRepaintCls);
// Measuring element width will make the browser to repaint it
this.getWidth();
// Removing empty content makes the icon to appear again and be redrawn
this.removeCls(syncRepaintCls);
};
}
if (Ext.isIE10m) {
Ext.override(Element, {
focus: function(defer, dom) {
var me = this,
ex;
dom = dom || me.dom;
if (Number(defer)) {
Ext.defer(me.focus, defer, me, [
null,
dom
]);
} else {
Ext.GlobalEvents.fireEvent('beforefocus', dom);
// IE10m has an acute problem with focusing input elements;
// when the element was just shown and did not have enough
// time to initialize, focusing it might fail. The problem
// is somewhat random in nature; most of the time focusing
// an input element will succeed, failing only occasionally.
// When it fails, the focus will be thrown to the document
// body element, with subsequent focusout/focusin event pair
// on the body, which throws off our focusenter/focusleave
// processing.
// Fortunately for us, when this focus failure happens, the
// resulting focusout event will happen *synchronously*
// unlike the normal focusing events which IE will fire
// asynchronously. Also fortunately for us, in most cases
// trying to focus the given element the second time
// immediately after it failed to focus the first time
// seems to do the trick; however when second focus attempt
// succeeds, it will result in focusout on the body and
// focusin on the given element, which again wreaks havoc
// on our focusenter/focusleave handling.
// The only workable solution we have is to pretend that
// focus never went to the document body and ignore the
// focusout and focusin caused by failed first focus attempt.
// To this end, we fudge the event stream in Focus publisher
// override.
if (dom && (dom.tagName === 'INPUT' || dom.tagname === 'TEXTAREA')) {
Ext.synchronouslyFocusing = document.activeElement;
}
// Also note that trying to focus an unfocusable element
// might throw an exception in IE8. What a cute idea, MS. :(
try {
dom.focus();
} catch (xcpt) {
ex = xcpt;
}
// Ok so now we have this situation when we tried to focus
// the first time but did not succeed. Let's try again but
// not if there was an exception the first time - when the
// "focus failure" happens it does so silently. :(
if (Ext.synchronouslyFocusing && document.activeElement !== dom && !ex) {
dom.focus();
}
Ext.synchronouslyFocusing = null;
}
return me;
}
});
}
Ext.apply(Ext, {
/**
* `true` to automatically uncache orphaned Ext.Elements periodically. If set to
* `false`, the application will be required to clean up orphaned Ext.Elements and
* it's listeners as to not cause memory leakage.
* @member Ext
*/
enableGarbageCollector: true,
// In sencha v5 isBorderBox is no longer needed since all supported browsers
// support border-box, but it is hard coded to true for backward compatibility
isBorderBox: true,
/**
* @property {Boolean} useShims
* @member Ext
* Set to `true` to use a {@link Ext.util.Floating#shim shim} on all floating Components
* and {@link Ext.LoadMask LoadMasks}
*/
useShims: false,
getElementById: function(id) {
var el = DOC.getElementById(id),
detachedBodyEl;
if (!el && (detachedBodyEl = Ext.detachedBodyEl)) {
el = detachedBodyEl.dom.querySelector(Ext.makeIdSelector(id));
}
return el;
},
/**
* Applies event listeners to elements by selectors when the document is ready.
* The event name is specified with an `@` suffix.
*
* Ext.addBehaviors({
* // add a listener for click on all anchors in element with id foo
* '#foo a@click': function(e, t){
* // do something
* },
*
* // add the same listener to multiple selectors (separated by comma BEFORE the @)
* '#foo a, #bar span.some-class@mouseover': function(){
* // do something
* }
* });
*
* @param {Object} obj The list of behaviors to apply
* @member Ext
*/
addBehaviors: function(o) {
if (!Ext.isReady) {
Ext.onInternalReady(function() {
Ext.addBehaviors(o);
});
} else {
var cache = {},
// simple cache for applying multiple behaviors to same selector does query multiple times
parts, b, s;
for (b in o) {
if ((parts = b.split('@'))[1]) {
// for Object prototype breakers
s = parts[0];
if (!cache[s]) {
cache[s] = Ext.fly(document).select(s, true);
}
cache[s].on(parts[1], o[b]);
}
}
cache = null;
}
}
});
if (Ext.isIE9m) {
Ext.getElementById = function(id) {
var el = DOC.getElementById(id),
detachedBodyEl;
if (!el && (detachedBodyEl = Ext.detachedBodyEl)) {
el = detachedBodyEl.dom.all[id];
}
return el;
};
proto.getById = function(id, asDom) {
var dom = this.dom,
ret = null,
entry, el;
if (dom) {
// for normal elements getElementById is the best solution, but if the el is
// not part of the document.body, we need to use all[]
el = (useDocForId && DOC.getElementById(id)) || dom.all[id];
if (el) {
if (asDom) {
ret = el;
} else {
// calling Element.get here is a real hit (2x slower) because it has to
// redetermine that we are giving it a dom el.
entry = Ext.cache[id];
if (entry) {
if (entry.skipGarbageCollection || !Ext.isGarbage(entry.dom)) {
ret = entry;
} else {
Ext.raise("Stale Element with id '" + el.id + "' found in Element cache. " + "Make sure to clean up Element instances using destroy()");
entry.destroy();
}
}
ret = ret || new Ext.Element(el);
}
}
}
return ret;
};
} else if (!DOC.querySelector) {
Ext.getDetachedBody = Ext.getBody;
Ext.getElementById = function(id) {
return DOC.getElementById(id);
};
proto.getById = function(id, asDom) {
var dom = DOC.getElementById(id);
return asDom ? dom : (dom ? Ext.get(dom) : null);
};
}
if (Ext.isIE && !(Ext.isIE9p && DOC.documentMode >= 9)) {
// Essentially all web browsers (Firefox, Internet Explorer, recent versions of Opera, Safari, Konqueror, and iCab,
// as a non-exhaustive list) return null when the specified attribute does not exist on the specified element.
// The DOM specification says that the correct return value in this case is actually the empty string, and some
// DOM implementations implement this behavior. The implementation of getAttribute in XUL (Gecko) actually follows
// the specification and returns an empty string. Consequently, you should use hasAttribute to check for an attribute's
// existence prior to calling getAttribute() if it is possible that the requested attribute does not exist on the specified element.
//
// https://developer.mozilla.org/en-US/docs/DOM/element.getAttribute
// http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-745549614
proto.getAttribute = function(name, ns) {
var d = this.dom,
type;
if (ns) {
type = typeof d[ns + ":" + name];
if (type !== 'undefined' && type !== 'unknown') {
return d[ns + ":" + name] || null;
}
return null;
}
if (name === "for") {
name = "htmlFor";
}
return d[name] || null;
};
}
Ext.onInternalReady(function() {
var transparentRe = /^(?:transparent|(?:rgba[(](?:\s*\d+\s*[,]){3}\s*0\s*[)]))$/i,
bodyCls = [],
//htmlCls = [],
origSetWidth = proto.setWidth,
origSetHeight = proto.setHeight,
origSetSize = proto.setSize,
pxRe = /^\d+(?:\.\d*)?px$/i,
colorStyles, i, name, camel;
if (supports.FixedTableWidthBug) {
// EXTJSIV-12665
// https://bugs.webkit.org/show_bug.cgi?id=130239
// Webkit browsers fail to layout correctly when a form field's width is less
// than the min-width of the body element. The only way to fix it seems to be
// to toggle the display style of the field's element before and after setting
// the width. Note: once the bug has been corrected by toggling the element's
// display, successive calls to setWidth will work without the hack. It's only
// when going from naturally widthed to having an explicit width that the bug
// occurs.
styleHooks.width = {
name: 'width',
set: function(dom, value, el) {
var style = dom.style,
needsFix = el._needsTableWidthFix,
origDisplay = style.display;
if (needsFix) {
style.display = 'none';
}
style.width = value;
if (needsFix) {
// repaint
dom.scrollWidth;
// jshint ignore:line
style.display = origDisplay;
}
}
};
proto.setWidth = function(width, animate) {
var me = this,
dom = me.dom,
style = dom.style,
needsFix = me._needsTableWidthFix,
origDisplay = style.display;
if (needsFix && !animate) {
style.display = 'none';
}
origSetWidth.call(me, width, animate);
if (needsFix && !animate) {
// repaint
dom.scrollWidth;
// jshint ignore:line
style.display = origDisplay;
}
return me;
};
proto.setSize = function(width, height, animate) {
var me = this,
dom = me.dom,
style = dom.style,
needsFix = me._needsTableWidthFix,
origDisplay = style.display;
if (needsFix && !animate) {
style.display = 'none';
}
origSetSize.call(me, width, height, animate);
if (needsFix && !animate) {
// repaint
dom.scrollWidth;
// jshint ignore:line
style.display = origDisplay;
}
return me;
};
}
if (Ext.isIE8) {
styleHooks.height = {
name: 'height',
set: function(dom, value, el) {
var component = el.component,
frameInfo, frameBodyStyle;
if (component && component._syncFrameHeight && el === component.el) {
frameBodyStyle = component.frameBody.dom.style;
if (pxRe.test(value)) {
frameInfo = component.getFrameInfo();
if (frameInfo) {
frameBodyStyle.height = (parseInt(value, 10) - frameInfo.height) + 'px';
}
} else if (!value || value === 'auto') {
frameBodyStyle.height = '';
}
}
dom.style.height = value;
}
};
proto.setHeight = function(height, animate) {
var component = this.component,
frameInfo, frameBodyStyle;
if (component && component._syncFrameHeight && this === component.el) {
frameBodyStyle = component.frameBody.dom.style;
if (!height || height === 'auto') {
frameBodyStyle.height = '';
} else {
frameInfo = component.getFrameInfo();
if (frameInfo) {
frameBodyStyle.height = (height - frameInfo.height) + 'px';
}
}
}
return origSetHeight.call(this, height, animate);
};
proto.setSize = function(width, height, animate) {
var component = this.component,
frameInfo, frameBodyStyle;
if (component && component._syncFrameHeight && this === component.el) {
frameBodyStyle = component.frameBody.dom.style;
if (!height || height === 'auto') {
frameBodyStyle.height = '';
} else {
frameInfo = component.getFrameInfo();
if (frameInfo) {
frameBodyStyle.height = (height - frameInfo.height) + 'px';
}
}
}
return origSetSize.call(this, width, height, animate);
};
}
// Element.unselectable relies on this listener to prevent selection in IE. Some other browsers support the event too
// but it is only strictly required for IE. In WebKit this listener causes subtle differences to how the browser handles
// the non-selection, e.g. whether or not the mouse cursor changes when attempting to select text.
Ext.getDoc().on('selectstart', function(ev, dom) {
var selectableCls = Element.selectableCls,
unselectableCls = Element.unselectableCls,
tagName = dom && dom.tagName;
tagName = tagName && tagName.toLowerCase();
// Element.unselectable is not really intended to handle selection within text fields and it is important that
// fields inside menus or panel headers don't inherit the unselectability. In most browsers this is automatic but in
// IE 9 the selectstart event can bubble up from text fields so we have to explicitly handle that case.
if (tagName === 'input' || tagName === 'textarea') {
return;
}
// Walk up the DOM checking the nodes. This may be 'slow' but selectstart events don't fire very often
while (dom && dom.nodeType === 1 && dom !== DOC.documentElement) {
var el = Ext.fly(dom);
// If the node has the class x-selectable then stop looking, the text selection is allowed
if (el.hasCls(selectableCls)) {
return;
}
// If the node has class x-unselectable then the text selection needs to be stopped
if (el.hasCls(unselectableCls)) {
ev.stopEvent();
return;
}
dom = dom.parentNode;
}
});
function fixTransparent(dom, el, inline, style) {
var value = style[this.name] || '';
return transparentRe.test(value) ? 'transparent' : value;
}
/*
* Helper function to create the function that will restore the selection.
*/
function makeSelectionRestoreFn(activeEl, start, end) {
return function() {
activeEl.selectionStart = start;
activeEl.selectionEnd = end;
};
}
/**
* Creates a function to call to clean up problems with the work-around for the
* WebKit RightMargin bug. The work-around is to add "display: 'inline-block'" to
* the element before calling getComputedStyle and then to restore its original
* display value. The problem with this is that it corrupts the selection of an
* INPUT or TEXTAREA element (as in the "I-beam" goes away but the focus remains).
* To cleanup after this, we need to capture the selection of any such element and
* then restore it after we have restored the display style.
*
* @param {HTMLElement} target The top-most element being adjusted.
* @private
*/
function getRightMarginFixCleaner(target) {
var hasInputBug = supports.DisplayChangeInputSelectionBug,
hasTextAreaBug = supports.DisplayChangeTextAreaSelectionBug,
activeEl, tag, start, end;
if (hasInputBug || hasTextAreaBug) {
activeEl = Element.getActiveElement();
tag = activeEl && activeEl.tagName;
if ((hasTextAreaBug && tag === 'TEXTAREA') || (hasInputBug && tag === 'INPUT' && activeEl.type === 'text')) {
if (Ext.fly(target).isAncestor(activeEl)) {
start = activeEl.selectionStart;
end = activeEl.selectionEnd;
if (Ext.isNumber(start) && Ext.isNumber(end)) {
// to be safe...
// We don't create the raw closure here inline because that
// will be costly even if we don't want to return it (nested
// function decls and exprs are often instantiated on entry
// regardless of whether execution ever reaches them):
return makeSelectionRestoreFn(activeEl, start, end);
}
}
}
}
return Ext.emptyFn;
}
// avoid special cases, just return a nop
function fixRightMargin(dom, el, inline, style) {
var result = style.marginRight,
domStyle, display;
// Ignore cases when the margin is correctly reported as 0, the bug only shows
// numbers larger.
if (result !== '0px') {
domStyle = dom.style;
display = domStyle.display;
domStyle.display = 'inline-block';
result = (inline ? style : dom.ownerDocument.defaultView.getComputedStyle(dom, null)).marginRight;
domStyle.display = display;
}
return result;
}
function fixRightMarginAndInputFocus(dom, el, inline, style) {
var result = style.marginRight,
domStyle, cleaner, display;
if (result !== '0px') {
domStyle = dom.style;
cleaner = getRightMarginFixCleaner(dom);
display = domStyle.display;
domStyle.display = 'inline-block';
result = (inline ? style : dom.ownerDocument.defaultView.getComputedStyle(dom, '')).marginRight;
domStyle.display = display;
cleaner();
}
return result;
}
// Fix bug caused by this: https://bugs.webkit.org/show_bug.cgi?id=13343
if (!supports.RightMargin) {
styleHooks.marginRight = styleHooks['margin-right'] = {
name: 'marginRight',
// TODO - Touch should use conditional compilation here or ensure that the
// underlying Ext.supports flags are set correctly...
get: (supports.DisplayChangeInputSelectionBug || supports.DisplayChangeTextAreaSelectionBug) ? fixRightMarginAndInputFocus : fixRightMargin
};
}
if (!supports.TransparentColor) {
colorStyles = [
'background-color',
'border-color',
'color',
'outline-color'
];
for (i = colorStyles.length; i--; ) {
name = colorStyles[i];
camel = Element.normalize(name);
styleHooks[name] = styleHooks[camel] = {
name: camel,
get: fixTransparent
};
}
}
// When elements are rotated 80 or 270 degrees, their border, margin and padding hooks
// need to be rotated as well.
proto.verticalStyleHooks90 = verticalStyleHooks90 = Ext.Object.chain(styleHooks);
proto.verticalStyleHooks270 = verticalStyleHooks270 = Ext.Object.chain(styleHooks);
verticalStyleHooks90.width = styleHooks.height || {
name: 'height'
};
verticalStyleHooks90.height = styleHooks.width || {
name: 'width'
};
verticalStyleHooks90['margin-top'] = {
name: 'marginLeft'
};
verticalStyleHooks90['margin-right'] = {
name: 'marginTop'
};
verticalStyleHooks90['margin-bottom'] = {
name: 'marginRight'
};
verticalStyleHooks90['margin-left'] = {
name: 'marginBottom'
};
verticalStyleHooks90['padding-top'] = {
name: 'paddingLeft'
};
verticalStyleHooks90['padding-right'] = {
name: 'paddingTop'
};
verticalStyleHooks90['padding-bottom'] = {
name: 'paddingRight'
};
verticalStyleHooks90['padding-left'] = {
name: 'paddingBottom'
};
verticalStyleHooks90['border-top'] = {
name: 'borderLeft'
};
verticalStyleHooks90['border-right'] = {
name: 'borderTop'
};
verticalStyleHooks90['border-bottom'] = {
name: 'borderRight'
};
verticalStyleHooks90['border-left'] = {
name: 'borderBottom'
};
verticalStyleHooks270.width = styleHooks.height || {
name: 'height'
};
verticalStyleHooks270.height = styleHooks.width || {
name: 'width'
};
verticalStyleHooks270['margin-top'] = {
name: 'marginRight'
};
verticalStyleHooks270['margin-right'] = {
name: 'marginBottom'
};
verticalStyleHooks270['margin-bottom'] = {
name: 'marginLeft'
};
verticalStyleHooks270['margin-left'] = {
name: 'marginTop'
};
verticalStyleHooks270['padding-top'] = {
name: 'paddingRight'
};
verticalStyleHooks270['padding-right'] = {
name: 'paddingBottom'
};
verticalStyleHooks270['padding-bottom'] = {
name: 'paddingLeft'
};
verticalStyleHooks270['padding-left'] = {
name: 'paddingTop'
};
verticalStyleHooks270['border-top'] = {
name: 'borderRight'
};
verticalStyleHooks270['border-right'] = {
name: 'borderBottom'
};
verticalStyleHooks270['border-bottom'] = {
name: 'borderLeft'
};
verticalStyleHooks270['border-left'] = {
name: 'borderTop'
};
/**
* @property {Boolean} scopeCss
* @member Ext
* Set this to true before onReady to prevent any styling from being added to
* the body element. By default a few styles such as font-family, and color
* are added to the body element via a "x-body" class. When this is set to
* `true` the "x-body" class is not added to the body element, but is added
* to the elements of root-level containers instead.
*/
if (!Ext.scopeCss) {
bodyCls.push(Ext.baseCSSPrefix + 'body');
}
if (supports.Touch) {
bodyCls.push(Ext.baseCSSPrefix + 'touch');
}
if (Ext.isIE && Ext.isIE9m) {
bodyCls.push(Ext.baseCSSPrefix + 'ie', Ext.baseCSSPrefix + 'ie9m');
// very often CSS needs to do checks like "IE7+" or "IE6 or 7". To help
// reduce the clutter (since CSS/SCSS cannot do these tests), we add some
// additional classes:
//
// x-ie7p : IE7+ : 7 <= ieVer
// x-ie7m : IE7- : ieVer <= 7
// x-ie8p : IE8+ : 8 <= ieVer
// x-ie8m : IE8- : ieVer <= 8
// x-ie9p : IE9+ : 9 <= ieVer
// x-ie78 : IE7 or 8 : 7 <= ieVer <= 8
//
bodyCls.push(Ext.baseCSSPrefix + 'ie8p');
if (Ext.isIE8) {
bodyCls.push(Ext.baseCSSPrefix + 'ie8');
} else {
bodyCls.push(Ext.baseCSSPrefix + 'ie9', Ext.baseCSSPrefix + 'ie9p');
}
if (Ext.isIE8m) {
bodyCls.push(Ext.baseCSSPrefix + 'ie8m');
}
}
if (Ext.isIE10) {
bodyCls.push(Ext.baseCSSPrefix + 'ie10');
}
if (Ext.isIE10p) {
bodyCls.push(Ext.baseCSSPrefix + 'ie10p');
}
if (Ext.isIE11) {
bodyCls.push(Ext.baseCSSPrefix + 'ie11');
}
if (Ext.isEdge) {
bodyCls.push(Ext.baseCSSPrefix + 'edge');
}
if (Ext.isGecko) {
bodyCls.push(Ext.baseCSSPrefix + 'gecko');
}
if (Ext.isOpera) {
bodyCls.push(Ext.baseCSSPrefix + 'opera');
}
if (Ext.isOpera12m) {
bodyCls.push(Ext.baseCSSPrefix + 'opera12m');
}
if (Ext.isWebKit) {
bodyCls.push(Ext.baseCSSPrefix + 'webkit');
}
if (Ext.isSafari) {
bodyCls.push(Ext.baseCSSPrefix + 'safari');
}
if (Ext.isChrome) {
bodyCls.push(Ext.baseCSSPrefix + 'chrome');
}
if (Ext.isMac) {
bodyCls.push(Ext.baseCSSPrefix + 'mac');
}
if (Ext.isLinux) {
bodyCls.push(Ext.baseCSSPrefix + 'linux');
}
if (!supports.CSS3BorderRadius) {
bodyCls.push(Ext.baseCSSPrefix + 'nbr');
}
if (!supports.CSS3LinearGradient) {
bodyCls.push(Ext.baseCSSPrefix + 'nlg');
}
if (supports.Touch) {
bodyCls.push(Ext.baseCSSPrefix + 'touch');
}
if (Ext.os.deviceType) {
bodyCls.push(Ext.baseCSSPrefix + Ext.os.deviceType.toLowerCase());
}
//Ext.fly(document.documentElement).addCls(htmlCls);
Ext.getBody().addCls(bodyCls);
}, null, {
priority: 1500
});
});
// onReady
// @tag core
/**
* @class Ext.GlobalEvents
*/
Ext.define('Ext.overrides.GlobalEvents', {
override: 'Ext.GlobalEvents',
/**
* @event resumelayouts
* Fires after global layout processing has been resumed in {@link
* Ext.Component#resumeLayouts}.
*/
deprecated: {
5: {
methods: {
addListener: function(ename, fn, scope, options, order, caller, eventOptions) {
var name, readyFn;
// The "ready" event was removed from Ext.globalEvents in 5.0 in favor of
// Ext.onReady(). This function adds compatibility for the ready event
if (ename === 'ready') {
readyFn = fn;
} else if (typeof ename !== 'string') {
for (name in ename) {
if (name === 'ready') {
readyFn = ename[name];
}
}
}
if (readyFn) {
Ext.log.warn("Ext.on('ready', fn) is deprecated. Please use Ext.onReady(fn) instead.");
Ext.onReady(readyFn);
}
this.callParent([
ename,
fn,
scope,
options,
order,
caller,
eventOptions
]);
}
}
}
}
});
/**
* @class Ext.Widget
*/
Ext.define('Ext.overrides.Widget', {
override: 'Ext.Widget',
uses: [
'Ext.Component'
],
$configStrict: false,
isComponent: true,
liquidLayout: true,
// in Ext JS the rendered flag is set as soon as a component has its element. Since
// widgets always have an element when constructed, they are always considered to be
// "rendered"
rendered: true,
rendering: true,
config: {
renderTo: null
},
constructor: function(config) {
var me = this,
renderTo;
me.callParent([
config
]);
// initialize the component layout
me.getComponentLayout();
renderTo = me.getRenderTo();
if (renderTo) {
me.render(renderTo);
}
},
addClsWithUI: function(cls) {
this.el.addCls(cls);
},
afterComponentLayout: Ext.emptyFn,
updateLayout: function() {
var owner = this.getRefOwner();
if (owner) {
owner.updateLayout();
}
},
destroy: function() {
var me = this,
ownerCt = me.ownerCt;
if (ownerCt && ownerCt.remove) {
ownerCt.remove(me, false);
}
me.callParent();
},
finishRender: function() {
this.rendering = false;
this.initBindable();
},
getAnimationProps: function() {
// see Ext.util.Animate mixin
return {};
},
getComponentLayout: function() {
var me = this,
layout = me.componentLayout;
if (!layout) {
layout = me.componentLayout = new Ext.layout.component.Auto();
layout.setOwner(me);
}
return layout;
},
getEl: function() {
return this.element;
},
/**
* @private
* Needed for when widget is rendered into a grid cell. The class to add to the cell element.
* @member Ext.Widget
*/
getTdCls: function() {
return Ext.baseCSSPrefix + this.getTdType() + '-' + (this.ui || 'default') + '-cell';
},
/**
* @private
* Partner method to {@link #getTdCls}.
*
* Returns the base type for the component. Defaults to return `this.xtype`, but
* All derived classes of {@link Ext.form.field.Text TextField} can return the type 'textfield',
* and all derived classes of {@link Ext.button.Button Button} can return the type 'button'
* @member Ext.Widget
*/
getTdType: function() {
return this.xtype;
},
getItemId: function() {
// needed by ComponentQuery
return this.itemId || this.id;
},
getSizeModel: function() {
return Ext.Component.prototype.getSizeModel.apply(this, arguments);
},
onAdded: function(container, pos, instanced) {
var me = this;
me.ownerCt = container;
me.onInheritedAdd(me, instanced);
},
onRemoved: function(destroying) {
var me = this;
if (!destroying) {
me.removeBindings();
}
me.onInheritedRemove(destroying);
me.ownerCt = me.ownerLayout = null;
},
parseBox: function(box) {
return Ext.Element.parseBox(box);
},
removeClsWithUI: function(cls) {
this.el.removeCls(cls);
},
render: function(container, position) {
var me = this,
element = me.element,
proto = Ext.Component.prototype,
nextSibling;
if (!me.ownerCt || me.floating) {
if (Ext.scopeCss) {
element.addCls(proto.rootCls);
}
element.addCls(proto.borderBoxCls);
}
if (position) {
nextSibling = container.childNodes[position];
if (nextSibling) {
Ext.fly(container).insertBefore(element, nextSibling);
return;
}
}
Ext.fly(container).appendChild(element);
},
setPosition: function(x, y) {
this.el.setLocalXY(x, y);
},
up: function() {
return Ext.Component.prototype.up.apply(this, arguments);
},
isAncestor: function() {
return Ext.Component.prototype.isAncestor.apply(this, arguments);
},
onFocusEnter: function() {
return Ext.Component.prototype.onFocusEnter.apply(this, arguments);
},
onFocusLeave: function() {
return Ext.Component.prototype.onFocusLeave.apply(this, arguments);
},
isLayoutChild: function(candidate) {
var ownerCt = this.ownerCt;
return ownerCt ? (ownerCt === candidate || ownerCt.isLayoutChild(candidate)) : false;
},
privates: {
doAddListener: function(name, fn, scope, options, order, caller, manager) {
if (name == 'painted' || name == 'resize') {
this.element.doAddListener(name, fn, scope || this, options, order);
}
this.callParent([
name,
fn,
scope,
options,
order,
caller,
manager
]);
},
doRemoveListener: function(name, fn, scope) {
if (name == 'painted' || name == 'resize') {
this.element.doRemoveListener(name, fn, scope);
}
this.callParent([
name,
fn,
scope
]);
}
}
}, function(Cls) {
var prototype = Cls.prototype;
if (Ext.isIE9m) {
// Since IE8/9 don't not support Object.defineProperty correctly we can't add the reference
// nodes on demand, so we just fall back to adding all references up front.
prototype.addElementReferenceOnDemand = prototype.addElementReference;
}
});
/**
* @class Ext.Progress
*/
Ext.define('Ext.overrides.Progress', {
override: 'Ext.Progress',
config: {
ui: 'default'
},
updateWidth: function(width, oldWidth) {
var me = this;
me.callParent([
width,
oldWidth
]);
width -= me.element.getBorderWidth('lr');
me.backgroundEl.setWidth(width);
me.textEl.setWidth(width);
}
});
Ext.define('Ext.overrides.app.domain.Component', {
override: 'Ext.app.domain.Component',
requires: [
'Ext.Component'
]
}, function(ComponentDomain) {
// The core Component domain monitors events on the Ext.Widget class
// in Ext Components are not widgets so we need to monitor Ext.Component as well.
ComponentDomain.monitor(Ext.Component);
});
// This is an override because it must be loaded very early, possibly before Ext.app.Application
// in dev mode so that Ext.application() can be called.
// Being an override also ensures that it is only included in a built app if Ext.app.Application
// is present.
//
// @override Ext.app.Application
/**
* @method application
* @member Ext
* Loads Ext.app.Application class and starts it up with given configuration after the
* page is ready.
*
* See `Ext.app.Application` for details.
*
* @param {Object/String} config Application config object or name of a class derived
* from Ext.app.Application.
*/
Ext.application = function(config) {
var createApp = function(App) {
// This won't be called until App class has been created.
Ext.onReady(function() {
var Viewport = Ext.viewport;
Viewport = Viewport && Viewport['Viewport'];
if (Viewport && Viewport.setup) {
Viewport.setup(App.prototype.config.viewport);
}
Ext.app.Application.instance = new App();
});
};
if (typeof config === "string") {
Ext.require(config, function() {
createApp(Ext.ClassManager.get(config));
});
} else {
config = Ext.apply({
extend: 'Ext.app.Application'
}, // can be replaced by config!
config);
// We have to process "paths" before creating Application class,
// or `requires` won't work.
Ext.app.setupPaths(config.name, config.appFolder, config.paths);
config['paths processed'] = true;
// Let Ext.define do the hard work but don't assign a class name.
Ext.define(config.name + ".$application", config, function() {
createApp(this);
});
}
};
/**
* @class Ext.app.Application
*/
Ext.define('Ext.overrides.app.Application', {
override: 'Ext.app.Application',
uses: [
'Ext.tip.QuickTipManager'
],
// @cmd-auto-dependency {aliasPrefix: "view.", mvc: true, requires: ["Ext.plugin.Viewport"]}
/**
* @cfg {Boolean/String} [autoCreateViewport=false]
* @deprecated 5.1 Instead use {@link #mainView}
* @member Ext.app.Application
*/
autoCreateViewport: false,
config: {
/**
* @cfg {Boolean} enableQuickTips
* @deprecated 6.2.0 Use {@link #quickTips}.
*/
enableQuickTips: null
},
/**
* @cfg {Boolean} quickTips
* True to automatically set up Ext.tip.QuickTip support.
*
* @since 6.2.0
*/
quickTips: true,
updateEnableQuickTips: function(enableQuickTips) {
this.setQuickTips(enableQuickTips);
},
applyMainView: function(mainView) {
var view, proto, config, plugins;
if (typeof mainView === 'string') {
view = this.getView(mainView);
} else {
view = Ext.ClassManager.getByConfig(mainView);
}
proto = view.prototype;
if (!proto.isViewport) {
plugins = proto.plugins;
// Need to copy over any plugins defined on the prototype.
plugins = [
'viewport'
].concat(plugins ? Ext.Array.from(plugins, true) : []);
config = {
plugins: plugins
};
}
return view.create(config);
},
getDependencies: function(cls, data, requires) {
var Controller = Ext.app.Controller,
proto = cls.prototype,
namespace = data.$namespace,
viewportClass = data.autoCreateViewport;
if (viewportClass) {
if (!namespace) {
Ext.raise("[Ext.app.Application] Can't resolve namespace for " + data.$className + ", did you forget to specify 'name' property?");
}
if (viewportClass === true) {
viewportClass = 'Viewport';
} else {
requires.push('Ext.plugin.Viewport');
}
Controller.processDependencies(proto, requires, namespace, 'view', viewportClass);
}
},
onBeforeLaunch: function() {
var me = this,
autoCreateViewport = me.autoCreateViewport;
if (me.getQuickTips()) {
me.initQuickTips();
}
if (autoCreateViewport) {
me.initViewport();
}
this.callParent(arguments);
},
getViewportName: function() {
var name = null,
autoCreate = this.autoCreateViewport;
if (autoCreate) {
name = (autoCreate === true) ? 'Viewport' : autoCreate;
}
return name;
},
initViewport: function() {
this.setMainView(this.getViewportName());
},
initQuickTips: function() {
Ext.tip.QuickTipManager.init();
}
});
Ext.define('Ext.overrides.app.domain.View', {
override: 'Ext.app.domain.View',
requires: [
'Ext.Component'
],
constructor: function(controller) {
this.callParent([
controller
]);
// The base class handles Ext.Widget, which encompasses
// component for modern, so we only need the override here.
this.monitoredClasses.push(Ext.Component);
}
});
/**
* @class Ext.dom.Helper
*/
Ext.define('Ext.overrides.dom.Helper', (function() {
var tableRe = /^(?:table|thead|tbody|tr|td)$/i,
tableElRe = /td|tr|tbody|thead/i,
ts = '<table>',
te = '</table>',
tbs = ts + '<tbody>',
tbe = '</tbody>' + te,
trs = tbs + '<tr>',
tre = '</tr>' + tbe;
return {
override: 'Ext.dom.Helper',
ieInsertHtml: function(where, el, html) {
var frag = null;
// IE's incomplete table implementation: http://www.ericvasilik.com/2006/07/code-karma.html
if (Ext.isIE9m && tableRe.test(el.tagName)) {
frag = this.insertIntoTable(el.tagName.toLowerCase(), where, el, html);
}
return frag;
},
ieOverwrite: function(el, html) {
// IE Inserting HTML into a table/tbody/tr requires extra processing:
// http://www.ericvasilik.com/2006/07/code-karma.html
if (Ext.isIE9m && tableRe.test(el.tagName)) {
// Clearing table elements requires removal of all elements.
while (el.firstChild) {
el.removeChild(el.firstChild);
}
if (html) {
return this.insertHtml('afterbegin', el, html);
}
}
},
ieTable: function(depth, openingTags, htmlContent, closingTags) {
var i = -1,
el = this.detachedDiv,
ns, nx;
el.innerHTML = [
openingTags,
htmlContent,
closingTags
].join('');
while (++i < depth) {
el = el.firstChild;
}
// If the result is multiple siblings, then encapsulate them into one fragment.
ns = el.nextSibling;
if (ns) {
ns = el;
el = document.createDocumentFragment();
while (ns) {
nx = ns.nextSibling;
el.appendChild(ns);
ns = nx;
}
}
return el;
},
/**
* @private
* @method insertIntoTable
* @member Ext.dom.Helper
* workaround for broken table implementation in IE9m
* http://www.ericvasilik.com/2006/07/code-karma.html
*/
insertIntoTable: function(tag, where, destinationEl, html) {
var node, before,
bb = where === 'beforebegin',
ab = where === 'afterbegin',
be = where === 'beforeend',
ae = where === 'afterend';
if (tag === 'td' && (ab || be) || !tableElRe.test(tag) && (bb || ae)) {
return null;
}
before = bb ? destinationEl : ae ? destinationEl.nextSibling : ab ? destinationEl.firstChild : null;
if (bb || ae) {
destinationEl = destinationEl.parentNode;
}
if (tag === 'td' || (tag === 'tr' && (be || ab))) {
node = this.ieTable(4, trs, html, tre);
} else if (((tag === 'tbody' || tag === 'thead') && (be || ab)) || (tag === 'tr' && (bb || ae))) {
node = this.ieTable(3, tbs, html, tbe);
} else {
node = this.ieTable(2, ts, html, te);
}
destinationEl.insertBefore(node, before);
return node;
}
};
})());
/**
* @class Ext.list.TreeItem
*/
Ext.define('Ext.overrides.list.TreeItem', {
override: 'Ext.list.TreeItem',
config: {
floated: null
},
// Implement a setter.
// There *is* no "floated" config in Classic.
// We're still an inner item, we just get put inside a Container.
setFloated: function(floated) {
var me = this,
el = me.element,
placeholder = me.placeholder,
node, wasExpanded;
if (me.treeItemFloated !== floated) {
if (floated) {
placeholder = el.clone(false, true);
// shallow, asDom
placeholder.id += '-placeholder';
// avoid duplicate id
me.placeholder = Ext.get(placeholder);
me.wasExpanded = me.getExpanded();
me.setExpanded(true);
el.addCls(me.floatedCls);
el.dom.parentNode.insertBefore(placeholder, el.dom);
me.floater = me.createFloater();
}
// toolkit-specific
else if (placeholder) {
wasExpanded = me.wasExpanded;
node = me.getNode();
me.setExpanded(wasExpanded);
if (!wasExpanded && node.isExpanded()) {
// If we have been floating and expanded a child, we may have been
// expanded as part of the ancestors. Attempt to restore state.
me.preventAnimation = true;
node.collapse();
me.preventAnimation = false;
}
me.floater.remove(me, false);
// don't destroy
el.removeCls(me.floatedCls);
placeholder.dom.parentNode.insertBefore(el.dom, placeholder.dom);
placeholder.destroy();
me.floater.destroy();
me.placeholder = me.floater = null;
}
// Use an internal property name. We are NOT really floated
me.treeItemFloated = floated;
}
},
getFloated: function() {
return this.treeItemFloated;
},
runAnimation: function(animation) {
return this.itemContainer.addAnimation(animation);
},
stopAnimation: function(animation) {
animation.jumpToEnd();
},
privates: {
createFloater: function() {
var me = this,
owner = me.getOwner(),
ownerTree = me.up('treelist'),
floater,
toolElement = me.getToolElement();
me.floater = floater = new Ext.container.Container({
cls: ownerTree.self.prototype.element.cls + ' ' + ownerTree.uiPrefix + ownerTree.getUi() + ' ' + Ext.baseCSSPrefix + 'treelist-floater',
floating: true,
// We do not get element resize events on IE8
// so fall back to 6.0.1 sizing to 200 wide.
width: Ext.isIE8 ? 200 : (ownerTree.expandedWidth - toolElement.getWidth()),
shadow: false,
renderTo: Ext.getBody(),
listeners: {
element: 'el',
click: function(e) {
return owner.onClick(e);
}
}
});
floater.add(me);
floater.show();
floater.el.alignTo(toolElement, 'tr?');
return floater;
}
}
});
/**
* @class Ext.plugin.Abstract
*/
Ext.define('Ext.overrides.plugin.Abstract', {
override: 'Ext.plugin.Abstract',
$configStrict: false,
$configPrefixed: false,
disabled: false,
/**
* @cfg {String|Array} stateEvents
* The configured list of stateEvents used to (optionally) participate in Owner Component's state management.
* @member Ext.plugin.Abstract
*/
/**
* @method
* The getState method is invoked by the client Component's State mixin when one or more of the the specified {@link #stateEvents} are raised.
*
* The supplied implementation is empty. If plugin Subclasses are to (optionally) participate in the client Component's
* state management, implementers should provide a suitable method which returns a state object.
* @return {Object} state
* @member Ext.plugin.Abstract
*/
getState: null,
/**
* @method
* The applyState method is invoked by the client Component's State mixin after initComponent method has been run for the client.
*
* The supplied implementation is empty. If plugin Subclasses are to (optionally) participate in the client Component's
* state management, implementers should provide a suitable method to utilize it.
* @param {Object} state The current plugin state object to be applied.
* @param {Object} allState The current aggregate state of the Component and all plugins.
* @member Ext.plugin.Abstract
*/
applyState: null,
/**
* The base implementation just sets the plugin's `disabled` flag to `false`
*
* Plugin subclasses which need more complex processing may implement an overriding implementation.
* @member Ext.plugin.Abstract
*/
enable: function() {
this.disabled = false;
},
/**
* The base implementation just sets the plugin's `disabled` flag to `true`
*
* Plugin subclasses which need more complex processing may implement an overriding implementation.
* @member Ext.plugin.Abstract
*/
disable: function() {
this.disabled = true;
}
});
/**
* @class Ext.sparkline.Base
*/
Ext.define('Ext.override.sparkline.Base', {
override: 'Ext.sparkline.Base',
statics: {
constructTip: function() {
return new Ext.tip['ToolTip']({
id: 'sparklines-tooltip',
showDelay: 0,
dismissDelay: 0,
hideDelay: 400
});
}
},
onMouseMove: function(e) {
this.tooltip.triggerEvent = e;
this.callParent([
e
]);
},
onMouseLeave: function(e) {
this.callParent([
e
]);
this.tooltip.target = null;
},
privates: {
hideTip: function() {
var tip = this.tooltip;
tip.target = null;
tip.hide();
},
showTip: function() {
var tip = this.tooltip;
tip.target = this.el;
tip.onTargetOver(tip.triggerEvent);
}
}
});
/**
* @class Ext.app.ViewController
*/
/**
* @method beforeRender
* @template
* Template method called by the owning component's
* {@link Ext.Component#method-beforeRender beforeRender} method.
* @param {Ext.Component} component The owner component attached to the
* ViewController
*/
/**
* @method afterRender
* @template
* Template method called by the owning component's
* {@link Ext.Component#method-afterRender afterRender} method.
* @param {Ext.Component} component The owner component attached to the
* ViewController
*/
/**
* @method boxReady
* @template
* Template method called by the owning component's
* {@link Ext.Component#method-onBoxReady onBoxReady} method.
* @param {Ext.Component} component The owner component attached to the
* ViewController
*/
Ext.define(null, {
override: 'Ext.event.publisher.Focus',
compatibility: Ext.isIE10m,
publishDelegatedDomEvent: function(e) {
var body = document.body,
el = Ext.synchronouslyFocusing;
// This horrid hack is necessary to work around the issue with input elements
// in IE10m that can fail to focus under certain conditions. See comment in
// Ext.dom.Element override.
if (el && ((e.type === 'focusout' && (e.srcElement === el || e.srcElement === window) && e.toElement === body) || (e.type === 'focusin' && (e.srcElement === body || e.srcElement === window) && e.fromElement === el && e.toElement === null))) {
return;
}
this.callParent([
e
]);
}
});
Ext.define(null, {
override: 'Ext.form.field.Checkbox',
compatibility: Ext.isIE8,
// IE8 does not support change event but it has propertychange which is even better
changeEventName: 'propertychange',
onChangeEvent: function(e) {
// IE8 propertychange fires for *any* property change but we're only interested in checked
// We also don't want to react to propertychange fired as the result of assigning
// checked property in setRawValue().
if (this.duringSetRawValue || e.browserEvent.propertyName !== 'checked') {
return;
}
this.callParent([
e
]);
},
updateCheckedCls: function(checked) {
var me = this,
displayEl = me.displayEl;
me.callParent([
checked
]);
// IE8 has a bug with font icons and pseudo-elements
if (displayEl && checked !== me.lastValue) {
displayEl.repaint();
}
}
});
Ext.define(null, {
override: 'Ext.form.field.Radio',
compatibility: Ext.isIE8,
getSubTplData: function(fieldData) {
var data = this.callParent([
fieldData
]);
// Rendering a radio button with checked attribute
// will have a curious side effect in IE8: the DOM
// node will have checked property set to `true` but
// radio group (radios with the same name attribute)
// will behave as if no radio is checked in the group;
// tabbing into the group will select first or last
// button instead of the checked one.
// So instead of rendering the attribute we will set
// checked value in the DOM after rendering. Apparently
// such a tiny nudge is enough for the browser to behave.
delete data.checked;
return data;
},
afterRender: function() {
this.callParent();
if (this.checked) {
this.inputEl.dom.checked = true;
}
},
onChange: function(newValue, oldValue) {
// We don't need to bother updating other radio buttons in IE8
// since it will fire propertychange event on any change, not only false -> true.
// This is unlike standard compliant browsers, see main class.
this.callSuper([
newValue,
oldValue
]);
}
});
Ext.define(null, {
override: 'Ext.scroll.Scroller',
compatibility: Ext.isIE8,
privates: {
// Important note: this code had to be copied as a whole
// because the scrollLeft assignment trickery only works
// reliably when it is done within the same function context.
doScrollTo: function(x, y, animate) {
var me = this,
element = me.getElement(),
maxPosition, dom, to, xInf, yInf;
if (element && !element.destroyed) {
dom = this.getElement().dom;
xInf = (x === Infinity);
yInf = (y === Infinity);
if (xInf || yInf) {
maxPosition = me.getMaxPosition();
if (xInf) {
x = maxPosition.x;
}
if (yInf) {
y = maxPosition.y;
}
}
x = me.convertX(x);
if (animate) {
to = {};
if (y != null) {
to.scrollTop = y;
}
if (x != null) {
to.scrollLeft = x;
}
element.animate(Ext.mergeIf({
to: {
scrollTop: y,
scrollLeft: x
}
}, animate));
} else {
// When we need to assign both scrollTop and scrollLeft,
// IE8 might fire scroll event on the first assignment
// but not on the second; that behavior is unlike the other
// browsers which will wait for the second assignment
// to happen before firing the event. This leads to our
// scrollstart event firing prematurely, when the scrolling
// has not actually finished yet.
// To work around that, we ignore the first event and then
// force another one by assigning scrollLeft the second time.
if (x != null && y != null) {
me.deferDomScroll = true;
}
if (y != null) {
dom.scrollTop = y;
}
if (x != null) {
dom.scrollLeft = x;
}
if (me.deferDomScroll) {
me.deferDomScroll = false;
// Reading the DOM makes sure the second assignment
// will fire the event.
+dom.scrollLeft;
dom.scrollLeft = x;
}
}
// Our position object will need refreshing before returning.
me.positionDirty = true;
}
},
onDomScroll: function() {
var me = this;
if (me.deferDomScroll) {
return;
}
me.callParent();
}
}
});