YUI Library Examples: Rich Text Editor (beta): Calendar Plugin

Rich Text Editor (beta): Calendar Plugin

This example adds a button to the Rich Text Editor's Toolbar that displays a Calendar Control for choosing dates. It also demonstrates how to manage the state of a custom button.

Click the Date button () in the Toolbar to display the Calendar Control, then select a date and it will be placed inside the Editor.

You can also click on a date that has been inserted in the Editor and the Date button will enable. When the Date button is enabled and you click it the corresponding date will be selected in the Calendar Control when it is displayed.

This text field can contain stylized text and graphics. To cycle through all formatting options, use the keyboard shortcut Control + Shift + T to place focus on the toolbar and navigate between option heading names.

Common formatting keyboard shortcuts:

  • Control Shift B sets text to bold
  • Control Shift I sets text to italic
  • Control Shift U underlines text
  • Control Shift [ aligns text left
  • Control Shift | centers text
  • Control Shift ] aligns text right
  • Control Shift L adds an HTML link
  • To exit this text editor use the keyboard shortcut Control Shift ESC.

You have left the Rich Text Editor.

Setting up the Editor's HTML

Setting up the Editor's HTML is done by creating a textarea control on the page.

1<form method="post" action="#" id="form1"
2<textarea id="editor" name="editor" rows="20" cols="75"
3<font face="Times New Roman">This is some more test text. This is some more <b>test <i>text</i></b></font>
4This is some more test text. This is some more test text. This is some more test text. 
5This is some more test text. This is some more test text. This is some more test text. 
6This is some more test text.  
7</textarea> 
8</form> 
view plain | print | ?

Setting up the Editor's Javascript

Once the textarea is on the page, then initialize the Editor like this:

1(function() { 
2    //Setup some private variables 
3    var Dom = YAHOO.util.Dom, 
4        Event = YAHOO.util.Event, 
5        cal = null//Reference to the calendar object we are about to create 
6        selectedDate = null//Reference to the current selected date. 
7 
8        //The Editor config 
9        var myConfig = { 
10            height: '300px'
11            width: '522px'
12            animate: true
13            dompath: true 
14        }; 
15 
16    //Now let's load the Editor.. 
17    var myEditor = new YAHOO.widget.Editor('editor', myConfig); 
18 
19})(); 
view plain | print | ?

Creating the Toolbar Button and Menu

Now we can create a button and add it to the Editor's Toolbar. First we subscribe to the Editor's toolbarLoaded Custom Event. From inside that function we will set up a new button config object literal with the following properties:

  • type: (push, menu, split, spin, select, color)
  • label: The text string displayed on the button
  • value: The value is also called the Command for the button.
  • menu: A YAHOO.widget.Overlay instance to be used as a menu.

Now add it to the Toolbar group called "insertitem" like this: myEditor.toolbar.addButtonToGroup(dateConfig, 'insertitem');

1//Snipped from above 
2var myEditor = new YAHOO.widget.Editor('editor', myConfig); 
3 
4//Subscribe to the toolbarLoaded Custom event fired in render 
5myEditor.on('toolbarLoaded'function() {  
6    
7    //Setup the config for the new "Insert Date" button 
8    var dateConfig = { 
9        type: 'push'//Using a standard push button 
10        label: 'Insert Date'//The name/title of the button 
11        value: 'insertdate'//The "Command" for the button 
12        menu: function() { 
13            //Create the Overlay instance we are going to use for the menu 
14            var menu = new YAHOO.widget.Overlay('insertdate', { 
15                width: '210px'
16                height: '220px'
17                xy: [-9000,-9000], 
18                visible: false 
19            }); 
20            //Setting the body to the container that we wish to render the calendar into. 
21            menu.setBody('<div id="cal1Container"></div>'); 
22            //Set the context to the bottom left corner of the Insert Date button 
23            menu.beforeShowEvent.subscribe(function() { 
24                menu.cfg.setProperty('context', [ 
25                    myEditor.toolbar.getButtonByValue('insertdate').get('element'), 
26                    'tl'
27                    'bl' 
28                ]); 
29            }); 
30            //Show the Overlay and prep the calendar's selected date 
31            menu.showEvent.subscribe(function() { 
32                cal.deselectAll(); 
33                Dom.removeClass(cal.cells, 'selected'); 
34                //selectedDate is populated in the onAvailable call later on.. 
35                if (selectedDate != null) { 
36                    cal.cfg.setProperty('selected', selectedDate); 
37                    cal.cfg.setProperty('pagedate'new Date(selectedDate), true); 
38                    selectedDate = null
39                } 
40                cal.render(); 
41            }); 
42            menu.render(document.body); 
43            menu.element.style.visibility = 'hidden';                 
44            //return the Overlay instance here 
45            return menu; 
46        }() //This fires the function right now to return the Overlay Instance to the menu property.. 
47    }; 
48    //Add the new button to the Toolbar Group called insertitem. 
49    myEditor.toolbar.addButtonToGroup(dateConfig, 'insertitem'); 
50}); 
51myEditor.render(); 
52
view plain | print | ?

