e28ee235e1735759b024b061e15058d65551a153
[roojs1] / Roo / bootstrap / form / HtmlEditorToolbar / Standard.js
1   
2 /**
3  * @class Roo.bootstrap.form.HtmlEditorToolbar.Standard
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         new Roo.bootstrap.form.HtmlEditorToolbar.Standard({
15             disable : { fonts: 1 , format: 1, ..., ... , ...],
16             btns : [ .... ]
17         })
18     }
19      
20  * 
21  * @cfg {Object} disable List of elements to disable..
22  * @cfg {Array} btns List of additional buttons.
23  * 
24  * 
25  * NEEDS Extra CSS? 
26  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27  */
28  
29 Roo.bootstrap.form.HtmlEditorToolbar.Standard = function(config)
30 {
31     
32     Roo.apply(this, config);
33     
34     // default disabled, based on 'good practice'..
35     this.disable = this.disable || {};
36     Roo.applyIf(this.disable, {
37         fontSize : true,
38         colors : true,
39         specialElements : true
40     });
41     Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.constructor.call(this, config);
42     
43     this.editor = config.editor;
44     this.editorcore = config.editor.editorcore;
45     
46     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.btnid; });
47     
48     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
49     // dont call parent... till later.
50 }
51 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbar.Standard, Roo.bootstrap.nav.Simplebar,  {
52      
53     bar : true,
54     
55     editor : false,
56     editorcore : false,
57     
58     
59     formats : [
60         "p" ,  
61         "h1","h2","h3","h4","h5","h6", 
62         "pre", "code", 
63         "abbr", "acronym", "address", "cite", "samp", "var",
64         'div','span'
65     ],
66     
67     
68     deleteBtn: false,
69     
70     onRender : function(ct, position)
71     {
72        // Roo.log("Call onRender: " + this.xtype);
73         
74        Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.onRender.call(this, ct, position);
75        Roo.log(this.el);
76        this.el.dom.style.marginBottom = '0';
77        var _this = this;
78        var editorcore = this.editorcore;
79        var editor= this.editor;
80        
81        var children = [];
82        var btn = function(id, cmd , toggle, handler, html){
83        
84             var  event = toggle ? 'toggle' : 'click';
85        
86             var a = {
87                 size : 'sm',
88                 xtype: 'Button',
89                 xns: Roo.bootstrap,
90                 //glyphicon : id,
91                 btnid : id,
92                 fa: id,
93                 cmd : cmd, // why id || cmd
94                 enableToggle: toggle !== false,
95                 html : html || '',
96                 pressed : toggle ? false : null,
97                 listeners : {}
98             };
99             a.listeners[toggle ? 'toggle' : 'click'] = function() {
100                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
101             };
102             children.push(a);
103             return a;
104        }
105        
106     //    var cb_box = function...
107         
108         var style = {
109                 xtype: 'Button',
110                 size : 'sm',
111                 xns: Roo.bootstrap,
112                 fa : 'font',
113                 //html : 'submit'
114                 menu : {
115                     xtype: 'Menu',
116                     xns: Roo.bootstrap,
117                     items:  []
118                 }
119         };
120         Roo.each(this.formats, function(f) {
121             style.menu.items.push({
122                 xtype :'MenuItem',
123                 xns: Roo.bootstrap,
124                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
125                 tagname : f,
126                 listeners : {
127                     click : function()
128                     {
129                         editorcore.insertTag(this.tagname);
130                         editor.focus();
131                     }
132                 }
133                 
134             });
135         });
136         children.push(style);   
137         
138         btn('bold',         false,true);
139         btn('italic',       false,true);
140         btn('align-left',   'justifyleft',true);
141         btn('align-center', 'justifycenter',true);
142         btn('align-right' , 'justifyright',true);
143         btn('link', false, true, this.onLinkClick);
144         
145         
146         btn('image', false, true, this.onImageClick);
147         btn('list','insertunorderedlist',true);
148         btn('list-ol','insertorderedlist',true);
149
150         btn('pencil', false,true, function(btn){
151                 Roo.log(this);
152                 this.toggleSourceEdit(btn.pressed);
153         });
154         
155         if (this.editor.btns.length > 0) {
156             for (var i = 0; i<this.editor.btns.length; i++) {
157                 children.push(this.editor.btns[i]);
158             }
159         }
160         
161         
162          
163         this.xtype = 'NavSimplebar'; // why?
164         
165         for(var i=0;i< children.length;i++) {
166             
167             this.buttons.add(this.addxtypeChild(children[i]));
168             
169         }
170         this.buildToolbarDelete();
171
172         editor.on('editorevent', this.updateToolbar, this);
173     },
174     
175     buildToolbarDelete : function()
176     {
177         
178        /* this.addxtypeChild({
179             xtype : 'Element',
180             xns : Roo.bootstrap,
181             cls : 'roo-htmleditor-fill'
182         });
183         */
184         this.deleteBtn = this.addxtypeChild({
185             size : 'sm',
186             xtype: 'Button',
187             xns: Roo.bootstrap,
188             fa: 'eraser',
189             tooltip : "Clear Formating / Delete",
190             listeners : {
191                 click : this.onDelete.createDelegate(this)
192             }
193         });
194         this.deleteBtn.hide();     
195         
196     },
197     
198     onImageClick : function()
199     {
200         if (this.input) {
201             this.input.un('change', this.onFileSelected, this);
202         }
203         this.input = Roo.get(document.body).createChild({ 
204           tag: 'input', 
205           type : 'file', 
206           style : 'display:none', 
207           multiple: 'multiple'
208        });
209         this.input.on('change', this.onFileSelected, this);
210         this.input.dom.click();
211     },
212     
213     onFileSelected : function(e)
214     {
215          e.preventDefault();
216         
217         if(typeof(this.input.dom.files) == 'undefined' || !this.input.dom.files.length){
218             return;
219         }
220     
221          
222         this.addFiles(Array.prototype.slice.call(this.input.dom.files));
223     },
224     
225     addFiles : function(far) {
226
227         if (!far.length) {
228             return;
229         }
230         
231         var f = far.pop();
232         
233         if (!f.type.match(/^image/)) {
234             this.addFiles(far);
235             return;
236         }
237          
238         var sn = this.selectedNode;
239         
240         var bl = sn  && this.editorcore.enableBlocks ? Roo.htmleditor.Block.factory(sn) : false;
241         
242         var editor =  this.editorcore;
243         
244         var reader = new FileReader();
245         reader.addEventListener('load', (function() {
246             if (bl) {
247                 bl.image_src = reader.result;
248                 //bl.caption = f.name;
249                 bl.updateElement(sn);
250                 editor.owner.fireEvent('editorevent', editor, false);
251                 // we only do the first file!! and replace.
252                 return;
253             }
254             if (this.editorcore.enableBlocks) {
255                 var fig = new Roo.htmleditor.BlockFigure({
256                     image_src :  reader.result,
257                     caption : '',
258                     caption_display : 'none'  //default to hide captions..
259                  });
260                 editor.insertAtCursor(fig.toHTML());
261                 editor.owner.fireEvent('editorevent', editor, false);
262                 return;
263             }
264             // just a standard img..
265             if (sn && sn.tagName.toUpperCase() == 'IMG') {
266                 sn.src = reader.result;
267                 editor.owner.fireEvent('editorevent', editor, false);
268                 return;
269             }
270             editor.insertAtCursor('<img src="' + reader.result +'">');
271             editor.owner.fireEvent('editorevent', editor, false);
272             
273         }).createDelegate(this));
274         reader.readAsDataURL(f);
275         
276     
277      },
278     
279     
280     onBtnClick : function(id)
281     {
282        this.editorcore.relayCmd(id);
283        this.editorcore.focus();
284     },
285     
286     onLinkClick : function(btn) {
287         var url = this.selectedNode && this.selectedNode.tagName.toUpperCase() == 'A' ?
288                 this.selectedNode.getAttribute('href') : '';
289             
290         Roo.bootstrap.MessageBox.show({
291             title : "Add / Edit Link URL",
292             msg : "Enter the URL for the link",
293             buttons: Roo.bootstrap.MessageBox.OKCANCEL,
294             minWidth: 250,
295             scope : this,
296             prompt:true,
297             multiline: false,
298             modal : true,
299             value : url,
300             fn:  function(pressed, newurl) {
301                 if (pressed != 'ok') {
302                     this.editorcore.focus();
303                     return;
304                 }
305                 if (url != '') {
306                     this.selectedNode.setAttribute('href', newurl);
307                     return;
308                 }
309                 if(newurl && newurl .match(/http(s):\/\/.+/)) {
310                     this.editorcore.relayCmd('createlink', newurl);
311                 }
312                 this.editorcore.focus();
313             }
314         });
315     },
316     /**
317      * Protected method that will not generally be called directly. It triggers
318      * a toolbar update by reading the markup state of the current selection in the editor.
319      */
320     updateToolbar: function(editor ,ev, sel){
321
322         if(!this.editorcore.activated){
323             this.editor.onFirstFocus(); // is this neeed?
324             return;
325         }
326
327         var btns = this.buttons; 
328         var doc = this.editorcore.doc;
329         var hasToggle  = false;
330         btns.each(function(e) {
331             if (e.enableToggle && e.cmd) {
332                 hasToggle = hasToggle  || (['align-left', 'align-right', 'align-center'].indexOf(e.btnid) < 0 && doc.queryCommandState(e.cmd));
333                 e.setActive(doc.queryCommandState(e.cmd));
334             }
335         }, this);
336         
337         
338         if (ev &&
339             (ev.type == 'mouseup' || ev.type == 'click' ) &&
340             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
341             // they have click on an image...
342             // let's see if we can change the selection...
343             sel = ev.target;
344             
345         }
346         
347         var ans = this.editorcore.getAllAncestors();
348         if (!sel) { 
349             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
350             sel = sel ? sel : this.editorcore.doc.body;
351             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
352             
353         }
354         
355         var lastSel = this.selectedNode;
356         this.selectedNode = sel;
357          
358         // ok see if we are editing a block?
359         
360         var db = false;
361         // you are not actually selecting the block.
362         if (sel && sel.hasAttribute('data-block')) {
363             db = sel;
364         } else if (sel && sel.closest('[data-block]')) {
365             db = sel.closest('[data-block]');
366         }
367         
368         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
369             e.classList.remove('roo-ed-selection');
370         });
371         
372         var block = false;
373         if (db && this.editorcore.enableBlocks) {
374             block = Roo.htmleditor.Block.factory(db);
375             
376             if (block) {
377                 db.className =  (db.classList.length > 0  ? db.className + ' ' : '') +
378                     ' roo-ed-selection';
379                 sel = this.selectedNode = db;
380             }
381         }
382         
383         // highlight the 'a'..
384         var tn = sel && sel.tagName.toUpperCase() || '';
385         if (!block && sel && tn != 'A') {
386             var asel = sel.closest('A');
387             if (asel) {
388                 sel = asel;
389             }
390         }
391        
392         btns.get('link').setActive(tn == 'A' && this.selectedNode.hasAttribute('href'));
393         
394         Roo.bootstrap.menu.Manager.hideAll();
395          
396         
397         
398         
399         
400         // handle delete button..
401         if (hasToggle || (tn.length && tn == 'BODY')) {
402             this.deleteBtn.hide();
403             return;
404             
405         }
406         this.deleteBtn.show();
407         
408         
409         
410         //this.editorsyncValue();
411     },
412     onFirstFocus: function() {
413         this.buttons.each(function(item){
414            item.enable();
415         });
416     },
417     
418     onDelete : function()
419     {
420         var range = this.editorcore.createRange();
421         var selection = this.editorcore.getSelection();
422         var sn = this.selectedNode;
423         range.setStart(sn,0);
424         range.setEnd(sn,0); 
425         
426         
427         if (sn.hasAttribute('data-block')) {
428             var block = Roo.htmleditor.Block.factory(this.selectedNode);
429             if (block) {
430                 sn = block.removeNode();
431                 sn.parentNode.removeChild(sn);
432                 selection.removeAllRanges();
433                 selection.addRange(range);
434                 this.updateToolbar(null, null, null);
435                 this.editorcore.fireEditorEvent(false);
436                 return;
437             }   
438              
439         }
440         if (!sn) {
441             return; // should not really happen..
442         }
443         if (sn && sn.tagName == 'BODY') {
444             return;
445         }
446         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
447         
448         // remove and keep parents.
449         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
450         a.replaceTag(sn);
451         
452         selection.removeAllRanges();
453         selection.addRange(range);
454         this.editorcore.fireEditorEvent(false);
455         
456         
457     },
458     
459     
460     toggleSourceEdit : function(sourceEditMode){
461         
462           
463         if(sourceEditMode){
464             Roo.log("disabling buttons");
465            this.buttons.each( function(item){
466                 if(item.cmd != 'pencil'){
467                     item.disable();
468                 }
469             });
470           
471         }else{
472             Roo.log("enabling buttons");
473             if(this.editorcore.initialized){
474                 this.buttons.each( function(item){
475                     item.enable();
476                 });
477             }
478             
479         }
480         Roo.log("calling toggole on editor");
481         // tell the editor that it's been pressed..
482         this.editor.toggleSourceEdit(sourceEditMode);
483        
484     }
485 });
486
487
488
489