Impact factor for posts is a measurement of importance.

Impact factor for users reflect their authority, reputation and contribution on a particular topic.

Rating reflects the quality of posts.

Rating on Voofie is not a simple average of all ratings, but a weighted average of rating, weighted by the impact factor of users who rated.

Explore exciting communities of

Building Citation & Footnote plugin for CKEditor

28 Jul 10

CKEditor is highly customizable and can be used to do a lot of amazing thing.  This tutorial gives a practical example on how to integrate CKEditor and external javascript to build a useful feature - Citation & Footnote

Bookmark and Share

Content

Citation Features

The aim of a Citation/Footnote plugin is to add a citation feature like those you can find in Microsoft Word to CKEditor. An example of a citation is like this ->[1], a numbered superscript refer to an uneditable and pre-formated citation that is added below the CKEditor.  A footnote is similar to a citation, except there is no limitation in the format of a footnote so people can supplement everything they want by adding a footnote.[2] The following diagram will show how the citation I have just added looks like in the CKEditor.

How Citations will look like

How Citations will look like

 

The features of the Citation/Footnote plugin are:

  • The Citation/Footnote is uneditable
  • The citations are automatically updated.  For example, when a new footnote is added between footnote 1 and 2, then the footnote 2 will now become footnote 3. The footnote text should also be added to the Footnote & Citation section as well. Similarly, when a footnote is deleted, the numbering should be correctly updated and the footnote text in Footnote & Citation section should be deleted.
  • The Citation/Footnote numbering is uneditable. Hence you cannot edit footnote 1 into 1 hahaha plugin hacked!
  • Citations can be re-used.  Hence you can add citation[1] again with the same numbering.
  • Citation is pre-formated according to the type of citation they are.  Citation for different citation media will have different input fields.
Citation Dialog Screens

Citation Dialog Screens

Plugin Design Consideration

Such a plugin is not trival and requires ways to overcome several issues, which you may encountered before as well:

Making things uneditable

Probably making a section of document uneditable is the most wanted function for CKEditor.  But this cannot be easily done as nearly all web html WYSIWYG editors make use of <body contentEditable="true"> function available to all browsers, which cannot set a particular section of document uneditable. So, to make things uneditable, we have two options:

1. Use readonly text input fields

If you want a small part of unformated text to be uneditable, then this option will work for you.  The inline superscripts in this plugin is implemented using readonly text inputs. To add a read-only short sentence, simply add a <input type="text" readonly="true" value="your read only text"/> . Definitely you will have to take note of the CSS styles of the input, such as setting its width and remove its border, etc.

2. Move the uneditable part outside the CKEditor

The readonly input only works for unformated stuff.  If you want formated stuff to be uneditable, you have to move it outside CKEditor. I know it is difficult to move middle part of the content outside CKEditor, probably above or below the CKEditor, without confusing the user. Luckily, I want to add the citations below the CKEditor so that will not be a problem at all. Alternatively, you may use two CKEditors separated by an uneditable section in the middle.

However, this pose another trouble: controling the outside environment from CKEditor inside, which will break the encapsulation and reuse of CKEditor. For instance, when you want to update the content of a DOM element outside the CKEditor, you have to encode its CSS Selector inside the CKEditor.  So I make use of events to help CKEditor communicate with outside. When I want to update the Footnote and Citation section outside CKEditor, the plugin will fire a 'refreshNote' event.  To fire an event, use editor.fire('refreshNote'); . Then the javascript outside will listen to the refreshNote event of the ckeditor instance, using ckeditorinstance.on('refreshNote',do_something_function)

Of course such an external section will not automatically included in CKEditor.getData(). So during submission you will want to combine the external section with CKEditor content first. Alternatively, I store the citation data inside the readonly input superscript, and regenerate the citation HTML in server-side.

Detecting DOM deletion/insertion

I want to update the Footnote and Citation section whenever a citation is deleted, inserted or updated. Insertion and update are easy as they are only possible when the OK button of the plugin, so I can fire a refreshNote event when OK button is pressed.  What's hard is when an inline citation number superscript is deleted.