Handling the Button's State on Node Change

After we have created the new button and added it to the Toolbar, we need to listen for events to trigger our new button.

We can do that by listening to the afterNodeChange CustomEvent.

The before/afterNodeChange events are fired when something interesting happens inside the Editor. Clicks, Key Presses, etc.

From inside the afterNodeChange Event we can get access the the last element (or current element) that was affected. We get access to this element via: obj._getSelectedElement()

Now that we have a reference we can use standard DOM manipulation to change the element and the Toolbar.

In this case, we are grabbing the current element and checking to see if it or its parent has a className of date.

If it does, then we are populating the var selectedDate with the innerHTML of the element.

We are also using this opportunity to select the "insertdate" button on the toolbar, so that it becomes active. We are doing this with the following command:this.toolbar.selectButton(this.toolbar.getButtonByValue('insertdate'))

this.toolbar.getButtonByValue('insertdate') this method will return a Button reference from the toolbar, that we can pass to this.toolbar.selectButton() and cause the button to be selected.

On the next nodeChange Event, our button will be disabled automatically and this handler will re-select it if it is needed.

1//Snipped from above 
2myEditor.toolbar.addButtonToGroup(dateConfig, 'insertitem'); 
3 
4 
5//Listening to the afterNode Change Custom Event 
6myEditor.on('afterNodeChange'function() { 
7    var el = this._getSelectedElement(); //Currently Selected Element 
8    //Does it or its parent have a class of 'date' 
9    if (Dom.hasClass(el, 'date') || Dom.hasClass(el.parentNode, 'date')) { 
10        //Set the button to selected 
11        this.toolbar.selectButton(this.toolbar.getButtonByValue('insertdate')); 
12        //Capture the innerHTML of the element and use it in the Menu's show Event. 
13        if (Dom.hasClass(el.parentNode, 'date')) { 
14            selectedDate = el.parentNode.innerHTML; 
15        } else { 
16            selectedDate = el.innerHTML; 
17        } 
18        var _button = this.toolbar.getButtonByValue('insertdate'); 
19        _button._menu.hide(); 
20    } 
21}, myEditor, true); 
22 
23myEditor.toolbar.on('insertdateClick'function(ev) { 
24    var calDate = ' <span class="date">' + (ev.calDate.getMonth() + 1) 
25        + '/' + ev.calDate.getDate() 
26        + '/' + ev.calDate.getFullYear() + '</span> '
27    this._focusWindow(); 
28    this.execCommand('inserthtml', calDate); 
29    this._focusWindow(); 
30    var _button = this.toolbar.getButtonByValue('insertdate'); 
31    _button._menu.hide(); 
32}, myEditor, true); 
view plain | print | ?

Rendering the Calendar and Handle Date Selection

Now we add this code at the bottom to be activated when the Element cal1Container becomes available in the DOM.

Once that Element is active on the page, we can now build and render our Calendar control.

Notice, that we are subscribing to the Calendar's selectEvent to actually execute the insertdateClick event for the Editor.

From there, we get a button reference and call the menu's hide method to hide the Calendar.

1//Snipped from above 
2myEditor.render(); 
3 
4Event.onAvailable('cal1Container'function() { 
5    //Create the new Calendar instance 
6    cal = new YAHOO.widget.Calendar('cal1''cal1Container'); 
7    //Setup the selectEvent 
8    cal.selectEvent.subscribe(function() { 
9        var calDate = cal.getSelectedDates()[0]; 
10        //This line will fire the event "insertdateClick" that we subscribed to above.. 
11        this.toolbar.fireEvent('insertdateClick', { type: 'insertdateClick', calDate: calDate }); 
12    }, myEditor, true); 
13    //render the Calendar 
14    cal.render(); 
15}); 
view plain | print | ?

Styling the Button

There are 2 important states to style a button in the toolbar.

First is the default state, that can be accessed via this CSS rule: .yui-skin-sam .yui-toolbar-container .yui-toolbar-insertdate span.yui-toolbar-icon

Second is the selected state, that can be accessed via this CSS rule: .yui-skin-sam .yui-toolbar-container .yui-button-insertdate-selected span.yui-toolbar-icon

.yui-toolbar-container is the class applied to the top-most container of the toolbar.

.yui-toolbar-icon is an extra SPAN injected into the button for spriting an image.

.yui-toolbar-VALUE is a dynamic class added to the button based on the value passed into the buttons config. It is used for specific styling of a button that may appear in several places on the page.

The Style Rules to Create the Calendar Button in this Example

