Index: openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/a11yhelp/plugin.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/a11yhelp/Attic/plugin.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/a11yhelp/plugin.js 13 Feb 2012 18:53:09 -0000 1.1 @@ -0,0 +1,47 @@ +/* +Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +/** + * @fileOverview Plugin definition for the a11yhelp, which provides a dialog + * with accessibility related help. + */ + +(function() +{ + var pluginName = 'a11yhelp', + commandName = 'a11yHelp'; + + CKEDITOR.plugins.add( pluginName, + { + // List of available localizations. + availableLangs : { en:1, he:1 }, + + init : function( editor ) + { + var plugin = this; + editor.addCommand( commandName, + { + exec : function() + { + var langCode = editor.langCode; + langCode = plugin.availableLangs[ langCode ] ? langCode : 'en'; + + CKEDITOR.scriptLoader.load( + CKEDITOR.getUrl( plugin.path + 'lang/' + langCode + '.js' ), + function() + { + CKEDITOR.tools.extend( editor.lang, plugin.langEntries[ langCode ] ); + editor.openDialog( commandName ); + }); + }, + modes : { wysiwyg:1, source:1 }, + readOnly : 1, + canUndo : false + }); + + CKEDITOR.dialog.add( commandName, this.path + 'dialogs/a11yhelp.js' ); + } + }); +})(); Index: openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/a11yhelp/dialogs/a11yhelp.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/a11yhelp/dialogs/Attic/a11yhelp.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/a11yhelp/dialogs/a11yhelp.js 13 Feb 2012 18:53:09 -0000 1.1 @@ -0,0 +1,222 @@ +/* +Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +CKEDITOR.dialog.add( 'a11yHelp', function( editor ) +{ + var lang = editor.lang.accessibilityHelp, + id = CKEDITOR.tools.getNextId(); + + // CharCode <-> KeyChar. + var keyMap = + { + 8 : "BACKSPACE", + 9 : "TAB" , + 13 : "ENTER" , + 16 : "SHIFT" , + 17 : "CTRL" , + 18 : "ALT" , + 19 : "PAUSE" , + 20 : "CAPSLOCK" , + 27 : "ESCAPE" , + 33 : "PAGE UP" , + 34 : "PAGE DOWN" , + 35 : "END" , + 36 : "HOME" , + 37 : "LEFT ARROW" , + 38 : "UP ARROW" , + 39 : "RIGHT ARROW" , + 40 : "DOWN ARROW" , + 45 : "INSERT" , + 46 : "DELETE" , + 91 : "LEFT WINDOW KEY" , + 92 : "RIGHT WINDOW KEY" , + 93 : "SELECT KEY" , + 96 : "NUMPAD 0" , + 97 : "NUMPAD 1" , + 98 : "NUMPAD 2" , + 99 : "NUMPAD 3" , + 100 : "NUMPAD 4" , + 101 : "NUMPAD 5" , + 102 : "NUMPAD 6" , + 103 : "NUMPAD 7" , + 104 : "NUMPAD 8" , + 105 : "NUMPAD 9" , + 106 : "MULTIPLY" , + 107 : "ADD" , + 109 : "SUBTRACT" , + 110 : "DECIMAL POINT" , + 111 : "DIVIDE" , + 112 : "F1" , + 113 : "F2" , + 114 : "F3" , + 115 : "F4" , + 116 : "F5" , + 117 : "F6" , + 118 : "F7" , + 119 : "F8" , + 120 : "F9" , + 121 : "F10" , + 122 : "F11" , + 123 : "F12" , + 144 : "NUM LOCK" , + 145 : "SCROLL LOCK" , + 186 : "SEMI-COLON" , + 187 : "EQUAL SIGN" , + 188 : "COMMA" , + 189 : "DASH" , + 190 : "PERIOD" , + 191 : "FORWARD SLASH" , + 192 : "GRAVE ACCENT" , + 219 : "OPEN BRACKET" , + 220 : "BACK SLASH" , + 221 : "CLOSE BRAKET" , + 222 : "SINGLE QUOTE" + }; + + // Modifier keys override. + keyMap[ CKEDITOR.ALT ] = 'ALT'; + keyMap[ CKEDITOR.SHIFT ] = 'SHIFT'; + keyMap[ CKEDITOR.CTRL ] = 'CTRL'; + + // Sort in desc. + var modifiers = [ CKEDITOR.ALT, CKEDITOR.SHIFT, CKEDITOR.CTRL ]; + + function representKeyStroke( keystroke ) + { + var quotient, + modifier, + presentation = []; + + for ( var i = 0; i < modifiers.length; i++ ) + { + modifier = modifiers[ i ]; + quotient = keystroke / modifiers[ i ]; + if ( quotient > 1 && quotient <= 2 ) + { + keystroke -= modifier; + presentation.push( keyMap[ modifier ] ); + } + } + + presentation.push( keyMap[ keystroke ] + || String.fromCharCode( keystroke ) ); + + return presentation.join( '+' ); + } + + var variablesPattern = /\$\{(.*?)\}/g; + function replaceVariables( match, name ) + { + var keystrokes = editor.config.keystrokes, + definition, + length = keystrokes.length; + + for ( var i = 0; i < length; i++ ) + { + definition = keystrokes[ i ]; + if ( definition[ 1 ] == name ) + break; + } + return representKeyStroke( definition[ 0 ] ); + } + + // Create the help list directly from lang file entries. + function buildHelpContents() + { + var pageTpl = '
%1
' + + '' + lang.contents + ' ', + sectionTpl = '

%1

%2
', + itemTpl = '
%1
%2
'; + + var pageHtml = [], + sections = lang.legend, + sectionLength = sections.length; + + for ( var i = 0; i < sectionLength; i++ ) + { + var section = sections[ i ], + sectionHtml = [], + items = section.items, + itemsLength = items.length; + + for ( var j = 0; j < itemsLength; j++ ) + { + var item = items[ j ], + itemHtml; + itemHtml = itemTpl.replace( '%1', item.name ). + replace( '%2', item.legend.replace( variablesPattern, replaceVariables ) ); + sectionHtml.push( itemHtml ); + } + + pageHtml.push( sectionTpl.replace( '%1', section.name ).replace( '%2', sectionHtml.join( '' ) ) ); + } + + return pageTpl.replace( '%1', pageHtml.join( '' ) ); + } + + return { + title : lang.title, + minWidth : 600, + minHeight : 400, + contents : [ + { + id : 'info', + label : editor.lang.common.generalTab, + expand : true, + elements : + [ + { + type : 'html', + id : 'legends', + style : 'white-space:normal;', + focus : function() {}, + html : buildHelpContents() + + '' + } + ] + } + ], + buttons : [ CKEDITOR.dialog.cancelButton ] + }; +}); Index: openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/a11yhelp/lang/en.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/a11yhelp/lang/Attic/en.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/a11yhelp/lang/en.js 13 Feb 2012 18:53:10 -0000 1.1 @@ -0,0 +1,108 @@ +/* +Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +CKEDITOR.plugins.setLang( 'a11yhelp', 'en', +{ + accessibilityHelp : + { + title : 'Accessibility Instructions', + contents : 'Help Contents. To close this dialog press ESC.', + legend : + [ + { + name : 'General', + items : + [ + { + name : 'Editor Toolbar', + legend: + 'Press ${toolbarFocus} to navigate to the toolbar. ' + + 'Move to the next and previous toolbar group with TAB and SHIFT-TAB. ' + + 'Move to the next and previous toolbar button with RIGHT ARROW or LEFT ARROW. ' + + 'Press SPACE or ENTER to activate the toolbar button.' + }, + + { + name : 'Editor Dialog', + legend : + 'Inside a dialog, press TAB to navigate to next dialog field, press SHIFT + TAB to move to previous field, press ENTER to submit dialog, press ESC to cancel dialog. ' + + 'For dialogs that have multiple tab pages, press ALT + F10 to navigate to tab-list. ' + + 'Then move to next tab with TAB OR RIGTH ARROW. ' + + 'Move to previous tab with SHIFT + TAB or LEFT ARROW. ' + + 'Press SPACE or ENTER to select the tab page.' + }, + + { + name : 'Editor Context Menu', + legend : + 'Press ${contextMenu} or APPLICATION KEY to open context-menu. ' + + 'Then move to next menu option with TAB or DOWN ARROW. ' + + 'Move to previous option with SHIFT+TAB or UP ARROW. ' + + 'Press SPACE or ENTER to select the menu option. ' + + 'Open sub-menu of current option wtih SPACE or ENTER or RIGHT ARROW. ' + + 'Go back to parent menu item with ESC or LEFT ARROW. ' + + 'Close context menu with ESC.' + }, + + { + name : 'Editor List Box', + legend : + 'Inside a list-box, move to next list item with TAB OR DOWN ARROW. ' + + 'Move to previous list item with SHIFT + TAB or UP ARROW. ' + + 'Press SPACE or ENTER to select the list option. ' + + 'Press ESC to close the list-box.' + }, + + { + name : 'Editor Element Path Bar', + legend : + 'Press ${elementsPathFocus} to navigate to the elements path bar. ' + + 'Move to next element button with TAB or RIGHT ARROW. ' + + 'Move to previous button with SHIFT+TAB or LEFT ARROW. ' + + 'Press SPACE or ENTER to select the element in editor.' + } + ] + }, + { + name : 'Commands', + items : + [ + { + name : ' Undo command', + legend : 'Press ${undo}' + }, + { + name : ' Redo command', + legend : 'Press ${redo}' + }, + { + name : ' Bold command', + legend : 'Press ${bold}' + }, + { + name : ' Italic command', + legend : 'Press ${italic}' + }, + { + name : ' Underline command', + legend : 'Press ${underline}' + }, + { + name : ' Link command', + legend : 'Press ${link}' + }, + { + name : ' Toolbar Collapse command', + legend : 'Press ${toolbarCollapse}' + }, + { + name : ' Accessibility Help', + legend : 'Press ${a11yHelp}' + } + ] + } + ] + } +}); Index: openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/a11yhelp/lang/he.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/a11yhelp/lang/Attic/he.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/a11yhelp/lang/he.js 13 Feb 2012 18:53:10 -0000 1.1 @@ -0,0 +1,216 @@ +/* +Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +CKEDITOR.plugins.setLang( 'a11yhelp', 'he', +{ + accessibilityHelp : + { + title : 'הוראות נגישות', + contents : 'הוראות נגישות. לסגירה לחץ אסקייפ (ESC).', + legend : + [ + { + name : 'כללי', + items : + [ + { + name : 'סרגל הכלים', + legend: + 'לחץ על ${toolbarFocus} כדי לנווט לסרגל הכלים. ' + + 'עבור לכפתור הבא עם מקש הטאב (TAB) או חץ שמאלי. ' + + 'עבור לכפתור הקודם עם מקש השיפט (SHIFT) + טאב (TAB) או חץ ימני. ' + + 'לחץ רווח או אנטר (ENTER) כדי להפעיל את הכפתור הנבחר.' + }, + + { + name : 'דיאלוגים (חלונות תשאול)', + legend : + 'בתוך דיאלוג, לחץ טאב (TAB) כדי לנווט לשדה הבא, לחץ שיפט (SHIFT) + טאב (TAB) כדי לנווט לשדה הקודם, לחץ אנטר (ENTER) כדי לשלוח את הדיאלוג, לחץ אסקייפ (ESC) כדי לבטל. ' + + 'בתוך דיאלוגים בעלי מספר טאבים (לשוניות), לחץ אלט (ALT) + F10 כדי לנווט לשורת הטאבים. ' + + 'נווט לטאב הבא עם טאב (TAB) או חץ שמאלי. ' + + 'עבור לטאב הקודם עם שיפט (SHIFT) + טאב (TAB) או חץ שמאלי. ' + + 'לחץ רווח או אנטר (ENTER) כדי להיכנס לטאב.' + }, + + { + name : 'תפריט ההקשר (Context Menu)', + legend : + 'לחץ ${contextMenu} או APPLICATION KEYכדי לפתוח את תפריט ההקשר. ' + + 'עבור לאפשרות הבאה עם טאב (TAB) או חץ למטה. ' + + 'עבור לאפשרות הקודמת עם שיפט (SHIFT) + טאב (TAB) או חץ למעלה. ' + + 'לחץ רווח או אנטר (ENTER) כדי לבחור את האפשרות. ' + + 'פתח את תת התפריט (Sub-menu) של האפשרות הנוכחית עם רווח או אנטר (ENTER) או חץ שמאלי. ' + + 'חזור לתפריט האב עם אסקייפ (ESC) או חץ שמאלי. ' + + 'סגור את תפריט ההקשר עם אסקייפ (ESC).' + }, + + { + name : 'תפריטים צפים (List boxes)', + legend : + 'בתוך תפריט צף, עבור לפריט הבא עם טאב (TAB) או חץ למטה. ' + + 'עבור לתפריט הקודם עם שיפט (SHIFT) + טאב (TAB) or חץ עליון. ' + + 'Press SPACE or ENTER to select the list option. ' + + 'Press ESC to close the list-box.' + }, + + { + name : 'עץ אלמנטים (Elements Path)', + legend : + 'לחץ ${elementsPathFocus} כדי לנווט לעץ האלמנטים. ' + + 'עבור לפריט הבא עם טאב (TAB) או חץ ימני. ' + + 'עבור לפריט הקודם עם שיפט (SHIFT) + טאב (TAB) או חץ שמאלי. ' + + 'לחץ רווח או אנטר (ENTER) כדי לבחור את האלמנט בעורך.' + } + ] + }, + { + name : 'פקודות', + items : + [ + { + name : ' ביטול צעד אחרון', + legend : 'לחץ ${undo}' + }, + { + name : ' חזרה על צעד אחרון', + legend : 'לחץ ${redo}' + }, + { + name : ' הדגשה', + legend : 'לחץ ${bold}' + }, + { + name : ' הטייה', + legend : 'לחץ ${italic}' + }, + { + name : ' הוספת קו תחתון', + legend : 'לחץ ${underline}' + }, + { + name : ' הוספת לינק', + legend : 'לחץ ${link}' + }, + { + name : ' כיווץ סרגל הכלים', + legend : 'לחץ ${toolbarCollapse}' + }, + { + name : ' הוראות נגישות', + legend : 'לחץ ${a11yHelp}' + } + ] + } + ] + } +}); +/* +Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +CKEDITOR.plugins.setLang( 'a11yhelp', 'he', +{ + accessibilityHelp : + { + title : 'הוראות נגישות', + contents : 'הוראות נגישות. לסגירה לחץ אסקייפ (ESC).', + legend : + [ + { + name : 'כללי', + items : + [ + { + name : 'סרגל הכלים', + legend: + 'לחץ על ${toolbarFocus} כדי לנווט לסרגל הכלים. ' + + 'עבור לכפתור הבא עם מקש הטאב (TAB) או חץ שמאלי. ' + + 'עבור לכפתור הקודם עם מקש השיפט (SHIFT) + טאב (TAB) או חץ ימני. ' + + 'לחץ רווח או אנטר (ENTER) כדי להפעיל את הכפתור הנבחר.' + }, + + { + name : 'דיאלוגים (חלונות תשאול)', + legend : + 'בתוך דיאלוג, לחץ טאב (TAB) כדי לנווט לשדה הבא, לחץ שיפט (SHIFT) + טאב (TAB) כדי לנווט לשדה הקודם, לחץ אנטר (ENTER) כדי לשלוח את הדיאלוג, לחץ אסקייפ (ESC) כדי לבטל. ' + + 'בתוך דיאלוגים בעלי מספר טאבים (לשוניות), לחץ אלט (ALT) + F10 כדי לנווט לשורת הטאבים. ' + + 'נווט לטאב הבא עם טאב (TAB) או חץ שמאלי. ' + + 'עבור לטאב הקודם עם שיפט (SHIFT) + טאב (TAB) או חץ שמאלי. ' + + 'לחץ רווח או אנטר (ENTER) כדי להיכנס לטאב.' + }, + + { + name : 'תפריט ההקשר (Context Menu)', + legend : + 'לחץ ${contextMenu} או APPLICATION KEYכדי לפתוח את תפריט ההקשר. ' + + 'עבור לאפשרות הבאה עם טאב (TAB) או חץ למטה. ' + + 'עבור לאפשרות הקודמת עם שיפט (SHIFT) + טאב (TAB) או חץ למעלה. ' + + 'לחץ רווח או אנטר (ENTER) כדי לבחור את האפשרות. ' + + 'פתח את תת התפריט (Sub-menu) של האפשרות הנוכחית עם רווח או אנטר (ENTER) או חץ שמאלי. ' + + 'חזור לתפריט האב עם אסקייפ (ESC) או חץ שמאלי. ' + + 'סגור את תפריט ההקשר עם אסקייפ (ESC).' + }, + + { + name : 'תפריטים צפים (List boxes)', + legend : + 'בתוך תפריט צף, עבור לפריט הבא עם טאב (TAB) או חץ למטה. ' + + 'עבור לתפריט הקודם עם שיפט (SHIFT) + טאב (TAB) or חץ עליון. ' + + 'Press SPACE or ENTER to select the list option. ' + + 'Press ESC to close the list-box.' + }, + + { + name : 'עץ אלמנטים (Elements Path)', + legend : + 'לחץ ${elementsPathFocus} כדי לנווט לעץ האלמנטים. ' + + 'עבור לפריט הבא עם טאב (TAB) או חץ ימני. ' + + 'עבור לפריט הקודם עם שיפט (SHIFT) + טאב (TAB) או חץ שמאלי. ' + + 'לחץ רווח או אנטר (ENTER) כדי לבחור את האלמנט בעורך.' + } + ] + }, + { + name : 'פקודות', + items : + [ + { + name : ' ביטול צעד אחרון', + legend : 'לחץ ${undo}' + }, + { + name : ' חזרה על צעד אחרון', + legend : 'לחץ ${redo}' + }, + { + name : ' הדגשה', + legend : 'לחץ ${bold}' + }, + { + name : ' הטייה', + legend : 'לחץ ${italic}' + }, + { + name : ' הוספת קו תחתון', + legend : 'לחץ ${underline}' + }, + { + name : ' הוספת לינק', + legend : 'לחץ ${link}' + }, + { + name : ' כיווץ סרגל הכלים', + legend : 'לחץ ${toolbarCollapse}' + }, + { + name : ' הוראות נגישות', + legend : 'לחץ ${a11yHelp}' + } + ] + } + ] + } +}); Index: openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/about/plugin.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/about/Attic/plugin.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/about/plugin.js 13 Feb 2012 18:53:10 -0000 1.1 @@ -0,0 +1,24 @@ +/* +Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +CKEDITOR.plugins.add( 'about', +{ + requires : [ 'dialog' ], + init : function( editor ) + { + var command = editor.addCommand( 'about', new CKEDITOR.dialogCommand( 'about' ) ); + command.modes = { wysiwyg:1, source:1 }; + command.canUndo = false; + command.readOnly = 1; + + editor.ui.addButton( 'About', + { + label : editor.lang.about.title, + command : 'about' + }); + + CKEDITOR.dialog.add( 'about', this.path + 'dialogs/about.js' ); + } +}); Index: openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/about/dialogs/about.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/about/dialogs/Attic/about.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/about/dialogs/about.js 13 Feb 2012 18:53:10 -0000 1.1 @@ -0,0 +1,76 @@ +/* +Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +CKEDITOR.dialog.add( 'about', function( editor ) +{ + var lang = editor.lang.about; + + return { + title : CKEDITOR.env.ie ? lang.dlgTitle : lang.title, + minWidth : 390, + minHeight : 230, + contents : [ + { + id : 'tab1', + label : '', + title : '', + expand : true, + padding : 0, + elements : + [ + { + type : 'html', + html : + '' + + '
' + + '' + + '

' + + 'CKEditor ' + CKEDITOR.version + ' (revision ' + CKEDITOR.revision + ')
' + + 'http://ckeditor.com' + + '

' + + '

' + + lang.help.replace( '$1', '' + lang.userGuide + '' ) + + '

' + + '

' + + lang.moreInfo + '
' + + 'http://ckeditor.com/license' + + '

' + + '

' + + lang.copy.replace( '$1', 'CKSource - Frederico Knabben' ) + + '

' + + '
' + } + ] + } + ], + buttons : [ CKEDITOR.dialog.cancelButton ] + }; +} ); Index: openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/about/dialogs/logo_ckeditor.png =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/about/dialogs/Attic/logo_ckeditor.png,v diff -u Binary files differ Index: openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/adobeair/plugin.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/adobeair/Attic/plugin.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/adobeair/plugin.js 13 Feb 2012 18:53:10 -0000 1.1 @@ -0,0 +1,228 @@ +/* +Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +(function() +{ + var eventNameList = [ 'click', 'keydown', 'mousedown', 'keypress', 'mouseover', 'mouseout' ]; + + // Inline event callbacks assigned via innerHTML/outerHTML, such as + // onclick/onmouseover, are ignored in AIR. + // Use DOM2 event listeners to substitue inline handlers instead. + function convertInlineHandlers( container ) + { + // TODO: document.querySelectorAll is not supported in AIR. + var children = container.getElementsByTag( '*' ), + count = children.count(), + child; + + for ( var i = 0; i < count; i++ ) + { + child = children.getItem( i ); + + (function( node ) + { + for ( var j = 0; j < eventNameList.length; j++ ) + { + (function( eventName ) + { + var inlineEventHandler = node.getAttribute( 'on' + eventName ); + if ( node.hasAttribute( 'on' + eventName ) ) + { + node.removeAttribute( 'on' + eventName ); + node.on( eventName, function( evt ) + { + var callFunc = /(return\s*)?CKEDITOR\.tools\.callFunction\(([^)]+)\)/.exec( inlineEventHandler ), + hasReturn = callFunc && callFunc[ 1 ], + callFuncArgs = callFunc && callFunc[ 2 ].split( ',' ), + preventDefault = /return false;/.test( inlineEventHandler ); + + if ( callFuncArgs ) + { + var nums = callFuncArgs.length, + argName; + + for ( var i = 0; i < nums; i++ ) + { + // Trim spaces around param. + callFuncArgs[ i ] = argName = CKEDITOR.tools.trim( callFuncArgs[ i ] ); + + // String form param. + var strPattern = argName.match( /^(["'])([^"']*?)\1$/ ); + if ( strPattern ) + { + callFuncArgs[ i ] = strPattern[ 2 ]; + continue; + } + + // Integer form param. + if ( argName.match( /\d+/ ) ) + { + callFuncArgs[ i ] = parseInt( argName, 10 ); + continue; + } + + // Speical variables. + switch( argName ) + { + case 'this' : + callFuncArgs[ i ] = node.$; + break; + case 'event' : + callFuncArgs[ i ] = evt.data.$; + break; + case 'null' : + callFuncArgs [ i ] = null; + break; + } + } + + var retval = CKEDITOR.tools.callFunction.apply( window, callFuncArgs ); + if ( hasReturn && retval === false ) + preventDefault = 1; + } + + if ( preventDefault ) + evt.data.preventDefault(); + }); + } + })( eventNameList[ j ] ); + } + })( child ); + } + } + + CKEDITOR.plugins.add( 'adobeair', + { + init : function( editor ) + { + if ( !CKEDITOR.env.air ) + return; + + // Body doesn't get default margin on AIR. + editor.addCss( 'body { padding: 8px }' ); + + editor.on( 'uiReady', function() + { + convertInlineHandlers( editor.container ); + + if ( editor.sharedSpaces ) + { + for ( var space in editor.sharedSpaces ) + convertInlineHandlers( editor.sharedSpaces[ space ] ); + } + + editor.on( 'elementsPathUpdate', function( evt ) { convertInlineHandlers( evt.data.space ); } ); + }); + + editor.on( 'contentDom', function() + { + // Hyperlinks are enabled in editable documents in Adobe + // AIR. Prevent their click behavior. + editor.document.on( 'click', function( ev ) + { + ev.data.preventDefault( true ); + }); + }); + } + }); + + CKEDITOR.ui.on( 'ready', function( evt ) + { + var ui = evt.data; + // richcombo, panelbutton and menu + if ( ui._.panel ) + { + var panel = ui._.panel._.panel, + holder; + + ( function() + { + // Adding dom event listeners off-line are not supported in AIR, + // waiting for panel iframe loaded. + if ( !panel.isLoaded ) + { + setTimeout( arguments.callee, 30 ); + return; + } + holder = panel._.holder; + convertInlineHandlers( holder ); + })(); + } + else if ( ui instanceof CKEDITOR.dialog ) + convertInlineHandlers( ui._.element ); + }); +})(); + +CKEDITOR.dom.document.prototype.write = CKEDITOR.tools.override( CKEDITOR.dom.document.prototype.write, + function( original_write ) + { + function appendElement( parent, tagName, fullTag, text ) + { + var node = parent.append( tagName ), + attrs = CKEDITOR.htmlParser.fragment.fromHtml( fullTag ).children[ 0 ].attributes; + attrs && node.setAttributes( attrs ); + text && node.append( parent.getDocument().createText( text ) ); + } + + return function( html, mode ) + { + // document.write() or document.writeln() fail silently after + // the page load event in Adobe AIR. + // DOM manipulation could be used instead. + if ( this.getBody() ) + { + // We're taking the below extra work only because innerHTML + // on element doesn't work as expected. + var doc = this, + head = this.getHead(); + + // Create style nodes for inline css. ( ' + + '' + + ''; + + var src = + CKEDITOR.env.air ? + 'javascript:void(0)' : + isCustomDomain ? + 'javascript:void((function(){' + + 'document.open();' + + 'document.domain=\'' + document.domain + '\';' + + 'document.close();' + + '})())"' + : + ''; + + var iframe = CKEDITOR.dom.element.createFromHtml( + '' ); + + iframe.on( 'load', function( e ) + { + e.removeListener(); + + var doc = iframe.getFrameDocument(); + doc.write( htmlToLoad ); + + if ( CKEDITOR.env.air ) + onPasteFrameLoad.call( this, doc.getWindow().$ ); + }, dialog ); + + iframe.setCustomData( 'dialog', dialog ); + + var container = this.getElement(); + container.setHtml( '' ); + container.append( iframe ); + + // IE need a redirect on focus to make + // the cursor blinking inside iframe. (#5461) + if ( CKEDITOR.env.ie ) + { + var focusGrabber = CKEDITOR.dom.element.createFromHtml( '' ); + focusGrabber.on( 'focus', function() + { + iframe.$.contentWindow.focus(); + }); + container.append( focusGrabber ); + + // Override focus handler on field. + this.focus = function() + { + focusGrabber.focus(); + this.fire( 'focus' ); + }; + } + + this.getInputElement = function(){ return iframe; }; + + // Force container to scale in IE. + if ( CKEDITOR.env.ie ) + { + container.setStyle( 'display', 'block' ); + container.setStyle( 'height', ( iframe.$.offsetHeight + 2 ) + 'px' ); + } + }, + commit : function( data ) + { + var container = this.getElement(), + editor = this.getDialog().getParentEditor(), + body = this.getInputElement().getFrameDocument().getBody(), + bogus = body.getBogus(), + html; + bogus && bogus.remove(); + + // Saving the contents so changes until paste is complete will not take place (#7500) + html = body.getHtml(); + + setTimeout( function(){ + editor.fire( 'paste', { 'html' : html } ); + }, 0 ); + } + } + ] + } + ] + }; +}); Index: openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/colorbutton/plugin.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/colorbutton/Attic/plugin.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/colorbutton/plugin.js 13 Feb 2012 18:53:11 -0000 1.1 @@ -0,0 +1,301 @@ +/* +Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +/** + * @fileOverview The "colorbutton" plugin that makes it possible to assign + * text and background colors to editor contents. + * + */ +CKEDITOR.plugins.add( 'colorbutton', +{ + requires : [ 'panelbutton', 'floatpanel', 'styles' ], + + init : function( editor ) + { + var config = editor.config, + lang = editor.lang.colorButton; + + var clickFn; + + if ( !CKEDITOR.env.hc ) + { + addButton( 'TextColor', 'fore', lang.textColorTitle ); + addButton( 'BGColor', 'back', lang.bgColorTitle ); + } + + function addButton( name, type, title ) + { + var colorBoxId = CKEDITOR.tools.getNextId() + '_colorBox'; + editor.ui.add( name, CKEDITOR.UI_PANELBUTTON, + { + label : title, + title : title, + className : 'cke_button_' + name.toLowerCase(), + modes : { wysiwyg : 1 }, + + panel : + { + css : editor.skin.editor.css, + attributes : { role : 'listbox', 'aria-label' : lang.panelTitle } + }, + + onBlock : function( panel, block ) + { + block.autoSize = true; + block.element.addClass( 'cke_colorblock' ); + block.element.setHtml( renderColors( panel, type, colorBoxId ) ); + // The block should not have scrollbars (#5933, #6056) + block.element.getDocument().getBody().setStyle( 'overflow', 'hidden' ); + + CKEDITOR.ui.fire( 'ready', this ); + + var keys = block.keys; + var rtl = editor.lang.dir == 'rtl'; + keys[ rtl ? 37 : 39 ] = 'next'; // ARROW-RIGHT + keys[ 40 ] = 'next'; // ARROW-DOWN + keys[ 9 ] = 'next'; // TAB + keys[ rtl ? 39 : 37 ] = 'prev'; // ARROW-LEFT + keys[ 38 ] = 'prev'; // ARROW-UP + keys[ CKEDITOR.SHIFT + 9 ] = 'prev'; // SHIFT + TAB + keys[ 32 ] = 'click'; // SPACE + }, + + // The automatic colorbox should represent the real color (#6010) + onOpen : function() + { + var selection = editor.getSelection(), + block = selection && selection.getStartElement(), + path = new CKEDITOR.dom.elementPath( block ), + color; + + // Find the closest block element. + block = path.block || path.blockLimit || editor.document.getBody(); + + // The background color might be transparent. In that case, look up the color in the DOM tree. + do + { + color = block && block.getComputedStyle( type == 'back' ? 'background-color' : 'color' ) || 'transparent'; + } + while ( type == 'back' && color == 'transparent' && block && ( block = block.getParent() ) ); + + // The box should never be transparent. + if ( !color || color == 'transparent' ) + color = '#ffffff'; + + this._.panel._.iframe.getFrameDocument().getById( colorBoxId ).setStyle( 'background-color', color ); + } + }); + } + + + function renderColors( panel, type, colorBoxId ) + { + var output = [], + colors = config.colorButton_colors.split( ',' ), + total = colors.length + ( config.colorButton_enableMore ? 2 : 1 ); + + var clickFn = CKEDITOR.tools.addFunction( function( color, type ) + { + if ( color == '?' ) + { + var applyColorStyle = arguments.callee; + function onColorDialogClose( evt ) + { + this.removeListener( 'ok', onColorDialogClose ); + this.removeListener( 'cancel', onColorDialogClose ); + + evt.name == 'ok' && applyColorStyle( this.getContentElement( 'picker', 'selectedColor' ).getValue(), type ); + } + + editor.openDialog( 'colordialog', function() + { + this.on( 'ok', onColorDialogClose ); + this.on( 'cancel', onColorDialogClose ); + } ); + + return; + } + + editor.focus(); + + panel.hide( false ); + + editor.fire( 'saveSnapshot' ); + + // Clean up any conflicting style within the range. + new CKEDITOR.style( config['colorButton_' + type + 'Style'], { color : 'inherit' } ).remove( editor.document ); + + if ( color ) + { + var colorStyle = config['colorButton_' + type + 'Style']; + + colorStyle.childRule = type == 'back' ? + function( element ) + { + // It's better to apply background color as the innermost style. (#3599) + // Except for "unstylable elements". (#6103) + return isUnstylable( element ); + } + : + function( element ) + { + // Fore color style must be applied inside links instead of around it. (#4772,#6908) + return !( element.is( 'a' ) || element.getElementsByTag( 'a' ).count() ) || isUnstylable( element ); + }; + + new CKEDITOR.style( colorStyle, { color : color } ).apply( editor.document ); + } + + editor.fire( 'saveSnapshot' ); + }); + + // Render the "Automatic" button. + output.push( + '' + + '' + + '' + + '' + + '' + + '' + + '
' + + '' + + '', + lang.auto, + '
' + + '
' + + '' ); + + // Render the color boxes. + for ( var i = 0 ; i < colors.length ; i++ ) + { + if ( ( i % 8 ) === 0 ) + output.push( '' ); + + var parts = colors[ i ].split( '/' ), + colorName = parts[ 0 ], + colorCode = parts[ 1 ] || colorName; + + // The data can be only a color code (without #) or colorName + color code + // If only a color code is provided, then the colorName is the color with the hash + // Convert the color from RGB to RRGGBB for better compatibility with IE and . See #5676 + if (!parts[1]) + colorName = '#' + colorName.replace( /^(.)(.)(.)$/, '$1$1$2$2$3$3' ); + + var colorLabel = editor.lang.colors[ colorCode ] || colorCode; + output.push( + '' ); + } + + // Render the "More Colors" button. + if ( config.colorButton_enableMore === undefined || config.colorButton_enableMore ) + { + output.push( + '' + + '' + + '' ); // tr is later in the code. + } + + output.push( '
' + + '' + + '' + + '' + + '
' + + '', + lang.more, + '' + + '
' ); + + return output.join( '' ); + } + + function isUnstylable( ele ) + { + return ( ele.getAttribute( 'contentEditable' ) == 'false' ) || ele.getAttribute( 'data-nostyle' ); + } + } +}); + +/** + * Whether to enable the More Colors button in the color selectors. + * @name CKEDITOR.config.colorButton_enableMore + * @default true + * @type Boolean + * @example + * config.colorButton_enableMore = false; + */ + +/** + * Defines the colors to be displayed in the color selectors. This is a string + * containing hexadecimal notation for HTML colors, without the "#" prefix. + *

+ * Since 3.3: A color name may optionally be defined by prefixing the entries with + * a name and the slash character. For example, "FontColor1/FF9900" will be + * displayed as the color #FF9900 in the selector, but will be output as "FontColor1". + * @name CKEDITOR.config.colorButton_colors + * @type String + * @default '000,800000,8B4513,2F4F4F,008080,000080,4B0082,696969,B22222,A52A2A,DAA520,006400,40E0D0,0000CD,800080,808080,F00,FF8C00,FFD700,008000,0FF,00F,EE82EE,A9A9A9,FFA07A,FFA500,FFFF00,00FF00,AFEEEE,ADD8E6,DDA0DD,D3D3D3,FFF0F5,FAEBD7,FFFFE0,F0FFF0,F0FFFF,F0F8FF,E6E6FA,FFF' + * @example + * // Brazil colors only. + * config.colorButton_colors = '00923E,F8C100,28166F'; + * @example + * config.colorButton_colors = 'FontColor1/FF9900,FontColor2/0066CC,FontColor3/F00' + */ +CKEDITOR.config.colorButton_colors = + '000,800000,8B4513,2F4F4F,008080,000080,4B0082,696969,' + + 'B22222,A52A2A,DAA520,006400,40E0D0,0000CD,800080,808080,' + + 'F00,FF8C00,FFD700,008000,0FF,00F,EE82EE,A9A9A9,' + + 'FFA07A,FFA500,FFFF00,00FF00,AFEEEE,ADD8E6,DDA0DD,D3D3D3,' + + 'FFF0F5,FAEBD7,FFFFE0,F0FFF0,F0FFFF,F0F8FF,E6E6FA,FFF'; + +/** + * Stores the style definition that applies the text foreground color. + * @name CKEDITOR.config.colorButton_foreStyle + * @type Object + * @default (see example) + * @example + * // This is actually the default value. + * config.colorButton_foreStyle = + * { + * element : 'span', + * styles : { 'color' : '#(color)' } + * }; + */ +CKEDITOR.config.colorButton_foreStyle = + { + element : 'span', + styles : { 'color' : '#(color)' }, + overrides : [ { element : 'font', attributes : { 'color' : null } } ] + }; + +/** + * Stores the style definition that applies the text background color. + * @name CKEDITOR.config.colorButton_backStyle + * @type Object + * @default (see example) + * @example + * // This is actually the default value. + * config.colorButton_backStyle = + * { + * element : 'span', + * styles : { 'background-color' : '#(color)' } + * }; + */ +CKEDITOR.config.colorButton_backStyle = + { + element : 'span', + styles : { 'background-color' : '#(color)' } + }; Index: openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/colordialog/plugin.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/colordialog/Attic/plugin.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/colordialog/plugin.js 13 Feb 2012 18:53:11 -0000 1.1 @@ -0,0 +1,15 @@ +/* +Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +CKEDITOR.plugins.colordialog = +{ + init : function( editor ) + { + editor.addCommand( 'colordialog', new CKEDITOR.dialogCommand( 'colordialog' ) ); + CKEDITOR.dialog.add( 'colordialog', this.path + 'dialogs/colordialog.js' ); + } +}; + +CKEDITOR.plugins.add( 'colordialog', CKEDITOR.plugins.colordialog ); Index: openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/colordialog/dialogs/colordialog.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/colordialog/dialogs/Attic/colordialog.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/colordialog/dialogs/colordialog.js 13 Feb 2012 18:53:11 -0000 1.1 @@ -0,0 +1,340 @@ +/* +Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +CKEDITOR.dialog.add( 'colordialog', function( editor ) + { + // Define some shorthands. + var $el = CKEDITOR.dom.element, + $doc = CKEDITOR.document, + $tools = CKEDITOR.tools, + lang = editor.lang.colordialog; + + // Reference the dialog. + var dialog; + + var spacer = + { + type : 'html', + html : ' ' + }; + + function clearSelected() + { + $doc.getById( selHiColorId ).removeStyle( 'background-color' ); + dialog.getContentElement( 'picker', 'selectedColor' ).setValue( '' ); + } + + function updateSelected( evt ) + { + if ( ! ( evt instanceof CKEDITOR.dom.event ) ) + evt = new CKEDITOR.dom.event( evt ); + + var target = evt.getTarget(), + color; + + if ( target.getName() == 'a' && ( color = target.getChild( 0 ).getHtml() ) ) + dialog.getContentElement( 'picker', 'selectedColor' ).setValue( color ); + } + + function updateHighlight( event ) + { + if ( ! ( event instanceof CKEDITOR.dom.event ) ) + event = event.data; + + var target = event.getTarget(), + color; + + if ( target.getName() == 'a' && ( color = target.getChild( 0 ).getHtml() ) ) + { + $doc.getById( hicolorId ).setStyle( 'background-color', color ); + $doc.getById( hicolorTextId ).setHtml( color ); + } + } + + function clearHighlight() + { + $doc.getById( hicolorId ).removeStyle( 'background-color' ); + $doc.getById( hicolorTextId ).setHtml( ' ' ); + } + + var onMouseout = $tools.addFunction( clearHighlight ), + onClick = updateSelected, + onClickHandler = CKEDITOR.tools.addFunction( onClick ), + onFocus = updateHighlight, + onBlur = clearHighlight; + + var onKeydownHandler = CKEDITOR.tools.addFunction( function( ev ) + { + ev = new CKEDITOR.dom.event( ev ); + var element = ev.getTarget(); + var relative, nodeToMove; + var keystroke = ev.getKeystroke(), + rtl = editor.lang.dir == 'rtl'; + + switch ( keystroke ) + { + // UP-ARROW + case 38 : + // relative is TR + if ( ( relative = element.getParent().getParent().getPrevious() ) ) + { + nodeToMove = relative.getChild( [element.getParent().getIndex(), 0] ); + nodeToMove.focus(); + onBlur( ev, element ); + onFocus( ev, nodeToMove ); + } + ev.preventDefault(); + break; + // DOWN-ARROW + case 40 : + // relative is TR + if ( ( relative = element.getParent().getParent().getNext() ) ) + { + nodeToMove = relative.getChild( [ element.getParent().getIndex(), 0 ] ); + if ( nodeToMove && nodeToMove.type == 1 ) + { + nodeToMove.focus(); + onBlur( ev, element ); + onFocus( ev, nodeToMove ); + } + } + ev.preventDefault(); + break; + // SPACE + // ENTER is already handled as onClick + case 32 : + onClick( ev ); + ev.preventDefault(); + break; + + // RIGHT-ARROW + case rtl ? 37 : 39 : + // relative is TD + if ( ( relative = element.getParent().getNext() ) ) + { + nodeToMove = relative.getChild( 0 ); + if ( nodeToMove.type == 1 ) + { + nodeToMove.focus(); + onBlur( ev, element ); + onFocus( ev, nodeToMove ); + ev.preventDefault( true ); + } + else + onBlur( null, element ); + } + // relative is TR + else if ( ( relative = element.getParent().getParent().getNext() ) ) + { + nodeToMove = relative.getChild( [ 0, 0 ] ); + if ( nodeToMove && nodeToMove.type == 1 ) + { + nodeToMove.focus(); + onBlur( ev, element ); + onFocus( ev, nodeToMove ); + ev.preventDefault( true ); + } + else + onBlur( null, element ); + } + break; + + // LEFT-ARROW + case rtl ? 39 : 37 : + // relative is TD + if ( ( relative = element.getParent().getPrevious() ) ) + { + nodeToMove = relative.getChild( 0 ); + nodeToMove.focus(); + onBlur( ev, element ); + onFocus( ev, nodeToMove ); + ev.preventDefault( true ); + } + // relative is TR + else if ( ( relative = element.getParent().getParent().getPrevious() ) ) + { + nodeToMove = relative.getLast().getChild( 0 ); + nodeToMove.focus(); + onBlur( ev, element ); + onFocus( ev, nodeToMove ); + ev.preventDefault( true ); + } + else + onBlur( null, element ); + break; + default : + // Do not stop not handled events. + return; + } + }); + + function createColorTable() + { + // Create the base colors array. + var aColors = [ '00', '33', '66', '99', 'cc', 'ff' ]; + + // This function combines two ranges of three values from the color array into a row. + function appendColorRow( rangeA, rangeB ) + { + for ( var i = rangeA ; i < rangeA + 3 ; i++ ) + { + var row = table.$.insertRow( -1 ); + + for ( var j = rangeB ; j < rangeB + 3 ; j++ ) + { + for ( var n = 0 ; n < 6 ; n++ ) + { + appendColorCell( row, '#' + aColors[j] + aColors[n] + aColors[i] ); + } + } + } + } + + // This function create a single color cell in the color table. + function appendColorCell( targetRow, color ) + { + var cell = new $el( targetRow.insertCell( -1 ) ); + cell.setAttribute( 'class', 'ColorCell' ); + cell.setStyle( 'background-color', color ); + + cell.setStyle( 'width', '15px' ); + cell.setStyle( 'height', '15px' ); + + var index = cell.$.cellIndex + 1 + 18 * targetRow.rowIndex; + cell.append( CKEDITOR.dom.element.createFromHtml( + '' + color + ' ', CKEDITOR.document ) ); + } + + appendColorRow( 0, 0 ); + appendColorRow( 3, 0 ); + appendColorRow( 0, 3 ); + appendColorRow( 3, 3 ); + + // Create the last row. + var oRow = table.$.insertRow(-1) ; + + // Create the gray scale colors cells. + for ( var n = 0 ; n < 6 ; n++ ) + { + appendColorCell( oRow, '#' + aColors[n] + aColors[n] + aColors[n] ) ; + } + + // Fill the row with black cells. + for ( var i = 0 ; i < 12 ; i++ ) + { + appendColorCell( oRow, '#000000' ) ; + } + } + + var table = new $el( 'table' ); + createColorTable(); + var html = table.getHtml(); + + var numbering = function( id ) + { + return CKEDITOR.tools.getNextId() + '_' + id; + }, + hicolorId = numbering( 'hicolor' ), + hicolorTextId = numbering( 'hicolortext' ), + selHiColorId = numbering( 'selhicolor' ), + tableLabelId = numbering( 'color_table_label' ); + + return { + title : lang.title, + minWidth : 360, + minHeight : 220, + onLoad : function() + { + // Update reference. + dialog = this; + }, + contents : [ + { + id : 'picker', + label : lang.title, + accessKey : 'I', + elements : + [ + { + type : 'hbox', + padding : 0, + widths : [ '70%', '10%', '30%' ], + children : + [ + { + type : 'html', + html : '' + + ( !CKEDITOR.env.webkit ? html : '' ) + + '
' + lang.options +'', + onLoad : function() + { + var table = CKEDITOR.document.getById( this.domId ); + table.on( 'mouseover', updateHighlight ); + // In WebKit, the table content must be inserted after this event call (#6150) + CKEDITOR.env.webkit && table.setHtml( html ); + }, + focus: function() + { + var firstColor = this.getElement().getElementsByTag( 'a' ).getItem( 0 ); + firstColor.focus(); + } + }, + spacer, + { + type : 'vbox', + padding : 0, + widths : [ '70%', '5%', '25%' ], + children : + [ + { + type : 'html', + html : '' + lang.highlight +'\ +
\ +
 
' + lang.selected + '\ +
' + }, + { + type : 'text', + label : lang.selected, + labelStyle: 'display:none', + id : 'selectedColor', + style : 'width: 74px', + onChange : function() + { + // Try to update color preview with new value. If fails, then set it no none. + try + { + $doc.getById( selHiColorId ).setStyle( 'background-color', this.getValue() ); + } + catch ( e ) + { + clearSelected(); + } + } + }, + spacer, + { + type : 'button', + id : 'clear', + style : 'margin-top: 5px', + label : lang.clear, + onClick : clearSelected + } + ] + } + ] + } + ] + } + ] + }; + } + ); Index: openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/contextmenu/plugin.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/contextmenu/Attic/plugin.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/contextmenu/plugin.js 13 Feb 2012 18:53:11 -0000 1.1 @@ -0,0 +1,179 @@ +/* +Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +CKEDITOR.plugins.add( 'contextmenu', +{ + requires : [ 'menu' ], + + // Make sure the base class (CKEDITOR.menu) is loaded before it (#3318). + onLoad : function() + { + CKEDITOR.plugins.contextMenu = CKEDITOR.tools.createClass( + { + base : CKEDITOR.menu, + + $ : function( editor ) + { + this.base.call( this, editor, + { + panel: + { + className : editor.skinClass + ' cke_contextmenu', + attributes : + { + 'aria-label' : editor.lang.contextmenu.options + } + } + }); + }, + + proto : + { + addTarget : function( element, nativeContextMenuOnCtrl ) + { + // Opera doesn't support 'contextmenu' event, we have duo approaches employed here: + // 1. Inherit the 'button override' hack we introduced in v2 (#4530), while this require the Opera browser + // option 'Allow script to detect context menu/right click events' to be always turned on. + // 2. Considering the fact that ctrl/meta key is not been occupied + // for multiple range selecting (like Gecko), we use this key + // combination as a fallback for triggering context-menu. (#4530) + if ( CKEDITOR.env.opera && !( 'oncontextmenu' in document.body )) + { + var contextMenuOverrideButton; + element.on( 'mousedown', function( evt ) + { + evt = evt.data; + if ( evt.$.button != 2 ) + { + if ( evt.getKeystroke() == CKEDITOR.CTRL + 1 ) + element.fire( 'contextmenu', evt ); + return; + } + + if ( nativeContextMenuOnCtrl + && ( CKEDITOR.env.mac ? evt.$.metaKey : evt.$.ctrlKey ) ) + return; + + var target = evt.getTarget(); + + if ( !contextMenuOverrideButton ) + { + var ownerDoc = target.getDocument(); + contextMenuOverrideButton = ownerDoc.createElement( 'input' ) ; + contextMenuOverrideButton.$.type = 'button' ; + ownerDoc.getBody().append( contextMenuOverrideButton ) ; + } + + contextMenuOverrideButton.setAttribute( 'style', 'position:absolute;top:' + ( evt.$.clientY - 2 ) + + 'px;left:' + ( evt.$.clientX - 2 ) + + 'px;width:5px;height:5px;opacity:0.01' ); + + } ); + + element.on( 'mouseup', function ( evt ) + { + if ( contextMenuOverrideButton ) + { + contextMenuOverrideButton.remove(); + contextMenuOverrideButton = undefined; + // Simulate 'contextmenu' event. + element.fire( 'contextmenu', evt.data ); + } + } ); + } + + element.on( 'contextmenu', function( event ) + { + var domEvent = event.data; + + if ( nativeContextMenuOnCtrl && + // Safari on Windows always show 'ctrlKey' as true in 'contextmenu' event, + // which make this property unreliable. (#4826) + ( CKEDITOR.env.webkit ? holdCtrlKey : ( CKEDITOR.env.mac ? domEvent.$.metaKey : domEvent.$.ctrlKey ) ) ) + return; + + + // Cancel the browser context menu. + domEvent.preventDefault(); + + var offsetParent = domEvent.getTarget().getDocument().getDocumentElement(), + offsetX = domEvent.$.clientX, + offsetY = domEvent.$.clientY; + + CKEDITOR.tools.setTimeout( function() + { + this.open( offsetParent, null, offsetX, offsetY ); + + // IE needs a short while to allow selection change before opening menu. (#7908) + }, CKEDITOR.env.ie? 200 : 0, this ); + }, + this ); + + if ( CKEDITOR.env.opera ) + { + // 'contextmenu' event triggered by Windows menu key is unpreventable, + // cancel the key event itself. (#6534) + element.on( 'keypress' , function ( evt ) + { + var domEvent = evt.data; + + if ( domEvent.$.keyCode === 0 ) + domEvent.preventDefault(); + }); + } + + if ( CKEDITOR.env.webkit ) + { + var holdCtrlKey, + onKeyDown = function( event ) + { + holdCtrlKey = CKEDITOR.env.mac ? event.data.$.metaKey : event.data.$.ctrlKey ; + }, + resetOnKeyUp = function() + { + holdCtrlKey = 0; + }; + + element.on( 'keydown', onKeyDown ); + element.on( 'keyup', resetOnKeyUp ); + element.on( 'contextmenu', resetOnKeyUp ); + } + }, + + open : function( offsetParent, corner, offsetX, offsetY ) + { + this.editor.focus(); + offsetParent = offsetParent || CKEDITOR.document.getDocumentElement(); + this.show( offsetParent, corner, offsetX, offsetY ); + } + } + }); + }, + + beforeInit : function( editor ) + { + editor.contextMenu = new CKEDITOR.plugins.contextMenu( editor ); + + editor.addCommand( 'contextMenu', + { + exec : function() + { + editor.contextMenu.open( editor.document.getBody() ); + } + }); + } +}); + +/** + * Whether to show the browser native context menu when the Ctrl or + * Meta (Mac) key is pressed on opening the context menu with the + * right mouse button click or the Menu key. + * @name CKEDITOR.config.browserContextMenuOnCtrl + * @since 3.0.2 + * @type Boolean + * @default true + * @example + * config.browserContextMenuOnCtrl = false; + */ Index: openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/devtools/plugin.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/devtools/Attic/plugin.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/devtools/plugin.js 13 Feb 2012 18:53:11 -0000 1.1 @@ -0,0 +1,173 @@ +/* +Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +CKEDITOR.plugins.add( 'devtools', +{ + lang : [ 'en' ], + + init : function( editor ) + { + editor._.showDialogDefinitionTooltips = 1; + }, + onLoad : function() + { + CKEDITOR.document.appendStyleText( CKEDITOR.config.devtools_styles || + '#cke_tooltip { padding: 5px; border: 2px solid #333; background: #ffffff }' + + '#cke_tooltip h2 { font-size: 1.1em; border-bottom: 1px solid; margin: 0; padding: 1px; }' + + '#cke_tooltip ul { padding: 0pt; list-style-type: none; }' ); + } +}); + +(function() +{ + function defaultCallback( editor, dialog, element, tabName ) + { + var lang = editor.lang.devTools, + link = '' + ( element ? element.type : 'content' ) + '', + str = + '

' + lang.title + '

' + + ''; + } + + function showTooltip( callback, el, editor, dialog, obj, tabName ) + { + var pos = el.getDocumentPosition(), + styles = { 'z-index' : CKEDITOR.dialog._.currentZIndex + 10, top : ( pos.y + el.getSize( 'height' ) ) + 'px' }; + + tooltip.setHtml( callback( editor, dialog, obj, tabName ) ); + tooltip.show(); + + // Translate coordinate for RTL. + if ( editor.lang.dir == 'rtl' ) + { + var viewPaneSize = CKEDITOR.document.getWindow().getViewPaneSize(); + styles.right = ( viewPaneSize.width - pos.x - el.getSize( 'width' ) ) + 'px'; + } + else + styles.left = pos.x + 'px'; + + tooltip.setStyles( styles ); + } + + var tooltip; + CKEDITOR.on( 'reset', function() + { + tooltip && tooltip.remove(); + tooltip = null; + }); + + CKEDITOR.on( 'dialogDefinition', function( evt ) + { + var editor = evt.editor; + if ( editor._.showDialogDefinitionTooltips ) + { + if ( !tooltip ) + { + tooltip = CKEDITOR.dom.element.createFromHtml( '
', CKEDITOR.document ); + tooltip.hide(); + tooltip.on( 'mouseover', function(){ this.show(); } ); + tooltip.on( 'mouseout', function(){ this.hide(); } ); + tooltip.appendTo( CKEDITOR.document.getBody() ); + } + + var dialog = evt.data.definition.dialog, + callback = editor.config.devtools_textCallback || defaultCallback; + + dialog.on( 'load', function() + { + var tabs = dialog.parts.tabs.getChildren(), tab; + for ( var i = 0, len = tabs.count(); i < len; i++ ) + { + tab = tabs.getItem( i ); + tab.on( 'mouseover', function() + { + var id = this.$.id; + showTooltip( callback, this, editor, dialog, null, id.substring( 4, id.lastIndexOf( '_' ) ) ); + }); + tab.on( 'mouseout', function() + { + tooltip.hide(); + }); + } + + dialog.foreach( function( obj ) + { + if ( obj.type in { hbox : 1, vbox : 1 } ) + return; + + var el = obj.getElement(); + if ( el ) + { + el.on( 'mouseover', function() + { + showTooltip( callback, this, editor, dialog, obj, dialog._.currentTabId ); + }); + el.on( 'mouseout', function() + { + tooltip.hide(); + }); + } + }); + }); + } + }); +})(); + +/** + * A function that returns the text to be displayed inside the Developer Tools tooltip when hovering over a dialog UI element. + * There are 4 parameters that are being passed into the function: editor, dialog window, element, tab name. + * @name editor.config.devtools_textCallback + * @since 3.6 + * @type Function + * @default (see example) + * @example + * // This is actually the default value. + * // Show dialog window name, tab ID, and element ID. + * config.devtools_textCallback = function( editor, dialog, element, tabName ) + * { + * var lang = editor.lang.devTools, + * link = '' + ( element ? element.type : 'content' ) + '', + * str = + * '

' + lang.title + '

' + + * ''; + * } + */ + +/** + * A setting that stores CSS rules to be injected into the page with styles to be applied to the tooltip element. + * @name CKEDITOR.config.devtools_styles + * @since 3.6 + * @type String + * @default (see example) + * @example + * // This is actually the default value. + * CKEDITOR.config.devtools_styles = " + * #cke_tooltip { padding: 5px; border: 2px solid #333; background: #ffffff } + * #cke_tooltip h2 { font-size: 1.1em; border-bottom: 1px solid; margin: 0; padding: 1px; } + * #cke_tooltip ul { padding: 0pt; list-style-type: none; } + * "; + */ Index: openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/devtools/lang/en.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/devtools/lang/Attic/en.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/devtools/lang/en.js 13 Feb 2012 18:53:11 -0000 1.1 @@ -0,0 +1,16 @@ +/* +Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +CKEDITOR.plugins.setLang( 'devtools', 'en', +{ + devTools : + { + title : 'Element Information', + dialogName : 'Dialog window name', + tabName : 'Tab name', + elementId : 'Element ID', + elementType : 'Element type' + } +}); Index: openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/dialog/dialogDefinition.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/dialog/Attic/dialogDefinition.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/dialog/dialogDefinition.js 13 Feb 2012 18:53:11 -0000 1.1 @@ -0,0 +1,1166 @@ +/* +Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +/** + * @fileOverview Defines the "virtual" dialog, dialog content and dialog button + * definition classes. + */ + +/** + * The definition of a dialog window. + *
+ * This class is not really part of the API. It just illustrates the properties + * that developers can use to define and create dialogs. + *
+ * @name CKEDITOR.dialog.definition + * @constructor + * @example + * // There is no constructor for this class, the user just has to define an + * // object with the appropriate properties. + * + * CKEDITOR.dialog.add( 'testOnly', function( editor ) + * { + * return { + * title : 'Test Dialog', + * resizable : CKEDITOR.DIALOG_RESIZE_BOTH, + * minWidth : 500, + * minHeight : 400, + * contents : [ + * { + * id : 'tab1', + * label : 'First Tab', + * title : 'First Tab Title', + * accessKey : 'Q', + * elements : [ + * { + * type : 'text', + * label : 'Test Text 1', + * id : 'testText1', + * 'default' : 'hello world!' + * } + * ] + * } + * ] + * }; + * }); + */ + +/** + * The dialog title, displayed in the dialog's header. Required. + * @name CKEDITOR.dialog.definition.prototype.title + * @field + * @type String + * @example + */ + +/** + * How the dialog can be resized, must be one of the four contents defined below. + *

+ * CKEDITOR.DIALOG_RESIZE_NONE
+ * CKEDITOR.DIALOG_RESIZE_WIDTH
+ * CKEDITOR.DIALOG_RESIZE_HEIGHT
+ * CKEDITOR.DIALOG_RESIZE_BOTH
+ * @name CKEDITOR.dialog.definition.prototype.resizable + * @field + * @type Number + * @default CKEDITOR.DIALOG_RESIZE_NONE + * @example + */ + +/** + * The minimum width of the dialog, in pixels. + * @name CKEDITOR.dialog.definition.prototype.minWidth + * @field + * @type Number + * @default 600 + * @example + */ + +/** + * The minimum height of the dialog, in pixels. + * @name CKEDITOR.dialog.definition.prototype.minHeight + * @field + * @type Number + * @default 400 + * @example + */ + + +/** + * The initial width of the dialog, in pixels. + * @name CKEDITOR.dialog.definition.prototype.width + * @field + * @type Number + * @default @CKEDITOR.dialog.definition.prototype.minWidth + * @since 3.5.3 + * @example + */ + +/** + * The initial height of the dialog, in pixels. + * @name CKEDITOR.dialog.definition.prototype.height + * @field + * @type Number + * @default @CKEDITOR.dialog.definition.prototype.minHeight + * @since 3.5.3 + * @example + */ + +/** + * The buttons in the dialog, defined as an array of + * {@link CKEDITOR.dialog.definition.button} objects. + * @name CKEDITOR.dialog.definition.prototype.buttons + * @field + * @type Array + * @default [ CKEDITOR.dialog.okButton, CKEDITOR.dialog.cancelButton ] + * @example + */ + +/** + * The contents in the dialog, defined as an array of + * {@link CKEDITOR.dialog.definition.content} objects. Required. + * @name CKEDITOR.dialog.definition.prototype.contents + * @field + * @type Array + * @example + */ + +/** + * The function to execute when OK is pressed. + * @name CKEDITOR.dialog.definition.prototype.onOk + * @field + * @type Function + * @example + */ + +/** + * The function to execute when Cancel is pressed. + * @name CKEDITOR.dialog.definition.prototype.onCancel + * @field + * @type Function + * @example + */ + +/** + * The function to execute when the dialog is displayed for the first time. + * @name CKEDITOR.dialog.definition.prototype.onLoad + * @field + * @type Function + * @example + */ + +/** + * The function to execute when the dialog is loaded (executed every time the dialog is opened). + * @name CKEDITOR.dialog.definition.prototype.onShow + * @field + * @type Function + * @example + */ + +/** + *
This class is not really part of the API. It just illustrates the properties + * that developers can use to define and create dialog content pages.
+ * @name CKEDITOR.dialog.definition.content + * @constructor + * @example + * // There is no constructor for this class, the user just has to define an + * // object with the appropriate properties. + */ + +/** + * The id of the content page. + * @name CKEDITOR.dialog.definition.content.prototype.id + * @field + * @type String + * @example + */ + +/** + * The tab label of the content page. + * @name CKEDITOR.dialog.definition.content.prototype.label + * @field + * @type String + * @example + */ + +/** + * The popup message of the tab label. + * @name CKEDITOR.dialog.definition.content.prototype.title + * @field + * @type String + * @example + */ + +/** + * The CTRL hotkey for switching to the tab. + * @name CKEDITOR.dialog.definition.content.prototype.accessKey + * @field + * @type String + * @example + * contentDefinition.accessKey = 'Q'; // Switch to this page when CTRL-Q is pressed. + */ + +/** + * The UI elements contained in this content page, defined as an array of + * {@link CKEDITOR.dialog.definition.uiElement} objects. + * @name CKEDITOR.dialog.definition.content.prototype.elements + * @field + * @type Array + * @example + */ + +/** + * The definition of user interface element (textarea, radio etc). + *
This class is not really part of the API. It just illustrates the properties + * that developers can use to define and create dialog UI elements.
+ * @name CKEDITOR.dialog.definition.uiElement + * @constructor + * @see CKEDITOR.ui.dialog.uiElement + * @example + * // There is no constructor for this class, the user just has to define an + * // object with the appropriate properties. + */ + +/** + * The id of the UI element. + * @name CKEDITOR.dialog.definition.uiElement.prototype.id + * @field + * @type String + * @example + */ + +/** + * The type of the UI element. Required. + * @name CKEDITOR.dialog.definition.uiElement.prototype.type + * @field + * @type String + * @example + */ + +/** + * The popup label of the UI element. + * @name CKEDITOR.dialog.definition.uiElement.prototype.title + * @field + * @type String + * @example + */ + +/** + * CSS class names to append to the UI element. + * @name CKEDITOR.dialog.definition.uiElement.prototype.className + * @field + * @type String + * @example + */ + +/** + * Inline CSS classes to append to the UI element. + * @name CKEDITOR.dialog.definition.uiElement.prototype.style + * @field + * @type String + * @example + */ + +/** + * Horizontal alignment (in container) of the UI element. + * @name CKEDITOR.dialog.definition.uiElement.prototype.align + * @field + * @type String + * @example + */ + +/** + * Function to execute the first time the UI element is displayed. + * @name CKEDITOR.dialog.definition.uiElement.prototype.onLoad + * @field + * @type Function + * @example + */ + +/** + * Function to execute whenever the UI element's parent dialog is displayed. + * @name CKEDITOR.dialog.definition.uiElement.prototype.onShow + * @field + * @type Function + * @example + */ + +/** + * Function to execute whenever the UI element's parent dialog is closed. + * @name CKEDITOR.dialog.definition.uiElement.prototype.onHide + * @field + * @type Function + * @example + */ + +/** + * Function to execute whenever the UI element's parent dialog's {@link CKEDITOR.dialog.definition.setupContent} method is executed. + * It usually takes care of the respective UI element as a standalone element. + * @name CKEDITOR.dialog.definition.uiElement.prototype.setup + * @field + * @type Function + * @example + */ + +/** + * Function to execute whenever the UI element's parent dialog's {@link CKEDITOR.dialog.definition.commitContent} method is executed. + * It usually takes care of the respective UI element as a standalone element. + * @name CKEDITOR.dialog.definition.uiElement.prototype.commit + * @field + * @type Function + * @example + */ + +// ----- hbox ----- + +/** + * Horizontal layout box for dialog UI elements, auto-expends to available width of container. + *
+ * This class is not really part of the API. It just illustrates the properties + * that developers can use to define and create horizontal layouts. + *

Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.hbox} object and can be accessed with {@link CKEDITOR.dialog#getContentElement}. + *
+ * @name CKEDITOR.dialog.definition.hbox + * @extends CKEDITOR.dialog.definition.uiElement + * @constructor + * @example + * // There is no constructor for this class, the user just has to define an + * // object with the appropriate properties. + * + * // Example: + * { + * type : 'hbox', + * widths : [ '25%', '25%', '50%' ], + * children : + * [ + * { + * type : 'text', + * id : 'id1', + * width : '40px', + * }, + * { + * type : 'text', + * id : 'id2', + * width : '40px', + * }, + * { + * type : 'text', + * id : 'id3' + * } + * ] + * } + */ + +/** + * Array of {@link CKEDITOR.ui.dialog.uiElement} objects inside this container. + * @name CKEDITOR.dialog.definition.hbox.prototype.children + * @field + * @type Array + * @example + */ + +/** + * (Optional) The widths of child cells. + * @name CKEDITOR.dialog.definition.hbox.prototype.widths + * @field + * @type Array + * @example + */ + +/** + * (Optional) The height of the layout. + * @name CKEDITOR.dialog.definition.hbox.prototype.height + * @field + * @type Number + * @example + */ + +/** + * The CSS styles to apply to this element. + * @name CKEDITOR.dialog.definition.hbox.prototype.styles + * @field + * @type String + * @example + */ + +/** + * (Optional) The padding width inside child cells. Example: 0, 1. + * @name CKEDITOR.dialog.definition.hbox.prototype.padding + * @field + * @type Number + * @example + */ + +/** + * (Optional) The alignment of the whole layout. Example: center, top. + * @name CKEDITOR.dialog.definition.hbox.prototype.align + * @field + * @type String + * @example + */ + +// ----- vbox ----- + +/** + * Vertical layout box for dialog UI elements. + *
+ * This class is not really part of the API. It just illustrates the properties + * that developers can use to define and create vertical layouts. + *

Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.vbox} object and can be accessed with {@link CKEDITOR.dialog#getContentElement}. + *
+ * + * @name CKEDITOR.dialog.definition.vbox + * @extends CKEDITOR.dialog.definition.uiElement + * @constructor + * @example + * // There is no constructor for this class, the user just has to define an + * // object with the appropriate properties. + * + * // Example: + * { + * type : 'vbox', + * align : 'right', + * width : '200px', + * children : + * [ + * { + * type : 'text', + * id : 'age', + * label : 'Age' + * }, + * { + * type : 'text', + * id : 'sex', + * label : 'Sex' + * }, + * { + * type : 'text', + * id : 'nationality', + * label : 'Nationality' + * } + * ] + * } + */ + +/** + * Array of {@link CKEDITOR.ui.dialog.uiElement} objects inside this container. + * @name CKEDITOR.dialog.definition.vbox.prototype.children + * @field + * @type Array + * @example + */ + +/** + * (Optional) The width of the layout. + * @name CKEDITOR.dialog.definition.vbox.prototype.width + * @field + * @type Array + * @example + */ + +/** + * (Optional) The heights of individual cells. + * @name CKEDITOR.dialog.definition.vbox.prototype.heights + * @field + * @type Number + * @example + */ + +/** + * The CSS styles to apply to this element. + * @name CKEDITOR.dialog.definition.vbox.prototype.styles + * @field + * @type String + * @example + */ + +/** + * (Optional) The padding width inside child cells. Example: 0, 1. + * @name CKEDITOR.dialog.definition.vbox.prototype.padding + * @field + * @type Number + * @example + */ + +/** + * (Optional) The alignment of the whole layout. Example: center, top. + * @name CKEDITOR.dialog.definition.vbox.prototype.align + * @field + * @type String + * @example + */ + +/** + * (Optional) Whether the layout should expand vertically to fill its container. + * @name CKEDITOR.dialog.definition.vbox.prototype.expand + * @field + * @type Boolean + * @example + */ + +// ----- labeled element ------ + +/** + * The definition of labeled user interface element (textarea, textInput etc). + *
This class is not really part of the API. It just illustrates the properties + * that developers can use to define and create dialog UI elements.
+ * @name CKEDITOR.dialog.definition.labeledElement + * @extends CKEDITOR.dialog.definition.uiElement + * @constructor + * @see CKEDITOR.ui.dialog.labeledElement + * @example + * // There is no constructor for this class, the user just has to define an + * // object with the appropriate properties. + */ + +/** + * The label of the UI element. + * @name CKEDITOR.dialog.definition.labeledElement.prototype.label + * @type String + * @field + * @example + * { + * type : 'text', + * label : 'My Label ' + * } + */ + +/** + * (Optional) Specify the layout of the label. Set to 'horizontal' for horizontal layout. + * The default layout is vertical. + * @name CKEDITOR.dialog.definition.labeledElement.prototype.labelLayout + * @type String + * @field + * @example + * { + * type : 'text', + * label : 'My Label ', + * labelLayout : 'horizontal', + * } + */ + +/** + * (Optional) Applies only to horizontal layouts: a two elements array of lengths to specify the widths of the +* label and the content element. See also {@link CKEDITOR.dialog.definition.labeledElement#labelLayout}. + * @name CKEDITOR.dialog.definition.labeledElement.prototype.widths + * @type Array + * @field + * @example + * { + * type : 'text', + * label : 'My Label ', + * labelLayout : 'horizontal', + * widths : [100, 200], + * } + */ + +/** + * Specify the inline style of the uiElement label. + * @name CKEDITOR.dialog.definition.labeledElement.prototype.labelStyle + * @type String + * @field + * @example + * { + * type : 'text', + * label : 'My Label ', + * labelStyle : 'color: red', + * } + */ + + +/** + * Specify the inline style of the input element. + * @name CKEDITOR.dialog.definition.labeledElement.prototype.inputStyle + * @type String + * @since 3.6.1 + * @field + * @example + * { + * type : 'text', + * label : 'My Label ', + * inputStyle : 'text-align:center', + * } + */ + +/** + * Specify the inline style of the input element container . + * @name CKEDITOR.dialog.definition.labeledElement.prototype.controlStyle + * @type String + * @since 3.6.1 + * @field + * @example + * { + * type : 'text', + * label : 'My Label ', + * controlStyle : 'width:3em', + * } + */ + + +// ----- button ------ + +/** + * The definition of a button. + *
+ * This class is not really part of the API. It just illustrates the properties + * that developers can use to define and create buttons. + *

Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.button} object and can be accessed with {@link CKEDITOR.dialog#getContentElement}. + *
+ * For a complete example of dialog definition, please check {@link CKEDITOR.dialog.add}. + * @name CKEDITOR.dialog.definition.button + * @extends CKEDITOR.dialog.definition.uiElement + * @constructor + * @example + * // There is no constructor for this class, the user just has to define an + * // object with the appropriate properties. + * + * // Example: + * { + * type : 'button', + * id : 'buttonId', + * label : 'Click me', + * title : 'My title', + * onClick : function() { + * // this = CKEDITOR.ui.dialog.button + * alert( 'Clicked: ' + this.id ); + * } + * } + */ + +/** + * Whether the button is disabled. + * @name CKEDITOR.dialog.definition.button.prototype.disabled + * @type Boolean + * @field + * @example + */ + +/** + * The label of the UI element. + * @name CKEDITOR.dialog.definition.button.prototype.label + * @type String + * @field + * @example + */ + +// ----- checkbox ------ + +/** + * The definition of a checkbox element. + *
+ * This class is not really part of the API. It just illustrates the properties + * that developers can use to define and create groups of checkbox buttons. + *

Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.checkbox} object and can be accessed with {@link CKEDITOR.dialog#getContentElement}. + *
+ * For a complete example of dialog definition, please check {@link CKEDITOR.dialog.add}. + * @name CKEDITOR.dialog.definition.checkbox + * @extends CKEDITOR.dialog.definition.uiElement + * @constructor + * @example + * // There is no constructor for this class, the user just has to define an + * // object with the appropriate properties. + * + * // Example: + * { + * type : 'checkbox', + * id : 'agree', + * label : 'I agree', + * 'default' : 'checked', + * onClick : function() { + * // this = CKEDITOR.ui.dialog.checkbox + * alert( 'Checked: ' + this.getValue() ); + * } + * } + */ + +/** + * (Optional) The validation function. + * @name CKEDITOR.dialog.definition.checkbox.prototype.validate + * @field + * @type Function + * @example + */ + +/** + * The label of the UI element. + * @name CKEDITOR.dialog.definition.checkbox.prototype.label + * @type String + * @field + * @example + */ + +/** + * The default state. + * @name CKEDITOR.dialog.definition.checkbox.prototype.default + * @type String + * @field + * @default + * '' (unchecked) + * @example + */ + +// ----- file ----- + +/** + * The definition of a file upload input. + *
+ * This class is not really part of the API. It just illustrates the properties + * that developers can use to define and create file upload elements. + *

Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.file} object and can be accessed with {@link CKEDITOR.dialog#getContentElement}. + *
+ * For a complete example of dialog definition, please check {@link CKEDITOR.dialog.add}. + * @name CKEDITOR.dialog.definition.file + * @extends CKEDITOR.dialog.definition.labeledElement + * @constructor + * @example + * // There is no constructor for this class, the user just has to define an + * // object with the appropriate properties. + * + * // Example: + * { + * type : 'file', + * id : 'upload', + * label : 'Select file from your computer', + * size : 38 + * }, + * { + * type : 'fileButton', + * id : 'fileId', + * label : 'Upload file', + * 'for' : [ 'tab1', 'upload' ] + * filebrowser : { + * onSelect : function( fileUrl, data ) { + * alert( 'Successfully uploaded: ' + fileUrl ); + * } + * } + * } + */ + +/** + * (Optional) The validation function. + * @name CKEDITOR.dialog.definition.file.prototype.validate + * @field + * @type Function + * @example + */ + +/** + * (Optional) The action attribute of the form element associated with this file upload input. + * If empty, CKEditor will use path to server connector for currently opened folder. + * @name CKEDITOR.dialog.definition.file.prototype.action + * @type String + * @field + * @example + */ + +/** + * The size of the UI element. + * @name CKEDITOR.dialog.definition.file.prototype.size + * @type Number + * @field + * @example + */ + +// ----- fileButton ----- + +/** + * The definition of a button for submitting the file in a file upload input. + *
+ * This class is not really part of the API. It just illustrates the properties + * that developers can use to define and create a button for submitting the file in a file upload input. + *

Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.fileButton} object and can be accessed with {@link CKEDITOR.dialog#getContentElement}. + *
+ * For a complete example of dialog definition, please check {@link CKEDITOR.dialog.add}. + * @name CKEDITOR.dialog.definition.fileButton + * @extends CKEDITOR.dialog.definition.uiElement + * @constructor + * @example + * // There is no constructor for this class, the user just has to define an + * // object with the appropriate properties. + * + * // Example: + * { + * type : 'file', + * id : 'upload', + * label : 'Select file from your computer', + * size : 38 + * }, + * { + * type : 'fileButton', + * id : 'fileId', + * label : 'Upload file', + * 'for' : [ 'tab1', 'upload' ] + * filebrowser : { + * onSelect : function( fileUrl, data ) { + * alert( 'Successfully uploaded: ' + fileUrl ); + * } + * } + * } + */ + +/** + * (Optional) The validation function. + * @name CKEDITOR.dialog.definition.fileButton.prototype.validate + * @field + * @type Function + * @example + */ + +/** + * The label of the UI element. + * @name CKEDITOR.dialog.definition.fileButton.prototype.label + * @type String + * @field + * @example + */ + +/** + * The instruction for CKEditor how to deal with file upload. + * By default, the file and fileButton elements will not work "as expected" if this attribute is not set. + * @name CKEDITOR.dialog.definition.fileButton.prototype.filebrowser + * @type String|Object + * @field + * @example + * // Update field with id 'txtUrl' in the 'tab1' tab when file is uploaded. + * filebrowser : 'tab1:txtUrl' + * + * // Call custom onSelect function when file is successfully uploaded. + * filebrowser : { + * onSelect : function( fileUrl, data ) { + * alert( 'Successfully uploaded: ' + fileUrl ); + * } + * } + */ + +/** + * An array that contains pageId and elementId of the file upload input element for which this button is created. + * @name CKEDITOR.dialog.definition.fileButton.prototype.for + * @type String + * @field + * @example + * [ pageId, elementId ] + */ + +// ----- html ----- + +/** + * The definition of a raw HTML element. + *
+ * This class is not really part of the API. It just illustrates the properties + * that developers can use to define and create elements made from raw HTML code. + *

Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.html} object and can be accessed with {@link CKEDITOR.dialog#getContentElement}. + *
+ * For a complete example of dialog definition, please check {@link CKEDITOR.dialog.add}.
+ * To access HTML elements use {@link CKEDITOR.dom.document#getById} + * @name CKEDITOR.dialog.definition.html + * @extends CKEDITOR.dialog.definition.uiElement + * @constructor + * @example + * // There is no constructor for this class, the user just has to define an + * // object with the appropriate properties. + * + * // Example 1: + * { + * type : 'html', + * html : '<h3>This is some sample HTML content.</h3>' + * } + * @example + * // Example 2: + * // Complete sample with document.getById() call when the "Ok" button is clicked. + * var dialogDefinition = + * { + * title : 'Sample dialog', + * minWidth : 300, + * minHeight : 200, + * onOk : function() { + * // "this" is now a CKEDITOR.dialog object. + * var document = this.getElement().getDocument(); + * // document = CKEDITOR.dom.document + * var element = document.getById( 'myDiv' ); + * if ( element ) + * alert( element.getHtml() ); + * }, + * contents : [ + * { + * id : 'tab1', + * label : '', + * title : '', + * elements : + * [ + * { + * type : 'html', + * html : '<div id="myDiv">Sample <b>text</b>.</div><div id="otherId">Another div.</div>' + * }, + * ] + * } + * ], + * buttons : [ CKEDITOR.dialog.cancelButton, CKEDITOR.dialog.okButton ] + * }; + */ + +/** + * (Required) HTML code of this element. + * @name CKEDITOR.dialog.definition.html.prototype.html + * @type String + * @field + * @example + */ + +// ----- radio ------ + +/** + * The definition of a radio group. + *
+ * This class is not really part of the API. It just illustrates the properties + * that developers can use to define and create groups of radio buttons. + *

Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.radio} object and can be accessed with {@link CKEDITOR.dialog#getContentElement}. + *
+ * For a complete example of dialog definition, please check {@link CKEDITOR.dialog.add}. + * @name CKEDITOR.dialog.definition.radio + * @extends CKEDITOR.dialog.definition.labeledElement + * @constructor + * @example + * // There is no constructor for this class, the user just has to define an + * // object with the appropriate properties. + * + * // Example: + * { + * type : 'radio', + * id : 'country', + * label : 'Which country is bigger', + * items : [ [ 'France', 'FR' ], [ 'Germany', 'DE' ] ] , + * style : 'color:green', + * 'default' : 'DE', + * onClick : function() { + * // this = CKEDITOR.ui.dialog.radio + * alert( 'Current value: ' + this.getValue() ); + * } + * } + */ + +/** + * The default value. + * @name CKEDITOR.dialog.definition.radio.prototype.default + * @type String + * @field + * @example + */ + +/** + * (Optional) The validation function. + * @name CKEDITOR.dialog.definition.radio.prototype.validate + * @field + * @type Function + * @example + */ + +/** + * An array of options. Each option is a 1- or 2-item array of format [ 'Description', 'Value' ]. If 'Value' is missing, then the value would be assumed to be the same as the description. + * @name CKEDITOR.dialog.definition.radio.prototype.items + * @field + * @type Array + * @example + */ + +// ----- selectElement ------ + +/** + * The definition of a select element. + *
+ * This class is not really part of the API. It just illustrates the properties + * that developers can use to define and create select elements. + *

Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.select} object and can be accessed with {@link CKEDITOR.dialog#getContentElement}. + *
+ * For a complete example of dialog definition, please check {@link CKEDITOR.dialog.add}. + * @name CKEDITOR.dialog.definition.select + * @extends CKEDITOR.dialog.definition.labeledElement + * @constructor + * @example + * // There is no constructor for this class, the user just has to define an + * // object with the appropriate properties. + * + * // Example: + * { + * type : 'select', + * id : 'sport', + * label : 'Select your favourite sport', + * items : [ [ 'Basketball' ], [ 'Baseball' ], [ 'Hockey' ], [ 'Football' ] ], + * 'default' : 'Football', + * onChange : function( api ) { + * // this = CKEDITOR.ui.dialog.select + * alert( 'Current value: ' + this.getValue() ); + * } + * } + */ + +/** + * The default value. + * @name CKEDITOR.dialog.definition.select.prototype.default + * @type String + * @field + * @example + */ + +/** + * (Optional) The validation function. + * @name CKEDITOR.dialog.definition.select.prototype.validate + * @field + * @type Function + * @example + */ + +/** + * An array of options. Each option is a 1- or 2-item array of format [ 'Description', 'Value' ]. If 'Value' is missing, then the value would be assumed to be the same as the description. + * @name CKEDITOR.dialog.definition.select.prototype.items + * @field + * @type Array + * @example + */ + +/** + * (Optional) Set this to true if you'd like to have a multiple-choice select box. + * @name CKEDITOR.dialog.definition.select.prototype.multiple + * @type Boolean + * @field + * @example + * @default false + */ + +/** + * (Optional) The number of items to display in the select box. + * @name CKEDITOR.dialog.definition.select.prototype.size + * @type Number + * @field + * @example + */ + +// ----- textInput ----- + +/** + * The definition of a text field (single line). + *
+ * This class is not really part of the API. It just illustrates the properties + * that developers can use to define and create text fields. + *

Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.textInput} object and can be accessed with {@link CKEDITOR.dialog#getContentElement}. + *
+ * For a complete example of dialog definition, please check {@link CKEDITOR.dialog.add}. + * @name CKEDITOR.dialog.definition.textInput + * @extends CKEDITOR.dialog.definition.labeledElement + * @constructor + * @example + * // There is no constructor for this class, the user just has to define an + * // object with the appropriate properties. + * + * { + * type : 'text', + * id : 'name', + * label : 'Your name', + * 'default' : '', + * validate : function() { + * if ( !this.getValue() ) + * { + * api.openMsgDialog( '', 'Name cannot be empty.' ); + * return false; + * } + * } + * } + */ + +/** + * The default value. + * @name CKEDITOR.dialog.definition.textInput.prototype.default + * @type String + * @field + * @example + */ + +/** + * (Optional) The maximum length. + * @name CKEDITOR.dialog.definition.textInput.prototype.maxLength + * @type Number + * @field + * @example + */ + +/** + * (Optional) The size of the input field. + * @name CKEDITOR.dialog.definition.textInput.prototype.size + * @type Number + * @field + * @example + */ + +/** + * (Optional) The validation function. + * @name CKEDITOR.dialog.definition.textInput.prototype.validate + * @field + * @type Function + * @example + */ + +// ----- textarea ------ + +/** + * The definition of a text field (multiple lines). + *
+ * This class is not really part of the API. It just illustrates the properties + * that developers can use to define and create textarea. + *

Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.textarea} object and can be accessed with {@link CKEDITOR.dialog#getContentElement}. + *
+ * For a complete example of dialog definition, please check {@link CKEDITOR.dialog.add}. + * @name CKEDITOR.dialog.definition.textarea + * @extends CKEDITOR.dialog.definition.labeledElement + * @constructor + * @example + * // There is no constructor for this class, the user just has to define an + * // object with the appropriate properties. + * + * // Example: + * { + * type : 'textarea', + * id : 'message', + * label : 'Your comment', + * 'default' : '', + * validate : function() { + * if ( this.getValue().length < 5 ) + * { + * api.openMsgDialog( 'The comment is too short.' ); + * return false; + * } + * } + * } + */ + +/** + * The number of rows. + * @name CKEDITOR.dialog.definition.textarea.prototype.rows + * @type Number + * @field + * @example + */ + +/** + * The number of columns. + * @name CKEDITOR.dialog.definition.textarea.prototype.cols + * @type Number + * @field + * @example + */ + +/** + * (Optional) The validation function. + * @name CKEDITOR.dialog.definition.textarea.prototype.validate + * @field + * @type Function + * @example + */ + +/** + * The default value. + * @name CKEDITOR.dialog.definition.textarea.prototype.default + * @type String + * @field + * @example + */ Index: openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/dialog/plugin.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/dialog/Attic/plugin.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/dialog/plugin.js 13 Feb 2012 18:53:11 -0000 1.1 @@ -0,0 +1,3308 @@ +/* +Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +/** + * @fileOverview The floating dialog plugin. + */ + +/** + * No resize for this dialog. + * @constant + */ +CKEDITOR.DIALOG_RESIZE_NONE = 0; + +/** + * Only allow horizontal resizing for this dialog, disable vertical resizing. + * @constant + */ +CKEDITOR.DIALOG_RESIZE_WIDTH = 1; + +/** + * Only allow vertical resizing for this dialog, disable horizontal resizing. + * @constant + */ +CKEDITOR.DIALOG_RESIZE_HEIGHT = 2; + +/* + * Allow the dialog to be resized in both directions. + * @constant + */ +CKEDITOR.DIALOG_RESIZE_BOTH = 3; + +(function() +{ + var cssLength = CKEDITOR.tools.cssLength; + function isTabVisible( tabId ) + { + return !!this._.tabs[ tabId ][ 0 ].$.offsetHeight; + } + + function getPreviousVisibleTab() + { + var tabId = this._.currentTabId, + length = this._.tabIdList.length, + tabIndex = CKEDITOR.tools.indexOf( this._.tabIdList, tabId ) + length; + + for ( var i = tabIndex - 1 ; i > tabIndex - length ; i-- ) + { + if ( isTabVisible.call( this, this._.tabIdList[ i % length ] ) ) + return this._.tabIdList[ i % length ]; + } + + return null; + } + + function getNextVisibleTab() + { + var tabId = this._.currentTabId, + length = this._.tabIdList.length, + tabIndex = CKEDITOR.tools.indexOf( this._.tabIdList, tabId ); + + for ( var i = tabIndex + 1 ; i < tabIndex + length ; i++ ) + { + if ( isTabVisible.call( this, this._.tabIdList[ i % length ] ) ) + return this._.tabIdList[ i % length ]; + } + + return null; + } + + + function clearOrRecoverTextInputValue( container, isRecover ) + { + var inputs = container.$.getElementsByTagName( 'input' ); + for ( var i = 0, length = inputs.length; i < length ; i++ ) + { + var item = new CKEDITOR.dom.element( inputs[ i ] ); + + if ( item.getAttribute( 'type' ).toLowerCase() == 'text' ) + { + if ( isRecover ) + { + item.setAttribute( 'value', item.getCustomData( 'fake_value' ) || '' ); + item.removeCustomData( 'fake_value' ); + } + else + { + item.setCustomData( 'fake_value', item.getAttribute( 'value' ) ); + item.setAttribute( 'value', '' ); + } + } + } + } + + // Handle dialog element validation state UI changes. + function handleFieldValidated( isValid, msg ) + { + var input = this.getInputElement(); + if ( input ) + { + isValid ? input.removeAttribute( 'aria-invalid' ) + : input.setAttribute( 'aria-invalid', true ); + } + + if ( !isValid ) + { + if ( this.select ) + this.select(); + else + this.focus(); + } + + msg && alert( msg ); + + this.fire( 'validated', { valid : isValid, msg : msg } ); + } + + function resetField() + { + var input = this.getInputElement(); + input && input.removeAttribute( 'aria-invalid' ); + } + + + /** + * This is the base class for runtime dialog objects. An instance of this + * class represents a single named dialog for a single editor instance. + * @param {Object} editor The editor which created the dialog. + * @param {String} dialogName The dialog's registered name. + * @constructor + * @example + * var dialogObj = new CKEDITOR.dialog( editor, 'smiley' ); + */ + CKEDITOR.dialog = function( editor, dialogName ) + { + // Load the dialog definition. + var definition = CKEDITOR.dialog._.dialogDefinitions[ dialogName ], + defaultDefinition = CKEDITOR.tools.clone( defaultDialogDefinition ), + buttonsOrder = editor.config.dialog_buttonsOrder || 'OS', + dir = editor.lang.dir; + + if ( ( buttonsOrder == 'OS' && CKEDITOR.env.mac ) || // The buttons in MacOS Apps are in reverse order (#4750) + ( buttonsOrder == 'rtl' && dir == 'ltr' ) || + ( buttonsOrder == 'ltr' && dir == 'rtl' ) ) + defaultDefinition.buttons.reverse(); + + + // Completes the definition with the default values. + definition = CKEDITOR.tools.extend( definition( editor ), defaultDefinition ); + + // Clone a functionally independent copy for this dialog. + definition = CKEDITOR.tools.clone( definition ); + + // Create a complex definition object, extending it with the API + // functions. + definition = new definitionObject( this, definition ); + + var doc = CKEDITOR.document; + + var themeBuilt = editor.theme.buildDialog( editor ); + + // Initialize some basic parameters. + this._ = + { + editor : editor, + element : themeBuilt.element, + name : dialogName, + contentSize : { width : 0, height : 0 }, + size : { width : 0, height : 0 }, + contents : {}, + buttons : {}, + accessKeyMap : {}, + + // Initialize the tab and page map. + tabs : {}, + tabIdList : [], + currentTabId : null, + currentTabIndex : null, + pageCount : 0, + lastTab : null, + tabBarMode : false, + + // Initialize the tab order array for input widgets. + focusList : [], + currentFocusIndex : 0, + hasFocus : false + }; + + this.parts = themeBuilt.parts; + + CKEDITOR.tools.setTimeout( function() + { + editor.fire( 'ariaWidget', this.parts.contents ); + }, + 0, this ); + + // Set the startup styles for the dialog, avoiding it enlarging the + // page size on the dialog creation. + var startStyles = { + position : CKEDITOR.env.ie6Compat ? 'absolute' : 'fixed', + top : 0, + visibility : 'hidden' + }; + + startStyles[ dir == 'rtl' ? 'right' : 'left' ] = 0; + this.parts.dialog.setStyles( startStyles ); + + + // Call the CKEDITOR.event constructor to initialize this instance. + CKEDITOR.event.call( this ); + + // Fire the "dialogDefinition" event, making it possible to customize + // the dialog definition. + this.definition = definition = CKEDITOR.fire( 'dialogDefinition', + { + name : dialogName, + definition : definition + } + , editor ).definition; + + var tabsToRemove = {}; + // Cache tabs that should be removed. + if ( !( 'removeDialogTabs' in editor._ ) && editor.config.removeDialogTabs ) + { + var removeContents = editor.config.removeDialogTabs.split( ';' ); + + for ( i = 0; i < removeContents.length; i++ ) + { + var parts = removeContents[ i ].split( ':' ); + if ( parts.length == 2 ) + { + var removeDialogName = parts[ 0 ]; + if ( !tabsToRemove[ removeDialogName ] ) + tabsToRemove[ removeDialogName ] = []; + tabsToRemove[ removeDialogName ].push( parts[ 1 ] ); + } + } + editor._.removeDialogTabs = tabsToRemove; + } + + // Remove tabs of this dialog. + if ( editor._.removeDialogTabs && ( tabsToRemove = editor._.removeDialogTabs[ dialogName ] ) ) + { + for ( i = 0; i < tabsToRemove.length; i++ ) + definition.removeContents( tabsToRemove[ i ] ); + } + + // Initialize load, show, hide, ok and cancel events. + if ( definition.onLoad ) + this.on( 'load', definition.onLoad ); + + if ( definition.onShow ) + this.on( 'show', definition.onShow ); + + if ( definition.onHide ) + this.on( 'hide', definition.onHide ); + + if ( definition.onOk ) + { + this.on( 'ok', function( evt ) + { + // Dialog confirm might probably introduce content changes (#5415). + editor.fire( 'saveSnapshot' ); + setTimeout( function () { editor.fire( 'saveSnapshot' ); }, 0 ); + if ( definition.onOk.call( this, evt ) === false ) + evt.data.hide = false; + }); + } + + if ( definition.onCancel ) + { + this.on( 'cancel', function( evt ) + { + if ( definition.onCancel.call( this, evt ) === false ) + evt.data.hide = false; + }); + } + + var me = this; + + // Iterates over all items inside all content in the dialog, calling a + // function for each of them. + var iterContents = function( func ) + { + var contents = me._.contents, + stop = false; + + for ( var i in contents ) + { + for ( var j in contents[i] ) + { + stop = func.call( this, contents[i][j] ); + if ( stop ) + return; + } + } + }; + + this.on( 'ok', function( evt ) + { + iterContents( function( item ) + { + if ( item.validate ) + { + var retval = item.validate( this ), + invalid = typeof ( retval ) == 'string' || retval === false; + + if ( invalid ) + { + evt.data.hide = false; + evt.stop(); + } + + handleFieldValidated.call( item, !invalid, typeof retval == 'string' ? retval : undefined ); + return invalid; + } + }); + }, this, null, 0 ); + + this.on( 'cancel', function( evt ) + { + iterContents( function( item ) + { + if ( item.isChanged() ) + { + if ( !confirm( editor.lang.common.confirmCancel ) ) + evt.data.hide = false; + return true; + } + }); + }, this, null, 0 ); + + this.parts.close.on( 'click', function( evt ) + { + if ( this.fire( 'cancel', { hide : true } ).hide !== false ) + this.hide(); + evt.data.preventDefault(); + }, this ); + + // Sort focus list according to tab order definitions. + function setupFocus() + { + var focusList = me._.focusList; + focusList.sort( function( a, b ) + { + // Mimics browser tab order logics; + if ( a.tabIndex != b.tabIndex ) + return b.tabIndex - a.tabIndex; + // Sort is not stable in some browsers, + // fall-back the comparator to 'focusIndex'; + else + return a.focusIndex - b.focusIndex; + }); + + var size = focusList.length; + for ( var i = 0; i < size; i++ ) + focusList[ i ].focusIndex = i; + } + + function changeFocus( forward ) + { + var focusList = me._.focusList, + offset = forward ? 1 : -1; + if ( focusList.length < 1 ) + return; + + var current = me._.currentFocusIndex; + + // Trigger the 'blur' event of any input element before anything, + // since certain UI updates may depend on it. + try + { + focusList[ current ].getInputElement().$.blur(); + } + catch( e ){} + + var startIndex = ( current + offset + focusList.length ) % focusList.length, + currentIndex = startIndex; + while ( !focusList[ currentIndex ].isFocusable() ) + { + currentIndex = ( currentIndex + offset + focusList.length ) % focusList.length; + if ( currentIndex == startIndex ) + break; + } + focusList[ currentIndex ].focus(); + + // Select whole field content. + if ( focusList[ currentIndex ].type == 'text' ) + focusList[ currentIndex ].select(); + } + + this.changeFocus = changeFocus; + + var processed; + + function focusKeydownHandler( evt ) + { + // If I'm not the top dialog, ignore. + if ( me != CKEDITOR.dialog._.currentTop ) + return; + + var keystroke = evt.data.getKeystroke(), + rtl = editor.lang.dir == 'rtl'; + + processed = 0; + if ( keystroke == 9 || keystroke == CKEDITOR.SHIFT + 9 ) + { + var shiftPressed = ( keystroke == CKEDITOR.SHIFT + 9 ); + + // Handling Tab and Shift-Tab. + if ( me._.tabBarMode ) + { + // Change tabs. + var nextId = shiftPressed ? getPreviousVisibleTab.call( me ) : getNextVisibleTab.call( me ); + me.selectPage( nextId ); + me._.tabs[ nextId ][ 0 ].focus(); + } + else + { + // Change the focus of inputs. + changeFocus( !shiftPressed ); + } + + processed = 1; + } + else if ( keystroke == CKEDITOR.ALT + 121 && !me._.tabBarMode && me.getPageCount() > 1 ) + { + // Alt-F10 puts focus into the current tab item in the tab bar. + me._.tabBarMode = true; + me._.tabs[ me._.currentTabId ][ 0 ].focus(); + processed = 1; + } + else if ( ( keystroke == 37 || keystroke == 39 ) && me._.tabBarMode ) + { + // Arrow keys - used for changing tabs. + nextId = ( keystroke == ( rtl ? 39 : 37 ) ? getPreviousVisibleTab.call( me ) : getNextVisibleTab.call( me ) ); + me.selectPage( nextId ); + me._.tabs[ nextId ][ 0 ].focus(); + processed = 1; + } + else if ( ( keystroke == 13 || keystroke == 32 ) && me._.tabBarMode ) + { + this.selectPage( this._.currentTabId ); + this._.tabBarMode = false; + this._.currentFocusIndex = -1; + changeFocus( true ); + processed = 1; + } + + if ( processed ) + { + evt.stop(); + evt.data.preventDefault(); + } + } + + function focusKeyPressHandler( evt ) + { + processed && evt.data.preventDefault(); + } + + var dialogElement = this._.element; + // Add the dialog keyboard handlers. + this.on( 'show', function() + { + dialogElement.on( 'keydown', focusKeydownHandler, this, null, 0 ); + // Some browsers instead, don't cancel key events in the keydown, but in the + // keypress. So we must do a longer trip in those cases. (#4531) + if ( CKEDITOR.env.opera || ( CKEDITOR.env.gecko && CKEDITOR.env.mac ) ) + dialogElement.on( 'keypress', focusKeyPressHandler, this ); + + } ); + this.on( 'hide', function() + { + dialogElement.removeListener( 'keydown', focusKeydownHandler ); + if ( CKEDITOR.env.opera || ( CKEDITOR.env.gecko && CKEDITOR.env.mac ) ) + dialogElement.removeListener( 'keypress', focusKeyPressHandler ); + + // Reset fields state when closing dialog. + iterContents( function( item ) { resetField.apply( item ); } ); + } ); + this.on( 'iframeAdded', function( evt ) + { + var doc = new CKEDITOR.dom.document( evt.data.iframe.$.contentWindow.document ); + doc.on( 'keydown', focusKeydownHandler, this, null, 0 ); + } ); + + // Auto-focus logic in dialog. + this.on( 'show', function() + { + // Setup tabIndex on showing the dialog instead of on loading + // to allow dynamic tab order happen in dialog definition. + setupFocus(); + + if ( editor.config.dialog_startupFocusTab + && me._.pageCount > 1 ) + { + me._.tabBarMode = true; + me._.tabs[ me._.currentTabId ][ 0 ].focus(); + } + else if ( !this._.hasFocus ) + { + this._.currentFocusIndex = -1; + + // Decide where to put the initial focus. + if ( definition.onFocus ) + { + var initialFocus = definition.onFocus.call( this ); + // Focus the field that the user specified. + initialFocus && initialFocus.focus(); + } + // Focus the first field in layout order. + else + changeFocus( true ); + + /* + * IE BUG: If the initial focus went into a non-text element (e.g. button), + * then IE would still leave the caret inside the editing area. + */ + if ( this._.editor.mode == 'wysiwyg' && CKEDITOR.env.ie ) + { + var $selection = editor.document.$.selection, + $range = $selection.createRange(); + + if ( $range ) + { + if ( $range.parentElement && $range.parentElement().ownerDocument == editor.document.$ + || $range.item && $range.item( 0 ).ownerDocument == editor.document.$ ) + { + var $myRange = document.body.createTextRange(); + $myRange.moveToElementText( this.getElement().getFirst().$ ); + $myRange.collapse( true ); + $myRange.select(); + } + } + } + } + }, this, null, 0xffffffff ); + + // IE6 BUG: Text fields and text areas are only half-rendered the first time the dialog appears in IE6 (#2661). + // This is still needed after [2708] and [2709] because text fields in hidden TR tags are still broken. + if ( CKEDITOR.env.ie6Compat ) + { + this.on( 'load', function( evt ) + { + var outer = this.getElement(), + inner = outer.getFirst(); + inner.remove(); + inner.appendTo( outer ); + }, this ); + } + + initDragAndDrop( this ); + initResizeHandles( this ); + + // Insert the title. + ( new CKEDITOR.dom.text( definition.title, CKEDITOR.document ) ).appendTo( this.parts.title ); + + // Insert the tabs and contents. + for ( var i = 0 ; i < definition.contents.length ; i++ ) + { + var page = definition.contents[i]; + page && this.addPage( page ); + } + + this.parts[ 'tabs' ].on( 'click', function( evt ) + { + var target = evt.data.getTarget(); + // If we aren't inside a tab, bail out. + if ( target.hasClass( 'cke_dialog_tab' ) ) + { + // Get the ID of the tab, without the 'cke_' prefix and the unique number suffix. + var id = target.$.id; + this.selectPage( id.substring( 4, id.lastIndexOf( '_' ) ) ); + + if ( this._.tabBarMode ) + { + this._.tabBarMode = false; + this._.currentFocusIndex = -1; + changeFocus( true ); + } + evt.data.preventDefault(); + } + }, this ); + + // Insert buttons. + var buttonsHtml = [], + buttons = CKEDITOR.dialog._.uiElementBuilders.hbox.build( this, + { + type : 'hbox', + className : 'cke_dialog_footer_buttons', + widths : [], + children : definition.buttons + }, buttonsHtml ).getChild(); + this.parts.footer.setHtml( buttonsHtml.join( '' ) ); + + for ( i = 0 ; i < buttons.length ; i++ ) + this._.buttons[ buttons[i].id ] = buttons[i]; + }; + + // Focusable interface. Use it via dialog.addFocusable. + function Focusable( dialog, element, index ) + { + this.element = element; + this.focusIndex = index; + // TODO: support tabIndex for focusables. + this.tabIndex = 0; + this.isFocusable = function() + { + return !element.getAttribute( 'disabled' ) && element.isVisible(); + }; + this.focus = function() + { + dialog._.currentFocusIndex = this.focusIndex; + this.element.focus(); + }; + // Bind events + element.on( 'keydown', function( e ) + { + if ( e.data.getKeystroke() in { 32:1, 13:1 } ) + this.fire( 'click' ); + } ); + element.on( 'focus', function() + { + this.fire( 'mouseover' ); + } ); + element.on( 'blur', function() + { + this.fire( 'mouseout' ); + } ); + } + + CKEDITOR.dialog.prototype = + { + destroy : function() + { + this.hide(); + this._.element.remove(); + }, + + /** + * Resizes the dialog. + * @param {Number} width The width of the dialog in pixels. + * @param {Number} height The height of the dialog in pixels. + * @function + * @example + * dialogObj.resize( 800, 640 ); + */ + resize : (function() + { + return function( width, height ) + { + if ( this._.contentSize && this._.contentSize.width == width && this._.contentSize.height == height ) + return; + + CKEDITOR.dialog.fire( 'resize', + { + dialog : this, + skin : this._.editor.skinName, + width : width, + height : height + }, this._.editor ); + + this.fire( 'resize', + { + skin : this._.editor.skinName, + width : width, + height : height + }, this._.editor ); + + // Update dialog position when dimension get changed in RTL. + if ( this._.editor.lang.dir == 'rtl' && this._.position ) + this._.position.x = CKEDITOR.document.getWindow().getViewPaneSize().width - + this._.contentSize.width - parseInt( this._.element.getFirst().getStyle( 'right' ), 10 ); + + this._.contentSize = { width : width, height : height }; + }; + })(), + + /** + * Gets the current size of the dialog in pixels. + * @returns {Object} An object with "width" and "height" properties. + * @example + * var width = dialogObj.getSize().width; + */ + getSize : function() + { + var element = this._.element.getFirst(); + return { width : element.$.offsetWidth || 0, height : element.$.offsetHeight || 0}; + }, + + /** + * Moves the dialog to an (x, y) coordinate relative to the window. + * @function + * @param {Number} x The target x-coordinate. + * @param {Number} y The target y-coordinate. + * @param {Boolean} save Flag indicate whether the dialog position should be remembered on next open up. + * @example + * dialogObj.move( 10, 40 ); + */ + move : (function() + { + var isFixed; + return function( x, y, save ) + { + // The dialog may be fixed positioned or absolute positioned. Ask the + // browser what is the current situation first. + var element = this._.element.getFirst(), + rtl = this._.editor.lang.dir == 'rtl'; + + if ( isFixed === undefined ) + isFixed = element.getComputedStyle( 'position' ) == 'fixed'; + + if ( isFixed && this._.position && this._.position.x == x && this._.position.y == y ) + return; + + // Save the current position. + this._.position = { x : x, y : y }; + + // If not fixed positioned, add scroll position to the coordinates. + if ( !isFixed ) + { + var scrollPosition = CKEDITOR.document.getWindow().getScrollPosition(); + x += scrollPosition.x; + y += scrollPosition.y; + } + + // Translate coordinate for RTL. + if ( rtl ) + { + var dialogSize = this.getSize(), + viewPaneSize = CKEDITOR.document.getWindow().getViewPaneSize(); + x = viewPaneSize.width - dialogSize.width - x; + } + + var styles = { 'top' : ( y > 0 ? y : 0 ) + 'px' }; + styles[ rtl ? 'right' : 'left' ] = ( x > 0 ? x : 0 ) + 'px'; + + element.setStyles( styles ); + + save && ( this._.moved = 1 ); + }; + })(), + + /** + * Gets the dialog's position in the window. + * @returns {Object} An object with "x" and "y" properties. + * @example + * var dialogX = dialogObj.getPosition().x; + */ + getPosition : function(){ return CKEDITOR.tools.extend( {}, this._.position ); }, + + /** + * Shows the dialog box. + * @example + * dialogObj.show(); + */ + show : function() + { + // Insert the dialog's element to the root document. + var element = this._.element; + var definition = this.definition; + if ( !( element.getParent() && element.getParent().equals( CKEDITOR.document.getBody() ) ) ) + element.appendTo( CKEDITOR.document.getBody() ); + else + element.setStyle( 'display', 'block' ); + + // FIREFOX BUG: Fix vanishing caret for Firefox 2 or Gecko 1.8. + if ( CKEDITOR.env.gecko && CKEDITOR.env.version < 10900 ) + { + var dialogElement = this.parts.dialog; + dialogElement.setStyle( 'position', 'absolute' ); + setTimeout( function() + { + dialogElement.setStyle( 'position', 'fixed' ); + }, 0 ); + } + + + // First, set the dialog to an appropriate size. + this.resize( this._.contentSize && this._.contentSize.width || definition.width || definition.minWidth, + this._.contentSize && this._.contentSize.height || definition.height || definition.minHeight ); + + // Reset all inputs back to their default value. + this.reset(); + + // Select the first tab by default. + this.selectPage( this.definition.contents[0].id ); + + // Set z-index. + if ( CKEDITOR.dialog._.currentZIndex === null ) + CKEDITOR.dialog._.currentZIndex = this._.editor.config.baseFloatZIndex; + this._.element.getFirst().setStyle( 'z-index', CKEDITOR.dialog._.currentZIndex += 10 ); + + // Maintain the dialog ordering and dialog cover. + // Also register key handlers if first dialog. + if ( CKEDITOR.dialog._.currentTop === null ) + { + CKEDITOR.dialog._.currentTop = this; + this._.parentDialog = null; + showCover( this._.editor ); + + element.on( 'keydown', accessKeyDownHandler ); + element.on( CKEDITOR.env.opera ? 'keypress' : 'keyup', accessKeyUpHandler ); + + // Prevent some keys from bubbling up. (#4269) + for ( var event in { keyup :1, keydown :1, keypress :1 } ) + element.on( event, preventKeyBubbling ); + } + else + { + this._.parentDialog = CKEDITOR.dialog._.currentTop; + var parentElement = this._.parentDialog.getElement().getFirst(); + parentElement.$.style.zIndex -= Math.floor( this._.editor.config.baseFloatZIndex / 2 ); + CKEDITOR.dialog._.currentTop = this; + } + + // Register the Esc hotkeys. + registerAccessKey( this, this, '\x1b', null, function() + { + this.getButton( 'cancel' ) && this.getButton( 'cancel' ).click(); + } ); + + // Reset the hasFocus state. + this._.hasFocus = false; + + CKEDITOR.tools.setTimeout( function() + { + this.layout(); + this.parts.dialog.setStyle( 'visibility', '' ); + + // Execute onLoad for the first show. + this.fireOnce( 'load', {} ); + CKEDITOR.ui.fire( 'ready', this ); + + this.fire( 'show', {} ); + this._.editor.fire( 'dialogShow', this ); + + // Save the initial values of the dialog. + this.foreach( function( contentObj ) { contentObj.setInitValue && contentObj.setInitValue(); } ); + + }, + 100, this ); + }, + + /** + * Rearrange the dialog to its previous position or the middle of the window. + * @since 3.5 + */ + layout : function() + { + var viewSize = CKEDITOR.document.getWindow().getViewPaneSize(), + dialogSize = this.getSize(); + + this.move( this._.moved ? this._.position.x : ( viewSize.width - dialogSize.width ) / 2, + this._.moved ? this._.position.y : ( viewSize.height - dialogSize.height ) / 2 ); + }, + + /** + * Executes a function for each UI element. + * @param {Function} fn Function to execute for each UI element. + * @returns {CKEDITOR.dialog} The current dialog object. + */ + foreach : function( fn ) + { + for ( var i in this._.contents ) + { + for ( var j in this._.contents[i] ) + fn.call( this, this._.contents[i][j] ); + } + return this; + }, + + /** + * Resets all input values in the dialog. + * @example + * dialogObj.reset(); + * @returns {CKEDITOR.dialog} The current dialog object. + */ + reset : (function() + { + var fn = function( widget ){ if ( widget.reset ) widget.reset( 1 ); }; + return function(){ this.foreach( fn ); return this; }; + })(), + + + /** + * Calls the {@link CKEDITOR.dialog.definition.uiElement#setup} method of each of the UI elements, with the arguments passed through it. + * It is usually being called when the dialog is opened, to put the initial value inside the field. + * @example + * dialogObj.setupContent(); + * @example + * var timestamp = ( new Date() ).valueOf(); + * dialogObj.setupContent( timestamp ); + */ + setupContent : function() + { + var args = arguments; + this.foreach( function( widget ) + { + if ( widget.setup ) + widget.setup.apply( widget, args ); + }); + }, + + /** + * Calls the {@link CKEDITOR.dialog.definition.uiElement#commit} method of each of the UI elements, with the arguments passed through it. + * It is usually being called when the user confirms the dialog, to process the values. + * @example + * dialogObj.commitContent(); + * @example + * var timestamp = ( new Date() ).valueOf(); + * dialogObj.commitContent( timestamp ); + */ + commitContent : function() + { + var args = arguments; + this.foreach( function( widget ) + { + // Make sure IE triggers "change" event on last focused input before closing the dialog. (#7915) + if ( CKEDITOR.env.ie && this._.currentFocusIndex == widget.focusIndex ) + widget.getInputElement().$.blur(); + + if ( widget.commit ) + widget.commit.apply( widget, args ); + }); + }, + + /** + * Hides the dialog box. + * @example + * dialogObj.hide(); + */ + hide : function() + { + if ( !this.parts.dialog.isVisible() ) + return; + + this.fire( 'hide', {} ); + this._.editor.fire( 'dialogHide', this ); + var element = this._.element; + element.setStyle( 'display', 'none' ); + this.parts.dialog.setStyle( 'visibility', 'hidden' ); + // Unregister all access keys associated with this dialog. + unregisterAccessKey( this ); + + // Close any child(top) dialogs first. + while( CKEDITOR.dialog._.currentTop != this ) + CKEDITOR.dialog._.currentTop.hide(); + + // Maintain dialog ordering and remove cover if needed. + if ( !this._.parentDialog ) + hideCover(); + else + { + var parentElement = this._.parentDialog.getElement().getFirst(); + parentElement.setStyle( 'z-index', parseInt( parentElement.$.style.zIndex, 10 ) + Math.floor( this._.editor.config.baseFloatZIndex / 2 ) ); + } + CKEDITOR.dialog._.currentTop = this._.parentDialog; + + // Deduct or clear the z-index. + if ( !this._.parentDialog ) + { + CKEDITOR.dialog._.currentZIndex = null; + + // Remove access key handlers. + element.removeListener( 'keydown', accessKeyDownHandler ); + element.removeListener( CKEDITOR.env.opera ? 'keypress' : 'keyup', accessKeyUpHandler ); + + // Remove bubbling-prevention handler. (#4269) + for ( var event in { keyup :1, keydown :1, keypress :1 } ) + element.removeListener( event, preventKeyBubbling ); + + var editor = this._.editor; + editor.focus(); + + if ( editor.mode == 'wysiwyg' && CKEDITOR.env.ie ) + { + var selection = editor.getSelection(); + selection && selection.unlock( true ); + } + } + else + CKEDITOR.dialog._.currentZIndex -= 10; + + delete this._.parentDialog; + // Reset the initial values of the dialog. + this.foreach( function( contentObj ) { contentObj.resetInitValue && contentObj.resetInitValue(); } ); + }, + + /** + * Adds a tabbed page into the dialog. + * @param {Object} contents Content definition. + * @example + */ + addPage : function( contents ) + { + var pageHtml = [], + titleHtml = contents.label ? ' title="' + CKEDITOR.tools.htmlEncode( contents.label ) + '"' : '', + elements = contents.elements, + vbox = CKEDITOR.dialog._.uiElementBuilders.vbox.build( this, + { + type : 'vbox', + className : 'cke_dialog_page_contents', + children : contents.elements, + expand : !!contents.expand, + padding : contents.padding, + style : contents.style || 'width: 100%;height:100%' + }, pageHtml ); + + // Create the HTML for the tab and the content block. + var page = CKEDITOR.dom.element.createFromHtml( pageHtml.join( '' ) ); + page.setAttribute( 'role', 'tabpanel' ); + + var env = CKEDITOR.env; + var tabId = 'cke_' + contents.id + '_' + CKEDITOR.tools.getNextNumber(), + tab = CKEDITOR.dom.element.createFromHtml( [ + ' 0 ? ' cke_last' : 'cke_first' ), + titleHtml, + ( !!contents.hidden ? ' style="display:none"' : '' ), + ' id="', tabId, '"', + env.gecko && env.version >= 10900 && !env.hc ? '' : ' href="javascript:void(0)"', + ' tabIndex="-1"', + ' hidefocus="true"', + ' role="tab">', + contents.label, + '' + ].join( '' ) ); + + page.setAttribute( 'aria-labelledby', tabId ); + + // Take records for the tabs and elements created. + this._.tabs[ contents.id ] = [ tab, page ]; + this._.tabIdList.push( contents.id ); + !contents.hidden && this._.pageCount++; + this._.lastTab = tab; + this.updateStyle(); + + var contentMap = this._.contents[ contents.id ] = {}, + cursor, + children = vbox.getChild(); + + while ( ( cursor = children.shift() ) ) + { + contentMap[ cursor.id ] = cursor; + if ( typeof( cursor.getChild ) == 'function' ) + children.push.apply( children, cursor.getChild() ); + } + + // Attach the DOM nodes. + + page.setAttribute( 'name', contents.id ); + page.appendTo( this.parts.contents ); + + tab.unselectable(); + this.parts.tabs.append( tab ); + + // Add access key handlers if access key is defined. + if ( contents.accessKey ) + { + registerAccessKey( this, this, 'CTRL+' + contents.accessKey, + tabAccessKeyDown, tabAccessKeyUp ); + this._.accessKeyMap[ 'CTRL+' + contents.accessKey ] = contents.id; + } + }, + + /** + * Activates a tab page in the dialog by its id. + * @param {String} id The id of the dialog tab to be activated. + * @example + * dialogObj.selectPage( 'tab_1' ); + */ + selectPage : function( id ) + { + if ( this._.currentTabId == id ) + return; + + // Returning true means that the event has been canceled + if ( this.fire( 'selectPage', { page : id, currentPage : this._.currentTabId } ) === true ) + return; + + // Hide the non-selected tabs and pages. + for ( var i in this._.tabs ) + { + var tab = this._.tabs[i][0], + page = this._.tabs[i][1]; + if ( i != id ) + { + tab.removeClass( 'cke_dialog_tab_selected' ); + page.hide(); + } + page.setAttribute( 'aria-hidden', i != id ); + } + + var selected = this._.tabs[ id ]; + selected[ 0 ].addClass( 'cke_dialog_tab_selected' ); + + // [IE] an invisible input[type='text'] will enlarge it's width + // if it's value is long when it shows, so we clear it's value + // before it shows and then recover it (#5649) + if ( CKEDITOR.env.ie6Compat || CKEDITOR.env.ie7Compat ) + { + clearOrRecoverTextInputValue( selected[ 1 ] ); + selected[ 1 ].show(); + setTimeout( function() + { + clearOrRecoverTextInputValue( selected[ 1 ], 1 ); + }, 0 ); + } + else + selected[ 1 ].show(); + + this._.currentTabId = id; + this._.currentTabIndex = CKEDITOR.tools.indexOf( this._.tabIdList, id ); + }, + + // Dialog state-specific style updates. + updateStyle : function() + { + // If only a single page shown, a different style is used in the central pane. + this.parts.dialog[ ( this._.pageCount === 1 ? 'add' : 'remove' ) + 'Class' ]( 'cke_single_page' ); + }, + + /** + * Hides a page's tab away from the dialog. + * @param {String} id The page's Id. + * @example + * dialog.hidePage( 'tab_3' ); + */ + hidePage : function( id ) + { + var tab = this._.tabs[id] && this._.tabs[id][0]; + if ( !tab || this._.pageCount == 1 || !tab.isVisible() ) + return; + // Switch to other tab first when we're hiding the active tab. + else if ( id == this._.currentTabId ) + this.selectPage( getPreviousVisibleTab.call( this ) ); + + tab.hide(); + this._.pageCount--; + this.updateStyle(); + }, + + /** + * Unhides a page's tab. + * @param {String} id The page's Id. + * @example + * dialog.showPage( 'tab_2' ); + */ + showPage : function( id ) + { + var tab = this._.tabs[id] && this._.tabs[id][0]; + if ( !tab ) + return; + tab.show(); + this._.pageCount++; + this.updateStyle(); + }, + + /** + * Gets the root DOM element of the dialog. + * @returns {CKEDITOR.dom.element} The <span> element containing this dialog. + * @example + * var dialogElement = dialogObj.getElement().getFirst(); + * dialogElement.setStyle( 'padding', '5px' ); + */ + getElement : function() + { + return this._.element; + }, + + /** + * Gets the name of the dialog. + * @returns {String} The name of this dialog. + * @example + * var dialogName = dialogObj.getName(); + */ + getName : function() + { + return this._.name; + }, + + /** + * Gets a dialog UI element object from a dialog page. + * @param {String} pageId id of dialog page. + * @param {String} elementId id of UI element. + * @example + * dialogObj.getContentElement( 'tabId', 'elementId' ).setValue( 'Example' ); + * @returns {CKEDITOR.ui.dialog.uiElement} The dialog UI element. + */ + getContentElement : function( pageId, elementId ) + { + var page = this._.contents[ pageId ]; + return page && page[ elementId ]; + }, + + /** + * Gets the value of a dialog UI element. + * @param {String} pageId id of dialog page. + * @param {String} elementId id of UI element. + * @example + * alert( dialogObj.getValueOf( 'tabId', 'elementId' ) ); + * @returns {Object} The value of the UI element. + */ + getValueOf : function( pageId, elementId ) + { + return this.getContentElement( pageId, elementId ).getValue(); + }, + + /** + * Sets the value of a dialog UI element. + * @param {String} pageId id of the dialog page. + * @param {String} elementId id of the UI element. + * @param {Object} value The new value of the UI element. + * @example + * dialogObj.setValueOf( 'tabId', 'elementId', 'Example' ); + */ + setValueOf : function( pageId, elementId, value ) + { + return this.getContentElement( pageId, elementId ).setValue( value ); + }, + + /** + * Gets the UI element of a button in the dialog's button row. + * @param {String} id The id of the button. + * @example + * @returns {CKEDITOR.ui.dialog.button} The button object. + */ + getButton : function( id ) + { + return this._.buttons[ id ]; + }, + + /** + * Simulates a click to a dialog button in the dialog's button row. + * @param {String} id The id of the button. + * @example + * @returns The return value of the dialog's "click" event. + */ + click : function( id ) + { + return this._.buttons[ id ].click(); + }, + + /** + * Disables a dialog button. + * @param {String} id The id of the button. + * @example + */ + disableButton : function( id ) + { + return this._.buttons[ id ].disable(); + }, + + /** + * Enables a dialog button. + * @param {String} id The id of the button. + * @example + */ + enableButton : function( id ) + { + return this._.buttons[ id ].enable(); + }, + + /** + * Gets the number of pages in the dialog. + * @returns {Number} Page count. + */ + getPageCount : function() + { + return this._.pageCount; + }, + + /** + * Gets the editor instance which opened this dialog. + * @returns {CKEDITOR.editor} Parent editor instances. + */ + getParentEditor : function() + { + return this._.editor; + }, + + /** + * Gets the element that was selected when opening the dialog, if any. + * @returns {CKEDITOR.dom.element} The element that was selected, or null. + */ + getSelectedElement : function() + { + return this.getParentEditor().getSelection().getSelectedElement(); + }, + + /** + * Adds element to dialog's focusable list. + * + * @param {CKEDITOR.dom.element} element + * @param {Number} [index] + */ + addFocusable: function( element, index ) { + if ( typeof index == 'undefined' ) + { + index = this._.focusList.length; + this._.focusList.push( new Focusable( this, element, index ) ); + } + else + { + this._.focusList.splice( index, 0, new Focusable( this, element, index ) ); + for ( var i = index + 1 ; i < this._.focusList.length ; i++ ) + this._.focusList[ i ].focusIndex++; + } + } + }; + + CKEDITOR.tools.extend( CKEDITOR.dialog, + /** + * @lends CKEDITOR.dialog + */ + { + /** + * Registers a dialog. + * @param {String} name The dialog's name. + * @param {Function|String} dialogDefinition + * A function returning the dialog's definition, or the URL to the .js file holding the function. + * The function should accept an argument "editor" which is the current editor instance, and + * return an object conforming to {@link CKEDITOR.dialog.definition}. + * @see CKEDITOR.dialog.definition + * @example + * // Full sample plugin, which does not only register a dialog window but also adds an item to the context menu. + * // To open the dialog window, choose "Open dialog" in the context menu. + * CKEDITOR.plugins.add( 'myplugin', + * { + * init: function( editor ) + * { + * editor.addCommand( 'mydialog',new CKEDITOR.dialogCommand( 'mydialog' ) ); + * + * if ( editor.contextMenu ) + * { + * editor.addMenuGroup( 'mygroup', 10 ); + * editor.addMenuItem( 'My Dialog', + * { + * label : 'Open dialog', + * command : 'mydialog', + * group : 'mygroup' + * }); + * editor.contextMenu.addListener( function( element ) + * { + * return { 'My Dialog' : CKEDITOR.TRISTATE_OFF }; + * }); + * } + * + * CKEDITOR.dialog.add( 'mydialog', function( api ) + * { + * // CKEDITOR.dialog.definition + * var dialogDefinition = + * { + * title : 'Sample dialog', + * minWidth : 390, + * minHeight : 130, + * contents : [ + * { + * id : 'tab1', + * label : 'Label', + * title : 'Title', + * expand : true, + * padding : 0, + * elements : + * [ + * { + * type : 'html', + * html : '<p>This is some sample HTML content.</p>' + * }, + * { + * type : 'textarea', + * id : 'textareaId', + * rows : 4, + * cols : 40 + * } + * ] + * } + * ], + * buttons : [ CKEDITOR.dialog.okButton, CKEDITOR.dialog.cancelButton ], + * onOk : function() { + * // "this" is now a CKEDITOR.dialog object. + * // Accessing dialog elements: + * var textareaObj = this.getContentElement( 'tab1', 'textareaId' ); + * alert( "You have entered: " + textareaObj.getValue() ); + * } + * }; + * + * return dialogDefinition; + * } ); + * } + * } ); + * + * CKEDITOR.replace( 'editor1', { extraPlugins : 'myplugin' } ); + */ + add : function( name, dialogDefinition ) + { + // Avoid path registration from multiple instances override definition. + if ( !this._.dialogDefinitions[name] + || typeof dialogDefinition == 'function' ) + this._.dialogDefinitions[name] = dialogDefinition; + }, + + exists : function( name ) + { + return !!this._.dialogDefinitions[ name ]; + }, + + getCurrent : function() + { + return CKEDITOR.dialog._.currentTop; + }, + + /** + * The default OK button for dialogs. Fires the "ok" event and closes the dialog if the event succeeds. + * @static + * @field + * @example + * @type Function + */ + okButton : (function() + { + var retval = function( editor, override ) + { + override = override || {}; + return CKEDITOR.tools.extend( { + id : 'ok', + type : 'button', + label : editor.lang.common.ok, + 'class' : 'cke_dialog_ui_button_ok', + onClick : function( evt ) + { + var dialog = evt.data.dialog; + if ( dialog.fire( 'ok', { hide : true } ).hide !== false ) + dialog.hide(); + } + }, override, true ); + }; + retval.type = 'button'; + retval.override = function( override ) + { + return CKEDITOR.tools.extend( function( editor ){ return retval( editor, override ); }, + { type : 'button' }, true ); + }; + return retval; + })(), + + /** + * The default cancel button for dialogs. Fires the "cancel" event and closes the dialog if no UI element value changed. + * @static + * @field + * @example + * @type Function + */ + cancelButton : (function() + { + var retval = function( editor, override ) + { + override = override || {}; + return CKEDITOR.tools.extend( { + id : 'cancel', + type : 'button', + label : editor.lang.common.cancel, + 'class' : 'cke_dialog_ui_button_cancel', + onClick : function( evt ) + { + var dialog = evt.data.dialog; + if ( dialog.fire( 'cancel', { hide : true } ).hide !== false ) + dialog.hide(); + } + }, override, true ); + }; + retval.type = 'button'; + retval.override = function( override ) + { + return CKEDITOR.tools.extend( function( editor ){ return retval( editor, override ); }, + { type : 'button' }, true ); + }; + return retval; + })(), + + /** + * Registers a dialog UI element. + * @param {String} typeName The name of the UI element. + * @param {Function} builder The function to build the UI element. + * @example + */ + addUIElement : function( typeName, builder ) + { + this._.uiElementBuilders[ typeName ] = builder; + } + }); + + CKEDITOR.dialog._ = + { + uiElementBuilders : {}, + + dialogDefinitions : {}, + + currentTop : null, + + currentZIndex : null + }; + + // "Inherit" (copy actually) from CKEDITOR.event. + CKEDITOR.event.implementOn( CKEDITOR.dialog ); + CKEDITOR.event.implementOn( CKEDITOR.dialog.prototype, true ); + + var defaultDialogDefinition = + { + resizable : CKEDITOR.DIALOG_RESIZE_BOTH, + minWidth : 600, + minHeight : 400, + buttons : [ CKEDITOR.dialog.okButton, CKEDITOR.dialog.cancelButton ] + }; + + // Tool function used to return an item from an array based on its id + // property. + var getById = function( array, id, recurse ) + { + for ( var i = 0, item ; ( item = array[ i ] ) ; i++ ) + { + if ( item.id == id ) + return item; + if ( recurse && item[ recurse ] ) + { + var retval = getById( item[ recurse ], id, recurse ) ; + if ( retval ) + return retval; + } + } + return null; + }; + + // Tool function used to add an item into an array. + var addById = function( array, newItem, nextSiblingId, recurse, nullIfNotFound ) + { + if ( nextSiblingId ) + { + for ( var i = 0, item ; ( item = array[ i ] ) ; i++ ) + { + if ( item.id == nextSiblingId ) + { + array.splice( i, 0, newItem ); + return newItem; + } + + if ( recurse && item[ recurse ] ) + { + var retval = addById( item[ recurse ], newItem, nextSiblingId, recurse, true ); + if ( retval ) + return retval; + } + } + + if ( nullIfNotFound ) + return null; + } + + array.push( newItem ); + return newItem; + }; + + // Tool function used to remove an item from an array based on its id. + var removeById = function( array, id, recurse ) + { + for ( var i = 0, item ; ( item = array[ i ] ) ; i++ ) + { + if ( item.id == id ) + return array.splice( i, 1 ); + if ( recurse && item[ recurse ] ) + { + var retval = removeById( item[ recurse ], id, recurse ); + if ( retval ) + return retval; + } + } + return null; + }; + + /** + * This class is not really part of the API. It is the "definition" property value + * passed to "dialogDefinition" event handlers. + * @constructor + * @name CKEDITOR.dialog.definitionObject + * @extends CKEDITOR.dialog.definition + * @example + * CKEDITOR.on( 'dialogDefinition', function( evt ) + * { + * var definition = evt.data.definition; + * var content = definition.getContents( 'page1' ); + * ... + * } ); + */ + var definitionObject = function( dialog, dialogDefinition ) + { + // TODO : Check if needed. + this.dialog = dialog; + + // Transform the contents entries in contentObjects. + var contents = dialogDefinition.contents; + for ( var i = 0, content ; ( content = contents[i] ) ; i++ ) + contents[ i ] = content && new contentObject( dialog, content ); + + CKEDITOR.tools.extend( this, dialogDefinition ); + }; + + definitionObject.prototype = + /** @lends CKEDITOR.dialog.definitionObject.prototype */ + { + /** + * Gets a content definition. + * @param {String} id The id of the content definition. + * @returns {CKEDITOR.dialog.definition.content} The content definition + * matching id. + */ + getContents : function( id ) + { + return getById( this.contents, id ); + }, + + /** + * Gets a button definition. + * @param {String} id The id of the button definition. + * @returns {CKEDITOR.dialog.definition.button} The button definition + * matching id. + */ + getButton : function( id ) + { + return getById( this.buttons, id ); + }, + + /** + * Adds a content definition object under this dialog definition. + * @param {CKEDITOR.dialog.definition.content} contentDefinition The + * content definition. + * @param {String} [nextSiblingId] The id of an existing content + * definition which the new content definition will be inserted + * before. Omit if the new content definition is to be inserted as + * the last item. + * @returns {CKEDITOR.dialog.definition.content} The inserted content + * definition. + */ + addContents : function( contentDefinition, nextSiblingId ) + { + return addById( this.contents, contentDefinition, nextSiblingId ); + }, + + /** + * Adds a button definition object under this dialog definition. + * @param {CKEDITOR.dialog.definition.button} buttonDefinition The + * button definition. + * @param {String} [nextSiblingId] The id of an existing button + * definition which the new button definition will be inserted + * before. Omit if the new button definition is to be inserted as + * the last item. + * @returns {CKEDITOR.dialog.definition.button} The inserted button + * definition. + */ + addButton : function( buttonDefinition, nextSiblingId ) + { + return addById( this.buttons, buttonDefinition, nextSiblingId ); + }, + + /** + * Removes a content definition from this dialog definition. + * @param {String} id The id of the content definition to be removed. + * @returns {CKEDITOR.dialog.definition.content} The removed content + * definition. + */ + removeContents : function( id ) + { + removeById( this.contents, id ); + }, + + /** + * Removes a button definition from the dialog definition. + * @param {String} id The id of the button definition to be removed. + * @returns {CKEDITOR.dialog.definition.button} The removed button + * definition. + */ + removeButton : function( id ) + { + removeById( this.buttons, id ); + } + }; + + /** + * This class is not really part of the API. It is the template of the + * objects representing content pages inside the + * CKEDITOR.dialog.definitionObject. + * @constructor + * @name CKEDITOR.dialog.definition.contentObject + * @example + * CKEDITOR.on( 'dialogDefinition', function( evt ) + * { + * var definition = evt.data.definition; + * var content = definition.getContents( 'page1' ); + * content.remove( 'textInput1' ); + * ... + * } ); + */ + function contentObject( dialog, contentDefinition ) + { + this._ = + { + dialog : dialog + }; + + CKEDITOR.tools.extend( this, contentDefinition ); + } + + contentObject.prototype = + /** @lends CKEDITOR.dialog.definition.contentObject.prototype */ + { + /** + * Gets a UI element definition under the content definition. + * @param {String} id The id of the UI element definition. + * @returns {CKEDITOR.dialog.definition.uiElement} + */ + get : function( id ) + { + return getById( this.elements, id, 'children' ); + }, + + /** + * Adds a UI element definition to the content definition. + * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition The + * UI elemnet definition to be added. + * @param {String} nextSiblingId The id of an existing UI element + * definition which the new UI element definition will be inserted + * before. Omit if the new button definition is to be inserted as + * the last item. + * @returns {CKEDITOR.dialog.definition.uiElement} The element + * definition inserted. + */ + add : function( elementDefinition, nextSiblingId ) + { + return addById( this.elements, elementDefinition, nextSiblingId, 'children' ); + }, + + /** + * Removes a UI element definition from the content definition. + * @param {String} id The id of the UI element definition to be + * removed. + * @returns {CKEDITOR.dialog.definition.uiElement} The element + * definition removed. + * @example + */ + remove : function( id ) + { + removeById( this.elements, id, 'children' ); + } + }; + + function initDragAndDrop( dialog ) + { + var lastCoords = null, + abstractDialogCoords = null, + element = dialog.getElement().getFirst(), + editor = dialog.getParentEditor(), + magnetDistance = editor.config.dialog_magnetDistance, + margins = editor.skin.margins || [ 0, 0, 0, 0 ]; + + if ( typeof magnetDistance == 'undefined' ) + magnetDistance = 20; + + function mouseMoveHandler( evt ) + { + var dialogSize = dialog.getSize(), + viewPaneSize = CKEDITOR.document.getWindow().getViewPaneSize(), + x = evt.data.$.screenX, + y = evt.data.$.screenY, + dx = x - lastCoords.x, + dy = y - lastCoords.y, + realX, realY; + + lastCoords = { x : x, y : y }; + abstractDialogCoords.x += dx; + abstractDialogCoords.y += dy; + + if ( abstractDialogCoords.x + margins[3] < magnetDistance ) + realX = - margins[3]; + else if ( abstractDialogCoords.x - margins[1] > viewPaneSize.width - dialogSize.width - magnetDistance ) + realX = viewPaneSize.width - dialogSize.width + ( editor.lang.dir == 'rtl' ? 0 : margins[1] ); + else + realX = abstractDialogCoords.x; + + if ( abstractDialogCoords.y + margins[0] < magnetDistance ) + realY = - margins[0]; + else if ( abstractDialogCoords.y - margins[2] > viewPaneSize.height - dialogSize.height - magnetDistance ) + realY = viewPaneSize.height - dialogSize.height + margins[2]; + else + realY = abstractDialogCoords.y; + + dialog.move( realX, realY, 1 ); + + evt.data.preventDefault(); + } + + function mouseUpHandler( evt ) + { + CKEDITOR.document.removeListener( 'mousemove', mouseMoveHandler ); + CKEDITOR.document.removeListener( 'mouseup', mouseUpHandler ); + + if ( CKEDITOR.env.ie6Compat ) + { + var coverDoc = currentCover.getChild( 0 ).getFrameDocument(); + coverDoc.removeListener( 'mousemove', mouseMoveHandler ); + coverDoc.removeListener( 'mouseup', mouseUpHandler ); + } + } + + dialog.parts.title.on( 'mousedown', function( evt ) + { + lastCoords = { x : evt.data.$.screenX, y : evt.data.$.screenY }; + + CKEDITOR.document.on( 'mousemove', mouseMoveHandler ); + CKEDITOR.document.on( 'mouseup', mouseUpHandler ); + abstractDialogCoords = dialog.getPosition(); + + if ( CKEDITOR.env.ie6Compat ) + { + var coverDoc = currentCover.getChild( 0 ).getFrameDocument(); + coverDoc.on( 'mousemove', mouseMoveHandler ); + coverDoc.on( 'mouseup', mouseUpHandler ); + } + + evt.data.preventDefault(); + }, dialog ); + } + + function initResizeHandles( dialog ) + { + var def = dialog.definition, + resizable = def.resizable; + + if ( resizable == CKEDITOR.DIALOG_RESIZE_NONE ) + return; + + var editor = dialog.getParentEditor(); + var wrapperWidth, wrapperHeight, + viewSize, origin, startSize, + dialogCover; + + var mouseDownFn = CKEDITOR.tools.addFunction( function( $event ) + { + startSize = dialog.getSize(); + + var content = dialog.parts.contents, + iframeDialog = content.$.getElementsByTagName( 'iframe' ).length; + + // Shim to help capturing "mousemove" over iframe. + if ( iframeDialog ) + { + dialogCover = CKEDITOR.dom.element.createFromHtml( '
' ); + content.append( dialogCover ); + } + + // Calculate the offset between content and chrome size. + wrapperHeight = startSize.height - dialog.parts.contents.getSize( 'height', ! ( CKEDITOR.env.gecko || CKEDITOR.env.opera || CKEDITOR.env.ie && CKEDITOR.env.quirks ) ); + wrapperWidth = startSize.width - dialog.parts.contents.getSize( 'width', 1 ); + + origin = { x : $event.screenX, y : $event.screenY }; + + viewSize = CKEDITOR.document.getWindow().getViewPaneSize(); + + CKEDITOR.document.on( 'mousemove', mouseMoveHandler ); + CKEDITOR.document.on( 'mouseup', mouseUpHandler ); + + if ( CKEDITOR.env.ie6Compat ) + { + var coverDoc = currentCover.getChild( 0 ).getFrameDocument(); + coverDoc.on( 'mousemove', mouseMoveHandler ); + coverDoc.on( 'mouseup', mouseUpHandler ); + } + + $event.preventDefault && $event.preventDefault(); + }); + + // Prepend the grip to the dialog. + dialog.on( 'load', function() + { + var direction = ''; + if ( resizable == CKEDITOR.DIALOG_RESIZE_WIDTH ) + direction = ' cke_resizer_horizontal'; + else if ( resizable == CKEDITOR.DIALOG_RESIZE_HEIGHT ) + direction = ' cke_resizer_vertical'; + var resizer = CKEDITOR.dom.element.createFromHtml( '' ); + dialog.parts.footer.append( resizer, 1 ); + }); + editor.on( 'destroy', function() { CKEDITOR.tools.removeFunction( mouseDownFn ); } ); + + function mouseMoveHandler( evt ) + { + var rtl = editor.lang.dir == 'rtl', + dx = ( evt.data.$.screenX - origin.x ) * ( rtl ? -1 : 1 ), + dy = evt.data.$.screenY - origin.y, + width = startSize.width, + height = startSize.height, + internalWidth = width + dx * ( dialog._.moved ? 1 : 2 ), + internalHeight = height + dy * ( dialog._.moved ? 1 : 2 ), + element = dialog._.element.getFirst(), + right = rtl && element.getComputedStyle( 'right' ), + position = dialog.getPosition(); + + if ( position.y + internalHeight > viewSize.height ) + internalHeight = viewSize.height - position.y; + + if ( ( rtl ? right : position.x ) + internalWidth > viewSize.width ) + internalWidth = viewSize.width - ( rtl ? right : position.x ); + + // Make sure the dialog will not be resized to the wrong side when it's in the leftmost position for RTL. + if ( ( resizable == CKEDITOR.DIALOG_RESIZE_WIDTH || resizable == CKEDITOR.DIALOG_RESIZE_BOTH ) ) + width = Math.max( def.minWidth || 0, internalWidth - wrapperWidth ); + + if ( resizable == CKEDITOR.DIALOG_RESIZE_HEIGHT || resizable == CKEDITOR.DIALOG_RESIZE_BOTH ) + height = Math.max( def.minHeight || 0, internalHeight - wrapperHeight ); + + dialog.resize( width, height ); + + if ( !dialog._.moved ) + dialog.layout(); + + evt.data.preventDefault(); + } + + function mouseUpHandler() + { + CKEDITOR.document.removeListener( 'mouseup', mouseUpHandler ); + CKEDITOR.document.removeListener( 'mousemove', mouseMoveHandler ); + + if ( dialogCover ) + { + dialogCover.remove(); + dialogCover = null; + } + + if ( CKEDITOR.env.ie6Compat ) + { + var coverDoc = currentCover.getChild( 0 ).getFrameDocument(); + coverDoc.removeListener( 'mouseup', mouseUpHandler ); + coverDoc.removeListener( 'mousemove', mouseMoveHandler ); + } + } + } + + var resizeCover; + // Caching resuable covers and allowing only one cover + // on screen. + var covers = {}, + currentCover; + + function cancelEvent( ev ) + { + ev.data.preventDefault(1); + } + + function showCover( editor ) + { + var win = CKEDITOR.document.getWindow(); + var config = editor.config, + backgroundColorStyle = config.dialog_backgroundCoverColor || 'white', + backgroundCoverOpacity = config.dialog_backgroundCoverOpacity, + baseFloatZIndex = config.baseFloatZIndex, + coverKey = CKEDITOR.tools.genKey( + backgroundColorStyle, + backgroundCoverOpacity, + baseFloatZIndex ), + coverElement = covers[ coverKey ]; + + if ( !coverElement ) + { + var html = [ + '
' + ]; + + if ( CKEDITOR.env.ie6Compat ) + { + // Support for custom document.domain in IE. + var isCustomDomain = CKEDITOR.env.isCustomDomain(), + iframeHtml = ''; + + html.push( + '' + + '' ); + } + + html.push( '
' ); + + coverElement = CKEDITOR.dom.element.createFromHtml( html.join( '' ) ); + coverElement.setOpacity( backgroundCoverOpacity != undefined ? backgroundCoverOpacity : 0.5 ); + + coverElement.on( 'keydown', cancelEvent ); + coverElement.on( 'keypress', cancelEvent ); + coverElement.on( 'keyup', cancelEvent ); + + coverElement.appendTo( CKEDITOR.document.getBody() ); + covers[ coverKey ] = coverElement; + } + else + coverElement. show(); + + currentCover = coverElement; + var resizeFunc = function() + { + var size = win.getViewPaneSize(); + coverElement.setStyles( + { + width : size.width + 'px', + height : size.height + 'px' + } ); + }; + + var scrollFunc = function() + { + var pos = win.getScrollPosition(), + cursor = CKEDITOR.dialog._.currentTop; + coverElement.setStyles( + { + left : pos.x + 'px', + top : pos.y + 'px' + }); + + if ( cursor ) + { + do + { + var dialogPos = cursor.getPosition(); + cursor.move( dialogPos.x, dialogPos.y ); + } while ( ( cursor = cursor._.parentDialog ) ); + } + }; + + resizeCover = resizeFunc; + win.on( 'resize', resizeFunc ); + resizeFunc(); + // Using Safari/Mac, focus must be kept where it is (#7027) + if ( !( CKEDITOR.env.mac && CKEDITOR.env.webkit ) ) + coverElement.focus(); + + if ( CKEDITOR.env.ie6Compat ) + { + // IE BUG: win.$.onscroll assignment doesn't work.. it must be window.onscroll. + // So we need to invent a really funny way to make it work. + var myScrollHandler = function() + { + scrollFunc(); + arguments.callee.prevScrollHandler.apply( this, arguments ); + }; + win.$.setTimeout( function() + { + myScrollHandler.prevScrollHandler = window.onscroll || function(){}; + window.onscroll = myScrollHandler; + }, 0 ); + scrollFunc(); + } + } + + function hideCover() + { + if ( !currentCover ) + return; + + var win = CKEDITOR.document.getWindow(); + currentCover.hide(); + win.removeListener( 'resize', resizeCover ); + + if ( CKEDITOR.env.ie6Compat ) + { + win.$.setTimeout( function() + { + var prevScrollHandler = window.onscroll && window.onscroll.prevScrollHandler; + window.onscroll = prevScrollHandler || null; + }, 0 ); + } + resizeCover = null; + } + + function removeCovers() + { + for ( var coverId in covers ) + covers[ coverId ].remove(); + covers = {}; + } + + var accessKeyProcessors = {}; + + var accessKeyDownHandler = function( evt ) + { + var ctrl = evt.data.$.ctrlKey || evt.data.$.metaKey, + alt = evt.data.$.altKey, + shift = evt.data.$.shiftKey, + key = String.fromCharCode( evt.data.$.keyCode ), + keyProcessor = accessKeyProcessors[( ctrl ? 'CTRL+' : '' ) + ( alt ? 'ALT+' : '') + ( shift ? 'SHIFT+' : '' ) + key]; + + if ( !keyProcessor || !keyProcessor.length ) + return; + + keyProcessor = keyProcessor[keyProcessor.length - 1]; + keyProcessor.keydown && keyProcessor.keydown.call( keyProcessor.uiElement, keyProcessor.dialog, keyProcessor.key ); + evt.data.preventDefault(); + }; + + var accessKeyUpHandler = function( evt ) + { + var ctrl = evt.data.$.ctrlKey || evt.data.$.metaKey, + alt = evt.data.$.altKey, + shift = evt.data.$.shiftKey, + key = String.fromCharCode( evt.data.$.keyCode ), + keyProcessor = accessKeyProcessors[( ctrl ? 'CTRL+' : '' ) + ( alt ? 'ALT+' : '') + ( shift ? 'SHIFT+' : '' ) + key]; + + if ( !keyProcessor || !keyProcessor.length ) + return; + + keyProcessor = keyProcessor[keyProcessor.length - 1]; + if ( keyProcessor.keyup ) + { + keyProcessor.keyup.call( keyProcessor.uiElement, keyProcessor.dialog, keyProcessor.key ); + evt.data.preventDefault(); + } + }; + + var registerAccessKey = function( uiElement, dialog, key, downFunc, upFunc ) + { + var procList = accessKeyProcessors[key] || ( accessKeyProcessors[key] = [] ); + procList.push( { + uiElement : uiElement, + dialog : dialog, + key : key, + keyup : upFunc || uiElement.accessKeyUp, + keydown : downFunc || uiElement.accessKeyDown + } ); + }; + + var unregisterAccessKey = function( obj ) + { + for ( var i in accessKeyProcessors ) + { + var list = accessKeyProcessors[i]; + for ( var j = list.length - 1 ; j >= 0 ; j-- ) + { + if ( list[j].dialog == obj || list[j].uiElement == obj ) + list.splice( j, 1 ); + } + if ( list.length === 0 ) + delete accessKeyProcessors[i]; + } + }; + + var tabAccessKeyUp = function( dialog, key ) + { + if ( dialog._.accessKeyMap[key] ) + dialog.selectPage( dialog._.accessKeyMap[key] ); + }; + + var tabAccessKeyDown = function( dialog, key ) + { + }; + + // ESC, ENTER + var preventKeyBubblingKeys = { 27 :1, 13 :1 }; + var preventKeyBubbling = function( e ) + { + if ( e.data.getKeystroke() in preventKeyBubblingKeys ) + e.data.stopPropagation(); + }; + + (function() + { + CKEDITOR.ui.dialog = + { + /** + * The base class of all dialog UI elements. + * @constructor + * @param {CKEDITOR.dialog} dialog Parent dialog object. + * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition Element + * definition. Accepted fields: + *
    + *
  • id (Required) The id of the UI element. See {@link + * CKEDITOR.dialog#getContentElement}
  • + *
  • type (Required) The type of the UI element. The + * value to this field specifies which UI element class will be used to + * generate the final widget.
  • + *
  • title (Optional) The popup tooltip for the UI + * element.
  • + *
  • hidden (Optional) A flag that tells if the element + * should be initially visible.
  • + *
  • className (Optional) Additional CSS class names + * to add to the UI element. Separated by space.
  • + *
  • style (Optional) Additional CSS inline styles + * to add to the UI element. A semicolon (;) is required after the last + * style declaration.
  • + *
  • accessKey (Optional) The alphanumeric access key + * for this element. Access keys are automatically prefixed by CTRL.
  • + *
  • on* (Optional) Any UI element definition field that + * starts with on followed immediately by a capital letter and + * probably more letters is an event handler. Event handlers may be further + * divided into registered event handlers and DOM event handlers. Please + * refer to {@link CKEDITOR.ui.dialog.uiElement#registerEvents} and + * {@link CKEDITOR.ui.dialog.uiElement#eventProcessors} for more + * information.
  • + *
+ * @param {Array} htmlList + * List of HTML code to be added to the dialog's content area. + * @param {Function|String} nodeNameArg + * A function returning a string, or a simple string for the node name for + * the root DOM node. Default is 'div'. + * @param {Function|Object} stylesArg + * A function returning an object, or a simple object for CSS styles applied + * to the DOM node. Default is empty object. + * @param {Function|Object} attributesArg + * A fucntion returning an object, or a simple object for attributes applied + * to the DOM node. Default is empty object. + * @param {Function|String} contentsArg + * A function returning a string, or a simple string for the HTML code inside + * the root DOM node. Default is empty string. + * @example + */ + uiElement : function( dialog, elementDefinition, htmlList, nodeNameArg, stylesArg, attributesArg, contentsArg ) + { + if ( arguments.length < 4 ) + return; + + var nodeName = ( nodeNameArg.call ? nodeNameArg( elementDefinition ) : nodeNameArg ) || 'div', + html = [ '<', nodeName, ' ' ], + styles = ( stylesArg && stylesArg.call ? stylesArg( elementDefinition ) : stylesArg ) || {}, + attributes = ( attributesArg && attributesArg.call ? attributesArg( elementDefinition ) : attributesArg ) || {}, + innerHTML = ( contentsArg && contentsArg.call ? contentsArg.call( this, dialog, elementDefinition ) : contentsArg ) || '', + domId = this.domId = attributes.id || CKEDITOR.tools.getNextId() + '_uiElement', + id = this.id = elementDefinition.id, + i; + + // Set the id, a unique id is required for getElement() to work. + attributes.id = domId; + + // Set the type and definition CSS class names. + var classes = {}; + if ( elementDefinition.type ) + classes[ 'cke_dialog_ui_' + elementDefinition.type ] = 1; + if ( elementDefinition.className ) + classes[ elementDefinition.className ] = 1; + if ( elementDefinition.disabled ) + classes[ 'cke_disabled' ] = 1; + + var attributeClasses = ( attributes['class'] && attributes['class'].split ) ? attributes['class'].split( ' ' ) : []; + for ( i = 0 ; i < attributeClasses.length ; i++ ) + { + if ( attributeClasses[i] ) + classes[ attributeClasses[i] ] = 1; + } + var finalClasses = []; + for ( i in classes ) + finalClasses.push( i ); + attributes['class'] = finalClasses.join( ' ' ); + + // Set the popup tooltop. + if ( elementDefinition.title ) + attributes.title = elementDefinition.title; + + // Write the inline CSS styles. + var styleStr = ( elementDefinition.style || '' ).split( ';' ); + + // Element alignment support. + if ( elementDefinition.align ) + { + var align = elementDefinition.align; + styles[ 'margin-left' ] = align == 'left' ? 0 : 'auto'; + styles[ 'margin-right' ] = align == 'right' ? 0 : 'auto'; + } + + for ( i in styles ) + styleStr.push( i + ':' + styles[i] ); + if ( elementDefinition.hidden ) + styleStr.push( 'display:none' ); + for ( i = styleStr.length - 1 ; i >= 0 ; i-- ) + { + if ( styleStr[i] === '' ) + styleStr.splice( i, 1 ); + } + if ( styleStr.length > 0 ) + attributes.style = ( attributes.style ? ( attributes.style + '; ' ) : '' ) + styleStr.join( '; ' ); + + // Write the attributes. + for ( i in attributes ) + html.push( i + '="' + CKEDITOR.tools.htmlEncode( attributes[i] ) + '" '); + + // Write the content HTML. + html.push( '>', innerHTML, '' ); + + // Add contents to the parent HTML array. + htmlList.push( html.join( '' ) ); + + ( this._ || ( this._ = {} ) ).dialog = dialog; + + // Override isChanged if it is defined in element definition. + if ( typeof( elementDefinition.isChanged ) == 'boolean' ) + this.isChanged = function(){ return elementDefinition.isChanged; }; + if ( typeof( elementDefinition.isChanged ) == 'function' ) + this.isChanged = elementDefinition.isChanged; + + // Overload 'get(set)Value' on definition. + if ( typeof( elementDefinition.setValue ) == 'function' ) + { + this.setValue = CKEDITOR.tools.override( this.setValue, function( org ) + { + return function( val ){ org.call( this, elementDefinition.setValue.call( this, val ) ); }; + } ); + } + + if ( typeof( elementDefinition.getValue ) == 'function' ) + { + this.getValue = CKEDITOR.tools.override( this.getValue, function( org ) + { + return function(){ return elementDefinition.getValue.call( this, org.call( this ) ); }; + } ); + } + + // Add events. + CKEDITOR.event.implementOn( this ); + + this.registerEvents( elementDefinition ); + if ( this.accessKeyUp && this.accessKeyDown && elementDefinition.accessKey ) + registerAccessKey( this, dialog, 'CTRL+' + elementDefinition.accessKey ); + + var me = this; + dialog.on( 'load', function() + { + var input = me.getInputElement(); + if ( input ) + { + var focusClass = me.type in { 'checkbox' : 1, 'ratio' : 1 } && CKEDITOR.env.ie && CKEDITOR.env.version < 8 ? 'cke_dialog_ui_focused' : ''; + input.on( 'focus', function() + { + dialog._.tabBarMode = false; + dialog._.hasFocus = true; + me.fire( 'focus' ); + focusClass && this.addClass( focusClass ); + + }); + + input.on( 'blur', function() + { + me.fire( 'blur' ); + focusClass && this.removeClass( focusClass ); + }); + } + } ); + + // Register the object as a tab focus if it can be included. + if ( this.keyboardFocusable ) + { + this.tabIndex = elementDefinition.tabIndex || 0; + + this.focusIndex = dialog._.focusList.push( this ) - 1; + this.on( 'focus', function() + { + dialog._.currentFocusIndex = me.focusIndex; + } ); + } + + // Completes this object with everything we have in the + // definition. + CKEDITOR.tools.extend( this, elementDefinition ); + }, + + /** + * Horizontal layout box for dialog UI elements, auto-expends to available width of container. + * @constructor + * @extends CKEDITOR.ui.dialog.uiElement + * @param {CKEDITOR.dialog} dialog + * Parent dialog object. + * @param {Array} childObjList + * Array of {@link CKEDITOR.ui.dialog.uiElement} objects inside this + * container. + * @param {Array} childHtmlList + * Array of HTML code that correspond to the HTML output of all the + * objects in childObjList. + * @param {Array} htmlList + * Array of HTML code that this element will output to. + * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition + * The element definition. Accepted fields: + *
    + *
  • widths (Optional) The widths of child cells.
  • + *
  • height (Optional) The height of the layout.
  • + *
  • padding (Optional) The padding width inside child + * cells.
  • + *
  • align (Optional) The alignment of the whole layout + *
  • + *
+ * @example + */ + hbox : function( dialog, childObjList, childHtmlList, htmlList, elementDefinition ) + { + if ( arguments.length < 4 ) + return; + + this._ || ( this._ = {} ); + + var children = this._.children = childObjList, + widths = elementDefinition && elementDefinition.widths || null, + height = elementDefinition && elementDefinition.height || null, + styles = {}, + i; + /** @ignore */ + var innerHTML = function() + { + var html = [ '' ]; + for ( i = 0 ; i < childHtmlList.length ; i++ ) + { + var className = 'cke_dialog_ui_hbox_child', + styles = []; + if ( i === 0 ) + className = 'cke_dialog_ui_hbox_first'; + if ( i == childHtmlList.length - 1 ) + className = 'cke_dialog_ui_hbox_last'; + html.push( ' 0 ) + html.push( 'style="' + styles.join('; ') + '" ' ); + html.push( '>', childHtmlList[i], '' ); + } + html.push( '' ); + return html.join( '' ); + }; + + var attribs = { role : 'presentation' }; + elementDefinition && elementDefinition.align && ( attribs.align = elementDefinition.align ); + + CKEDITOR.ui.dialog.uiElement.call( + this, + dialog, + elementDefinition || { type : 'hbox' }, + htmlList, + 'table', + styles, + attribs, + innerHTML ); + }, + + /** + * Vertical layout box for dialog UI elements. + * @constructor + * @extends CKEDITOR.ui.dialog.hbox + * @param {CKEDITOR.dialog} dialog + * Parent dialog object. + * @param {Array} childObjList + * Array of {@link CKEDITOR.ui.dialog.uiElement} objects inside this + * container. + * @param {Array} childHtmlList + * Array of HTML code that correspond to the HTML output of all the + * objects in childObjList. + * @param {Array} htmlList + * Array of HTML code that this element will output to. + * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition + * The element definition. Accepted fields: + *
    + *
  • width (Optional) The width of the layout.
  • + *
  • heights (Optional) The heights of individual cells. + *
  • + *
  • align (Optional) The alignment of the layout.
  • + *
  • padding (Optional) The padding width inside child + * cells.
  • + *
  • expand (Optional) Whether the layout should expand + * vertically to fill its container.
  • + *
+ * @example + */ + vbox : function( dialog, childObjList, childHtmlList, htmlList, elementDefinition ) + { + if ( arguments.length < 3 ) + return; + + this._ || ( this._ = {} ); + + var children = this._.children = childObjList, + width = elementDefinition && elementDefinition.width || null, + heights = elementDefinition && elementDefinition.heights || null; + /** @ignore */ + var innerHTML = function() + { + var html = [ '' ); + for ( var i = 0 ; i < childHtmlList.length ; i++ ) + { + var styles = []; + html.push( '' ); + } + html.push( '
0 ) + html.push( 'style="', styles.join( '; ' ), '" ' ); + html.push( ' class="cke_dialog_ui_vbox_child">', childHtmlList[i], '
' ); + return html.join( '' ); + }; + CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition || { type : 'vbox' }, htmlList, 'div', null, { role : 'presentation' }, innerHTML ); + } + }; + })(); + + CKEDITOR.ui.dialog.uiElement.prototype = + { + /** + * Gets the root DOM element of this dialog UI object. + * @returns {CKEDITOR.dom.element} Root DOM element of UI object. + * @example + * uiElement.getElement().hide(); + */ + getElement : function() + { + return CKEDITOR.document.getById( this.domId ); + }, + + /** + * Gets the DOM element that the user inputs values. + * This function is used by setValue(), getValue() and focus(). It should + * be overrided in child classes where the input element isn't the root + * element. + * @returns {CKEDITOR.dom.element} The element where the user input values. + * @example + * var rawValue = textInput.getInputElement().$.value; + */ + getInputElement : function() + { + return this.getElement(); + }, + + /** + * Gets the parent dialog object containing this UI element. + * @returns {CKEDITOR.dialog} Parent dialog object. + * @example + * var dialog = uiElement.getDialog(); + */ + getDialog : function() + { + return this._.dialog; + }, + + /** + * Sets the value of this dialog UI object. + * @param {Object} value The new value. + * @param {Boolean} noChangeEvent Internal commit, to supress 'change' event on this element. + * @returns {CKEDITOR.dialog.uiElement} The current UI element. + * @example + * uiElement.setValue( 'Dingo' ); + */ + setValue : function( value, noChangeEvent ) + { + this.getInputElement().setValue( value ); + !noChangeEvent && this.fire( 'change', { value : value } ); + return this; + }, + + /** + * Gets the current value of this dialog UI object. + * @returns {Object} The current value. + * @example + * var myValue = uiElement.getValue(); + */ + getValue : function() + { + return this.getInputElement().getValue(); + }, + + /** + * Tells whether the UI object's value has changed. + * @returns {Boolean} true if changed, false if not changed. + * @example + * if ( uiElement.isChanged() ) + *   confirm( 'Value changed! Continue?' ); + */ + isChanged : function() + { + // Override in input classes. + return false; + }, + + /** + * Selects the parent tab of this element. Usually called by focus() or overridden focus() methods. + * @returns {CKEDITOR.dialog.uiElement} The current UI element. + * @example + * focus : function() + * { + * this.selectParentTab(); + * // do something else. + * } + */ + selectParentTab : function() + { + var element = this.getInputElement(), + cursor = element, + tabId; + while ( ( cursor = cursor.getParent() ) && cursor.$.className.search( 'cke_dialog_page_contents' ) == -1 ) + { /*jsl:pass*/ } + + // Some widgets don't have parent tabs (e.g. OK and Cancel buttons). + if ( !cursor ) + return this; + + tabId = cursor.getAttribute( 'name' ); + // Avoid duplicate select. + if ( this._.dialog._.currentTabId != tabId ) + this._.dialog.selectPage( tabId ); + return this; + }, + + /** + * Puts the focus to the UI object. Switches tabs if the UI object isn't in the active tab page. + * @returns {CKEDITOR.dialog.uiElement} The current UI element. + * @example + * uiElement.focus(); + */ + focus : function() + { + this.selectParentTab().getInputElement().focus(); + return this; + }, + + /** + * Registers the on* event handlers defined in the element definition. + * The default behavior of this function is: + *
    + *
  1. + * If the on* event is defined in the class's eventProcesors list, + * then the registration is delegated to the corresponding function + * in the eventProcessors list. + *
  2. + *
  3. + * If the on* event is not defined in the eventProcessors list, then + * register the event handler under the corresponding DOM event of + * the UI element's input DOM element (as defined by the return value + * of {@link CKEDITOR.ui.dialog.uiElement#getInputElement}). + *
  4. + *
+ * This function is only called at UI element instantiation, but can + * be overridded in child classes if they require more flexibility. + * @param {CKEDITOR.dialog.definition.uiElement} definition The UI element + * definition. + * @returns {CKEDITOR.dialog.uiElement} The current UI element. + * @example + */ + registerEvents : function( definition ) + { + var regex = /^on([A-Z]\w+)/, + match; + + var registerDomEvent = function( uiElement, dialog, eventName, func ) + { + dialog.on( 'load', function() + { + uiElement.getInputElement().on( eventName, func, uiElement ); + }); + }; + + for ( var i in definition ) + { + if ( !( match = i.match( regex ) ) ) + continue; + if ( this.eventProcessors[i] ) + this.eventProcessors[i].call( this, this._.dialog, definition[i] ); + else + registerDomEvent( this, this._.dialog, match[1].toLowerCase(), definition[i] ); + } + + return this; + }, + + /** + * The event processor list used by + * {@link CKEDITOR.ui.dialog.uiElement#getInputElement} at UI element + * instantiation. The default list defines three on* events: + *
    + *
  1. onLoad - Called when the element's parent dialog opens for the + * first time
  2. + *
  3. onShow - Called whenever the element's parent dialog opens.
  4. + *
  5. onHide - Called whenever the element's parent dialog closes.
  6. + *
+ * @field + * @type Object + * @example + * // This connects the 'click' event in CKEDITOR.ui.dialog.button to onClick + * // handlers in the UI element's definitions. + * CKEDITOR.ui.dialog.button.eventProcessors = CKEDITOR.tools.extend( {}, + *   CKEDITOR.ui.dialog.uiElement.prototype.eventProcessors, + *   { onClick : function( dialog, func ) { this.on( 'click', func ); } }, + *   true ); + */ + eventProcessors : + { + onLoad : function( dialog, func ) + { + dialog.on( 'load', func, this ); + }, + + onShow : function( dialog, func ) + { + dialog.on( 'show', func, this ); + }, + + onHide : function( dialog, func ) + { + dialog.on( 'hide', func, this ); + } + }, + + /** + * The default handler for a UI element's access key down event, which + * tries to put focus to the UI element.
+ * Can be overridded in child classes for more sophisticaed behavior. + * @param {CKEDITOR.dialog} dialog The parent dialog object. + * @param {String} key The key combination pressed. Since access keys + * are defined to always include the CTRL key, its value should always + * include a 'CTRL+' prefix. + * @example + */ + accessKeyDown : function( dialog, key ) + { + this.focus(); + }, + + /** + * The default handler for a UI element's access key up event, which + * does nothing.
+ * Can be overridded in child classes for more sophisticated behavior. + * @param {CKEDITOR.dialog} dialog The parent dialog object. + * @param {String} key The key combination pressed. Since access keys + * are defined to always include the CTRL key, its value should always + * include a 'CTRL+' prefix. + * @example + */ + accessKeyUp : function( dialog, key ) + { + }, + + /** + * Disables a UI element. + * @example + */ + disable : function() + { + var element = this.getElement(), + input = this.getInputElement(); + input.setAttribute( 'disabled', 'true' ); + element.addClass( 'cke_disabled' ); + }, + + /** + * Enables a UI element. + * @example + */ + enable : function() + { + var element = this.getElement(), + input = this.getInputElement(); + input.removeAttribute( 'disabled' ); + element.removeClass( 'cke_disabled' ); + }, + + /** + * Determines whether an UI element is enabled or not. + * @returns {Boolean} Whether the UI element is enabled. + * @example + */ + isEnabled : function() + { + return !this.getElement().hasClass( 'cke_disabled' ); + }, + + /** + * Determines whether an UI element is visible or not. + * @returns {Boolean} Whether the UI element is visible. + * @example + */ + isVisible : function() + { + return this.getInputElement().isVisible(); + }, + + /** + * Determines whether an UI element is focus-able or not. + * Focus-able is defined as being both visible and enabled. + * @returns {Boolean} Whether the UI element can be focused. + * @example + */ + isFocusable : function() + { + if ( !this.isEnabled() || !this.isVisible() ) + return false; + return true; + } + }; + + CKEDITOR.ui.dialog.hbox.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.uiElement, + /** + * @lends CKEDITOR.ui.dialog.hbox.prototype + */ + { + /** + * Gets a child UI element inside this container. + * @param {Array|Number} indices An array or a single number to indicate the child's + * position in the container's descendant tree. Omit to get all the children in an array. + * @returns {Array|CKEDITOR.ui.dialog.uiElement} Array of all UI elements in the container + * if no argument given, or the specified UI element if indices is given. + * @example + * var checkbox = hbox.getChild( [0,1] ); + * checkbox.setValue( true ); + */ + getChild : function( indices ) + { + // If no arguments, return a clone of the children array. + if ( arguments.length < 1 ) + return this._.children.concat(); + + // If indices isn't array, make it one. + if ( !indices.splice ) + indices = [ indices ]; + + // Retrieve the child element according to tree position. + if ( indices.length < 2 ) + return this._.children[ indices[0] ]; + else + return ( this._.children[ indices[0] ] && this._.children[ indices[0] ].getChild ) ? + this._.children[ indices[0] ].getChild( indices.slice( 1, indices.length ) ) : + null; + } + }, true ); + + CKEDITOR.ui.dialog.vbox.prototype = new CKEDITOR.ui.dialog.hbox(); + + + + (function() + { + var commonBuilder = { + build : function( dialog, elementDefinition, output ) + { + var children = elementDefinition.children, + child, + childHtmlList = [], + childObjList = []; + for ( var i = 0 ; ( i < children.length && ( child = children[i] ) ) ; i++ ) + { + var childHtml = []; + childHtmlList.push( childHtml ); + childObjList.push( CKEDITOR.dialog._.uiElementBuilders[ child.type ].build( dialog, child, childHtml ) ); + } + return new CKEDITOR.ui.dialog[elementDefinition.type]( dialog, childObjList, childHtmlList, output, elementDefinition ); + } + }; + + CKEDITOR.dialog.addUIElement( 'hbox', commonBuilder ); + CKEDITOR.dialog.addUIElement( 'vbox', commonBuilder ); + })(); + + /** + * Generic dialog command. It opens a specific dialog when executed. + * @constructor + * @augments CKEDITOR.commandDefinition + * @param {string} dialogName The name of the dialog to open when executing + * this command. + * @example + * // Register the "link" command, which opens the "link" dialog. + * editor.addCommand( 'link', new CKEDITOR.dialogCommand( 'link' ) ); + */ + CKEDITOR.dialogCommand = function( dialogName ) + { + this.dialogName = dialogName; + }; + + CKEDITOR.dialogCommand.prototype = + { + /** @ignore */ + exec : function( editor ) + { + // Special treatment for Opera. (#8031) + CKEDITOR.env.opera ? + CKEDITOR.tools.setTimeout( function() { editor.openDialog( this.dialogName ) }, 0, this ) + : editor.openDialog( this.dialogName ); + }, + + // Dialog commands just open a dialog ui, thus require no undo logic, + // undo support should dedicate to specific dialog implementation. + canUndo: false, + + editorFocus : CKEDITOR.env.ie || CKEDITOR.env.webkit + }; + + (function() + { + var notEmptyRegex = /^([a]|[^a])+$/, + integerRegex = /^\d*$/, + numberRegex = /^\d*(?:\.\d+)?$/, + htmlLengthRegex = /^(((\d*(\.\d+))|(\d*))(px|\%)?)?$/, + cssLengthRegex = /^(((\d*(\.\d+))|(\d*))(px|em|ex|in|cm|mm|pt|pc|\%)?)?$/i, + inlineStyleRegex = /^(\s*[\w-]+\s*:\s*[^:;]+(?:;|$))*$/; + + CKEDITOR.VALIDATE_OR = 1; + CKEDITOR.VALIDATE_AND = 2; + + CKEDITOR.dialog.validate = + { + functions : function() + { + var args = arguments; + return function() + { + /** + * It's important for validate functions to be able to accept the value + * as argument in addition to this.getValue(), so that it is possible to + * combine validate functions together to make more sophisticated + * validators. + */ + var value = this && this.getValue ? this.getValue() : args[ 0 ]; + + var msg = undefined, + relation = CKEDITOR.VALIDATE_AND, + functions = [], i; + + for ( i = 0 ; i < args.length ; i++ ) + { + if ( typeof( args[i] ) == 'function' ) + functions.push( args[i] ); + else + break; + } + + if ( i < args.length && typeof( args[i] ) == 'string' ) + { + msg = args[i]; + i++; + } + + if ( i < args.length && typeof( args[i]) == 'number' ) + relation = args[i]; + + var passed = ( relation == CKEDITOR.VALIDATE_AND ? true : false ); + for ( i = 0 ; i < functions.length ; i++ ) + { + if ( relation == CKEDITOR.VALIDATE_AND ) + passed = passed && functions[i]( value ); + else + passed = passed || functions[i]( value ); + } + + return !passed ? msg : true; + }; + }, + + regex : function( regex, msg ) + { + /* + * Can be greatly shortened by deriving from functions validator if code size + * turns out to be more important than performance. + */ + return function() + { + var value = this && this.getValue ? this.getValue() : arguments[0]; + return !regex.test( value ) ? msg : true; + }; + }, + + notEmpty : function( msg ) + { + return this.regex( notEmptyRegex, msg ); + }, + + integer : function( msg ) + { + return this.regex( integerRegex, msg ); + }, + + 'number' : function( msg ) + { + return this.regex( numberRegex, msg ); + }, + + 'cssLength' : function( msg ) + { + return this.functions( function( val ){ return cssLengthRegex.test( CKEDITOR.tools.trim( val ) ); }, msg ); + }, + + 'htmlLength' : function( msg ) + { + return this.functions( function( val ){ return htmlLengthRegex.test( CKEDITOR.tools.trim( val ) ); }, msg ); + }, + + 'inlineStyle' : function( msg ) + { + return this.functions( function( val ){ return inlineStyleRegex.test( CKEDITOR.tools.trim( val ) ); }, msg ); + }, + + equals : function( value, msg ) + { + return this.functions( function( val ){ return val == value; }, msg ); + }, + + notEqual : function( value, msg ) + { + return this.functions( function( val ){ return val != value; }, msg ); + } + }; + + CKEDITOR.on( 'instanceDestroyed', function( evt ) + { + // Remove dialog cover on last instance destroy. + if ( CKEDITOR.tools.isEmpty( CKEDITOR.instances ) ) + { + var currentTopDialog; + while ( ( currentTopDialog = CKEDITOR.dialog._.currentTop ) ) + currentTopDialog.hide(); + removeCovers(); + } + + var dialogs = evt.editor._.storedDialogs; + for ( var name in dialogs ) + dialogs[ name ].destroy(); + + }); + + })(); + + // Extend the CKEDITOR.editor class with dialog specific functions. + CKEDITOR.tools.extend( CKEDITOR.editor.prototype, + /** @lends CKEDITOR.editor.prototype */ + { + /** + * Loads and opens a registered dialog. + * @param {String} dialogName The registered name of the dialog. + * @param {Function} callback The function to be invoked after dialog instance created. + * @see CKEDITOR.dialog.add + * @example + * CKEDITOR.instances.editor1.openDialog( 'smiley' ); + * @returns {CKEDITOR.dialog} The dialog object corresponding to the dialog displayed. null if the dialog name is not registered. + */ + openDialog : function( dialogName, callback ) + { + if ( this.mode == 'wysiwyg' && CKEDITOR.env.ie ) + { + var selection = this.getSelection(); + selection && selection.lock(); + } + + var dialogDefinitions = CKEDITOR.dialog._.dialogDefinitions[ dialogName ], + dialogSkin = this.skin.dialog; + + if ( CKEDITOR.dialog._.currentTop === null ) + showCover( this ); + + // If the dialogDefinition is already loaded, open it immediately. + if ( typeof dialogDefinitions == 'function' && dialogSkin._isLoaded ) + { + var storedDialogs = this._.storedDialogs || + ( this._.storedDialogs = {} ); + + var dialog = storedDialogs[ dialogName ] || + ( storedDialogs[ dialogName ] = new CKEDITOR.dialog( this, dialogName ) ); + + callback && callback.call( dialog, dialog ); + dialog.show(); + + return dialog; + } + else if ( dialogDefinitions == 'failed' ) + { + hideCover(); + throw new Error( '[CKEDITOR.dialog.openDialog] Dialog "' + dialogName + '" failed when loading definition.' ); + } + + var me = this; + + function onDialogFileLoaded( success ) + { + var dialogDefinition = CKEDITOR.dialog._.dialogDefinitions[ dialogName ], + skin = me.skin.dialog; + + // Check if both skin part and definition is loaded. + if ( !skin._isLoaded || loadDefinition && typeof success == 'undefined' ) + return; + + // In case of plugin error, mark it as loading failed. + if ( typeof dialogDefinition != 'function' ) + CKEDITOR.dialog._.dialogDefinitions[ dialogName ] = 'failed'; + + me.openDialog( dialogName, callback ); + } + + if ( typeof dialogDefinitions == 'string' ) + { + var loadDefinition = 1; + CKEDITOR.scriptLoader.load( CKEDITOR.getUrl( dialogDefinitions ), onDialogFileLoaded, null, 0, 1 ); + } + + CKEDITOR.skins.load( this, 'dialog', onDialogFileLoaded ); + + return null; + } + }); +})(); + +CKEDITOR.plugins.add( 'dialog', + { + requires : [ 'dialogui' ] + }); + +// Dialog related configurations. + +/** + * The color of the dialog background cover. It should be a valid CSS color + * string. + * @name CKEDITOR.config.dialog_backgroundCoverColor + * @type String + * @default 'white' + * @example + * config.dialog_backgroundCoverColor = 'rgb(255, 254, 253)'; + */ + +/** + * The opacity of the dialog background cover. It should be a number within the + * range [0.0, 1.0]. + * @name CKEDITOR.config.dialog_backgroundCoverOpacity + * @type Number + * @default 0.5 + * @example + * config.dialog_backgroundCoverOpacity = 0.7; + */ + +/** + * If the dialog has more than one tab, put focus into the first tab as soon as dialog is opened. + * @name CKEDITOR.config.dialog_startupFocusTab + * @type Boolean + * @default false + * @example + * config.dialog_startupFocusTab = true; + */ + +/** + * The distance of magnetic borders used in moving and resizing dialogs, + * measured in pixels. + * @name CKEDITOR.config.dialog_magnetDistance + * @type Number + * @default 20 + * @example + * config.dialog_magnetDistance = 30; + */ + +/** + * The guideline to follow when generating the dialog buttons. There are 3 possible options: + *
    + *
  • 'OS' - the buttons will be displayed in the default order of the user's OS;
  • + *
  • 'ltr' - for Left-To-Right order;
  • + *
  • 'rtl' - for Right-To-Left order.
  • + *
+ * @name CKEDITOR.config.dialog_buttonsOrder + * @type String + * @default 'OS' + * @since 3.5 + * @example + * config.dialog_buttonsOrder = 'rtl'; + */ + +/** + * The dialog contents to removed. It's a string composed by dialog name and tab name with a colon between them. + * Separate each pair with semicolon (see example). + * Note: All names are case-sensitive. + * Note: Be cautious when specifying dialog tabs that are mandatory, like "info", dialog functionality might be broken because of this! + * @name CKEDITOR.config.removeDialogTabs + * @type String + * @since 3.5 + * @default '' + * @example + * config.removeDialogTabs = 'flash:advanced;image:Link'; + */ + +/** + * Fired when a dialog definition is about to be used to create a dialog into + * an editor instance. This event makes it possible to customize the definition + * before creating it. + *

Note that this event is called only the first time a specific dialog is + * opened. Successive openings will use the cached dialog, and this event will + * not get fired.

+ * @name CKEDITOR#dialogDefinition + * @event + * @param {CKEDITOR.dialog.definition} data The dialog defination that + * is being loaded. + * @param {CKEDITOR.editor} editor The editor instance that will use the + * dialog. + */ + +/** + * Fired when a tab is going to be selected in a dialog + * @name CKEDITOR.dialog#selectPage + * @event + * @param {String} page The id of the page that it's gonna be selected. + * @param {String} currentPage The id of the current page. + */ + +/** + * Fired when the user tries to dismiss a dialog + * @name CKEDITOR.dialog#cancel + * @event + * @param {Boolean} hide Whether the event should proceed or not. + */ + +/** + * Fired when the user tries to confirm a dialog + * @name CKEDITOR.dialog#ok + * @event + * @param {Boolean} hide Whether the event should proceed or not. + */ + +/** + * Fired when a dialog is shown + * @name CKEDITOR.dialog#show + * @event + */ + +/** + * Fired when a dialog is shown + * @name CKEDITOR.editor#dialogShow + * @event + */ + +/** + * Fired when a dialog is hidden + * @name CKEDITOR.dialog#hide + * @event + */ + +/** + * Fired when a dialog is hidden + * @name CKEDITOR.editor#dialogHide + * @event + */ + +/** + * Fired when a dialog is being resized. The event is fired on + * both the 'CKEDITOR.dialog' object and the dialog instance + * since 3.5.3, previously it's available only in the global object. + * @name CKEDITOR.dialog#resize + * @since 3.5 + * @event + * @param {CKEDITOR.dialog} dialog The dialog being resized (if + * it's fired on the dialog itself, this parameter isn't sent). + * @param {String} skin The skin name. + * @param {Number} width The new width. + * @param {Number} height The new height. + */ Index: openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/dialogadvtab/plugin.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/dialogadvtab/Attic/plugin.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/dialogadvtab/plugin.js 13 Feb 2012 18:53:11 -0000 1.1 @@ -0,0 +1,208 @@ +/* +Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +(function() +{ + +function setupAdvParams( element ) +{ + var attrName = this.att; + + var value = element && element.hasAttribute( attrName ) && element.getAttribute( attrName ) || ''; + + if ( value !== undefined ) + this.setValue( value ); +} + +function commitAdvParams() +{ + // Dialogs may use different parameters in the commit list, so, by + // definition, we take the first CKEDITOR.dom.element available. + var element; + + for ( var i = 0 ; i < arguments.length ; i++ ) + { + if ( arguments[ i ] instanceof CKEDITOR.dom.element ) + { + element = arguments[ i ]; + break; + } + } + + if ( element ) + { + var attrName = this.att, + value = this.getValue(); + + if ( value ) + element.setAttribute( attrName, value ); + else + element.removeAttribute( attrName, value ); + } +} + +CKEDITOR.plugins.add( 'dialogadvtab', +{ + /** + * + * @param tabConfig + * id, dir, classes, styles + */ + createAdvancedTab : function( editor, tabConfig ) + { + if ( !tabConfig ) + tabConfig = { id:1, dir:1, classes:1, styles:1 }; + + var lang = editor.lang.common; + + var result = + { + id : 'advanced', + label : lang.advancedTab, + title : lang.advancedTab, + elements : + [ + { + type : 'vbox', + padding : 1, + children : [] + } + ] + }; + + var contents = []; + + if ( tabConfig.id || tabConfig.dir ) + { + if ( tabConfig.id ) + { + contents.push( + { + id : 'advId', + att : 'id', + type : 'text', + label : lang.id, + setup : setupAdvParams, + commit : commitAdvParams + }); + } + + if ( tabConfig.dir ) + { + contents.push( + { + id : 'advLangDir', + att : 'dir', + type : 'select', + label : lang.langDir, + 'default' : '', + style : 'width:100%', + items : + [ + [ lang.notSet, '' ], + [ lang.langDirLTR, 'ltr' ], + [ lang.langDirRTL, 'rtl' ] + ], + setup : setupAdvParams, + commit : commitAdvParams + }); + } + + result.elements[ 0 ].children.push( + { + type : 'hbox', + widths : [ '50%', '50%' ], + children : [].concat( contents ) + }); + } + + if ( tabConfig.styles || tabConfig.classes ) + { + contents = []; + + if ( tabConfig.styles ) + { + contents.push( + { + id : 'advStyles', + att : 'style', + type : 'text', + label : lang.styles, + 'default' : '', + + validate : CKEDITOR.dialog.validate.inlineStyle( lang.invalidInlineStyle ), + onChange : function(){}, + + getStyle : function( name, defaultValue ) + { + var match = this.getValue().match( new RegExp( name + '\\s*:\\s*([^;]*)', 'i') ); + return match ? match[ 1 ] : defaultValue; + }, + + updateStyle : function( name, value ) + { + var styles = this.getValue(); + + // Remove the current value. + if ( styles ) + { + styles = styles + .replace( new RegExp( '\\s*' + name + '\s*:[^;]*(?:$|;\s*)', 'i' ), '' ) + .replace( /^[;\s]+/, '' ) + .replace( /\s+$/, '' ); + } + + if ( value ) + { + styles && !(/;\s*$/).test( styles ) && ( styles += '; ' ); + styles += name + ': ' + value; + } + + this.setValue( styles, 1 ); + + }, + + setup : setupAdvParams, + + commit : commitAdvParams + + }); + } + + if ( tabConfig.classes ) + { + contents.push( + { + type : 'hbox', + widths : [ '45%', '55%' ], + children : + [ + { + id : 'advCSSClasses', + att : 'class', + type : 'text', + label : lang.cssClasses, + 'default' : '', + setup : setupAdvParams, + commit : commitAdvParams + + } + ] + }); + } + + result.elements[ 0 ].children.push( + { + type : 'hbox', + widths : [ '50%', '50%' ], + children : [].concat( contents ) + }); + } + + return result; + } +}); + +})(); Index: openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/dialogui/plugin.js =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/dialogui/Attic/plugin.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/packages/xowiki/www/resources/ckeditor/_source/plugins/dialogui/plugin.js 13 Feb 2012 18:53:11 -0000 1.1 @@ -0,0 +1,1541 @@ +/* +Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +/** @fileoverview The "dialogui" plugin. */ + +CKEDITOR.plugins.add( 'dialogui' ); + +(function() +{ + var initPrivateObject = function( elementDefinition ) + { + this._ || ( this._ = {} ); + this._['default'] = this._.initValue = elementDefinition['default'] || ''; + this._.required = elementDefinition[ 'required' ] || false; + var args = [ this._ ]; + for ( var i = 1 ; i < arguments.length ; i++ ) + args.push( arguments[i] ); + args.push( true ); + CKEDITOR.tools.extend.apply( CKEDITOR.tools, args ); + return this._; + }, + textBuilder = + { + build : function( dialog, elementDefinition, output ) + { + return new CKEDITOR.ui.dialog.textInput( dialog, elementDefinition, output ); + } + }, + commonBuilder = + { + build : function( dialog, elementDefinition, output ) + { + return new CKEDITOR.ui.dialog[elementDefinition.type]( dialog, elementDefinition, output ); + } + }, + containerBuilder = + { + build : function( dialog, elementDefinition, output ) + { + var children = elementDefinition.children, + child, + childHtmlList = [], + childObjList = []; + for ( var i = 0 ; ( i < children.length && ( child = children[i] ) ) ; i++ ) + { + var childHtml = []; + childHtmlList.push( childHtml ); + childObjList.push( CKEDITOR.dialog._.uiElementBuilders[ child.type ].build( dialog, child, childHtml ) ); + } + return new CKEDITOR.ui.dialog[ elementDefinition.type ]( dialog, childObjList, childHtmlList, output, elementDefinition ); + } + }, + commonPrototype = + { + isChanged : function() + { + return this.getValue() != this.getInitValue(); + }, + + reset : function( noChangeEvent ) + { + this.setValue( this.getInitValue(), noChangeEvent ); + }, + + setInitValue : function() + { + this._.initValue = this.getValue(); + }, + + resetInitValue : function() + { + this._.initValue = this._['default']; + }, + + getInitValue : function() + { + return this._.initValue; + } + }, + commonEventProcessors = CKEDITOR.tools.extend( {}, CKEDITOR.ui.dialog.uiElement.prototype.eventProcessors, + { + onChange : function( dialog, func ) + { + if ( !this._.domOnChangeRegistered ) + { + dialog.on( 'load', function() + { + this.getInputElement().on( 'change', function() + { + // Make sure 'onchange' doesn't get fired after dialog closed. (#5719) + if ( !dialog.parts.dialog.isVisible() ) + return; + + this.fire( 'change', { value : this.getValue() } ); + }, this ); + }, this ); + this._.domOnChangeRegistered = true; + } + + this.on( 'change', func ); + } + }, true ), + eventRegex = /^on([A-Z]\w+)/, + cleanInnerDefinition = function( def ) + { + // An inner UI element should not have the parent's type, title or events. + for ( var i in def ) + { + if ( eventRegex.test( i ) || i == 'title' || i == 'type' ) + delete def[i]; + } + return def; + }; + + CKEDITOR.tools.extend( CKEDITOR.ui.dialog, + /** @lends CKEDITOR.ui.dialog */ + { + /** + * Base class for all dialog elements with a textual label on the left. + * @constructor + * @example + * @extends CKEDITOR.ui.dialog.uiElement + * @param {CKEDITOR.dialog} dialog + * Parent dialog object. + * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition + * The element definition. Accepted fields: + *
    + *
  • label (Required) The label string.
  • + *
  • labelLayout (Optional) Put 'horizontal' here if the + * label element is to be layed out horizontally. Otherwise a vertical + * layout will be used.
  • + *
  • widths (Optional) This applies only for horizontal + * layouts - an 2-element array of lengths to specify the widths of the + * label and the content element.
  • + *
+ * @param {Array} htmlList + * List of HTML code to output to. + * @param {Function} contentHtml + * A function returning the HTML code string to be added inside the content + * cell. + */ + labeledElement : function( dialog, elementDefinition, htmlList, contentHtml ) + { + if ( arguments.length < 4 ) + return; + + var _ = initPrivateObject.call( this, elementDefinition ); + _.labelId = CKEDITOR.tools.getNextId() + '_label'; + var children = this._.children = []; + /** @ignore */ + var innerHTML = function() + { + var html = [], + requiredClass = elementDefinition.required ? ' cke_required' : '' ; + if ( elementDefinition.labelLayout != 'horizontal' ) + html.push( '', + '' ); + else + { + var hboxDefinition = { + type : 'hbox', + widths : elementDefinition.widths, + padding : 0, + children : + [ + { + type : 'html', + html : '