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