1.yui-skin-sam .yui-toolbar-container .yui-toolbar-insertdate span.yui-toolbar-icon { 
2    background-imageurl( assets/calendar_default.gif ); 
3    background-position1px 0px
4} 
5.yui-skin-sam .yui-toolbar-container .yui-button-insertdate-selected span.yui-toolbar-icon { 
6    background-imageurl( assets/calendar_active.gif ); 
7    background-position1px 0px
8} 
9/* Turn off Right border of button before us */ 
10.yui-skin-sam .yui-toolbar-container .yui-toolbar-insertimage { 
11    border-rightnone
12} 
view plain | print | ?

Full Example Javascript Source

1(function() { 
2    var Dom = YAHOO.util.Dom, 
3        Event = YAHOO.util.Event, 
4        cal = null
5        selectedDate = null
6 
7        var myConfig = { 
8            height: '300px'
9            width: '522px'
10            animate: true
11            dompath: true 
12        }; 
13 
14 
15    var myEditor = new YAHOO.widget.Editor('editor', myConfig); 
16 
17    myEditor.on('toolbarLoaded'function() {  
18        
19        var dateConfig = { 
20            type: 'push', label: 'Insert Date', value: 'insertdate'
21            menu: function() { 
22                var menu = new YAHOO.widget.Overlay('insertdate', { 
23                    width: '210px'
24                    height: '220px'
25                    xy: [-9000,-9000], 
26                    visible: false 
27                }); 
28                menu.setBody('<div id="cal1Container"></div>'); 
29                menu.beforeShowEvent.subscribe(function() { 
30                    menu.cfg.setProperty('context', [ 
31                        myEditor.toolbar.getButtonByValue('insertdate').get('element'), 
32                        'tl''bl' 
33                    ]); 
34                }); 
35                menu.showEvent.subscribe(function() { 
36                    cal.deselectAll(); 
37                    Dom.removeClass(cal.cells, 'selected'); 
38                    if (selectedDate != null) { 
39                        cal.cfg.setProperty('selected', selectedDate); 
40                        cal.cfg.setProperty('pagedate'new Date(selectedDate), true); 
41                        selectedDate = null
42                    } 
43                    cal.render(); 
44                }); 
45                menu.render(document.body); 
46                menu.element.style.visibility = 'hidden';                 
47                return menu; 
48            }() 
49        }; 
50        myEditor.toolbar.addButtonToGroup(dateConfig, 'insertitem'); 
51 
52        myEditor.on('afterNodeChange'function() { 
53            var el = this._getSelectedElement(); 
54            if (Dom.hasClass(el, 'date') || Dom.hasClass(el.parentNode, 'date')) { 
55                this.toolbar.selectButton(this.toolbar.getButtonByValue('insertdate')); 
56                if (Dom.hasClass(el.parentNode, 'date')) { 
57                    selectedDate = el.parentNode.innerHTML; 
58                } else { 
59                    selectedDate = el.innerHTML; 
60                } 
61            } 
62            var _button = this.toolbar.getButtonByValue('insertdate'); 
63            _button._menu.hide(); 
64        }, myEditor, true); 
65 
66        myEditor.toolbar.on('insertdateClick'function(ev) { 
67            var calDate = '<span class="date">' + (ev.calDate.getMonth() + 1) 
68                + '/' + ev.calDate.getDate() 
69                + '/' + ev.calDate.getFullYear() + '</span>'
70            this._focusWindow(); 
71            this.execCommand('inserthtml', calDate); 
72            this._focusWindow(); 
73            var _button = this.toolbar.getButtonByValue('insertdate'); 
74            _button._menu.hide(); 
75        }, myEditor, true); 
76    }); 
77    myEditor.render(); 
78 
79    Event.onAvailable('cal1Container'function() { 
80        cal = new YAHOO.widget.Calendar('cal1''cal1Container'); 
81        cal.selectEvent.subscribe(function() { 
82            var calDate = cal.getSelectedDates()[0]; 
83            this.toolbar.fireEvent('insertdateClick', { type: 'insertdateClick', calDate: calDate }); 
84        }, myEditor, true); 
85        cal.render(); 
86    }); 
87 
88})(); 
view plain | print | ?

YUI Logger Output:

Logger Console

INFO 483ms (+7) 5:31:29 PM:

example:

Found (#cal1Container) - render the calendar

INFO 476ms (+2) 5:31:29 PM:

example:

Adding new button (insertdate) to toolbar

INFO 474ms (+319) 5:31:29 PM:

example:

Editor Toolbar loaded

INFO 155ms (+148) 5:31:28 PM:

LogReader instance0:

LogReader initialized

INFO 7ms (+7) 5:31:28 PM:

example:

Creating Editor

INFO 0ms (+0) 5:31:28 PM:

global:

Logger initialized

Note: You are viewing this example in debug mode with logging enabled. This can significantly slow performance.

Reload with logging
and debugging disabled.

More Rich Text Editor (beta) Resources:

Copyright © 2007 Yahoo! Inc. All rights reserved.

Privacy Policy - Terms of Service - Copyright Policy - Job Openings