fix #7610 - 206_minor_fixes_to_portal
[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                 cls : 'roo-html-editor-btn-' + id,
94                 cmd : cmd, // why id || cmd
95                 enableToggle: toggle !== false,
96                 html : html || '',
97                 pressed : toggle ? false : null,
98                 listeners : {}
99             };
100             a.listeners[toggle ? 'toggle' : 'click'] = function() {
101                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
102             };
103             children.push(a);
104             return a;
105        }
106        
107     //    var cb_box = function...
108         
109         var style = {
110                 xtype: 'Button',
111                 size : 'sm',
112                 xns: Roo.bootstrap,
113                 fa : 'font',
114                 cls : 'roo-html-editor-font-chooser',
115                 //html : 'submit'
116                 menu : {
117                     xtype: 'Menu',
118                     xns: Roo.bootstrap,
119                     items:  []
120                 }
121         };
122         Roo.each(this.formats, function(f) {
123             style.menu.items.push({
124                 xtype :'MenuItem',
125                 xns: Roo.bootstrap,
126                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
127                 tagname : f,
128                 listeners : {
129                     click : function()
130                     {
131                         editorcore.insertTag(this.tagname);
132                         editor.focus();
133                     }
134                 }
135                 
136             });
137         });
138         children.push(style);   
139         
140         btn('bold',         'bold',true);
141         btn('italic',       'italic',true);
142         btn('underline',     'underline',true);
143         btn('align-left',   'justifyleft',true);
144         btn('align-center', 'justifycenter',true);
145         btn('align-right' , 'justifyright',true);
146         btn('link', false, true, this.onLinkClick);
147         
148         
149         btn('image', false, true, this.onImageClick);
150         btn('list','insertunorderedlist',true);
151         btn('list-ol','insertorderedlist',true);
152
153         btn('pencil', false,true, function(btn){
154                 Roo.log(this);
155                 this.toggleSourceEdit(btn.pressed);
156         });
157         
158         if (this.editor.btns.length > 0) {
159             for (var i = 0; i<this.editor.btns.length; i++) {
160                 children.push(this.editor.btns[i]);
161             }
162         }
163         
164         
165          
166         this.xtype = 'NavSimplebar'; // why?
167         
168         for(var i=0;i< children.length;i++) {
169             
170             this.buttons.add(this.addxtypeChild(children[i]));
171             
172         }
173         this.buildToolbarDelete();
174
175         editor.on('editorevent', this.updateToolbar, this);
176     },
177     
178     buildToolbarDelete : function()
179     {
180         
181        /* this.addxtypeChild({
182             xtype : 'Element',
183             xns : Roo.bootstrap,
184             cls : 'roo-htmleditor-fill'
185         });
186         */
187         this.deleteBtn = this.addxtypeChild({
188             size : 'sm',
189             xtype: 'Button',
190             xns: Roo.bootstrap,
191             fa: 'trash',
192             listeners : {
193                 click : this.onDelete.createDelegate(this)
194             }
195         });
196         this.deleteBtn.hide();     
197         
198     },
199     
200     onImageClick : function()
201     {
202         if (this.input) {
203             this.input.un('change', this.onFileSelected, this);
204         }
205         this.input = Roo.get(document.body).createChild({ 
206           tag: 'input', 
207           type : 'file', 
208           style : 'display:none', 
209           multiple: 'multiple'
210        });
211         this.input.on('change', this.onFileSelected, this);
212         this.input.dom.click();
213     },
214     
215     onFileSelected : function(e)
216     {
217          e.preventDefault();
218         
219         if(typeof(this.input.dom.files) == 'undefined' || !this.input.dom.files.length){
220             return;
221         }
222     
223          
224         this.addFiles(Array.prototype.slice.call(this.input.dom.files), false);
225     },
226     
227     addFiles : function(far, fire_add) {
228
229          
230         var editor =  this.editorcore;
231   
232         if (!far.length) {
233             if (fire_add) {
234                 this.editor.syncValue();
235                 editor.owner.fireEvent('editorevent', editor.owner, false);
236                 editor.owner.fireEvent('imageadd', editor.owner, false);
237             }
238             return;
239         }
240         
241         var f = far.pop();
242         
243         if (!f.type.match(/^image/)) {
244             this.addFiles(far, fire_add);
245             return;
246         }
247          
248         var sn = this.selectedNode;
249         
250         var bl = sn  && this.editorcore.enableBlocks ? Roo.htmleditor.Block.factory(sn) : false;
251         
252         
253         var reader = new FileReader();
254         reader.addEventListener('load', (function() {
255             if (bl) {
256                 var oldSrc = bl.image_src;
257                 bl.image_src = reader.result;
258                 //bl.caption = f.name;
259                 bl.updateElement(sn);
260                 this.editor.syncValue();
261                 editor.owner.fireEvent('editorevent', editor.owner, false);
262                 editor.owner.fireEvent('imageupdate', editor.owner, sn, oldSrc);
263                 // we only do the first file!! and replace.
264                 return;
265             }
266             if (this.editorcore.enableBlocks) {
267                 var fig = new Roo.htmleditor.BlockFigure({
268                     image_src :  reader.result,
269                     caption : '',
270                     caption_display : 'none'  //default to hide captions..
271                  });
272                 editor.insertAtCursor(fig.toHTML());
273                 this.addFiles(far, true);
274                 return;
275             }
276             // just a standard img..
277             if (sn && sn.tagName.toUpperCase() == 'IMG') {
278                 var oldSrc = sn.src;
279                 sn.src = reader.result;
280                 this.editor.syncValue();
281                 editor.owner.fireEvent('editorevent', editor.owner, false);
282                 editor.owner.fireEvent('imageupdate', editor.owner, sn, oldSrc);
283                 return;
284             }
285             editor.insertAtCursor('<img src="' + reader.result +'">');
286             this.addFiles(far, true);
287             
288         }).createDelegate(this));
289         reader.readAsDataURL(f);
290         
291     
292      },
293     
294     
295     onBtnClick : function(id)
296     {
297        this.editorcore.relayCmd(id);
298        this.editorcore.focus();
299     },
300     
301     onLinkClick : function(btn) {
302         var url = this.selectedNode && this.selectedNode.tagName.toUpperCase() == 'A' ?
303                 this.selectedNode.getAttribute('href') : '';
304             
305         Roo.bootstrap.MessageBox.show({
306             title : "Add / Edit Link URL",
307             msg : "Enter the URL for the link",
308             buttons: Roo.bootstrap.MessageBox.OKCANCEL,
309             minWidth: 250,
310             scope : this,
311             prompt:true,
312             multiline: false,
313             modal : true,
314             value : url,
315             fn:  function(pressed, newurl) {
316                 if (pressed != 'ok') {
317                     this.editorcore.focus();
318                     return;
319                 }
320                 if (url != '') {
321                     this.selectedNode.setAttribute('href', newurl);
322                     this.editor.syncValue();
323                     return;
324                 }
325                 if(newurl && newurl .match(/http(s):\/\/.+/)) {
326                     this.editorcore.relayCmd('createlink', newurl);
327                 }
328                 this.editorcore.focus();
329             },
330             cls : this.editorcore.linkDialogCls
331         });
332     },
333     /**
334      * Protected method that will not generally be called directly. It triggers
335      * a toolbar update by reading the markup state of the current selection in the editor.
336      */
337     updateToolbar: function(editor ,ev, sel){
338
339         if(!this.editorcore.activated){
340             this.editor.onFirstFocus(); // is this neeed?
341             return;
342         }
343
344         var btns = this.buttons; 
345         var doc = this.editorcore.doc;
346         var hasToggle  = false;
347         btns.each(function(e) {
348             if (e.enableToggle && e.cmd) {
349                 hasToggle = hasToggle  || (['align-left', 'align-right', 'align-center', 'image' , 'link', 'underline'].indexOf(e.btnid) < 0 && doc.queryCommandState(e.cmd));
350                 e.setActive(doc.queryCommandState(e.cmd));
351             }
352         }, this);
353         
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         var ans = this.editorcore.getAllAncestors();
365         if (!sel) { 
366             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
367             sel = sel ? sel : this.editorcore.doc.body;
368             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
369             
370         }
371         
372         var lastSel = this.selectedNode;
373         this.selectedNode = sel;
374          
375         // ok see if we are editing a block?
376         
377         var db = false;
378         // you are not actually selecting the block.
379         if (sel && sel.hasAttribute('data-block')) {
380             db = sel;
381         } else if (sel && sel.closest('[data-block]')) {
382             db = sel.closest('[data-block]');
383         }
384         
385         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
386             e.classList.remove('roo-ed-selection');
387         });
388         
389         var block = false;
390         if (db && this.editorcore.enableBlocks) {
391             block = Roo.htmleditor.Block.factory(db);
392             
393             if (block) {
394                 db.className =  (db.classList.length > 0  ? db.className + ' ' : '') +
395                     ' roo-ed-selection';
396                 sel = this.selectedNode = db;
397             }
398         }
399         
400         // highlight the 'a'..
401         var tn = sel && sel.tagName.toUpperCase() || '';
402         if (!block && sel && tn != 'A') {
403             var asel = sel.closest('A');
404             if (asel) {
405                 sel = asel;
406             }
407         }
408        
409         btns.get('link').setActive(tn == 'A' && this.selectedNode.hasAttribute('href'));
410         btns.get('image').setActive(tn == 'IMG' || this.editorcore.enableBlocks && tn == 'FIGURE');
411         btns.get('underline').setActive(tn == 'U' || sel.closest('u') ? true : false);
412         
413         Roo.bootstrap.menu.Manager.hideAll();
414          
415         
416         
417         
418         
419         // handle delete button..
420         if (hasToggle || (tn.length && tn == 'BODY')) {
421             this.deleteBtn.hide();
422             return;
423             
424         }
425         this.deleteBtn.show();
426         
427         
428         
429         //this.editorsyncValue();
430     },
431     onFirstFocus: function() {
432         this.buttons.each(function(item){
433            item.enable();
434         });
435     },
436     
437     onDelete : function()
438     {
439         var range = this.editorcore.createRange();
440         var selection = this.editorcore.getSelection();
441         var sn = this.selectedNode;
442         range.setStart(sn,0);
443         range.setEnd(sn,0); 
444         
445         
446         if (sn.hasAttribute('data-block')) {
447             var block = Roo.htmleditor.Block.factory(this.selectedNode);
448             if (block) {
449                 sn = block.removeNode();
450                 sn.parentNode.removeChild(sn);
451                 selection.removeAllRanges();
452                 selection.addRange(range);
453                 this.updateToolbar(null, null, null);
454                 if (sn.tagName.toUpperCase() == 'FIGURE') {
455                     this.editor.syncValue();
456                     this.editor.fireEvent('imagedelete', this.editor, sn);
457                 }
458                 
459                 this.selectedNode = false;
460                 this.editorcore.fireEditorEvent(false);
461                 return;
462             }   
463              
464         }
465         if (!sn) {
466             return; // should not really happen..
467         }
468         if (sn && sn.tagName == 'BODY') {
469             return;
470         }
471         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
472         
473         // remove and keep parents.
474         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
475         a.replaceTag(sn);
476         
477         selection.removeAllRanges();
478         selection.addRange(range);
479         if (sn.tagName.toUpperCase() == 'IMG"') {
480             this.editor.syncValue();
481             this.editor.fireEvent('imagedelete', this.editor, sn);
482         }
483         
484         this.selectedNode = false;
485         this.editorcore.fireEditorEvent(false);
486         
487         
488     },
489     
490     
491     toggleSourceEdit : function(sourceEditMode){
492         
493           
494         if(sourceEditMode){
495             Roo.log("disabling buttons");
496            this.buttons.each( function(item){
497                 if(item.cmd != 'pencil'){
498                     item.disable();
499                 }
500             });
501           
502         }else{
503             Roo.log("enabling buttons");
504             if(this.editorcore.initialized){
505                 this.buttons.each( function(item){
506                     item.enable();
507                 });
508             }
509             
510         }
511         Roo.log("calling toggole on editor");
512         // tell the editor that it's been pressed..
513         this.editor.toggleSourceEdit(sourceEditMode);
514        
515     }
516 });
517
518
519
520