在開始以前,感性的認知一下CKEditor源碼的組織形式是頗有用的. CKEditor固有的一些文件被組織到ckeditor\_source目錄裏. 核心的功能,諸如DOM元素操做,事件處理,初始化腳本和一些環境設置被包含在ckeditor\_source\core文件夾內. 而其它的一些功能, 好比格式化,拷貝和粘貼, 圖片和連接, 都被實現爲插件形式放在ckeditor\_source\plugins文件夾內. 每一個文件夾表示一個插件. 而且在每一個文件夾內, 有一個plugin.js的文件包含了該插件須要用到的代碼. css
你能夠看到源代碼被組織成不一樣的文件. 爲了減小HTTP請求, CKEditor把不一樣的文件壓縮並打包到ckeditor.js和ckeditor_basic.js裏, 這種方式是運行編輯器的默認方式. 在開發的過程當中, 你會但願經過ckedtior_source.js來代替ckeditor.js的執行. html
如今, 建立ckeditor\_source\plugins\footnote目錄,並在目錄裏建立plugin.js文件. 程序員
爲了開始開發你的插件 , 你須要首先註冊你的插件,這樣 CKEditor 才能載入它 . 在 ckeditor\_source\core\config.js 裏,修改CKEDITOR.config的屬性extraPlugins爲 'footnote'; ajax
此配置將會告訴編輯器在footnote目錄下載入plugin.js. 基本的plugin.js結構以下: 數組
CKEDITOR.plugins.add('footnote',
{
init: function(editor)
{
//plugin code goes here
}
}); 異步
CKEDITOR.plugins.add('footnote',
{
init: function(editor)
{
var pluginName = 'footnote';
CKEDITOR.dialog.add(pluginName, this.path + 'dialogs/footnote.js');
editor.addCommand(pluginName, new CKEDITOR.dialogCommand(pluginName));
editor.ui.addButton('Footnote',
{
label: 'Footnote or Citation',
command: pluginName
});
}
}); async
editor.ui.addButton函數聲明包含了兩個參數, 按鈕名字和按鈕定義. 在按鈕定義中可能的屬性還包含: 編輯器
· label: 當鼠標位於按鈕之上時所出現的文字提示 ide
· className: 按鈕的css類名. 默認爲: ‘cke_button_’ + 命令名稱 函數
· click: 當點擊按鈕後所調用的函數. 默認爲: 執行由 命令鍵值 指定的命令.
command: 將在按鈕點擊以後執行的命令
最簡單的增長按鈕圖片的辦法是利用屬性icon. 代碼以下:
CKEDITOR.plugins.add('myplugin',
{
init: function(editor)
{
//plugin code goes here
editor.ui.addButton('myplugin',
{
label: 'myplugin',
command: FCKCommand_myplugin,
icon: CKEDITOR.plugins.getPath('myplugin') + 'myplugin.png'
});
}
});
CKEditor以命令的方式提供了大部分的功能. 命令和函數之間的不一樣是 命令是有「ON」, 「OFF「和未啓用狀態的.
定義一個命令
editor.addCommand函數聲明瞭兩個參數, 命令名字和命令的對象定義. 可能的命令定義的屬性包含:
· exec: 最小形式, 定義的對象擁有一個exec方法, 該方法會在命令執行時執行.
· modes: 命令能被執行的模式. 默認爲: {wysiwyg:1} 有效的模式是: wysiwyg, source
· editorFocus: 在執行命令時,是否給予編輯器焦點. 默認: true
· state: 命令的狀態. 默認: CKEDITOR.TRISTATE_OFF
· canUndo: 該命令是否會和redo/undo系統掛鉤
· async: 在異步功能,例如ajax中被用到. 在執行完命令後, afterCommandExec事件將不會被自動觸發. 它將指望程序員可以手工觸發該事件.
執行命令
觸發命令是很簡單的, 經過 editor.execCommand(commandName);
命令狀態
命令有三個狀態: ON, OFF和DISABLED. 狀態可以經過setState進行設置,例如 editor.getCommand(commandName).setState(state);
狀態值將會是: CKEDITOR.TRISTATE_ON, CKEDITOR.TRISTATE_OFF, CKEDITOR.TRISTATE_DISABLED 中的一個.
當設置爲未啓用的時候, 按鈕將會表現爲灰色, 而且經過execCommand執行的命令將無效. 當設置爲on的時候, 按鈕就會被高亮. 當命令的狀態改變的時候, 命令將會觸發一個’state’事件.
對話框是一些插件的核心. 爲了使用該對話框, 他們必須首先在plugin.js中註冊, 包括定義, 經過調用CKEDITOR.dialog.add以下:
CKEDITOR.dialog.add(pluginName, this.path + 'dialogs/pluginName.js');
以後,若是你想經過點擊一個按鈕觸發這個對話框, 你能夠經過使用CKEDITOR.dialogCommand來簡單的完成這項工做.
editor.addCommand(pluginName, new CKEDITOR.dialogCommand(pluginName));
對話框定義
按約定,有關對話框的代碼將會被放到dialogs/.js中. 和按鈕和命令相似, 對話框也是經過定義一些屬性和方法來定義的. 下面的代碼展現了一個標準的.js的模板:
( function(){
var exampleDialog = function(editor){
return {
title : /* title in string*/,
minWidth : /*number of pixels*/,
minHeight : /*number of pixels*/,
buttons: /*array of button definitions*/,
onOk: /*function*/ ,
onLoad: /*function*/,
onShow: /*function*/,
onHide: /*function*/,
onCancel: /*function*/,
resizable: /* none,width,height or both */,
contents: /*content definition, basically the UI of the dialog*/
}
}
CKEDITOR.dialog.add('insertHTML', function(editor) {
return exampleDialog(editor);
});
})();
定義裏有如下一些屬性/方法:
該屬性定義了對話框底部可用的按鈕. 它是一個 CKEDITOR.dialog.buttonDefinition對象的數組. 默認的值是 [ CKEDITOR.dialog.okButton, CKEDITOR.dialog.cancelButton ]. 爲了增長你本身的按鈕, 你需要寫下按鈕的定義,並把它加到該數組裏. 例如:
buttons:[{
type:'button',
id:'someButtonID', /* note: this is not the CSS ID attribute! */
label: 'Button',
onClick: function(){
//action on clicking the button
}
},CKEDITOR.dialog.okButton, CKEDITOR.dialog.cancelButton],
效果以下:
onOK, onLoad, onShow, onHide, onCancel 事件在不一樣的狀況下會被觸發. onLoad在對話框第一次打開的時候被觸發, 能夠用來針對該對話框作一些初始化. onShow在對話框顯示的時候被觸發. onLoad和onShow的不一樣之處在於onLoad將只觸發一次,而無論對話框被打開或關掉多少次, 而同時onShow將在對話框每次顯示的時候被觸發. 這是由於在按下ok或cancel的時候,對話框將被隱藏(觸發onHide),而不是徹底的卸載. onOk和onCancel將在ok或cancel按鈕被按下的時候觸發.
該屬性用來描述對話框是否能夠被調整大小. 可能的值包含: CKEDITOR.DIALOG_RESIZE_NONE, CKEDITOR.DIALOG_RESIZE_WIDTH, CKEDITOR.DIALOG_RESIZE_HEIGHT and CKEDITOR.DIALOG_RESIZE_BOTH. Default is CKEDITOR.DIALOG_RESIZE_NONE
對話框的用戶接口是對話框屬性定義的內容. 對話框的UI部分會首先被組織爲幾個頁面, 每一個頁面表示了對話框的一個tab頁. 在每一個頁面中, 輸入元素(按鈕,文本輸入,文本區域, 單選, 下拉, 多選和文件)都將被輔助性的結構元素(vbox,hbox和label)組織起來. 經過寫這些UI元素的定義, 你將不需要寫任何html代碼而完成對這些UI的行爲和外表的定義. 固然,替代使用這些所提供的對象來構建用戶界面的方法是依然選擇用原始的HTML代碼去實現html的UI元素。
內容域是一個contentDefinition的數組, 每一個定義表明了一個對話框的tab頁. 下面是一個模板:
contents: [{
id: 'page1', /* not CSS ID attribute! */
label: 'Page1',
accessKey: 'P',
elements:[ /*elements */]
}, {
id:'page2',
label:'Page2',
accessKey: 'Q',
elements:[/*elements*/]
}]
結果以下:
在定義了tab頁面以後, 你能夠經過傳遞一個uiElements的數組做爲contentDefinition的元素來定義每一個tab頁的UI. 那裏右兩種類型的uiElements, 結構型的和輸入型的. 結構型元素(vbox和hbox)使用表格來放置元素的位置. 結構型的元素能夠鑲嵌從而造成複雜的結構. 當輸入的uiElements擁有一些比較重要的特徵的時候這會使得他們比DOM輸入元素更有用.
elements:[{
type : 'hbox',
widths : [ '100px', '100px', '100px' ],
children :
[{
type:'html',
html:'<div>Cell1</div>',
},{
type:'html',
html:'<div>Cell2</div>',
},{
type: 'vbox',
children:[{
type:'html',
html:'<div>Cell3</div>',
},{
type:'html',
html:'<div>Cell4</div>'
}]
}]
輸入型元素有不少的屬性和方法可用. 更多的細節請參考uiElement文檔, 同時這裏也有一些比較重要的:
· id:(必須的) 注意這不是css的id屬性, 可是是被CKEditor所使用的標識符. CKEditor將會自動設置css id屬性但和這個id無關. 爲了獲得此對象所表示的元素, 你可使用getContentElement. 例如, 若是你想獲得uiElement內部的某個uiElement, 你可使用:
this.getDialog().getContentElement(pageIdName,elementIdName) //pageIdName is the ID for page containing the element
· type:(必須的) 是如下的其中一個: text, password, textarea, checkbox, button, select, file, fileButton, html
· label: 和輸入元素同時顯示的文本標籤
· labelLayout: ‘horizontal’橫向 或 ‘vertical’ 縱向. 當設置爲縱向的時候,標籤會在元素的頂部.
· on* events: 函數或事件. 第一個字符必須大寫. 事件能夠是DOM事件,例如onChange, onClick等,就像onShow, onHide 和onLoad, 這些在對話框被激活的時候調用的事件同樣.
· validate: 函數用來驗證輸入元素的值. 例如爲了驗證值不爲空, 你可使用: validate : CKEDITOR.dialog.validate.notEmpty(ErrorMessage)
若是域是空的,那麼錯誤消息將在你點擊ok按鈕的時候彈出.
內置的驗證器包含:
· regex(regex, msg)
· notEmpty(msg)
· integer(msg) //regex: /^\d*$/
· number(msg) //regex: /^\d*(? :\.\d+)?$/
· equals(value, msg)
· notEqual(value, msg)
· setup: 函數會在父對話框的setupContent方法被調用時執行. 可以被用來初始化一些域的值. 例如當編輯一個文檔中已存圖片域的時候, 你可能但願初始化該圖片的寬度. 這裏有個例子:
onShow : function(){ //onShow function for the dialog definition
//... other code ...
var elem= this.getParentEditor().getSelection().getSelectedElement(); //get the current selected element
this.setupContent(elem); //this function will call all the setup function for all uiElements
//... other code...
},
contents: [{
id: 'InfoTab',
label: 'Info Tab',
elements:[
{ //input element for the width
type: 'text',
id: 'widthInput',
label: 'Width',
labelLayout: 'horizontal',
setup: function(element){
this.setValue(element.getAttribute('width'));
}
}]
}]
· commit: 函數在父對話框的commitContent方法被調用的時候執行. 例如, 它能在ok按鈕被按下時變動圖片的大小.
上下文菜單是你在CKEditor中右鍵所彈出的菜單. 就像CKEditor的其它特徵同樣,它也很容易被定製. 下面的代碼展現瞭如何增長一個「Code「菜單項的過程:
if(editor.addMenuItems)
{
editor.addMenuItems( //have to add menu item first
{
insertCode: //name of the menu item
{
label: 'Code',
command: 'insertCode',
group: 'insertCode' //have to be added in config
}
});
}
if(editor.contextMenu)
{
editor.contextMenu.addListener(function(element, selection) //function to be run when context menu is displayed
{
if(! element || !element.is('pre'))
return null;
return { insertCode: CKEDITOR.TRISTATE_OFF };
});
}
前述代碼在上下文菜單中增長了額外的insertCode元素. 當右擊在非‘pre’元素上時, insertCode菜單將不會顯示. 當右擊在‘pre’元素上時insertCode菜單將會顯示OFF狀態.
除了上面的代碼, 你依然須要把insertCode組放到config中,這樣才能排列菜單顯示的順序. 如下是須要在config.js中增長的代碼:
config.menu_groups = 'clipboard,form,tablecell,tablecellproperties,tablerow,tablecolumn,table,anchor,link,image,flash,checkbox,radio,textfield,hiddenfield,imagebutton,button,select,textarea,insertCode';
你會看到insertCode插入在了menu_groups配置的最後面. 最終的結果是:
寫完全部的代碼以後, 就需要把你的代碼產品化. 就像前面提到的那樣, 原始的代碼包含了不少文件,都會被壓縮成一些個文件從而下降HTTP請求. 有必要把你的代碼和這些文件合併。
打包這些文件其實是由ckeditor.pack控制的,你添加了文件須要修改ckeditor根目錄下的ckeditor.pack文件. 把你的源代碼文件放到‘ckeditor.js’的列表中, 將會把你的文件添加進去.
而事實上的打包,你需要額外的兩個文件ckpackager.exe和ckpackager.jar. 在你下載完這兩個文件後,在你的ckeditor根下運行下面的命令:
ckpackager.exe ckeditor.pack
如今你的源碼文件將會自動被打包並拷貝到ckeditor的根目錄下. 能夠開始享用你的插件了!