Luckily, I can detect the deletion of DOM element using DOMNodeRemoved event, which is supported by all browsers except our beloved IE family (MS sucks, stop delivering non-standard stuff pls). To combat the IE issue, for IE clients refreshNote function will be fired from time to time so there's no real-time update of citations when a citation is deleted.  To detect whether the client is using IE, use if(CKEDITOR.env.ie)

Source Code

Here I post the full source code for the plugin.  I hope the comments are self-explanatory enough.  If you don't understand, just ask!

plugin.js

CKEDITOR.plugins.add('footnote',
{
init: function(editor)
{
editor.notesUpdate = function(){
setTimeout(editor.refreshNote , 0);
}
editor.refreshNote = function(){
var dupSup = {};
var count=0;
var allsup = editor.document.getElementsByTag('input'); //get all the input elements
for(var i=0;i<allsup.count();i++){
var sup = allsup.getItem(i);
if(sup.hasClass('supNote')){
//if the input element is a footnote/citation, update the current footnote numbers
//so that there's no gap in the numbers
//for example, when footnote 2 is deleted, then footnote 3 become footnote 2
if(sup.getAttribute('supKey') in dupSup){
sup.setAttribute('value',dupSup[sup.getAttribute('supKey')]);
}else{
count++;
var strcount=count+'';
sup.setAttribute('value',strcount);
sup.setStyle('width',strcount.length*8+'px');
dupSup[sup.getAttribute('supKey')]=strcount;
}
}
}
editor.fire('refreshNote'); //fire refreshNote event so that external javascript can listen
};
editor.formatSup = function(element){
function a(s){
return element.getAttribute(s);
}
if(a('suptype')=='footnote')
return a('footnote');
else{ // generate the Citation HTML according to different citation types
var ct = a('citationtype');
var authors = a('authors'),
date = a('date')?(" ("+a('date')+"), "):'',
title = a('url')?("<a style='color:blue' href='"+a('url')+"'>"+a('title')+"</a>"):(a('title')?a('title'):''),
publisher = a('publisher')?a('publisher')+". ":'',
page = a('page'),
doi = a('doi')?('doi:<a style="color:blue" href="http://dx.doi.org/'+a('doi')+'">'+a('doi')+'</a>'):'',
isbn = a('isbn')?('ISBN:' +a('isbn')):'',
volume = a('volume'),
edition = a('edition')?(a('edition')+", "):'',
journal = a('journal')?("<em>"+a('journal')+"</em>"):'',
accessdate = a('accessdate')?("Retrieved "+a('accessdate')):'',
newspaper = a('newspaper')?(a('newspaper')+", "):'',
issue = a('issue');
aspace = (authors&&title&&!date)?', ':'',
arttitle=title?('"'+title+'", '):'';
var result='';
if(ct=='book'){
result = authors+aspace+date+(title?("<span style='font-style:italic'>"+title+"</span>, "):'')+
(volume?(volume+", "):'')+edition+publisher+(page?('pp.'+page+', '):'')+isbn;
}else if(ct=='paper'){
result = authors+aspace+date+ arttitle+ "<span style='font-style:italic'>"+journal+"</span>"+(a('publisher')?(' ('+a('publisher')+') '):'') +
(volume?("<span style='font-weight:bold'>"+volume+"</span>"):'')+(issue?('('+issue+')'):'')+(page?(":"+page+', '):'')+doi+isbn;
}else if(ct=='news'){
result = authors+aspace+date+arttitle+ "<span style='font-style:italic'>"+newspaper+"</span>"+accessdate;
}else if(ct=='website'){
result = authors+aspace+date+(a('title')?arttitle:(a('url')+', '))+publisher+accessdate;
}
return result.replace(/, $/,'.');
}
};
editor.genUsedList = function(limit,value){ //generate a list of all used footnote/citation
var allsup = editor.document.getElementsByTag('input');
var results = {};
for(var i=0;i<allsup.count();i++){
var sup = allsup.getItem(i);
if(sup.hasClass('supNote')){
if(limit!='' && sup.getAttribute('supType')!=limit)
continue;
if(value){
results[sup.getAttribute('value')]=editor.formatSup(sup);
}else{
results[sup.getAttribute('supKey')]=editor.formatSup(sup);
}
}
}
return results;
};
var pluginName = 'footnote';
CKEDITOR.dialog.add(pluginName, this.path + 'dialogs/footnote.js');
editor.addCommand(pluginName, new CKEDITOR.dialogCommand(pluginName));
editor.ui.addButton('Footnote', //toolbat button for footnote
{
label: 'Footnote',
command: pluginName,
});
CKEDITOR.dialog.add('citation',this.path+'dialogs/footnote.js');
editor.addCommand('citation', new CKEDITOR.dialogCommand('citation'));
editor.ui.addButton('Citation', //toolbar button for citation
{
label: 'Citation',
command: 'citation'
});
if(CKEDITOR.env.ie){
//IE have no DOMNodeRemoved event, so we refreshNote periodically to keep Citation and numbers consistent
setInterval(editor.refreshNote , 15000);
}
if(editor.addMenuItems) //Initializing menu items for use in Context Menu
{
editor.addMenuItems(
{
footnote:
{
label: 'Footnote',
command: 'footnote',
group: 'footnote'
},
citation:{
label:'Citation',
command:'citation',
group: 'footnote'
}
});
}
if(editor.contextMenu) //if contextMenu is enabled
{
editor.contextMenu.addListener(function(element, selection) //execute following code when context menu is shown
{
if(element && element.is('input')){
if(element.getAttribute('supType')=='footnote'){
//return footnote menu item with unactivated state
return { footnote: CKEDITOR.TRISTATE_OFF };
}else if(element.getAttribute('supType')=='citation')
//return citation menu item with unactivated state
return { citation: CKEDITOR.TRISTATE_OFF };
}else
return null;
});
}
editor.on('contentDom', function() //when the pre-filled data in CKEditor is ready
{
var doc=editor.document;
var allsup = doc.getElementsByTag('input'); //select all the input elements
for (var i=0;i<allsup.count();i++){
var sup = allsup.getItem(i);
if(sup.hasClass('supNote')){
//if the input element is a footnote/citation, bind editor.notesUpdate to DOMNodeRemoved
sup.on('DOMNodeRemoved', editor.notesUpdate);
}
}
});
}
});

