最近忙于開發(fā)工作流,想起之前開發(fā)的OA ,缺少一個重要的功能:表單設計器。因為我們的OA是基于Sharepoint開發(fā)的,如果沒有表單設計器,定義一個列表的界面需要開發(fā)一個feature,或則需要VS開發(fā)一個aspx頁面。這事一個很麻煩的事情。所以考慮實現(xiàn)一個表單設計器。
于是我到網(wǎng)上找HTML 編輯器,找到好幾個,分別有CKEditor,TinyMCE,還有一個基于JQuery的一個編輯器XHEditor。這幾個編輯器我就不做比較了。我這里選擇使用CKEditor。既然要做表單設計器,我們的需要擴展這HTML編輯器,CKEditor提供了方便可擴展的插件體系,我們可以很方便的自定義一些自己的插件。這只介紹概述CKEditor插件開發(fā)。
首先我們到http://ckeditor.com/download下載CKEditor,這里我使用的是CKEditor 3.6。解壓后目錄如下:
CKEditor的源碼存放在_source目錄下面,根目錄下面的ckeditor.js是經(jīng)過壓縮的代碼。Dom元素操作,事件處理,初始化腳本和其他一些環(huán)境設置都在ckeditor\_souce\core目錄下面。其他功能,如格式化,復制粘貼,圖片和鏈接都是以插件的形式實現(xiàn)的,存放在ckeditor\_source\plugins文件夾下面,每一個文件夾為一個插件。每個文件夾下面都有一個plugin.js的腳本文件。
為了減少HTML 請求數(shù)量,CKEditor壓縮并打包成ckeditor.js 和ckeditor_basic.js。默認運行壓縮后的ckeditor。在開發(fā)過程中,如果你想運行未壓縮的源碼,則把ckeditor.js替換成ckeditor_source.js就可以了。
我們以Hello World插件為例子。呵呵。在plugins目錄下面新建一個HelloWorld文件夾,并在下面建立一個plugin.js文件。
要CKEditor能夠調用我們開發(fā)的插件,我們需要在CKEditor注冊我們開發(fā)的插件。打開根目錄下面的config.js。設置CKEDITOR.editorConfig屬性
config.extraPlugins = 'HelloWorld';
完整的代碼如下:
CKEDITOR.editorConfig = function( config )
{
// Define changes to default configuration here. For example:
// config.language = 'fr';
// config.uiColor = '#AADC6E';
config.extraPlugins = 'HelloWorld';
};
這樣CKEditor會從Plugin文件夾找HelloWorld文件夾下面的plugin.js,并加載插件。
我們需要在CKEditor的工具欄上加入HelloWorld的按鈕。單擊按鈕出發(fā)一個命令。命令可以觸發(fā)一個事件,或調用方法。我們通過CKEDITOR.plugins.add方法來添加插件。
CKEDITOR.plugins.add('HelloWorld', {
init: function (editor) {
var pluginName = 'HelloWorld';
CKEDITOR.dialog.add(pluginName, this.path + 'dialogs/HelloWorld.js');
editor.addCommand(pluginName, new CKEDITOR.dialogCommand(pluginName));
editor.ui.addButton(pluginName,
{
label: 'Hello',
command: pluginName
});
}
});
上面代碼中,我們添加了一個HelloWorld的按鈕,和HelloWorld的命令。
通過方法editor.ui.addButton添加一個按鈕,這個方法有兩個參數(shù)。一個是按鈕的名字,另外一個是按鈕的定義。
定義有以下幾個屬性:
label:當鼠標移動到按鈕上面是提示此文本信息。
className:樣式名,默認是'cke_button_' + command
click:按鈕的單擊事件出發(fā)的方法。如果沒有實現(xiàn)單擊事件,則執(zhí)行指定key的命令。
command:按鈕單擊默認執(zhí)行的命令。
下面是按鈕的部分源碼。
CKEDITOR.ui.button = function( definition )
{
// Copy all definition properties to this object.
CKEDITOR.tools.extend( this, definition,
// Set defaults.
{
title : definition.label,
className : definition.className || ( definition.command && 'cke_button_' + definition.command ) || '',
click : definition.click || function( editor )
{
editor.execCommand( definition.command );
}
});
this._ = {};
};
editor表示一個編輯器的實例。通過調用其addCommand(commandName, commandDefinition) 方法,來添加命令。我們實例化了一個CKEDITOR.dialogCommand,此命令繼承至CKEDITOR.commandDefinition,該命令執(zhí)行時打開一個特定的對話框。我們現(xiàn)在把這個按鈕加入到ToolBar里,修改Config.js。
CKEDITOR.editorConfig = function (config) {
// Define changes to default configuration here. For example:
// config.language = 'fr';
// config.uiColor = '#AADC6E';
config.toolbar =
[
{ name: 'document', items: ['Source', '-', 'Save', 'NewPage', 'DocProps', 'Preview', 'Print', '-', 'Templates'] },
{ name: 'clipboard', items: ['Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Undo', 'Redo'] },
{ name: 'editing', items: ['Find', 'Replace', '-', 'SelectAll', '-', 'SpellChecker', 'Scayt'] },
{ name: 'forms', items: ['Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton', 'HiddenField'] },
'/',
{ name: 'basicstyles', items: ['Bold', 'Italic', 'Underline', 'Strike', 'Subscript', 'Superscript', '-', 'RemoveFormat'] },
{ name: 'paragraph', items: ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'Blockquote', 'CreateDiv', '-', 'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock', '-', 'BidiLtr', 'BidiRtl'] },
{ name: 'links', items: ['Link', 'Unlink', 'Anchor'] },
{ name: 'insert', items: ['Image', 'Flash', 'Table', 'HorizontalRule', 'Smiley', 'SpecialChar', 'PageBreak', 'Iframe'] },
'/',
{ name: 'styles', items: ['Styles', 'Format', 'Font', 'FontSize'] },
{ name: 'colors', items: ['TextColor', 'BGColor'] },
{ name: 'tools', items: ['Maximize', 'ShowBlocks', '-', 'About'] },
'/',
{ name: 'extent', items: ['HelloWorld'] }
];
config.extraPlugins += (config.extraPlugins ? ',HelloWorld' : 'HelloWorld');
};
注釋:’/’表示換行,’-‘標識分隔符 。
config.toolbar的默認值是Full。Full的菜單有哪些呢?打開ckeditor\_source\plugins\toolbar\plugin.js查看toolbar_Full的定義。
CKEDITOR.config.toolbar_Full =
[
{ name: 'document', items : [ 'Source','-','Save','NewPage','DocProps','Preview','Print','-','Templates' ] },
{ name: 'clipboard', items : [ 'Cut','Copy','Paste','PasteText','PasteFromWord','-','Undo','Redo' ] },
{ name: 'editing', items : [ 'Find','Replace','-','SelectAll','-','SpellChecker', 'Scayt' ] },
{ name: 'forms', items : [ 'Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton', 'HiddenField' ] },
'/',
{ name: 'basicstyles', items : [ 'Bold','Italic','Underline','Strike','Subscript','Superscript','-','RemoveFormat' ] },
{ name: 'paragraph', items : [ 'NumberedList','BulletedList','-','Outdent','Indent','-','Blockquote','CreateDiv','-','JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock','-','BidiLtr','BidiRtl' ] },
{ name: 'links', items : [ 'Link','Unlink','Anchor' ] },
{ name: 'insert', items : [ 'Image','Flash','Table','HorizontalRule','Smiley','SpecialChar','PageBreak','Iframe' ] },
'/',
{ name: 'styles', items : [ 'Styles','Format','Font','FontSize' ] },
{ name: 'colors', items : [ 'TextColor','BGColor' ] },
{ name: 'tools', items : [ 'Maximize', 'ShowBlocks','-','About' ] }
];
那么我們可以模仿來定義Mine的ToolBar。再次編輯config.js。
CKEDITOR.editorConfig = function (config) {
// Define changes to default configuration here. For example:
// config.language = 'fr';
// config.uiColor = '#AADC6E';
config.toolbar_Mine =
[
{ name: 'document', items: ['Source', '-', 'Save', 'NewPage', 'DocProps', 'Preview', 'Print', '-', 'Templates'] },
{ name: 'clipboard', items: ['Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Undo', 'Redo'] },
{ name: 'editing', items: ['Find', 'Replace', '-', 'SelectAll', '-', 'SpellChecker', 'Scayt'] },
{ name: 'forms', items: ['Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton', 'HiddenField'] },
'/',
{ name: 'basicstyles', items: ['Bold', 'Italic', 'Underline', 'Strike', 'Subscript', 'Superscript', '-', 'RemoveFormat'] },
{ name: 'paragraph', items: ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'Blockquote', 'CreateDiv', '-', 'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock', '-', 'BidiLtr', 'BidiRtl'] },
{ name: 'links', items: ['Link', 'Unlink', 'Anchor'] },
{ name: 'insert', items: ['Image', 'Flash', 'Table', 'HorizontalRule', 'Smiley', 'SpecialChar', 'PageBreak', 'Iframe'] },
'/',
{ name: 'styles', items: ['Styles', 'Format', 'Font', 'FontSize'] },
{ name: 'colors', items: ['TextColor', 'BGColor'] },
{ name: 'tools', items: ['Maximize', 'ShowBlocks', '-', 'About'] },
'/',
{ name: 'extent', items: ['HelloWorld'] }
];
config.toolbar = 'Mine';
config.extraPlugins += (config.extraPlugins ? ',HelloWorld' : 'HelloWorld');
};
CKEDITOR.plugins.add('HelloWorld', {
init: function (editor) {
var pluginName = 'HelloWorld';
CKEDITOR.dialog.add(pluginName, this.path + 'dialogs/HelloWorld.js');
editor.addCommand(pluginName, new CKEDITOR.dialogCommand(pluginName));
editor.ui.addButton(pluginName,
{
label: 'Hello',
command: pluginName,
icon: this.path + 'images/hello.png'
});
}
});
Dialog是開發(fā)插件的關鍵,在前面我們使用CKEDITOR.dialog.add方法添加了一個對話框。 有兩個參數(shù)。一個是對話框名,一個對話框定義。
然后我們還添加了一個dialog命令。這個命令會打開我們的dialog。
editor.addCommand(pluginName, new CKEDITOR.dialogCommand(pluginName));
我們的對話框定義放在ckeditor\_source\plugins\HelloWorld\dialogs\HelloWorld.js。
(function () {
function HelloWorldDialog(editor) {
return {
title: '對誰說Hello',
minWidth: 300,
minHeight: 80,
buttons: [{
type: 'button',
id: 'someButtonID',
label: 'Button',
onClick: function () {
alert('Custom Button');
}
},
CKEDITOR.dialog.okButton,
CKEDITOR.dialog.cancelButton],
contents:
[
{
id: 'info',
label: '名字',
title: '名字',
elements:
[
{
id: 'text',
type: 'text',
style: 'width: 50%;',
label: '名字',
'default': '',
required: true,
validate: CKEDITOR.dialog.validate.notEmpty('名字不能為空'),
commit: function () {
var text = ‘Hello ’+this.getValue();
alert(text);
}
}
]
}
],
onLoad: function () {
alert('onLoad');
},
onShow: function () {
alert('onShow');
},
onHide: function () {
alert('onHide');
},
onOk: function () {
this.commitContent();
},
onCancel: function () {
alert('onCancel');
},
resizable: CKEDITOR.DIALOG_RESIZE_HEIGHT
};
}
CKEDITOR.dialog.add('HelloWorld', function (editor) {
return HelloWorldDialog(editor);
});
})();
{
type: 'button',
id: 'someButtonID',
label: 'Button',
onClick: function () {
alert('Custom Button');
}
}
commit: function () {
var text = ‘Hello ’+this.getValue();
alert(text);
}
這里我們調用CKEDITOR.ui.dialog.uiElement的getValue方法來獲取到名字文本框的值。這里只是alert,稍后在改進怎么把值加入到設計器里面。
還有一個是setup方法,則由CKEDITOR.dialog.definition.setupContent方法調用執(zhí)行。
type:有以下幾個值。text, password, textarea, checkbox, button, select, file, fileButton, html
labelLayout:'horizontal' 或則'vertical'。
on*: 定義事件方法。事件可以是Dom事件,例如onChange,onClick,也可以是onShow,onHide,onLoad。
validate:驗證用戶的輸入。例如:驗證值不能為空。validate : CKEDITOR.dialog.validate.notEmpty(ErrorMessage)。當單擊btnOk按鈕,如果值為空,則會彈出ErrorMessage信息來提示。你還可以用其他的驗證方法來驗證輸入:
onLoad,onShow,onHide,onOk,onCancel這幾個方法看字面意思就知道干嘛的了。就不多介紹了。我們這里當onOk,就是確定按鈕單擊的時候,執(zhí)行了commitContent方法,從而執(zhí)行了CKEDITOR.dialog.definition.uiElement.commit方法。
resizable:一個枚舉。用于修改窗體的大小,是否可以修改高度,寬度,還是兩者都可以。有以下幾個定義。默認值是CKEDITOR.DIALOG_RESIZE_NONE。
CKEDITOR.DIALOG_RESIZE_NONE
CKEDITOR.DIALOG_RESIZE_WIDTH
CKEDITOR.DIALOG_RESIZE_HEIGHT
CKEDITOR.DIALOG_RESIZE_BOTH
那現(xiàn)在在來運行看看效果:
單擊確定按鈕則彈出Hello 某某某。
當然我們不想這樣Alert。我們希望能夠把Hello 某某某寫到編輯器里面。
這里我們用到了CKEDITOR.dom.element對象。這對象里面包含了很多對編輯器DOM操作的方法。如我們要創(chuàng)建一個span元素。var element = new CKEDITOR.dom.element('span', editor.document);。我們需要設置值則調用setText方法等。
那我們修改剛剛做好的dialot。
(function () {
function HelloWorldDialog(editor) {
return {
title: '對誰說Hello',
minWidth: 300,
minHeight: 80,
buttons: [
CKEDITOR.dialog.okButton,
CKEDITOR.dialog.cancelButton],
contents:
[
{
id: 'info',
label: '名字',
title: '名字',
elements:
[
{
id: 'text',
type: 'text',
style: 'width: 50%;',
label: '名字',
'default': '',
required: true,
validate: CKEDITOR.dialog.validate.notEmpty('名字不能為空'),
commit: function (editor) {
var text = 'Hello '+this.getValue();
var element = new CKEDITOR.dom.element('span', editor.document);
element.setText(text);
editor.insertElement(element);
}
}
]
}
],
onOk: function () {
this.commitContent(editor);
},
resizable: CKEDITOR.DIALOG_RESIZE_HEIGHT
};
}
CKEDITOR.dialog.add('HelloWorld', function (editor) {
return HelloWorldDialog(editor);
});
})();
參考資料:
http://docs.cksource.com/ckeditor_api/index.html
http://www.cnblogs.com/moozi/archive/2010/01/06/1640034.html
http://www.cnblogs.com/xiangyan168/archive/2011/05/19/2050991.html
http://ajithmanmadhan.wordpress.com/2009/12/16/customizing-ckeditor-and-adding-a-new-toolbar-button/