1 /** 2 * @author Lukas Fryc 3 */ 4 5 (function($, rf) { 6 rf.ui = rf.ui || {}; 7 8 /** 9 * Default component configuration 10 */ 11 var defaultOptions = { 12 toolbar : 'Basic', 13 skin: 'moono', 14 readonly : false, 15 style : '', 16 styleClass : '', 17 editorStyle : '', 18 editorClass : '', 19 width : '100%', 20 height : '200px' 21 }; 22 23 var eventsForDirty = [ "key", "paste", "undo", "redo" ]; 24 25 rf.ui.Editor = function(componentId, options, config) { 26 $super.constructor.call(this, componentId); 27 this.options = $.extend({}, defaultOptions, options); 28 29 this.componentId = componentId; 30 this.textareaId = componentId + ':inp'; 31 this.editorElementId = 'cke_' + this.textareaId; 32 this.valueChanged = false; 33 this.dirtyState = false; 34 this.config = $.extend({}, config); 35 36 this.attachToDom(this.componentId); 37 38 $(document).ready($.proxy(this.__initializationHandler, this)); 39 rf.Event.bindById(this.__getTextarea(), 'init', this.options.oninit, this); 40 rf.Event.bindById(this.__getTextarea(), 'dirty', this.options.ondirty, this); 41 }; 42 43 rf.BaseComponent.extend(rf.ui.Editor); 44 45 var $super = rf.ui.Editor.$super; 46 47 $.extend(rf.ui.Editor.prototype, { 48 49 name : "Editor", 50 51 __initializationHandler : function() { 52 this.ckeditor = CKEDITOR.replace(this.textareaId, this.__getConfiguration()); 53 54 // register event handlers 55 if (this.__getForm()) { 56 this.__updateTextareaHandlerWrapper = rf.Event.bind(this.__getForm(), 'ajaxsubmit', $.proxy(this.__updateTextareaHandler, this)); 57 } 58 this.ckeditor.on('instanceReady', $.proxy(this.__instanceReadyHandler, this)); 59 this.ckeditor.on('blur', $.proxy(this.__blurHandler, this)); 60 this.ckeditor.on('focus', $.proxy(this.__focusHandler, this)); 61 // register handlers for 'dirty' event 62 for (var i in eventsForDirty) { 63 this.ckeditor.on(eventsForDirty[i], $.proxy(this.__checkDirtyHandlerWithDelay, this)); 64 } 65 // interval for dirty checking 66 this.dirtyCheckingInterval = window.setInterval($.proxy(this.__checkDirtyHandler, this), 100); 67 }, 68 69 __checkDirtyHandlerWithDelay : function() { 70 window.setTimeout($.proxy(this.__checkDirtyHandler, this), 0); 71 }, 72 73 __checkDirtyHandler : function() { 74 if (this.ckeditor.checkDirty()) { 75 this.dirtyState = true; 76 this.valueChanged = true; 77 this.ckeditor.resetDirty(); 78 this.__dirtyHandler(); 79 } 80 }, 81 82 __dirtyHandler : function() { 83 this.invokeEvent.call(this, "dirty", document.getElementById(this.textareaId)); 84 }, 85 86 __updateTextareaHandler : function() { 87 this.ckeditor.updateElement(); 88 }, 89 90 __instanceReadyHandler : function(e) { 91 this.__setupStyling(); 92 this.__setupPassThroughAttributes(); 93 94 this.invokeEvent.call(this, "init", document.getElementById(this.textareaId), e); 95 }, 96 97 __blurHandler : function(e) { 98 this.invokeEvent.call(this, "blur", document.getElementById(this.textareaId), e); 99 if (this.isDirty()) { 100 this.valueChanged = true; 101 this.__changeHandler(); 102 } 103 this.dirtyState = false; 104 }, 105 106 __focusHandler : function(e) { 107 this.invokeEvent.call(this, "focus", document.getElementById(this.textareaId), e); 108 }, 109 110 __changeHandler : function(e) { 111 this.invokeEvent.call(this, "change", document.getElementById(this.textareaId), e); 112 }, 113 114 __getTextarea : function() { 115 return $(document.getElementById(this.textareaId)); 116 }, 117 118 /** 119 * Returns the form where this editor component is placed 120 */ 121 __getForm : function() { 122 return $('form').has(this.__getTextarea()).get(0); 123 }, 124 125 __getConfiguration : function() { 126 var textarea = this.__getTextarea(); 127 return $.extend({ 128 skin : this.options.skin, 129 toolbar : this.__getToolbar(), 130 readOnly : textarea.attr('readonly') || this.options.readonly, 131 width : this.__resolveUnits(this.options.width), 132 height : this.__resolveUnits(this.options.height), 133 bodyClass : 'rf-ed-b', 134 defaultLanguage : this.options.lang, 135 contentsLanguage : this.options.lang 136 }, this.config); 137 }, 138 139 __setupStyling : function() { 140 var span = $(document.getElementById(this.editorElementId)); 141 if (!span.hasClass('rf-ed')) { 142 span.addClass('rf-ed'); 143 } 144 var styleClass = $.trim(this.options.styleClass + ' ' + this.options.editorClass); 145 if (this.initialStyle == undefined) { 146 this.initialStyle = span.attr('style'); 147 } 148 var style = this.__concatStyles(this.initialStyle, this.options.style, this.options.editorStyle); 149 if (this.oldStyleClass !== styleClass) { 150 if (this.oldStyleClass) { 151 span.removeClass(this.oldStyleClass); 152 } 153 span.addClass(styleClass); 154 this.oldStyleClass = styleClass; 155 } 156 if (this.oldStyle !== style) { 157 span.attr('style', style); 158 this.oldStyle = style; 159 } 160 }, 161 162 __setupPassThroughAttributes : function() { 163 var textarea = this.__getTextarea(); 164 var span = $(document.getElementById(this.editorElementId)); 165 166 // title 167 span.attr('title', textarea.attr('title')); 168 }, 169 170 __concatStyles : function() { 171 var result = ""; 172 for ( var i = 0; i < arguments.length; i++) { 173 var style = $.trim(arguments[i]); 174 if (style) { 175 result = result + style + "; "; 176 } 177 } 178 return result; 179 }, 180 181 __getToolbar : function() { 182 var toolbar = this.options.toolbar; 183 184 var lowercase = toolbar.toLowerCase(); 185 if (lowercase === 'basic') { 186 return 'Basic'; 187 } 188 if (lowercase === 'full') { 189 return 'Full'; 190 } 191 192 return toolbar; 193 }, 194 195 __setOptions : function(options) { 196 this.options = $.extend({}, defaultOptions, options); 197 }, 198 199 __resolveUnits : function(dimension) { 200 var dimension = $.trim(dimension); 201 if (dimension.match(/^[0-9]+$/)) { 202 return dimension + 'px'; 203 } else { 204 return dimension; 205 } 206 }, 207 208 getEditor : function() { 209 return this.ckeditor; 210 }, 211 212 setValue : function(newValue) { 213 this.ckeditor.setData(newValue, $.proxy(function() { 214 this.valueChanged = false; 215 this.dirtyState = false; 216 this.ckeditor.resetDirty(); 217 }, this)); 218 }, 219 220 getValue : function() { 221 return this.ckeditor.getData(); 222 }, 223 224 getInput : function() { 225 return document.getElementById(this.textareaId); 226 }, 227 228 focus : function() { 229 this.ckeditor.focus(); 230 }, 231 232 blur : function() { 233 this.ckeditor.focusManager.blur(true); 234 }, 235 236 isFocused : function() { 237 return this.ckeditor.focusManager.hasFocus; 238 }, 239 240 isDirty : function() { 241 return this.dirtyState || this.ckeditor.checkDirty(); 242 }, 243 244 isValueChanged : function() { 245 return this.valueChanged || this.isDirty(); 246 }, 247 248 setReadOnly : function(readOnly) { 249 this.ckeditor.setReadOnly(readOnly !== false); 250 }, 251 252 isReadOnly : function() { 253 return this.ckeditor.readOnly; 254 }, 255 256 destroy : function() { 257 window.clearInterval(this.dirtyCheckingInterval); 258 259 if (this.__getForm()) { 260 rf.Event.unbind(this.__getForm(), 'ajaxsubmit', this.__updateTextareaHandlerWrapper); 261 } 262 263 if (this.ckeditor) { 264 this.ckeditor.destroy(); 265 this.ckeditor = null; 266 } 267 268 this.__getTextarea().show(); 269 270 $super.destroy.call(this); 271 } 272 }); 273 })(RichFaces.jQuery, RichFaces); 274