Dialog - footnote.js

( function(){
function dialogShow(){ //initialize dialog value with pre-existing citations & footnotes
this.currentNote = false;
var editor = this.getParentEditor(),
sel = editor.getSelection();
this.currentNote = sel.getSelectedElement(); //currently selected element.
if(this.currentNote && this.currentNote.hasClass('supNote'))
this.setupContent(this.currentNote);
this.previewNote = editor.document.createElement('input');
};

function dialogOK(){ //after pressing OK, update
var editor = this.getParentEditor();
if($('input[name=usedRadio]:radio:checked').length>0){ //if current note is re-used
var val = $('input[name=usedRadio]:radio:checked').val();
var source = new CKEDITOR.dom.element($('.supNote[supKey='+val+']',editor.document.getBody().$).get(0));
elem=source.clone();
editor.insertElement(elem);
this.currentNote = elem;
}else{
if(!this.currentNote){ //new note is added
elem = editor.document.createElement('input'); //set inital values for the input.supNote element
elem.setAttribute('value','1');
elem.setStyle('width','8px');
elem.setAttribute('readonly','true');
elem.setAttribute('type','text');
elem.setAttribute('supType',this.getName());
elem.setAttribute('supKey',Math.floor(Math.random()*10000000));
elem.addClass('supNote');
editor.insertElement(elem);
this.currentNote = elem;
}
this.commitContent(this.currentNote); //execute commit functions for relevant dialogs/inputs
}
this.currentNote.on('DOMNodeRemoved',editor.notesUpdate);
editor.refreshNote();
}
function getCalendarDate() //get current date in english format
{
var months = new Array("January","February","March","April","May","June","July","August","September","October","November","December");
var now = new Date();
var monthnumber = now.getMonth();
var monthname = months[monthnumber];
var monthday = now.getDate();
var year = now.getFullYear();
var dateString = monthday+' '+monthname +' '+year;
return dateString;
}

var used = { //used tab page allow users to add a pre-existing citation/footnote again
//this tab page is used by both citation & footnote elements
id: 'used',
label: 'Used',
title: 'Used',
accessKey: 'U',
elements:[{
type:'html',
html:'<div id="used" style="overflow-y:scroll;width:100%;background:white;height:200px;width:340px"></div>',
onShow: function()
{
//jQuery Here! Sometimes it is a good choice to use jQuery functions instead of CKEditor APIs.
$('#used').html('');
usedlist= this.getDialog().getParentEditor().genUsedList(this.getDialog().getName(),false);
for( var key in usedlist)
$('#used').append('<table style="width:100%;border-bottom:1px solid #CCCCCC;"><tr><td style="width:20px;vertical-align:middle;"><input type="radio" name="usedRadio" value="'+
key+'"/></td><td style="padding:5px; white-space:normal">'+usedlist[key]+'</tr></div>');
}
}]
};
var citationFields = { //fields for different citation formats
'book':['authors', 'title','publisher','date','volume','edition','page','url','isbn'],
'paper':['authors','title','journal','volume','issue','page','publisher','date','url','doi'],
'news': ['authors', 'title', 'newspaper','date','url','accessdate'],
'website':['authors','title','publisher','date','url','accessdate']
};
var allfields = ['authors', 'title','publisher','date','volume','edition','page','url','doi','isbn','journal','issue','newspaper','accessdate'];
var footnoteDialog = function(editor){ //layout for footnote dialog
return {
title : 'Footnote',
minWidth : 300,
minHeight : 340,
onShow: dialogShow,
onOk: dialogOK,
contents: [{
id: 'footnote',
label: 'Footnote',
title: 'Footnote',
accessKey:'F',
elements:[{
type: 'textarea',
id: 'txtFootnote',
label: 'Footnote',
setup: function(element){
this.setValue(element.getAttribute('footnote'));
},
validate: function() //validate before dialog close
{
if($('input[name=usedRadio]:radio:checked').length==0 && this.getValue()==''){
alert('Please enter your footnote or choose a used footnote.');
return false; //return false: validation fails and dialog won't close
}
return true; //validation fails and dialog will close
},
commit: function(element){
element.setAttribute('footnote',this.getValue());
}
}]
},used] //used tab page goes here
}
}
var eOC = function(element){ //executed during commit
if(this.isEnabled()){ //"this" refer to the calling dialog element
element.setAttribute(this.id,this.getValue()); //element refer to the supNote input element
}else{
if(element.hasAttribute(this.id))
element.removeAttribute(this.id)
}
}
var eOS = function(element){ //executed during setup
if(element.hasAttribute(this.id))
this.setValue(element.getAttribute(this.id))
}
var onC = function(){ //executed during onChange
if(this.getDialog().previewNote){
this.getDialog().commitContent(this.getDialog().previewNote);
$('#citationpreview').html(this.getDialog().getParentEditor().formatSup(this.getDialog().previewNote));
}
}
var citationDialog = function(editor){ //citation dialog layout
return {
title: 'Citation',
minWidth: 340,
minHeight : 340,
onShow: dialogShow,
onOk: dialogOK,
contents:[{
id:'citation',
label: 'Citation',
title: 'Citation',
accessKey: 'C',
elements:[{
type: 'select',
id: 'citationType',
label: 'Citation Media Type',
labelLayout:'horizontal',
items:[
["Book","book"],
['Website','website'],
["Journal/Paper/Thesis/Dissertation", 'paper'],
['News Article', 'news'],
],

commit:eOC,
setup:eOS,
onChange: function(){ //when dialog fields change, update the citation format preview
for(var i=0;i<allfields.length;i++){
//enable input fields according to different citation formats
//and hide fields that are not used for a particular citation format
var fields=citationFields[this.getValue()];
var enable=false;
var currField = this.getDialog().getContentElement('citation',allfields[i]);
for(var j=0;j<fields.length;j++){
if(fields[j]==allfields[i]){
currField.enable();
currField.getElement().show();
currField.getElement().getParent().setStyle('padding','5px 0');
enable=true;
break;
}
}
if(!enable){
currField.disable();
currField.getElement().hide();
currField.getElement().getParent().setStyle('padding','0px');
}
}
if(this.getDialog().previewNote){
// console.log(this.getDialog().previewNote);
this.getDialog().previewNote.setAttribute(this.id,this.getValue());
$('#citationpreview').html(this.getDialog().getParentEditor().formatSup(this.getDialog().previewNote));
}
}
},{
type: 'text',
id: 'authors',
label: 'Authors',
commit:eOC,
setup:eOS,
onChange:onC
},{
type: 'text',
id: 'title',
label: 'Title',
commit:eOC,
setup:eOS,
onChange:onC
},{
type: 'text',
id: 'url',
label: 'URL',
commit:eOC,
setup:eOS,
onChange:onC
},{
type: 'text',
id: 'journal',
label: 'Journal',
commit:eOC,
setup:eOS,
onChange:onC
},{
type: 'text',
id: 'publisher',
label: 'Publisher',
commit:eOC,
setup:eOS,
onChange:onC
},{
type:'text',
id:'newspaper',
label:'Newspaper',
commit:eOC,
setup:eOS,
onChange:onC
},{
type: 'text',
id:'date',
label: 'Publication Date or Year',
commit:eOC,
setup:eOS,
onChange:onC
},{
type: 'text',
id: 'volume',
label: 'Volume',
commit:eOC,
setup:eOS,
onChange:onC
},{
type:'text',
id:'issue',
label: 'Issue',
commit:eOC,
setup:eOS,
onChange:onC
},{
type: 'text',
id:'edition',
label: 'Edition',
commit:eOC,
setup:eOS,
onChange:onC
},{
type: 'text',
id:'page',
label: 'Page Number',
commit:eOC,
setup:eOS,
onChange:onC
},{
type: 'text',
id:'doi',
label: 'doi',
commit:eOC,
setup:eOS,
onChange:onC
},{
type: 'text',
id: 'isbn',
label: 'ISBN',
commit:eOC,
setup:eOS,
onChange:onC
},{
type:'text',
id:'accessdate',
label:'Access Date',
commit:eOC,
setup:eOS,
onChange:onC,
onShow:function(){
if(this.getValue()=='')
this.setValue(getCalendarDate());
}
},{
type:'html',
html:'Citation Preview:<br/><div id="citationpreview" style="text-decoration:normal;font-weight:normal;font-style:normal;white-space:normal;width:328px;background-color:white;border:1px solid #000000;padding:5px"></div>',
}]
},used]
}
}


CKEDITOR.dialog.add('footnote', function(editor) {
return footnoteDialog(editor);
});
CKEDITOR.dialog.add('citation', function(editor){
return citationDialog(editor);
});
})();

 

