3 * @class Roo.bootstrap.form.HtmlEditorToolbar.Standard
4 * @parent Roo.bootstrap.form.HtmlEditor
5 * @extends Roo.bootstrap.nav.Simplebar
11 new Roo.bootstrap.form.HtmlEditor({
14 new Roo.bootstrap.form.HtmlEditorToolbar.Standard({
15 disable : { fonts: 1 , format: 1, ..., ... , ...],
21 * @cfg {Object} disable List of elements to disable..
22 * @cfg {Array} btns List of additional buttons.
26 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
29 Roo.bootstrap.form.HtmlEditorToolbar.Standard = function(config)
32 Roo.apply(this, config);
34 // default disabled, based on 'good practice'..
35 this.disable = this.disable || {};
36 Roo.applyIf(this.disable, {
39 specialElements : true
41 Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.constructor.call(this, config);
43 this.editor = config.editor;
44 this.editorcore = config.editor.editorcore;
46 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.btnid; });
48 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
49 // dont call parent... till later.
51 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbar.Standard, Roo.bootstrap.nav.Simplebar, {
61 "h1","h2","h3","h4","h5","h6",
63 "abbr", "acronym", "address", "cite", "samp", "var",
70 onRender : function(ct, position)
72 // Roo.log("Call onRender: " + this.xtype);
74 Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.onRender.call(this, ct, position);
76 this.el.dom.style.marginBottom = '0';
78 var editorcore = this.editorcore;
79 var editor= this.editor;
82 var btn = function(id, cmd , toggle, handler, html){
84 var event = toggle ? 'toggle' : 'click';
93 cls : 'roo-html-editor-btn-' + id,
94 cmd : cmd, // why id || cmd
95 enableToggle: toggle !== false,
97 pressed : toggle ? false : null,
100 a.listeners[toggle ? 'toggle' : 'click'] = function() {
101 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
107 // var cb_box = function...
114 cls : 'roo-html-editor-font-chooser',
122 Roo.each(this.formats, function(f) {
123 style.menu.items.push({
126 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
131 editorcore.insertTag(this.tagname);
138 children.push(style);
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);
149 btn('image', false, true, this.onImageClick);
150 btn('list','insertunorderedlist',true);
151 btn('list-ol','insertorderedlist',true);
153 btn('pencil', false,true, function(btn){
155 this.toggleSourceEdit(btn.pressed);
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]);
166 this.xtype = 'NavSimplebar'; // why?
168 for(var i=0;i< children.length;i++) {
170 this.buttons.add(this.addxtypeChild(children[i]));
173 this.buildToolbarDelete();
175 editor.on('editorevent', this.updateToolbar, this);
178 buildToolbarDelete : function()
181 /* this.addxtypeChild({
184 cls : 'roo-htmleditor-fill'
187 this.deleteBtn = this.addxtypeChild({
193 click : this.onDelete.createDelegate(this)
196 this.deleteBtn.hide();
200 onImageClick : function()
203 this.input.un('change', this.onFileSelected, this);
205 this.input = Roo.get(document.body).createChild({
208 style : 'display:none',
211 this.input.on('change', this.onFileSelected, this);
212 this.input.dom.click();
215 onFileSelected : function(e)
219 if(typeof(this.input.dom.files) == 'undefined' || !this.input.dom.files.length){
224 this.addFiles(Array.prototype.slice.call(this.input.dom.files), false);
227 addFiles : function(far, fire_add) {
230 var editor = this.editorcore;
234 this.editor.syncValue();
235 editor.owner.fireEvent('editorevent', editor.owner, false);
236 editor.owner.fireEvent('imageadd', editor.owner, false);
243 if (!f.type.match(/^image/)) {
244 this.addFiles(far, fire_add);
248 var sn = this.selectedNode;
250 var bl = sn && this.editorcore.enableBlocks ? Roo.htmleditor.Block.factory(sn) : false;
253 var reader = new FileReader();
254 reader.addEventListener('load', (function() {
256 bl.image_src = reader.result;
257 //bl.caption = f.name;
258 bl.updateElement(sn);
259 this.editor.syncValue();
260 editor.owner.fireEvent('editorevent', editor.owner, false);
261 editor.owner.fireEvent('imageupdate', editor.owner, sn);
262 // we only do the first file!! and replace.
265 if (this.editorcore.enableBlocks) {
266 var fig = new Roo.htmleditor.BlockFigure({
267 image_src : reader.result,
269 caption_display : 'none' //default to hide captions..
271 editor.insertAtCursor(fig.toHTML());
272 this.addFiles(far, true);
275 // just a standard img..
276 if (sn && sn.tagName.toUpperCase() == 'IMG') {
277 sn.src = reader.result;
278 this.editor.syncValue();
279 editor.owner.fireEvent('editorevent', editor.owner, false);
280 editor.owner.fireEvent('imageupdate', editor.owner, sn);
283 editor.insertAtCursor('<img src="' + reader.result +'">');
284 this.addFiles(far, true);
286 }).createDelegate(this));
287 reader.readAsDataURL(f);
293 onBtnClick : function(id)
295 this.editorcore.relayCmd(id);
296 this.editorcore.focus();
299 onLinkClick : function(btn) {
300 var url = this.selectedNode && this.selectedNode.tagName.toUpperCase() == 'A' ?
301 this.selectedNode.getAttribute('href') : '';
303 Roo.bootstrap.MessageBox.show({
304 title : "Add / Edit Link URL",
305 msg : "Enter the URL for the link",
306 buttons: Roo.bootstrap.MessageBox.OKCANCEL,
313 fn: function(pressed, newurl) {
314 if (pressed != 'ok') {
315 this.editorcore.focus();
319 this.selectedNode.setAttribute('href', newurl);
322 if(newurl && newurl .match(/http(s):\/\/.+/)) {
323 this.editorcore.relayCmd('createlink', newurl);
325 this.editorcore.focus();
330 * Protected method that will not generally be called directly. It triggers
331 * a toolbar update by reading the markup state of the current selection in the editor.
333 updateToolbar: function(editor ,ev, sel){
335 if(!this.editorcore.activated){
336 this.editor.onFirstFocus(); // is this neeed?
340 var btns = this.buttons;
341 var doc = this.editorcore.doc;
342 var hasToggle = false;
343 btns.each(function(e) {
344 if (e.enableToggle && e.cmd) {
345 hasToggle = hasToggle || (['align-left', 'align-right', 'align-center', 'image' , 'link', 'underline'].indexOf(e.btnid) < 0 && doc.queryCommandState(e.cmd));
346 e.setActive(doc.queryCommandState(e.cmd));
352 (ev.type == 'mouseup' || ev.type == 'click' ) &&
353 ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
354 // they have click on an image...
355 // let's see if we can change the selection...
360 var ans = this.editorcore.getAllAncestors();
362 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
363 sel = sel ? sel : this.editorcore.doc.body;
364 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
368 var lastSel = this.selectedNode;
369 this.selectedNode = sel;
371 // ok see if we are editing a block?
374 // you are not actually selecting the block.
375 if (sel && sel.hasAttribute('data-block')) {
377 } else if (sel && sel.closest('[data-block]')) {
378 db = sel.closest('[data-block]');
381 Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
382 e.classList.remove('roo-ed-selection');
386 if (db && this.editorcore.enableBlocks) {
387 block = Roo.htmleditor.Block.factory(db);
390 db.className = (db.classList.length > 0 ? db.className + ' ' : '') +
392 sel = this.selectedNode = db;
396 // highlight the 'a'..
397 var tn = sel && sel.tagName.toUpperCase() || '';
398 if (!block && sel && tn != 'A') {
399 var asel = sel.closest('A');
405 btns.get('link').setActive(tn == 'A' && this.selectedNode.hasAttribute('href'));
406 btns.get('image').setActive(tn == 'IMG' || this.editorcore.enableBlocks && tn == 'FIGURE');
407 btns.get('underline').setActive(tn == 'U' || sel.closest('u') ? true : false);
409 Roo.bootstrap.menu.Manager.hideAll();
415 // handle delete button..
416 if (hasToggle || (tn.length && tn == 'BODY')) {
417 this.deleteBtn.hide();
421 this.deleteBtn.show();
425 //this.editorsyncValue();
427 onFirstFocus: function() {
428 this.buttons.each(function(item){
433 onDelete : function()
435 var range = this.editorcore.createRange();
436 var selection = this.editorcore.getSelection();
437 var sn = this.selectedNode;
438 range.setStart(sn,0);
442 if (sn.hasAttribute('data-block')) {
443 var block = Roo.htmleditor.Block.factory(this.selectedNode);
445 sn = block.removeNode();
446 sn.parentNode.removeChild(sn);
447 selection.removeAllRanges();
448 selection.addRange(range);
449 this.updateToolbar(null, null, null);
450 if (sn.tagName.toUpperCase() == 'FIGURE') {
451 this.editor.syncValue();
452 this.editor.fireEvent('imagedelete', this.editor, sn);
455 this.selectedNode = false;
456 this.editorcore.fireEditorEvent(false);
462 return; // should not really happen..
464 if (sn && sn.tagName == 'BODY') {
467 var stn = sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
469 // remove and keep parents.
470 a = new Roo.htmleditor.FilterKeepChildren({tag : false});
473 selection.removeAllRanges();
474 selection.addRange(range);
475 if (sn.tagName.toUpperCase() == 'IMG"') {
476 this.editor.syncValue();
477 this.editor.fireEvent('imagedelete', this.editor, sn);
480 this.selectedNode = false;
481 this.editorcore.fireEditorEvent(false);
487 toggleSourceEdit : function(sourceEditMode){
491 Roo.log("disabling buttons");
492 this.buttons.each( function(item){
493 if(item.cmd != 'pencil'){
499 Roo.log("enabling buttons");
500 if(this.editorcore.initialized){
501 this.buttons.each( function(item){
507 Roo.log("calling toggole on editor");
508 // tell the editor that it's been pressed..
509 this.editor.toggleSourceEdit(sourceEditMode);