      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