Javascript in the controling page:

var ckeditorinstance;
function refreshNote(ev){
var notes=ckeditorinstance.genUsedList('',true); //get the list of citation/footnote
var noteslength=0;
var out=''
for(var i in notes){
noteslength++;
out+="<li>"+notes[i]+"</li>";
}
if(noteslength>0){
$('#footnote_header').show();
$('#footnotes').html('<ol id="footnotes">'+out+'</ol>');
//append the citation/footnotes to element outside CKEditor
}
}

$(document).ready(function() {

CKEDITOR.on('instanceReady', function(ev){
ckeditorinstance = CKEDITOR.instances.instancename; //replace instancename with the name you use for your CKEditor instance
ckeditorinstance.on('refreshNote', refreshNote); //listen to the refreshNote event and call refreshNote function
});
});

Footnotes and Citations

  1. George Wu"CKEditor Plugin Development",Retrieved 3 August 2010
  2. This is an example of footnote. No predefined format!
12 Comments

Vincent (vwinstead)

Avatar for vwinstead
0
03 Aug 10

This looks great!   I'm going to implement this on my site.  (http://www.guadiy.com)  I have been needing something like this for a while now but I have not had a chance to spend time on it.  Thanks!

-Vincent


Reply

George Wu (georgewu)

Avatar for georgewu
0
05 Aug 10

