Roo/bootstrap/form/HtmlEditorToolbar/Context.js
[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 = editorcore.createRange();
293         var selection = this.editorcore.getSelection();
294         range.setStart(stn,0);
295         range.setEnd(stn,0); 
296         
297         var sn = this.selectedNode;
298         if (sn.hasAttribute('data-block')) {
299             var block = Roo.htmleditor.Block.factory(tb.selectedNode)
300             if (block) {
301                 block.removeNode();
302                 selection.removeAllRanges();
303                 selection.addRange(range);
304                 this.updateToolbar(null, null, null);
305             }   
306              
307         }
308         if (!sn) {
309             return; // should not really happen..
310         }
311         if (sn && sn.tagName == 'BODY') {
312             return;
313         }
314         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
315         
316         // remove and keep parents.
317         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
318         a.replaceTag(sn);
319         
320         selection.removeAllRanges();
321         selection.addRange(range);
322         this.updateToolbar(null, null, null);
323         
324         
325     },
326     /**
327      * Protected method that will not generally be called directly. It triggers
328      * a toolbar update by reading the markup state of the current selection in the editor.
329      *
330      * Note you can force an update by calling on('editorevent', scope, false)
331      */
332     updateToolbar: function(editor ,ev, sel)
333     {
334         var ty = this.constructor.types;
335         
336         
337         if (ev) {
338             ev.stopEvent(); // se if we can stop this looping with mutiple events.
339         }
340         
341          
342         // capture mouse up - this is handy for selecting images..
343         // perhaps should go somewhere else...
344         if(!this.editorcore.activated){
345             this.editor.onFirstFocus();
346             return;
347         }
348         //Roo.log(ev ? ev.target : 'NOTARGET');
349         
350         
351         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
352         // selectNode - might want to handle IE?
353          
354         if (ev &&
355             (ev.type == 'mouseup' || ev.type == 'click' ) &&
356             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
357             // they have click on an image...
358             // let's see if we can change the selection...
359             sel = ev.target;
360             
361             // this triggers looping?
362             //this.editorcore.selectNode(sel);
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                 ans = this.editorcore.getAllAncestors();
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 (this.active_group) {
430             this.hideActiveGroup();
431         }
432         this.showActiveGroup(tn);
433         
434     },
435     hideActiveGroup : function()
436     {
437         this.hide();
438         if (this.active_group === false) {
439             return;
440         }
441         this.active_group.forEach(function(e) {
442             e.hide();
443         });
444         this.active_group = false;
445     },
446     showActiveGroup : function(tn)
447     {
448         if (typeof(this.button_groups[tn]) == 'undefined') {
449             return;
450         }
451         
452         this.active_group = this.button_groups[tn];
453         
454          this.active_group.forEach(function(e) {
455             e.show();
456         });
457         
458         // update attributes
459         if (this.selectedNode.hasAttribute('data-block') ) {
460             var block = Roo.htmleditor.Block.factory(this.selectedNode);
461             this.active_group.forEach(function(e) {
462                 e.setValue(this.selectedNode.getAttribute(block[e.name]));
463             }, this);
464                 
465             return;
466             
467         }
468         // based on attributes...
469         this.active_group.forEach(function(e) {
470              e.setValue(this.selectedNode.getAttribute(e.attrname));
471         }, this);
472         this.show();
473             
474     }
475     
476 });