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