good that my work is useful to you.

the concept of your site is pretty interesting.  If you want to drive traffic to your site, you may post the how-to articles in Voofie as links.  Tagging them how-to and DIY will be very helpful


Reply

Vincent (vwinstead)

Avatar for vwinstead
0
06 Aug 10

Almost there

Well I got the plugin to show up and work properly on the toolbar.  The dialog appears when you click the button and it inserts the footnote number into the editor instance. 

However, it doesn't seem to show the footnote anywhere on the page after it gets inserted.  Are you supposed to add the elements with the id=footnote and id=footnote_header or does the plugin do that automatically.  I tried adding it and it still didn't work. 

I am using this on a php page.  Do you think that makes any difference?  Also I'm on Firefox v3.6.6.
  It would be nice if you had a working example somewhere so I could do a 'view source' on the page and confirm a couple things.  So - any ideas on how to get this working?

....by the way, do you have any icon files for this?  I used some old ones for now.

Last update: 06 Aug 10


Reply

George Wu (georgewu)

Avatar for georgewu
0
07 Aug 10

Yes, I have add an ol element with id="footnote" below CKEditor.

A working example obviously is this site ~ :) Try it on http://www.voofie.com/save/article/

Please note that the footnote and citation html code are contained in CKEditor data in my design.  The data of citation is stored in the source code for input element instead.  The following is a sample:

