1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2007, 2008, 2009, 2010, 2013, 2014, 2016 Synacor, Inc. 5 * 6 * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at: https://www.zimbra.com/license 9 * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15 10 * have been added to cover use of software over a computer network and provide for limited attribution 11 * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B. 12 * 13 * Software distributed under the License is distributed on an "AS IS" basis, 14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. 15 * See the License for the specific language governing rights and limitations under the License. 16 * The Original Code is Zimbra Open Source Web Client. 17 * The Initial Developer of the Original Code is Zimbra, Inc. All rights to the Original Code were 18 * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015. 19 * 20 * All portions of the code are Copyright (C) 2007, 2008, 2009, 2010, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 /** 25 * Lite Html Editor 26 * 27 * It uses an text area as an editor where text is plain or all with the same style. 28 * 29 * @author Rajesh Segu 30 * @class 31 * @constructor 32 */ 33 ZmLiteHtmlEditor = function(params) { 34 if (arguments.length == 0) return; 35 36 params.className = params.className || "ZmLiteHtmlEditor"; 37 DwtComposite.call(this, params); 38 39 this._mode = params.mode || ZmLiteHtmlEditor.TEXT; 40 this._initialize(); 41 42 43 }; 44 45 ZmLiteHtmlEditor.prototype = new DwtComposite(); 46 ZmLiteHtmlEditor.prototype.constructor = ZmLiteHtmlEditor; 47 48 //Constants 49 50 ZmLiteHtmlEditor.HTML = 1; 51 ZmLiteHtmlEditor.TEXT = 2; 52 53 ZmLiteHtmlEditor._VALUE = "value"; 54 55 // Font Styles 56 ZmLiteHtmlEditor.BOLD_STYLE = "bold"; 57 ZmLiteHtmlEditor.ITALIC_STYLE = "italic"; 58 ZmLiteHtmlEditor.UNDERLINE_STYLE = "underline"; 59 60 ZmLiteHtmlEditor.FONT_SIZE_STYLE = "fontsize"; 61 ZmLiteHtmlEditor.FONT_FAMILY_STYLE = "fontfamily"; 62 ZmLiteHtmlEditor.FONT_SIZE_VALUES = ["8pt", "10pt", "12pt", "14pt", "18pt", "24pt", "36pt"]; 63 64 ZmLiteHtmlEditor.FONT_COLOR = "fontcolor"; 65 66 ZmLiteHtmlEditor.FONT_SIZE_VALUES = ["8pt", "10pt", "12pt", "14pt", "18pt", "24pt", "36pt"]; 67 ZmLiteHtmlEditor.FONT_FAMILY = [ 68 {name:"Tahoma", value:"Tahoma, Verdana, Arial, Helvetica, sans-serif" }, 69 {name:"Arial", value:"Arial, Helvetica, sans-serif" }, 70 {name:"Times New Roman", value:"Times New Roman, Times, serif" }, 71 {name:"Courier", value:"Courier, Courier New, mono" }, 72 {name:"Verdana", value:"Verdana, Tahoma, Arial, Helvetica, sans-serif" } 73 ]; 74 75 ZmLiteHtmlEditor.STYLE = {}; 76 ZmLiteHtmlEditor.STYLE[ZmLiteHtmlEditor.BOLD_STYLE] = "fontWeight"; 77 ZmLiteHtmlEditor.STYLE[ZmLiteHtmlEditor.ITALIC_STYLE] = "fontStyle"; 78 ZmLiteHtmlEditor.STYLE[ZmLiteHtmlEditor.UNDERLINE_STYLE] = "textDecoration"; 79 ZmLiteHtmlEditor.STYLE[ZmLiteHtmlEditor.FONT_SIZE_STYLE] = "fontSize"; 80 ZmLiteHtmlEditor.STYLE[ZmLiteHtmlEditor.FONT_FAMILY_STYLE] = "fontFamily"; 81 ZmLiteHtmlEditor.STYLE[ZmLiteHtmlEditor.FONT_COLOR] = "color"; 82 83 84 // Public methods 85 86 ZmLiteHtmlEditor.prototype.toString = 87 function() { 88 return "ZmLiteHtmlEditor"; 89 }; 90 91 ZmLiteHtmlEditor.prototype.getEditor = 92 function(){ 93 return this._textArea; 94 }; 95 96 ZmLiteHtmlEditor.prototype.getContent = 97 function(){ 98 return this._mode == ZmLiteHtmlEditor.HTML 99 ? this.getHtmlContent() 100 : this.getTextContent(); 101 }; 102 103 ZmLiteHtmlEditor.prototype.getTextContent = 104 function(){ 105 return this._textArea.value || ""; 106 }; 107 108 //<B><U><I><font color="#FF9900"><font face="arial"><font size=16>RAJESH SEGU IS HERE</I></U></B> 109 //width: 100%; height: 40px; font-family: Courier,Courier New,mono; font-size: 18pt; color: rgb(0, 0, 0); font-weight: bold; font-style: italic; text-decoration: underline; 110 111 ZmLiteHtmlEditor.prototype.getHtmlContent = 112 function(tag){ 113 114 var html = ["<span style='",this.getCSS(),"'>", 115 AjxStringUtil.htmlEncode(this.getTextContent(), true).replace(/\r?\n/g, "<br/>"), 116 "</span>"]; 117 118 return html.join(""); 119 }; 120 121 ZmLiteHtmlEditor.prototype.getCSS = 122 function() { 123 var style = this._textArea.style; 124 var css = []; 125 126 if (style.fontFamily) 127 css.push("font-family: " + style.fontFamily); 128 if (style.fontSize) 129 css.push("font-size: " + style.fontSize); 130 if (style.color) 131 css.push("color: " + style.color); 132 if (style.fontWeight) 133 css.push("font-weight: " + style.fontWeight); 134 if (style.fontStyle) 135 css.push("font-style: " + style.fontStyle); 136 if (style.textDecoration) 137 css.push("text-decoration: " + style.textDecoration); 138 if (style.color) 139 css.push("color: " + style.color); 140 141 return css.join(";"); 142 }; 143 144 //Supports only text content 145 ZmLiteHtmlEditor.prototype.setContent = 146 function(content){ 147 this._textArea.value = (content || ""); 148 }; 149 150 ZmLiteHtmlEditor.prototype.setSelectionText = function(text) { 151 Dwt.setSelectionText(this._textArea, text); 152 }; 153 154 ZmLiteHtmlEditor.prototype.clear = 155 function() { 156 this.setContent(""); 157 this._setDefaultStyles(); 158 } 159 160 ZmLiteHtmlEditor.prototype.addModeChangeListener = 161 function(listener){ 162 this.addListener(DwtEvent.STATE_CHANGE, listener); 163 }; 164 165 ZmLiteHtmlEditor.prototype.getMode = 166 function(){ 167 return this._mode; 168 }; 169 170 ZmLiteHtmlEditor.prototype.setMode = 171 function(mode, force) { 172 173 if ( (!force && mode == this._mode) || 174 (mode != ZmLiteHtmlEditor.HTML && mode != ZmLiteHtmlEditor.TEXT) ) 175 { 176 return; 177 } 178 179 this._mode = mode; 180 181 if(mode == ZmLiteHtmlEditor.HTML) { 182 this._createFormatToolBar(); 183 this._enableToolbar(true); 184 this._setDefaultStyles(); 185 }else{ 186 this._enableToolbar(false); 187 this._clearAllStyles(); 188 } 189 190 this.resetSize(); 191 192 // Notify mode change listeners. 193 this.notifyListeners(DwtEvent.STATE_CHANGE); 194 }; 195 196 ZmLiteHtmlEditor.prototype.isHtmlMode = 197 function(){ 198 return ( this._mode == ZmLiteHtmlEditor.HTML ); 199 }; 200 201 ZmLiteHtmlEditor.prototype.reverseMode = 202 function(){ 203 this.setMode(( (this._mode == ZmLiteHtmlEditor.HTML)? ZmLiteHtmlEditor.TEXT : ZmLiteHtmlEditor.HTML),false); 204 }; 205 206 ZmLiteHtmlEditor.prototype.setSize = 207 function( width, height ){ 208 DwtComposite.prototype.setSize.call(this, width, height); 209 this.resetSize(); 210 }; 211 212 ZmLiteHtmlEditor.prototype.resetSize = function(){ 213 var height = this.getHtmlElement().offsetHeight; 214 var toolbarHeight = this._basicToolBar.getSize().y; 215 if (this._mode == ZmLiteHtmlEditor.HTML) { 216 toolbarHeight += this._formatToolBar.getSize().y; 217 } 218 this._textArea.style.width = "100%"; 219 this._textArea.style.height = height - toolbarHeight - 2 + "px"; 220 }; 221 222 //KeyPress event listener 223 ZmLiteHtmlEditor.prototype.addKeyPressListener = 224 function(listener){ 225 this._keyPressListener = listener; 226 }; 227 228 229 ZmLiteHtmlEditor.prototype.enable = 230 function(enable){ 231 if(this._textArea) 232 this._textArea.disabled = (!enable); 233 }; 234 235 ZmLiteHtmlEditor.prototype.insertText = 236 function(text) { 237 this._textArea.focus(); 238 this._textArea.value += text; 239 }; 240 241 ZmLiteHtmlEditor.prototype.getBasicToolBar = 242 function() { 243 return this._basicToolBar; 244 }; 245 246 ZmLiteHtmlEditor.prototype.getFormatToolBar = 247 function() { 248 return this._formatToolBar; 249 }; 250 251 //Private Methods 252 253 ZmLiteHtmlEditor.prototype._initialize = function() { 254 var id = this.getHTMLElId(); 255 this._createHtmlFromTemplate(this.TEMPLATE, { id: id }); 256 this._textArea = this._initEditor(); 257 258 this._textArea[ AjxEnv.isIE ? "onkeydown" : "onkeypress" ] = AjxCallback.simpleClosure(this._keyPressHandler,this); 259 260 var toolBarArgs = { 261 parent:this, 262 parentElement: id + "_toolBar", 263 posStyle:Dwt.RELATIVE_STYLE, 264 buttons: [ZmOperation.IM_HTML], 265 index:0 266 }; 267 this._basicToolBar = new ZmButtonToolBar(toolBarArgs); 268 this._basicToolBar.addSelectionListener(ZmOperation.IM_HTML, new AjxListener(this, this._changeEditorModeListener)); 269 270 this.setMode(this._mode, true); 271 }; 272 273 ZmLiteHtmlEditor.prototype._initEditor = function(){ 274 var htmlEl = Dwt.byId(this.getHTMLElId() + "_textarea"); 275 276 this._textAreaId = "textarea_" + Dwt.getNextId(); 277 var html = [ 278 "<textarea id='", 279 this._textAreaId, 280 "' class='ZmHtmlEditorTextArea' style='width:100%;'></textarea>" 281 ].join(""); 282 htmlEl.innerHTML = html; 283 return Dwt.byId(this._textAreaId); 284 }; 285 286 ZmLiteHtmlEditor.prototype._keyPressHandler = 287 function(ev){ 288 if (AjxEnv.isIE) 289 ev = window.event; 290 291 if(this._keyPressListener){ 292 this._keyPressListener.run(ev); 293 } 294 295 }; 296 297 ZmLiteHtmlEditor.prototype._changeEditorModeListener = function() { 298 this.reverseMode(); 299 this.focus(); 300 }; 301 302 //Styles 303 304 ZmLiteHtmlEditor.prototype._clearAllStyles = 305 function(){ 306 var style = this._textArea.style; 307 style.cssText = ""; 308 this.resetSize(); 309 }; 310 311 ZmLiteHtmlEditor.prototype._setDefaultStyles = 312 function(){ 313 // var style = this._textArea.style; 314 // style.fontFamily = appCtxt.get(ZmSetting.COMPOSE_INIT_FONT_FAMILY); 315 // style.fontSize = appCtxt.get(ZmSetting.COMPOSE_INIT_FONT_SIZE); 316 // style.color = appCtxt.get(ZmSetting.COMPOSE_INIT_FONT_COLOR); 317 // style.fontWeight = "normal"; 318 // style.fontStyle = "normal"; 319 // style.textDecoration = "none"; 320 }; 321 322 ZmLiteHtmlEditor.prototype.setStyle = 323 function(property,value){ 324 this._textArea.style[property] = value; 325 }; 326 327 ZmLiteHtmlEditor.prototype.getStyle = 328 function(property){ 329 return this._textArea.style[property]; 330 }; 331 332 //Toolbar 333 334 ZmLiteHtmlEditor.prototype._createFormatToolBar = function(){ 335 if (!this._formatToolBar) { 336 var formatToolBar = this._formatToolBar = new DwtToolBar({parent:this, className:"ZToolbar", 337 posStyle:DwtControl.RELATIVE_STYLE, cellSpacing:2, index:0}); 338 this._initFormatToolBar(formatToolBar); 339 ZmLiteHtmlEditor._toolbarHeight = ZmLiteHtmlEditor._toolbarHeight || this._formatToolBar.getHtmlElement().offsetHeight; 340 this.setSize(Dwt.DEFAULT, this.getH() + ZmLiteHtmlEditor._toolbarHeight); 341 } 342 }; 343 344 ZmLiteHtmlEditor.prototype._enableToolbar = 345 function(enable) { 346 var visible = !!enable; 347 if (this._formatToolBar && (visible != !!this._formatToolBar.getVisible())) { 348 this._formatToolBar.setVisible(visible); 349 var sizeDiff = visible ? ZmLiteHtmlEditor._toolbarHeight : -ZmLiteHtmlEditor._toolbarHeight; 350 this.setSize(Dwt.DEFAULT, this.getH() + sizeDiff); 351 } 352 }; 353 354 ZmLiteHtmlEditor.prototype._initFormatToolBar = function(tb){ 355 356 this._createFontFamilyMenu(tb); 357 358 this._createFontSizeMenu(tb); 359 360 new DwtControl({parent:tb, className:"vertSep"}); 361 362 var listener = new AjxListener(this, this._fontStyleListener); 363 var params = {parent:tb, style:DwtButton.TOGGLE_STYLE}; 364 this._boldButton = new DwtToolBarButton(params); 365 this._boldButton.setImage("Bold"); 366 this._boldButton.setToolTipContent(ZmMsg.boldText); 367 this._boldButton.setData(ZmLiteHtmlEditor._VALUE, ZmLiteHtmlEditor.BOLD_STYLE); 368 this._boldButton.addSelectionListener(listener); 369 370 this._italicButton = new DwtToolBarButton(params); 371 this._italicButton.setImage("Italics"); 372 this._italicButton.setToolTipContent(ZmMsg.italicText); 373 this._italicButton.setData(ZmLiteHtmlEditor._VALUE, ZmLiteHtmlEditor.ITALIC_STYLE); 374 this._italicButton.addSelectionListener(listener); 375 376 this._underlineButton = new DwtToolBarButton(params); 377 this._underlineButton.setImage("Underline"); 378 this._underlineButton.setToolTipContent(ZmMsg.underlineText); 379 this._underlineButton.setData(ZmLiteHtmlEditor._VALUE, ZmLiteHtmlEditor.UNDERLINE_STYLE); 380 this._underlineButton.addSelectionListener(listener); 381 382 new DwtControl({parent:tb, className:"vertSep"}); 383 384 this._fontColorButton = new ZmLiteHtmlEditorColorPicker(tb,null,"ZToolbarButton"); 385 this._fontColorButton.dontStealFocus(); 386 this._fontColorButton.setImage("FontColor"); 387 this._fontColorButton.showColorDisplay(true); 388 this._fontColorButton.setToolTipContent(ZmMsg.fontColor); 389 this._fontColorButton.setData(ZmLiteHtmlEditor._VALUE, ZmLiteHtmlEditor.FONT_COLOR); 390 this._fontColorButton.setColor("#000000"); 391 this._fontColorButton.addSelectionListener(new AjxListener(this, this._fontStyleListener)); 392 }; 393 394 ZmLiteHtmlEditor.prototype._createFontFamilyMenu = 395 function(tb) { 396 this._fontFamilyButton = new DwtToolBarButton({parent:tb}); 397 this._fontFamilyButton.dontStealFocus(); 398 this._fontFamilyButton.setAlign(DwtLabel.ALIGN_LEFT); 399 var menu = new ZmPopupMenu(this._fontFamilyButton); 400 var listener = new AjxListener(this, this._fontFamilyListener); 401 402 for (var i = 0; i < ZmLiteHtmlEditor.FONT_FAMILY.length; i++) { 403 var item = ZmLiteHtmlEditor.FONT_FAMILY[i]; 404 var mi = menu.createMenuItem(item.name, {text:item.name}); 405 mi.addSelectionListener(listener); 406 mi.setData(ZmLiteHtmlEditor._VALUE, i); 407 if(i == 0){ 408 this._fontFamilyButton.setText(item.name); 409 } 410 } 411 412 this._fontFamilyButton.setMenu(menu); 413 }; 414 415 ZmLiteHtmlEditor.prototype._createFontSizeMenu = 416 function(tb) { 417 this._fontSizeButton = new DwtToolBarButton({parent:tb}); 418 this._fontSizeButton.dontStealFocus(); 419 var menu = new ZmPopupMenu(this._fontSizeButton); 420 var listener = new AjxListener(this, this._fontSizeListener); 421 422 for (var i = 0; i < ZmLiteHtmlEditor.FONT_SIZE_VALUES.length; i++) { 423 var item = ZmLiteHtmlEditor.FONT_SIZE_VALUES[i]; 424 var num = i+1; 425 var text = num + " (" + item + ")"; 426 var mi = menu.createMenuItem(i, {text:text}); 427 mi.addSelectionListener(listener); 428 mi.setData(ZmHtmlEditor.VALUE, num); 429 if(i == 0){ 430 this._fontSizeButton.setText(text); 431 } 432 } 433 434 this._fontSizeButton.setMenu(menu); 435 436 }; 437 438 ZmLiteHtmlEditor.prototype._fontFamilyListener = 439 function(ev) { 440 var id = ev.item.getData(ZmLiteHtmlEditor._VALUE); 441 this.setStyle("fontFamily",ZmLiteHtmlEditor.FONT_FAMILY[id].value); 442 this._fontFamilyButton.setText(ZmLiteHtmlEditor.FONT_FAMILY[id].name); 443 this.focus(); 444 }; 445 446 ZmLiteHtmlEditor.prototype._fontSizeListener = 447 function(ev) { 448 var num = ev.item.getData(ZmLiteHtmlEditor._VALUE); 449 var size = ZmLiteHtmlEditor.FONT_SIZE_VALUES[num-1]; 450 this.setStyle("fontSize",size); 451 this._fontSizeButton.setText(num + " (" + size + ")"); 452 this.focus(); 453 }; 454 455 ZmLiteHtmlEditor.prototype._fontStyleListener = 456 function(ev) { 457 458 var styleType = ev.item.getData(ZmHtmlEditor.VALUE); 459 var style = ZmLiteHtmlEditor.STYLE[styleType]; 460 if(!style) return; 461 462 var value = this.getStyle(style); 463 if(styleType == ZmLiteHtmlEditor.UNDERLINE_STYLE){ 464 this.setStyle( style , (( !value || value == "none" ) ? "underline" : "none")); 465 }else if (styleType == ZmLiteHtmlEditor.BOLD_STYLE){ 466 this.setStyle( style , (( !value || value == "normal" ) ? "bold" : "normal")); 467 }else if(styleType == ZmLiteHtmlEditor.ITALIC_STYLE){ 468 this.setStyle( style , (( !value || value == "normal" ) ? "italic" : "normal")); 469 }else if(styleType == ZmLiteHtmlEditor.FONT_COLOR){ 470 this.setStyle( style, ( ev.item.getColor() || "#000000" ) ); 471 } 472 this.focus(); 473 }; 474 475 ZmLiteHtmlEditor.prototype.focus = 476 function() { 477 this.getEditor().focus(); 478 }; 479 480 ZmLiteHtmlEditorColorPicker = function(parent,style,className) { 481 DwtButtonColorPicker.call(this, parent,style,className); 482 } 483 484 ZmLiteHtmlEditorColorPicker.prototype = new DwtButtonColorPicker; 485 ZmLiteHtmlEditorColorPicker.prototype.constructor = ZmLiteHtmlEditorColorPicker; 486 487 ZmLiteHtmlEditorColorPicker.prototype.TEMPLATE = "dwt.Widgets#ZToolbarButton"; 488