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