cf6ceb2827347050a2de00df214cbcaef1716e6b
[roojs1] / Roo / form / HtmlEditor / ToolbarContext.js
1 // <script type="text/javascript">
2 /*
3  * Based on
4  * Ext JS Library 1.1.1
5  * Copyright(c) 2006-2007, Ext JS, LLC.
6  *  
7  
8  */
9
10  
11 /**
12  * @class Roo.form.HtmlEditor.ToolbarContext
13  * Context Toolbar
14  * 
15  * Usage:
16  *
17  new Roo.form.HtmlEditor({
18     ....
19     toolbars : [
20         { xtype: 'ToolbarStandard', styles : {} }
21         { xtype: 'ToolbarContext', disable : {} }
22     ]
23 })
24
25      
26  * 
27  * @config : {Object} disable List of elements to disable.. (not done yet.)
28  * @config : {Object} styles  Map of styles available.
29  * 
30  */
31
32 Roo.form.HtmlEditor.ToolbarContext = function(config)
33 {
34     
35     Roo.apply(this, config);
36     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
37     // dont call parent... till later.
38     this.styles = this.styles || {};
39 }
40 Roo.form.HtmlEditor.ToolbarContext.types = {
41     'IMG' : {
42         width : {
43             title: "Width",
44             width: 40
45         },
46         height:  {
47             title: "Height",
48             width: 40
49         },
50         align: {
51             title: "Align",
52             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
53             width : 80
54             
55         },
56         border: {
57             title: "Border",
58             width: 40
59         },
60         alt: {
61             title: "Alt",
62             width: 120
63         },
64         src : {
65             title: "Src",
66             width: 220
67         }
68         
69     },
70     'A' : {
71         name : {
72             title: "Name",
73             width: 50
74         },
75         href:  {
76             title: "Href",
77             width: 220
78         } // border?
79         
80     },
81     'TABLE' : {
82         rows : {
83             title: "Rows",
84             width: 20
85         },
86         cols : {
87             title: "Cols",
88             width: 20
89         },
90         width : {
91             title: "Width",
92             width: 40
93         },
94         height : {
95             title: "Height",
96             width: 40
97         },
98         border : {
99             title: "Border",
100             width: 20
101         }
102     },
103     'TD' : {
104         width : {
105             title: "Width",
106             width: 40
107         },
108         height : {
109             title: "Height",
110             width: 40
111         },   
112         align: {
113             title: "Align",
114             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
115             width: 80
116         },
117         valign: {
118             title: "Valign",
119             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
120             width: 80
121         },
122         colspan: {
123             title: "Colspan",
124             width: 20
125             
126         }
127     },
128     'INPUT' : {
129         name : {
130             title: "name",
131             width: 120
132         },
133         value : {
134             title: "Value",
135             width: 120
136         },
137         width : {
138             title: "Width",
139             width: 40
140         }
141     },
142     'LABEL' : {
143         'for' : {
144             title: "For",
145             width: 120
146         }
147     },
148     'TEXTAREA' : {
149           name : {
150             title: "name",
151             width: 120
152         },
153         rows : {
154             title: "Rows",
155             width: 20
156         },
157         cols : {
158             title: "Cols",
159             width: 20
160         }
161     },
162     'SELECT' : {
163         name : {
164             title: "name",
165             width: 120
166         },
167         selectoptions : {
168             title: "Options",
169             width: 200
170         }
171     },
172     
173     // should we really allow this??
174     // should this just be 
175     'BODY' : {
176         title : {
177             title: "title",
178             width: 200,
179             disabled : true
180         }
181     },
182     '*' : {
183         // empty..
184     }
185 };
186
187
188
189 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
190     
191     tb: false,
192     
193     rendered: false,
194     
195     editor : false,
196     /**
197      * @cfg {Object} disable  List of toolbar elements to disable
198          
199      */
200     disable : false,
201     /**
202      * @cfg {Object} styles List of styles 
203      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
204      *
205      * These must be defined in the page, so they get rendered correctly..
206      * .headline { }
207      * TD.underline { }
208      * 
209      */
210     styles : false,
211     
212     
213     
214     toolbars : false,
215     
216     init : function(editor)
217     {
218         this.editor = editor;
219         
220         
221         var fid = editor.frameId;
222         var etb = this;
223         function btn(id, toggle, handler){
224             var xid = fid + '-'+ id ;
225             return {
226                 id : xid,
227                 cmd : id,
228                 cls : 'x-btn-icon x-edit-'+id,
229                 enableToggle:toggle !== false,
230                 scope: editor, // was editor...
231                 handler:handler||editor.relayBtnCmd,
232                 clickEvent:'mousedown',
233                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
234                 tabIndex:-1
235             };
236         }
237         // create a new element.
238         var wdiv = editor.wrap.createChild({
239                 tag: 'div'
240             }, editor.wrap.dom.firstChild.nextSibling, true);
241         
242         // can we do this more than once??
243         
244          // stop form submits
245       
246  
247         // disable everything...
248         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
249         this.toolbars = {};
250            
251         for (var i in  ty) {
252           
253             this.toolbars[i] = this.buildToolbar(ty[i],i);
254         }
255         this.tb = this.toolbars.BODY;
256         this.tb.el.show();
257         this.buildFooter();
258         this.footer.show();
259          
260         this.rendered = true;
261         
262         // the all the btns;
263         editor.on('editorevent', this.updateToolbar, this);
264         // other toolbars need to implement this..
265         //editor.on('editmodechange', this.updateToolbar, this);
266     },
267     
268     
269     
270     /**
271      * Protected method that will not generally be called directly. It triggers
272      * a toolbar update by reading the markup state of the current selection in the editor.
273      */
274     updateToolbar: function(editor,ev,sel){
275
276         //Roo.log(ev);
277         // capture mouse up - this is handy for selecting images..
278         // perhaps should go somewhere else...
279         if(!this.editor.activated){
280              this.editor.onFirstFocus();
281             return;
282         }
283         
284         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
285         // selectNode - might want to handle IE?
286         if (ev &&
287             (ev.type == 'mouseup' || ev.type == 'click' ) &&
288             ev.target && ev.target.tagName == 'IMG') {
289             // they have click on an image...
290             // let's see if we can change the selection...
291             sel = ev.target;
292          
293               var nodeRange = sel.ownerDocument.createRange();
294             try {
295                 nodeRange.selectNode(sel);
296             } catch (e) {
297                 nodeRange.selectNodeContents(sel);
298             }
299             //nodeRange.collapse(true);
300             var s = editor.win.getSelection();
301             s.removeAllRanges();
302             s.addRange(nodeRange);
303         }  
304         
305       
306         var updateFooter = sel ? false : true;
307         
308         
309         var ans = this.editor.getAllAncestors();
310         
311         // pick
312         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
313         
314         if (!sel) { 
315             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
316             sel = sel ? sel : this.editor.doc.body;
317             sel = sel.tagName.length ? sel : this.editor.doc.body;
318             
319         }
320         // pick a menu that exists..
321         var tn = sel.tagName.toUpperCase();
322         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
323         
324         tn = sel.tagName.toUpperCase();
325         
326         var lastSel = this.tb.selectedNode
327         
328         this.tb.selectedNode = sel;
329         
330         // if current menu does not match..
331         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
332                 
333             this.tb.el.hide();
334             ///console.log("show: " + tn);
335             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
336             this.tb.el.show();
337             // update name
338             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
339             
340             
341             // update attributes
342             if (this.tb.fields) {
343                 this.tb.fields.each(function(e) {
344                    e.setValue(sel.getAttribute(e.name));
345                 });
346             }
347             
348             var styles = [];
349             for(var i in this.styles) {
350                 styles.push(i);
351             }
352             
353             // update styles
354             if (styles.length) { 
355                 var st = this.tb.fields.item(0);
356                 
357                 st.store.removeAll();
358                
359                 
360                 var cn = sel.className.split(/\s+/);
361                 
362                 var avs = [];
363                 if (this.styles['*']) {
364                     
365                     Roo.each(this.styles['*'], function(v) {
366                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
367                     });
368                 }
369                 if (this.styles[tn]) { 
370                     Roo.each(this.styles[tn], function(v) {
371                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
372                     });
373                 }
374                 
375                 st.store.loadData(avs);
376                 st.collapse();
377                 st.setValue(cn);
378             }
379             // flag our selected Node.
380             this.tb.selectedNode = sel;
381            
382            
383             Roo.menu.MenuMgr.hideAll();
384
385         }
386         
387         if (!updateFooter) {
388             return;
389         }
390         // update the footer
391         //
392         var html = '';
393         
394         this.footerEls = ans.reverse();
395         Roo.each(this.footerEls, function(a,i) {
396             if (!a) { return; }
397             html += html.length ? ' &gt; '  :  '';
398             
399             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
400             
401         });
402        
403         // 
404         var sz = this.footDisp.up('td').getSize();
405         this.footDisp.dom.style.width = (sz.width -10) + 'px';
406         this.footDisp.dom.style.marginLeft = '5px';
407         
408         this.footDisp.dom.style.overflow = 'hidden';
409         
410         this.footDisp.dom.innerHTML = html;
411             
412         //this.editorsyncValue();
413     },
414    
415        
416     // private
417     onDestroy : function(){
418         if(this.rendered){
419             
420             this.tb.items.each(function(item){
421                 if(item.menu){
422                     item.menu.removeAll();
423                     if(item.menu.el){
424                         item.menu.el.destroy();
425                     }
426                 }
427                 item.destroy();
428             });
429              
430         }
431     },
432     onFirstFocus: function() {
433         // need to do this for all the toolbars..
434         this.tb.items.each(function(item){
435            item.enable();
436         });
437     },
438     buildToolbar: function(tlist, nm)
439     {
440         var editor = this.editor;
441          // create a new element.
442         var wdiv = editor.wrap.createChild({
443                 tag: 'div'
444             }, editor.wrap.dom.firstChild.nextSibling, true);
445         
446        
447         var tb = new Roo.Toolbar(wdiv);
448         // add the name..
449         
450         tb.add(nm+ ":&nbsp;");
451         
452         var styles = [];
453         for(var i in this.styles) {
454             styles.push(i);
455         }
456         
457         // styles...
458         if (styles && styles.length) {
459             
460             // this needs a multi-select checkbox...
461             tb.addField( new Roo.form.ComboBox({
462                 store: new Roo.data.SimpleStore({
463                     id : 'val',
464                     fields: ['val', 'selected'],
465                     data : [] 
466                 }),
467                 name : 'className',
468                 displayField:'val',
469                 typeAhead: false,
470                 mode: 'local',
471                 editable : false,
472                 triggerAction: 'all',
473                 emptyText:'Select Style',
474                 selectOnFocus:true,
475                 width: 130,
476                 listeners : {
477                     'select': function(c, r, i) {
478                         // initial support only for on class per el..
479                         tb.selectedNode.className =  r ? r.get('val') : '';
480                         editor.syncValue();
481                     }
482                 }
483     
484             }));
485         }
486             
487         
488         
489         for (var i in tlist) {
490             
491             var item = tlist[i];
492             tb.add(item.title + ":&nbsp;");
493             
494             
495             
496             
497             if (item.opts) {
498                 // opts == pulldown..
499                 tb.addField( new Roo.form.ComboBox({
500                     store: new Roo.data.SimpleStore({
501                         id : 'val',
502                         fields: ['val'],
503                         data : item.opts  
504                     }),
505                     name : i,
506                     displayField:'val',
507                     typeAhead: false,
508                     mode: 'local',
509                     editable : false,
510                     triggerAction: 'all',
511                     emptyText:'Select',
512                     selectOnFocus:true,
513                     width: item.width ? item.width  : 130,
514                     listeners : {
515                         'select': function(c, r, i) {
516                             tb.selectedNode.setAttribute(c.name, r.get('val'));
517                         }
518                     }
519
520                 }));
521                 continue;
522                     
523                  
524                 
525                 tb.addField( new Roo.form.TextField({
526                     name: i,
527                     width: 100,
528                     //allowBlank:false,
529                     value: ''
530                 }));
531                 continue;
532             }
533             tb.addField( new Roo.form.TextField({
534                 name: i,
535                 width: item.width,
536                 //allowBlank:true,
537                 value: '',
538                 listeners: {
539                     'change' : function(f, nv, ov) {
540                         tb.selectedNode.setAttribute(f.name, nv);
541                     }
542                 }
543             }));
544              
545         }
546         tb.el.on('click', function(e){
547             e.preventDefault(); // what does this do?
548         });
549         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
550         tb.el.hide();
551         tb.name = nm;
552         // dont need to disable them... as they will get hidden
553         return tb;
554          
555         
556     },
557     buildFooter : function()
558     {
559         
560         var fel = this.editor.wrap.createChild();
561         this.footer = new Roo.Toolbar(fel);
562         // toolbar has scrolly on left / right?
563         var footDisp= new Roo.Toolbar.Fill();
564         var _t = this;
565         this.footer.add(
566             {
567                 text : '&lt;',
568                 xtype: 'Button',
569                 handler : function() {
570                     _t.footDisp.scrollTo('left',0,true)
571                 }
572             }
573         );
574         this.footer.add( footDisp );
575         this.footer.add( 
576             {
577                 text : '&gt;',
578                 xtype: 'Button',
579                 handler : function() {
580                     // no animation..
581                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
582                 }
583             }
584         );
585         var fel = Roo.get(footDisp.el);
586         fel.addClass('x-editor-context');
587         this.footDispWrap = fel; 
588         this.footDispWrap.overflow  = 'hidden';
589         
590         this.footDisp = fel.createChild();
591         this.footDispWrap.on('click', this.onContextClick, this)
592         
593         
594     },
595     onContextClick : function (ev,dom)
596     {
597         ev.preventDefault();
598         var  cn = dom.className;
599         Roo.log(cn);
600         if (!cn.match(/x-ed-loc-/)) {
601             return;
602         }
603         var n = cn.split('-').pop();
604         var ans = this.footerEls;
605         var sel = ans[n];
606         
607          // pick
608         var range = this.editor.createRange();
609         
610         range.selectNodeContents(sel);
611         //range.selectNode(sel);
612         
613         
614         var selection = this.editor.getSelection();
615         selection.removeAllRanges();
616         selection.addRange(range);
617         
618         
619         
620         this.updateToolbar(null, null, sel);
621         
622         
623     }
624     
625     
626     
627     
628     
629 });
630
631
632
633
634