e5598052bba992b170090f10ecc131deee8b4403
[roojs1] / Roo / bootstrap / form / HtmlEditorToolbar / Context.js
1   
2 /**
3  * @class Roo.bootstrap.form.HtmlEditorToolbar.Context
4  * @parent Roo.bootstrap.form.HtmlEditor
5  * @extends Roo.bootstrap.nav.Simplebar
6  * Basic Toolbar
7  * 
8  * @example
9  * Usage:
10  *
11  new Roo.bootstrap.form.HtmlEditor({
12     ....
13     toolbars : [
14         {
15             xtyle: 'Standard',
16             disable : { fonts: 1 , format: 1, ..., ... , ...],
17             btns : [ .... ]
18         },
19         {
20             xtyle : 'Context',
21             ....
22         }
23     }
24      
25  * 
26  * 
27  */
28  
29 Roo.bootstrap.form.HtmlEditorToolbar.Context = function(config)
30 {
31     
32     Roo.apply(this, config);
33     
34     
35     Roo.bootstrap.form.HtmlEditorToolbar.Context.superclass.constructor.call(this, config);
36     
37     this.editor = config.editor;
38     this.editorcore = config.editor.editorcore;
39     
40     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
41     
42 }
43
44 Roo.bootstrap.form.HtmlEditorToolbar.Context.types = {
45     'IMG' : [
46         {
47             name : 'width',
48             title: "Width",
49             width: 40
50         },
51         {
52             name : 'height',
53             title: "Height",
54             width: 40
55         },
56         {
57             name : 'align',
58             title: "Align",
59             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
60             width : 80
61             
62         },
63         {
64             name : 'border',
65             title: "Border",
66             width: 40
67         },
68         {
69             name : 'alt',
70             title: "Alt",
71             width: 120
72         },
73         {
74             name : 'src',
75             title: "Src",
76             width: 220
77         }
78         
79     ],
80     
81     /*
82     'A' : [
83         {
84             name : 'name',
85             title: "Name",
86             width: 50
87         },
88         {
89             name : 'target',
90             title: "Target",
91             width: 120
92         },
93         {
94             name : 'href',
95             title: "Href",
96             width: 220
97         } // border?
98         
99     ],
100     */
101     /*
102     'INPUT' : [
103         {
104             name : 'name',
105             title: "name",
106             width: 120
107         },
108         {
109             name : 'value',
110             title: "Value",
111             width: 120
112         },
113         {
114             name : 'width',
115             title: "Width",
116             width: 40
117         }
118     ],
119     'LABEL' : [
120          {
121             name : 'for',
122             title: "For",
123             width: 120
124         }
125     ],
126     'TEXTAREA' : [
127         {
128             name : 'name',
129             title: "name",
130             width: 120
131         },
132         {
133             name : 'rows',
134             title: "Rows",
135             width: 20
136         },
137         {
138             name : 'cols',
139             title: "Cols",
140             width: 20
141         }
142     ],
143     'SELECT' : [
144         {
145             name : 'name',
146             title: "name",
147             width: 120
148         },
149         {
150             name : 'selectoptions',
151             title: "Options",
152             width: 200
153         }
154     ],
155     
156     // should we really allow this??
157     // should this just be 
158     'BODY' : [
159         
160         {
161             name : 'title',
162             title: "Title",
163             width: 200,
164             disabled : true
165         }
166     ],
167     */
168     '*' : [
169         // empty.
170     ]
171
172 };
173
174 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbar.Context, Roo.bootstrap.nav.Simplebar,  {
175     
176     editor : false,
177     editorcore : false,
178     buttons : false,
179     
180     button_groups : false, // subtoolbars...  - buttson?
181     active_group : false,
182     
183     selectedNode : false,
184     
185     onRender : function(ct, position)
186     {
187        // Roo.log("Call onRender: " + this.xtype);
188         
189         this.constructor.superclass.onRender.call(this, ct, position);
190        
191         
192          
193         
194           
195         // disable everything...
196         var ty = this.constructor.types;
197         this.button_groups = {};
198         // block toolbars are built in updateToolbar when needed.
199         for (var i in  ty) {
200             this.button_groups[i] = this.buildToolbarGroup(ty[i],i);
201         }
202         this.buildToolbarDelete();
203           this.hide();
204         // the all the btns;
205         this.editor.on('editorevent', this.updateToolbar, this);
206         
207     },
208     onFirstFocus: function() {
209        
210     },
211     
212     
213     buildToolbarGroup: function(tlist, key )
214     {
215         var editor = this.editor;
216         var editorcore = this.editorcore;
217         var tb = this;
218        
219         var ret = [];
220          
221         for (var i = 0; i < tlist.length; i++) {
222             
223             // newer versions will use xtype cfg to create menus.
224             if (typeof(tlist[i].xtype) != 'undefined') {
225                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
226                 continue;
227             }
228             
229             var item = tlist[i];
230             ret.push(
231                 this.addxtypeChild({
232                     xtype : 'Element',
233                     xns : Roo.bootstrap,
234                     cls : 'roo-htmleditor-context-label-' + key + '-' + item.name,
235                     html : item.title
236                 })  
237             );
238             
239             // add a text entry!?
240             ret.push(
241                 this.addxtypeChild({
242                     xtype : 'Input',
243                     xns : Roo.bootstrap.form,
244                     cls : 'roo-htmleditor-context-entry-' + key + '-' + item.name,
245                     name: '-roo-edit-' + item.name,
246                     attrname : item.name,
247                     width: item.width,
248                     //allowBlank:true,
249                     value: '',
250                     listeners: {
251                         'change' : function(f, nv, ov) {
252                             tb.selectedNode.setAttribute(f.attrname, nv);
253                             editorcore.syncValue();
254                         }
255                     }
256                 })
257             );
258                 
259         }
260         // hide them all..
261         ret.forEach(function(e) {
262             e.hide();
263         });
264         ret.name = key;
265         
266         return ret;
267     },
268     buildToolbarDelete : function()
269     {
270         
271         this.addxtypeChild({
272             xtype : 'Element',
273             xns : Roo.bootstrap,
274             cls : 'roo-htmleditor-fill'
275         });
276         
277         this.deleteBtn = this.addxtypeChild({
278             size : 'sm',
279             xtype: 'Button',
280             xns: Roo.bootstrap,
281             fa: 'trash',
282             listeners : {
283                 click : this.onDelete.createDelegate(this)
284             }
285         });
286         this.deleteBtn.hide();     
287         
288     },
289     
290     
291     onDelete : function()
292     {
293         var range = this.editorcore.createRange();
294         var selection = this.editorcore.getSelection();
295         var sn = this.selectedNode;
296         range.setStart(sn,0);
297         range.setEnd(sn,0); 
298         
299         
300         if (sn.hasAttribute('data-block')) {
301             var block = Roo.htmleditor.Block.factory(tb.selectedNode);
302             if (block) {
303                 block.removeNode();
304                 selection.removeAllRanges();
305                 selection.addRange(range);
306                 this.updateToolbar(null, null, null);
307             }   
308              
309         }
310         if (!sn) {
311             return; // should not really happen..
312         }
313         if (sn && sn.tagName == 'BODY') {
314             return;
315         }
316         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
317         
318         // remove and keep parents.
319         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
320         a.replaceTag(sn);
321         
322         selection.removeAllRanges();
323         selection.addRange(range);
324         this.editorcore.fireEditorEvent(false);
325         
326         
327     },
328     /**
329      * Protected method that will not generally be called directly. It triggers
330      * a toolbar update by reading the markup state of the current selection in the editor.
331      *
332      * Note you can force an update by calling on('editorevent', scope, false)
333      */
334     updateToolbar: function(editor ,ev, sel)
335     {
336         var ty = this.constructor.types;
337         
338         
339         if (ev) {
340             ev.stopEvent(); // se if we can stop this looping with mutiple events.
341         }
342         
343          
344         // capture mouse up - this is handy for selecting images..
345         // perhaps should go somewhere else...
346         if(!this.editorcore.activated){
347             this.editor.onFirstFocus();
348             return;
349         }
350         //Roo.log(ev ? ev.target : 'NOTARGET');
351         
352         
353         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
354         // selectNode - might want to handle IE?
355          
356         if (ev &&
357             (ev.type == 'mouseup' || ev.type == 'click' ) &&
358             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
359             // they have click on an image...
360             // let's see if we can change the selection...
361             sel = ev.target;
362            
363              
364         }
365         
366         // this forces an id..
367         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
368             e.classList.remove('roo-ed-selection');
369         });
370           
371         var ans = this.editorcore.getAllAncestors();
372         
373                
374         if (!sel) { 
375             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
376             sel = sel ? sel : this.editorcore.doc.body;
377             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
378             
379         }
380         
381         var tn = sel.tagName.toUpperCase();
382         var lastSel = this.selectedNode;
383         this.selectedNode = sel;
384         var left_label = tn;
385         
386         // ok see if we are editing a block?
387         
388         var db = false;
389         // you are not actually selecting the block.
390         if (sel && sel.hasAttribute('data-block')) {
391             db = sel;
392         } else if (sel && sel.closest('[data-block]')) {
393             
394             db = sel.closest('[data-block]');
395              
396         }
397         
398         
399         var block = false;
400         if (db && this.editorcore.enableBlocks) {
401             block = Roo.htmleditor.Block.factory(db);
402             
403             
404             if (block) {
405                 db.className = (
406                         db.classList.length > 0  ? db.className + ' ' : ''
407                     )  + 'roo-ed-selection';
408                  
409                  // since we removed it earlier... its not there..
410                 tn = 'BLOCK.' + db.getAttribute('data-block');
411                 
412                 //this.editorcore.selectNode(db);
413                 if (typeof(this.button_groups[tn]) == 'undefined') {
414                    this.button_groups[tn] = this.buildBlockToolbar( block );
415                 }
416                 this.selectedNode = db;
417                 left_label = block.friendly_name;
418                  
419             }
420               
421             
422         }
423         
424         
425         if ( this.active_group !== false && this.active_group.name == tn && lastSel == this.selectedNode && ev !== false) {
426             return; // no change?
427         }
428         
429         if (tn == 'BODY') {
430             this.deleteBtn.hide();
431             this.hide();
432             this.hideActiveGroup();
433             return;
434             
435         }
436         
437         
438         if (this.active_group) {
439             this.hideActiveGroup();
440         }
441         this.showActiveGroup(tn);
442         this.show();
443         this.deleteBtn.show();
444         
445     },
446     hideActiveGroup : function()
447     {
448         this.hide();
449         if (this.active_group === false) {
450             return;
451         }
452         this.active_group.forEach(function(e) {
453             e.hide();
454         });
455         this.active_group = false;
456     },
457     showActiveGroup : function(tn)
458     {
459         
460         if (typeof(this.button_groups[tn]) == 'undefined') {
461             
462             return;
463         }
464         
465         this.active_group = this.button_groups[tn];
466         
467         this.active_group.forEach(function(e) {
468             e.show();
469         });
470         
471         // update attributes
472         if (this.selectedNode.hasAttribute('data-block') ) {
473             var block = Roo.htmleditor.Block.factory(this.selectedNode);
474             this.active_group.forEach(function(e) {
475                 e.setValue(this.selectedNode.getAttribute(block[e.name]));
476             }, this);
477                 
478             return;
479             
480         }
481         // based on attributes...
482         this.active_group.forEach(function(e) {
483             if (typeof(e.attrname) == 'undefined') {
484                 return;
485             }
486              e.setValue(this.selectedNode.getAttribute(e.attrname));
487         }, this);
488         
489             
490     }
491     
492 });