<input authors="Someone" citationtype="book" class="supNote" date="" edition="" isbn="" page="" 
publisher="somepublisher" readonly="true" style="width: 8px;" supkey="2770754" suptype="citation"
title="Somebook" type="text" url="http://someadress" value="1" volume="" />

In my design, I use the information stored in the input element to regenerate the HTML code in server side.  An alternative (probably better and simpler) way is to append the HTML from the #footnote element to the CKEditor data using javascript before submission


Reply

Vincent (vwinstead)

Avatar for vwinstead
0
08 Aug 10

I keep playing around with the code but I can't seem to get it working.

I think the success of the plugin depends somehow on how you are setting up the ckeditor instance.  On the page you mentioned this is what the code looks like:

<div>
<label for="id_page_content">Article main body</label>:
<script type="text/javascript" src="/site_media/ckeditor/ckeditor.js"></script>
<textarea name="page_content" id="id_page_content"></textarea>
<script type="text/javascript">
$(function(){
CKEDITOR.replace('page_content', {toolbar:'Default', height:'450',
on:{instanceReady:function(ev){
this.dataProcessor.writer.indentationChars='';
}}
});
});
</script>
</div>
<h2 id='footnote_header' style='display:none'>Footnote and Citations</h2>
<div id='footnotes'></div>

1. Why is the ckeditor.js script re-referenced every time a new instance is created?
2.  What is this for?

               on:{instanceReady:function(ev){
this.dataProcessor.writer.indentationChars='';
}}
});

3.  Does it make any difference if I'm not calling CKEDITOR.replace like this?

