Upgrade to ExtJS 3.0.3 - Released 10/11/2009
[extjs.git] / docs / resources / docs.js
1 Ext.BLANK_IMAGE_URL = 'resources/s.gif';
2
3 Docs = {};
4
5 ApiPanel = function() {
6     ApiPanel.superclass.constructor.call(this, {
7         id:'api-tree',
8         region:'west',
9         split:true,
10         width: 280,
11         minSize: 175,
12         maxSize: 500,
13         collapsible: true,
14         margins:'0 0 5 5',
15         cmargins:'0 0 0 0',
16         rootVisible:false,
17         lines:false,
18         autoScroll:true,
19         animCollapse:false,
20         animate: false,
21         collapseMode:'mini',
22         loader: new Ext.tree.TreeLoader({
23             preloadChildren: true,
24             clearOnLoad: false
25         }),
26         root: new Ext.tree.AsyncTreeNode({
27             text:'Ext JS',
28             id:'root',
29             expanded:true,
30             children:[Docs.classData]
31          }),
32         collapseFirst:false
33     });
34     // no longer needed!
35     //new Ext.tree.TreeSorter(this, {folderSort:true,leafAttr:'isClass'});
36
37     this.getSelectionModel().on('beforeselect', function(sm, node){
38         return node.isLeaf();
39     });
40 };
41
42 Ext.extend(ApiPanel, Ext.tree.TreePanel, {
43     selectClass : function(cls){
44         if(cls){
45             var parts = cls.split('.');
46             var last = parts.length-1;
47             var res = [];
48             var pkg = [];
49             for(var i = 0; i < last; i++){ // things get nasty - static classes can have .
50                 var p = parts[i];
51                 var fc = p.charAt(0);
52                 var staticCls = fc.toUpperCase() == fc;
53                 if(p == 'Ext' || !staticCls){
54                     pkg.push(p);
55                     res[i] = 'pkg-'+pkg.join('.');
56                 }else if(staticCls){
57                     --last;
58                     res.splice(i, 1);
59                 }
60             }
61             res[last] = cls;
62
63             this.selectPath('/root/apidocs/'+res.join('/'));
64         }
65     }
66 });
67
68
69 DocPanel = Ext.extend(Ext.Panel, {
70     closable: true,
71     autoScroll:true,
72
73     initComponent : function(){
74         var ps = this.cclass.split('.');
75         this.title = ps[ps.length-1];
76
77         DocPanel.superclass.initComponent.call(this);
78     },
79
80     scrollToMember : function(member){
81         var el = Ext.fly(this.cclass + '-' + member);
82         if(el){
83             var top = (el.getOffsetsTo(this.body)[1]) + this.body.dom.scrollTop;
84             this.body.scrollTo('top', top-25, {duration:.75, callback: this.hlMember.createDelegate(this, [member])});
85         }
86     },
87
88     scrollToSection : function(id){
89         var el = Ext.getDom(id);
90         if(el){
91             var top = (Ext.fly(el).getOffsetsTo(this.body)[1]) + this.body.dom.scrollTop;
92             this.body.scrollTo('top', top-25, {duration:.5, callback: function(){
93                 Ext.fly(el).next('h2').pause(.2).highlight('#8DB2E3', {attr:'color'});
94             }});
95         }
96     },
97
98     hlMember : function(member){
99         var el = Ext.fly(this.cclass + '-' + member);
100         if(el){
101             el.up('tr').highlight('#cadaf9');
102         }
103     }
104 });
105
106
107 MainPanel = function(){
108
109     this.searchStore = new Ext.data.Store({
110         proxy: new Ext.data.ScriptTagProxy({
111             url: 'http://extjs.com/playpen/api.php'
112         }),
113         reader: new Ext.data.JsonReader({
114                 root: 'data'
115             },
116             ['cls', 'member', 'type', 'doc']
117         ),
118         baseParams: {},
119         listeners: {
120             'beforeload' : function(){
121                 this.baseParams.qt = Ext.getCmp('search-type').getValue();
122             }
123         }
124     });
125
126     MainPanel.superclass.constructor.call(this, {
127         id:'doc-body',
128         region:'center',
129         margins:'0 5 5 0',
130         resizeTabs: true,
131         minTabWidth: 135,
132         tabWidth: 135,
133         plugins: new Ext.ux.TabCloseMenu(),
134         enableTabScroll: true,
135         activeTab: 0,
136
137         items: {
138             id:'welcome-panel',
139             title: 'API Home',
140             autoLoad: {url: 'welcome.html', callback: this.initSearch, scope: this},
141             iconCls:'icon-docs',
142             autoScroll: true,
143             tbar: [
144                 'Search: ', ' ',
145                 new Ext.ux.SelectBox({
146                     listClass:'x-combo-list-small',
147                     width:90,
148                     value:'Starts with',
149                     id:'search-type',
150                     store: new Ext.data.SimpleStore({
151                         fields: ['text'],
152                         expandData: true,
153                         data : ['Starts with', 'Ends with', 'Any match']
154                     }),
155                     displayField: 'text'
156                 }), ' ',
157                 new Ext.app.SearchField({
158                     width:240,
159                     store: this.searchStore,
160                     paramName: 'q'
161                 })
162             ]
163         }
164     });
165 };
166
167 Ext.extend(MainPanel, Ext.TabPanel, {
168
169     initEvents : function(){
170         MainPanel.superclass.initEvents.call(this);
171         this.body.on('click', this.onClick, this);
172         this.body.on({
173             mouseover: this.onMarginEnter,
174             mouseout: this.onMarginLeave,
175             scope: this,
176             delegate: 'td.micon'
177         });
178         new Ext.ToolTip({
179             renderTo: document.body,
180             target: this.body,
181             delegate: 'td.micon',
182             showDelay: 100,
183             dismissDelay: 1000,
184             listeners: {
185 //              Don't show if you *can't* expand it.
186                 beforeshow: function(t) {
187                     var tr = Ext.get(t.triggerElement.parentNode);
188                     var result = tr.hasClass("expandable");
189                     if (result) {
190                         t.body.dom.innerHTML = (tr.hasClass("expanded")) ? "Click to collapse" : "Click to expand";
191                     }
192                     return result;
193                 }
194             }
195         });
196     },
197
198     onMarginEnter: function(e) {
199         var t = e.getTarget('td.micon', 2, true);
200         if (t && Ext.fly(t.dom.parentNode).hasClass("expandable")) {
201             t.addClass('over');
202         }
203     },
204
205     onMarginLeave: function(e) {
206         var t = e.getTarget('td.micon', 2, true), toTd = Ext.get(e.getRelatedTarget());
207         if (toTd) toTd = toTd.up('td.micon', 2);
208         if (!toTd || (toTd !== t)) {
209                t.removeClass('over');
210         }
211     },
212
213     onClick: function(e, target){
214         if(target = e.getTarget('a:not(.exi)', 3)){
215             var cls = Ext.fly(target).getAttributeNS('ext', 'cls');
216             e.stopEvent();
217             if(cls){
218                 var member = Ext.fly(target).getAttributeNS('ext', 'member');
219                 this.loadClass(target.href, cls, member);
220             }else if(target.className == 'inner-link'){
221                 this.getActiveTab().scrollToSection(target.href.split('#')[1]);
222             }else{
223                 window.open(target.href);
224             }
225         }else if(target = e.getTarget('.micon', 2)){
226             e.stopEvent();
227             var tr = Ext.fly(target.parentNode);
228             if(tr.hasClass('expandable')){
229                 tr.toggleClass('expanded');
230             }
231         }
232     },
233
234     loadClass : function(href, cls, member){
235         var id = 'docs-' + cls;
236         var tab = this.getComponent(id);
237         if(tab){
238             this.setActiveTab(tab);
239             if(member){
240                 tab.scrollToMember(member);
241             }
242         }else{
243             var autoLoad = {url: href};
244             if(member){
245                 autoLoad.callback = function(){
246                     Ext.getCmp(id).scrollToMember(member);
247                 }
248             }
249             var p = this.add(new DocPanel({
250                 id: id,
251                 cclass : cls,
252                 autoLoad: autoLoad,
253                 iconCls: Docs.icons[cls]
254             }));
255             this.setActiveTab(p);
256         }
257     },
258
259     initSearch : function(){
260         // Custom rendering Template for the View
261         var resultTpl = new Ext.XTemplate(
262             '<tpl for=".">',
263             '<div class="search-item">',
264                 '<a class="member" ext:cls="{cls}" ext:member="{member}" href="output/{cls}.html">',
265                 '<img src="resources/images/default/s.gif" class="item-icon icon-{type}"/>{member}',
266                 '</a> ',
267                 '<a class="cls" ext:cls="{cls}" href="output/{cls}.html">{cls}</a>',
268                 '<p>{doc}</p>',
269             '</div></tpl>'
270         );
271
272         var p = new Ext.DataView({
273             applyTo: 'search',
274             tpl: resultTpl,
275             loadingText:'Searching...',
276             store: this.searchStore,
277             itemSelector: 'div.search-item',
278             emptyText: '<h3>Use the search field above to search the Ext API for classes, properties, config options, methods and events.</h3>'
279         });
280     },
281
282     doSearch : function(e){
283         var k = e.getKey();
284         if(!e.isSpecialKey()){
285             var text = e.target.value;
286             if(!text){
287                 this.searchStore.baseParams.q = '';
288                 this.searchStore.removeAll();
289             }else{
290                 this.searchStore.baseParams.q = text;
291                 this.searchStore.reload();
292             }
293         }
294     }
295 });
296
297
298 Ext.onReady(function(){
299
300     Ext.QuickTips.init();
301
302     var api = new ApiPanel();
303     var mainPanel = new MainPanel();
304
305     api.on('click', function(node, e){
306          if(node.isLeaf()){
307             e.stopEvent();
308             mainPanel.loadClass(node.attributes.href, node.id);
309          }
310     });
311
312     mainPanel.on('tabchange', function(tp, tab){
313         api.selectClass(tab.cclass);
314     });
315
316     var hd = new Ext.Panel({
317         border: false,
318         layout:'anchor',
319         region:'north',
320         cls: 'docs-header',
321         height:60,
322         items: [{
323             xtype:'box',
324             el:'header',
325             border:false,
326             anchor: 'none -25'
327         },
328         new Ext.Toolbar({
329             cls:'top-toolbar',
330             items:[ ' ',
331             new Ext.form.TextField({
332                 width: 200,
333                 emptyText:'Find a Class',
334                 listeners:{
335                     render: function(f){
336                         f.el.on('keydown', filterTree, f, {buffer: 350});
337                     }
338                 }
339             }), ' ', ' ',
340             {
341                 iconCls: 'icon-expand-all',
342                 tooltip: 'Expand All',
343                 handler: function(){ api.root.expand(true); }
344             }, '-', {
345                 iconCls: 'icon-collapse-all',
346                 tooltip: 'Collapse All',
347                 handler: function(){ api.root.collapse(true); }
348             }, '->', {
349                 tooltip:'Hide Inherited Members',
350                 iconCls: 'icon-hide-inherited',
351                 enableToggle: true,
352                 toggleHandler : function(b, pressed){
353                      mainPanel[pressed ? 'addClass' : 'removeClass']('hide-inherited');
354                 }
355             }, '-', {
356                 tooltip:'Expand All Members',
357                 iconCls: 'icon-expand-members',
358                 enableToggle: true,
359                 toggleHandler : function(b, pressed){
360                     mainPanel[pressed ? 'addClass' : 'removeClass']('full-details');
361                 }
362             }]
363         })]
364     })
365
366     var viewport = new Ext.Viewport({
367         layout:'border',
368         items:[ hd, api, mainPanel ]
369     });
370
371     api.expandPath('/root/apidocs');
372
373     // allow for link in
374     var page = window.location.href.split('?')[1];
375     if(page){
376         var ps = Ext.urlDecode(page);
377         var cls = ps['class'];
378         mainPanel.loadClass('output/' + cls + '.html', cls, ps.member);
379     }
380
381     viewport.doLayout();
382
383     setTimeout(function(){
384         Ext.get('loading').remove();
385         Ext.get('loading-mask').fadeOut({remove:true});
386     }, 250);
387
388     var filter = new Ext.tree.TreeFilter(api, {
389         clearBlank: true,
390         autoClear: true
391     });
392     var hiddenPkgs = [];
393     function filterTree(e){
394         var text = e.target.value;
395         Ext.each(hiddenPkgs, function(n){
396             n.ui.show();
397         });
398         if(!text){
399             filter.clear();
400             return;
401         }
402         api.expandAll();
403
404         var re = new RegExp('^' + Ext.escapeRe(text), 'i');
405         filter.filterBy(function(n){
406             return !n.attributes.isClass || re.test(n.text);
407         });
408
409         // hide empty packages that weren't filtered
410         hiddenPkgs = [];
411         api.root.cascade(function(n){
412             if(!n.attributes.isClass && n.ui.ctNode.offsetHeight < 3){
413                 n.ui.hide();
414                 hiddenPkgs.push(n);
415             }
416         });
417     }
418
419 });
420
421
422 Ext.app.SearchField = Ext.extend(Ext.form.TwinTriggerField, {
423     initComponent : function(){
424         if(!this.store.baseParams){
425             this.store.baseParams = {};
426         }
427         Ext.app.SearchField.superclass.initComponent.call(this);
428         this.on('specialkey', function(f, e){
429             if(e.getKey() == e.ENTER){
430                 this.onTrigger2Click();
431             }
432         }, this);
433     },
434
435     validationEvent:false,
436     validateOnBlur:false,
437     trigger1Class:'x-form-clear-trigger',
438     trigger2Class:'x-form-search-trigger',
439     hideTrigger1:true,
440     width:180,
441     hasSearch : false,
442     paramName : 'query',
443
444     onTrigger1Click : function(){
445         if(this.hasSearch){
446             this.store.baseParams[this.paramName] = '';
447             this.store.removeAll();
448             this.el.dom.value = '';
449             this.triggers[0].hide();
450             this.hasSearch = false;
451             this.focus();
452         }
453     },
454
455     onTrigger2Click : function(){
456         var v = this.getRawValue();
457         if(v.length < 1){
458             this.onTrigger1Click();
459             return;
460         }
461         if(v.length < 2){
462             Ext.Msg.alert('Invalid Search', 'You must enter a minimum of 2 characters to search the API');
463             return;
464         }
465         this.store.baseParams[this.paramName] = v;
466         var o = {start: 0};
467         this.store.reload({params:o});
468         this.hasSearch = true;
469         this.triggers[0].show();
470         this.focus();
471     }
472 });
473
474
475 /**
476  * Makes a ComboBox more closely mimic an HTML SELECT.  Supports clicking and dragging
477  * through the list, with item selection occurring when the mouse button is released.
478  * When used will automatically set {@link #editable} to false and call {@link Ext.Element#unselectable}
479  * on inner elements.  Re-enabling editable after calling this will NOT work.
480  *
481  * @author Corey Gilmore
482  * http://extjs.com/forum/showthread.php?t=6392
483  *
484  * @history 2007-07-08 jvs
485  * Slight mods for Ext 2.0
486  */
487 Ext.ux.SelectBox = function(config){
488     this.searchResetDelay = 1000;
489     config = config || {};
490     config = Ext.apply(config || {}, {
491         editable: false,
492         forceSelection: true,
493         rowHeight: false,
494         lastSearchTerm: false,
495         triggerAction: 'all',
496         mode: 'local'
497     });
498
499     Ext.ux.SelectBox.superclass.constructor.apply(this, arguments);
500
501     this.lastSelectedIndex = this.selectedIndex || 0;
502 };
503
504 Ext.extend(Ext.ux.SelectBox, Ext.form.ComboBox, {
505     lazyInit: false,
506     initEvents : function(){
507         Ext.ux.SelectBox.superclass.initEvents.apply(this, arguments);
508         // you need to use keypress to capture upper/lower case and shift+key, but it doesn't work in IE
509         this.el.on('keydown', this.keySearch, this, true);
510         this.cshTask = new Ext.util.DelayedTask(this.clearSearchHistory, this);
511     },
512
513     keySearch : function(e, target, options) {
514         var raw = e.getKey();
515         var key = String.fromCharCode(raw);
516         var startIndex = 0;
517
518         if( !this.store.getCount() ) {
519             return;
520         }
521
522         switch(raw) {
523             case Ext.EventObject.HOME:
524                 e.stopEvent();
525                 this.selectFirst();
526                 return;
527
528             case Ext.EventObject.END:
529                 e.stopEvent();
530                 this.selectLast();
531                 return;
532
533             case Ext.EventObject.PAGEDOWN:
534                 this.selectNextPage();
535                 e.stopEvent();
536                 return;
537
538             case Ext.EventObject.PAGEUP:
539                 this.selectPrevPage();
540                 e.stopEvent();
541                 return;
542         }
543
544         // skip special keys other than the shift key
545         if( (e.hasModifier() && !e.shiftKey) || e.isNavKeyPress() || e.isSpecialKey() ) {
546             return;
547         }
548         if( this.lastSearchTerm == key ) {
549             startIndex = this.lastSelectedIndex;
550         }
551         this.search(this.displayField, key, startIndex);
552         this.cshTask.delay(this.searchResetDelay);
553     },
554
555     onRender : function(ct, position) {
556         this.store.on('load', this.calcRowsPerPage, this);
557         Ext.ux.SelectBox.superclass.onRender.apply(this, arguments);
558         if( this.mode == 'local' ) {
559             this.calcRowsPerPage();
560         }
561     },
562
563     onSelect : function(record, index, skipCollapse){
564         if(this.fireEvent('beforeselect', this, record, index) !== false){
565             this.setValue(record.data[this.valueField || this.displayField]);
566             if( !skipCollapse ) {
567                 this.collapse();
568             }
569             this.lastSelectedIndex = index + 1;
570             this.fireEvent('select', this, record, index);
571         }
572     },
573
574     render : function(ct) {
575         Ext.ux.SelectBox.superclass.render.apply(this, arguments);
576         if( Ext.isSafari ) {
577             this.el.swallowEvent('mousedown', true);
578         }
579         this.el.unselectable();
580         this.innerList.unselectable();
581         this.trigger.unselectable();
582         this.innerList.on('mouseup', function(e, target, options) {
583             if( target.id && target.id == this.innerList.id ) {
584                 return;
585             }
586             this.onViewClick();
587         }, this);
588
589         this.innerList.on('mouseover', function(e, target, options) {
590             if( target.id && target.id == this.innerList.id ) {
591                 return;
592             }
593             this.lastSelectedIndex = this.view.getSelectedIndexes()[0] + 1;
594             this.cshTask.delay(this.searchResetDelay);
595         }, this);
596
597         this.trigger.un('click', this.onTriggerClick, this);
598         this.trigger.on('mousedown', function(e, target, options) {
599             e.preventDefault();
600             this.onTriggerClick();
601         }, this);
602
603         this.on('collapse', function(e, target, options) {
604             Ext.getDoc().un('mouseup', this.collapseIf, this);
605         }, this, true);
606
607         this.on('expand', function(e, target, options) {
608             Ext.getDoc().on('mouseup', this.collapseIf, this);
609         }, this, true);
610     },
611
612     clearSearchHistory : function() {
613         this.lastSelectedIndex = 0;
614         this.lastSearchTerm = false;
615     },
616
617     selectFirst : function() {
618         this.focusAndSelect(this.store.data.first());
619     },
620
621     selectLast : function() {
622         this.focusAndSelect(this.store.data.last());
623     },
624
625     selectPrevPage : function() {
626         if( !this.rowHeight ) {
627             return;
628         }
629         var index = Math.max(this.selectedIndex-this.rowsPerPage, 0);
630         this.focusAndSelect(this.store.getAt(index));
631     },
632
633     selectNextPage : function() {
634         if( !this.rowHeight ) {
635             return;
636         }
637         var index = Math.min(this.selectedIndex+this.rowsPerPage, this.store.getCount() - 1);
638         this.focusAndSelect(this.store.getAt(index));
639     },
640
641     search : function(field, value, startIndex) {
642         field = field || this.displayField;
643         this.lastSearchTerm = value;
644         var index = this.store.find.apply(this.store, arguments);
645         if( index !== -1 ) {
646             this.focusAndSelect(index);
647         }
648     },
649
650     focusAndSelect : function(record) {
651         var index = typeof record === 'number' ? record : this.store.indexOf(record);
652         this.select(index, this.isExpanded());
653         this.onSelect(this.store.getAt(record), index, this.isExpanded());
654     },
655
656     calcRowsPerPage : function() {
657         if( this.store.getCount() ) {
658             this.rowHeight = Ext.fly(this.view.getNode(0)).getHeight();
659             this.rowsPerPage = this.maxHeight / this.rowHeight;
660         } else {
661             this.rowHeight = false;
662         }
663     }
664
665 });
666
667 Ext.Ajax.on('requestcomplete', function(ajax, xhr, o){
668     if(typeof urchinTracker == 'function' && o && o.url){
669         urchinTracker(o.url);
670     }
671 });