/ Work

[JS tech.] CKeditor 專注本業之 Dialog 超棘手 part 4

寫到這裡,我想說一句公道話,那就是,我之前寫的三篇都可以直接扔進垃圾桶,是一般垃圾不用回收了(翻桌)。編輯器最重要的一個部分,就是自訂的對話框(Dialog),編輯器預設的對話框,跟 FCKeditor 有些許的差異,像是上傳檔案的對話框,在 CKeditor 裡面就沒有在預設工具列內(主要是要提倡跟自家的 CKFinder 合併使用)。

首先呢,在 CKeditor 裡面,所有的對話框都是使用 JavaScript 來產生,所以在設定上並不像以前 FCKeditor 那樣改改他的原始的 html 檔案就能更動對話框,所以關於這一點,對於 JavaScript 不是很熟悉的人恐怕會遇到很多地雷。但是,我看了看還是覺得,這次在 CKeditor 裡面,還是必須要稍微看過原始碼,才能真的有效控制整個 Dialog 的狀況啊(攤手)。

我們繼續來看 Dialog 與 UI,這兩個部分其實是互相綁定的,所以如果把 UI 的部分拿掉,Dialog 可能會半殘。從原始碼看來,UI 有絕大部分是給 Dialog 使用的(CKEDITOR.ui.dialog),所以如果把 UI 的 Core 拿掉的話,那可想而知會發生什麼事情,而 CKEDITOR.dialog 系列的類別,其實是用來做設定(dialogDefinition)的,跟產生對話框並沒有直接的關係。

接著,我們先來看看設定的部分在做些甚麼事情。

  • CKEDITOR.dialog
    用來產生與修改對話框,前提是必須要有已經設定(註冊)好的對話框按鈕元件。
  • CKEDITOR.dialog.add
    加入(註冊)一個對話框,對話框的對象必須是已定義好的元件(Object)。
  • CKEDITOR.dialog.addIframe, CKEDITOR.dialog.addUIElement
    歐雷~歐雷~歐雷,目前還無法使用 Iframe,而 UIElement 在 builder 有莫名奇妙的雷。
  • CKEDITOR.dialog.exists
    檢查對話框是否已存在(已註冊)。

只有一個?那其他的呢?其他的 Class(buttonDefinition, contentDefinition, contentDefinitionObject, dialogDefinition, dialogDefinitionObject, uiElementDefinition, dialogCommand)並不是能夠直接呼叫的 API,而是要在設定中取得元件之後所能夠使用的方法,具體的使用方法依照官方範例如下:

	CKEDITOR.on( 'dialogDefinition', function( evt )
	{
	    var definition = evt.data.definition;
	    var content = definition.getContents( 'page1' );
	    ...
	} );

至於大家很關心的 Uploader 與 FileBrowser 的部分,官方的 簡單說明是這樣的(真的非常簡單)。雖然他也提供了 另外一種說明,但是他依舊非常的 CKEDITOR,令人完全看不懂啊!所以說,檔案上傳到底怎麼辦呢?這裡暫時先不提,等到我說完如何加入新的按鈕之後再補上。首先,我們來看看如何在 Toolbar 裡面加入按鈕,底下是一個典型的 CKEDITOR 編輯器的 Toolbar 設定:

	var editor = CKEDITOR.replace( 'editor1', {
	                toolbar: [
	                    ['Source','-','Save','NewPage','Preview','-','Templates'],
	                    ['Cut','Copy','Paste','PasteText','PasteFromWord','-','Print', 'SpellChecker', 'Scayt'],
	                    ['Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat'],
	                    ['Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton', 'HiddenField'],
	                    '/',
	                    ['Bold','Italic','Underline','Strike','-','Subscript','Superscript'],
	                    ['NumberedList','BulletedList','-','Outdent','Indent','Blockquote'],
	                    ['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'],
	                    ['Link','Unlink','Anchor'],
	                    ['Image','Flash','Table','HorizontalRule','Smiley','SpecialChar','PageBreak'],
	                    '/',
	                    ['Styles','Format','Font','FontSize'],
	                    ['TextColor','BGColor'],
	                    ['Maximize', 'ShowBlocks','-','About','MyButton']
	                ]
	            });


	// 必須要等待編輯器將 pluginsLoaded 做完才可以繼續做 Dialog 設置與綁定動作。
	editor.on( 'pluginsLoaded', function( ev )
	{
	    // 檢查 Dialog 是否已經存在,不存在才要建立
	    if ( !CKEDITOR.dialog.exists( 'myDialog' ) )
	    {
	        // 註冊一個新的 Dialog,名稱就叫做 'myDialog'
	        CKEDITOR.dialog.add( 'myDialog', function( editor )
	        {
	        // 這裡所返回的,就是 dialogDefinition 的設定。
	            return {
	                title : 'My Dialog',
	                minWidth : 400,
	                minHeight : 200,
	                contents : [
	                    {
	                    // 這裡是定義 Dialog 的內容,所使用到的都是那些 UI 與 Dialog 的 Definition。
	                    // 裡面的東西有複雜到,待會我會直接寫出檔案上傳的範例。
	                    }
	                ]
	            };
	        } );
	    }
	    // 建立一個新的編輯器命令,並將這個編輯器命令綁定在剛剛建立好的 myDialog 上面。
	    editor.addCommand( 'myDialogCmd', new CKEDITOR.dialogCommand( 'myDialog' ) );
	    // 綁定剛剛 Toolbar 所設定的 'MyButton' 並且將命令執行綁定在剛剛的編輯器命令上。
	    editor.ui.addButton( 'MyButton',
	        {
	            label : 'My Dialog',
	            command : 'myDialogCmd'
	        } );
	});


	id : 'Upload',
	hidden : true,
	filebrowser : 'uploadButton',
	label : editor.lang.image.upload,
	// 在 Dialog 中註冊元件
	elements :
	    [
	        {
	            type : 'file',
	            id : 'upload',
	            label : editor.lang.image.btnUpload,
	            style: 'height:40px',
	            size : 38
	        },
	        {
	            type : 'fileButton',
	            id : 'uploadButton',
	            filebrowser : 'info:txtUrl',
	            label : editor.lang.image.btnUpload,
	            'for' : [ 'Upload', 'upload' ]
	        }
	    ]

那檔案會上傳到哪裡呢?好問題!檔案上傳的資料要在 Toolbar 設定完之後另外設定,官方有說明,其實也就是這樣而已:

	var editor = CKEDITOR.replace( 'editor1', {
	                toolbar: [
	                    [...略...],'MyButton']
	                ],
	                filebrowserUploadUrl : '/uploader/upload.php'
	            });

/uploader/upload.php?CKEditor=editor1&CKEditorFuncNum=1&langCode=en

而這個上傳所要用到的監聽很特殊,需要用 CKEDITOR.tools.callFunction 呼叫並取得返回值,而這個 API 的說明是""!根據我明查暗訪的結果,這個 callFunction 還必須要先 addFunction,而 addFunction 需要有一個依照 CKEDITOR 所建立的元素(instance),然後這一整串流程在 API 中的說明是""!

所以,CKEDITOR 的 Dialog 到底是改得更順手,還是更麻煩了呢?各位看倌你覺得呢(茶),其他的 Dialog 跟 UI 我會繼續在找時間補上,畢竟這兩個區塊才是我們真正關心的部分啊!