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