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.superclass.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.buildToolbar(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                     width: item.width,
246                     //allowBlank:true,
247                     value: '',
248                     listeners: {
249                         'change' : function(f, nv, ov) {
250                             tb.selectedNode.setAttribute(f.attrname, nv);
251                             editorcore.syncValue();
252                         }
253                     }
254                 })
255             );
256                 
257         }
258         // hide them all..
259         ret.forEach(function(e) {
260             e.hide();
261         });
262         ret.name = key;
263         
264         return ret;
265     },
266     buildToolbarDelete : function()
267     {
268         
269         this.addxtypeChild({
270             xtype : 'Element',
271             xns : Roo.bootstrap,
272             cls : 'roo-htmleditor-fill'
273         });
274         
275         this.deleteBtn = this.addxtypeChild({
276             size : 'sm',
277             xtype: 'Button',
278             xns: Roo.bootstrap,
279             fa: 'trash',
280             listeners : {
281                 click : this.onDelete.createDelegate(this)
282             }
283         });
284         this.deleteBtn.hide();     
285         
286     },
287     
288     
289     onDelete : function()
290     {
291         var range = editorcore.createRange();
292         var selection = this.editorcore.getSelection();
293         range.setStart(stn,0);
294         range.setEnd(stn,0); 
295         
296         var sn = this.selectedNode;
297         if (sn.hasAttribute('data-block')) {
298             var block = Roo.htmleditor.Block.factory(tb.selectedNode)
299             if (block) {
300                 block.removeNode();
301                 selection.removeAllRanges();
302                 selection.addRange(range);
303                 this.updateToolbar(null, null, null);
304             }   
305              
306         }
307         if (!sn) {
308             return; // should not really happen..
309         }
310         if (sn && sn.tagName == 'BODY') {
311             return;
312         }
313         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
314         
315         // remove and keep parents.
316         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
317         a.replaceTag(sn);
318         
319         selection.removeAllRanges();
320         selection.addRange(range);
321         this.updateToolbar(null, null, null);
322         
323         
324     },
325     /**
326      * Protected method that will not generally be called directly. It triggers
327      * a toolbar update by reading the markup state of the current selection in the editor.
328      *
329      * Note you can force an update by calling on('editorevent', scope, false)
330      */
331     updateToolbar: function(editor ,ev, sel)
332     {
333         var ty = this.constructor.superclass.types;
334         
335         
336         if (ev) {
337             ev.stopEvent(); // se if we can stop this looping with mutiple events.
338         }
339         
340          
341         // capture mouse up - this is handy for selecting images..
342         // perhaps should go somewhere else...
343         if(!this.editorcore.activated){
344             this.editor.onFirstFocus();
345             return;
346         }
347         //Roo.log(ev ? ev.target : 'NOTARGET');
348         
349         
350         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
351         // selectNode - might want to handle IE?
352          
353         if (ev &&
354             (ev.type == 'mouseup' || ev.type == 'click' ) &&
355             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
356             // they have click on an image...
357             // let's see if we can change the selection...
358             sel = ev.target;
359             
360             // this triggers looping?
361             //this.editorcore.selectNode(sel);
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.name == tn && lastSel == this.selectedNode && ev !== false) {
425             return; // no change?
426         }
427         
428         if (this.active_group) {
429             this.hideActiveGroup();
430         }
431         this.showActiveGroup(tn);
432         
433     },
434     hideActiveGroup : function()
435     {
436         if (this.active_group === false) {
437             return;
438         }
439         this.active_group.forEach(function(e) {
440             e.hide();
441         });
442         this.active_group = false;
443     },
444     showActiveGroup : function(tn)
445     {
446         if (typeof(this.button_groups[tn]) == 'undefined') {
447             return;
448         }
449         
450         this.active_group = this.button_groups[tn];
451         
452         // update attributes
453         if (this.selectedNode.hasAttribute('data-block') ) {
454             var block = Roo.htmleditor.Block.factory(this.selectedNode);
455             this.active_group.forEach(function(e) {
456                 e.setValue(this.selectedNode.getAttribute(block[e.name]));
457             }, this);
458                 
459             return;
460             
461         }
462         // based on attributes...
463         this.active_group.forEach(function(e) {
464              e.setValue(this.selectedNode.getAttribute(e.attrname));
465         }, this);
466             
467             
468     }
469     
470 });