$(function(){

I've never seen that method before.  I just call it straight with CKEDITOR.replace

4.  You said to use ol elements, but this one is using a div element for the footnotes.  Does it matter?

5.  Are you sure there's nothing that has been left out that is taking place behind the scenes?  Is there some javascript that you haven't mentioned on your description?


Reply

Vincent (vwinstead)

Avatar for vwinstead
0
08 Aug 10

Version info

I also noticed that the ckeditor instance on that example site is using version 3.0.1 but I'm trying to integrate this plugin onto version 3.3.1

It's possible that the changes since that version are significant enough to break some things.  What do you think?


Reply

Vincent (vwinstead)

Avatar for vwinstead
0
08 Aug 10

Found the problem

I found the issue.  The code presented on this page needs some tweaking.  There are some missing pieces.  There's nothing wrong with the presented plugin code, but the code added to the page doesn't get the correct results. 

Here's what I got working:
1. Added variable definitions at the top
2. changed "ckeditorinstance" to "contentfck" so that "contentfck.on" works correctly.
3.  the ".instancename" didn't seem to be referencing anything so I had to change this to the id of the textarea element (id_page_content).  This might be obvious to some, but not me - I was assuming it just worked as-is.

Here's the "controlling page" code I used:

var sbar;
var form_dirty=false;
var contentfck,summaryfck;
var draft_id;

function refreshNote(ev){
var notes=contentfck.genUsedList('',true); //get the list of citation/footnote
var noteslength=0;
var out=''
for(var i in notes){
noteslength++;
out+="<li>"+notes[i]+"</li>";
}
if(noteslength>0){
$('#footnote_header').show();
$('#footnotes').html('<ol id="footnotes">'+out+'</ol>');
//append the citation/footnotes to element outside CKEditor
}
}

$(document).ready(function() {

CKEDITOR.on('instanceReady', function(ev){
contentfck=CKEDITOR.instances.id_page_content;
contentfck.on('refreshNote',refreshNote); //listen to the refreshNote event and call refreshNote function
});
});

Reply

George Wu (georgewu)

Avatar for georgewu
0
08 Aug 10

Thank you vwinstead for pointing out my mistake in the code. I tried to make the code look more general by masking out my own variables, but I have made a mistake on that.  I will correct the code in the article and include more comments.  Thank you again!


Reply

Vincent (vwinstead)

Avatar for vwinstead
0
08 Sep 10

A couple more pointers

You might want to mention some of the basics here such as the css.  In order to add the css for the footnote and citation superscripts to show up properly I pasted the css code inside the ckeditor/contents.css file.  This file controls the content inside the text section of the editor instance.  You need to add a class called "supNote".

There's also a way to configure ckeditor to use a custom css file, but I couldn't quite get that working so I didn't waste time on it.

Also, add the 16x16 icon gif images to the plugin contents.  Sorry, but I didn't find your icons to be very descriptive so I made my own :)  A footprint for the footnotes and an open book for citations (see below).

plugin icons

In order to get the code to use your custom icons you put the gif files into the same folder as plugin.js (ckeditor/plugins/footnote).  Inside plugin.js you need to alter the code to use the new icons like this:

        editor.ui.addButton('Footnote', //toolbat button for footnote
            {
                label: 'Footnote',
                command: pluginName,
                icon : this.path + 'icon1.gif'
            });

Now I have to work on storing the html code for displaying the footnotes ... or like you said, just re-rendering it at each page load from the content.

Last update: 08 Sep 10


Reply

Vincent (vwinstead)

Avatar for vwinstead
0
09 Sep 10

All done

OK - I have fully integrated the plugin now.  I made a few little changes to the code in order to make it work with my site (multiple instances on a page).  I also now have the html code inserted right into the input element as one of its attributes.  It works out pretty good!

You can check out the resulting footnotes on section 1 of this page:
http://www.getupanddiy.com/projects/bronze-sculpting-lost-wax-casting/index.php

Try editing the first section and you'll see that the footnotes show up correctly for only that section that you're editing.  On the project page, footnotes are all added together from all the sections and put at the bottom of the page.

Thanks for the code!
 


Reply

jordannoret

Avatar for jordannoret
0
21 Apr 11

Context Menu

I have the plugin working so far, but when I right click on a footnote in CKEditor, both the "Footnote" and "Text Field Properties" menus show up in the context menu.  Is there a way to turn off the context menu for the input DOM object?


Reply

eon

Avatar for eon
0
05 Oct 11

Can't create a footnote next to an existing footnote

If your cursor is next to a footnote and you use the context menu or toolbar button to try to create another footnote (footnotes are often multiples), you can't. Instead you'll either edit the one already there because of how CKEditor selects ranges or, if you have a context menu item that only does "create", it overwrites the existing footnote rather than creating another to the right. 

Is there any way around this?


Reply

Please login to post comment.

What is Voofie?

Voofie organizes knowledge, discovers useful resources and recognizes knowledgable users.

Bookmark your blog in Voofie to get more traffic as well as building a reputation in your field!

Explore more about it. Become a member—our FREE Registration takes just seconds.

Page Info
59Impacts
4.8/2 rates
6154
Your Rating:
Version: 5
Last update: 08 Aug 10
History Permalink