2
0
mirror of https://github.com/ACSPRI/queXS synced 2024-04-02 12:12:16 +00:00

Can define centre and project information in administrative menu

Able to edit questionnaires RS text and name
Can disable/enable questionnaires for viewing in admin interface
RS text entered via new CKEditor including ability to insert tokens/template replace text
Added setting table and associated getter/setter functions (currently only used for centre information but could add more)
This commit is contained in:
azammitdcarf
2011-01-18 03:32:21 +00:00
parent ca02adf4c2
commit e5615b708a
628 changed files with 117476 additions and 21 deletions

View File

@@ -0,0 +1,211 @@
/*
Copyright (c) 2003-2010, 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 = '<div class="cke_accessibility_legend" role="document" aria-labelledby="' + id + '_arialbl" tabIndex="-1">%1</div>' +
'<span id="' + id + '_arialbl" class="cke_voice_label">' + lang.contents + ' </span>',
sectionTpl = '<h1>%1</h1><dl>%2</dl>',
itemTpl = '<dt>%1</dt><dd>%2</dd>';
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',
focus : function() {},
html : buildHelpContents() +
'<style type="text/css">' +
'.cke_accessibility_legend' +
'{' +
'width:600px;' +
'height:400px;' +
'padding-right:5px;' +
'overflow-y:auto;' +
'overflow-x:hidden;' +
'}' +
'.cke_accessibility_legend h1' +
'{' +
'font-size: 20px;' +
'border-bottom: 1px solid #AAA;' +
'margin: 5px 0px 15px;' +
'}' +
'.cke_accessibility_legend dl' +
'{' +
'margin-left: 5px;' +
'}' +
'.cke_accessibility_legend dt' +
'{' +
'font-size: 13px;' +
'font-weight: bold;' +
'}' +
'.cke_accessibility_legend dd' +
'{' +
'white-space:normal;' +
'margin:10px' +
'}' +
'</style>'
}
]
}
],
buttons : [ CKEDITOR.dialog.cancelButton ]
};
});

View File

@@ -0,0 +1,108 @@
/*
Copyright (c) 2003-2010, 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 next toolbar button with TAB or RIGHT ARROW. ' +
'Move to previous button with SHIFT+TAB 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}'
}
]
}
]
}
});

View File

@@ -0,0 +1,216 @@
/*
Copyright (c) 2003-2010, 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-2010, 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}'
}
]
}
]
}
});

View File

@@ -0,0 +1,46 @@
/*
Copyright (c) 2003-2010, 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.lang[ langCode ] );
editor.openDialog( commandName );
});
},
modes : { wysiwyg:1, source:1 },
canUndo : false
});
CKEDITOR.dialog.add( commandName, this.path + 'dialogs/a11yhelp.js' );
}
});
})();

View File

@@ -0,0 +1,73 @@
/*
Copyright (c) 2003-2010, 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 :
'<style type="text/css">' +
'.cke_about_container' +
'{' +
'color:#000 !important;' +
'padding:10px 10px 0;' +
'margin-top:5px' +
'}' +
'.cke_about_container p' +
'{' +
'margin: 0 0 10px;' +
'}' +
'.cke_about_container .cke_about_logo' +
'{' +
'height:81px;' +
'background-color:#fff;' +
'background-image:url(' + CKEDITOR.plugins.get( 'about' ).path + 'dialogs/logo_ckeditor.png);' +
'background-position:center; ' +
'background-repeat:no-repeat;' +
'margin-bottom:10px;' +
'}' +
'.cke_about_container a' +
'{' +
'cursor:pointer !important;' +
'color:blue !important;' +
'text-decoration:underline !important;' +
'}' +
'</style>' +
'<div class="cke_about_container">' +
'<div class="cke_about_logo"></div>' +
'<p>' +
'CKEditor ' + CKEDITOR.version + ' (revision ' + CKEDITOR.revision + ')<br>' +
'<a href="http://ckeditor.com/">http://ckeditor.com</a>' +
'</p>' +
'<p>' +
lang.moreInfo + '<br>' +
'<a href="http://ckeditor.com/license">http://ckeditor.com/license</a>' +
'</p>' +
'<p>' +
lang.copy.replace( '$1', '<a href="http://cksource.com/">CKSource</a> - Frederico Knabben' ) +
'</p>' +
'</div>'
}
]
}
],
buttons : [ CKEDITOR.dialog.cancelButton ]
};
} );

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -0,0 +1,23 @@
/*
Copyright (c) 2003-2010, 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;
editor.ui.addButton( 'About',
{
label : editor.lang.about.title,
command : 'about'
});
CKEDITOR.dialog.add( 'about', this.path + 'dialogs/about.js' );
}
});

View File

@@ -0,0 +1,228 @@
/*
Copyright (c) 2003-2010, 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 <html> element doesn't work as expected.
var doc = this,
head = this.getHead();
// Create style nodes for inline css. ( <style> content doesn't applied when setting via innerHTML )
html = html.replace( /(<style[^>]*>)([\s\S]*?)<\/style>/gi,
function ( match, startTag, styleText )
{
appendElement( head, 'style', startTag, styleText );
return '';
});
html = html.replace( /<base\b[^>]*\/>/i,
function( match )
{
appendElement( head, 'base', match );
return '';
});
html = html.replace( /<title>([\s\S]*)<\/title>/i,
function( match, title )
{
doc.$.title = title;
return '';
});
// Move the rest of head stuff.
html = html.replace( /<head>([\s\S]*)<\/head>/i,
function( headHtml )
{
// Inject the <head> HTML inside a <div>.
// Do that before getDocumentHead because WebKit moves
// <link css> elements to the <head> at this point.
var div = new CKEDITOR.dom.element( 'div', doc );
div.setHtml( headHtml );
// Move the <div> nodes to <head>.
div.moveChildren( head );
return '';
});
html.replace( /(<body[^>]*>)([\s\S]*)(?=$|<\/body>)/i,
function( match, startTag, innerHTML )
{
doc.getBody().setHtml( innerHTML );
var attrs = CKEDITOR.htmlParser.fragment.fromHtml( startTag ).children[ 0 ].attributes;
attrs && doc.getBody().setAttributes( attrs );
});
}
else
original_write.apply( this, arguments );
};
});

View File

@@ -0,0 +1,86 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
/**
* @file AutoGrow plugin
*/
(function(){
var resizeEditor = function( editor )
{
var doc = editor.document,
currentHeight = editor.window.getViewPaneSize().height,
newHeight;
// We can not use documentElement to calculate the height for IE (#6061).
// It is not good for IE Quirks, yet using offsetHeight would also not work as expected (#6408).
// We do the same for FF because of the html height workaround (#6341).
if ( CKEDITOR.env.ie || CKEDITOR.env.gecko )
newHeight = doc.getBody().$.scrollHeight + ( CKEDITOR.env.ie && CKEDITOR.env.quirks ? 0 : 24 );
else
newHeight = doc.getDocumentElement().$.offsetHeight;
var min = editor.config.autoGrow_minHeight,
max = editor.config.autoGrow_maxHeight;
( min == undefined ) && ( editor.config.autoGrow_minHeight = min = 200 );
if ( min )
newHeight = Math.max( newHeight, min );
if ( max )
newHeight = Math.min( newHeight, max );
if ( newHeight != currentHeight )
{
newHeight = editor.fire( 'autoGrow', { currentHeight : currentHeight, newHeight : newHeight } ).newHeight;
editor.resize( editor.container.getStyle( 'width' ), newHeight, true );
}
};
CKEDITOR.plugins.add( 'autogrow',
{
init : function( editor )
{
for ( var eventName in { contentDom:1, key:1, selectionChange:1, insertElement:1 } )
{
editor.on( eventName, function( evt )
{
var maximize = editor.getCommand( 'maximize' );
// Some time is required for insertHtml, and it gives other events better performance as well.
if ( evt.editor.mode == 'wysiwyg' &&
// Disable autogrow when the editor is maximized .(#6339)
( !maximize || maximize.state != CKEDITOR.TRISTATE_ON ) )
{
setTimeout( function(){ resizeEditor( evt.editor ); }, 100 );
}
});
}
}
});
})();
/**
* The minimum height to which the editor can reach using AutoGrow.
* @name CKEDITOR.config.autoGrow_minHeight
* @type Number
* @default 200
* @since 3.4
* @example
* config.autoGrow_minHeight = 300;
*/
/**
* The maximum height to which the editor can reach using AutoGrow. Zero means unlimited.
* @name CKEDITOR.config.autoGrow_maxHeight
* @type Number
* @default 0
* @since 3.4
* @example
* config.autoGrow_maxHeight = 400;
*/
/**
* Fired when the AutoGrow plugin is about to change the size of the editor.
* @name CKEDITOR#autogrow
* @event
* @param {Number} data.currentHeight The current height of the editor (before the resizing).
* @param {Number} data.newHeight The new height of the editor (after the resizing). It can be changed
* to determine another height to be used instead.
*/

View File

@@ -0,0 +1,101 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.add( 'basicstyles',
{
requires : [ 'styles', 'button' ],
init : function( editor )
{
// All buttons use the same code to register. So, to avoid
// duplications, let's use this tool function.
var addButtonCommand = function( buttonName, buttonLabel, commandName, styleDefiniton )
{
var style = new CKEDITOR.style( styleDefiniton );
editor.attachStyleStateChange( style, function( state )
{
editor.getCommand( commandName ).setState( state );
});
editor.addCommand( commandName, new CKEDITOR.styleCommand( style ) );
editor.ui.addButton( buttonName,
{
label : buttonLabel,
command : commandName
});
};
var config = editor.config,
lang = editor.lang;
addButtonCommand( 'Bold' , lang.bold , 'bold' , config.coreStyles_bold );
addButtonCommand( 'Italic' , lang.italic , 'italic' , config.coreStyles_italic );
addButtonCommand( 'Underline' , lang.underline , 'underline' , config.coreStyles_underline );
addButtonCommand( 'Strike' , lang.strike , 'strike' , config.coreStyles_strike );
addButtonCommand( 'Subscript' , lang.subscript , 'subscript' , config.coreStyles_subscript );
addButtonCommand( 'Superscript' , lang.superscript , 'superscript' , config.coreStyles_superscript );
}
});
// Basic Inline Styles.
/**
* The style definition to be used to apply the bold style in the text.
* @type Object
* @example
* config.coreStyles_bold = { element : 'b', overrides : 'strong' };
* @example
* config.coreStyles_bold = { element : 'span', attributes : {'class': 'Bold'} };
*/
CKEDITOR.config.coreStyles_bold = { element : 'strong', overrides : 'b' };
/**
* The style definition to be used to apply the italic style in the text.
* @type Object
* @default { element : 'em', overrides : 'i' }
* @example
* config.coreStyles_italic = { element : 'i', overrides : 'em' };
* @example
* CKEDITOR.config.coreStyles_italic = { element : 'span', attributes : {'class': 'Italic'} };
*/
CKEDITOR.config.coreStyles_italic = { element : 'em', overrides : 'i' };
/**
* The style definition to be used to apply the underline style in the text.
* @type Object
* @default { element : 'u' }
* @example
* CKEDITOR.config.coreStyles_underline = { element : 'span', attributes : {'class': 'Underline'}};
*/
CKEDITOR.config.coreStyles_underline = { element : 'u' };
/**
* The style definition to be used to apply the strike style in the text.
* @type Object
* @default { element : 'strike' }
* @example
* CKEDITOR.config.coreStyles_strike = { element : 'span', attributes : {'class': 'StrikeThrough'}, overrides : 'strike' };
*/
CKEDITOR.config.coreStyles_strike = { element : 'strike' };
/**
* The style definition to be used to apply the subscript style in the text.
* @type Object
* @default { element : 'sub' }
* @example
* CKEDITOR.config.coreStyles_subscript = { element : 'span', attributes : {'class': 'Subscript'}, overrides : 'sub' };
*/
CKEDITOR.config.coreStyles_subscript = { element : 'sub' };
/**
* The style definition to be used to apply the superscript style in the text.
* @type Object
* @default { element : 'sup' }
* @example
* CKEDITOR.config.coreStyles_superscript = { element : 'span', attributes : {'class': 'Superscript'}, overrides : 'sup' };
*/
CKEDITOR.config.coreStyles_superscript = { element : 'sup' };

View File

@@ -0,0 +1,265 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
(function()
{
var guardElements = { table:1, tbody: 1, ul:1, ol:1, blockquote:1, div:1, tr: 1 },
directSelectionGuardElements = {},
// All guard elements which can have a direction applied on them.
allGuardElements = {};
CKEDITOR.tools.extend( directSelectionGuardElements, guardElements, { tr:1, p:1, div:1, li:1 } );
CKEDITOR.tools.extend( allGuardElements, directSelectionGuardElements, { td:1 } );
function onSelectionChange( e )
{
setToolbarStates( e );
handleMixedDirContent( e );
}
function setToolbarStates( evt )
{
var editor = evt.editor,
path = evt.data.path;
var useComputedState = editor.config.useComputedState,
selectedElement;
useComputedState = useComputedState === undefined || useComputedState;
// We can use computedState provided by the browser or traverse parents manually.
if ( !useComputedState )
selectedElement = getElementForDirection( path.lastElement );
selectedElement = selectedElement || path.block || path.blockLimit;
if ( !selectedElement || selectedElement.getName() == 'body' )
return;
var selectionDir = useComputedState ?
selectedElement.getComputedStyle( 'direction' ) :
selectedElement.getStyle( 'direction' ) || selectedElement.getAttribute( 'dir' );
editor.getCommand( 'bidirtl' ).setState( selectionDir == 'rtl' ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF );
editor.getCommand( 'bidiltr' ).setState( selectionDir == 'ltr' ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF );
}
function handleMixedDirContent( evt )
{
var editor = evt.editor,
chromeRoot = editor.container.getChild( 1 ),
directionNode = evt.data.path.block || evt.data.path.blockLimit;
if ( directionNode && editor.lang.dir != directionNode.getComputedStyle( 'direction' ) )
chromeRoot.addClass( 'cke_mixed_dir_content' );
else
chromeRoot.removeClass( 'cke_mixed_dir_content' );
}
/**
* Returns element with possibility of applying the direction.
* @param node
*/
function getElementForDirection( node )
{
while ( node && !( node.getName() in allGuardElements || node.is( 'body' ) ) )
{
var parent = node.getParent();
if ( !parent )
break;
node = parent;
}
return node;
}
function switchDir( element, dir, editor, database )
{
// Mark this element as processed by switchDir.
CKEDITOR.dom.element.setMarker( database, element, 'bidi_processed', 1 );
// Check whether one of the ancestors has already been styled.
var parent = element;
while ( ( parent = parent.getParent() ) && !parent.is( 'body' ) )
{
if ( parent.getCustomData( 'bidi_processed' ) )
{
// Ancestor style must dominate.
element.removeStyle( 'direction' );
element.removeAttribute( 'dir' );
return null;
}
}
var useComputedState = ( 'useComputedState' in editor.config ) ? editor.config.useComputedState : 1;
var elementDir = useComputedState ? element.getComputedStyle( 'direction' )
: element.getStyle( 'direction' ) || element.hasAttribute( 'dir' );
// Stop if direction is same as present.
if ( elementDir == dir )
return null;
// Reuse computedState if we already have it.
var dirBefore = useComputedState ? elementDir : element.getComputedStyle( 'direction' );
// Clear direction on this element.
element.removeStyle( 'direction' );
// Do the second check when computed state is ON, to check
// if we need to apply explicit direction on this element.
if ( useComputedState )
{
element.removeAttribute( 'dir' );
if ( dir != element.getComputedStyle( 'direction' ) )
element.setAttribute( 'dir', dir );
}
else
// Set new direction for this element.
element.setAttribute( 'dir', dir );
// If the element direction changed, we need to switch the margins of
// the element and all its children, so it will get really reflected
// like a mirror. (#5910)
if ( dir != dirBefore )
{
editor.fire( 'dirChanged',
{
node : element,
dir : dir
} );
}
editor.forceNextSelectionCheck();
return null;
}
function getFullySelected( range, elements )
{
var ancestor = range.getCommonAncestor( false, true );
range.enlarge( CKEDITOR.ENLARGE_BLOCK_CONTENTS );
if ( range.checkBoundaryOfElement( ancestor, CKEDITOR.START )
&& range.checkBoundaryOfElement( ancestor, CKEDITOR.END ) )
{
var parent;
while ( ancestor && ancestor.type == CKEDITOR.NODE_ELEMENT
&& ( parent = ancestor.getParent() )
&& parent.getChildCount() == 1
&& ( !( ancestor.getName() in elements ) || ( parent.getName() in elements ) )
)
ancestor = parent;
return ancestor.type == CKEDITOR.NODE_ELEMENT
&& ( ancestor.getName() in elements )
&& ancestor;
}
}
function bidiCommand( dir )
{
return function( editor )
{
var selection = editor.getSelection(),
enterMode = editor.config.enterMode,
ranges = selection.getRanges();
if ( ranges && ranges.length )
{
var database = {};
// Creates bookmarks for selection, as we may split some blocks.
var bookmarks = selection.createBookmarks();
var rangeIterator = ranges.createIterator(),
range,
i = 0;
while ( ( range = rangeIterator.getNextRange( 1 ) ) )
{
// Apply do directly selected elements from guardElements.
var selectedElement = range.getEnclosedNode();
// If this is not our element of interest, apply to fully selected elements from guardElements.
if ( !selectedElement || selectedElement
&& !( selectedElement.type == CKEDITOR.NODE_ELEMENT && selectedElement.getName() in directSelectionGuardElements )
)
selectedElement = getFullySelected( range, guardElements );
if ( selectedElement && !selectedElement.isReadOnly() )
switchDir( selectedElement, dir, editor, database );
var iterator,
block;
// Walker searching for guardElements.
var walker = new CKEDITOR.dom.walker( range );
var start = bookmarks[ i ].startNode,
end = bookmarks[ i++ ].endNode;
walker.evaluator = function( node )
{
return !! ( node.type == CKEDITOR.NODE_ELEMENT
&& node.getName() in guardElements
&& !( node.getName() == ( enterMode == CKEDITOR.ENTER_P ) ? 'p' : 'div'
&& node.getParent().type == CKEDITOR.NODE_ELEMENT
&& node.getParent().getName() == 'blockquote' )
// Element must be fully included in the range as well. (#6485).
&& node.getPosition( start ) & CKEDITOR.POSITION_FOLLOWING
&& ( ( node.getPosition( end ) & CKEDITOR.POSITION_PRECEDING + CKEDITOR.POSITION_CONTAINS ) == CKEDITOR.POSITION_PRECEDING ) );
};
while ( ( block = walker.next() ) )
switchDir( block, dir, editor, database );
iterator = range.createIterator();
iterator.enlargeBr = enterMode != CKEDITOR.ENTER_BR;
while ( ( block = iterator.getNextParagraph( enterMode == CKEDITOR.ENTER_P ? 'p' : 'div' ) ) )
!block.isReadOnly() && switchDir( block, dir, editor, database );
}
CKEDITOR.dom.element.clearAllMarkers( database );
editor.forceNextSelectionCheck();
// Restore selection position.
selection.selectBookmarks( bookmarks );
editor.focus();
}
};
}
CKEDITOR.plugins.add( 'bidi',
{
requires : [ 'styles', 'button' ],
init : function( editor )
{
// All buttons use the same code to register. So, to avoid
// duplications, let's use this tool function.
var addButtonCommand = function( buttonName, buttonLabel, commandName, commandExec )
{
editor.addCommand( commandName, new CKEDITOR.command( editor, { exec : commandExec }) );
editor.ui.addButton( buttonName,
{
label : buttonLabel,
command : commandName
});
};
var lang = editor.lang.bidi;
addButtonCommand( 'BidiLtr', lang.ltr, 'bidiltr', bidiCommand( 'ltr' ) );
addButtonCommand( 'BidiRtl', lang.rtl, 'bidirtl', bidiCommand( 'rtl' ) );
editor.on( 'selectionChange', onSelectionChange );
}
});
})();

View File

@@ -0,0 +1,301 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
/**
* @file Blockquote.
*/
(function()
{
function getState( editor, path )
{
var firstBlock = path.block || path.blockLimit;
if ( !firstBlock || firstBlock.getName() == 'body' )
return CKEDITOR.TRISTATE_OFF;
// See if the first block has a blockquote parent.
if ( firstBlock.getAscendant( 'blockquote', true ) )
return CKEDITOR.TRISTATE_ON;
return CKEDITOR.TRISTATE_OFF;
}
function onSelectionChange( evt )
{
var editor = evt.editor,
command = editor.getCommand( 'blockquote' );
command.state = getState( editor, evt.data.path );
command.fire( 'state' );
}
function noBlockLeft( bqBlock )
{
for ( var i = 0, length = bqBlock.getChildCount(), child ; i < length && ( child = bqBlock.getChild( i ) ) ; i++ )
{
if ( child.type == CKEDITOR.NODE_ELEMENT && child.isBlockBoundary() )
return false;
}
return true;
}
var commandObject =
{
exec : function( editor )
{
var state = editor.getCommand( 'blockquote' ).state,
selection = editor.getSelection(),
range = selection && selection.getRanges( true )[0];
if ( !range )
return;
var bookmarks = selection.createBookmarks();
// Kludge for #1592: if the bookmark nodes are in the beginning of
// blockquote, then move them to the nearest block element in the
// blockquote.
if ( CKEDITOR.env.ie )
{
var bookmarkStart = bookmarks[0].startNode,
bookmarkEnd = bookmarks[0].endNode,
cursor;
if ( bookmarkStart && bookmarkStart.getParent().getName() == 'blockquote' )
{
cursor = bookmarkStart;
while ( ( cursor = cursor.getNext() ) )
{
if ( cursor.type == CKEDITOR.NODE_ELEMENT &&
cursor.isBlockBoundary() )
{
bookmarkStart.move( cursor, true );
break;
}
}
}
if ( bookmarkEnd
&& bookmarkEnd.getParent().getName() == 'blockquote' )
{
cursor = bookmarkEnd;
while ( ( cursor = cursor.getPrevious() ) )
{
if ( cursor.type == CKEDITOR.NODE_ELEMENT &&
cursor.isBlockBoundary() )
{
bookmarkEnd.move( cursor );
break;
}
}
}
}
var iterator = range.createIterator(),
block;
if ( state == CKEDITOR.TRISTATE_OFF )
{
var paragraphs = [];
while ( ( block = iterator.getNextParagraph() ) )
paragraphs.push( block );
// If no paragraphs, create one from the current selection position.
if ( paragraphs.length < 1 )
{
var para = editor.document.createElement( editor.config.enterMode == CKEDITOR.ENTER_P ? 'p' : 'div' ),
firstBookmark = bookmarks.shift();
range.insertNode( para );
para.append( new CKEDITOR.dom.text( '\ufeff', editor.document ) );
range.moveToBookmark( firstBookmark );
range.selectNodeContents( para );
range.collapse( true );
firstBookmark = range.createBookmark();
paragraphs.push( para );
bookmarks.unshift( firstBookmark );
}
// Make sure all paragraphs have the same parent.
var commonParent = paragraphs[0].getParent(),
tmp = [];
for ( var i = 0 ; i < paragraphs.length ; i++ )
{
block = paragraphs[i];
commonParent = commonParent.getCommonAncestor( block.getParent() );
}
// The common parent must not be the following tags: table, tbody, tr, ol, ul.
var denyTags = { table : 1, tbody : 1, tr : 1, ol : 1, ul : 1 };
while ( denyTags[ commonParent.getName() ] )
commonParent = commonParent.getParent();
// Reconstruct the block list to be processed such that all resulting blocks
// satisfy parentNode.equals( commonParent ).
var lastBlock = null;
while ( paragraphs.length > 0 )
{
block = paragraphs.shift();
while ( !block.getParent().equals( commonParent ) )
block = block.getParent();
if ( !block.equals( lastBlock ) )
tmp.push( block );
lastBlock = block;
}
// If any of the selected blocks is a blockquote, remove it to prevent
// nested blockquotes.
while ( tmp.length > 0 )
{
block = tmp.shift();
if ( block.getName() == 'blockquote' )
{
var docFrag = new CKEDITOR.dom.documentFragment( editor.document );
while ( block.getFirst() )
{
docFrag.append( block.getFirst().remove() );
paragraphs.push( docFrag.getLast() );
}
docFrag.replace( block );
}
else
paragraphs.push( block );
}
// Now we have all the blocks to be included in a new blockquote node.
var bqBlock = editor.document.createElement( 'blockquote' );
bqBlock.insertBefore( paragraphs[0] );
while ( paragraphs.length > 0 )
{
block = paragraphs.shift();
bqBlock.append( block );
}
}
else if ( state == CKEDITOR.TRISTATE_ON )
{
var moveOutNodes = [],
database = {};
while ( ( block = iterator.getNextParagraph() ) )
{
var bqParent = null,
bqChild = null;
while ( block.getParent() )
{
if ( block.getParent().getName() == 'blockquote' )
{
bqParent = block.getParent();
bqChild = block;
break;
}
block = block.getParent();
}
// Remember the blocks that were recorded down in the moveOutNodes array
// to prevent duplicates.
if ( bqParent && bqChild && !bqChild.getCustomData( 'blockquote_moveout' ) )
{
moveOutNodes.push( bqChild );
CKEDITOR.dom.element.setMarker( database, bqChild, 'blockquote_moveout', true );
}
}
CKEDITOR.dom.element.clearAllMarkers( database );
var movedNodes = [],
processedBlockquoteBlocks = [];
database = {};
while ( moveOutNodes.length > 0 )
{
var node = moveOutNodes.shift();
bqBlock = node.getParent();
// If the node is located at the beginning or the end, just take it out
// without splitting. Otherwise, split the blockquote node and move the
// paragraph in between the two blockquote nodes.
if ( !node.getPrevious() )
node.remove().insertBefore( bqBlock );
else if ( !node.getNext() )
node.remove().insertAfter( bqBlock );
else
{
node.breakParent( node.getParent() );
processedBlockquoteBlocks.push( node.getNext() );
}
// Remember the blockquote node so we can clear it later (if it becomes empty).
if ( !bqBlock.getCustomData( 'blockquote_processed' ) )
{
processedBlockquoteBlocks.push( bqBlock );
CKEDITOR.dom.element.setMarker( database, bqBlock, 'blockquote_processed', true );
}
movedNodes.push( node );
}
CKEDITOR.dom.element.clearAllMarkers( database );
// Clear blockquote nodes that have become empty.
for ( i = processedBlockquoteBlocks.length - 1 ; i >= 0 ; i-- )
{
bqBlock = processedBlockquoteBlocks[i];
if ( noBlockLeft( bqBlock ) )
bqBlock.remove();
}
if ( editor.config.enterMode == CKEDITOR.ENTER_BR )
{
var firstTime = true;
while ( movedNodes.length )
{
node = movedNodes.shift();
if ( node.getName() == 'div' )
{
docFrag = new CKEDITOR.dom.documentFragment( editor.document );
var needBeginBr = firstTime && node.getPrevious() &&
!( node.getPrevious().type == CKEDITOR.NODE_ELEMENT && node.getPrevious().isBlockBoundary() );
if ( needBeginBr )
docFrag.append( editor.document.createElement( 'br' ) );
var needEndBr = node.getNext() &&
!( node.getNext().type == CKEDITOR.NODE_ELEMENT && node.getNext().isBlockBoundary() );
while ( node.getFirst() )
node.getFirst().remove().appendTo( docFrag );
if ( needEndBr )
docFrag.append( editor.document.createElement( 'br' ) );
docFrag.replace( node );
firstTime = false;
}
}
}
}
selection.selectBookmarks( bookmarks );
editor.focus();
}
};
CKEDITOR.plugins.add( 'blockquote',
{
init : function( editor )
{
editor.addCommand( 'blockquote', commandObject );
editor.ui.addButton( 'Blockquote',
{
label : editor.lang.blockquote,
command : 'blockquote'
} );
editor.on( 'selectionChange', onSelectionChange );
},
requires : [ 'domiterator' ]
} );
})();

View File

@@ -0,0 +1,295 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.add( 'button',
{
beforeInit : function( editor )
{
editor.ui.addHandler( CKEDITOR.UI_BUTTON, CKEDITOR.ui.button.handler );
}
});
/**
* Button UI element.
* @constant
* @example
*/
CKEDITOR.UI_BUTTON = 1;
/**
* Represents a button UI element. This class should not be called directly. To
* create new buttons use {@link CKEDITOR.ui.prototype.addButton} instead.
* @constructor
* @param {Object} definition The button definition.
* @example
*/
CKEDITOR.ui.button = function( definition )
{
// Copy all definition properties to this object.
CKEDITOR.tools.extend( this, definition,
// Set defaults.
{
title : definition.label,
className : definition.className || ( definition.command && 'cke_button_' + definition.command ) || '',
click : definition.click || function( editor )
{
editor.execCommand( definition.command );
}
});
this._ = {};
};
/**
* Transforms a button definition in a {@link CKEDITOR.ui.button} instance.
* @type Object
* @example
*/
CKEDITOR.ui.button.handler =
{
create : function( definition )
{
return new CKEDITOR.ui.button( definition );
}
};
/**
* Handles a button click.
* @private
*/
CKEDITOR.ui.button._ =
{
instances : [],
keydown : function( index, ev )
{
var instance = CKEDITOR.ui.button._.instances[ index ];
if ( instance.onkey )
{
ev = new CKEDITOR.dom.event( ev );
return ( instance.onkey( instance, ev.getKeystroke() ) !== false );
}
},
focus : function( index, ev )
{
var instance = CKEDITOR.ui.button._.instances[ index ],
retVal;
if ( instance.onfocus )
retVal = ( instance.onfocus( instance, new CKEDITOR.dom.event( ev ) ) !== false );
// FF2: prevent focus event been bubbled up to editor container, which caused unexpected editor focus.
if ( CKEDITOR.env.gecko && CKEDITOR.env.version < 10900 )
ev.preventBubble();
return retVal;
}
};
( function()
{
var keydownFn = CKEDITOR.tools.addFunction( CKEDITOR.ui.button._.keydown, CKEDITOR.ui.button._ ),
focusFn = CKEDITOR.tools.addFunction( CKEDITOR.ui.button._.focus, CKEDITOR.ui.button._ );
CKEDITOR.ui.button.prototype =
{
canGroup : true,
/**
* Renders the button.
* @param {CKEDITOR.editor} editor The editor instance which this button is
* to be used by.
* @param {Array} output The output array to which append the HTML relative
* to this button.
* @example
*/
render : function( editor, output )
{
var env = CKEDITOR.env,
id = this._.id = CKEDITOR.tools.getNextId(),
classes = '',
command = this.command, // Get the command name.
clickFn,
index;
this._.editor = editor;
var instance =
{
id : id,
button : this,
editor : editor,
focus : function()
{
var element = CKEDITOR.document.getById( id );
element.focus();
},
execute : function()
{
this.button.click( editor );
}
};
instance.clickFn = clickFn = CKEDITOR.tools.addFunction( instance.execute, instance );
instance.index = index = CKEDITOR.ui.button._.instances.push( instance ) - 1;
// Indicate a mode sensitive button.
if ( this.modes )
{
var modeStates = {};
editor.on( 'beforeModeUnload', function()
{
modeStates[ editor.mode ] = this._.state;
}, this );
editor.on( 'mode', function()
{
var mode = editor.mode;
// Restore saved button state.
this.setState( this.modes[ mode ] ?
modeStates[ mode ] != undefined ? modeStates[ mode ] :
CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED );
}, this);
}
else if ( command )
{
// Get the command instance.
command = editor.getCommand( command );
if ( command )
{
command.on( 'state', function()
{
this.setState( command.state );
}, this);
classes += 'cke_' + (
command.state == CKEDITOR.TRISTATE_ON ? 'on' :
command.state == CKEDITOR.TRISTATE_DISABLED ? 'disabled' :
'off' );
}
}
if ( !command )
classes += 'cke_off';
if ( this.className )
classes += ' ' + this.className;
output.push(
'<span class="cke_button">',
'<a id="', id, '"' +
' class="', classes, '"',
env.gecko && env.version >= 10900 && !env.hc ? '' : '" href="javascript:void(\''+ ( this.title || '' ).replace( "'", '' )+ '\')"',
' title="', this.title, '"' +
' tabindex="-1"' +
' hidefocus="true"' +
' role="button"' +
' aria-labelledby="' + id + '_label"' +
( this.hasArrow ? ' aria-haspopup="true"' : '' ) );
// Some browsers don't cancel key events in the keydown but in the
// keypress.
// TODO: Check if really needed for Gecko+Mac.
if ( env.opera || ( env.gecko && env.mac ) )
{
output.push(
' onkeypress="return false;"' );
}
// With Firefox, we need to force the button to redraw, otherwise it
// will remain in the focus state.
if ( env.gecko )
{
output.push(
' onblur="this.style.cssText = this.style.cssText;"' );
}
output.push(
' onkeydown="return CKEDITOR.tools.callFunction(', keydownFn, ', ', index, ', event);"' +
' onfocus="return CKEDITOR.tools.callFunction(', focusFn,', ', index, ', event);"' +
' onclick="CKEDITOR.tools.callFunction(', clickFn, ', this); return false;">' +
'<span class="cke_icon"' );
if ( this.icon )
{
var offset = ( this.iconOffset || 0 ) * -16;
output.push( ' style="background-image:url(', CKEDITOR.getUrl( this.icon ), ');background-position:0 ' + offset + 'px;"' );
}
output.push(
'>&nbsp;</span>' +
'<span id="', id, '_label" class="cke_label">', this.label, '</span>' );
if ( this.hasArrow )
{
output.push(
'<span class="cke_buttonarrow">'
// BLACK DOWN-POINTING TRIANGLE
+ ( CKEDITOR.env.hc ? '&#9660;' : '&nbsp;' )
+ '</span>' );
}
output.push(
'</a>',
'</span>' );
if ( this.onRender )
this.onRender();
return instance;
},
setState : function( state )
{
if ( this._.state == state )
return false;
this._.state = state;
var element = CKEDITOR.document.getById( this._.id );
if ( element )
{
element.setState( state );
state == CKEDITOR.TRISTATE_DISABLED ?
element.setAttribute( 'aria-disabled', true ) :
element.removeAttribute( 'aria-disabled' );
state == CKEDITOR.TRISTATE_ON ?
element.setAttribute( 'aria-pressed', true ) :
element.removeAttribute( 'aria-pressed' );
return true;
}
else
return false;
}
};
})();
/**
* Adds a button definition to the UI elements list.
* @param {String} The button name.
* @param {Object} The button definition.
* @example
* editorInstance.ui.addButton( 'MyBold',
* {
* label : 'My Bold',
* command : 'bold'
* });
*/
CKEDITOR.ui.prototype.addButton = function( name, definition )
{
this.add( name, CKEDITOR.UI_BUTTON, definition );
};
CKEDITOR.on( 'reset', function()
{
CKEDITOR.ui.button._.instances = [];
});

View File

@@ -0,0 +1,212 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.dialog.add( 'paste', function( editor )
{
var lang = editor.lang.clipboard;
var isCustomDomain = CKEDITOR.env.isCustomDomain();
function onPasteFrameLoad( win )
{
var doc = new CKEDITOR.dom.document( win.document ),
docElement = doc.$;
var script = doc.getById( 'cke_actscrpt' );
script && script.remove();
CKEDITOR.env.ie ?
docElement.body.contentEditable = "true" :
docElement.designMode = "on";
// IE before version 8 will leave cursor blinking inside the document after
// editor blurred unless we clean up the selection. (#4716)
if ( CKEDITOR.env.ie && CKEDITOR.env.version < 8 )
{
doc.getWindow().on( 'blur', function()
{
docElement.selection.empty();
} );
}
doc.on( "keydown", function( e )
{
var domEvent = e.data,
key = domEvent.getKeystroke(),
processed;
switch( key )
{
case 27 :
this.hide();
processed = 1;
break;
case 9 :
case CKEDITOR.SHIFT + 9 :
this.changeFocus( true );
processed = 1;
}
processed && domEvent.preventDefault();
}, this );
editor.fire( 'ariaWidget', new CKEDITOR.dom.element( win.frameElement ) );
}
return {
title : lang.title,
minWidth : CKEDITOR.env.ie && CKEDITOR.env.quirks ? 370 : 350,
minHeight : CKEDITOR.env.quirks ? 250 : 245,
onShow : function()
{
// FIREFOX BUG: Force the browser to render the dialog to make the to-be-
// inserted iframe editable. (#3366)
this.parts.dialog.$.offsetHeight;
var htmlToLoad =
'<html dir="' + editor.config.contentsLangDirection + '"' +
' lang="' + ( editor.config.contentsLanguage || editor.langCode ) + '">' +
'<head><style>body { margin: 3px; height: 95%; } </style></head><body>' +
'<script id="cke_actscrpt" type="text/javascript">' +
'window.parent.CKEDITOR.tools.callFunction( ' + CKEDITOR.tools.addFunction( onPasteFrameLoad, this ) + ', this );' +
'</script></body>' +
'</html>';
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' +
' class="cke_pasteframe"' +
' frameborder="0" ' +
' allowTransparency="true"' +
' src="' + src + '"' +
' role="region"' +
' aria-label="' + lang.pasteArea + '"' +
' aria-describedby="' + this.getContentElement( 'general', 'pasteMsg' ).domId + '"' +
' aria-multiple="true"' +
'></iframe>' );
iframe.on( 'load', function( e )
{
e.removeListener();
var doc = iframe.getFrameDocument();
doc.write( htmlToLoad );
if ( CKEDITOR.env.air )
onPasteFrameLoad.call( this, doc.getWindow().$ );
},
this );
iframe.setCustomData( 'dialog', this );
var field = this.getContentElement( 'general', 'editing_area' ),
container = field.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( '<span tabindex="-1" style="position:absolute;" role="presentation"></span>' );
focusGrabber.on( 'focus', function()
{
iframe.$.contentWindow.focus();
});
container.append( focusGrabber );
// Override focus handler on field.
field.focus = function()
{
focusGrabber.focus();
this.fire( 'focus' );
};
}
field.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' );
}
},
onHide : function()
{
if ( CKEDITOR.env.ie )
this.getParentEditor().document.getBody().$.contentEditable = 'true';
},
onLoad : function()
{
if ( ( CKEDITOR.env.ie7Compat || CKEDITOR.env.ie6Compat ) && editor.lang.dir == 'rtl' )
this.parts.contents.setStyle( 'overflow', 'hidden' );
},
onOk : function()
{
var container = this.getContentElement( 'general', 'editing_area' ).getElement(),
iframe = container.getElementsByTag( 'iframe' ).getItem( 0 ),
editor = this.getParentEditor(),
html = iframe.$.contentWindow.document.body.innerHTML;
setTimeout( function(){
editor.fire( 'paste', { 'html' : html } );
}, 0 );
},
contents : [
{
id : 'general',
label : editor.lang.common.generalTab,
elements : [
{
type : 'html',
id : 'securityMsg',
html : '<div style="white-space:normal;width:340px;">' + lang.securityMsg + '</div>'
},
{
type : 'html',
id : 'pasteMsg',
html : '<div style="white-space:normal;width:340px;">'+lang.pasteMsg +'</div>'
},
{
type : 'html',
id : 'editing_area',
style : 'width: 100%; height: 100%;',
html : '',
focus : function()
{
var win = this.getInputElement().$.contentWindow;
// #3291 : JAWS needs the 500ms delay to detect that the editor iframe
// iframe is no longer editable. So that it will put the focus into the
// Paste from Word dialog's editable area instead.
setTimeout( function()
{
win.focus();
}, 500 );
}
}
]
}
]
};
});

View File

@@ -0,0 +1,412 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
/**
* @file Clipboard support
*/
(function()
{
// Tries to execute any of the paste, cut or copy commands in IE. Returns a
// boolean indicating that the operation succeeded.
var execIECommand = function( editor, command )
{
var doc = editor.document,
body = doc.getBody();
var enabled = 0;
var onExec = function()
{
enabled = 1;
};
// The following seems to be the only reliable way to detect that
// clipboard commands are enabled in IE. It will fire the
// onpaste/oncut/oncopy events only if the security settings allowed
// the command to execute.
body.on( command, onExec );
// IE6/7: document.execCommand has problem to paste into positioned element.
( CKEDITOR.env.version > 7 ? doc.$ : doc.$.selection.createRange() ) [ 'execCommand' ]( command );
body.removeListener( command, onExec );
return enabled;
};
// Attempts to execute the Cut and Copy operations.
var tryToCutCopy =
CKEDITOR.env.ie ?
function( editor, type )
{
return execIECommand( editor, type );
}
: // !IE.
function( editor, type )
{
try
{
// Other browsers throw an error if the command is disabled.
return editor.document.$.execCommand( type, false, null );
}
catch( e )
{
return false;
}
};
// A class that represents one of the cut or copy commands.
var cutCopyCmd = function( type )
{
this.type = type;
this.canUndo = ( this.type == 'cut' ); // We can't undo copy to clipboard.
};
cutCopyCmd.prototype =
{
exec : function( editor, data )
{
this.type == 'cut' && fixCut( editor );
var success = tryToCutCopy( editor, this.type );
if ( !success )
alert( editor.lang.clipboard[ this.type + 'Error' ] ); // Show cutError or copyError.
return success;
}
};
// Paste command.
var pasteCmd =
{
canUndo : false,
exec :
CKEDITOR.env.ie ?
function( editor )
{
// Prevent IE from pasting at the begining of the document.
editor.focus();
if ( !editor.document.getBody().fire( 'beforepaste' )
&& !execIECommand( editor, 'paste' ) )
{
editor.fire( 'pasteDialog' );
return false;
}
}
:
function( editor )
{
try
{
if ( !editor.document.getBody().fire( 'beforepaste' )
&& !editor.document.$.execCommand( 'Paste', false, null ) )
{
throw 0;
}
}
catch ( e )
{
setTimeout( function()
{
editor.fire( 'pasteDialog' );
}, 0 );
return false;
}
}
};
// Listens for some clipboard related keystrokes, so they get customized.
var onKey = function( event )
{
if ( this.mode != 'wysiwyg' )
return;
switch ( event.data.keyCode )
{
// Paste
case CKEDITOR.CTRL + 86 : // CTRL+V
case CKEDITOR.SHIFT + 45 : // SHIFT+INS
var body = this.document.getBody();
// Simulate 'beforepaste' event for all none-IEs.
if ( !CKEDITOR.env.ie && body.fire( 'beforepaste' ) )
event.cancel();
// Simulate 'paste' event for Opera/Firefox2.
else if ( CKEDITOR.env.opera
|| CKEDITOR.env.gecko && CKEDITOR.env.version < 10900 )
body.fire( 'paste' );
return;
// Cut
case CKEDITOR.CTRL + 88 : // CTRL+X
case CKEDITOR.SHIFT + 46 : // SHIFT+DEL
// Save Undo snapshot.
var editor = this;
this.fire( 'saveSnapshot' ); // Save before paste
setTimeout( function()
{
editor.fire( 'saveSnapshot' ); // Save after paste
}, 0 );
}
};
// Allow to peek clipboard content by redirecting the
// pasting content into a temporary bin and grab the content of it.
function getClipboardData( evt, mode, callback )
{
var doc = this.document;
// Avoid recursions on 'paste' event or consequent paste too fast. (#5730)
if ( doc.getById( 'cke_pastebin' ) )
return;
// If the browser supports it, get the data directly
if ( mode == 'text' && evt.data && evt.data.$.clipboardData )
{
// evt.data.$.clipboardData.types contains all the flavours in Mac's Safari, but not on windows.
var plain = evt.data.$.clipboardData.getData( 'text/plain' );
if ( plain )
{
evt.data.preventDefault();
callback( plain );
return;
}
}
var sel = this.getSelection(),
range = new CKEDITOR.dom.range( doc );
// Create container to paste into
var pastebin = new CKEDITOR.dom.element( mode == 'text' ? 'textarea' : CKEDITOR.env.webkit ? 'body' : 'div', doc );
pastebin.setAttribute( 'id', 'cke_pastebin' );
// Safari requires a filler node inside the div to have the content pasted into it. (#4882)
CKEDITOR.env.webkit && pastebin.append( doc.createText( '\xa0' ) );
doc.getBody().append( pastebin );
pastebin.setStyles(
{
position : 'absolute',
// Position the bin exactly at the position of the selected element
// to avoid any subsequent document scroll.
top : sel.getStartElement().getDocumentPosition().y + 'px',
width : '1px',
height : '1px',
overflow : 'hidden'
});
// It's definitely a better user experience if we make the paste-bin pretty unnoticed
// by pulling it off the screen.
pastebin.setStyle( this.config.contentsLangDirection == 'ltr' ? 'left' : 'right', '-1000px' );
var bms = sel.createBookmarks();
// Turn off design mode temporarily before give focus to the paste bin.
if ( mode == 'text' )
{
if ( CKEDITOR.env.ie )
{
var ieRange = doc.getBody().$.createTextRange();
ieRange.moveToElementText( pastebin.$ );
ieRange.execCommand( 'Paste' );
evt.data.preventDefault();
}
else
{
doc.$.designMode = 'off';
pastebin.$.focus();
}
}
else
{
range.setStartAt( pastebin, CKEDITOR.POSITION_AFTER_START );
range.setEndAt( pastebin, CKEDITOR.POSITION_BEFORE_END );
range.select( true );
}
// Wait a while and grab the pasted contents
window.setTimeout( function()
{
mode == 'text' && !CKEDITOR.env.ie && ( doc.$.designMode = 'on' );
pastebin.remove();
// Grab the HTML contents.
// We need to look for a apple style wrapper on webkit it also adds
// a div wrapper if you copy/paste the body of the editor.
// Remove hidden div and restore selection.
var bogusSpan;
pastebin = ( CKEDITOR.env.webkit
&& ( bogusSpan = pastebin.getFirst() )
&& ( bogusSpan.is && bogusSpan.hasClass( 'Apple-style-span' ) ) ?
bogusSpan : pastebin );
sel.selectBookmarks( bms );
callback( pastebin[ 'get' + ( mode == 'text' ? 'Value' : 'Html' ) ]() );
}, 0 );
}
// Cutting off control type element in IE standards breaks the selection entirely. (#4881)
function fixCut( editor )
{
if ( !CKEDITOR.env.ie || CKEDITOR.env.quirks )
return;
var sel = editor.getSelection();
var control;
if( ( sel.getType() == CKEDITOR.SELECTION_ELEMENT ) && ( control = sel.getSelectedElement() ) )
{
var range = sel.getRanges()[ 0 ];
var dummy = editor.document.createText( '' );
dummy.insertBefore( control );
range.setStartBefore( dummy );
range.setEndAfter( control );
sel.selectRanges( [ range ] );
// Clear up the fix if the paste wasn't succeeded.
setTimeout( function()
{
// Element still online?
if ( control.getParent() )
{
dummy.remove();
sel.selectElement( control );
}
}, 0 );
}
}
// Register the plugin.
CKEDITOR.plugins.add( 'clipboard',
{
requires : [ 'dialog', 'htmldataprocessor' ],
init : function( editor )
{
// Inserts processed data into the editor at the end of the
// events chain.
editor.on( 'paste', function( evt )
{
var data = evt.data;
if ( data[ 'html' ] )
editor.insertHtml( data[ 'html' ] );
else if ( data[ 'text' ] )
editor.insertText( data[ 'text' ] );
}, null, null, 1000 );
editor.on( 'pasteDialog', function( evt )
{
setTimeout( function()
{
// Open default paste dialog.
editor.openDialog( 'paste' );
}, 0 );
});
function addButtonCommand( buttonName, commandName, command, ctxMenuOrder )
{
var lang = editor.lang[ commandName ];
editor.addCommand( commandName, command );
editor.ui.addButton( buttonName,
{
label : lang,
command : commandName
});
// If the "menu" plugin is loaded, register the menu item.
if ( editor.addMenuItems )
{
editor.addMenuItem( commandName,
{
label : lang,
command : commandName,
group : 'clipboard',
order : ctxMenuOrder
});
}
}
addButtonCommand( 'Cut', 'cut', new cutCopyCmd( 'cut' ), 1 );
addButtonCommand( 'Copy', 'copy', new cutCopyCmd( 'copy' ), 4 );
addButtonCommand( 'Paste', 'paste', pasteCmd, 8 );
CKEDITOR.dialog.add( 'paste', CKEDITOR.getUrl( this.path + 'dialogs/paste.js' ) );
editor.on( 'key', onKey, editor );
var mode = editor.config.forcePasteAsPlainText ? 'text' : 'html';
// We'll be catching all pasted content in one line, regardless of whether the
// it's introduced by a document command execution (e.g. toolbar buttons) or
// user paste behaviors. (e.g. Ctrl-V)
editor.on( 'contentDom', function()
{
var body = editor.document.getBody();
body.on( ( (mode == 'text' && CKEDITOR.env.ie ) || CKEDITOR.env.webkit ) ? 'paste' : 'beforepaste',
function( evt )
{
if ( depressBeforeEvent )
return;
getClipboardData.call( editor, evt, mode, function ( data )
{
// The very last guard to make sure the
// paste has successfully happened.
if ( !data )
return;
var dataTransfer = {};
dataTransfer[ mode ] = data;
editor.fire( 'paste', dataTransfer );
} );
});
body.on( 'beforecut', function() { !depressBeforeEvent && fixCut( editor ); } );
});
// If the "contextmenu" plugin is loaded, register the listeners.
if ( editor.contextMenu )
{
var depressBeforeEvent;
function stateFromNamedCommand( command )
{
// IE Bug: queryCommandEnabled('paste') fires also 'beforepaste(copy/cut)',
// guard to distinguish from the ordinary sources( either
// keyboard paste or execCommand ) (#4874).
CKEDITOR.env.ie && ( depressBeforeEvent = 1 );
var retval = editor.document.$.queryCommandEnabled( command ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED;
depressBeforeEvent = 0;
return retval;
}
editor.contextMenu.addListener( function( element, selection )
{
var readOnly = selection.getCommonAncestor().isReadOnly();
return {
cut : !readOnly && stateFromNamedCommand( 'Cut' ),
copy : stateFromNamedCommand( 'Copy' ),
paste : !readOnly && ( CKEDITOR.env.webkit ? CKEDITOR.TRISTATE_OFF : stateFromNamedCommand( 'Paste' ) )
};
});
}
}
});
})();
/**
* Fired when a clipboard operation is about to be taken into the editor.
* Listeners can manipulate the data to be pasted before having it effectively
* inserted into the document.
* @name CKEDITOR.editor#paste
* @since 3.1
* @event
* @param {String} [data.html] The HTML data to be pasted. If not available, e.data.text will be defined.
* @param {String} [data.text] The plain text data to be pasted, available when plain text operations are to used. If not available, e.data.html will be defined.
*/

View File

@@ -0,0 +1,291 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
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;
// 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.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();
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.
return element.getName() != 'a' || isUnstylable( element );
};
new CKEDITOR.style( colorStyle, { color : color } ).apply( editor.document );
}
editor.fire( 'saveSnapshot' );
});
// Render the "Automatic" button.
output.push(
'<a class="cke_colorauto" _cke_focus=1 hidefocus=true' +
' title="', lang.auto, '"' +
' onclick="CKEDITOR.tools.callFunction(', clickFn, ',null,\'', type, '\');return false;"' +
' href="javascript:void(\'', lang.auto, '\')"' +
' role="option" aria-posinset="1" aria-setsize="', total, '">' +
'<table role="presentation" cellspacing=0 cellpadding=0 width="100%">' +
'<tr>' +
'<td>' +
'<span class="cke_colorbox" id="', colorBoxId, '"></span>' +
'</td>' +
'<td colspan=7 align=center>',
lang.auto,
'</td>' +
'</tr>' +
'</table>' +
'</a>' +
'<table role="presentation" cellspacing=0 cellpadding=0 width="100%">' );
// Render the color boxes.
for ( var i = 0 ; i < colors.length ; i++ )
{
if ( ( i % 8 ) === 0 )
output.push( '</tr><tr>' );
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 <font>. See #5676
if (!parts[1])
colorName = '#' + colorName.replace( /^(.)(.)(.)$/, '$1$1$2$2$3$3' );
var colorLabel = editor.lang.colors[ colorCode ] || colorCode;
output.push(
'<td>' +
'<a class="cke_colorbox" _cke_focus=1 hidefocus=true' +
' title="', colorLabel, '"' +
' onclick="CKEDITOR.tools.callFunction(', clickFn, ',\'', colorName, '\',\'', type, '\'); return false;"' +
' href="javascript:void(\'', colorLabel, '\')"' +
' role="option" aria-posinset="', ( i + 2 ), '" aria-setsize="', total, '">' +
'<span class="cke_colorbox" style="background-color:#', colorCode, '"></span>' +
'</a>' +
'</td>' );
}
// Render the "More Colors" button.
if ( config.colorButton_enableMore === undefined || config.colorButton_enableMore )
{
output.push(
'</tr>' +
'<tr>' +
'<td colspan=8 align=center>' +
'<a class="cke_colormore" _cke_focus=1 hidefocus=true' +
' title="', lang.more, '"' +
' onclick="CKEDITOR.tools.callFunction(', clickFn, ',\'?\',\'', type, '\');return false;"' +
' href="javascript:void(\'', lang.more, '\')"',
' role="option" aria-posinset="', total, '" aria-setsize="', total, '">',
lang.more,
'</a>' +
'</td>' ); // tr is later in the code.
}
output.push( '</tr></table>' );
return output.join( '' );
}
function isUnstylable( ele )
{
return ( ele.getAttribute( 'contentEditable' ) == 'false' ) || ele.getAttribute( 'data-cke-nostyle' );
}
}
});
/**
* Whether to enable the "More Colors..." button in the color selectors.
* @default true
* @type Boolean
* @example
* config.colorButton_enableMore = false;
*/
/**
* Defines the colors to be displayed in the color selectors. It's a string
* containing the hexadecimal notation for HTML colors, without the "#" prefix.
*
* Since 3.3: A name may be optionally defined by prefixing the entries with the
* name and the slash character. For example, "FontColor1/FF9900" will be
* displayed as the color #FF9900 in the selector, but will be outputted as "FontColor1".
* @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';
/**
* Holds the style definition to be used to apply the text foreground color.
* @type Object
* @example
* // This is basically the default setting value.
* config.colorButton_foreStyle =
* {
* element : 'span',
* styles : { 'color' : '#(color)' }
* };
*/
CKEDITOR.config.colorButton_foreStyle =
{
element : 'span',
styles : { 'color' : '#(color)' },
overrides : [ { element : 'font', attributes : { 'color' : null } } ]
};
/**
* Holds the style definition to be used to apply the text background color.
* @type Object
* @example
* // This is basically the default setting value.
* config.colorButton_backStyle =
* {
* element : 'span',
* styles : { 'background-color' : '#(color)' }
* };
*/
CKEDITOR.config.colorButton_backStyle =
{
element : 'span',
styles : { 'background-color' : '#(color)' }
};

View File

@@ -0,0 +1,340 @@
/*
Copyright (c) 2003-2010, 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 : '&nbsp;'
};
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( '&nbsp;' );
}
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(
'<a href="javascript: void(0);" role="option"' +
' aria-posinset="' + index + '"' +
' aria-setsize="' + 13 * 18 + '"' +
' style="cursor: pointer;display:block;width:100%;height:100% " title="'+ CKEDITOR.tools.htmlEncode( color )+ '"' +
' onkeydown="CKEDITOR.tools.callFunction( ' + onKeydownHandler + ', event, this )"' +
' onclick="CKEDITOR.tools.callFunction(' + onClickHandler + ', event, this ); return false;"' +
' tabindex="-1"><span class="cke_voice_label">' + color + '</span>&nbsp;</a>', 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 : '<table role="listbox" aria-labelledby="' + tableLabelId + '" onmouseout="CKEDITOR.tools.callFunction( ' + onMouseout + ' );">' +
( !CKEDITOR.env.webkit ? html : '' ) +
'</table><span id="' + tableLabelId + '" class="cke_voice_label">' + lang.options +'</span>',
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 : '<span>' + lang.highlight +'</span>\
<div id="' + hicolorId + '" style="border: 1px solid; height: 74px; width: 74px;"></div>\
<div id="' + hicolorTextId + '">&nbsp;</div><span>' + lang.selected + '</span>\
<div id="' + selHiColorId + '" style="border: 1px solid; height: 20px; width: 74px;"></div>'
},
{
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
}
]
}
]
}
]
}
]
};
}
);

View File

@@ -0,0 +1,13 @@
( function()
{
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 );
} )();

View File

@@ -0,0 +1,177 @@
/*
Copyright (c) 2003-2010, 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 );
},
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 the
* META (Mac) key is pressed while opening the context menu.
* @name CKEDITOR.config.browserContextMenuOnCtrl
* @since 3.0.2
* @type Boolean
* @default true
* @example
* config.browserContextMenuOnCtrl = false;
*/

View File

@@ -0,0 +1,315 @@
/*
Copyright (c) 2003-2010, 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.
*/
/**
* 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.dialogDefinition
* @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.dialogDefinition.prototype.title
* @field
* @type String
* @example
*/
/**
* How the dialog can be resized, must be one of the four contents defined below.
* <br /><br />
* <strong>CKEDITOR.DIALOG_RESIZE_NONE</strong><br />
* <strong>CKEDITOR.DIALOG_RESIZE_WIDTH</strong><br />
* <strong>CKEDITOR.DIALOG_RESIZE_HEIGHT</strong><br />
* <strong>CKEDITOR.DIALOG_RESIZE_BOTH</strong><br />
* @name CKEDITOR.dialog.dialogDefinition.prototype.resizable
* @field
* @type Number
* @default CKEDITOR.DIALOG_RESIZE_NONE
* @example
*/
/**
* The minimum width of the dialog, in pixels.
* @name CKEDITOR.dialog.dialogDefinition.prototype.minWidth
* @field
* @type Number
* @default 600
* @example
*/
/**
* The minimum height of the dialog, in pixels.
* @name CKEDITOR.dialog.dialogDefinition.prototype.minHeight
* @field
* @type Number
* @default 400
* @example
*/
/**
* The buttons in the dialog, defined as an array of
* {@link CKEDITOR.dialog.buttonDefinition} objects.
* @name CKEDITOR.dialog.dialogDefinition.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.contentDefinition} objects. Required.
* @name CKEDITOR.dialog.dialogDefinition.prototype.contents
* @field
* @type Array
* @example
*/
/**
* The function to execute when OK is pressed.
* @name CKEDITOR.dialog.dialogDefinition.prototype.onOk
* @field
* @type Function
* @example
*/
/**
* The function to execute when Cancel is pressed.
* @name CKEDITOR.dialog.dialogDefinition.prototype.onCancel
* @field
* @type Function
* @example
*/
/**
* The function to execute when the dialog is displayed for the first time.
* @name CKEDITOR.dialog.dialogDefinition.prototype.onLoad
* @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.contentDefinition
* @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.contentDefinition.prototype.id
* @field
* @type String
* @example
*/
/**
* The tab label of the content page.
* @name CKEDITOR.dialog.contentDefinition.prototype.label
* @field
* @type String
* @example
*/
/**
* The popup message of the tab label.
* @name CKEDITOR.dialog.contentDefinition.prototype.title
* @field
* @type String
* @example
*/
/**
* The CTRL hotkey for switching to the tab.
* @name CKEDITOR.dialog.contentDefinition.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.uiElementDefinition} objects.
* @name CKEDITOR.dialog.contentDefinition.prototype.elements
* @field
* @type Array
* @example
*/
/**
* This class is not really part of the API. It just illustrates the properties
* that developers can use to define and create dialog buttons.
* @name CKEDITOR.dialog.buttonDefinition
* @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 dialog button. Required.
* @name CKEDITOR.dialog.buttonDefinition.prototype.id
* @type String
* @field
* @example
*/
/**
* The label of the dialog button. Required.
* @name CKEDITOR.dialog.buttonDefinition.prototype.label
* @type String
* @field
* @example
*/
/**
* The popup message of the dialog button.
* @name CKEDITOR.dialog.buttonDefinition.prototype.title
* @type String
* @field
* @example
*/
/**
* The CTRL hotkey for the button.
* @name CKEDITOR.dialog.buttonDefinition.prototype.accessKey
* @type String
* @field
* @example
* exitButton.accessKey = 'X'; // Button will be pressed when user presses CTRL-X
*/
/**
* Whether the button is disabled.
* @name CKEDITOR.dialog.buttonDefinition.prototype.disabled
* @type Boolean
* @field
* @default false
* @example
*/
/**
* The function to execute when the button is clicked.
* @name CKEDITOR.dialog.buttonDefinition.prototype.onClick
* @type Function
* @field
* @example
*/
/**
* 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.uiElementDefinition
* @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.uiElementDefinition.prototype.id
* @field
* @type String
* @example
*/
/**
* The type of the UI element. Required.
* @name CKEDITOR.dialog.uiElementDefinition.prototype.type
* @field
* @type String
* @example
*/
/**
* The popup label of the UI element.
* @name CKEDITOR.dialog.uiElementDefinition.prototype.title
* @field
* @type String
* @example
*/
/**
* CSS class names to append to the UI element.
* @name CKEDITOR.dialog.uiElementDefinition.prototype.className
* @field
* @type String
* @example
*/
/**
* Inline CSS classes to append to the UI element.
* @name CKEDITOR.dialog.uiElementDefinition.prototype.style
* @field
* @type String
* @example
*/
/**
* Function to execute the first time the UI element is displayed.
* @name CKEDITOR.dialog.uiElementDefinition.prototype.onLoad
* @field
* @type Function
* @example
*/
/**
* Function to execute whenever the UI element's parent dialog is displayed.
* @name CKEDITOR.dialog.uiElementDefinition.prototype.onShow
* @field
* @type Function
* @example
*/
/**
* Function to execute whenever the UI element's parent dialog is closed.
* @name CKEDITOR.dialog.uiElementDefinition.prototype.onHide
* @field
* @type Function
* @example
*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,213 @@
/*
Copyright (c) 2003-2010, 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();
// Broadcast Lang Dir change
if ( attrName == 'dir' )
{
var dir = element.getAttribute( attrName );
if ( dir != value && element.getParent() )
this._.dialog._.editor.fire( 'dirChanged', { node : element, dir : value || element.getDirection( 1 ) } );
}
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' : '',
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;
}
});
})();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,535 @@
/*
* Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.html or http://ckeditor.com/license
*/
(function()
{
/**
* Add to collection with DUP examination.
* @param {Object} collection
* @param {Object} element
* @param {Object} database
*/
function addSafely( collection, element, database )
{
// 1. IE doesn't support customData on text nodes;
// 2. Text nodes never get chance to appear twice;
if ( !element.is || !element.getCustomData( 'block_processed' ) )
{
element.is && CKEDITOR.dom.element.setMarker( database, element, 'block_processed', true );
collection.push( element );
}
}
function getNonEmptyChildren( element )
{
var retval = [];
var children = element.getChildren();
for ( var i = 0 ; i < children.count() ; i++ )
{
var child = children.getItem( i );
if ( ! ( child.type === CKEDITOR.NODE_TEXT
&& ( /^[ \t\n\r]+$/ ).test( child.getText() ) ) )
retval.push( child );
}
return retval;
}
/**
* Dialog reused by both 'creatediv' and 'editdiv' commands.
* @param {Object} editor
* @param {String} command The command name which indicate what the current command is.
*/
function divDialog( editor, command )
{
// Definition of elements at which div operation should stopped.
var divLimitDefinition = ( function(){
// Customzie from specialize blockLimit elements
var definition = CKEDITOR.tools.extend( {}, CKEDITOR.dtd.$blockLimit );
// Exclude 'div' itself.
delete definition.div;
// Exclude 'td' and 'th' when 'wrapping table'
if ( editor.config.div_wrapTable )
{
delete definition.td;
delete definition.th;
}
return definition;
})();
// DTD of 'div' element
var dtd = CKEDITOR.dtd.div;
/**
* Get the first div limit element on the element's path.
* @param {Object} element
*/
function getDivLimitElement( element )
{
var pathElements = new CKEDITOR.dom.elementPath( element ).elements;
var divLimit;
for ( var i = 0; i < pathElements.length ; i++ )
{
if ( pathElements[ i ].getName() in divLimitDefinition )
{
divLimit = pathElements[ i ];
break;
}
}
return divLimit;
}
/**
* Init all fields' setup/commit function.
* @memberof divDialog
*/
function setupFields()
{
this.foreach( function( field )
{
// Exclude layout container elements
if ( /^(?!vbox|hbox)/.test( field.type ) )
{
if ( !field.setup )
{
// Read the dialog fields values from the specified
// element attributes.
field.setup = function( element )
{
field.setValue( element.getAttribute( field.id ) || '' );
};
}
if ( !field.commit )
{
// Set element attributes assigned by the dialog
// fields.
field.commit = function( element )
{
var fieldValue = this.getValue();
// ignore default element attribute values
if ( 'dir' == field.id && element.getComputedStyle( 'direction' ) == fieldValue )
return;
if ( fieldValue )
element.setAttribute( field.id, fieldValue );
else
element.removeAttribute( field.id );
};
}
}
} );
}
/**
* Wrapping 'div' element around appropriate blocks among the selected ranges.
* @param {Object} editor
*/
function createDiv( editor )
{
// new adding containers OR detected pre-existed containers.
var containers = [];
// node markers store.
var database = {};
// All block level elements which contained by the ranges.
var containedBlocks = [], block;
// Get all ranges from the selection.
var selection = editor.document.getSelection(),
ranges = selection.getRanges();
var bookmarks = selection.createBookmarks();
var i, iterator;
// Calcualte a default block tag if we need to create blocks.
var blockTag = editor.config.enterMode == CKEDITOR.ENTER_DIV ? 'div' : 'p';
// collect all included elements from dom-iterator
for ( i = 0 ; i < ranges.length ; i++ )
{
iterator = ranges[ i ].createIterator();
while ( ( block = iterator.getNextParagraph() ) )
{
// include contents of blockLimit elements.
if ( block.getName() in divLimitDefinition )
{
var j, childNodes = block.getChildren();
for ( j = 0 ; j < childNodes.count() ; j++ )
addSafely( containedBlocks, childNodes.getItem( j ) , database );
}
else
{
// Bypass dtd disallowed elements.
while ( !dtd[ block.getName() ] && block.getName() != 'body' )
block = block.getParent();
addSafely( containedBlocks, block, database );
}
}
}
CKEDITOR.dom.element.clearAllMarkers( database );
var blockGroups = groupByDivLimit( containedBlocks );
var ancestor, blockEl, divElement;
for ( i = 0 ; i < blockGroups.length ; i++ )
{
var currentNode = blockGroups[ i ][ 0 ];
// Calculate the common parent node of all contained elements.
ancestor = currentNode.getParent();
for ( j = 1 ; j < blockGroups[ i ].length; j++ )
ancestor = ancestor.getCommonAncestor( blockGroups[ i ][ j ] );
divElement = new CKEDITOR.dom.element( 'div', editor.document );
// Normalize the blocks in each group to a common parent.
for ( j = 0; j < blockGroups[ i ].length ; j++ )
{
currentNode = blockGroups[ i ][ j ];
while ( !currentNode.getParent().equals( ancestor ) )
currentNode = currentNode.getParent();
// This could introduce some duplicated elements in array.
blockGroups[ i ][ j ] = currentNode;
}
// Wrapped blocks counting
var fixedBlock = null;
for ( j = 0 ; j < blockGroups[ i ].length ; j++ )
{
currentNode = blockGroups[ i ][ j ];
// Avoid DUP elements introduced by grouping.
if ( !( currentNode.getCustomData && currentNode.getCustomData( 'block_processed' ) ) )
{
currentNode.is && CKEDITOR.dom.element.setMarker( database, currentNode, 'block_processed', true );
// Establish new container, wrapping all elements in this group.
if ( !j )
divElement.insertBefore( currentNode );
divElement.append( currentNode );
}
}
CKEDITOR.dom.element.clearAllMarkers( database );
containers.push( divElement );
}
selection.selectBookmarks( bookmarks );
return containers;
}
function getDiv( editor )
{
var path = new CKEDITOR.dom.elementPath( editor.getSelection().getStartElement() ),
blockLimit = path.blockLimit,
div = blockLimit && blockLimit.getAscendant( 'div', true );
return div;
}
/**
* Divide a set of nodes to different groups by their path's blocklimit element.
* Note: the specified nodes should be in source order naturally, which mean they are supposed to producea by following class:
* * CKEDITOR.dom.range.Iterator
* * CKEDITOR.dom.domWalker
* @return {Array []} the grouped nodes
*/
function groupByDivLimit( nodes )
{
var groups = [],
lastDivLimit = null,
path, block;
for ( var i = 0 ; i < nodes.length ; i++ )
{
block = nodes[i];
var limit = getDivLimitElement( block );
if ( !limit.equals( lastDivLimit ) )
{
lastDivLimit = limit ;
groups.push( [] ) ;
}
groups[ groups.length - 1 ].push( block ) ;
}
return groups;
}
// Synchronous field values to other impacted fields is required, e.g. div styles
// change should also alter inline-style text.
function commitInternally( targetFields )
{
var dialog = this.getDialog(),
element = dialog._element && dialog._element.clone()
|| new CKEDITOR.dom.element( 'div', editor.document );
// Commit this field and broadcast to target fields.
this.commit( element, true );
targetFields = [].concat( targetFields );
var length = targetFields.length, field;
for ( var i = 0; i < length; i++ )
{
field = dialog.getContentElement.apply( dialog, targetFields[ i ].split( ':' ) );
field && field.setup && field.setup( element, true );
}
}
// Registered 'CKEDITOR.style' instances.
var styles = {} ;
/**
* Hold a collection of created block container elements.
*/
var containers = [];
/**
* @type divDialog
*/
return {
title : editor.lang.div.title,
minWidth : 400,
minHeight : 165,
contents :
[
{
id :'info',
label :editor.lang.common.generalTab,
title :editor.lang.common.generalTab,
elements :
[
{
type :'hbox',
widths : [ '50%', '50%' ],
children :
[
{
id :'elementStyle',
type :'select',
style :'width: 100%;',
label :editor.lang.div.styleSelectLabel,
'default' : '',
// Options are loaded dynamically.
items :
[
[ editor.lang.common.notSet , '' ]
],
onChange : function()
{
commitInternally.call( this, [ 'info:class', 'advanced:dir', 'advanced:style' ] );
},
setup : function( element )
{
for ( var name in styles )
styles[ name ].checkElementRemovable( element, true ) && this.setValue( name );
},
commit: function( element )
{
var styleName;
if ( ( styleName = this.getValue() ) )
{
var style = styles[ styleName ];
var customData = element.getCustomData( 'elementStyle' ) || '';
style.applyToObject( element );
element.setCustomData( 'elementStyle', customData + style._.definition.attributes.style );
}
}
},
{
id :'class',
type :'text',
label :editor.lang.common.cssClass,
'default' : ''
}
]
}
]
},
{
id :'advanced',
label :editor.lang.common.advancedTab,
title :editor.lang.common.advancedTab,
elements :
[
{
type :'vbox',
padding :1,
children :
[
{
type :'hbox',
widths : [ '50%', '50%' ],
children :
[
{
type :'text',
id :'id',
label :editor.lang.common.id,
'default' : ''
},
{
type :'text',
id :'lang',
label :editor.lang.link.langCode,
'default' : ''
}
]
},
{
type :'hbox',
children :
[
{
type :'text',
id :'style',
style :'width: 100%;',
label :editor.lang.common.cssStyle,
'default' : '',
commit : function( element )
{
// Merge with 'elementStyle', which is of higher priority.
var merged = this.getValue() + ( element.getCustomData( 'elementStyle' ) || '' );
element.setAttribute( 'style', merged );
}
}
]
},
{
type :'hbox',
children :
[
{
type :'text',
id :'title',
style :'width: 100%;',
label :editor.lang.common.advisoryTitle,
'default' : ''
}
]
},
{
type :'select',
id :'dir',
style :'width: 100%;',
label :editor.lang.common.langDir,
'default' : '',
items :
[
[ editor.lang.common.notSet , '' ],
[
editor.lang.common.langDirLtr,
'ltr'
],
[
editor.lang.common.langDirRtl,
'rtl'
]
]
}
]
}
]
}
],
onLoad : function()
{
setupFields.call( this );
// Preparing for the 'elementStyle' field.
var dialog = this,
stylesField = this.getContentElement( 'info', 'elementStyle' );
// Reuse the 'stylescombo' plugin's styles definition.
editor.getStylesSet( function( stylesDefinitions )
{
var styleName;
if ( stylesDefinitions )
{
// Digg only those styles that apply to 'div'.
for ( var i = 0 ; i < stylesDefinitions.length ; i++ )
{
var styleDefinition = stylesDefinitions[ i ];
if ( styleDefinition.element && styleDefinition.element == 'div' )
{
styleName = styleDefinition.name;
styles[ styleName ] = new CKEDITOR.style( styleDefinition );
// Populate the styles field options with style name.
stylesField.items.push( [ styleName, styleName ] );
stylesField.add( styleName, styleName );
}
}
}
// We should disable the content element
// it if no options are available at all.
stylesField[ stylesField.items.length > 1 ? 'enable' : 'disable' ]();
// Now setup the field value manually.
setTimeout( function() { stylesField.setup( dialog._element ); }, 0 );
} );
},
onShow : function()
{
// Whether always create new container regardless of existed
// ones.
if ( command == 'editdiv' )
{
// Try to discover the containers that already existed in
// ranges
var div = getDiv( editor );
// update dialog field values
div && this.setupContent( this._element = div );
}
},
onOk : function()
{
if ( command == 'editdiv' )
containers = [ this._element ];
else
containers = createDiv( editor, true );
// Update elements attributes
var size = containers.length;
for ( var i = 0; i < size; i++ )
{
this.commitContent( containers[ i ] );
// Remove empty 'style' attribute.
!containers[ i ].getAttribute( 'style' ) && containers[ i ].removeAttribute( 'style' );
}
this.hide();
},
onHide : function()
{
// Remove style only when editing existing DIV. (#6315)
if ( command == 'editdiv' )
this._element.removeCustomData( 'elementStyle' );
delete this._element;
}
};
}
CKEDITOR.dialog.add( 'creatediv', function( editor )
{
return divDialog( editor, 'creatediv' );
} );
CKEDITOR.dialog.add( 'editdiv', function( editor )
{
return divDialog( editor, 'editdiv' );
} );
} )();
/*
* @name CKEDITOR.config.div_wrapTable
* Whether to wrap the whole table instead of indivisual cells when created 'div' in table cell.
* @type Boolean
* @default false
* @example config.div_wrapTable = true;
*/

View File

@@ -0,0 +1,121 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
/**
* @fileOverview The "div" plugin. It wraps the selected block level elements with a 'div' element with specified styles and attributes.
*
*/
(function()
{
CKEDITOR.plugins.add( 'div',
{
requires : [ 'editingblock', 'domiterator', 'styles' ],
init : function( editor )
{
var lang = editor.lang.div;
editor.addCommand( 'creatediv', new CKEDITOR.dialogCommand( 'creatediv' ) );
editor.addCommand( 'editdiv', new CKEDITOR.dialogCommand( 'editdiv' ) );
editor.addCommand( 'removediv',
{
exec : function( editor )
{
var selection = editor.getSelection(),
ranges = selection && selection.getRanges(),
range,
bookmarks = selection.createBookmarks(),
walker,
toRemove = [];
function findDiv( node )
{
var path = new CKEDITOR.dom.elementPath( node ),
blockLimit = path.blockLimit,
div = blockLimit.is( 'div' ) && blockLimit;
if ( div && !div.data( 'cke-div-added' ) )
{
toRemove.push( div );
div.data( 'cke-div-added' );
}
}
for ( var i = 0 ; i < ranges.length ; i++ )
{
range = ranges[ i ];
if ( range.collapsed )
findDiv( selection.getStartElement() );
else
{
walker = new CKEDITOR.dom.walker( range );
walker.evaluator = findDiv;
walker.lastForward();
}
}
for ( i = 0 ; i < toRemove.length ; i++ )
toRemove[ i ].remove( true );
selection.selectBookmarks( bookmarks );
}
} );
editor.ui.addButton( 'CreateDiv',
{
label : lang.toolbar,
command :'creatediv'
} );
if ( editor.addMenuItems )
{
editor.addMenuItems(
{
editdiv :
{
label : lang.edit,
command : 'editdiv',
group : 'div',
order : 1
},
removediv:
{
label : lang.remove,
command : 'removediv',
group : 'div',
order : 5
}
} );
if ( editor.contextMenu )
{
editor.contextMenu.addListener( function( element, selection )
{
if ( !element || element.isReadOnly() )
return null;
var elementPath = new CKEDITOR.dom.elementPath( element ),
blockLimit = elementPath.blockLimit;
if ( blockLimit && blockLimit.getAscendant( 'div', true ) )
{
return {
editdiv : CKEDITOR.TRISTATE_OFF,
removediv : CKEDITOR.TRISTATE_OFF
};
}
return null;
} );
}
}
CKEDITOR.dialog.add( 'creatediv', this.path + 'dialogs/div.js' );
CKEDITOR.dialog.add( 'editdiv', this.path + 'dialogs/div.js' );
}
} );
})();

View File

@@ -0,0 +1,356 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
/**
* @file DOM iterator, which iterates over list items, lines and paragraphs.
*/
CKEDITOR.plugins.add( 'domiterator' );
(function()
{
/**
* @name CKEDITOR.dom.iterator
*/
function iterator( range )
{
if ( arguments.length < 1 )
return;
this.range = range;
this.forceBrBreak = 0;
// Whether include <br>s into the enlarged range.(#3730).
this.enlargeBr = 1;
this.enforceRealBlocks = 0;
this._ || ( this._ = {} );
}
var beginWhitespaceRegex = /^[\r\n\t ]+$/,
isBookmark = CKEDITOR.dom.walker.bookmark();
iterator.prototype = {
getNextParagraph : function( blockTag )
{
// The block element to be returned.
var block;
// The range object used to identify the paragraph contents.
var range;
// Indicats that the current element in the loop is the last one.
var isLast;
// Indicate at least one of the range boundaries is inside a preformat block.
var touchPre;
// Instructs to cleanup remaining BRs.
var removePreviousBr, removeLastBr;
// This is the first iteration. Let's initialize it.
if ( !this._.lastNode )
{
range = this.range.clone();
// Shrink the range to exclude harmful "noises" (#4087, #4450, #5435).
range.shrink( CKEDITOR.NODE_ELEMENT, true );
touchPre = range.endContainer.hasAscendant( 'pre', true )
|| range.startContainer.hasAscendant( 'pre', true );
range.enlarge( this.forceBrBreak && !touchPre || !this.enlargeBr ?
CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS : CKEDITOR.ENLARGE_BLOCK_CONTENTS );
var walker = new CKEDITOR.dom.walker( range ),
ignoreBookmarkTextEvaluator = CKEDITOR.dom.walker.bookmark( true, true );
// Avoid anchor inside bookmark inner text.
walker.evaluator = ignoreBookmarkTextEvaluator;
this._.nextNode = walker.next();
// TODO: It's better to have walker.reset() used here.
walker = new CKEDITOR.dom.walker( range );
walker.evaluator = ignoreBookmarkTextEvaluator;
var lastNode = walker.previous();
this._.lastNode = lastNode.getNextSourceNode( true );
// We may have an empty text node at the end of block due to [3770].
// If that node is the lastNode, it would cause our logic to leak to the
// next block.(#3887)
if ( this._.lastNode &&
this._.lastNode.type == CKEDITOR.NODE_TEXT &&
!CKEDITOR.tools.trim( this._.lastNode.getText() ) &&
this._.lastNode.getParent().isBlockBoundary() )
{
var testRange = new CKEDITOR.dom.range( range.document );
testRange.moveToPosition( this._.lastNode, CKEDITOR.POSITION_AFTER_END );
if ( testRange.checkEndOfBlock() )
{
var path = new CKEDITOR.dom.elementPath( testRange.endContainer );
var lastBlock = path.block || path.blockLimit;
this._.lastNode = lastBlock.getNextSourceNode( true );
}
}
// Probably the document end is reached, we need a marker node.
if ( !this._.lastNode )
{
this._.lastNode = this._.docEndMarker = range.document.createText( '' );
this._.lastNode.insertAfter( lastNode );
}
// Let's reuse this variable.
range = null;
}
var currentNode = this._.nextNode;
lastNode = this._.lastNode;
this._.nextNode = null;
while ( currentNode )
{
// closeRange indicates that a paragraph boundary has been found,
// so the range can be closed.
var closeRange = 0,
parentPre = currentNode.hasAscendant( 'pre' );
// includeNode indicates that the current node is good to be part
// of the range. By default, any non-element node is ok for it.
var includeNode = ( currentNode.type != CKEDITOR.NODE_ELEMENT ),
continueFromSibling = 0;
// If it is an element node, let's check if it can be part of the
// range.
if ( !includeNode )
{
var nodeName = currentNode.getName();
if ( currentNode.isBlockBoundary( this.forceBrBreak &&
!parentPre && { br : 1 } ) )
{
// <br> boundaries must be part of the range. It will
// happen only if ForceBrBreak.
if ( nodeName == 'br' )
includeNode = 1;
else if ( !range && !currentNode.getChildCount() && nodeName != 'hr' )
{
// If we have found an empty block, and haven't started
// the range yet, it means we must return this block.
block = currentNode;
isLast = currentNode.equals( lastNode );
break;
}
// The range must finish right before the boundary,
// including possibly skipped empty spaces. (#1603)
if ( range )
{
range.setEndAt( currentNode, CKEDITOR.POSITION_BEFORE_START );
// The found boundary must be set as the next one at this
// point. (#1717)
if ( nodeName != 'br' )
this._.nextNode = currentNode;
}
closeRange = 1;
}
else
{
// If we have child nodes, let's check them.
if ( currentNode.getFirst() )
{
// If we don't have a range yet, let's start it.
if ( !range )
{
range = new CKEDITOR.dom.range( this.range.document );
range.setStartAt( currentNode, CKEDITOR.POSITION_BEFORE_START );
}
currentNode = currentNode.getFirst();
continue;
}
includeNode = 1;
}
}
else if ( currentNode.type == CKEDITOR.NODE_TEXT )
{
// Ignore normal whitespaces (i.e. not including &nbsp; or
// other unicode whitespaces) before/after a block node.
if ( beginWhitespaceRegex.test( currentNode.getText() ) )
includeNode = 0;
}
// The current node is good to be part of the range and we are
// starting a new range, initialize it first.
if ( includeNode && !range )
{
range = new CKEDITOR.dom.range( this.range.document );
range.setStartAt( currentNode, CKEDITOR.POSITION_BEFORE_START );
}
// The last node has been found.
isLast = ( ( !closeRange || includeNode ) && currentNode.equals( lastNode ) );
// If we are in an element boundary, let's check if it is time
// to close the range, otherwise we include the parent within it.
if ( range && !closeRange )
{
while ( !currentNode.getNext() && !isLast )
{
var parentNode = currentNode.getParent();
if ( parentNode.isBlockBoundary( this.forceBrBreak
&& !parentPre && { br : 1 } ) )
{
closeRange = 1;
isLast = isLast || ( parentNode.equals( lastNode) );
break;
}
currentNode = parentNode;
includeNode = 1;
isLast = ( currentNode.equals( lastNode ) );
continueFromSibling = 1;
}
}
// Now finally include the node.
if ( includeNode )
range.setEndAt( currentNode, CKEDITOR.POSITION_AFTER_END );
currentNode = currentNode.getNextSourceNode( continueFromSibling, null, lastNode );
isLast = !currentNode;
// We have found a block boundary. Let's close the range and move out of the
// loop.
if ( isLast || ( closeRange && range ) )
break;
}
// Now, based on the processed range, look for (or create) the block to be returned.
if ( !block )
{
// If no range has been found, this is the end.
if ( !range )
{
this._.docEndMarker && this._.docEndMarker.remove();
this._.nextNode = null;
return null;
}
var startPath = new CKEDITOR.dom.elementPath( range.startContainer );
var startBlockLimit = startPath.blockLimit,
checkLimits = { div : 1, th : 1, td : 1 };
block = startPath.block;
if ( !block
&& !this.enforceRealBlocks
&& checkLimits[ startBlockLimit.getName() ]
&& range.checkStartOfBlock()
&& range.checkEndOfBlock() )
block = startBlockLimit;
else if ( !block || ( this.enforceRealBlocks && block.getName() == 'li' ) )
{
// Create the fixed block.
block = this.range.document.createElement( blockTag || 'p' );
// Move the contents of the temporary range to the fixed block.
range.extractContents().appendTo( block );
block.trim();
// Insert the fixed block into the DOM.
range.insertNode( block );
removePreviousBr = removeLastBr = true;
}
else if ( block.getName() != 'li' )
{
// If the range doesn't includes the entire contents of the
// block, we must split it, isolating the range in a dedicated
// block.
if ( !range.checkStartOfBlock() || !range.checkEndOfBlock() )
{
// The resulting block will be a clone of the current one.
block = block.clone( false );
// Extract the range contents, moving it to the new block.
range.extractContents().appendTo( block );
block.trim();
// Split the block. At this point, the range will be in the
// right position for our intents.
var splitInfo = range.splitBlock();
removePreviousBr = !splitInfo.wasStartOfBlock;
removeLastBr = !splitInfo.wasEndOfBlock;
// Insert the new block into the DOM.
range.insertNode( block );
}
}
else if ( !isLast )
{
// LIs are returned as is, with all their children (due to the
// nested lists). But, the next node is the node right after
// the current range, which could be an <li> child (nested
// lists) or the next sibling <li>.
this._.nextNode = ( block.equals( lastNode ) ? null :
range.getBoundaryNodes().endNode.getNextSourceNode( true, null, lastNode ) );
}
}
// Ignore bookmark nodes.(#3783)
var bookmarkGuard = CKEDITOR.dom.walker.bookmark( false, true );
if ( removePreviousBr )
{
var previousSibling = block.getPrevious();
if ( previousSibling && previousSibling.type == CKEDITOR.NODE_ELEMENT )
{
if ( previousSibling.getName() == 'br' )
previousSibling.remove();
else if ( previousSibling.getLast() && previousSibling.getLast().$.nodeName.toLowerCase() == 'br' )
previousSibling.getLast().remove();
}
}
if ( removeLastBr )
{
var lastChild = block.getLast();
if ( lastChild && lastChild.type == CKEDITOR.NODE_ELEMENT && lastChild.getName() == 'br' )
{
// Take care not to remove the block expanding <br> in non-IE browsers.
if ( CKEDITOR.env.ie
|| lastChild.getPrevious( bookmarkGuard )
|| lastChild.getNext( bookmarkGuard ) )
lastChild.remove();
}
}
// Get a reference for the next element. This is important because the
// above block can be removed or changed, so we can rely on it for the
// next interation.
if ( !this._.nextNode )
{
this._.nextNode = ( isLast || block.equals( lastNode ) ) ? null :
block.getNextSourceNode( true, null, lastNode );
}
if ( !bookmarkGuard( this._.nextNode ) )
{
this._.nextNode = this._.nextNode.getNextSourceNode( true, null, function( node )
{ return !node.equals( lastNode ) && bookmarkGuard( node ); } );
}
return block;
}
};
CKEDITOR.dom.range.prototype.createIterator = function()
{
return new iterator( this );
};
})();

View File

@@ -0,0 +1,229 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
/**
* @fileOverview The default editing block plugin, which holds the editing area
* and source view.
*/
(function()
{
var getMode = function( editor, mode )
{
return editor._.modes && editor._.modes[ mode || editor.mode ];
};
// This is a semaphore used to avoid recursive calls between
// the following data handling functions.
var isHandlingData;
CKEDITOR.plugins.add( 'editingblock',
{
init : function( editor )
{
if ( !editor.config.editingBlock )
return;
editor.on( 'themeSpace', function( event )
{
if ( event.data.space == 'contents' )
event.data.html += '<br>';
});
editor.on( 'themeLoaded', function()
{
editor.fireOnce( 'editingBlockReady' );
});
editor.on( 'uiReady', function()
{
editor.setMode( editor.config.startupMode );
});
editor.on( 'afterSetData', function()
{
if ( !isHandlingData )
{
function setData()
{
isHandlingData = true;
getMode( editor ).loadData( editor.getData() );
isHandlingData = false;
}
if ( editor.mode )
setData();
else
{
editor.on( 'mode', function()
{
setData();
editor.removeListener( 'mode', arguments.callee );
});
}
}
});
editor.on( 'beforeGetData', function()
{
if ( !isHandlingData && editor.mode )
{
isHandlingData = true;
editor.setData( getMode( editor ).getData() );
isHandlingData = false;
}
});
editor.on( 'getSnapshot', function( event )
{
if ( editor.mode )
event.data = getMode( editor ).getSnapshotData();
});
editor.on( 'loadSnapshot', function( event )
{
if ( editor.mode )
getMode( editor ).loadSnapshotData( event.data );
});
// For the first "mode" call, we'll also fire the "instanceReady"
// event.
editor.on( 'mode', function( event )
{
// Do that once only.
event.removeListener();
// Redirect the focus into editor for webkit. (#5713)
CKEDITOR.env.webkit && editor.container.on( 'focus', function()
{
editor.focus();
});
if ( editor.config.startupFocus )
editor.focus();
// Fire instanceReady for both the editor and CKEDITOR, but
// defer this until the whole execution has completed
// to guarantee the editor is fully responsible.
setTimeout( function(){
editor.fireOnce( 'instanceReady' );
CKEDITOR.fire( 'instanceReady', null, editor );
}, 0 );
});
}
});
/**
* The current editing mode. An editing mode is basically a viewport for
* editing or content viewing. By default the possible values for this
* property are "wysiwyg" and "source".
* @type String
* @example
* alert( CKEDITOR.instances.editor1.mode ); // "wysiwyg" (e.g.)
*/
CKEDITOR.editor.prototype.mode = '';
/**
* Registers an editing mode. This function is to be used mainly by plugins.
* @param {String} mode The mode name.
* @param {Object} modeEditor The mode editor definition.
* @example
*/
CKEDITOR.editor.prototype.addMode = function( mode, modeEditor )
{
modeEditor.name = mode;
( this._.modes || ( this._.modes = {} ) )[ mode ] = modeEditor;
};
/**
* Sets the current editing mode in this editor instance.
* @param {String} mode A registered mode name.
* @example
* // Switch to "source" view.
* CKEDITOR.instances.editor1.setMode( 'source' );
*/
CKEDITOR.editor.prototype.setMode = function( mode )
{
var data,
holderElement = this.getThemeSpace( 'contents' ),
isDirty = this.checkDirty();
// Unload the previous mode.
if ( this.mode )
{
if ( mode == this.mode )
return;
this.fire( 'beforeModeUnload' );
var currentMode = getMode( this );
data = currentMode.getData();
currentMode.unload( holderElement );
this.mode = '';
}
holderElement.setHtml( '' );
// Load required mode.
var modeEditor = getMode( this, mode );
if ( !modeEditor )
throw '[CKEDITOR.editor.setMode] Unknown mode "' + mode + '".';
if ( !isDirty )
{
this.on( 'mode', function()
{
this.resetDirty();
this.removeListener( 'mode', arguments.callee );
});
}
modeEditor.load( holderElement, ( typeof data ) != 'string' ? this.getData() : data);
};
/**
* Moves the selection focus to the editing are space in the editor.
*/
CKEDITOR.editor.prototype.focus = function()
{
var mode = getMode( this );
if ( mode )
mode.focus();
};
})();
/**
* The mode to load at the editor startup. It depends on the plugins
* loaded. By default, the "wysiwyg" and "source" modes are available.
* @type String
* @default 'wysiwyg'
* @example
* config.startupMode = 'source';
*/
CKEDITOR.config.startupMode = 'wysiwyg';
/**
* Sets whether the editor should have the focus when the page loads.
* @type Boolean
* @default false
* @example
* config.startupFocus = true;
*/
/**
* Whether to render or not the editing block area in the editor interface.
* @type Boolean
* @default true
* @example
* config.editingBlock = false;
*/
CKEDITOR.config.editingBlock = true;
/**
* Fired when a CKEDITOR instance is created, fully initialized and ready for interaction.
* @name CKEDITOR#instanceReady
* @event
* @param {CKEDITOR.editor} editor The editor instance that has been created.
*/

View File

@@ -0,0 +1,201 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
/**
* @fileOverview The "elementspath" plugin. It shows all elements in the DOM
* parent tree relative to the current selection in the editing area.
*/
(function()
{
var commands =
{
toolbarFocus :
{
exec : function( editor )
{
var idBase = editor._.elementsPath.idBase;
var element = CKEDITOR.document.getById( idBase + '0' );
// Make the first button focus accessible for IE. (#3417)
// Adobe AIR instead need while of delay.
element && element.focus( CKEDITOR.env.ie || CKEDITOR.env.air );
}
}
};
var emptyHtml = '<span class="cke_empty">&nbsp;</span>';
CKEDITOR.plugins.add( 'elementspath',
{
requires : [ 'selection' ],
init : function( editor )
{
var spaceId = 'cke_path_' + editor.name;
var spaceElement;
var getSpaceElement = function()
{
if ( !spaceElement )
spaceElement = CKEDITOR.document.getById( spaceId );
return spaceElement;
};
var idBase = 'cke_elementspath_' + CKEDITOR.tools.getNextNumber() + '_';
editor._.elementsPath = { idBase : idBase, filters : [] };
editor.on( 'themeSpace', function( event )
{
if ( event.data.space == 'bottom' )
{
event.data.html +=
'<span id="' + spaceId + '_label" class="cke_voice_label">' + editor.lang.elementsPath.eleLabel + '</span>' +
'<div id="' + spaceId + '" class="cke_path" role="group" aria-labelledby="' + spaceId + '_label">' + emptyHtml + '</div>';
}
});
function onClick( elementIndex )
{
editor.focus();
var element = editor._.elementsPath.list[ elementIndex ];
if ( element.is( 'body' ) )
{
var range = new CKEDITOR.dom.range( editor.document );
range.selectNodeContents( element );
range.select();
}
else
editor.getSelection().selectElement( element );
}
var onClickHanlder = CKEDITOR.tools.addFunction( onClick );
var onKeyDownHandler = CKEDITOR.tools.addFunction( function( elementIndex, ev )
{
var idBase = editor._.elementsPath.idBase,
element;
ev = new CKEDITOR.dom.event( ev );
var rtl = editor.lang.dir == 'rtl';
switch ( ev.getKeystroke() )
{
case rtl ? 39 : 37 : // LEFT-ARROW
case 9 : // TAB
element = CKEDITOR.document.getById( idBase + ( elementIndex + 1 ) );
if ( !element )
element = CKEDITOR.document.getById( idBase + '0' );
element.focus();
return false;
case rtl ? 37 : 39 : // RIGHT-ARROW
case CKEDITOR.SHIFT + 9 : // SHIFT + TAB
element = CKEDITOR.document.getById( idBase + ( elementIndex - 1 ) );
if ( !element )
element = CKEDITOR.document.getById( idBase + ( editor._.elementsPath.list.length - 1 ) );
element.focus();
return false;
case 27 : // ESC
editor.focus();
return false;
case 13 : // ENTER // Opera
case 32 : // SPACE
onClick( elementIndex );
return false;
}
return true;
});
editor.on( 'selectionChange', function( ev )
{
var env = CKEDITOR.env,
selection = ev.data.selection,
element = selection.getStartElement(),
html = [],
editor = ev.editor,
elementsList = editor._.elementsPath.list = [],
filters = editor._.elementsPath.filters;
while ( element )
{
var ignore = 0;
for ( var i = 0; i < filters.length; i++ )
{
if ( filters[ i ]( element ) === false )
{
ignore = 1;
break;
}
}
if ( !ignore )
{
var index = elementsList.push( element ) - 1;
var name;
if ( element.data( 'cke-real-element-type' ) )
name = element.data( 'cke-real-element-type' );
else
name = element.getName();
// Use this variable to add conditional stuff to the
// HTML (because we are doing it in reverse order... unshift).
var extra = '';
// Some browsers don't cancel key events in the keydown but in the
// keypress.
// TODO: Check if really needed for Gecko+Mac.
if ( env.opera || ( env.gecko && env.mac ) )
extra += ' onkeypress="return false;"';
// With Firefox, we need to force the button to redraw, otherwise it
// will remain in the focus state.
if ( env.gecko )
extra += ' onblur="this.style.cssText = this.style.cssText;"';
var label = editor.lang.elementsPath.eleTitle.replace( /%1/, name );
html.unshift(
'<a' +
' id="', idBase, index, '"' +
' href="javascript:void(\'', name, '\')"' +
' tabindex="-1"' +
' title="', label, '"' +
( ( CKEDITOR.env.gecko && CKEDITOR.env.version < 10900 ) ?
' onfocus="event.preventBubble();"' : '' ) +
' hidefocus="true" ' +
' onkeydown="return CKEDITOR.tools.callFunction(', onKeyDownHandler, ',', index, ', event );"' +
extra ,
' onclick="CKEDITOR.tools.callFunction('+ onClickHanlder, ',', index, '); return false;"',
' role="button" aria-labelledby="' + idBase + index + '_label">',
name,
'<span id="', idBase, index, '_label" class="cke_label">' + label + '</span>',
'</a>' );
}
if ( name == 'body' )
break;
element = element.getParent();
}
var space = getSpaceElement();
space.setHtml( html.join('') + emptyHtml );
editor.fire( 'elementsPathUpdate', { space : space } );
});
editor.on( 'contentDomUnload', function()
{
// If the spaceElement hasn't been initialized, don't try to do it at this time
// Only reuse existing reference.
spaceElement && spaceElement.setHtml( emptyHtml );
});
editor.addCommand( 'elementsPathFocus', commands.toolbarFocus );
}
});
})();

View File

@@ -0,0 +1,381 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
(function()
{
CKEDITOR.plugins.add( 'enterkey',
{
requires : [ 'keystrokes', 'indent' ],
init : function( editor )
{
var specialKeys = editor.specialKeys;
specialKeys[ 13 ] = enter;
specialKeys[ CKEDITOR.SHIFT + 13 ] = shiftEnter;
}
});
CKEDITOR.plugins.enterkey =
{
enterBlock : function( editor, mode, range, forceMode )
{
// Get the range for the current selection.
range = range || getRange( editor );
// We may not have valid ranges to work on, like when inside a
// contenteditable=false element.
if ( !range )
return;
var doc = range.document;
// Exit the list when we're inside an empty list item block. (#5376)
if ( range.checkStartOfBlock() && range.checkEndOfBlock() )
{
var path = new CKEDITOR.dom.elementPath( range.startContainer ),
block = path.block;
if ( block && ( block.is( 'li' ) || block.getParent().is( 'li' ) ) )
{
editor.execCommand( 'outdent' );
return;
}
}
// Determine the block element to be used.
var blockTag = ( mode == CKEDITOR.ENTER_DIV ? 'div' : 'p' );
// Split the range.
var splitInfo = range.splitBlock( blockTag );
if ( !splitInfo )
return;
// Get the current blocks.
var previousBlock = splitInfo.previousBlock,
nextBlock = splitInfo.nextBlock;
var isStartOfBlock = splitInfo.wasStartOfBlock,
isEndOfBlock = splitInfo.wasEndOfBlock;
var node;
// If this is a block under a list item, split it as well. (#1647)
if ( nextBlock )
{
node = nextBlock.getParent();
if ( node.is( 'li' ) )
{
nextBlock.breakParent( node );
nextBlock.move( nextBlock.getNext(), 1 );
}
}
else if ( previousBlock && ( node = previousBlock.getParent() ) && node.is( 'li' ) )
{
previousBlock.breakParent( node );
range.moveToElementEditStart( previousBlock.getNext() );
previousBlock.move( previousBlock.getPrevious() );
}
// If we have both the previous and next blocks, it means that the
// boundaries were on separated blocks, or none of them where on the
// block limits (start/end).
if ( !isStartOfBlock && !isEndOfBlock )
{
// If the next block is an <li> with another list tree as the first
// child, we'll need to append a filler (<br>/NBSP) or the list item
// wouldn't be editable. (#1420)
if ( nextBlock.is( 'li' )
&& ( node = nextBlock.getFirst( CKEDITOR.dom.walker.invisible( true ) ) )
&& node.is && node.is( 'ul', 'ol' ) )
( CKEDITOR.env.ie ? doc.createText( '\xa0' ) : doc.createElement( 'br' ) ).insertBefore( node );
// Move the selection to the end block.
if ( nextBlock )
range.moveToElementEditStart( nextBlock );
}
else
{
var newBlock,
newBlockDir;
if ( previousBlock )
{
// Do not enter this block if it's a header tag, or we are in
// a Shift+Enter (#77). Create a new block element instead
// (later in the code).
if ( previousBlock.is( 'li' ) || !headerTagRegex.test( previousBlock.getName() ) )
{
// Otherwise, duplicate the previous block.
newBlock = previousBlock.clone();
}
}
else if ( nextBlock )
newBlock = nextBlock.clone();
if ( !newBlock )
{
newBlock = doc.createElement( blockTag );
if ( previousBlock && ( newBlockDir = previousBlock.getDirection() ) )
newBlock.setAttribute( 'dir', newBlockDir );
}
// Force the enter block unless we're talking of a list item.
else if ( forceMode && !newBlock.is( 'li' ) )
newBlock.renameNode( blockTag );
// Recreate the inline elements tree, which was available
// before hitting enter, so the same styles will be available in
// the new block.
var elementPath = splitInfo.elementPath;
if ( elementPath )
{
for ( var i = 0, len = elementPath.elements.length ; i < len ; i++ )
{
var element = elementPath.elements[ i ];
if ( element.equals( elementPath.block ) || element.equals( elementPath.blockLimit ) )
break;
if ( CKEDITOR.dtd.$removeEmpty[ element.getName() ] )
{
element = element.clone();
newBlock.moveChildren( element );
newBlock.append( element );
}
}
}
if ( !CKEDITOR.env.ie )
newBlock.appendBogus();
range.insertNode( newBlock );
// This is tricky, but to make the new block visible correctly
// we must select it.
// The previousBlock check has been included because it may be
// empty if we have fixed a block-less space (like ENTER into an
// empty table cell).
if ( CKEDITOR.env.ie && isStartOfBlock && ( !isEndOfBlock || !previousBlock.getChildCount() ) )
{
// Move the selection to the new block.
range.moveToElementEditStart( isEndOfBlock ? previousBlock : newBlock );
range.select();
}
// Move the selection to the new block.
range.moveToElementEditStart( isStartOfBlock && !isEndOfBlock ? nextBlock : newBlock );
}
if ( !CKEDITOR.env.ie )
{
if ( nextBlock )
{
// If we have split the block, adds a temporary span at the
// range position and scroll relatively to it.
var tmpNode = doc.createElement( 'span' );
// We need some content for Safari.
tmpNode.setHtml( '&nbsp;' );
range.insertNode( tmpNode );
tmpNode.scrollIntoView();
range.deleteContents();
}
else
{
// We may use the above scroll logic for the new block case
// too, but it gives some weird result with Opera.
newBlock.scrollIntoView();
}
}
range.select();
},
enterBr : function( editor, mode, range, forceMode )
{
// Get the range for the current selection.
range = range || getRange( editor );
// We may not have valid ranges to work on, like when inside a
// contenteditable=false element.
if ( !range )
return;
var doc = range.document;
// Determine the block element to be used.
var blockTag = ( mode == CKEDITOR.ENTER_DIV ? 'div' : 'p' );
var isEndOfBlock = range.checkEndOfBlock();
var elementPath = new CKEDITOR.dom.elementPath( editor.getSelection().getStartElement() );
var startBlock = elementPath.block,
startBlockTag = startBlock && elementPath.block.getName();
var isPre = false;
if ( !forceMode && startBlockTag == 'li' )
{
enterBlock( editor, mode, range, forceMode );
return;
}
// If we are at the end of a header block.
if ( !forceMode && isEndOfBlock && headerTagRegex.test( startBlockTag ) )
{
var newBlock,
newBlockDir;
if ( ( newBlockDir = startBlock.getDirection() ) )
{
newBlock = doc.createElement( 'div' );
newBlock.setAttribute( 'dir', newBlockDir );
newBlock.insertAfter( startBlock );
range.setStart( newBlock, 0 );
}
else
{
// Insert a <br> after the current paragraph.
doc.createElement( 'br' ).insertAfter( startBlock );
// A text node is required by Gecko only to make the cursor blink.
if ( CKEDITOR.env.gecko )
doc.createText( '' ).insertAfter( startBlock );
// IE has different behaviors regarding position.
range.setStartAt( startBlock.getNext(), CKEDITOR.env.ie ? CKEDITOR.POSITION_BEFORE_START : CKEDITOR.POSITION_AFTER_START );
}
}
else
{
var lineBreak;
isPre = ( startBlockTag == 'pre' );
// Gecko prefers <br> as line-break inside <pre> (#4711).
if ( isPre && !CKEDITOR.env.gecko )
lineBreak = doc.createText( CKEDITOR.env.ie ? '\r' : '\n' );
else
lineBreak = doc.createElement( 'br' );
range.deleteContents();
range.insertNode( lineBreak );
// A text node is required by Gecko only to make the cursor blink.
// We need some text inside of it, so the bogus <br> is properly
// created.
if ( !CKEDITOR.env.ie )
doc.createText( '\ufeff' ).insertAfter( lineBreak );
// If we are at the end of a block, we must be sure the bogus node is available in that block.
if ( isEndOfBlock && !CKEDITOR.env.ie )
lineBreak.getParent().appendBogus();
// Now we can remove the text node contents, so the caret doesn't
// stop on it.
if ( !CKEDITOR.env.ie )
lineBreak.getNext().$.nodeValue = '';
// IE has different behavior regarding position.
if ( CKEDITOR.env.ie )
range.setStartAt( lineBreak, CKEDITOR.POSITION_AFTER_END );
else
range.setStartAt( lineBreak.getNext(), CKEDITOR.POSITION_AFTER_START );
// Scroll into view, for non IE.
if ( !CKEDITOR.env.ie )
{
var dummy = null;
// BR is not positioned in Opera and Webkit.
if ( !CKEDITOR.env.gecko )
{
dummy = doc.createElement( 'span' );
// We need have some contents for Webkit to position it
// under parent node. ( #3681)
dummy.setHtml('&nbsp;');
}
else
dummy = doc.createElement( 'br' );
dummy.insertBefore( lineBreak.getNext() );
dummy.scrollIntoView();
dummy.remove();
}
}
// This collapse guarantees the cursor will be blinking.
range.collapse( true );
range.select( isPre );
}
};
var plugin = CKEDITOR.plugins.enterkey,
enterBr = plugin.enterBr,
enterBlock = plugin.enterBlock,
headerTagRegex = /^h[1-6]$/;
function shiftEnter( editor )
{
// Only effective within document.
if ( editor.mode != 'wysiwyg' )
return false;
// On SHIFT+ENTER:
// 1. We want to enforce the mode to be respected, instead
// of cloning the current block. (#77)
// 2. Always perform a block break when inside <pre> (#5402).
if ( editor.getSelection().getStartElement().hasAscendant( 'pre', true ) )
{
setTimeout( function() { enterBlock( editor, editor.config.enterMode, null, true ); }, 0 );
return true;
}
else
return enter( editor, editor.config.shiftEnterMode, 1 );
}
function enter( editor, mode, forceMode )
{
forceMode = editor.config.forceEnterMode || forceMode;
// Only effective within document.
if ( editor.mode != 'wysiwyg' )
return false;
if ( !mode )
mode = editor.config.enterMode;
// Use setTimout so the keys get cancelled immediatelly.
setTimeout( function()
{
editor.fire( 'saveSnapshot' ); // Save undo step.
if ( mode == CKEDITOR.ENTER_BR || editor.getSelection().getStartElement().hasAscendant( 'pre', 1 ) )
enterBr( editor, mode, null, forceMode );
else
enterBlock( editor, mode, null, forceMode );
}, 0 );
return true;
}
function getRange( editor )
{
// Get the selection ranges.
var ranges = editor.getSelection().getRanges( true );
// Delete the contents of all ranges except the first one.
for ( var i = ranges.length - 1 ; i > 0 ; i-- )
{
ranges[ i ].deleteContents();
}
// Return the first range.
return ranges[ 0 ];
}
})();

View File

@@ -0,0 +1,226 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
(function()
{
// Base HTML entities.
var htmlbase = 'nbsp,gt,lt,quot';
var entities =
// Latin-1 Entities
'iexcl,cent,pound,curren,yen,brvbar,sect,uml,copy,ordf,laquo,' +
'not,shy,reg,macr,deg,plusmn,sup2,sup3,acute,micro,para,middot,' +
'cedil,sup1,ordm,raquo,frac14,frac12,frac34,iquest,times,divide,' +
// Symbols
'fnof,bull,hellip,prime,Prime,oline,frasl,weierp,image,real,trade,' +
'alefsym,larr,uarr,rarr,darr,harr,crarr,lArr,uArr,rArr,dArr,hArr,' +
'forall,part,exist,empty,nabla,isin,notin,ni,prod,sum,minus,lowast,' +
'radic,prop,infin,ang,and,or,cap,cup,int,there4,sim,cong,asymp,ne,' +
'equiv,le,ge,sub,sup,nsub,sube,supe,oplus,otimes,perp,sdot,lceil,' +
'rceil,lfloor,rfloor,lang,rang,loz,spades,clubs,hearts,diams,' +
// Other Special Characters
'circ,tilde,ensp,emsp,thinsp,zwnj,zwj,lrm,rlm,ndash,mdash,lsquo,' +
'rsquo,sbquo,ldquo,rdquo,bdquo,dagger,Dagger,permil,lsaquo,rsaquo,' +
'euro';
// Latin Letters Entities
var latin =
'Agrave,Aacute,Acirc,Atilde,Auml,Aring,AElig,Ccedil,Egrave,Eacute,' +
'Ecirc,Euml,Igrave,Iacute,Icirc,Iuml,ETH,Ntilde,Ograve,Oacute,Ocirc,' +
'Otilde,Ouml,Oslash,Ugrave,Uacute,Ucirc,Uuml,Yacute,THORN,szlig,' +
'agrave,aacute,acirc,atilde,auml,aring,aelig,ccedil,egrave,eacute,' +
'ecirc,euml,igrave,iacute,icirc,iuml,eth,ntilde,ograve,oacute,ocirc,' +
'otilde,ouml,oslash,ugrave,uacute,ucirc,uuml,yacute,thorn,yuml,' +
'OElig,oelig,Scaron,scaron,Yuml';
// Greek Letters Entities.
var greek =
'Alpha,Beta,Gamma,Delta,Epsilon,Zeta,Eta,Theta,Iota,Kappa,Lambda,Mu,' +
'Nu,Xi,Omicron,Pi,Rho,Sigma,Tau,Upsilon,Phi,Chi,Psi,Omega,alpha,' +
'beta,gamma,delta,epsilon,zeta,eta,theta,iota,kappa,lambda,mu,nu,xi,' +
'omicron,pi,rho,sigmaf,sigma,tau,upsilon,phi,chi,psi,omega,thetasym,' +
'upsih,piv';
/**
* Create a mapping table between one character and it's entity form from a list of entity names.
* @param reverse {Boolean} Whether create a reverse map from the entity string form to actual character.
*/
function buildTable( entities, reverse )
{
var table = {},
regex = [];
// Entities that the browsers DOM don't transform to the final char
// automatically.
var specialTable =
{
nbsp : '\u00A0', // IE | FF
shy : '\u00AD', // IE
gt : '\u003E', // IE | FF | -- | Opera
lt : '\u003C' // IE | FF | Safari | Opera
};
entities = entities.replace( /\b(nbsp|shy|gt|lt|amp)(?:,|$)/g, function( match, entity )
{
var org = reverse ? '&' + entity + ';' : specialTable[ entity ],
result = reverse ? specialTable[ entity ] : '&' + entity + ';';
table[ org ] = result;
regex.push( org );
return '';
});
if ( !reverse )
{
// Transforms the entities string into an array.
entities = entities.split( ',' );
// Put all entities inside a DOM element, transforming them to their
// final chars.
var div = document.createElement( 'div' ),
chars;
div.innerHTML = '&' + entities.join( ';&' ) + ';';
chars = div.innerHTML;
div = null;
// Add all chars to the table.
for ( var i = 0 ; i < chars.length ; i++ )
{
var charAt = chars.charAt( i );
table[ charAt ] = '&' + entities[ i ] + ';';
regex.push( charAt );
}
}
table.regex = regex.join( reverse ? '|' : '' );
return table;
}
CKEDITOR.plugins.add( 'entities',
{
afterInit : function( editor )
{
var config = editor.config;
var dataProcessor = editor.dataProcessor,
htmlFilter = dataProcessor && dataProcessor.htmlFilter;
if ( htmlFilter )
{
// Mandatory HTML base entities.
var selectedEntities = htmlbase;
if ( config.entities )
{
selectedEntities += ',' + entities;
if ( config.entities_latin )
selectedEntities += ',' + latin;
if ( config.entities_greek )
selectedEntities += ',' + greek;
if ( config.entities_additional )
selectedEntities += ',' + config.entities_additional;
}
var entitiesTable = buildTable( selectedEntities );
// Create the Regex used to find entities in the text.
var entitiesRegex = '[' + entitiesTable.regex + ']';
delete entitiesTable.regex;
if ( config.entities && config.entities_processNumerical )
entitiesRegex = '[^ -~]|' + entitiesRegex ;
entitiesRegex = new RegExp( entitiesRegex, 'g' );
function getEntity( character )
{
return config.entities_processNumerical == 'force' || !entitiesTable[ character ] ?
'&#' + character.charCodeAt(0) + ';'
: entitiesTable[ character ];
}
// Decode entities that the browsers has transformed
// at first place.
var baseEntitiesTable = buildTable( [ htmlbase, 'shy' ].join( ',' ) , true ),
baseEntitiesRegex = new RegExp( baseEntitiesTable.regex, 'g' );
function getChar( character )
{
return baseEntitiesTable[ character ];
}
htmlFilter.addRules(
{
text : function( text )
{
return text.replace( baseEntitiesRegex, getChar )
.replace( entitiesRegex, getEntity );
}
});
}
}
});
})();
/**
* Whether to use HTML entities in the output.
* @type Boolean
* @default true
* @example
* config.entities = false;
*/
CKEDITOR.config.entities = true;
/**
* Whether to convert some Latin characters (Latin alphabet No&#46; 1, ISO 8859-1)
* to HTML entities. The list of entities can be found at the
* <a href="http://www.w3.org/TR/html4/sgml/entities.html#h-24.2.1">W3C HTML 4.01 Specification, section 24.2.1</a>.
* @type Boolean
* @default true
* @example
* config.entities_latin = false;
*/
CKEDITOR.config.entities_latin = true;
/**
* Whether to convert some symbols, mathematical symbols, and Greek letters to
* HTML entities. This may be more relevant for users typing text written in Greek.
* The list of entities can be found at the
* <a href="http://www.w3.org/TR/html4/sgml/entities.html#h-24.3.1">W3C HTML 4.01 Specification, section 24.3.1</a>.
* @type Boolean
* @default true
* @example
* config.entities_greek = false;
*/
CKEDITOR.config.entities_greek = true;
/**
* Whether to convert all remaining characters, not comprised in the ASCII
* character table, to their relative decimal numeric representation of HTML entity.
* When specified as the value 'force', it will simply convert all entities into the above form.
* For example, the phrase "This is Chinese: &#27721;&#35821;." is outputted
* as "This is Chinese: &amp;#27721;&amp;#35821;."
* @type Boolean
* @type Boolean|String
* @default false
* @example
* config.entities_processNumerical = true;
* config.entities_processNumerical = 'force'; //Convert from "&nbsp;" into "&#160;";
*/
/**
* An additional list of entities to be used. It's a string containing each
* entry separated by a comma. Entities names or number must be used, exclusing
* the "&amp;" preffix and the ";" termination.
* @default '#39' // The single quote (') character.
* @type String
* @example
*/
CKEDITOR.config.entities_additional = '#39';

View File

@@ -0,0 +1,126 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
(function()
{
var htmlFilterRules =
{
elements :
{
$ : function( element )
{
var attributes = element.attributes,
realHtml = attributes && attributes[ 'data-cke-realelement' ],
realFragment = realHtml && new CKEDITOR.htmlParser.fragment.fromHtml( decodeURIComponent( realHtml ) ),
realElement = realFragment && realFragment.children[ 0 ];
// If we have width/height in the element, we must move it into
// the real element.
if ( realElement && element.attributes[ 'data-cke-resizable' ] )
{
var style = element.attributes.style;
if ( style )
{
// Get the width from the style.
var match = /(?:^|\s)width\s*:\s*(\d+)/i.exec( style ),
width = match && match[1];
// Get the height from the style.
match = /(?:^|\s)height\s*:\s*(\d+)/i.exec( style );
var height = match && match[1];
if ( width )
realElement.attributes.width = width;
if ( height )
realElement.attributes.height = height;
}
}
return realElement;
}
}
};
CKEDITOR.plugins.add( 'fakeobjects',
{
requires : [ 'htmlwriter' ],
afterInit : function( editor )
{
var dataProcessor = editor.dataProcessor,
htmlFilter = dataProcessor && dataProcessor.htmlFilter;
if ( htmlFilter )
htmlFilter.addRules( htmlFilterRules );
}
});
})();
CKEDITOR.editor.prototype.createFakeElement = function( realElement, className, realElementType, isResizable )
{
var lang = this.lang.fakeobjects,
label = lang[ realElementType ] || lang.unknown;
var attributes =
{
'class' : className,
src : CKEDITOR.getUrl( 'images/spacer.gif' ),
'data-cke-realelement' : encodeURIComponent( realElement.getOuterHtml() ),
'data-cke-real-node-type' : realElement.type,
alt : label,
title : label,
align : realElement.getAttribute( 'align' ) || ''
};
if ( realElementType )
attributes[ 'data-cke-real-element-type' ] = realElementType;
if ( isResizable )
attributes[ 'data-cke-resizable' ] = isResizable;
return this.document.createElement( 'img', { attributes : attributes } );
};
CKEDITOR.editor.prototype.createFakeParserElement = function( realElement, className, realElementType, isResizable )
{
var lang = this.lang.fakeobjects,
label = lang[ realElementType ] || lang.unknown,
html;
var writer = new CKEDITOR.htmlParser.basicWriter();
realElement.writeHtml( writer );
html = writer.getHtml();
var attributes =
{
'class' : className,
src : CKEDITOR.getUrl( 'images/spacer.gif' ),
'data-cke-realelement' : encodeURIComponent( html ),
'data-cke-real-node-type' : realElement.type,
alt : label,
title : label,
align : realElement.attributes.align || ''
};
if ( realElementType )
attributes[ 'data-cke-real-element-type' ] = realElementType;
if ( isResizable )
attributes[ 'data-cke-resizable' ] = isResizable;
return new CKEDITOR.htmlParser.element( 'img', attributes );
};
CKEDITOR.editor.prototype.restoreRealElement = function( fakeElement )
{
if ( fakeElement.data( 'cke-real-node-type' ) != CKEDITOR.NODE_ELEMENT )
return null;
return CKEDITOR.dom.element.createFromHtml(
decodeURIComponent( fakeElement.data( 'cke-realelement' ) ),
this.document );
};

View File

@@ -0,0 +1,501 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
/**
* @fileOverview The "filebrowser" plugin, it adds support for file uploads and
* browsing.
*
* When file is selected inside of the file browser or uploaded, its url is
* inserted automatically to a field, which is described in the 'filebrowser'
* attribute. To specify field that should be updated, pass the tab id and
* element id, separated with a colon.
*
* Example 1: (Browse)
*
* <pre>
* {
* type : 'button',
* id : 'browse',
* filebrowser : 'tabId:elementId',
* label : editor.lang.common.browseServer
* }
* </pre>
*
* If you set the 'filebrowser' attribute on any element other than
* 'fileButton', the 'Browse' action will be triggered.
*
* Example 2: (Quick Upload)
*
* <pre>
* {
* type : 'fileButton',
* id : 'uploadButton',
* filebrowser : 'tabId:elementId',
* label : editor.lang.common.uploadSubmit,
* 'for' : [ 'upload', 'upload' ]
* }
* </pre>
*
* If you set the 'filebrowser' attribute on a fileButton element, the
* 'QuickUpload' action will be executed.
*
* Filebrowser plugin also supports more advanced configuration (through
* javascript object).
*
* The following settings are supported:
*
* <pre>
* [action] - Browse or QuickUpload
* [target] - field to update, tabId:elementId
* [params] - additional arguments to be passed to the server connector (optional)
* [onSelect] - function to execute when file is selected/uploaded (optional)
* [url] - the URL to be called (optional)
* </pre>
*
* Example 3: (Quick Upload)
*
* <pre>
* {
* type : 'fileButton',
* label : editor.lang.common.uploadSubmit,
* id : 'buttonId',
* filebrowser :
* {
* action : 'QuickUpload', //required
* target : 'tab1:elementId', //required
* params : //optional
* {
* type : 'Files',
* currentFolder : '/folder/'
* },
* onSelect : function( fileUrl, errorMessage ) //optional
* {
* // Do not call the built-in selectFuntion
* // return false;
* }
* },
* 'for' : [ 'tab1', 'myFile' ]
* }
* </pre>
*
* Suppose we have a file element with id 'myFile', text field with id
* 'elementId' and a fileButton. If filebowser.url is not specified explicitly,
* form action will be set to 'filebrowser[DialogName]UploadUrl' or, if not
* specified, to 'filebrowserUploadUrl'. Additional parameters from 'params'
* object will be added to the query string. It is possible to create your own
* uploadHandler and cancel the built-in updateTargetElement command.
*
* Example 4: (Browse)
*
* <pre>
* {
* type : 'button',
* id : 'buttonId',
* label : editor.lang.common.browseServer,
* filebrowser :
* {
* action : 'Browse',
* url : '/ckfinder/ckfinder.html&amp;type=Images',
* target : 'tab1:elementId'
* }
* }
* </pre>
*
* In this example, after pressing a button, file browser will be opened in a
* popup. If we don't specify filebrowser.url attribute,
* 'filebrowser[DialogName]BrowseUrl' or 'filebrowserBrowseUrl' will be used.
* After selecting a file in a file browser, an element with id 'elementId' will
* be updated. Just like in the third example, a custom 'onSelect' function may be
* defined.
*/
( function()
{
/**
* Adds (additional) arguments to given url.
*
* @param {String}
* url The url.
* @param {Object}
* params Additional parameters.
*/
function addQueryString( url, params )
{
var queryString = [];
if ( !params )
return url;
else
{
for ( var i in params )
queryString.push( i + "=" + encodeURIComponent( params[ i ] ) );
}
return url + ( ( url.indexOf( "?" ) != -1 ) ? "&" : "?" ) + queryString.join( "&" );
}
/**
* Make a string's first character uppercase.
*
* @param {String}
* str String.
*/
function ucFirst( str )
{
str += '';
var f = str.charAt( 0 ).toUpperCase();
return f + str.substr( 1 );
}
/**
* The onlick function assigned to the 'Browse Server' button. Opens the
* file browser and updates target field when file is selected.
*
* @param {CKEDITOR.event}
* evt The event object.
*/
function browseServer( evt )
{
var dialog = this.getDialog();
var editor = dialog.getParentEditor();
editor._.filebrowserSe = this;
var width = editor.config[ 'filebrowser' + ucFirst( dialog.getName() ) + 'WindowWidth' ]
|| editor.config.filebrowserWindowWidth || '80%';
var height = editor.config[ 'filebrowser' + ucFirst( dialog.getName() ) + 'WindowHeight' ]
|| editor.config.filebrowserWindowHeight || '70%';
var params = this.filebrowser.params || {};
params.CKEditor = editor.name;
params.CKEditorFuncNum = editor._.filebrowserFn;
if ( !params.langCode )
params.langCode = editor.langCode;
var url = addQueryString( this.filebrowser.url, params );
editor.popup( url, width, height, editor.config.fileBrowserWindowFeatures );
}
/**
* The onlick function assigned to the 'Upload' button. Makes the final
* decision whether form is really submitted and updates target field when
* file is uploaded.
*
* @param {CKEDITOR.event}
* evt The event object.
*/
function uploadFile( evt )
{
var dialog = this.getDialog();
var editor = dialog.getParentEditor();
editor._.filebrowserSe = this;
// If user didn't select the file, stop the upload.
if ( !dialog.getContentElement( this[ 'for' ][ 0 ], this[ 'for' ][ 1 ] ).getInputElement().$.value )
return false;
if ( !dialog.getContentElement( this[ 'for' ][ 0 ], this[ 'for' ][ 1 ] ).getAction() )
return false;
return true;
}
/**
* Setups the file element.
*
* @param {CKEDITOR.ui.dialog.file}
* fileInput The file element used during file upload.
* @param {Object}
* filebrowser Object containing filebrowser settings assigned to
* the fileButton associated with this file element.
*/
function setupFileElement( editor, fileInput, filebrowser )
{
var params = filebrowser.params || {};
params.CKEditor = editor.name;
params.CKEditorFuncNum = editor._.filebrowserFn;
if ( !params.langCode )
params.langCode = editor.langCode;
fileInput.action = addQueryString( filebrowser.url, params );
fileInput.filebrowser = filebrowser;
}
/**
* Traverse through the content definition and attach filebrowser to
* elements with 'filebrowser' attribute.
*
* @param String
* dialogName Dialog name.
* @param {CKEDITOR.dialog.dialogDefinitionObject}
* definition Dialog definition.
* @param {Array}
* elements Array of {@link CKEDITOR.dialog.contentDefinition}
* objects.
*/
function attachFileBrowser( editor, dialogName, definition, elements )
{
var element, fileInput;
for ( var i in elements )
{
element = elements[ i ];
if ( element.type == 'hbox' || element.type == 'vbox' )
attachFileBrowser( editor, dialogName, definition, element.children );
if ( !element.filebrowser )
continue;
if ( typeof element.filebrowser == 'string' )
{
var fb =
{
action : ( element.type == 'fileButton' ) ? 'QuickUpload' : 'Browse',
target : element.filebrowser
};
element.filebrowser = fb;
}
if ( element.filebrowser.action == 'Browse' )
{
var url = element.filebrowser.url;
if ( url === undefined )
{
url = editor.config[ 'filebrowser' + ucFirst( dialogName ) + 'BrowseUrl' ];
if ( url === undefined )
url = editor.config.filebrowserBrowseUrl;
}
if ( url )
{
element.onClick = browseServer;
element.filebrowser.url = url;
element.hidden = false;
}
}
else if ( element.filebrowser.action == 'QuickUpload' && element[ 'for' ] )
{
url = element.filebrowser.url;
if ( url === undefined )
{
url = editor.config[ 'filebrowser' + ucFirst( dialogName ) + 'UploadUrl' ];
if ( url === undefined )
url = editor.config.filebrowserUploadUrl;
}
if ( url )
{
var onClick = element.onClick;
element.onClick = function( evt )
{
// "element" here means the definition object, so we need to find the correct
// button to scope the event call
var sender = evt.sender;
if ( onClick && onClick.call( sender, evt ) === false )
return false;
return uploadFile.call( sender, evt );
};
element.filebrowser.url = url;
element.hidden = false;
setupFileElement( editor, definition.getContents( element[ 'for' ][ 0 ] ).get( element[ 'for' ][ 1 ] ), element.filebrowser );
}
}
}
}
/**
* Updates the target element with the url of uploaded/selected file.
*
* @param {String}
* url The url of a file.
*/
function updateTargetElement( url, sourceElement )
{
var dialog = sourceElement.getDialog();
var targetElement = sourceElement.filebrowser.target || null;
url = url.replace( /#/g, '%23' );
// If there is a reference to targetElement, update it.
if ( targetElement )
{
var target = targetElement.split( ':' );
var element = dialog.getContentElement( target[ 0 ], target[ 1 ] );
if ( element )
{
element.setValue( url );
dialog.selectPage( target[ 0 ] );
}
}
}
/**
* Returns true if filebrowser is configured in one of the elements.
*
* @param {CKEDITOR.dialog.dialogDefinitionObject}
* definition Dialog definition.
* @param String
* tabId The tab id where element(s) can be found.
* @param String
* elementId The element id (or ids, separated with a semicolon) to check.
*/
function isConfigured( definition, tabId, elementId )
{
if ( elementId.indexOf( ";" ) !== -1 )
{
var ids = elementId.split( ";" );
for ( var i = 0 ; i < ids.length ; i++ )
{
if ( isConfigured( definition, tabId, ids[i] ) )
return true;
}
return false;
}
var elementFileBrowser = definition.getContents( tabId ).get( elementId ).filebrowser;
return ( elementFileBrowser && elementFileBrowser.url );
}
function setUrl( fileUrl, data )
{
var dialog = this._.filebrowserSe.getDialog(),
targetInput = this._.filebrowserSe[ 'for' ],
onSelect = this._.filebrowserSe.filebrowser.onSelect;
if ( targetInput )
dialog.getContentElement( targetInput[ 0 ], targetInput[ 1 ] ).reset();
if ( typeof data == 'function' && data.call( this._.filebrowserSe ) === false )
return;
if ( onSelect && onSelect.call( this._.filebrowserSe, fileUrl, data ) === false )
return;
// The "data" argument may be used to pass the error message to the editor.
if ( typeof data == 'string' && data )
alert( data );
if ( fileUrl )
updateTargetElement( fileUrl, this._.filebrowserSe );
}
CKEDITOR.plugins.add( 'filebrowser',
{
init : function( editor, pluginPath )
{
editor._.filebrowserFn = CKEDITOR.tools.addFunction( setUrl, editor );
}
} );
CKEDITOR.on( 'dialogDefinition', function( evt )
{
var definition = evt.data.definition,
element;
// Associate filebrowser to elements with 'filebrowser' attribute.
for ( var i in definition.contents )
{
if ( ( element = definition.contents[ i ] ) )
{
attachFileBrowser( evt.editor, evt.data.name, definition, element.elements );
if ( element.hidden && element.filebrowser )
{
element.hidden = !isConfigured( definition, element[ 'id' ], element.filebrowser );
}
}
}
} );
} )();
/**
* The location of an external file browser, that should be launched when "Browse Server" button is pressed.
* If configured, the "Browse Server" button will appear in Link, Image and Flash dialogs.
* @see The <a href="http://docs.cksource.com/CKEditor_3.x/Developers_Guide/File_Browser_(Uploader)">File Browser/Uploader</a> documentation.
* @name CKEDITOR.config.filebrowserBrowseUrl
* @since 3.0
* @type String
* @default '' (empty string = disabled)
* @example
* config.filebrowserBrowseUrl = '/browser/browse.php';
*/
/**
* The location of a script that handles file uploads.
* If set, the "Upload" tab will appear in "Link", "Image" and "Flash" dialogs.
* @name CKEDITOR.config.filebrowserUploadUrl
* @see The <a href="http://docs.cksource.com/CKEditor_3.x/Developers_Guide/File_Browser_(Uploader)">File Browser/Uploader</a> documentation.
* @since 3.0
* @type String
* @default '' (empty string = disabled)
* @example
* config.filebrowserUploadUrl = '/uploader/upload.php';
*/
/**
* The location of an external file browser, that should be launched when "Browse Server" button is pressed in the Image dialog.
* If not set, CKEditor will use {@link CKEDITOR.config.filebrowserBrowseUrl}.
* @name CKEDITOR.config.filebrowserImageBrowseUrl
* @since 3.0
* @type String
* @default '' (empty string = disabled)
* @example
* config.filebrowserImageBrowseUrl = '/browser/browse.php?type=Images';
*/
/**
* The location of an external file browser, that should be launched when "Browse Server" button is pressed in the Flash dialog.
* If not set, CKEditor will use {@link CKEDITOR.config.filebrowserBrowseUrl}.
* @name CKEDITOR.config.filebrowserFlashBrowseUrl
* @since 3.0
* @type String
* @default '' (empty string = disabled)
* @example
* config.filebrowserFlashBrowseUrl = '/browser/browse.php?type=Flash';
*/
/**
* The location of a script that handles file uploads in the Image dialog.
* If not set, CKEditor will use {@link CKEDITOR.config.filebrowserUploadUrl}.
* @name CKEDITOR.config.filebrowserImageUploadUrl
* @since 3.0
* @type String
* @default '' (empty string = disabled)
* @example
* config.filebrowserImageUploadUrl = '/uploader/upload.php?type=Images';
*/
/**
* The location of a script that handles file uploads in the Flash dialog.
* If not set, CKEditor will use {@link CKEDITOR.config.filebrowserUploadUrl}.
* @name CKEDITOR.config.filebrowserFlashUploadUrl
* @since 3.0
* @type String
* @default '' (empty string = disabled)
* @example
* config.filebrowserFlashUploadUrl = '/uploader/upload.php?type=Flash';
*/
/**
* The location of an external file browser, that should be launched when "Browse Server" button is pressed in the Link tab of Image dialog.
* If not set, CKEditor will use {@link CKEDITOR.config.filebrowserBrowseUrl}.
* @name CKEDITOR.config.filebrowserImageBrowseLinkUrl
* @since 3.2
* @type String
* @default '' (empty string = disabled)
* @example
* config.filebrowserImageBrowseLinkUrl = '/browser/browse.php';
*/
/**
* The "features" to use in the file browser popup window.
* @name CKEDITOR.config.filebrowserWindowFeatures
* @since 3.4.1
* @type String
* @default 'location=no,menubar=no,toolbar=no,dependent=yes,minimizable=no,modal=yes,alwaysRaised=yes,resizable=yes,scrollbars=yes'
* @example
* config.filebrowserWindowFeatures = 'resizable=yes,scrollbars=no';
*/

View File

@@ -0,0 +1,890 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
(function()
{
var isReplace;
function findEvaluator( node )
{
return node.type == CKEDITOR.NODE_TEXT && node.getLength() > 0 && ( !isReplace || !node.isReadOnly() );
}
/**
* Elements which break characters been considered as sequence.
*/
function nonCharactersBoundary( node )
{
return !( node.type == CKEDITOR.NODE_ELEMENT && node.isBlockBoundary(
CKEDITOR.tools.extend( {}, CKEDITOR.dtd.$empty, CKEDITOR.dtd.$nonEditable ) ) );
}
/**
* Get the cursor object which represent both current character and it's dom
* position thing.
*/
var cursorStep = function()
{
return {
textNode : this.textNode,
offset : this.offset,
character : this.textNode ?
this.textNode.getText().charAt( this.offset ) : null,
hitMatchBoundary : this._.matchBoundary
};
};
var pages = [ 'find', 'replace' ],
fieldsMapping = [
[ 'txtFindFind', 'txtFindReplace' ],
[ 'txtFindCaseChk', 'txtReplaceCaseChk' ],
[ 'txtFindWordChk', 'txtReplaceWordChk' ],
[ 'txtFindCyclic', 'txtReplaceCyclic' ] ];
/**
* Synchronize corresponding filed values between 'replace' and 'find' pages.
* @param {String} currentPageId The page id which receive values.
*/
function syncFieldsBetweenTabs( currentPageId )
{
var sourceIndex, targetIndex,
sourceField, targetField;
sourceIndex = currentPageId === 'find' ? 1 : 0;
targetIndex = 1 - sourceIndex;
var i, l = fieldsMapping.length;
for ( i = 0 ; i < l ; i++ )
{
sourceField = this.getContentElement( pages[ sourceIndex ],
fieldsMapping[ i ][ sourceIndex ] );
targetField = this.getContentElement( pages[ targetIndex ],
fieldsMapping[ i ][ targetIndex ] );
targetField.setValue( sourceField.getValue() );
}
}
var findDialog = function( editor, startupPage )
{
// Style object for highlights: (#5018)
// 1. Defined as full match style to avoid compromising ordinary text color styles.
// 2. Must be apply onto inner-most text to avoid conflicting with ordinary text color styles visually.
var highlightStyle = new CKEDITOR.style( CKEDITOR.tools.extend( { fullMatch : true, childRule : function(){ return 0; } },
editor.config.find_highlight ) );
/**
* Iterator which walk through the specified range char by char. By
* default the walking will not stop at the character boundaries, until
* the end of the range is encountered.
* @param { CKEDITOR.dom.range } range
* @param {Boolean} matchWord Whether the walking will stop at character boundary.
*/
var characterWalker = function( range , matchWord )
{
var self = this;
var walker =
new CKEDITOR.dom.walker( range );
walker.guard = matchWord ? nonCharactersBoundary : function( node )
{
!nonCharactersBoundary( node ) && ( self._.matchBoundary = true );
};
walker[ 'evaluator' ] = findEvaluator;
walker.breakOnFalse = 1;
if ( range.startContainer.type == CKEDITOR.NODE_TEXT )
{
this.textNode = range.startContainer;
this.offset = range.startOffset - 1;
}
this._ = {
matchWord : matchWord,
walker : walker,
matchBoundary : false
};
};
characterWalker.prototype = {
next : function()
{
return this.move();
},
back : function()
{
return this.move( true );
},
move : function( rtl )
{
var currentTextNode = this.textNode;
// Already at the end of document, no more character available.
if ( currentTextNode === null )
return cursorStep.call( this );
this._.matchBoundary = false;
// There are more characters in the text node, step forward.
if ( currentTextNode
&& rtl
&& this.offset > 0 )
{
this.offset--;
return cursorStep.call( this );
}
else if ( currentTextNode
&& this.offset < currentTextNode.getLength() - 1 )
{
this.offset++;
return cursorStep.call( this );
}
else
{
currentTextNode = null;
// At the end of the text node, walking foward for the next.
while ( !currentTextNode )
{
currentTextNode =
this._.walker[ rtl ? 'previous' : 'next' ].call( this._.walker );
// Stop searching if we're need full word match OR
// already reach document end.
if ( this._.matchWord && !currentTextNode
|| this._.walker._.end )
break;
}
// Found a fresh text node.
this.textNode = currentTextNode;
if ( currentTextNode )
this.offset = rtl ? currentTextNode.getLength() - 1 : 0;
else
this.offset = 0;
}
return cursorStep.call( this );
}
};
/**
* A range of cursors which represent a trunk of characters which try to
* match, it has the same length as the pattern string.
*/
var characterRange = function( characterWalker, rangeLength )
{
this._ = {
walker : characterWalker,
cursors : [],
rangeLength : rangeLength,
highlightRange : null,
isMatched : 0
};
};
characterRange.prototype = {
/**
* Translate this range to {@link CKEDITOR.dom.range}
*/
toDomRange : function()
{
var range = new CKEDITOR.dom.range( editor.document );
var cursors = this._.cursors;
if ( cursors.length < 1 )
{
var textNode = this._.walker.textNode;
if ( textNode )
range.setStartAfter( textNode );
else
return null;
}
else
{
var first = cursors[0],
last = cursors[ cursors.length - 1 ];
range.setStart( first.textNode, first.offset );
range.setEnd( last.textNode, last.offset + 1 );
}
return range;
},
/**
* Reflect the latest changes from dom range.
*/
updateFromDomRange : function( domRange )
{
var cursor,
walker = new characterWalker( domRange );
this._.cursors = [];
do
{
cursor = walker.next();
if ( cursor.character )
this._.cursors.push( cursor );
}
while ( cursor.character );
this._.rangeLength = this._.cursors.length;
},
setMatched : function()
{
this._.isMatched = true;
},
clearMatched : function()
{
this._.isMatched = false;
},
isMatched : function()
{
return this._.isMatched;
},
/**
* Hightlight the current matched chunk of text.
*/
highlight : function()
{
// Do not apply if nothing is found.
if ( this._.cursors.length < 1 )
return;
// Remove the previous highlight if there's one.
if ( this._.highlightRange )
this.removeHighlight();
// Apply the highlight.
var range = this.toDomRange(),
bookmark = range.createBookmark();
highlightStyle.applyToRange( range );
range.moveToBookmark( bookmark );
this._.highlightRange = range;
// Scroll the editor to the highlighted area.
var element = range.startContainer;
if ( element.type != CKEDITOR.NODE_ELEMENT )
element = element.getParent();
element.scrollIntoView();
// Update the character cursors.
this.updateFromDomRange( range );
},
/**
* Remove highlighted find result.
*/
removeHighlight : function()
{
if ( !this._.highlightRange )
return;
var bookmark = this._.highlightRange.createBookmark();
highlightStyle.removeFromRange( this._.highlightRange );
this._.highlightRange.moveToBookmark( bookmark );
this.updateFromDomRange( this._.highlightRange );
this._.highlightRange = null;
},
isReadOnly : function()
{
if ( !this._.highlightRange )
return 0;
return this._.highlightRange.startContainer.isReadOnly();
},
moveBack : function()
{
var retval = this._.walker.back(),
cursors = this._.cursors;
if ( retval.hitMatchBoundary )
this._.cursors = cursors = [];
cursors.unshift( retval );
if ( cursors.length > this._.rangeLength )
cursors.pop();
return retval;
},
moveNext : function()
{
var retval = this._.walker.next(),
cursors = this._.cursors;
// Clear the cursors queue if we've crossed a match boundary.
if ( retval.hitMatchBoundary )
this._.cursors = cursors = [];
cursors.push( retval );
if ( cursors.length > this._.rangeLength )
cursors.shift();
return retval;
},
getEndCharacter : function()
{
var cursors = this._.cursors;
if ( cursors.length < 1 )
return null;
return cursors[ cursors.length - 1 ].character;
},
getNextCharacterRange : function( maxLength )
{
var lastCursor,
nextRangeWalker,
cursors = this._.cursors;
if ( ( lastCursor = cursors[ cursors.length - 1 ] ) && lastCursor.textNode )
nextRangeWalker = new characterWalker( getRangeAfterCursor( lastCursor ) );
// In case it's an empty range (no cursors), figure out next range from walker (#4951).
else
nextRangeWalker = this._.walker;
return new characterRange( nextRangeWalker, maxLength );
},
getCursors : function()
{
return this._.cursors;
}
};
// The remaining document range after the character cursor.
function getRangeAfterCursor( cursor , inclusive )
{
var range = new CKEDITOR.dom.range();
range.setStart( cursor.textNode,
( inclusive ? cursor.offset : cursor.offset + 1 ) );
range.setEndAt( editor.document.getBody(),
CKEDITOR.POSITION_BEFORE_END );
return range;
}
// The document range before the character cursor.
function getRangeBeforeCursor( cursor )
{
var range = new CKEDITOR.dom.range();
range.setStartAt( editor.document.getBody(),
CKEDITOR.POSITION_AFTER_START );
range.setEnd( cursor.textNode, cursor.offset );
return range;
}
var KMP_NOMATCH = 0,
KMP_ADVANCED = 1,
KMP_MATCHED = 2;
/**
* Examination the occurrence of a word which implement KMP algorithm.
*/
var kmpMatcher = function( pattern, ignoreCase )
{
var overlap = [ -1 ];
if ( ignoreCase )
pattern = pattern.toLowerCase();
for ( var i = 0 ; i < pattern.length ; i++ )
{
overlap.push( overlap[i] + 1 );
while ( overlap[ i + 1 ] > 0
&& pattern.charAt( i ) != pattern
.charAt( overlap[ i + 1 ] - 1 ) )
overlap[ i + 1 ] = overlap[ overlap[ i + 1 ] - 1 ] + 1;
}
this._ = {
overlap : overlap,
state : 0,
ignoreCase : !!ignoreCase,
pattern : pattern
};
};
kmpMatcher.prototype =
{
feedCharacter : function( c )
{
if ( this._.ignoreCase )
c = c.toLowerCase();
while ( true )
{
if ( c == this._.pattern.charAt( this._.state ) )
{
this._.state++;
if ( this._.state == this._.pattern.length )
{
this._.state = 0;
return KMP_MATCHED;
}
return KMP_ADVANCED;
}
else if ( !this._.state )
return KMP_NOMATCH;
else
this._.state = this._.overlap[ this._.state ];
}
return null;
},
reset : function()
{
this._.state = 0;
}
};
var wordSeparatorRegex =
/[.,"'?!;: \u0085\u00a0\u1680\u280e\u2028\u2029\u202f\u205f\u3000]/;
var isWordSeparator = function( c )
{
if ( !c )
return true;
var code = c.charCodeAt( 0 );
return ( code >= 9 && code <= 0xd )
|| ( code >= 0x2000 && code <= 0x200a )
|| wordSeparatorRegex.test( c );
};
var finder = {
searchRange : null,
matchRange : null,
find : function( pattern, matchCase, matchWord, matchCyclic, highlightMatched, cyclicRerun )
{
if ( !this.matchRange )
this.matchRange =
new characterRange(
new characterWalker( this.searchRange ),
pattern.length );
else
{
this.matchRange.removeHighlight();
this.matchRange = this.matchRange.getNextCharacterRange( pattern.length );
}
var matcher = new kmpMatcher( pattern, !matchCase ),
matchState = KMP_NOMATCH,
character = '%';
while ( character !== null )
{
this.matchRange.moveNext();
while ( ( character = this.matchRange.getEndCharacter() ) )
{
matchState = matcher.feedCharacter( character );
if ( matchState == KMP_MATCHED )
break;
if ( this.matchRange.moveNext().hitMatchBoundary )
matcher.reset();
}
if ( matchState == KMP_MATCHED )
{
if ( matchWord )
{
var cursors = this.matchRange.getCursors(),
tail = cursors[ cursors.length - 1 ],
head = cursors[ 0 ];
var headWalker = new characterWalker( getRangeBeforeCursor( head ), true ),
tailWalker = new characterWalker( getRangeAfterCursor( tail ), true );
if ( ! ( isWordSeparator( headWalker.back().character )
&& isWordSeparator( tailWalker.next().character ) ) )
continue;
}
this.matchRange.setMatched();
if ( highlightMatched !== false )
this.matchRange.highlight();
return true;
}
}
this.matchRange.clearMatched();
this.matchRange.removeHighlight();
// Clear current session and restart with the default search
// range.
// Re-run the finding once for cyclic.(#3517)
if ( matchCyclic && !cyclicRerun )
{
this.searchRange = getSearchRange( 1 );
this.matchRange = null;
return arguments.callee.apply( this,
Array.prototype.slice.call( arguments ).concat( [ true ] ) );
}
return false;
},
/**
* Record how much replacement occurred toward one replacing.
*/
replaceCounter : 0,
replace : function( dialog, pattern, newString, matchCase, matchWord,
matchCyclic , isReplaceAll )
{
isReplace = 1;
// Successiveness of current replace/find.
var result = 0;
// 1. Perform the replace when there's already a match here.
// 2. Otherwise perform the find but don't replace it immediately.
if ( this.matchRange && this.matchRange.isMatched()
&& !this.matchRange._.isReplaced && !this.matchRange.isReadOnly() )
{
// Turn off highlight for a while when saving snapshots.
this.matchRange.removeHighlight();
var domRange = this.matchRange.toDomRange();
var text = editor.document.createText( newString );
if ( !isReplaceAll )
{
// Save undo snaps before and after the replacement.
var selection = editor.getSelection();
selection.selectRanges( [ domRange ] );
editor.fire( 'saveSnapshot' );
}
domRange.deleteContents();
domRange.insertNode( text );
if ( !isReplaceAll )
{
selection.selectRanges( [ domRange ] );
editor.fire( 'saveSnapshot' );
}
this.matchRange.updateFromDomRange( domRange );
if ( !isReplaceAll )
this.matchRange.highlight();
this.matchRange._.isReplaced = true;
this.replaceCounter++;
result = 1;
}
else
result = this.find( pattern, matchCase, matchWord, matchCyclic, !isReplaceAll );
isReplace = 0;
return result;
}
};
/**
* The range in which find/replace happened, receive from user
* selection prior.
*/
function getSearchRange( isDefault )
{
var searchRange,
sel = editor.getSelection(),
body = editor.document.getBody();
if ( sel && !isDefault )
{
searchRange = sel.getRanges()[ 0 ].clone();
searchRange.collapse( true );
}
else
{
searchRange = new CKEDITOR.dom.range();
searchRange.setStartAt( body, CKEDITOR.POSITION_AFTER_START );
}
searchRange.setEndAt( body, CKEDITOR.POSITION_BEFORE_END );
return searchRange;
}
var lang = editor.lang.findAndReplace;
return {
title : lang.title,
resizable : CKEDITOR.DIALOG_RESIZE_NONE,
minWidth : 350,
minHeight : 165,
buttons : [ CKEDITOR.dialog.cancelButton ], // Cancel button only.
contents : [
{
id : 'find',
label : lang.find,
title : lang.find,
accessKey : '',
elements : [
{
type : 'hbox',
widths : [ '230px', '90px' ],
children :
[
{
type : 'text',
id : 'txtFindFind',
label : lang.findWhat,
isChanged : false,
labelLayout : 'horizontal',
accessKey : 'F'
},
{
type : 'button',
align : 'left',
style : 'width:100%',
label : lang.find,
onClick : function()
{
var dialog = this.getDialog();
if ( !finder.find( dialog.getValueOf( 'find', 'txtFindFind' ),
dialog.getValueOf( 'find', 'txtFindCaseChk' ),
dialog.getValueOf( 'find', 'txtFindWordChk' ),
dialog.getValueOf( 'find', 'txtFindCyclic' ) ) )
alert( lang
.notFoundMsg );
}
}
]
},
{
type : 'vbox',
padding : 0,
children :
[
{
type : 'checkbox',
id : 'txtFindCaseChk',
isChanged : false,
style : 'margin-top:28px',
label : lang.matchCase
},
{
type : 'checkbox',
id : 'txtFindWordChk',
isChanged : false,
label : lang.matchWord
},
{
type : 'checkbox',
id : 'txtFindCyclic',
isChanged : false,
'default' : true,
label : lang.matchCyclic
}
]
}
]
},
{
id : 'replace',
label : lang.replace,
accessKey : 'M',
elements : [
{
type : 'hbox',
widths : [ '230px', '90px' ],
children :
[
{
type : 'text',
id : 'txtFindReplace',
label : lang.findWhat,
isChanged : false,
labelLayout : 'horizontal',
accessKey : 'F'
},
{
type : 'button',
align : 'left',
style : 'width:100%',
label : lang.replace,
onClick : function()
{
var dialog = this.getDialog();
if ( !finder.replace( dialog,
dialog.getValueOf( 'replace', 'txtFindReplace' ),
dialog.getValueOf( 'replace', 'txtReplace' ),
dialog.getValueOf( 'replace', 'txtReplaceCaseChk' ),
dialog.getValueOf( 'replace', 'txtReplaceWordChk' ),
dialog.getValueOf( 'replace', 'txtReplaceCyclic' ) ) )
alert( lang
.notFoundMsg );
}
}
]
},
{
type : 'hbox',
widths : [ '230px', '90px' ],
children :
[
{
type : 'text',
id : 'txtReplace',
label : lang.replaceWith,
isChanged : false,
labelLayout : 'horizontal',
accessKey : 'R'
},
{
type : 'button',
align : 'left',
style : 'width:100%',
label : lang.replaceAll,
isChanged : false,
onClick : function()
{
var dialog = this.getDialog();
var replaceNums;
finder.replaceCounter = 0;
// Scope to full document.
finder.searchRange = getSearchRange( 1 );
if ( finder.matchRange )
{
finder.matchRange.removeHighlight();
finder.matchRange = null;
}
editor.fire( 'saveSnapshot' );
while ( finder.replace( dialog,
dialog.getValueOf( 'replace', 'txtFindReplace' ),
dialog.getValueOf( 'replace', 'txtReplace' ),
dialog.getValueOf( 'replace', 'txtReplaceCaseChk' ),
dialog.getValueOf( 'replace', 'txtReplaceWordChk' ),
false, true ) )
{ /*jsl:pass*/ }
if ( finder.replaceCounter )
{
alert( lang.replaceSuccessMsg.replace( /%1/, finder.replaceCounter ) );
editor.fire( 'saveSnapshot' );
}
else
alert( lang.notFoundMsg );
}
}
]
},
{
type : 'vbox',
padding : 0,
children :
[
{
type : 'checkbox',
id : 'txtReplaceCaseChk',
isChanged : false,
label : lang
.matchCase
},
{
type : 'checkbox',
id : 'txtReplaceWordChk',
isChanged : false,
label : lang
.matchWord
},
{
type : 'checkbox',
id : 'txtReplaceCyclic',
isChanged : false,
'default' : true,
label : lang
.matchCyclic
}
]
}
]
}
],
onLoad : function()
{
var dialog = this;
// Keep track of the current pattern field in use.
var patternField, wholeWordChkField;
// Ignore initial page select on dialog show
var isUserSelect = 0;
this.on( 'hide', function()
{
isUserSelect = 0;
});
this.on( 'show', function()
{
isUserSelect = 1;
});
this.selectPage = CKEDITOR.tools.override( this.selectPage, function( originalFunc )
{
return function( pageId )
{
originalFunc.call( dialog, pageId );
var currPage = dialog._.tabs[ pageId ];
var patternFieldInput, patternFieldId, wholeWordChkFieldId;
patternFieldId = pageId === 'find' ? 'txtFindFind' : 'txtFindReplace';
wholeWordChkFieldId = pageId === 'find' ? 'txtFindWordChk' : 'txtReplaceWordChk';
patternField = dialog.getContentElement( pageId,
patternFieldId );
wholeWordChkField = dialog.getContentElement( pageId,
wholeWordChkFieldId );
// Prepare for check pattern text filed 'keyup' event
if ( !currPage.initialized )
{
patternFieldInput = CKEDITOR.document
.getById( patternField._.inputId );
currPage.initialized = true;
}
// Synchronize fields on tab switch.
if ( isUserSelect )
syncFieldsBetweenTabs.call( this, pageId );
};
} );
},
onShow : function()
{
// Establish initial searching start position.
finder.searchRange = getSearchRange();
this.selectPage( startupPage );
},
onHide : function()
{
var range;
if ( finder.matchRange && finder.matchRange.isMatched() )
{
finder.matchRange.removeHighlight();
editor.focus();
range = finder.matchRange.toDomRange();
if ( range )
editor.getSelection().selectRanges( [ range ] );
}
// Clear current session before dialog close
delete finder.matchRange;
},
onFocus : function()
{
if ( startupPage == 'replace' )
return this.getContentElement( 'replace', 'txtFindReplace' );
else
return this.getContentElement( 'find', 'txtFindFind' );
}
};
};
CKEDITOR.dialog.add( 'find', function( editor )
{
return findDialog( editor, 'find' );
});
CKEDITOR.dialog.add( 'replace', function( editor )
{
return findDialog( editor, 'replace' );
});
})();

View File

@@ -0,0 +1,46 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.add( 'find',
{
init : function( editor )
{
var forms = CKEDITOR.plugins.find;
editor.ui.addButton( 'Find',
{
label : editor.lang.findAndReplace.find,
command : 'find'
});
var findCommand = editor.addCommand( 'find', new CKEDITOR.dialogCommand( 'find' ) );
findCommand.canUndo = false;
editor.ui.addButton( 'Replace',
{
label : editor.lang.findAndReplace.replace,
command : 'replace'
});
var replaceCommand = editor.addCommand( 'replace', new CKEDITOR.dialogCommand( 'replace' ) );
replaceCommand.canUndo = false;
CKEDITOR.dialog.add( 'find', this.path + 'dialogs/find.js' );
CKEDITOR.dialog.add( 'replace', this.path + 'dialogs/find.js' );
},
requires : [ 'styles' ]
} );
/**
* Defines the style to be used to highlight results with the find dialog.
* @type Object
* @default { element : 'span', styles : { 'background-color' : '#004', 'color' : '#fff' } }
* @example
* // Highlight search results with blue on yellow.
* config.find_highlight =
* {
* element : 'span',
* styles : { 'background-color' : '#ff0', 'color' : '#00f' }
* };
*/
CKEDITOR.config.find_highlight = { element : 'span', styles : { 'background-color' : '#004', 'color' : '#fff' } };

View File

@@ -0,0 +1,698 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
(function()
{
/*
* It is possible to set things in three different places.
* 1. As attributes in the object tag.
* 2. As param tags under the object tag.
* 3. As attributes in the embed tag.
* It is possible for a single attribute to be present in more than one place.
* So let's define a mapping between a sementic attribute and its syntactic
* equivalents.
* Then we'll set and retrieve attribute values according to the mapping,
* instead of having to check and set each syntactic attribute every time.
*
* Reference: http://kb.adobe.com/selfservice/viewContent.do?externalId=tn_12701
*/
var ATTRTYPE_OBJECT = 1,
ATTRTYPE_PARAM = 2,
ATTRTYPE_EMBED = 4;
var attributesMap =
{
id : [ { type : ATTRTYPE_OBJECT, name : 'id' } ],
classid : [ { type : ATTRTYPE_OBJECT, name : 'classid' } ],
codebase : [ { type : ATTRTYPE_OBJECT, name : 'codebase'} ],
pluginspage : [ { type : ATTRTYPE_EMBED, name : 'pluginspage' } ],
src : [ { type : ATTRTYPE_PARAM, name : 'movie' }, { type : ATTRTYPE_EMBED, name : 'src' } ],
name : [ { type : ATTRTYPE_EMBED, name : 'name' } ],
align : [ { type : ATTRTYPE_OBJECT, name : 'align' } ],
title : [ { type : ATTRTYPE_OBJECT, name : 'title' }, { type : ATTRTYPE_EMBED, name : 'title' } ],
'class' : [ { type : ATTRTYPE_OBJECT, name : 'class' }, { type : ATTRTYPE_EMBED, name : 'class'} ],
width : [ { type : ATTRTYPE_OBJECT, name : 'width' }, { type : ATTRTYPE_EMBED, name : 'width' } ],
height : [ { type : ATTRTYPE_OBJECT, name : 'height' }, { type : ATTRTYPE_EMBED, name : 'height' } ],
hSpace : [ { type : ATTRTYPE_OBJECT, name : 'hSpace' }, { type : ATTRTYPE_EMBED, name : 'hSpace' } ],
vSpace : [ { type : ATTRTYPE_OBJECT, name : 'vSpace' }, { type : ATTRTYPE_EMBED, name : 'vSpace' } ],
style : [ { type : ATTRTYPE_OBJECT, name : 'style' }, { type : ATTRTYPE_EMBED, name : 'style' } ],
type : [ { type : ATTRTYPE_EMBED, name : 'type' } ]
};
var names = [ 'play', 'loop', 'menu', 'quality', 'scale', 'salign', 'wmode', 'bgcolor', 'base', 'flashvars', 'allowScriptAccess',
'allowFullScreen' ];
for ( var i = 0 ; i < names.length ; i++ )
attributesMap[ names[i] ] = [ { type : ATTRTYPE_EMBED, name : names[i] }, { type : ATTRTYPE_PARAM, name : names[i] } ];
names = [ 'allowFullScreen', 'play', 'loop', 'menu' ];
for ( i = 0 ; i < names.length ; i++ )
attributesMap[ names[i] ][0]['default'] = attributesMap[ names[i] ][1]['default'] = true;
function loadValue( objectNode, embedNode, paramMap )
{
var attributes = attributesMap[ this.id ];
if ( !attributes )
return;
var isCheckbox = ( this instanceof CKEDITOR.ui.dialog.checkbox );
for ( var i = 0 ; i < attributes.length ; i++ )
{
var attrDef = attributes[ i ];
switch ( attrDef.type )
{
case ATTRTYPE_OBJECT:
if ( !objectNode )
continue;
if ( objectNode.getAttribute( attrDef.name ) !== null )
{
var value = objectNode.getAttribute( attrDef.name );
if ( isCheckbox )
this.setValue( value.toLowerCase() == 'true' );
else
this.setValue( value );
return;
}
else if ( isCheckbox )
this.setValue( !!attrDef[ 'default' ] );
break;
case ATTRTYPE_PARAM:
if ( !objectNode )
continue;
if ( attrDef.name in paramMap )
{
value = paramMap[ attrDef.name ];
if ( isCheckbox )
this.setValue( value.toLowerCase() == 'true' );
else
this.setValue( value );
return;
}
else if ( isCheckbox )
this.setValue( !!attrDef[ 'default' ] );
break;
case ATTRTYPE_EMBED:
if ( !embedNode )
continue;
if ( embedNode.getAttribute( attrDef.name ) )
{
value = embedNode.getAttribute( attrDef.name );
if ( isCheckbox )
this.setValue( value.toLowerCase() == 'true' );
else
this.setValue( value );
return;
}
else if ( isCheckbox )
this.setValue( !!attrDef[ 'default' ] );
}
}
}
function commitValue( objectNode, embedNode, paramMap )
{
var attributes = attributesMap[ this.id ];
if ( !attributes )
return;
var isRemove = ( this.getValue() === '' ),
isCheckbox = ( this instanceof CKEDITOR.ui.dialog.checkbox );
for ( var i = 0 ; i < attributes.length ; i++ )
{
var attrDef = attributes[i];
switch ( attrDef.type )
{
case ATTRTYPE_OBJECT:
if ( !objectNode )
continue;
var value = this.getValue();
if ( isRemove || isCheckbox && value === attrDef[ 'default' ] )
objectNode.removeAttribute( attrDef.name );
else
objectNode.setAttribute( attrDef.name, value );
break;
case ATTRTYPE_PARAM:
if ( !objectNode )
continue;
value = this.getValue();
if ( isRemove || isCheckbox && value === attrDef[ 'default' ] )
{
if ( attrDef.name in paramMap )
paramMap[ attrDef.name ].remove();
}
else
{
if ( attrDef.name in paramMap )
paramMap[ attrDef.name ].setAttribute( 'value', value );
else
{
var param = CKEDITOR.dom.element.createFromHtml( '<cke:param></cke:param>', objectNode.getDocument() );
param.setAttributes( { name : attrDef.name, value : value } );
if ( objectNode.getChildCount() < 1 )
param.appendTo( objectNode );
else
param.insertBefore( objectNode.getFirst() );
}
}
break;
case ATTRTYPE_EMBED:
if ( !embedNode )
continue;
value = this.getValue();
if ( isRemove || isCheckbox && value === attrDef[ 'default' ])
embedNode.removeAttribute( attrDef.name );
else
embedNode.setAttribute( attrDef.name, value );
}
}
}
CKEDITOR.dialog.add( 'flash', function( editor )
{
var makeObjectTag = !editor.config.flashEmbedTagOnly,
makeEmbedTag = editor.config.flashAddEmbedTag || editor.config.flashEmbedTagOnly;
var previewPreloader,
previewAreaHtml = '<div>' + CKEDITOR.tools.htmlEncode( editor.lang.common.preview ) +'<br>' +
'<div id="cke_FlashPreviewLoader' + CKEDITOR.tools.getNextNumber() + '" style="display:none"><div class="loading">&nbsp;</div></div>' +
'<div id="cke_FlashPreviewBox' + CKEDITOR.tools.getNextNumber() + '" class="FlashPreviewBox"></div></div>';
return {
title : editor.lang.flash.title,
minWidth : 420,
minHeight : 310,
onShow : function()
{
// Clear previously saved elements.
this.fakeImage = this.objectNode = this.embedNode = null;
previewPreloader = new CKEDITOR.dom.element( 'embed', editor.document );
// Try to detect any embed or object tag that has Flash parameters.
var fakeImage = this.getSelectedElement();
if ( fakeImage && fakeImage.data( 'cke-real-element-type' ) && fakeImage.data( 'cke-real-element-type' ) == 'flash' )
{
this.fakeImage = fakeImage;
var realElement = editor.restoreRealElement( fakeImage ),
objectNode = null, embedNode = null, paramMap = {};
if ( realElement.getName() == 'cke:object' )
{
objectNode = realElement;
var embedList = objectNode.getElementsByTag( 'embed', 'cke' );
if ( embedList.count() > 0 )
embedNode = embedList.getItem( 0 );
var paramList = objectNode.getElementsByTag( 'param', 'cke' );
for ( var i = 0, length = paramList.count() ; i < length ; i++ )
{
var item = paramList.getItem( i ),
name = item.getAttribute( 'name' ),
value = item.getAttribute( 'value' );
paramMap[ name ] = value;
}
}
else if ( realElement.getName() == 'cke:embed' )
embedNode = realElement;
this.objectNode = objectNode;
this.embedNode = embedNode;
this.setupContent( objectNode, embedNode, paramMap, fakeImage );
}
},
onOk : function()
{
// If there's no selected object or embed, create one. Otherwise, reuse the
// selected object and embed nodes.
var objectNode = null,
embedNode = null,
paramMap = null;
if ( !this.fakeImage )
{
if ( makeObjectTag )
{
objectNode = CKEDITOR.dom.element.createFromHtml( '<cke:object></cke:object>', editor.document );
var attributes = {
classid : 'clsid:d27cdb6e-ae6d-11cf-96b8-444553540000',
codebase : 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0'
};
objectNode.setAttributes( attributes );
}
if ( makeEmbedTag )
{
embedNode = CKEDITOR.dom.element.createFromHtml( '<cke:embed></cke:embed>', editor.document );
embedNode.setAttributes(
{
type : 'application/x-shockwave-flash',
pluginspage : 'http://www.macromedia.com/go/getflashplayer'
} );
if ( objectNode )
embedNode.appendTo( objectNode );
}
}
else
{
objectNode = this.objectNode;
embedNode = this.embedNode;
}
// Produce the paramMap if there's an object tag.
if ( objectNode )
{
paramMap = {};
var paramList = objectNode.getElementsByTag( 'param', 'cke' );
for ( var i = 0, length = paramList.count() ; i < length ; i++ )
paramMap[ paramList.getItem( i ).getAttribute( 'name' ) ] = paramList.getItem( i );
}
// A subset of the specified attributes/styles
// should also be applied on the fake element to
// have better visual effect. (#5240)
var extraStyles = {}, extraAttributes = {};
this.commitContent( objectNode, embedNode, paramMap, extraStyles, extraAttributes );
// Refresh the fake image.
var newFakeImage = editor.createFakeElement( objectNode || embedNode, 'cke_flash', 'flash', true );
newFakeImage.setAttributes( extraAttributes );
newFakeImage.setStyles( extraStyles );
if ( this.fakeImage )
{
newFakeImage.replace( this.fakeImage );
editor.getSelection().selectElement( newFakeImage );
}
else
editor.insertElement( newFakeImage );
},
onHide : function()
{
if ( this.preview )
this.preview.setHtml('');
},
contents : [
{
id : 'info',
label : editor.lang.common.generalTab,
accessKey : 'I',
elements :
[
{
type : 'vbox',
padding : 0,
children :
[
{
type : 'hbox',
widths : [ '280px', '110px' ],
align : 'right',
children :
[
{
id : 'src',
type : 'text',
label : editor.lang.common.url,
required : true,
validate : CKEDITOR.dialog.validate.notEmpty( editor.lang.flash.validateSrc ),
setup : loadValue,
commit : commitValue,
onLoad : function()
{
var dialog = this.getDialog(),
updatePreview = function( src ){
// Query the preloader to figure out the url impacted by based href.
previewPreloader.setAttribute( 'src', src );
dialog.preview.setHtml( '<embed height="100%" width="100%" src="'
+ CKEDITOR.tools.htmlEncode( previewPreloader.getAttribute( 'src' ) )
+ '" type="application/x-shockwave-flash"></embed>' );
};
// Preview element
dialog.preview = dialog.getContentElement( 'info', 'preview' ).getElement().getChild( 3 );
// Sync on inital value loaded.
this.on( 'change', function( evt ){
if ( evt.data && evt.data.value )
updatePreview( evt.data.value );
} );
// Sync when input value changed.
this.getInputElement().on( 'change', function( evt ){
updatePreview( this.getValue() );
}, this );
}
},
{
type : 'button',
id : 'browse',
filebrowser : 'info:src',
hidden : true,
// v-align with the 'src' field.
// TODO: We need something better than a fixed size here.
style : 'display:inline-block;margin-top:10px;',
label : editor.lang.common.browseServer
}
]
}
]
},
{
type : 'hbox',
widths : [ '25%', '25%', '25%', '25%', '25%' ],
children :
[
{
type : 'text',
id : 'width',
style : 'width:95px',
label : editor.lang.common.width,
validate : CKEDITOR.dialog.validate.integer( editor.lang.common.invalidWidth ),
setup : function( objectNode, embedNode, paramMap, fakeImage )
{
loadValue.apply( this, arguments );
if ( fakeImage )
{
var fakeImageWidth = parseInt( fakeImage.$.style.width, 10 );
if ( !isNaN( fakeImageWidth ) )
this.setValue( fakeImageWidth );
}
},
commit : function( objectNode, embedNode, paramMap, extraStyles )
{
commitValue.apply( this, arguments );
if ( this.getValue() )
extraStyles.width = this.getValue() + 'px';
}
},
{
type : 'text',
id : 'height',
style : 'width:95px',
label : editor.lang.common.height,
validate : CKEDITOR.dialog.validate.integer( editor.lang.common.invalidHeight ),
setup : function( objectNode, embedNode, paramMap, fakeImage )
{
loadValue.apply( this, arguments );
if ( fakeImage )
{
var fakeImageHeight = parseInt( fakeImage.$.style.height, 10 );
if ( !isNaN( fakeImageHeight ) )
this.setValue( fakeImageHeight );
}
},
commit : function( objectNode, embedNode, paramMap, extraStyles )
{
commitValue.apply( this, arguments );
if ( this.getValue() )
extraStyles.height = this.getValue() + 'px';
}
},
{
type : 'text',
id : 'hSpace',
style : 'width:95px',
label : editor.lang.flash.hSpace,
validate : CKEDITOR.dialog.validate.integer( editor.lang.flash.validateHSpace ),
setup : loadValue,
commit : commitValue
},
{
type : 'text',
id : 'vSpace',
style : 'width:95px',
label : editor.lang.flash.vSpace,
validate : CKEDITOR.dialog.validate.integer( editor.lang.flash.validateVSpace ),
setup : loadValue,
commit : commitValue
}
]
},
{
type : 'vbox',
children :
[
{
type : 'html',
id : 'preview',
style : 'width:95%;',
html : previewAreaHtml
}
]
}
]
},
{
id : 'Upload',
hidden : true,
filebrowser : 'uploadButton',
label : editor.lang.common.upload,
elements :
[
{
type : 'file',
id : 'upload',
label : editor.lang.common.upload,
size : 38
},
{
type : 'fileButton',
id : 'uploadButton',
label : editor.lang.common.uploadSubmit,
filebrowser : 'info:src',
'for' : [ 'Upload', 'upload' ]
}
]
},
{
id : 'properties',
label : editor.lang.flash.propertiesTab,
elements :
[
{
type : 'hbox',
widths : [ '50%', '50%' ],
children :
[
{
id : 'scale',
type : 'select',
label : editor.lang.flash.scale,
'default' : '',
style : 'width : 100%;',
items :
[
[ editor.lang.common.notSet , ''],
[ editor.lang.flash.scaleAll, 'showall' ],
[ editor.lang.flash.scaleNoBorder, 'noborder' ],
[ editor.lang.flash.scaleFit, 'exactfit' ]
],
setup : loadValue,
commit : commitValue
},
{
id : 'allowScriptAccess',
type : 'select',
label : editor.lang.flash.access,
'default' : '',
style : 'width : 100%;',
items :
[
[ editor.lang.common.notSet , ''],
[ editor.lang.flash.accessAlways, 'always' ],
[ editor.lang.flash.accessSameDomain, 'samedomain' ],
[ editor.lang.flash.accessNever, 'never' ]
],
setup : loadValue,
commit : commitValue
}
]
},
{
type : 'hbox',
widths : [ '50%', '50%' ],
children :
[
{
id : 'wmode',
type : 'select',
label : editor.lang.flash.windowMode,
'default' : '',
style : 'width : 100%;',
items :
[
[ editor.lang.common.notSet , '' ],
[ editor.lang.flash.windowModeWindow, 'window' ],
[ editor.lang.flash.windowModeOpaque, 'opaque' ],
[ editor.lang.flash.windowModeTransparent, 'transparent' ]
],
setup : loadValue,
commit : commitValue
},
{
id : 'quality',
type : 'select',
label : editor.lang.flash.quality,
'default' : 'high',
style : 'width : 100%;',
items :
[
[ editor.lang.common.notSet , '' ],
[ editor.lang.flash.qualityBest, 'best' ],
[ editor.lang.flash.qualityHigh, 'high' ],
[ editor.lang.flash.qualityAutoHigh, 'autohigh' ],
[ editor.lang.flash.qualityMedium, 'medium' ],
[ editor.lang.flash.qualityAutoLow, 'autolow' ],
[ editor.lang.flash.qualityLow, 'low' ]
],
setup : loadValue,
commit : commitValue
}
]
},
{
type : 'hbox',
widths : [ '50%', '50%' ],
children :
[
{
id : 'align',
type : 'select',
label : editor.lang.common.align,
'default' : '',
style : 'width : 100%;',
items :
[
[ editor.lang.common.notSet , ''],
[ editor.lang.common.alignLeft , 'left'],
[ editor.lang.flash.alignAbsBottom , 'absBottom'],
[ editor.lang.flash.alignAbsMiddle , 'absMiddle'],
[ editor.lang.flash.alignBaseline , 'baseline'],
[ editor.lang.common.alignBottom , 'bottom'],
[ editor.lang.common.alignMiddle , 'middle'],
[ editor.lang.common.alignRight , 'right'],
[ editor.lang.flash.alignTextTop , 'textTop'],
[ editor.lang.common.alignTop , 'top']
],
setup : loadValue,
commit : function( objectNode, embedNode, paramMap, extraStyles, extraAttributes )
{
var value = this.getValue();
commitValue.apply( this, arguments );
value && ( extraAttributes.align = value );
}
},
{
type : 'html',
html : '<div></div>'
}
]
},
{
type : 'fieldset',
label : CKEDITOR.tools.htmlEncode( editor.lang.flash.flashvars ),
children :
[
{
type : 'vbox',
padding : 0,
children :
[
{
type : 'checkbox',
id : 'menu',
label : editor.lang.flash.chkMenu,
'default' : true,
setup : loadValue,
commit : commitValue
},
{
type : 'checkbox',
id : 'play',
label : editor.lang.flash.chkPlay,
'default' : true,
setup : loadValue,
commit : commitValue
},
{
type : 'checkbox',
id : 'loop',
label : editor.lang.flash.chkLoop,
'default' : true,
setup : loadValue,
commit : commitValue
},
{
type : 'checkbox',
id : 'allowFullScreen',
label : editor.lang.flash.chkFull,
'default' : true,
setup : loadValue,
commit : commitValue
}
]
}
]
}
]
},
{
id : 'advanced',
label : editor.lang.common.advancedTab,
elements :
[
{
type : 'hbox',
widths : [ '45%', '55%' ],
children :
[
{
type : 'text',
id : 'id',
label : editor.lang.common.id,
setup : loadValue,
commit : commitValue
},
{
type : 'text',
id : 'title',
label : editor.lang.common.advisoryTitle,
setup : loadValue,
commit : commitValue
}
]
},
{
type : 'hbox',
widths : [ '45%', '55%' ],
children :
[
{
type : 'text',
id : 'bgcolor',
label : editor.lang.flash.bgcolor,
setup : loadValue,
commit : commitValue
},
{
type : 'text',
id : 'class',
label : editor.lang.common.cssClass,
setup : loadValue,
commit : commitValue
}
]
},
{
type : 'text',
id : 'style',
label : editor.lang.common.cssStyle,
setup : loadValue,
commit : commitValue
}
]
}
]
};
} );
})();

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 B

View File

@@ -0,0 +1,168 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
(function()
{
var flashFilenameRegex = /\.swf(?:$|\?)/i;
var cssifyLength = CKEDITOR.tools.cssLength;
function isFlashEmbed( element )
{
var attributes = element.attributes;
return ( attributes.type == 'application/x-shockwave-flash' || flashFilenameRegex.test( attributes.src || '' ) );
}
function createFakeElement( editor, realElement )
{
var fakeElement = editor.createFakeParserElement( realElement, 'cke_flash', 'flash', true ),
fakeStyle = fakeElement.attributes.style || '';
var width = realElement.attributes.width,
height = realElement.attributes.height;
if ( typeof width != 'undefined' )
fakeStyle = fakeElement.attributes.style = fakeStyle + 'width:' + cssifyLength( width ) + ';';
if ( typeof height != 'undefined' )
fakeStyle = fakeElement.attributes.style = fakeStyle + 'height:' + cssifyLength( height ) + ';';
return fakeElement;
}
CKEDITOR.plugins.add( 'flash',
{
init : function( editor )
{
editor.addCommand( 'flash', new CKEDITOR.dialogCommand( 'flash' ) );
editor.ui.addButton( 'Flash',
{
label : editor.lang.common.flash,
command : 'flash'
});
CKEDITOR.dialog.add( 'flash', this.path + 'dialogs/flash.js' );
editor.addCss(
'img.cke_flash' +
'{' +
'background-image: url(' + CKEDITOR.getUrl( this.path + 'images/placeholder.png' ) + ');' +
'background-position: center center;' +
'background-repeat: no-repeat;' +
'border: 1px solid #a9a9a9;' +
'width: 80px;' +
'height: 80px;' +
'}'
);
// If the "menu" plugin is loaded, register the menu items.
if ( editor.addMenuItems )
{
editor.addMenuItems(
{
flash :
{
label : editor.lang.flash.properties,
command : 'flash',
group : 'flash'
}
});
}
editor.on( 'doubleclick', function( evt )
{
var element = evt.data.element;
if ( element.is( 'img' ) && element.data( 'cke-real-element-type' ) == 'flash' )
evt.data.dialog = 'flash';
});
// If the "contextmenu" plugin is loaded, register the listeners.
if ( editor.contextMenu )
{
editor.contextMenu.addListener( function( element, selection )
{
if ( element && element.is( 'img' ) && !element.isReadOnly()
&& element.data( 'cke-real-element-type' ) == 'flash' )
return { flash : CKEDITOR.TRISTATE_OFF };
});
}
},
afterInit : function( editor )
{
var dataProcessor = editor.dataProcessor,
dataFilter = dataProcessor && dataProcessor.dataFilter;
if ( dataFilter )
{
dataFilter.addRules(
{
elements :
{
'cke:object' : function( element )
{
var attributes = element.attributes,
classId = attributes.classid && String( attributes.classid ).toLowerCase();
if ( !classId )
{
// Look for the inner <embed>
for ( var i = 0 ; i < element.children.length ; i++ )
{
if ( element.children[ i ].name == 'cke:embed' )
{
if ( !isFlashEmbed( element.children[ i ] ) )
return null;
return createFakeElement( editor, element );
}
}
return null;
}
return createFakeElement( editor, element );
},
'cke:embed' : function( element )
{
if ( !isFlashEmbed( element ) )
return null;
return createFakeElement( editor, element );
}
}
},
5);
}
},
requires : [ 'fakeobjects' ]
});
})();
CKEDITOR.tools.extend( CKEDITOR.config,
{
/**
* Save as EMBED tag only. This tag is unrecommended.
* @type Boolean
* @default false
*/
flashEmbedTagOnly : false,
/**
* Add EMBED tag as alternative: &lt;object&gt&lt;embed&gt&lt;/embed&gt&lt;/object&gt
* @type Boolean
* @default false
*/
flashAddEmbedTag : true,
/**
* Use embedTagOnly and addEmbedTag values on edit.
* @type Boolean
* @default false
*/
flashConvertOnEdit : false
} );

View File

@@ -0,0 +1,405 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.add( 'floatpanel',
{
requires : [ 'panel' ]
});
(function()
{
var panels = {};
var isShowing = false;
function getPanel( editor, doc, parentElement, definition, level )
{
// Generates the panel key: docId-eleId-skinName-langDir[-uiColor][-CSSs][-level]
var key = CKEDITOR.tools.genKey( doc.getUniqueId(), parentElement.getUniqueId(), editor.skinName, editor.lang.dir,
editor.uiColor || '', definition.css || '', level || '' );
var panel = panels[ key ];
if ( !panel )
{
panel = panels[ key ] = new CKEDITOR.ui.panel( doc, definition );
panel.element = parentElement.append( CKEDITOR.dom.element.createFromHtml( panel.renderHtml( editor ), doc ) );
panel.element.setStyles(
{
display : 'none',
position : 'absolute'
});
}
return panel;
}
CKEDITOR.ui.floatPanel = CKEDITOR.tools.createClass(
{
$ : function( editor, parentElement, definition, level )
{
definition.forceIFrame = 1;
var doc = parentElement.getDocument(),
panel = getPanel( editor, doc, parentElement, definition, level || 0 ),
element = panel.element,
iframe = element.getFirst().getFirst();
this.element = element;
this._ =
{
// The panel that will be floating.
panel : panel,
parentElement : parentElement,
definition : definition,
document : doc,
iframe : iframe,
children : [],
dir : editor.lang.dir
};
editor.on( 'mode', function(){ this.hide(); }, this );
},
proto :
{
addBlock : function( name, block )
{
return this._.panel.addBlock( name, block );
},
addListBlock : function( name, multiSelect )
{
return this._.panel.addListBlock( name, multiSelect );
},
getBlock : function( name )
{
return this._.panel.getBlock( name );
},
/*
corner (LTR):
1 = top-left
2 = top-right
3 = bottom-right
4 = bottom-left
corner (RTL):
1 = top-right
2 = top-left
3 = bottom-left
4 = bottom-right
*/
showBlock : function( name, offsetParent, corner, offsetX, offsetY )
{
var panel = this._.panel,
block = panel.showBlock( name );
this.allowBlur( false );
isShowing = 1;
var element = this.element,
iframe = this._.iframe,
definition = this._.definition,
position = offsetParent.getDocumentPosition( element.getDocument() ),
rtl = this._.dir == 'rtl';
var left = position.x + ( offsetX || 0 ),
top = position.y + ( offsetY || 0 );
// Floating panels are off by (-1px, 0px) in RTL mode. (#3438)
if ( rtl && ( corner == 1 || corner == 4 ) )
left += offsetParent.$.offsetWidth;
else if ( !rtl && ( corner == 2 || corner == 3 ) )
left += offsetParent.$.offsetWidth - 1;
if ( corner == 3 || corner == 4 )
top += offsetParent.$.offsetHeight - 1;
// Memorize offsetParent by it's ID.
this._.panel._.offsetParentId = offsetParent.getId();
element.setStyles(
{
top : top + 'px',
left: 0,
display : ''
});
// Don't use display or visibility style because we need to
// calculate the rendering layout later and focus the element.
element.setOpacity( 0 );
// To allow the context menu to decrease back their width
element.getFirst().removeStyle( 'width' );
// Configure the IFrame blur event. Do that only once.
if ( !this._.blurSet )
{
// Non IE prefer the event into a window object.
var focused = CKEDITOR.env.ie ? iframe : new CKEDITOR.dom.window( iframe.$.contentWindow );
// With addEventListener compatible browsers, we must
// useCapture when registering the focus/blur events to
// guarantee they will be firing in all situations. (#3068, #3222 )
CKEDITOR.event.useCapture = true;
focused.on( 'blur', function( ev )
{
if ( !this.allowBlur() )
return;
// As we are using capture to register the listener,
// the blur event may get fired even when focusing
// inside the window itself, so we must ensure the
// target is out of it.
var target;
if ( CKEDITOR.env.ie && !this.allowBlur()
|| ( target = ev.data.getTarget() )
&& target.getName && target.getName() != 'iframe' )
return;
if ( this.visible && !this._.activeChild && !isShowing )
this.hide();
},
this );
focused.on( 'focus', function()
{
this._.focused = true;
this.hideChild();
this.allowBlur( true );
},
this );
CKEDITOR.event.useCapture = false;
this._.blurSet = 1;
}
panel.onEscape = CKEDITOR.tools.bind( function( keystroke )
{
if ( this.onEscape && this.onEscape( keystroke ) === false )
return false;
},
this );
CKEDITOR.tools.setTimeout( function()
{
if ( rtl )
left -= element.$.offsetWidth;
var panelLoad = CKEDITOR.tools.bind( function ()
{
var target = element.getFirst();
if ( block.autoSize )
{
// We must adjust first the width or IE6 could include extra lines in the height computation
var widthNode = block.element.$;
if ( CKEDITOR.env.gecko || CKEDITOR.env.opera )
widthNode = widthNode.parentNode;
if ( CKEDITOR.env.ie )
widthNode = widthNode.document.body;
var width = widthNode.scrollWidth;
// Account for extra height needed due to IE quirks box model bug:
// http://en.wikipedia.org/wiki/Internet_Explorer_box_model_bug
// (#3426)
if ( CKEDITOR.env.ie && CKEDITOR.env.quirks && width > 0 )
width += ( target.$.offsetWidth || 0 ) - ( target.$.clientWidth || 0 );
// A little extra at the end.
// If not present, IE6 might break into the next line, but also it looks better this way
width += 4 ;
target.setStyle( 'width', width + 'px' );
// IE doesn't compute the scrollWidth if a filter is applied previously
block.element.addClass( 'cke_frameLoaded' );
var height = block.element.$.scrollHeight;
// Account for extra height needed due to IE quirks box model bug:
// http://en.wikipedia.org/wiki/Internet_Explorer_box_model_bug
// (#3426)
if ( CKEDITOR.env.ie && CKEDITOR.env.quirks && height > 0 )
height += ( target.$.offsetHeight || 0 ) - ( target.$.clientHeight || 0 );
target.setStyle( 'height', height + 'px' );
// Fix IE < 8 visibility.
panel._.currentBlock.element.setStyle( 'display', 'none' ).removeStyle( 'display' );
}
else
target.removeStyle( 'height' );
var panelElement = panel.element,
panelWindow = panelElement.getWindow(),
windowScroll = panelWindow.getScrollPosition(),
viewportSize = panelWindow.getViewPaneSize(),
panelSize =
{
'height' : panelElement.$.offsetHeight,
'width' : panelElement.$.offsetWidth
};
// If the menu is horizontal off, shift it toward
// the opposite language direction.
if ( rtl ? left < 0 : left + panelSize.width > viewportSize.width + windowScroll.x )
left += ( panelSize.width * ( rtl ? 1 : -1 ) );
// Vertical off screen is simpler.
if ( top + panelSize.height > viewportSize.height + windowScroll.y )
top -= panelSize.height;
// If IE is in RTL, we have troubles with absolute
// position and horizontal scrolls. Here we have a
// series of hacks to workaround it. (#6146)
if ( CKEDITOR.env.ie )
{
var offsetParent = new CKEDITOR.dom.element( element.$.offsetParent ),
scrollParent = offsetParent;
// Quirks returns <body>, but standards returns <html>.
if ( scrollParent.getName() == 'html' )
scrollParent = scrollParent.getDocument().getBody();
if ( scrollParent.getComputedStyle( 'direction' ) == 'rtl' )
{
// For IE8, there is not much logic on this, but it works.
if ( CKEDITOR.env.ie8Compat )
left -= element.getDocument().getDocumentElement().$.scrollLeft * 2;
else
left -= ( offsetParent.$.scrollWidth - offsetParent.$.clientWidth );
}
}
// Trigger the onHide event of the previously active panel to prevent
// incorrect styles from being applied (#6170)
var innerElement = element.getFirst(),
activePanel;
if ( ( activePanel = innerElement.getCustomData( 'activePanel' ) ) )
activePanel.onHide && activePanel.onHide.call( this, 1 );
innerElement.setCustomData( 'activePanel', this );
element.setStyles(
{
top : top + 'px',
left : left + 'px'
} );
element.setOpacity( 1 );
} , this );
panel.isLoaded ? panelLoad() : panel.onLoad = panelLoad;
// Set the panel frame focus, so the blur event gets fired.
CKEDITOR.tools.setTimeout( function()
{
iframe.$.contentWindow.focus();
// We need this get fired manually because of unfired focus() function.
this.allowBlur( true );
}, 0, this);
}, CKEDITOR.env.air ? 200 : 0, this);
this.visible = 1;
if ( this.onShow )
this.onShow.call( this );
isShowing = 0;
},
hide : function()
{
if ( this.visible && ( !this.onHide || this.onHide.call( this ) !== true ) )
{
this.hideChild();
this.element.setStyle( 'display', 'none' );
this.visible = 0;
this.element.getFirst().removeCustomData( 'activePanel' );
}
},
allowBlur : function( allow ) // Prevent editor from hiding the panel. #3222.
{
var panel = this._.panel;
if ( allow != undefined )
panel.allowBlur = allow;
return panel.allowBlur;
},
showAsChild : function( panel, blockName, offsetParent, corner, offsetX, offsetY )
{
// Skip reshowing of child which is already visible.
if ( this._.activeChild == panel && panel._.panel._.offsetParentId == offsetParent.getId() )
return;
this.hideChild();
panel.onHide = CKEDITOR.tools.bind( function()
{
// Use a timeout, so we give time for this menu to get
// potentially focused.
CKEDITOR.tools.setTimeout( function()
{
if ( !this._.focused )
this.hide();
},
0, this );
},
this );
this._.activeChild = panel;
this._.focused = false;
panel.showBlock( blockName, offsetParent, corner, offsetX, offsetY );
/* #3767 IE: Second level menu may not have borders */
if ( CKEDITOR.env.ie7Compat || ( CKEDITOR.env.ie8 && CKEDITOR.env.ie6Compat ) )
{
setTimeout(function()
{
panel.element.getChild( 0 ).$.style.cssText += '';
}, 100);
}
},
hideChild : function()
{
var activeChild = this._.activeChild;
if ( activeChild )
{
delete activeChild.onHide;
delete this._.activeChild;
activeChild.hide();
}
}
}
});
CKEDITOR.on( 'instanceDestroyed', function()
{
var isLastInstance = CKEDITOR.tools.isEmpty( CKEDITOR.instances );
for ( var i in panels )
{
var panel = panels[ i ];
// Safe to destroy it since there're no more instances.(#4241)
if ( isLastInstance )
panel.destroy();
// Panel might be used by other instances, just hide them.(#4552)
else
panel.element.hide();
}
// Remove the registration.
isLastInstance && ( panels = {} );
} );
})();

View File

@@ -0,0 +1,234 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
(function()
{
function addCombo( editor, comboName, styleType, lang, entries, defaultLabel, styleDefinition )
{
var config = editor.config;
// Gets the list of fonts from the settings.
var names = entries.split( ';' ),
values = [];
// Create style objects for all fonts.
var styles = {};
for ( var i = 0 ; i < names.length ; i++ )
{
var parts = names[ i ];
if ( parts )
{
parts = parts.split( '/' );
var vars = {},
name = names[ i ] = parts[ 0 ];
vars[ styleType ] = values[ i ] = parts[ 1 ] || name;
styles[ name ] = new CKEDITOR.style( styleDefinition, vars );
styles[ name ]._.definition.name = name;
}
else
names.splice( i--, 1 );
}
editor.ui.addRichCombo( comboName,
{
label : lang.label,
title : lang.panelTitle,
className : 'cke_' + ( styleType == 'size' ? 'fontSize' : 'font' ),
panel :
{
css : editor.skin.editor.css.concat( config.contentsCss ),
multiSelect : false,
attributes : { 'aria-label' : lang.panelTitle }
},
init : function()
{
this.startGroup( lang.panelTitle );
for ( var i = 0 ; i < names.length ; i++ )
{
var name = names[ i ];
// Add the tag entry to the panel list.
this.add( name, styles[ name ].buildPreview(), name );
}
},
onClick : function( value )
{
editor.focus();
editor.fire( 'saveSnapshot' );
var style = styles[ value ];
if ( this.getValue() == value )
style.remove( editor.document );
else
style.apply( editor.document );
editor.fire( 'saveSnapshot' );
},
onRender : function()
{
editor.on( 'selectionChange', function( ev )
{
var currentValue = this.getValue();
var elementPath = ev.data.path,
elements = elementPath.elements;
// For each element into the elements path.
for ( var i = 0, element ; i < elements.length ; i++ )
{
element = elements[i];
// Check if the element is removable by any of
// the styles.
for ( var value in styles )
{
if ( styles[ value ].checkElementRemovable( element, true ) )
{
if ( value != currentValue )
this.setValue( value );
return;
}
}
}
// If no styles match, just empty it.
this.setValue( '', defaultLabel );
},
this);
}
});
}
CKEDITOR.plugins.add( 'font',
{
requires : [ 'richcombo', 'styles' ],
init : function( editor )
{
var config = editor.config;
addCombo( editor, 'Font', 'family', editor.lang.font, config.font_names, config.font_defaultLabel, config.font_style );
addCombo( editor, 'FontSize', 'size', editor.lang.fontSize, config.fontSize_sizes, config.fontSize_defaultLabel, config.fontSize_style );
}
});
})();
/**
* The list of fonts names to be displayed in the Font combo in the toolbar.
* Entries are separated by semi-colons (;), while it's possible to have more
* than one font for each entry, in the HTML way (separated by comma).
*
* A display name may be optionally defined by prefixing the entries with the
* name and the slash character. For example, "Arial/Arial, Helvetica, sans-serif"
* will be displayed as "Arial" in the list, but will be outputted as
* "Arial, Helvetica, sans-serif".
* @type String
* @example
* config.font_names =
* 'Arial/Arial, Helvetica, sans-serif;' +
* 'Times New Roman/Times New Roman, Times, serif;' +
* 'Verdana';
* @example
* config.font_names = 'Arial;Times New Roman;Verdana';
*/
CKEDITOR.config.font_names =
'Arial/Arial, Helvetica, sans-serif;' +
'Comic Sans MS/Comic Sans MS, cursive;' +
'Courier New/Courier New, Courier, monospace;' +
'Georgia/Georgia, serif;' +
'Lucida Sans Unicode/Lucida Sans Unicode, Lucida Grande, sans-serif;' +
'Tahoma/Tahoma, Geneva, sans-serif;' +
'Times New Roman/Times New Roman, Times, serif;' +
'Trebuchet MS/Trebuchet MS, Helvetica, sans-serif;' +
'Verdana/Verdana, Geneva, sans-serif';
/**
* The text to be displayed in the Font combo is none of the available values
* matches the current cursor position or text selection.
* @type String
* @example
* // If the default site font is Arial, we may making it more explicit to the end user.
* config.font_defaultLabel = 'Arial';
*/
CKEDITOR.config.font_defaultLabel = '';
/**
* The style definition to be used to apply the font in the text.
* @type Object
* @example
* // This is actually the default value for it.
* config.font_style =
* {
* element : 'span',
* styles : { 'font-family' : '#(family)' },
* overrides : [ { element : 'font', attributes : { 'face' : null } } ]
* };
*/
CKEDITOR.config.font_style =
{
element : 'span',
styles : { 'font-family' : '#(family)' },
overrides : [ { element : 'font', attributes : { 'face' : null } } ]
};
/**
* The list of fonts size to be displayed in the Font Size combo in the
* toolbar. Entries are separated by semi-colons (;).
*
* Any kind of "CSS like" size can be used, like "12px", "2.3em", "130%",
* "larger" or "x-small".
*
* A display name may be optionally defined by prefixing the entries with the
* name and the slash character. For example, "Bigger Font/14px" will be
* displayed as "Bigger Font" in the list, but will be outputted as "14px".
* @type String
* @default '8/8px;9/9px;10/10px;11/11px;12/12px;14/14px;16/16px;18/18px;20/20px;22/22px;24/24px;26/26px;28/28px;36/36px;48/48px;72/72px'
* @example
* config.fontSize_sizes = '16/16px;24/24px;48/48px;';
* @example
* config.fontSize_sizes = '12px;2.3em;130%;larger;x-small';
* @example
* config.fontSize_sizes = '12 Pixels/12px;Big/2.3em;30 Percent More/130%;Bigger/larger;Very Small/x-small';
*/
CKEDITOR.config.fontSize_sizes =
'8/8px;9/9px;10/10px;11/11px;12/12px;14/14px;16/16px;18/18px;20/20px;22/22px;24/24px;26/26px;28/28px;36/36px;48/48px;72/72px';
/**
* The text to be displayed in the Font Size combo is none of the available
* values matches the current cursor position or text selection.
* @type String
* @example
* // If the default site font size is 12px, we may making it more explicit to the end user.
* config.fontSize_defaultLabel = '12px';
*/
CKEDITOR.config.fontSize_defaultLabel = '';
/**
* The style definition to be used to apply the font size in the text.
* @type Object
* @example
* // This is actually the default value for it.
* config.fontSize_style =
* {
* element : 'span',
* styles : { 'font-size' : '#(size)' },
* overrides : [ { element : 'font', attributes : { 'size' : null } } ]
* };
*/
CKEDITOR.config.fontSize_style =
{
element : 'span',
styles : { 'font-size' : '#(size)' },
overrides : [ { element : 'font', attributes : { 'size' : null } } ]
};

View File

@@ -0,0 +1,194 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.add( 'format',
{
requires : [ 'richcombo', 'styles' ],
init : function( editor )
{
var config = editor.config,
lang = editor.lang.format;
// Gets the list of tags from the settings.
var tags = config.format_tags.split( ';' );
// Create style objects for all defined styles.
var styles = {};
for ( var i = 0 ; i < tags.length ; i++ )
{
var tag = tags[ i ];
styles[ tag ] = new CKEDITOR.style( config[ 'format_' + tag ] );
styles[ tag ]._.enterMode = editor.config.enterMode;
}
editor.ui.addRichCombo( 'Format',
{
label : lang.label,
title : lang.panelTitle,
className : 'cke_format',
panel :
{
css : editor.skin.editor.css.concat( config.contentsCss ),
multiSelect : false,
attributes : { 'aria-label' : lang.panelTitle }
},
init : function()
{
this.startGroup( lang.panelTitle );
for ( var tag in styles )
{
var label = lang[ 'tag_' + tag ];
// Add the tag entry to the panel list.
this.add( tag, '<' + tag + '>' + label + '</' + tag + '>', label );
}
},
onClick : function( value )
{
editor.focus();
editor.fire( 'saveSnapshot' );
styles[ value ].apply( editor.document );
// Save the undo snapshot after all changes are affected. (#4899)
setTimeout( function()
{
editor.fire( 'saveSnapshot' );
}, 0 );
},
onRender : function()
{
editor.on( 'selectionChange', function( ev )
{
var currentTag = this.getValue();
var elementPath = ev.data.path;
for ( var tag in styles )
{
if ( styles[ tag ].checkActive( elementPath ) )
{
if ( tag != currentTag )
this.setValue( tag, editor.lang.format[ 'tag_' + tag ] );
return;
}
}
// If no styles match, just empty it.
this.setValue( '' );
},
this);
}
});
}
});
/**
* A list of semi colon separated style names (by default tags) representing
* the style definition for each entry to be displayed in the Format combo in
* the toolbar. Each entry must have its relative definition configuration in a
* setting named "format_(tagName)". For example, the "p" entry has its
* definition taken from config.format_p.
* @type String
* @default 'p;h1;h2;h3;h4;h5;h6;pre;address;div'
* @example
* config.format_tags = 'p;h2;h3;pre'
*/
CKEDITOR.config.format_tags = 'p;h1;h2;h3;h4;h5;h6;pre;address;div';
/**
* The style definition to be used to apply the "Normal" format.
* @type Object
* @default { element : 'p' }
* @example
* config.format_p = { element : 'p', attributes : { class : 'normalPara' } };
*/
CKEDITOR.config.format_p = { element : 'p' };
/**
* The style definition to be used to apply the "Normal (DIV)" format.
* @type Object
* @default { element : 'div' }
* @example
* config.format_div = { element : 'div', attributes : { class : 'normalDiv' } };
*/
CKEDITOR.config.format_div = { element : 'div' };
/**
* The style definition to be used to apply the "Formatted" format.
* @type Object
* @default { element : 'pre' }
* @example
* config.format_pre = { element : 'pre', attributes : { class : 'code' } };
*/
CKEDITOR.config.format_pre = { element : 'pre' };
/**
* The style definition to be used to apply the "Address" format.
* @type Object
* @default { element : 'address' }
* @example
* config.format_address = { element : 'address', attributes : { class : 'styledAddress' } };
*/
CKEDITOR.config.format_address = { element : 'address' };
/**
* The style definition to be used to apply the "Heading 1" format.
* @type Object
* @default { element : 'h1' }
* @example
* config.format_h1 = { element : 'h1', attributes : { class : 'contentTitle1' } };
*/
CKEDITOR.config.format_h1 = { element : 'h1' };
/**
* The style definition to be used to apply the "Heading 1" format.
* @type Object
* @default { element : 'h2' }
* @example
* config.format_h2 = { element : 'h2', attributes : { class : 'contentTitle2' } };
*/
CKEDITOR.config.format_h2 = { element : 'h2' };
/**
* The style definition to be used to apply the "Heading 1" format.
* @type Object
* @default { element : 'h3' }
* @example
* config.format_h3 = { element : 'h3', attributes : { class : 'contentTitle3' } };
*/
CKEDITOR.config.format_h3 = { element : 'h3' };
/**
* The style definition to be used to apply the "Heading 1" format.
* @type Object
* @default { element : 'h4' }
* @example
* config.format_h4 = { element : 'h4', attributes : { class : 'contentTitle4' } };
*/
CKEDITOR.config.format_h4 = { element : 'h4' };
/**
* The style definition to be used to apply the "Heading 1" format.
* @type Object
* @default { element : 'h5' }
* @example
* config.format_h5 = { element : 'h5', attributes : { class : 'contentTitle5' } };
*/
CKEDITOR.config.format_h5 = { element : 'h5' };
/**
* The style definition to be used to apply the "Heading 1" format.
* @type Object
* @default { element : 'h6' }
* @example
* config.format_h6 = { element : 'h6', attributes : { class : 'contentTitle6' } };
*/
CKEDITOR.config.format_h6 = { element : 'h6' };

View File

@@ -0,0 +1,135 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.dialog.add( 'button', function( editor )
{
return {
title : editor.lang.button.title,
minWidth : 350,
minHeight : 150,
onShow : function()
{
delete this.button;
var element = this.getParentEditor().getSelection().getSelectedElement();
if ( element && element.is( 'input' ) )
{
var type = element.getAttribute( 'type' );
if ( type in { button:1, reset:1, submit:1 } )
{
this.button = element;
this.setupContent( element );
}
}
},
onOk : function()
{
var editor,
element = this.button,
isInsertMode = !element;
if ( isInsertMode )
{
editor = this.getParentEditor();
element = editor.document.createElement( 'input' );
}
if ( isInsertMode )
editor.insertElement( element );
this.commitContent( { element : element } );
},
contents : [
{
id : 'info',
label : editor.lang.button.title,
title : editor.lang.button.title,
elements : [
{
id : '_cke_saved_name',
type : 'text',
label : editor.lang.common.name,
'default' : '',
setup : function( element )
{
this.setValue(
element.data( 'cke-saved-name' ) ||
element.getAttribute( 'name' ) ||
'' );
},
commit : function( data )
{
var element = data.element;
if ( this.getValue() )
element.data( 'cke-saved-name', this.getValue() );
else
{
element.data( 'cke-saved-name', false );
element.removeAttribute( 'name' );
}
}
},
{
id : 'value',
type : 'text',
label : editor.lang.button.text,
accessKey : 'V',
'default' : '',
setup : function( element )
{
this.setValue( element.getAttribute( 'value' ) || '' );
},
commit : function( data )
{
var element = data.element;
if ( this.getValue() )
element.setAttribute( 'value', this.getValue() );
else
element.removeAttribute( 'value' );
}
},
{
id : 'type',
type : 'select',
label : editor.lang.button.type,
'default' : 'button',
accessKey : 'T',
items :
[
[ editor.lang.button.typeBtn, 'button' ],
[ editor.lang.button.typeSbm, 'submit' ],
[ editor.lang.button.typeRst, 'reset' ]
],
setup : function( element )
{
this.setValue( element.getAttribute( 'type' ) || '' );
},
commit : function( data )
{
var element = data.element;
if ( CKEDITOR.env.ie )
{
var elementType = element.getAttribute( 'type' );
var currentType = this.getValue();
if ( currentType != elementType )
{
var replace = CKEDITOR.dom.element.createFromHtml( '<input type="' + currentType +
'"></input>', editor.document );
element.copyAttributes( replace, { type : 1 } );
replace.replace( element );
editor.getSelection().selectElement( replace );
data.element = replace;
}
}
else
element.setAttribute( 'type', this.getValue() );
}
}
]
}
]
};
});

View File

@@ -0,0 +1,153 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.dialog.add( 'checkbox', function( editor )
{
return {
title : editor.lang.checkboxAndRadio.checkboxTitle,
minWidth : 350,
minHeight : 140,
onShow : function()
{
delete this.checkbox;
var element = this.getParentEditor().getSelection().getSelectedElement();
if ( element && element.getAttribute( 'type' ) == 'checkbox' )
{
this.checkbox = element;
this.setupContent( element );
}
},
onOk : function()
{
var editor,
element = this.checkbox,
isInsertMode = !element;
if ( isInsertMode )
{
editor = this.getParentEditor();
element = editor.document.createElement( 'input' );
element.setAttribute( 'type', 'checkbox' );
editor.insertElement( element );
}
this.commitContent( { element : element } );
},
contents : [
{
id : 'info',
label : editor.lang.checkboxAndRadio.checkboxTitle,
title : editor.lang.checkboxAndRadio.checkboxTitle,
startupFocus : 'txtName',
elements : [
{
id : 'txtName',
type : 'text',
label : editor.lang.common.name,
'default' : '',
accessKey : 'N',
setup : function( element )
{
this.setValue(
element.data( 'cke-saved-name' ) ||
element.getAttribute( 'name' ) ||
'' );
},
commit : function( data )
{
var element = data.element;
// IE failed to update 'name' property on input elements, protect it now.
if ( this.getValue() )
element.data( 'cke-saved-name', this.getValue() );
else
{
element.data( 'cke-saved-name', false );
element.removeAttribute( 'name' );
}
}
},
{
id : 'txtValue',
type : 'text',
label : editor.lang.checkboxAndRadio.value,
'default' : '',
accessKey : 'V',
setup : function( element )
{
var value = element.getAttribute( 'value' );
// IE Return 'on' as default attr value.
this.setValue( CKEDITOR.env.ie && value == 'on' ? '' : value );
},
commit : function( data )
{
var element = data.element,
value = this.getValue();
if ( value && !( CKEDITOR.env.ie && value == 'on' ) )
element.setAttribute( 'value', value );
else
{
if ( CKEDITOR.env.ie )
{
// Remove attribute 'value' of checkbox (#4721).
var checkbox = new CKEDITOR.dom.element( 'input', element.getDocument() );
element.copyAttributes( checkbox, { value: 1 } );
checkbox.replace( element );
editor.getSelection().selectElement( checkbox );
data.element = checkbox;
}
else
element.removeAttribute( 'value' );
}
}
},
{
id : 'cmbSelected',
type : 'checkbox',
label : editor.lang.checkboxAndRadio.selected,
'default' : '',
accessKey : 'S',
value : "checked",
setup : function( element )
{
this.setValue( element.getAttribute( 'checked' ) );
},
commit : function( data )
{
var element = data.element;
if ( CKEDITOR.env.ie )
{
var isElementChecked = !!element.getAttribute( 'checked' ),
isChecked = !!this.getValue();
if ( isElementChecked != isChecked )
{
var replace = CKEDITOR.dom.element.createFromHtml( '<input type="checkbox"'
+ ( isChecked ? ' checked="checked"' : '' )
+ '/>', editor.document );
element.copyAttributes( replace, { type : 1, checked : 1 } );
replace.replace( element );
editor.getSelection().selectElement( replace );
data.element = replace;
}
}
else
{
var value = this.getValue();
if ( value )
element.setAttribute( 'checked', 'checked' );
else
element.removeAttribute( 'checked' );
}
}
}
]
}
]
};
});

View File

@@ -0,0 +1,177 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.dialog.add( 'form', function( editor )
{
var autoAttributes =
{
action : 1,
id : 1,
method : 1,
enctype : 1,
target : 1
};
return {
title : editor.lang.form.title,
minWidth : 350,
minHeight : 200,
onShow : function()
{
delete this.form;
var element = this.getParentEditor().getSelection().getStartElement();
var form = element && element.getAscendant( 'form', true );
if ( form )
{
this.form = form;
this.setupContent( form );
}
},
onOk : function()
{
var editor,
element = this.form,
isInsertMode = !element;
if ( isInsertMode )
{
editor = this.getParentEditor();
element = editor.document.createElement( 'form' );
element.append( editor.document.createElement( 'br' ) );
}
if ( isInsertMode )
editor.insertElement( element );
this.commitContent( element );
},
onLoad : function()
{
function autoSetup( element )
{
this.setValue( element.getAttribute( this.id ) || '' );
}
function autoCommit( element )
{
if ( this.getValue() )
element.setAttribute( this.id, this.getValue() );
else
element.removeAttribute( this.id );
}
this.foreach( function( contentObj )
{
if ( autoAttributes[ contentObj.id ] )
{
contentObj.setup = autoSetup;
contentObj.commit = autoCommit;
}
} );
},
contents : [
{
id : 'info',
label : editor.lang.form.title,
title : editor.lang.form.title,
elements : [
{
id : 'txtName',
type : 'text',
label : editor.lang.common.name,
'default' : '',
accessKey : 'N',
setup : function( element )
{
this.setValue( element.data( 'cke-saved-name' ) ||
element.getAttribute( 'name' ) ||
'' );
},
commit : function( element )
{
if ( this.getValue() )
element.data( 'cke-saved-name', this.getValue() );
else
{
element.data( 'cke-saved-name', false );
element.removeAttribute( 'name' );
}
}
},
{
id : 'action',
type : 'text',
label : editor.lang.form.action,
'default' : '',
accessKey : 'T'
},
{
type : 'hbox',
widths : [ '45%', '55%' ],
children :
[
{
id : 'id',
type : 'text',
label : editor.lang.common.id,
'default' : '',
accessKey : 'I'
},
{
id : 'enctype',
type : 'select',
label : editor.lang.form.encoding,
style : 'width:100%',
accessKey : 'E',
'default' : '',
items :
[
[ '' ],
[ 'text/plain' ],
[ 'multipart/form-data' ],
[ 'application/x-www-form-urlencoded' ]
]
}
]
},
{
type : 'hbox',
widths : [ '45%', '55%' ],
children :
[
{
id : 'target',
type : 'select',
label : editor.lang.common.target,
style : 'width:100%',
accessKey : 'M',
'default' : '',
items :
[
[ editor.lang.common.notSet, '' ],
[ editor.lang.common.targetNew, '_blank' ],
[ editor.lang.common.targetTop, '_top' ],
[ editor.lang.common.targetSelf, '_self' ],
[ editor.lang.common.targetParent, '_parent' ]
]
},
{
id : 'method',
type : 'select',
label : editor.lang.form.method,
accessKey : 'M',
'default' : 'GET',
items :
[
[ 'GET', 'get' ],
[ 'POST', 'post' ]
]
}
]
}
]
}
]
};
});

View File

@@ -0,0 +1,98 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.dialog.add( 'hiddenfield', function( editor )
{
return {
title : editor.lang.hidden.title,
hiddenField : null,
minWidth : 350,
minHeight : 110,
onShow : function()
{
delete this.hiddenField;
var editor = this.getParentEditor(),
selection = editor.getSelection(),
element = selection.getSelectedElement();
if ( element && element.data( 'cke-real-element-type' ) && element.data( 'cke-real-element-type' ) == 'hiddenfield' )
{
this.hiddenField = element;
element = editor.restoreRealElement( this.hiddenField );
this.setupContent( element );
selection.selectElement( this.hiddenField );
}
},
onOk : function()
{
var name = this.getValueOf( 'info', '_cke_saved_name' ),
value = this.getValueOf( 'info', 'value' ),
editor = this.getParentEditor(),
element = CKEDITOR.env.ie ? editor.document.createElement( '<input name="' + CKEDITOR.tools.htmlEncode( name ) + '">' ) : editor.document.createElement( 'input' );
element.setAttribute( 'type', 'hidden' );
this.commitContent( element );
var fakeElement = editor.createFakeElement( element, 'cke_hidden', 'hiddenfield' );
if ( !this.hiddenField )
editor.insertElement( fakeElement );
else
{
fakeElement.replace( this.hiddenField );
editor.getSelection().selectElement( fakeElement );
}
return true;
},
contents : [
{
id : 'info',
label : editor.lang.hidden.title,
title : editor.lang.hidden.title,
elements : [
{
id : '_cke_saved_name',
type : 'text',
label : editor.lang.hidden.name,
'default' : '',
accessKey : 'N',
setup : function( element )
{
this.setValue(
element.data( 'cke-saved-name' ) ||
element.getAttribute( 'name' ) ||
'' );
},
commit : function( element )
{
if ( this.getValue() )
element.setAttribute( 'name', this.getValue() );
else
{
element.removeAttribute( 'name' );
}
}
},
{
id : 'value',
type : 'text',
label : editor.lang.hidden.value,
'default' : '',
accessKey : 'V',
setup : function( element )
{
this.setValue( element.getAttribute( 'value' ) || '' );
},
commit : function( element )
{
if ( this.getValue() )
element.setAttribute( 'value', this.getValue() );
else
element.removeAttribute( 'value' );
}
}
]
}
]
};
});

View File

@@ -0,0 +1,135 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.dialog.add( 'radio', function( editor )
{
return {
title : editor.lang.checkboxAndRadio.radioTitle,
minWidth : 350,
minHeight : 140,
onShow : function()
{
delete this.radioButton;
var element = this.getParentEditor().getSelection().getSelectedElement();
if ( element && element.getName() == 'input' && element.getAttribute( 'type' ) == 'radio' )
{
this.radioButton = element;
this.setupContent( element );
}
},
onOk : function()
{
var editor,
element = this.radioButton,
isInsertMode = !element;
if ( isInsertMode )
{
editor = this.getParentEditor();
element = editor.document.createElement( 'input' );
element.setAttribute( 'type', 'radio' );
}
if ( isInsertMode )
editor.insertElement( element );
this.commitContent( { element : element } );
},
contents : [
{
id : 'info',
label : editor.lang.checkboxAndRadio.radioTitle,
title : editor.lang.checkboxAndRadio.radioTitle,
elements : [
{
id : 'name',
type : 'text',
label : editor.lang.common.name,
'default' : '',
accessKey : 'N',
setup : function( element )
{
this.setValue(
element.data( 'cke-saved-name' ) ||
element.getAttribute( 'name' ) ||
'' );
},
commit : function( data )
{
var element = data.element;
if ( this.getValue() )
element.data( 'cke-saved-name', this.getValue() );
else
{
element.data( 'cke-saved-name', false );
element.removeAttribute( 'name' );
}
}
},
{
id : 'value',
type : 'text',
label : editor.lang.checkboxAndRadio.value,
'default' : '',
accessKey : 'V',
setup : function( element )
{
this.setValue( element.getAttribute( 'value' ) || '' );
},
commit : function( data )
{
var element = data.element;
if ( this.getValue() )
element.setAttribute( 'value', this.getValue() );
else
element.removeAttribute( 'value' );
}
},
{
id : 'checked',
type : 'checkbox',
label : editor.lang.checkboxAndRadio.selected,
'default' : '',
accessKey : 'S',
value : "checked",
setup : function( element )
{
this.setValue( element.getAttribute( 'checked' ) );
},
commit : function( data )
{
var element = data.element;
if ( !CKEDITOR.env.ie )
{
if ( this.getValue() )
element.setAttribute( 'checked', 'checked' );
else
element.removeAttribute( 'checked' );
}
else
{
var isElementChecked = element.getAttribute( 'checked' );
var isChecked = !!this.getValue();
if ( isElementChecked != isChecked )
{
var replace = CKEDITOR.dom.element.createFromHtml( '<input type="radio"'
+ ( isChecked ? ' checked="checked"' : '' )
+ '></input>', editor.document );
element.copyAttributes( replace, { type : 1, checked : 1 } );
replace.replace( element );
editor.getSelection().selectElement( replace );
data.element = replace;
}
}
}
}
]
}
]
};
});

View File

@@ -0,0 +1,556 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.dialog.add( 'select', function( editor )
{
// Add a new option to a SELECT object (combo or list).
function addOption( combo, optionText, optionValue, documentObject, index )
{
combo = getSelect( combo );
var oOption;
if ( documentObject )
oOption = documentObject.createElement( "OPTION" );
else
oOption = document.createElement( "OPTION" );
if ( combo && oOption && oOption.getName() == 'option' )
{
if ( CKEDITOR.env.ie ) {
if ( !isNaN( parseInt( index, 10) ) )
combo.$.options.add( oOption.$, index );
else
combo.$.options.add( oOption.$ );
oOption.$.innerHTML = optionText.length > 0 ? optionText : '';
oOption.$.value = optionValue;
}
else
{
if ( index !== null && index < combo.getChildCount() )
combo.getChild( index < 0 ? 0 : index ).insertBeforeMe( oOption );
else
combo.append( oOption );
oOption.setText( optionText.length > 0 ? optionText : '' );
oOption.setValue( optionValue );
}
}
else
return false;
return oOption;
}
// Remove all selected options from a SELECT object.
function removeSelectedOptions( combo )
{
combo = getSelect( combo );
// Save the selected index
var iSelectedIndex = getSelectedIndex( combo );
// Remove all selected options.
for ( var i = combo.getChildren().count() - 1 ; i >= 0 ; i-- )
{
if ( combo.getChild( i ).$.selected )
combo.getChild( i ).remove();
}
// Reset the selection based on the original selected index.
setSelectedIndex( combo, iSelectedIndex );
}
//Modify option from a SELECT object.
function modifyOption( combo, index, title, value )
{
combo = getSelect( combo );
if ( index < 0 )
return false;
var child = combo.getChild( index );
child.setText( title );
child.setValue( value );
return child;
}
function removeAllOptions( combo )
{
combo = getSelect( combo );
while ( combo.getChild( 0 ) && combo.getChild( 0 ).remove() )
{ /*jsl:pass*/ }
}
// Moves the selected option by a number of steps (also negative).
function changeOptionPosition( combo, steps, documentObject )
{
combo = getSelect( combo );
var iActualIndex = getSelectedIndex( combo );
if ( iActualIndex < 0 )
return false;
var iFinalIndex = iActualIndex + steps;
iFinalIndex = ( iFinalIndex < 0 ) ? 0 : iFinalIndex;
iFinalIndex = ( iFinalIndex >= combo.getChildCount() ) ? combo.getChildCount() - 1 : iFinalIndex;
if ( iActualIndex == iFinalIndex )
return false;
var oOption = combo.getChild( iActualIndex ),
sText = oOption.getText(),
sValue = oOption.getValue();
oOption.remove();
oOption = addOption( combo, sText, sValue, ( !documentObject ) ? null : documentObject, iFinalIndex );
setSelectedIndex( combo, iFinalIndex );
return oOption;
}
function getSelectedIndex( combo )
{
combo = getSelect( combo );
return combo ? combo.$.selectedIndex : -1;
}
function setSelectedIndex( combo, index )
{
combo = getSelect( combo );
if ( index < 0 )
return null;
var count = combo.getChildren().count();
combo.$.selectedIndex = ( index >= count ) ? ( count - 1 ) : index;
return combo;
}
function getOptions( combo )
{
combo = getSelect( combo );
return combo ? combo.getChildren() : false;
}
function getSelect( obj )
{
if ( obj && obj.domId && obj.getInputElement().$ ) // Dialog element.
return obj.getInputElement();
else if ( obj && obj.$ )
return obj;
return false;
}
return {
title : editor.lang.select.title,
minWidth : CKEDITOR.env.ie ? 460 : 395,
minHeight : CKEDITOR.env.ie ? 320 : 300,
onShow : function()
{
delete this.selectBox;
this.setupContent( 'clear' );
var element = this.getParentEditor().getSelection().getSelectedElement();
if ( element && element.getName() == "select" )
{
this.selectBox = element;
this.setupContent( element.getName(), element );
// Load Options into dialog.
var objOptions = getOptions( element );
for ( var i = 0 ; i < objOptions.count() ; i++ )
this.setupContent( 'option', objOptions.getItem( i ) );
}
},
onOk : function()
{
var editor = this.getParentEditor(),
element = this.selectBox,
isInsertMode = !element;
if ( isInsertMode )
element = editor.document.createElement( 'select' );
this.commitContent( element );
if ( isInsertMode )
{
editor.insertElement( element );
if ( CKEDITOR.env.ie )
{
var sel = editor.getSelection(),
bms = sel.createBookmarks();
setTimeout(function()
{
sel.selectBookmarks( bms );
}, 0 );
}
}
},
contents : [
{
id : 'info',
label : editor.lang.select.selectInfo,
title : editor.lang.select.selectInfo,
accessKey : '',
elements : [
{
id : 'txtName',
type : 'text',
widths : [ '25%','75%' ],
labelLayout : 'horizontal',
label : editor.lang.common.name,
'default' : '',
accessKey : 'N',
align : 'center',
style : 'width:350px',
setup : function( name, element )
{
if ( name == 'clear' )
this.setValue( this[ 'default' ] || '' );
else if ( name == 'select' )
{
this.setValue(
element.data( 'cke-saved-name' ) ||
element.getAttribute( 'name' ) ||
'' );
}
},
commit : function( element )
{
if ( this.getValue() )
element.data( 'cke-saved-name', this.getValue() );
else
{
element.data( 'cke-saved-name', false );
element.removeAttribute( 'name' );
}
}
},
{
id : 'txtValue',
type : 'text',
widths : [ '25%','75%' ],
labelLayout : 'horizontal',
label : editor.lang.select.value,
style : 'width:350px',
'default' : '',
className : 'cke_disabled',
onLoad : function()
{
this.getInputElement().setAttribute( 'readOnly', true );
},
setup : function( name, element )
{
if ( name == 'clear' )
this.setValue( '' );
else if ( name == 'option' && element.getAttribute( 'selected' ) )
this.setValue( element.$.value );
}
},
{
type : 'hbox',
widths : [ '175px', '170px' ],
align : 'center',
children :
[
{
id : 'txtSize',
type : 'text',
align : 'center',
labelLayout : 'horizontal',
label : editor.lang.select.size,
'default' : '',
accessKey : 'S',
style : 'width:175px',
validate: function()
{
var func = CKEDITOR.dialog.validate.integer( editor.lang.common.validateNumberFailed );
return ( ( this.getValue() === '' ) || func.apply( this ) );
},
setup : function( name, element )
{
if ( name == 'select' )
this.setValue( element.getAttribute( 'size' ) || '' );
if ( CKEDITOR.env.webkit )
this.getInputElement().setStyle( 'width', '86px' );
},
commit : function( element )
{
if ( this.getValue() )
element.setAttribute( 'size', this.getValue() );
else
element.removeAttribute( 'size' );
}
},
{
type : 'html',
html : '<span>' + CKEDITOR.tools.htmlEncode( editor.lang.select.lines ) + '</span>'
}
]
},
{
type : 'html',
html : '<span>' + CKEDITOR.tools.htmlEncode( editor.lang.select.opAvail ) + '</span>'
},
{
type : 'hbox',
widths : [ '115px', '115px' ,'100px' ],
align : 'top',
children :
[
{
type : 'vbox',
children :
[
{
id : 'txtOptName',
type : 'text',
label : editor.lang.select.opText,
style : 'width:115px',
setup : function( name, element )
{
if ( name == 'clear' )
this.setValue( "" );
}
},
{
type : 'select',
id : 'cmbName',
label : '',
title : '',
size : 5,
style : 'width:115px;height:75px',
items : [],
onChange : function()
{
var dialog = this.getDialog(),
values = dialog.getContentElement( 'info', 'cmbValue' ),
optName = dialog.getContentElement( 'info', 'txtOptName' ),
optValue = dialog.getContentElement( 'info', 'txtOptValue' ),
iIndex = getSelectedIndex( this );
setSelectedIndex( values, iIndex );
optName.setValue( this.getValue() );
optValue.setValue( values.getValue() );
},
setup : function( name, element )
{
if ( name == 'clear' )
removeAllOptions( this );
else if ( name == 'option' )
addOption( this, element.getText(), element.getText(),
this.getDialog().getParentEditor().document );
},
commit : function( element )
{
var dialog = this.getDialog(),
optionsNames = getOptions( this ),
optionsValues = getOptions( dialog.getContentElement( 'info', 'cmbValue' ) ),
selectValue = dialog.getContentElement( 'info', 'txtValue' ).getValue();
removeAllOptions( element );
for ( var i = 0 ; i < optionsNames.count() ; i++ )
{
var oOption = addOption( element, optionsNames.getItem( i ).getValue(),
optionsValues.getItem( i ).getValue(), dialog.getParentEditor().document );
if ( optionsValues.getItem( i ).getValue() == selectValue )
{
oOption.setAttribute( 'selected', 'selected' );
oOption.selected = true;
}
}
}
}
]
},
{
type : 'vbox',
children :
[
{
id : 'txtOptValue',
type : 'text',
label : editor.lang.select.opValue,
style : 'width:115px',
setup : function( name, element )
{
if ( name == 'clear' )
this.setValue( "" );
}
},
{
type : 'select',
id : 'cmbValue',
label : '',
size : 5,
style : 'width:115px;height:75px',
items : [],
onChange : function()
{
var dialog = this.getDialog(),
names = dialog.getContentElement( 'info', 'cmbName' ),
optName = dialog.getContentElement( 'info', 'txtOptName' ),
optValue = dialog.getContentElement( 'info', 'txtOptValue' ),
iIndex = getSelectedIndex( this );
setSelectedIndex( names, iIndex );
optName.setValue( names.getValue() );
optValue.setValue( this.getValue() );
},
setup : function( name, element )
{
if ( name == 'clear' )
removeAllOptions( this );
else if ( name == 'option' )
{
var oValue = element.getValue();
addOption( this, oValue, oValue,
this.getDialog().getParentEditor().document );
if ( element.getAttribute( 'selected' ) == 'selected' )
this.getDialog().getContentElement( 'info', 'txtValue' ).setValue( oValue );
}
}
}
]
},
{
type : 'vbox',
padding : 5,
children :
[
{
type : 'button',
style : '',
label : editor.lang.select.btnAdd,
title : editor.lang.select.btnAdd,
style : 'width:100%;',
onClick : function()
{
//Add new option.
var dialog = this.getDialog(),
parentEditor = dialog.getParentEditor(),
optName = dialog.getContentElement( 'info', 'txtOptName' ),
optValue = dialog.getContentElement( 'info', 'txtOptValue' ),
names = dialog.getContentElement( 'info', 'cmbName' ),
values = dialog.getContentElement( 'info', 'cmbValue' );
addOption(names, optName.getValue(), optName.getValue(), dialog.getParentEditor().document );
addOption(values, optValue.getValue(), optValue.getValue(), dialog.getParentEditor().document );
optName.setValue( "" );
optValue.setValue( "" );
}
},
{
type : 'button',
label : editor.lang.select.btnModify,
title : editor.lang.select.btnModify,
style : 'width:100%;',
onClick : function()
{
//Modify selected option.
var dialog = this.getDialog(),
optName = dialog.getContentElement( 'info', 'txtOptName' ),
optValue = dialog.getContentElement( 'info', 'txtOptValue' ),
names = dialog.getContentElement( 'info', 'cmbName' ),
values = dialog.getContentElement( 'info', 'cmbValue' ),
iIndex = getSelectedIndex( names );
if ( iIndex >= 0 )
{
modifyOption( names, iIndex, optName.getValue(), optName.getValue() );
modifyOption( values, iIndex, optValue.getValue(), optValue.getValue() );
}
}
},
{
type : 'button',
style : 'width:100%;',
label : editor.lang.select.btnUp,
title : editor.lang.select.btnUp,
onClick : function()
{
//Move up.
var dialog = this.getDialog(),
names = dialog.getContentElement( 'info', 'cmbName' ),
values = dialog.getContentElement( 'info', 'cmbValue' );
changeOptionPosition( names, -1, dialog.getParentEditor().document );
changeOptionPosition( values, -1, dialog.getParentEditor().document );
}
},
{
type : 'button',
style : 'width:100%;',
label : editor.lang.select.btnDown,
title : editor.lang.select.btnDown,
onClick : function()
{
//Move down.
var dialog = this.getDialog(),
names = dialog.getContentElement( 'info', 'cmbName' ),
values = dialog.getContentElement( 'info', 'cmbValue' );
changeOptionPosition( names, 1, dialog.getParentEditor().document );
changeOptionPosition( values, 1, dialog.getParentEditor().document );
}
}
]
}
]
},
{
type : 'hbox',
widths : [ '40%', '20%', '40%' ],
children :
[
{
type : 'button',
label : editor.lang.select.btnSetValue,
title : editor.lang.select.btnSetValue,
onClick : function()
{
//Set as default value.
var dialog = this.getDialog(),
values = dialog.getContentElement( 'info', 'cmbValue' ),
txtValue = dialog.getContentElement( 'info', 'txtValue' );
txtValue.setValue( values.getValue() );
}
},
{
type : 'button',
label : editor.lang.select.btnDelete,
title : editor.lang.select.btnDelete,
onClick : function()
{
// Delete option.
var dialog = this.getDialog(),
names = dialog.getContentElement( 'info', 'cmbName' ),
values = dialog.getContentElement( 'info', 'cmbValue' ),
optName = dialog.getContentElement( 'info', 'txtOptName' ),
optValue = dialog.getContentElement( 'info', 'txtOptValue' );
removeSelectedOptions( names );
removeSelectedOptions( values );
optName.setValue( "" );
optValue.setValue( "" );
}
},
{
id : 'chkMulti',
type : 'checkbox',
label : editor.lang.select.chkMulti,
'default' : '',
accessKey : 'M',
value : "checked",
setup : function( name, element )
{
if ( name == 'select' )
this.setValue( element.getAttribute( 'multiple' ) );
if ( CKEDITOR.env.webkit )
this.getElement().getParent().setStyle( 'vertical-align', 'middle' );
},
commit : function( element )
{
if ( this.getValue() )
element.setAttribute( 'multiple', this.getValue() );
else
element.removeAttribute( 'multiple' );
}
}
]
}
]
}
]
};
});

View File

@@ -0,0 +1,114 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.dialog.add( 'textarea', function( editor )
{
return {
title : editor.lang.textarea.title,
minWidth : 350,
minHeight : 150,
onShow : function()
{
delete this.textarea;
var element = this.getParentEditor().getSelection().getSelectedElement();
if ( element && element.getName() == "textarea" )
{
this.textarea = element;
this.setupContent( element );
}
},
onOk : function()
{
var editor,
element = this.textarea,
isInsertMode = !element;
if ( isInsertMode )
{
editor = this.getParentEditor();
element = editor.document.createElement( 'textarea' );
}
this.commitContent( element );
if ( isInsertMode )
editor.insertElement( element );
},
contents : [
{
id : 'info',
label : editor.lang.textarea.title,
title : editor.lang.textarea.title,
elements : [
{
id : '_cke_saved_name',
type : 'text',
label : editor.lang.common.name,
'default' : '',
accessKey : 'N',
setup : function( element )
{
this.setValue(
element.data( 'cke-saved-name' ) ||
element.getAttribute( 'name' ) ||
'' );
},
commit : function( element )
{
if ( this.getValue() )
element.data( 'cke-saved-name', this.getValue() );
else
{
element.data( 'cke-saved-name', false );
element.removeAttribute( 'name' );
}
}
},
{
id : 'cols',
type : 'text',
label : editor.lang.textarea.cols,
'default' : '',
accessKey : 'C',
style : 'width:50px',
validate : CKEDITOR.dialog.validate.integer( editor.lang.common.validateNumberFailed ),
setup : function( element )
{
var value = element.hasAttribute( 'cols' ) && element.getAttribute( 'cols' );
this.setValue( value || '' );
},
commit : function( element )
{
if ( this.getValue() )
element.setAttribute( 'cols', this.getValue() );
else
element.removeAttribute( 'cols' );
}
},
{
id : 'rows',
type : 'text',
label : editor.lang.textarea.rows,
'default' : '',
accessKey : 'R',
style : 'width:50px',
validate : CKEDITOR.dialog.validate.integer( editor.lang.common.validateNumberFailed ),
setup : function( element )
{
var value = element.hasAttribute( 'rows' ) && element.getAttribute( 'rows' );
this.setValue( value || '' );
},
commit : function( element )
{
if ( this.getValue() )
element.setAttribute( 'rows', this.getValue() );
else
element.removeAttribute( 'rows' );
}
}
]
}
]
};
});

View File

@@ -0,0 +1,199 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.dialog.add( 'textfield', function( editor )
{
var autoAttributes =
{
value : 1,
size : 1,
maxLength : 1
};
var acceptedTypes =
{
text : 1,
password : 1
};
return {
title : editor.lang.textfield.title,
minWidth : 350,
minHeight : 150,
onShow : function()
{
delete this.textField;
var element = this.getParentEditor().getSelection().getSelectedElement();
if ( element && element.getName() == "input" &&
( acceptedTypes[ element.getAttribute( 'type' ) ] || !element.getAttribute( 'type' ) ) )
{
this.textField = element;
this.setupContent( element );
}
},
onOk : function()
{
var editor,
element = this.textField,
isInsertMode = !element;
if ( isInsertMode )
{
editor = this.getParentEditor();
element = editor.document.createElement( 'input' );
element.setAttribute( 'type', 'text' );
}
if ( isInsertMode )
editor.insertElement( element );
this.commitContent( { element : element } );
},
onLoad : function()
{
var autoSetup = function( element )
{
var value = element.hasAttribute( this.id ) && element.getAttribute( this.id );
this.setValue( value || '' );
};
var autoCommit = function( data )
{
var element = data.element;
var value = this.getValue();
if ( value )
element.setAttribute( this.id, value );
else
element.removeAttribute( this.id );
};
this.foreach( function( contentObj )
{
if ( autoAttributes[ contentObj.id ] )
{
contentObj.setup = autoSetup;
contentObj.commit = autoCommit;
}
} );
},
contents : [
{
id : 'info',
label : editor.lang.textfield.title,
title : editor.lang.textfield.title,
elements : [
{
type : 'hbox',
widths : [ '50%', '50%' ],
children :
[
{
id : '_cke_saved_name',
type : 'text',
label : editor.lang.textfield.name,
'default' : '',
accessKey : 'N',
setup : function( element )
{
this.setValue(
element.data( 'cke-saved-name' ) ||
element.getAttribute( 'name' ) ||
'' );
},
commit : function( data )
{
var element = data.element;
if ( this.getValue() )
element.data( 'cke-saved-name', this.getValue() );
else
{
element.data( 'cke-saved-name', false );
element.removeAttribute( 'name' );
}
}
},
{
id : 'value',
type : 'text',
label : editor.lang.textfield.value,
'default' : '',
accessKey : 'V'
}
]
},
{
type : 'hbox',
widths : [ '50%', '50%' ],
children :
[
{
id : 'size',
type : 'text',
label : editor.lang.textfield.charWidth,
'default' : '',
accessKey : 'C',
style : 'width:50px',
validate : CKEDITOR.dialog.validate.integer( editor.lang.common.validateNumberFailed )
},
{
id : 'maxLength',
type : 'text',
label : editor.lang.textfield.maxChars,
'default' : '',
accessKey : 'M',
style : 'width:50px',
validate : CKEDITOR.dialog.validate.integer( editor.lang.common.validateNumberFailed )
}
],
onLoad : function()
{
// Repaint the style for IE7 (#6068)
if ( CKEDITOR.env.ie7Compat )
this.getElement().setStyle( 'zoom', '100%' );
}
},
{
id : 'type',
type : 'select',
label : editor.lang.textfield.type,
'default' : 'text',
accessKey : 'M',
items :
[
[ editor.lang.textfield.typeText, 'text' ],
[ editor.lang.textfield.typePass, 'password' ]
],
setup : function( element )
{
this.setValue( element.getAttribute( 'type' ) );
},
commit : function( data )
{
var element = data.element;
if ( CKEDITOR.env.ie )
{
var elementType = element.getAttribute( 'type' );
var myType = this.getValue();
if ( elementType != myType )
{
var replace = CKEDITOR.dom.element.createFromHtml( '<input type="' + myType + '"></input>', editor.document );
element.copyAttributes( replace, { type : 1 } );
replace.replace( element );
editor.getSelection().selectElement( replace );
data.element = replace;
}
}
else
element.setAttribute( 'type', this.getValue() );
}
}
]
}
]
};
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 B

View File

@@ -0,0 +1,284 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
/**
* @file Forms Plugin
*/
CKEDITOR.plugins.add( 'forms',
{
init : function( editor )
{
var lang = editor.lang;
editor.addCss(
'form' +
'{' +
'border: 1px dotted #FF0000;' +
'padding: 2px;' +
'}\n' );
editor.addCss(
'img.cke_hidden' +
'{' +
'background-image: url(' + CKEDITOR.getUrl( this.path + 'images/hiddenfield.gif' ) + ');' +
'background-position: center center;' +
'background-repeat: no-repeat;' +
'border: 1px solid #a9a9a9;' +
'width: 16px !important;' +
'height: 16px !important;' +
'}' );
// All buttons use the same code to register. So, to avoid
// duplications, let's use this tool function.
var addButtonCommand = function( buttonName, commandName, dialogFile )
{
editor.addCommand( commandName, new CKEDITOR.dialogCommand( commandName ) );
editor.ui.addButton( buttonName,
{
label : lang.common[ buttonName.charAt(0).toLowerCase() + buttonName.slice(1) ],
command : commandName
});
CKEDITOR.dialog.add( commandName, dialogFile );
};
var dialogPath = this.path + 'dialogs/';
addButtonCommand( 'Form', 'form', dialogPath + 'form.js' );
addButtonCommand( 'Checkbox', 'checkbox', dialogPath + 'checkbox.js' );
addButtonCommand( 'Radio', 'radio', dialogPath + 'radio.js' );
addButtonCommand( 'TextField', 'textfield', dialogPath + 'textfield.js' );
addButtonCommand( 'Textarea', 'textarea', dialogPath + 'textarea.js' );
addButtonCommand( 'Select', 'select', dialogPath + 'select.js' );
addButtonCommand( 'Button', 'button', dialogPath + 'button.js' );
addButtonCommand( 'ImageButton', 'imagebutton', CKEDITOR.plugins.getPath('image') + 'dialogs/image.js' );
addButtonCommand( 'HiddenField', 'hiddenfield', dialogPath + 'hiddenfield.js' );
// If the "menu" plugin is loaded, register the menu items.
if ( editor.addMenuItems )
{
editor.addMenuItems(
{
form :
{
label : lang.form.menu,
command : 'form',
group : 'form'
},
checkbox :
{
label : lang.checkboxAndRadio.checkboxTitle,
command : 'checkbox',
group : 'checkbox'
},
radio :
{
label : lang.checkboxAndRadio.radioTitle,
command : 'radio',
group : 'radio'
},
textfield :
{
label : lang.textfield.title,
command : 'textfield',
group : 'textfield'
},
hiddenfield :
{
label : lang.hidden.title,
command : 'hiddenfield',
group : 'hiddenfield'
},
imagebutton :
{
label : lang.image.titleButton,
command : 'imagebutton',
group : 'imagebutton'
},
button :
{
label : lang.button.title,
command : 'button',
group : 'button'
},
select :
{
label : lang.select.title,
command : 'select',
group : 'select'
},
textarea :
{
label : lang.textarea.title,
command : 'textarea',
group : 'textarea'
}
});
}
// If the "contextmenu" plugin is loaded, register the listeners.
if ( editor.contextMenu )
{
editor.contextMenu.addListener( function( element )
{
if ( element && element.hasAscendant( 'form', true ) && !element.isReadOnly() )
return { form : CKEDITOR.TRISTATE_OFF };
});
editor.contextMenu.addListener( function( element )
{
if ( element && !element.isReadOnly() )
{
var name = element.getName();
if ( name == 'select' )
return { select : CKEDITOR.TRISTATE_OFF };
if ( name == 'textarea' )
return { textarea : CKEDITOR.TRISTATE_OFF };
if ( name == 'input' )
{
var type = element.getAttribute( 'type' );
if ( type == 'text' || type == 'password' )
return { textfield : CKEDITOR.TRISTATE_OFF };
if ( type == 'button' || type == 'submit' || type == 'reset' )
return { button : CKEDITOR.TRISTATE_OFF };
if ( type == 'checkbox' )
return { checkbox : CKEDITOR.TRISTATE_OFF };
if ( type == 'radio' )
return { radio : CKEDITOR.TRISTATE_OFF };
if ( type == 'image' )
return { imagebutton : CKEDITOR.TRISTATE_OFF };
}
if ( name == 'img' && element.data( 'cke-real-element-type' ) == 'hiddenfield' )
return { hiddenfield : CKEDITOR.TRISTATE_OFF };
}
});
}
editor.on( 'doubleclick', function( evt )
{
var element = evt.data.element;
if ( element.is( 'form' ) )
evt.data.dialog = 'form';
else if ( element.is( 'select' ) )
evt.data.dialog = 'select';
else if ( element.is( 'textarea' ) )
evt.data.dialog = 'textarea';
else if ( element.is( 'img' ) && element.data( 'cke-real-element-type' ) == 'hiddenfield' )
evt.data.dialog = 'hiddenfield';
else if ( element.is( 'input' ) )
{
var type = element.getAttribute( 'type' );
switch ( type )
{
case 'text' :
case 'password' :
evt.data.dialog = 'textfield';
break;
case 'button' :
case 'submit' :
case 'reset' :
evt.data.dialog = 'button';
break;
case 'checkbox' :
evt.data.dialog = 'checkbox';
break;
case 'radio' :
evt.data.dialog = 'radio';
break;
case 'image' :
evt.data.dialog = 'imagebutton';
break;
}
}
});
},
afterInit : function( editor )
{
var dataProcessor = editor.dataProcessor,
htmlFilter = dataProcessor && dataProcessor.htmlFilter,
dataFilter = dataProcessor && dataProcessor.dataFilter;
// Cleanup certain IE form elements default values.
if ( CKEDITOR.env.ie )
{
htmlFilter && htmlFilter.addRules(
{
elements :
{
input : function( input )
{
var attrs = input.attributes,
type = attrs.type;
if ( type == 'checkbox' || type == 'radio' )
attrs.value == 'on' && delete attrs.value;
}
}
} );
}
if ( dataFilter )
{
dataFilter.addRules(
{
elements :
{
input : function( element )
{
if ( element.attributes.type == 'hidden' )
return editor.createFakeParserElement( element, 'cke_hidden', 'hiddenfield' );
}
}
} );
}
},
requires : [ 'image', 'fakeobjects' ]
} );
if ( CKEDITOR.env.ie )
{
CKEDITOR.dom.element.prototype.hasAttribute = function( name )
{
var $attr = this.$.attributes.getNamedItem( name );
if ( this.getName() == 'input' )
{
switch ( name )
{
case 'class' :
return this.$.className.length > 0;
case 'checked' :
return !!this.$.checked;
case 'value' :
var type = this.getAttribute( 'type' );
if ( type == 'checkbox' || type == 'radio' )
return this.$.value != 'on';
break;
default:
}
}
return !!( $attr && $attr.specified );
};
}

View File

@@ -0,0 +1,36 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
/**
* @file Horizontal Rule plugin.
*/
(function()
{
var horizontalruleCmd =
{
canUndo : false, // The undo snapshot will be handled by 'insertElement'.
exec : function( editor )
{
editor.insertElement( editor.document.createElement( 'hr' ) );
}
};
var pluginName = 'horizontalrule';
// Register a plugin named "horizontalrule".
CKEDITOR.plugins.add( pluginName,
{
init : function( editor )
{
editor.addCommand( pluginName, horizontalruleCmd );
editor.ui.addButton( 'HorizontalRule',
{
label : editor.lang.horizontalrule,
command : pluginName
});
}
});
})();

View File

@@ -0,0 +1,543 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
(function()
{
// Regex to scan for &nbsp; at the end of blocks, which are actually placeholders.
// Safari transforms the &nbsp; to \xa0. (#4172)
var tailNbspRegex = /^[\t\r\n ]*(?:&nbsp;|\xa0)$/;
var protectedSourceMarker = '{cke_protected}';
// Return the last non-space child node of the block (#4344).
function lastNoneSpaceChild( block )
{
var lastIndex = block.children.length,
last = block.children[ lastIndex - 1 ];
while ( last && last.type == CKEDITOR.NODE_TEXT && !CKEDITOR.tools.trim( last.value ) )
last = block.children[ --lastIndex ];
return last;
}
function trimFillers( block, fromSource )
{
// If the current node is a block, and if we're converting from source or
// we're not in IE then search for and remove any tailing BR node.
//
// Also, any &nbsp; at the end of blocks are fillers, remove them as well.
// (#2886)
var children = block.children, lastChild = lastNoneSpaceChild( block );
if ( lastChild )
{
if ( ( fromSource || !CKEDITOR.env.ie ) && lastChild.type == CKEDITOR.NODE_ELEMENT && lastChild.name == 'br' )
children.pop();
if ( lastChild.type == CKEDITOR.NODE_TEXT && tailNbspRegex.test( lastChild.value ) )
children.pop();
}
}
function blockNeedsExtension( block, fromSource, extendEmptyBlock )
{
if( !fromSource && ( !extendEmptyBlock ||
typeof extendEmptyBlock == 'function' && ( extendEmptyBlock( block ) === false ) ) )
return false;
// 1. For IE version >=8, empty blocks are displayed correctly themself in wysiwiyg;
// 2. For the rest, at least table cell and list item need no filler space.
// (#6248)
if ( fromSource && CKEDITOR.env.ie &&
( document.documentMode > 7
|| block.name in CKEDITOR.dtd.tr
|| block.name in CKEDITOR.dtd.$listItem ) )
return false;
var lastChild = lastNoneSpaceChild( block );
return !lastChild || lastChild &&
( lastChild.type == CKEDITOR.NODE_ELEMENT && lastChild.name == 'br'
// Some of the controls in form needs extension too,
// to move cursor at the end of the form. (#4791)
|| block.name == 'form' && lastChild.name == 'input' );
}
function getBlockExtension( isOutput, emptyBlockFiller )
{
return function( node )
{
trimFillers( node, !isOutput );
if ( blockNeedsExtension( node, !isOutput, emptyBlockFiller ) )
{
if ( isOutput || CKEDITOR.env.ie )
node.add( new CKEDITOR.htmlParser.text( '\xa0' ) );
else
node.add( new CKEDITOR.htmlParser.element( 'br', {} ) );
}
};
}
var dtd = CKEDITOR.dtd;
// Find out the list of block-like tags that can contain <br>.
var blockLikeTags = CKEDITOR.tools.extend( {}, dtd.$block, dtd.$listItem, dtd.$tableContent );
for ( var i in blockLikeTags )
{
if ( ! ( 'br' in dtd[i] ) )
delete blockLikeTags[i];
}
// We just avoid filler in <pre> right now.
// TODO: Support filler for <pre>, line break is also occupy line height.
delete blockLikeTags.pre;
var defaultDataFilterRules =
{
elements : {},
attributeNames :
[
// Event attributes (onXYZ) must not be directly set. They can become
// active in the editing area (IE|WebKit).
[ ( /^on/ ), 'data-cke-pa-on' ]
]
};
var defaultDataBlockFilterRules = { elements : {} };
for ( i in blockLikeTags )
defaultDataBlockFilterRules.elements[ i ] = getBlockExtension();
var defaultHtmlFilterRules =
{
elementNames :
[
// Remove the "cke:" namespace prefix.
[ ( /^cke:/ ), '' ],
// Ignore <?xml:namespace> tags.
[ ( /^\?xml:namespace$/ ), '' ]
],
attributeNames :
[
// Attributes saved for changes and protected attributes.
[ ( /^data-cke-(saved|pa)-/ ), '' ],
// All "data-cke" attributes are to be ignored.
[ ( /^data-cke.*/ ), '' ],
[ 'hidefocus', '' ]
],
elements :
{
$ : function( element )
{
var attribs = element.attributes;
if ( attribs )
{
// Elements marked as temporary are to be ignored.
if ( attribs[ 'data-cke-temp' ] )
return false;
// Remove duplicated attributes - #3789.
var attributeNames = [ 'name', 'href', 'src' ],
savedAttributeName;
for ( var i = 0 ; i < attributeNames.length ; i++ )
{
savedAttributeName = 'data-cke-saved-' + attributeNames[ i ];
savedAttributeName in attribs && ( delete attribs[ attributeNames[ i ] ] );
}
}
return element;
},
embed : function( element )
{
var parent = element.parent;
// If the <embed> is child of a <object>, copy the width
// and height attributes from it.
if ( parent && parent.name == 'object' )
{
var parentWidth = parent.attributes.width,
parentHeight = parent.attributes.height;
parentWidth && ( element.attributes.width = parentWidth );
parentHeight && ( element.attributes.height = parentHeight );
}
},
// Restore param elements into self-closing.
param : function( param )
{
param.children = [];
param.isEmpty = true;
return param;
},
// Remove empty link but not empty anchor.(#3829)
a : function( element )
{
if ( !( element.children.length ||
element.attributes.name ||
element.attributes[ 'data-cke-saved-name' ] ) )
{
return false;
}
},
// Remove dummy span in webkit.
span: function( element )
{
if ( element.attributes[ 'class' ] == 'Apple-style-span' )
delete element.name;
},
html : function( element )
{
delete element.attributes.contenteditable;
delete element.attributes[ 'class' ];
},
body : function( element )
{
delete element.attributes.spellcheck;
delete element.attributes.contenteditable;
},
style : function( element )
{
var child = element.children[ 0 ];
child && child.value && ( child.value = CKEDITOR.tools.trim( child.value ));
if ( !element.attributes.type )
element.attributes.type = 'text/css';
},
title : function( element )
{
var titleText = element.children[ 0 ];
titleText && ( titleText.value = element.attributes[ 'data-cke-title' ] || '' );
}
},
attributes :
{
'class' : function( value, element )
{
// Remove all class names starting with "cke_".
return CKEDITOR.tools.ltrim( value.replace( /(?:^|\s+)cke_[^\s]*/g, '' ) ) || false;
}
},
comment : function( contents )
{
// If this is a comment for protected source.
if ( contents.substr( 0, protectedSourceMarker.length ) == protectedSourceMarker )
{
// Remove the extra marker for real comments from it.
if ( contents.substr( protectedSourceMarker.length, 3 ) == '{C}' )
contents = contents.substr( protectedSourceMarker.length + 3 );
else
contents = contents.substr( protectedSourceMarker.length );
return new CKEDITOR.htmlParser.cdata( decodeURIComponent( contents ) );
}
return contents;
}
};
if ( CKEDITOR.env.ie )
{
// IE outputs style attribute in capital letters. We should convert
// them back to lower case.
defaultHtmlFilterRules.attributes.style = function( value, element )
{
return value.toLowerCase();
};
}
function protectReadOnly( element )
{
element.attributes.contenteditable = "false";
}
function unprotectReadyOnly( element )
{
delete element.attributes.contenteditable;
}
// Disable form elements editing mode provided by some browers. (#5746)
for ( i in { input : 1, textarea : 1 } )
{
defaultDataFilterRules.elements[ i ] = protectReadOnly;
defaultHtmlFilterRules.elements[ i ] = unprotectReadyOnly;
}
var protectAttributeRegex = /<((?:a|area|img|input)\b[\s\S]*?\s)((href|src|name)\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|(?:[^ "'>]+)))([^>]*)>/gi,
findSavedSrcRegex = /\sdata-cke-saved-src\s*=/;
var protectElementsRegex = /(?:<style(?=[ >])[^>]*>[\s\S]*<\/style>)|(?:<(:?link|meta|base)[^>]*>)/gi,
encodedElementsRegex = /<cke:encoded>([^<]*)<\/cke:encoded>/gi;
var protectElementNamesRegex = /(<\/?)((?:object|embed|param|html|body|head|title)[^>]*>)/gi,
unprotectElementNamesRegex = /(<\/?)cke:((?:html|body|head|title)[^>]*>)/gi;
var protectSelfClosingRegex = /<cke:(param|embed)([^>]*?)\/?>(?!\s*<\/cke:\1)/gi;
function protectAttributes( html )
{
return html.replace( protectAttributeRegex, function( tag, beginning, fullAttr, attrName, end )
{
// We should not rewrite the _cke_saved_src attribute (#5218)
if ( attrName == 'src' && findSavedSrcRegex.test( tag ) )
return tag;
else
return '<' + beginning + fullAttr + ' data-cke-saved-' + fullAttr + end + '>';
});
}
function protectElements( html )
{
return html.replace( protectElementsRegex, function( match )
{
return '<cke:encoded>' + encodeURIComponent( match ) + '</cke:encoded>';
});
}
function unprotectElements( html )
{
return html.replace( encodedElementsRegex, function( match, encoded )
{
return decodeURIComponent( encoded );
});
}
function protectElementsNames( html )
{
return html.replace( protectElementNamesRegex, '$1cke:$2');
}
function unprotectElementNames( html )
{
return html.replace( unprotectElementNamesRegex, '$1$2' );
}
function protectSelfClosingElements( html )
{
return html.replace( protectSelfClosingRegex, '<cke:$1$2></cke:$1>' );
}
function protectPreFormatted( html )
{
return html.replace( /(<pre\b[^>]*>)(\r\n|\n)/g, '$1$2$2' );
}
function protectRealComments( html )
{
return html.replace( /<!--(?!{cke_protected})[\s\S]+?-->/g, function( match )
{
return '<!--' + protectedSourceMarker +
'{C}' +
encodeURIComponent( match ).replace( /--/g, '%2D%2D' ) +
'-->';
});
}
function unprotectRealComments( html )
{
return html.replace( /<!--\{cke_protected\}\{C\}([\s\S]+?)-->/g, function( match, data )
{
return decodeURIComponent( data );
});
}
function protectSource( data, protectRegexes )
{
var protectedHtml = [],
tempRegex = /<\!--\{cke_temp(comment)?\}(\d*?)-->/g;
var regexes =
[
// Script tags will also be forced to be protected, otherwise
// IE will execute them.
( /<script[\s\S]*?<\/script>/gi ),
// <noscript> tags (get lost in IE and messed up in FF).
/<noscript[\s\S]*?<\/noscript>/gi
]
.concat( protectRegexes );
// First of any other protection, we must protect all comments
// to avoid loosing them (of course, IE related).
// Note that we use a different tag for comments, as we need to
// transform them when applying filters.
data = data.replace( (/<!--[\s\S]*?-->/g), function( match )
{
return '<!--{cke_tempcomment}' + ( protectedHtml.push( match ) - 1 ) + '-->';
});
for ( var i = 0 ; i < regexes.length ; i++ )
{
data = data.replace( regexes[i], function( match )
{
match = match.replace( tempRegex, // There could be protected source inside another one. (#3869).
function( $, isComment, id )
{
return protectedHtml[ id ];
}
);
return '<!--{cke_temp}' + ( protectedHtml.push( match ) - 1 ) + '-->';
});
}
data = data.replace( tempRegex, function( $, isComment, id )
{
return '<!--' + protectedSourceMarker +
( isComment ? '{C}' : '' ) +
encodeURIComponent( protectedHtml[ id ] ).replace( /--/g, '%2D%2D' ) +
'-->';
}
);
return data;
}
CKEDITOR.plugins.add( 'htmldataprocessor',
{
requires : [ 'htmlwriter' ],
init : function( editor )
{
var dataProcessor = editor.dataProcessor = new CKEDITOR.htmlDataProcessor( editor );
dataProcessor.writer.forceSimpleAmpersand = editor.config.forceSimpleAmpersand;
dataProcessor.dataFilter.addRules( defaultDataFilterRules );
dataProcessor.dataFilter.addRules( defaultDataBlockFilterRules );
dataProcessor.htmlFilter.addRules( defaultHtmlFilterRules );
var defaultHtmlBlockFilterRules = { elements : {} };
for ( i in blockLikeTags )
defaultHtmlBlockFilterRules.elements[ i ] = getBlockExtension( true, editor.config.fillEmptyBlocks );
dataProcessor.htmlFilter.addRules( defaultHtmlBlockFilterRules );
},
onLoad : function()
{
! ( 'fillEmptyBlocks' in CKEDITOR.config ) && ( CKEDITOR.config.fillEmptyBlocks = 1 );
}
});
CKEDITOR.htmlDataProcessor = function( editor )
{
this.editor = editor;
this.writer = new CKEDITOR.htmlWriter();
this.dataFilter = new CKEDITOR.htmlParser.filter();
this.htmlFilter = new CKEDITOR.htmlParser.filter();
};
CKEDITOR.htmlDataProcessor.prototype =
{
toHtml : function( data, fixForBody )
{
// The source data is already HTML, but we need to clean
// it up and apply the filter.
data = protectSource( data, this.editor.config.protectedSource );
// Before anything, we must protect the URL attributes as the
// browser may changing them when setting the innerHTML later in
// the code.
data = protectAttributes( data );
// Protect elements than can't be set inside a DIV. E.g. IE removes
// style tags from innerHTML. (#3710)
data = protectElements( data );
// Certain elements has problem to go through DOM operation, protect
// them by prefixing 'cke' namespace. (#3591)
data = protectElementsNames( data );
// All none-IE browsers ignore self-closed custom elements,
// protecting them into open-close. (#3591)
data = protectSelfClosingElements( data );
// Compensate one leading line break after <pre> open as browsers
// eat it up. (#5789)
data = protectPreFormatted( data );
// Call the browser to help us fixing a possibly invalid HTML
// structure.
var div = new CKEDITOR.dom.element( 'div' );
// Add fake character to workaround IE comments bug. (#3801)
div.setHtml( 'a' + data );
data = div.getHtml().substr( 1 );
// Unprotect "some" of the protected elements at this point.
data = unprotectElementNames( data );
data = unprotectElements( data );
// Restore the comments that have been protected, in this way they
// can be properly filtered.
data = unprotectRealComments( data );
// Now use our parser to make further fixes to the structure, as
// well as apply the filter.
var fragment = CKEDITOR.htmlParser.fragment.fromHtml( data, fixForBody ),
writer = new CKEDITOR.htmlParser.basicWriter();
fragment.writeHtml( writer, this.dataFilter );
data = writer.getHtml( true );
// Protect the real comments again.
data = protectRealComments( data );
return data;
},
toDataFormat : function( html, fixForBody )
{
var writer = this.writer,
fragment = CKEDITOR.htmlParser.fragment.fromHtml( html, fixForBody );
writer.reset();
fragment.writeHtml( writer, this.htmlFilter );
return writer.getHtml( true );
}
};
})();
/**
* Whether to force using "&" instead of "&amp;amp;" in elements attributes
* values, it's not recommended to change this setting for compliance with the
* W3C XHTML 1.0 standards (<a href="http://www.w3.org/TR/xhtml1/#C_12">C.12, XHTML 1.0</a>).
* @name CKEDITOR.config.forceSimpleAmpersand
* @name CKEDITOR.config.forceSimpleAmpersand
* @type Boolean
* @default false
* @example
* config.forceSimpleAmpersand = false;
*/
/**
* Whether a filler text (non-breaking space entity - &nbsp;) will be inserted into empty block elements in HTML output,
* this is used to render block elements properly with line-height; When a function is instead specified,
* it'll be passed a {@link CKEDITOR.htmlParser.element} to decide whether adding the filler text
* by expecting a boolean return value.
* @name CKEDITOR.config.fillEmptyBlocks
* @since 3.5
* @type Boolean
* @default true
* @example
* config.fillEmptyBlocks = false; // Prevent filler nodes in all empty blocks.
*
* // Prevent filler node only in float cleaners.
* config.fillEmptyBlocks = function( element )
* {
* if ( element.attributes[ 'class' ].indexOf ( 'clear-both' ) != -1 )
* return false;
* }
*/

View File

@@ -0,0 +1,319 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.add( 'htmlwriter' );
/**
* Class used to write HTML data.
* @constructor
* @example
* var writer = new CKEDITOR.htmlWriter();
* writer.openTag( 'p' );
* writer.attribute( 'class', 'MyClass' );
* writer.openTagClose( 'p' );
* writer.text( 'Hello' );
* writer.closeTag( 'p' );
* alert( writer.getHtml() ); "&lt;p class="MyClass"&gt;Hello&lt;/p&gt;"
*/
CKEDITOR.htmlWriter = CKEDITOR.tools.createClass(
{
base : CKEDITOR.htmlParser.basicWriter,
$ : function()
{
// Call the base contructor.
this.base();
/**
* The characters to be used for each identation step.
* @type String
* @default "\t" (tab)
* @example
* // Use two spaces for indentation.
* editorInstance.dataProcessor.writer.indentationChars = ' ';
*/
this.indentationChars = '\t';
/**
* The characters to be used to close "self-closing" elements, like "br" or
* "img".
* @type String
* @default " /&gt;"
* @example
* // Use HTML4 notation for self-closing elements.
* editorInstance.dataProcessor.writer.selfClosingEnd = '>';
*/
this.selfClosingEnd = ' />';
/**
* The characters to be used for line breaks.
* @type String
* @default "\n" (LF)
* @example
* // Use CRLF for line breaks.
* editorInstance.dataProcessor.writer.lineBreakChars = '\r\n';
*/
this.lineBreakChars = '\n';
this.forceSimpleAmpersand = 0;
this.sortAttributes = 1;
this._.indent = 0;
this._.indentation = '';
// Indicate preformatted block context status. (#5789)
this._.inPre = 0;
this._.rules = {};
var dtd = CKEDITOR.dtd;
for ( var e in CKEDITOR.tools.extend( {}, dtd.$nonBodyContent, dtd.$block, dtd.$listItem, dtd.$tableContent ) )
{
this.setRules( e,
{
indent : 1,
breakBeforeOpen : 1,
breakAfterOpen : 1,
breakBeforeClose : !dtd[ e ][ '#' ],
breakAfterClose : 1
});
}
this.setRules( 'br',
{
breakAfterOpen : 1
});
this.setRules( 'title',
{
indent : 0,
breakAfterOpen : 0
});
this.setRules( 'style',
{
indent : 0,
breakBeforeClose : 1
});
// Disable indentation on <pre>.
this.setRules( 'pre',
{
indent : 0
});
},
proto :
{
/**
* Writes the tag opening part for a opener tag.
* @param {String} tagName The element name for this tag.
* @param {Object} attributes The attributes defined for this tag. The
* attributes could be used to inspect the tag.
* @example
* // Writes "&lt;p".
* writer.openTag( 'p', { class : 'MyClass', id : 'MyId' } );
*/
openTag : function( tagName, attributes )
{
var rules = this._.rules[ tagName ];
if ( this._.indent )
this.indentation();
// Do not break if indenting.
else if ( rules && rules.breakBeforeOpen )
{
this.lineBreak();
this.indentation();
}
this._.output.push( '<', tagName );
},
/**
* Writes the tag closing part for a opener tag.
* @param {String} tagName The element name for this tag.
* @param {Boolean} isSelfClose Indicates that this is a self-closing tag,
* like "br" or "img".
* @example
* // Writes "&gt;".
* writer.openTagClose( 'p', false );
* @example
* // Writes " /&gt;".
* writer.openTagClose( 'br', true );
*/
openTagClose : function( tagName, isSelfClose )
{
var rules = this._.rules[ tagName ];
if ( isSelfClose )
this._.output.push( this.selfClosingEnd );
else
{
this._.output.push( '>' );
if ( rules && rules.indent )
this._.indentation += this.indentationChars;
}
if ( rules && rules.breakAfterOpen )
this.lineBreak();
tagName == 'pre' && ( this._.inPre = 1 );
},
/**
* Writes an attribute. This function should be called after opening the
* tag with {@link #openTagClose}.
* @param {String} attName The attribute name.
* @param {String} attValue The attribute value.
* @example
* // Writes ' class="MyClass"'.
* writer.attribute( 'class', 'MyClass' );
*/
attribute : function( attName, attValue )
{
if ( typeof attValue == 'string' )
{
this.forceSimpleAmpersand && ( attValue = attValue.replace( /&amp;/g, '&' ) );
// Browsers don't always escape special character in attribute values. (#4683, #4719).
attValue = CKEDITOR.tools.htmlEncodeAttr( attValue );
}
this._.output.push( ' ', attName, '="', attValue, '"' );
},
/**
* Writes a closer tag.
* @param {String} tagName The element name for this tag.
* @example
* // Writes "&lt;/p&gt;".
* writer.closeTag( 'p' );
*/
closeTag : function( tagName )
{
var rules = this._.rules[ tagName ];
if ( rules && rules.indent )
this._.indentation = this._.indentation.substr( this.indentationChars.length );
if ( this._.indent )
this.indentation();
// Do not break if indenting.
else if ( rules && rules.breakBeforeClose )
{
this.lineBreak();
this.indentation();
}
this._.output.push( '</', tagName, '>' );
tagName == 'pre' && ( this._.inPre = 0 );
if ( rules && rules.breakAfterClose )
this.lineBreak();
},
/**
* Writes text.
* @param {String} text The text value
* @example
* // Writes "Hello Word".
* writer.text( 'Hello Word' );
*/
text : function( text )
{
if ( this._.indent )
{
this.indentation();
!this._.inPre && ( text = CKEDITOR.tools.ltrim( text ) );
}
this._.output.push( text );
},
/**
* Writes a comment.
* @param {String} comment The comment text.
* @example
* // Writes "&lt;!-- My comment --&gt;".
* writer.comment( ' My comment ' );
*/
comment : function( comment )
{
if ( this._.indent )
this.indentation();
this._.output.push( '<!--', comment, '-->' );
},
/**
* Writes a line break. It uses the {@link #lineBreakChars} property for it.
* @example
* // Writes "\n" (e.g.).
* writer.lineBreak();
*/
lineBreak : function()
{
if ( !this._.inPre && this._.output.length > 0 )
this._.output.push( this.lineBreakChars );
this._.indent = 1;
},
/**
* Writes the current indentation chars. It uses the
* {@link #indentationChars} property, repeating it for the current
* indentation steps.
* @example
* // Writes "\t" (e.g.).
* writer.indentation();
*/
indentation : function()
{
if( !this._.inPre )
this._.output.push( this._.indentation );
this._.indent = 0;
},
/**
* Sets formatting rules for a give element. The possible rules are:
* <ul>
* <li><b>indent</b>: indent the element contents.</li>
* <li><b>breakBeforeOpen</b>: break line before the opener tag for this element.</li>
* <li><b>breakAfterOpen</b>: break line after the opener tag for this element.</li>
* <li><b>breakBeforeClose</b>: break line before the closer tag for this element.</li>
* <li><b>breakAfterClose</b>: break line after the closer tag for this element.</li>
* </ul>
*
* All rules default to "false". Each call to the function overrides
* already present rules, leaving the undefined untouched.
*
* By default, all elements available in the {@link CKEDITOR.dtd.$block),
* {@link CKEDITOR.dtd.$listItem} and {@link CKEDITOR.dtd.$tableContent}
* lists have all the above rules set to "true". Additionaly, the "br"
* element has the "breakAfterOpen" set to "true".
* @param {String} tagName The element name to which set the rules.
* @param {Object} rules An object containing the element rules.
* @example
* // Break line before and after "img" tags.
* writer.setRules( 'img',
* {
* breakBeforeOpen : true
* breakAfterOpen : true
* });
* @example
* // Reset the rules for the "h1" tag.
* writer.setRules( 'h1', {} );
*/
setRules : function( tagName, rules )
{
var currentRules = this._.rules[ tagName ];
if ( currentRules )
CKEDITOR.tools.extend( currentRules, rules, true );
else
this._.rules[ tagName ] = rules;
}
}
});

View File

@@ -0,0 +1,257 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
(function()
{
// Map 'true' and 'false' values to match W3C's specifications
// http://www.w3.org/TR/REC-html40/present/frames.html#h-16.5
var checkboxValues =
{
scrolling : { 'true' : 'yes', 'false' : 'no' },
frameborder : { 'true' : '1', 'false' : '0' }
};
function loadValue( iframeNode )
{
var isCheckbox = this instanceof CKEDITOR.ui.dialog.checkbox;
if ( iframeNode.hasAttribute( this.id ) )
{
var value = iframeNode.getAttribute( this.id );
if ( isCheckbox )
this.setValue( checkboxValues[ this.id ][ 'true' ] == value.toLowerCase() );
else
this.setValue( value );
}
}
function commitValue( iframeNode )
{
var isRemove = this.getValue() === '',
isCheckbox = this instanceof CKEDITOR.ui.dialog.checkbox,
value = this.getValue();
if ( isRemove )
iframeNode.removeAttribute( this.att || this.id );
else if ( isCheckbox )
iframeNode.setAttribute( this.id, checkboxValues[ this.id ][ value ] );
else
iframeNode.setAttribute( this.att || this.id, value );
}
CKEDITOR.dialog.add( 'iframe', function( editor )
{
var iframeLang = editor.lang.iframe,
commonLang = editor.lang.common,
dialogadvtab = editor.plugins.dialogadvtab;
return {
title : iframeLang.title,
minWidth : 350,
minHeight : 260,
onShow : function()
{
// Clear previously saved elements.
this.fakeImage = this.iframeNode = null;
var fakeImage = this.getSelectedElement();
if ( fakeImage && fakeImage.data( 'cke-real-element-type' ) && fakeImage.data( 'cke-real-element-type' ) == 'iframe' )
{
this.fakeImage = fakeImage;
var iframeNode = editor.restoreRealElement( fakeImage );
this.iframeNode = iframeNode;
this.setupContent( iframeNode, fakeImage );
}
},
onOk : function()
{
var iframeNode;
if ( !this.fakeImage )
iframeNode = new CKEDITOR.dom.element( 'iframe' );
else
iframeNode = this.iframeNode;
// A subset of the specified attributes/styles
// should also be applied on the fake element to
// have better visual effect. (#5240)
var extraStyles = {}, extraAttributes = {};
this.commitContent( iframeNode, extraStyles, extraAttributes );
// Refresh the fake image.
var newFakeImage = editor.createFakeElement( iframeNode, 'cke_iframe', 'iframe', true );
newFakeImage.setAttributes( extraAttributes );
newFakeImage.setStyles( extraStyles );
if ( this.fakeImage )
{
newFakeImage.replace( this.fakeImage );
editor.getSelection().selectElement( newFakeImage );
}
else
editor.insertElement( newFakeImage );
},
contents : [
{
id : 'info',
label : commonLang.generalTab,
accessKey : 'I',
elements :
[
{
type : 'vbox',
padding : 0,
children :
[
{
id : 'src',
type : 'text',
label : commonLang.url,
required : true,
validate : CKEDITOR.dialog.validate.notEmpty( iframeLang.noUrl ),
setup : loadValue,
commit : commitValue
}
]
},
{
type : 'hbox',
children :
[
{
id : 'width',
type : 'text',
style : 'width:100%',
labelLayout : 'vertical',
label : commonLang.width,
validate : CKEDITOR.dialog.validate.integer( commonLang.invalidWidth ),
setup : function( iframeNode, fakeImage )
{
loadValue.apply( this, arguments );
if ( fakeImage )
{
var fakeImageWidth = parseInt( fakeImage.$.style.width, 10 );
if ( !isNaN( fakeImageWidth ) )
this.setValue( fakeImageWidth );
}
},
commit : function( iframeNode, extraStyles )
{
commitValue.apply( this, arguments );
if ( this.getValue() )
extraStyles.width = this.getValue() + 'px';
}
},
{
id : 'height',
type : 'text',
style : 'width:100%',
labelLayout : 'vertical',
label : commonLang.height,
validate : CKEDITOR.dialog.validate.integer( commonLang.invalidHeight ),
setup : function( iframeNode, fakeImage )
{
loadValue.apply( this, arguments );
if ( fakeImage )
{
var fakeImageHeight = parseInt( fakeImage.$.style.height, 10 );
if ( !isNaN( fakeImageHeight ) )
this.setValue( fakeImageHeight );
}
},
commit : function( iframeNode, extraStyles )
{
commitValue.apply( this, arguments );
if ( this.getValue() )
extraStyles.height = this.getValue() + 'px';
}
},
{
id : 'align',
type : 'select',
'default' : '',
items :
[
[ commonLang.notSet , '' ],
[ commonLang.alignLeft , 'left' ],
[ commonLang.alignRight , 'right' ],
[ commonLang.alignTop , 'top' ],
[ commonLang.alignMiddle , 'middle' ],
[ commonLang.alignBottom , 'bottom' ]
],
style : 'width:100%',
labelLayout : 'vertical',
label : commonLang.align,
setup : function( iframeNode, fakeImage )
{
loadValue.apply( this, arguments );
if ( fakeImage )
{
var fakeImageAlign = fakeImage.getAttribute( 'align' );
this.setValue( fakeImageAlign && fakeImageAlign.toLowerCase() || '' );
}
},
commit : function( iframeNode, extraStyles, extraAttributes )
{
commitValue.apply( this, arguments );
if ( this.getValue() )
extraAttributes.align = this.getValue();
}
}
]
},
{
type : 'hbox',
widths : [ '50%', '50%' ],
children :
[
{
id : 'scrolling',
type : 'checkbox',
label : iframeLang.scrolling,
setup : loadValue,
commit : commitValue
},
{
id : 'frameborder',
type : 'checkbox',
label : iframeLang.border,
setup : loadValue,
commit : commitValue
}
]
},
{
type : 'hbox',
widths : [ '50%', '50%' ],
children :
[
{
id : 'name',
type : 'text',
label : commonLang.name,
setup : loadValue,
commit : commitValue
},
{
id : 'title',
type : 'text',
label : commonLang.advisoryTitle,
setup : loadValue,
commit : commitValue
}
]
},
{
id : 'longdesc',
type : 'text',
label : commonLang.longDescr,
setup : loadValue,
commit : commitValue
}
]
},
dialogadvtab && dialogadvtab.createAdvancedTab( editor, { id:1, classes:1, styles:1 })
]
};
});
})();

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 B

View File

@@ -0,0 +1,106 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
(function()
{
function createFakeElement( editor, realElement )
{
var fakeElement = editor.createFakeParserElement( realElement, 'cke_iframe', 'iframe', true ),
fakeStyle = fakeElement.attributes.style || '';
var width = realElement.attributes.width,
height = realElement.attributes.height;
if ( typeof width != 'undefined' )
fakeStyle += 'width:' + CKEDITOR.tools.cssLength( width ) + ';';
if ( typeof height != 'undefined' )
fakeStyle += 'height:' + CKEDITOR.tools.cssLength( height ) + ';';
fakeElement.attributes.style = fakeStyle;
return fakeElement;
}
CKEDITOR.plugins.add( 'iframe',
{
requires : [ 'dialog', 'fakeobjects' ],
init : function( editor )
{
var pluginName = 'iframe',
lang = editor.lang.iframe;
CKEDITOR.dialog.add( pluginName, this.path + 'dialogs/iframe.js' );
editor.addCommand( pluginName, new CKEDITOR.dialogCommand( pluginName ) );
editor.addCss(
'img.cke_iframe' +
'{' +
'background-image: url(' + CKEDITOR.getUrl( this.path + 'images/placeholder.png' ) + ');' +
'background-position: center center;' +
'background-repeat: no-repeat;' +
'border: 1px solid #a9a9a9;' +
'width: 80px;' +
'height: 80px;' +
'}'
);
editor.ui.addButton( 'Iframe',
{
label : lang.toolbar,
command : pluginName
});
editor.on( 'doubleclick', function( evt )
{
var element = evt.data.element;
if ( element.is( 'img' ) && element.data( 'cke-real-element-type' ) == 'iframe' )
evt.data.dialog = 'iframe';
});
if ( editor.addMenuItems )
{
editor.addMenuItems(
{
iframe :
{
label : lang.title,
command : 'iframe',
group : 'image'
}
});
}
// If the "contextmenu" plugin is loaded, register the listeners.
if ( editor.contextMenu )
{
editor.contextMenu.addListener( function( element, selection )
{
if ( element && element.is( 'img' ) && element.data( 'cke-real-element-type' ) == 'iframe' )
return { iframe : CKEDITOR.TRISTATE_OFF };
});
}
},
afterInit : function( editor )
{
var dataProcessor = editor.dataProcessor,
dataFilter = dataProcessor && dataProcessor.dataFilter;
if ( dataFilter )
{
dataFilter.addRules(
{
elements :
{
iframe : function( element )
{
return createFakeElement( editor, element );
}
}
});
}
}
});
})();

View File

@@ -0,0 +1,136 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
/**
* @fileOverview Plugin for making iframe based dialogs.
*/
CKEDITOR.plugins.add( 'iframedialog',
{
requires : [ 'dialog' ],
onLoad : function()
{
CKEDITOR.dialog.addIframe = function( name, title, src, width, height, onContentLoad )
{
var element =
{
type : 'iframe',
src : src,
width : '100%',
height : '100%'
};
if ( typeof( onContentLoad ) == 'function' )
element.onContentLoad = onContentLoad;
var definition =
{
title : title,
minWidth : width,
minHeight : height,
contents :
[
{
id : 'iframe',
label : title,
expand : true,
elements : [ element ]
}
]
};
return this.add( name, function(){ return definition; } );
};
(function()
{
/**
* An iframe element.
* @extends CKEDITOR.ui.dialog.uiElement
* @example
* @constructor
* @param {CKEDITOR.dialog} dialog
* Parent dialog object.
* @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition
* The element definition. Accepted fields:
* <ul>
* <li><strong>src</strong> (Required) The src field of the iframe. </li>
* <li><strong>width</strong> (Required) The iframe's width.</li>
* <li><strong>height</strong> (Required) The iframe's height.</li>
* <li><strong>onContentLoad</strong> (Optional) A function to be executed
* after the iframe's contents has finished loading.</li>
* </ul>
* @param {Array} htmlList
* List of HTML code to output to.
*/
var iframeElement = function( dialog, elementDefinition, htmlList )
{
if ( arguments.length < 3 )
return;
var _ = ( this._ || ( this._ = {} ) ),
contentLoad = elementDefinition.onContentLoad && CKEDITOR.tools.bind( elementDefinition.onContentLoad, this ),
cssWidth = CKEDITOR.tools.cssLength( elementDefinition.width ),
cssHeight = CKEDITOR.tools.cssLength( elementDefinition.height );
_.frameId = CKEDITOR.tools.getNextId() + '_iframe';
// IE BUG: Parent container does not resize to contain the iframe automatically.
dialog.on( 'load', function()
{
var iframe = CKEDITOR.document.getById( _.frameId ),
parentContainer = iframe.getParent();
parentContainer.setStyles(
{
width : cssWidth,
height : cssHeight
} );
} );
var attributes =
{
src : '%2',
id : _.frameId,
frameborder : 0,
allowtransparency : true
};
var myHtml = [];
if ( typeof( elementDefinition.onContentLoad ) == 'function' )
attributes.onload = 'CKEDITOR.tools.callFunction(%1);';
CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition, myHtml, 'iframe',
{
width : cssWidth,
height : cssHeight
}, attributes, '' );
// Put a placeholder for the first time.
htmlList.push( '<div style="width:' + cssWidth + ';height:' + cssHeight + ';" id="' + this.domId + '"></div>' );
// Iframe elements should be refreshed whenever it is shown.
myHtml = myHtml.join( '' );
dialog.on( 'show', function()
{
var iframe = CKEDITOR.document.getById( _.frameId ),
parentContainer = iframe.getParent(),
callIndex = CKEDITOR.tools.addFunction( contentLoad ),
html = myHtml.replace( '%1', callIndex ).replace( '%2', CKEDITOR.tools.htmlEncode( elementDefinition.src ) );
parentContainer.setHtml( html );
} );
};
iframeElement.prototype = new CKEDITOR.ui.dialog.uiElement;
CKEDITOR.dialog.addUIElement( 'iframe',
{
build : function( dialog, elementDefinition, output )
{
return new iframeElement( dialog, elementDefinition, output );
}
} );
})();
}
} );

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,81 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
/**
* @file Image plugin
*/
CKEDITOR.plugins.add( 'image',
{
init : function( editor )
{
var pluginName = 'image';
// Register the dialog.
CKEDITOR.dialog.add( pluginName, this.path + 'dialogs/image.js' );
// Register the command.
editor.addCommand( pluginName, new CKEDITOR.dialogCommand( pluginName ) );
// Register the toolbar button.
editor.ui.addButton( 'Image',
{
label : editor.lang.common.image,
command : pluginName
});
editor.on( 'doubleclick', function( evt )
{
var element = evt.data.element;
if ( element.is( 'img' ) && !element.data( 'cke-realelement' ) )
evt.data.dialog = 'image';
});
// If the "menu" plugin is loaded, register the menu items.
if ( editor.addMenuItems )
{
editor.addMenuItems(
{
image :
{
label : editor.lang.image.menu,
command : 'image',
group : 'image'
}
});
}
// If the "contextmenu" plugin is loaded, register the listeners.
if ( editor.contextMenu )
{
editor.contextMenu.addListener( function( element, selection )
{
if ( !element || !element.is( 'img' ) || element.data( 'cke-realelement' ) || element.isReadOnly() )
return null;
return { image : CKEDITOR.TRISTATE_OFF };
});
}
}
} );
/**
* Whether to remove links when emptying the link URL field in the image dialog.
* @type Boolean
* @default true
* @example
* config.image_removeLinkByEmptyURL = false;
*/
CKEDITOR.config.image_removeLinkByEmptyURL = true;
/**
* Padding text to set off the image in preview area.
* @name CKEDITOR.config.image_previewText
* @type String
* @default "Lorem ipsum dolor..." placehoder text.
* @example
* config.image_previewText = CKEDITOR.tools.repeat( '___ ', 100 );
*/

View File

@@ -0,0 +1,468 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
/**
* @file Increse and decrease indent commands.
*/
(function()
{
var listNodeNames = { ol : 1, ul : 1 };
var isNotWhitespaces = CKEDITOR.dom.walker.whitespaces( true ),
isNotBookmark = CKEDITOR.dom.walker.bookmark( false, true );
function setState( editor, state )
{
editor.getCommand( this.name ).setState( state );
}
function onSelectionChange( evt )
{
var editor = evt.editor;
var elementPath = evt.data.path,
list = elementPath && elementPath.contains( listNodeNames );
if ( list )
return setState.call( this, editor, CKEDITOR.TRISTATE_OFF );
if ( !this.useIndentClasses && this.name == 'indent' )
return setState.call( this, editor, CKEDITOR.TRISTATE_OFF );
var path = evt.data.path,
firstBlock = path.block || path.blockLimit;
if ( !firstBlock )
return setState.call( this, editor, CKEDITOR.TRISTATE_DISABLED );
if ( this.useIndentClasses )
{
var indentClass = firstBlock.$.className.match( this.classNameRegex ),
indentStep = 0;
if ( indentClass )
{
indentClass = indentClass[1];
indentStep = this.indentClassMap[ indentClass ];
}
if ( ( this.name == 'outdent' && !indentStep ) ||
( this.name == 'indent' && indentStep == editor.config.indentClasses.length ) )
return setState.call( this, editor, CKEDITOR.TRISTATE_DISABLED );
return setState.call( this, editor, CKEDITOR.TRISTATE_OFF );
}
else
{
var indent = parseInt( firstBlock.getStyle( getIndentCssProperty( firstBlock ) ), 10 );
if ( isNaN( indent ) )
indent = 0;
if ( indent <= 0 )
return setState.call( this, editor, CKEDITOR.TRISTATE_DISABLED );
return setState.call( this, editor, CKEDITOR.TRISTATE_OFF );
}
}
function indentCommand( editor, name )
{
this.name = name;
this.useIndentClasses = editor.config.indentClasses && editor.config.indentClasses.length > 0;
if ( this.useIndentClasses )
{
this.classNameRegex = new RegExp( '(?:^|\\s+)(' + editor.config.indentClasses.join( '|' ) + ')(?=$|\\s)' );
this.indentClassMap = {};
for ( var i = 0 ; i < editor.config.indentClasses.length ; i++ )
this.indentClassMap[ editor.config.indentClasses[i] ] = i + 1;
}
this.startDisabled = name == 'outdent';
}
// Returns the CSS property to be used for identing a given element.
function getIndentCssProperty( element, dir )
{
return ( dir || element.getComputedStyle( 'direction' ) ) == 'ltr' ? 'margin-left' : 'margin-right';
}
function isListItem( node )
{
return node.type = CKEDITOR.NODE_ELEMENT && node.is( 'li' );
}
indentCommand.prototype = {
exec : function( editor )
{
var self = this, database = {};
function indentList( listNode )
{
// Our starting and ending points of the range might be inside some blocks under a list item...
// So before playing with the iterator, we need to expand the block to include the list items.
var startContainer = range.startContainer,
endContainer = range.endContainer;
while ( startContainer && !startContainer.getParent().equals( listNode ) )
startContainer = startContainer.getParent();
while ( endContainer && !endContainer.getParent().equals( listNode ) )
endContainer = endContainer.getParent();
if ( !startContainer || !endContainer )
return;
// Now we can iterate over the individual items on the same tree depth.
var block = startContainer,
itemsToMove = [],
stopFlag = false;
while ( !stopFlag )
{
if ( block.equals( endContainer ) )
stopFlag = true;
itemsToMove.push( block );
block = block.getNext();
}
if ( itemsToMove.length < 1 )
return;
// Do indent or outdent operations on the array model of the list, not the
// list's DOM tree itself. The array model demands that it knows as much as
// possible about the surrounding lists, we need to feed it the further
// ancestor node that is still a list.
var listParents = listNode.getParents( true );
for ( var i = 0 ; i < listParents.length ; i++ )
{
if ( listParents[i].getName && listNodeNames[ listParents[i].getName() ] )
{
listNode = listParents[i];
break;
}
}
var indentOffset = self.name == 'indent' ? 1 : -1,
startItem = itemsToMove[0],
lastItem = itemsToMove[ itemsToMove.length - 1 ];
// Convert the list DOM tree into a one dimensional array.
var listArray = CKEDITOR.plugins.list.listToArray( listNode, database );
// Apply indenting or outdenting on the array.
var baseIndent = listArray[ lastItem.getCustomData( 'listarray_index' ) ].indent;
for ( i = startItem.getCustomData( 'listarray_index' ); i <= lastItem.getCustomData( 'listarray_index' ); i++ )
{
listArray[ i ].indent += indentOffset;
// Make sure the newly created sublist get a brand-new element of the same type. (#5372)
var listRoot = listArray[ i ].parent;
listArray[ i ].parent = new CKEDITOR.dom.element( listRoot.getName(), listRoot.getDocument() );
}
for ( i = lastItem.getCustomData( 'listarray_index' ) + 1 ;
i < listArray.length && listArray[i].indent > baseIndent ; i++ )
listArray[i].indent += indentOffset;
// Convert the array back to a DOM forest (yes we might have a few subtrees now).
// And replace the old list with the new forest.
var newList = CKEDITOR.plugins.list.arrayToList( listArray, database, null, editor.config.enterMode, listNode.getDirection() );
// Avoid nested <li> after outdent even they're visually same,
// recording them for later refactoring.(#3982)
if ( self.name == 'outdent' )
{
var parentLiElement;
if ( ( parentLiElement = listNode.getParent() ) && parentLiElement.is( 'li' ) )
{
var children = newList.listNode.getChildren(),
pendingLis = [],
count = children.count(),
child;
for ( i = count - 1 ; i >= 0 ; i-- )
{
if ( ( child = children.getItem( i ) ) && child.is && child.is( 'li' ) )
pendingLis.push( child );
}
}
}
if ( newList )
newList.listNode.replace( listNode );
// Move the nested <li> to be appeared after the parent.
if ( pendingLis && pendingLis.length )
{
for ( i = 0; i < pendingLis.length ; i++ )
{
var li = pendingLis[ i ],
followingList = li;
// Nest preceding <ul>/<ol> inside current <li> if any.
while ( ( followingList = followingList.getNext() ) &&
followingList.is &&
followingList.getName() in listNodeNames )
{
// IE requires a filler NBSP for nested list inside empty list item,
// otherwise the list item will be inaccessiable. (#4476)
if ( CKEDITOR.env.ie && !li.getFirst( function( node ){ return isNotWhitespaces( node ) && isNotBookmark( node ); } ) )
li.append( range.document.createText( '\u00a0' ) );
li.append( followingList );
}
li.insertAfter( parentLiElement );
}
}
}
function indentBlock()
{
var iterator = range.createIterator(),
enterMode = editor.config.enterMode;
iterator.enforceRealBlocks = true;
iterator.enlargeBr = enterMode != CKEDITOR.ENTER_BR;
var block;
while ( ( block = iterator.getNextParagraph() ) )
indentElement( block );
}
function indentElement( element, dir )
{
if ( element.getCustomData( 'indent_processed' ) )
return false;
if ( self.useIndentClasses )
{
// Transform current class name to indent step index.
var indentClass = element.$.className.match( self.classNameRegex ),
indentStep = 0;
if ( indentClass )
{
indentClass = indentClass[1];
indentStep = self.indentClassMap[ indentClass ];
}
// Operate on indent step index, transform indent step index back to class
// name.
if ( self.name == 'outdent' )
indentStep--;
else
indentStep++;
if ( indentStep < 0 )
return false;
indentStep = Math.min( indentStep, editor.config.indentClasses.length );
indentStep = Math.max( indentStep, 0 );
element.$.className = CKEDITOR.tools.ltrim( element.$.className.replace( self.classNameRegex, '' ) );
if ( indentStep > 0 )
element.addClass( editor.config.indentClasses[ indentStep - 1 ] );
}
else
{
var indentCssProperty = getIndentCssProperty( element, dir ),
currentOffset = parseInt( element.getStyle( indentCssProperty ), 10 );
if ( isNaN( currentOffset ) )
currentOffset = 0;
var indentOffset = editor.config.indentOffset || 40;
currentOffset += ( self.name == 'indent' ? 1 : -1 ) * indentOffset;
if ( currentOffset < 0 )
return false;
currentOffset = Math.max( currentOffset, 0 );
currentOffset = Math.ceil( currentOffset / indentOffset ) * indentOffset;
element.setStyle( indentCssProperty, currentOffset ? currentOffset + ( editor.config.indentUnit || 'px' ) : '' );
if ( element.getAttribute( 'style' ) === '' )
element.removeAttribute( 'style' );
}
CKEDITOR.dom.element.setMarker( database, element, 'indent_processed', 1 );
return true;
}
var selection = editor.getSelection(),
bookmarks = selection.createBookmarks( 1 ),
ranges = selection && selection.getRanges( 1 ),
range;
var iterator = ranges.createIterator();
while ( ( range = iterator.getNextRange() ) )
{
var rangeRoot = range.getCommonAncestor(),
nearestListBlock = rangeRoot;
while ( nearestListBlock && !( nearestListBlock.type == CKEDITOR.NODE_ELEMENT &&
listNodeNames[ nearestListBlock.getName() ] ) )
nearestListBlock = nearestListBlock.getParent();
// Avoid having selection enclose the entire list. (#6138)
// [<ul><li>...</li></ul>] =><ul><li>[...]</li></ul>
if ( !nearestListBlock )
{
var selectedNode = range.getEnclosedNode();
if ( selectedNode
&& selectedNode.type == CKEDITOR.NODE_ELEMENT
&& selectedNode.getName() in listNodeNames)
{
range.setStartAt( selectedNode, CKEDITOR.POSITION_AFTER_START );
range.setEndAt( selectedNode, CKEDITOR.POSITION_BEFORE_END );
nearestListBlock = selectedNode;
}
}
// Avoid selection anchors under list root.
// <ul>[<li>...</li>]</ul> => <ul><li>[...]</li></ul>
if ( nearestListBlock && range.startContainer.type == CKEDITOR.NODE_ELEMENT
&& range.startContainer.getName() in listNodeNames )
{
var walker = new CKEDITOR.dom.walker( range );
walker.evaluator = isListItem;
range.startContainer = walker.next();
}
if ( nearestListBlock && range.endContainer.type == CKEDITOR.NODE_ELEMENT
&& range.endContainer.getName() in listNodeNames )
{
walker = new CKEDITOR.dom.walker( range );
walker.evaluator = isListItem;
range.endContainer = walker.previous();
}
if ( nearestListBlock )
{
var firstListItem = nearestListBlock.getFirst( isListItem ),
hasMultipleItems = !!firstListItem.getNext( isListItem ),
rangeStart = range.startContainer,
indentWholeList = firstListItem.equals( rangeStart ) || firstListItem.contains( rangeStart );
// Indent the entire list if cursor is inside the first list item. (#3893)
// Only do that for indenting or when using indent classes or when there is something to outdent. (#6141)
if ( !( indentWholeList &&
( self.name == 'indent' || self.useIndentClasses || parseInt( nearestListBlock.getStyle( getIndentCssProperty( nearestListBlock ) ), 10 ) ) &&
indentElement( nearestListBlock, !hasMultipleItems && firstListItem.getDirection() ) ) )
indentList( nearestListBlock );
}
else
indentBlock();
}
// Clean up the markers.
CKEDITOR.dom.element.clearAllMarkers( database );
editor.forceNextSelectionCheck();
selection.selectBookmarks( bookmarks );
}
};
CKEDITOR.plugins.add( 'indent',
{
init : function( editor )
{
// Register commands.
var indent = new indentCommand( editor, 'indent' ),
outdent = new indentCommand( editor, 'outdent' );
editor.addCommand( 'indent', indent );
editor.addCommand( 'outdent', outdent );
// Register the toolbar buttons.
editor.ui.addButton( 'Indent',
{
label : editor.lang.indent,
command : 'indent'
});
editor.ui.addButton( 'Outdent',
{
label : editor.lang.outdent,
command : 'outdent'
});
// Register the state changing handlers.
editor.on( 'selectionChange', CKEDITOR.tools.bind( onSelectionChange, indent ) );
editor.on( 'selectionChange', CKEDITOR.tools.bind( onSelectionChange, outdent ) );
// [IE6/7] Raw lists are using margin instead of padding for visual indentation in wysiwyg mode. (#3893)
if ( CKEDITOR.env.ie6Compat || CKEDITOR.env.ie7Compat )
{
editor.addCss(
"ul,ol" +
"{" +
" margin-left: 0px;" +
" padding-left: 40px;" +
"}" );
}
// Register dirChanged listener.
editor.on( 'dirChanged', function( e )
{
var range = new CKEDITOR.dom.range( editor.document );
range.setStartBefore( e.data.node );
range.setEndAfter( e.data.node );
var walker = new CKEDITOR.dom.walker( range ),
node;
while ( ( node = walker.next() ) )
{
if ( node.type == CKEDITOR.NODE_ELEMENT )
{
// A child with the defined dir is to be ignored.
if ( !node.equals( e.data.node ) && node.getDirection() )
{
range.setStartAfter( node );
walker = new CKEDITOR.dom.walker( range );
continue;
}
// Switch alignment classes.
var classes = editor.config.indentClasses;
if ( classes )
{
var suffix = ( e.data.dir == 'ltr' ) ? [ '_rtl', '' ] : [ '', '_rtl' ];
for ( var i = 0; i < classes.length; i++ )
{
if ( node.hasClass( classes[ i ] + suffix[ 0 ] ) )
{
node.removeClass( classes[ i ] + suffix[ 0 ] );
node.addClass( classes[ i ] + suffix[ 1 ] );
}
}
}
// Switch the margins.
var marginLeft = node.getStyle( 'margin-right' ),
marginRight = node.getStyle( 'margin-left' );
marginLeft ? node.setStyle( 'margin-left', marginLeft ) : node.removeStyle( 'margin-left' );
marginRight ? node.setStyle( 'margin-right', marginRight ) : node.removeStyle( 'margin-right' );
}
}
});
},
requires : [ 'domiterator', 'list' ]
} );
})();
/**
* Size of each indentation step
* @name CKEDITOR.config.indentOffset
* @type Number
* @default 40
* @example
* config.indentOffset = 4;
*/
/**
* Unit for the indentation style
* @name CKEDITOR.config.indentUnit
* @type String
* @default 'px'
* @example
* config.indentUnit = 'em';
*/
/**
* List of classes to use for indenting the contents. If it's null, no classes will be used
* and instead the {@link #indentUnit} and {@link #indentOffset} properties will be used.
* @name CKEDITOR.config.indentClasses
* @type Array
* default null
* @example
* // Use the classes 'Indent1', 'Indent2', 'Indent3'
* config.indentClasses = ['Indent1', 'Indent2', 'Indent3'];
*/

View File

@@ -0,0 +1,239 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
/**
* @file Justify commands.
*/
(function()
{
function getState( editor, path )
{
var firstBlock = path.block || path.blockLimit;
if ( !firstBlock || firstBlock.getName() == 'body' )
return CKEDITOR.TRISTATE_OFF;
return ( getAlignment( firstBlock, editor.config.useComputedState ) == this.value ) ?
CKEDITOR.TRISTATE_ON :
CKEDITOR.TRISTATE_OFF;
}
function getAlignment( element, useComputedState )
{
useComputedState = useComputedState === undefined || useComputedState;
var align;
if ( useComputedState )
align = element.getComputedStyle( 'text-align' );
else
{
while ( !element.hasAttribute || !( element.hasAttribute( 'align' ) || element.getStyle( 'text-align' ) ) )
{
var parent = element.getParent();
if ( !parent )
break;
element = parent;
}
align = element.getStyle( 'text-align' ) || element.getAttribute( 'align' ) || '';
}
align && ( align = align.replace( /-moz-|-webkit-|start|auto/i, '' ) );
!align && useComputedState && ( align = element.getComputedStyle( 'direction' ) == 'rtl' ? 'right' : 'left' );
return align;
}
function onSelectionChange( evt )
{
var command = evt.editor.getCommand( this.name );
command.state = getState.call( this, evt.editor, evt.data.path );
command.fire( 'state' );
}
function justifyCommand( editor, name, value )
{
this.name = name;
this.value = value;
var classes = editor.config.justifyClasses;
if ( classes )
{
switch ( value )
{
case 'left' :
this.cssClassName = classes[0];
break;
case 'center' :
this.cssClassName = classes[1];
break;
case 'right' :
this.cssClassName = classes[2];
break;
case 'justify' :
this.cssClassName = classes[3];
break;
}
this.cssClassRegex = new RegExp( '(?:^|\\s+)(?:' + classes.join( '|' ) + ')(?=$|\\s)' );
}
}
function onDirChanged( e )
{
var editor = e.editor;
var range = new CKEDITOR.dom.range( editor.document );
range.setStartBefore( e.data.node );
range.setEndAfter( e.data.node );
var walker = new CKEDITOR.dom.walker( range ),
node;
while ( ( node = walker.next() ) )
{
if ( node.type == CKEDITOR.NODE_ELEMENT )
{
// A child with the defined dir is to be ignored.
if ( !node.equals( e.data.node ) && node.getDirection() )
{
range.setStartAfter( node );
walker = new CKEDITOR.dom.walker( range );
continue;
}
// Switch the alignment.
var classes = editor.config.justifyClasses;
if ( classes )
{
// The left align class.
if ( node.hasClass( classes[ 0 ] ) )
{
node.removeClass( classes[ 0 ] );
node.addClass( classes[ 2 ] );
}
// The right align class.
else if ( node.hasClass( classes[ 2 ] ) )
{
node.removeClass( classes[ 2 ] );
node.addClass( classes[ 0 ] );
}
}
// Always switch CSS margins.
var style = 'text-align';
var align = node.getStyle( style );
if ( align == 'left' )
node.setStyle( style, 'right' );
else if ( align == 'right' )
node.setStyle( style, 'left' );
}
}
}
justifyCommand.prototype = {
exec : function( editor )
{
var selection = editor.getSelection(),
enterMode = editor.config.enterMode;
if ( !selection )
return;
var bookmarks = selection.createBookmarks(),
ranges = selection.getRanges( true );
var cssClassName = this.cssClassName,
iterator,
block;
var useComputedState = editor.config.useComputedState;
useComputedState = useComputedState === undefined || useComputedState;
for ( var i = ranges.length - 1 ; i >= 0 ; i-- )
{
iterator = ranges[ i ].createIterator();
iterator.enlargeBr = enterMode != CKEDITOR.ENTER_BR;
while ( ( block = iterator.getNextParagraph() ) )
{
block.removeAttribute( 'align' );
block.removeStyle( 'text-align' );
// Remove any of the alignment classes from the className.
var className = cssClassName && ( block.$.className =
CKEDITOR.tools.ltrim( block.$.className.replace( this.cssClassRegex, '' ) ) );
var apply =
( this.state == CKEDITOR.TRISTATE_OFF ) &&
( !useComputedState || ( getAlignment( block, true ) != this.value ) );
if ( cssClassName )
{
// Append the desired class name.
if ( apply )
block.addClass( cssClassName );
else if ( !className )
block.removeAttribute( 'class' );
}
else if ( apply )
block.setStyle( 'text-align', this.value );
}
}
editor.focus();
editor.forceNextSelectionCheck();
selection.selectBookmarks( bookmarks );
}
};
CKEDITOR.plugins.add( 'justify',
{
init : function( editor )
{
var left = new justifyCommand( editor, 'justifyleft', 'left' ),
center = new justifyCommand( editor, 'justifycenter', 'center' ),
right = new justifyCommand( editor, 'justifyright', 'right' ),
justify = new justifyCommand( editor, 'justifyblock', 'justify' );
editor.addCommand( 'justifyleft', left );
editor.addCommand( 'justifycenter', center );
editor.addCommand( 'justifyright', right );
editor.addCommand( 'justifyblock', justify );
editor.ui.addButton( 'JustifyLeft',
{
label : editor.lang.justify.left,
command : 'justifyleft'
} );
editor.ui.addButton( 'JustifyCenter',
{
label : editor.lang.justify.center,
command : 'justifycenter'
} );
editor.ui.addButton( 'JustifyRight',
{
label : editor.lang.justify.right,
command : 'justifyright'
} );
editor.ui.addButton( 'JustifyBlock',
{
label : editor.lang.justify.block,
command : 'justifyblock'
} );
editor.on( 'selectionChange', CKEDITOR.tools.bind( onSelectionChange, left ) );
editor.on( 'selectionChange', CKEDITOR.tools.bind( onSelectionChange, right ) );
editor.on( 'selectionChange', CKEDITOR.tools.bind( onSelectionChange, center ) );
editor.on( 'selectionChange', CKEDITOR.tools.bind( onSelectionChange, justify ) );
editor.on( 'dirChanged', onDirChanged );
},
requires : [ 'domiterator' ]
});
})();

View File

@@ -0,0 +1,225 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
// Register a plugin named "sample".
CKEDITOR.plugins.add( 'keystrokes',
{
beforeInit : function( editor )
{
/**
* Controls keystrokes typing in this editor instance.
* @name CKEDITOR.editor.prototype.keystrokeHandler
* @type CKEDITOR.keystrokeHandler
* @example
*/
editor.keystrokeHandler = new CKEDITOR.keystrokeHandler( editor );
editor.specialKeys = {};
},
init : function( editor )
{
var keystrokesConfig = editor.config.keystrokes,
blockedConfig = editor.config.blockedKeystrokes;
var keystrokes = editor.keystrokeHandler.keystrokes,
blockedKeystrokes = editor.keystrokeHandler.blockedKeystrokes;
for ( var i = 0 ; i < keystrokesConfig.length ; i++ )
keystrokes[ keystrokesConfig[i][0] ] = keystrokesConfig[i][1];
for ( i = 0 ; i < blockedConfig.length ; i++ )
blockedKeystrokes[ blockedConfig[i] ] = 1;
}
});
/**
* Controls keystrokes typing in an editor instance.
* @constructor
* @param {CKEDITOR.editor} editor The editor instance.
* @example
*/
CKEDITOR.keystrokeHandler = function( editor )
{
if ( editor.keystrokeHandler )
return editor.keystrokeHandler;
/**
* List of keystrokes associated to commands. Each entry points to the
* command to be executed.
* @type Object
* @example
*/
this.keystrokes = {};
/**
* List of keystrokes that should be blocked if not defined at
* {@link keystrokes}. In this way it is possible to block the default
* browser behavior for those keystrokes.
* @type Object
* @example
*/
this.blockedKeystrokes = {};
this._ =
{
editor : editor
};
return this;
};
(function()
{
var cancel;
var onKeyDown = function( event )
{
// The DOM event object is passed by the "data" property.
event = event.data;
var keyCombination = event.getKeystroke();
var command = this.keystrokes[ keyCombination ];
var editor = this._.editor;
cancel = ( editor.fire( 'key', { keyCode : keyCombination } ) === true );
if ( !cancel )
{
if ( command )
{
var data = { from : 'keystrokeHandler' };
cancel = ( editor.execCommand( command, data ) !== false );
}
if ( !cancel )
{
var handler = editor.specialKeys[ keyCombination ];
cancel = ( handler && handler( editor ) === true );
if ( !cancel )
cancel = !!this.blockedKeystrokes[ keyCombination ];
}
}
if ( cancel )
event.preventDefault( true );
return !cancel;
};
var onKeyPress = function( event )
{
if ( cancel )
{
cancel = false;
event.data.preventDefault( true );
}
};
CKEDITOR.keystrokeHandler.prototype =
{
/**
* Attaches this keystroke handle to a DOM object. Keystrokes typed
** over this object will get handled by this keystrokeHandler.
* @param {CKEDITOR.dom.domObject} domObject The DOM object to attach
* to.
* @example
*/
attach : function( domObject )
{
// For most browsers, it is enough to listen to the keydown event
// only.
domObject.on( 'keydown', onKeyDown, this );
// 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.
if ( CKEDITOR.env.opera || ( CKEDITOR.env.gecko && CKEDITOR.env.mac ) )
domObject.on( 'keypress', onKeyPress, this );
}
};
})();
/**
* A list of keystrokes to be blocked if not defined in the {@link CKEDITOR.config.keystrokes}
* setting. In this way it is possible to block the default browser behavior
* for those keystrokes.
* @type Array
* @default (see example)
* @example
* // This is actually the default value.
* config.blockedKeystrokes =
* [
* CKEDITOR.CTRL + 66 &#47;*B*&#47;,
* CKEDITOR.CTRL + 73 &#47;*I*&#47;,
* CKEDITOR.CTRL + 85 &#47;*U*&#47;
* ];
*/
CKEDITOR.config.blockedKeystrokes =
[
CKEDITOR.CTRL + 66 /*B*/,
CKEDITOR.CTRL + 73 /*I*/,
CKEDITOR.CTRL + 85 /*U*/
];
/**
* A list associating keystrokes to editor commands. Each element in the list
* is an array where the first item is the keystroke, and the second is the
* name of the command to be executed.
* @type Array
* @default (see example)
* @example
* // This is actually the default value.
* config.keystrokes =
* [
* [ CKEDITOR.ALT + 121 &#47;*F10*&#47;, 'toolbarFocus' ],
* [ CKEDITOR.ALT + 122 &#47;*F11*&#47;, 'elementsPathFocus' ],
*
* [ CKEDITOR.SHIFT + 121 &#47;*F10*&#47;, 'contextMenu' ],
*
* [ CKEDITOR.CTRL + 90 &#47;*Z*&#47;, 'undo' ],
* [ CKEDITOR.CTRL + 89 &#47;*Y*&#47;, 'redo' ],
* [ CKEDITOR.CTRL + CKEDITOR.SHIFT + 90 &#47;*Z*&#47;, 'redo' ],
*
* [ CKEDITOR.CTRL + 76 &#47;*L*&#47;, 'link' ],
*
* [ CKEDITOR.CTRL + 66 &#47;*B*&#47;, 'bold' ],
* [ CKEDITOR.CTRL + 73 &#47;*I*&#47;, 'italic' ],
* [ CKEDITOR.CTRL + 85 &#47;*U*&#47;, 'underline' ],
*
* [ CKEDITOR.ALT + 109 &#47;*-*&#47;, 'toolbarCollapse' ]
* ];
*/
CKEDITOR.config.keystrokes =
[
[ CKEDITOR.ALT + 121 /*F10*/, 'toolbarFocus' ],
[ CKEDITOR.ALT + 122 /*F11*/, 'elementsPathFocus' ],
[ CKEDITOR.SHIFT + 121 /*F10*/, 'contextMenu' ],
[ CKEDITOR.CTRL + CKEDITOR.SHIFT + 121 /*F10*/, 'contextMenu' ],
[ CKEDITOR.CTRL + 90 /*Z*/, 'undo' ],
[ CKEDITOR.CTRL + 89 /*Y*/, 'redo' ],
[ CKEDITOR.CTRL + CKEDITOR.SHIFT + 90 /*Z*/, 'redo' ],
[ CKEDITOR.CTRL + 76 /*L*/, 'link' ],
[ CKEDITOR.CTRL + 66 /*B*/, 'bold' ],
[ CKEDITOR.CTRL + 73 /*I*/, 'italic' ],
[ CKEDITOR.CTRL + 85 /*U*/, 'underline' ],
[ CKEDITOR.ALT + 109 /*-*/, 'toolbarCollapse' ],
[ CKEDITOR.ALT + 48 /*0*/, 'a11yHelp' ]
];
/**
* Fired when any keyboard key (or combination) is pressed into the editing area.
* @name CKEDITOR#key
* @event
* @param {Number} data.keyCode A number representing the key code (or
* combination). It is the sum of the current key code and the
* {@link CKEDITOR.CTRL}, {@link CKEDITOR.SHIFT} and {@link CKEDITOR.ALT}
* constants, if those are pressed.
*/

View File

@@ -0,0 +1,99 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.dialog.add( 'anchor', function( editor )
{
// Function called in onShow to load selected element.
var loadElements = function( editor, selection, element )
{
this.editMode = true;
this.editObj = element;
var attributeValue = this.editObj.getAttribute( 'name' );
if ( attributeValue )
this.setValueOf( 'info','txtName', attributeValue );
else
this.setValueOf( 'info','txtName', "" );
};
return {
title : editor.lang.anchor.title,
minWidth : 300,
minHeight : 60,
onOk : function()
{
// Always create a new anchor, because of IE BUG.
var name = this.getValueOf( 'info', 'txtName' ),
element = CKEDITOR.env.ie ?
editor.document.createElement( '<a name="' + CKEDITOR.tools.htmlEncode( name ) + '">' ) :
editor.document.createElement( 'a' );
// Move contents and attributes of old anchor to new anchor.
if ( this.editMode )
{
this.editObj.copyAttributes( element, { name : 1 } );
this.editObj.moveChildren( element );
}
// Set name.
element.data( 'cke-saved-name', false );
element.setAttribute( 'name', name );
// Insert a new anchor.
var fakeElement = editor.createFakeElement( element, 'cke_anchor', 'anchor' );
if ( !this.editMode )
editor.insertElement( fakeElement );
else
{
fakeElement.replace( this.fakeObj );
editor.getSelection().selectElement( fakeElement );
}
return true;
},
onShow : function()
{
this.editObj = false;
this.fakeObj = false;
this.editMode = false;
var selection = editor.getSelection();
var element = selection.getSelectedElement();
if ( element && element.data( 'cke-real-element-type' ) && element.data( 'cke-real-element-type' ) == 'anchor' )
{
this.fakeObj = element;
element = editor.restoreRealElement( this.fakeObj );
loadElements.apply( this, [ editor, selection, element ] );
selection.selectElement( this.fakeObj );
}
this.getContentElement( 'info', 'txtName' ).focus();
},
contents : [
{
id : 'info',
label : editor.lang.anchor.title,
accessKey : 'I',
elements :
[
{
type : 'text',
id : 'txtName',
label : editor.lang.anchor.name,
required: true,
validate : function()
{
if ( !this.getValue() )
{
alert( editor.lang.anchor.errorName );
return false;
}
return true;
}
}
]
}
]
};
} );

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 B

View File

@@ -0,0 +1,241 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.add( 'link',
{
init : function( editor )
{
// Add the link and unlink buttons.
editor.addCommand( 'link', new CKEDITOR.dialogCommand( 'link' ) );
editor.addCommand( 'anchor', new CKEDITOR.dialogCommand( 'anchor' ) );
editor.addCommand( 'unlink', new CKEDITOR.unlinkCommand() );
editor.ui.addButton( 'Link',
{
label : editor.lang.link.toolbar,
command : 'link'
} );
editor.ui.addButton( 'Unlink',
{
label : editor.lang.unlink,
command : 'unlink'
} );
editor.ui.addButton( 'Anchor',
{
label : editor.lang.anchor.toolbar,
command : 'anchor'
} );
CKEDITOR.dialog.add( 'link', this.path + 'dialogs/link.js' );
CKEDITOR.dialog.add( 'anchor', this.path + 'dialogs/anchor.js' );
// Add the CSS styles for anchor placeholders.
editor.addCss(
'img.cke_anchor' +
'{' +
'background-image: url(' + CKEDITOR.getUrl( this.path + 'images/anchor.gif' ) + ');' +
'background-position: center center;' +
'background-repeat: no-repeat;' +
'border: 1px solid #a9a9a9;' +
'width: 18px !important;' +
'height: 18px !important;' +
'}\n' +
'a.cke_anchor' +
'{' +
'background-image: url(' + CKEDITOR.getUrl( this.path + 'images/anchor.gif' ) + ');' +
'background-position: 0 center;' +
'background-repeat: no-repeat;' +
'border: 1px solid #a9a9a9;' +
'padding-left: 18px;' +
'}'
);
// Register selection change handler for the unlink button.
editor.on( 'selectionChange', function( evt )
{
/*
* Despite our initial hope, document.queryCommandEnabled() does not work
* for this in Firefox. So we must detect the state by element paths.
*/
var command = editor.getCommand( 'unlink' ),
element = evt.data.path.lastElement && evt.data.path.lastElement.getAscendant( 'a', true );
if ( element && element.getName() == 'a' && element.getAttribute( 'href' ) )
command.setState( CKEDITOR.TRISTATE_OFF );
else
command.setState( CKEDITOR.TRISTATE_DISABLED );
} );
editor.on( 'doubleclick', function( evt )
{
var element = CKEDITOR.plugins.link.getSelectedLink( editor ) || evt.data.element;
if ( !element.isReadOnly() )
{
if ( element.is( 'a' ) )
evt.data.dialog = ( element.getAttribute( 'name' ) && !element.getAttribute( 'href' ) ) ? 'anchor' : 'link';
else if ( element.is( 'img' ) && element.data( 'cke-real-element-type' ) == 'anchor' )
evt.data.dialog = 'anchor';
}
});
// If the "menu" plugin is loaded, register the menu items.
if ( editor.addMenuItems )
{
editor.addMenuItems(
{
anchor :
{
label : editor.lang.anchor.menu,
command : 'anchor',
group : 'anchor'
},
link :
{
label : editor.lang.link.menu,
command : 'link',
group : 'link',
order : 1
},
unlink :
{
label : editor.lang.unlink,
command : 'unlink',
group : 'link',
order : 5
}
});
}
// If the "contextmenu" plugin is loaded, register the listeners.
if ( editor.contextMenu )
{
editor.contextMenu.addListener( function( element, selection )
{
if ( !element || element.isReadOnly() )
return null;
var isAnchor = ( element.is( 'img' ) && element.data( 'cke-real-element-type' ) == 'anchor' );
if ( !isAnchor )
{
if ( !( element = CKEDITOR.plugins.link.getSelectedLink( editor ) ) )
return null;
isAnchor = ( element.getAttribute( 'name' ) && !element.getAttribute( 'href' ) );
}
return isAnchor ?
{ anchor : CKEDITOR.TRISTATE_OFF } :
{ link : CKEDITOR.TRISTATE_OFF, unlink : CKEDITOR.TRISTATE_OFF };
});
}
},
afterInit : function( editor )
{
// Register a filter to displaying placeholders after mode change.
var dataProcessor = editor.dataProcessor,
dataFilter = dataProcessor && dataProcessor.dataFilter;
if ( dataFilter )
{
dataFilter.addRules(
{
elements :
{
a : function( element )
{
var attributes = element.attributes;
if ( attributes.name && !attributes.href )
return editor.createFakeParserElement( element, 'cke_anchor', 'anchor' );
}
}
});
}
},
requires : [ 'fakeobjects' ]
} );
CKEDITOR.plugins.link =
{
/**
* Get the surrounding link element of current selection.
* @param editor
* @example CKEDITOR.plugins.link.getSelectedLink( editor );
* @since 3.2.1
* The following selection will all return the link element.
* <pre>
* <a href="#">li^nk</a>
* <a href="#">[link]</a>
* text[<a href="#">link]</a>
* <a href="#">li[nk</a>]
* [<b><a href="#">li]nk</a></b>]
* [<a href="#"><b>li]nk</b></a>
* </pre>
*/
getSelectedLink : function( editor )
{
try
{
var selection = editor.getSelection();
if ( selection.getType() == CKEDITOR.SELECTION_ELEMENT )
{
var selectedElement = selection.getSelectedElement();
if ( selectedElement.is( 'a' ) )
return selectedElement;
}
var range = selection.getRanges( true )[ 0 ];
range.shrink( CKEDITOR.SHRINK_TEXT );
var root = range.getCommonAncestor();
return root.getAscendant( 'a', true );
}
catch( e ) { return null; }
}
};
CKEDITOR.unlinkCommand = function(){};
CKEDITOR.unlinkCommand.prototype =
{
/** @ignore */
exec : function( editor )
{
/*
* execCommand( 'unlink', ... ) in Firefox leaves behind <span> tags at where
* the <a> was, so again we have to remove the link ourselves. (See #430)
*
* TODO: Use the style system when it's complete. Let's use execCommand()
* as a stopgap solution for now.
*/
var selection = editor.getSelection(),
bookmarks = selection.createBookmarks(),
ranges = selection.getRanges(),
rangeRoot,
element;
for ( var i = 0 ; i < ranges.length ; i++ )
{
rangeRoot = ranges[i].getCommonAncestor( true );
element = rangeRoot.getAscendant( 'a', true );
if ( !element )
continue;
ranges[i].selectNodeContents( element );
}
selection.selectRanges( ranges );
editor.document.$.execCommand( 'unlink', false, null );
selection.selectBookmarks( bookmarks );
},
startDisabled : true
};
CKEDITOR.tools.extend( CKEDITOR.config,
{
linkShowAdvancedTab : true,
linkShowTargetTab : true
} );

View File

@@ -0,0 +1,723 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
/**
* @file Insert and remove numbered and bulleted lists.
*/
(function()
{
var listNodeNames = { ol : 1, ul : 1 },
emptyTextRegex = /^[\n\r\t ]*$/;
var whitespaces = CKEDITOR.dom.walker.whitespaces(),
bookmarks = CKEDITOR.dom.walker.bookmark(),
nonEmpty = function( node ){ return !( whitespaces( node ) || bookmarks( node ) ); };
CKEDITOR.plugins.list = {
/*
* Convert a DOM list tree into a data structure that is easier to
* manipulate. This operation should be non-intrusive in the sense that it
* does not change the DOM tree, with the exception that it may add some
* markers to the list item nodes when database is specified.
*/
listToArray : function( listNode, database, baseArray, baseIndentLevel, grandparentNode )
{
if ( !listNodeNames[ listNode.getName() ] )
return [];
if ( !baseIndentLevel )
baseIndentLevel = 0;
if ( !baseArray )
baseArray = [];
// Iterate over all list items to and look for inner lists.
for ( var i = 0, count = listNode.getChildCount() ; i < count ; i++ )
{
var listItem = listNode.getChild( i );
// It may be a text node or some funny stuff.
if ( listItem.$.nodeName.toLowerCase() != 'li' )
continue;
var itemObj = { 'parent' : listNode, indent : baseIndentLevel, element : listItem, contents : [] };
if ( !grandparentNode )
{
itemObj.grandparent = listNode.getParent();
if ( itemObj.grandparent && itemObj.grandparent.$.nodeName.toLowerCase() == 'li' )
itemObj.grandparent = itemObj.grandparent.getParent();
}
else
itemObj.grandparent = grandparentNode;
if ( database )
CKEDITOR.dom.element.setMarker( database, listItem, 'listarray_index', baseArray.length );
baseArray.push( itemObj );
for ( var j = 0, itemChildCount = listItem.getChildCount(), child; j < itemChildCount ; j++ )
{
child = listItem.getChild( j );
if ( child.type == CKEDITOR.NODE_ELEMENT && listNodeNames[ child.getName() ] )
// Note the recursion here, it pushes inner list items with
// +1 indentation in the correct order.
CKEDITOR.plugins.list.listToArray( child, database, baseArray, baseIndentLevel + 1, itemObj.grandparent );
else
itemObj.contents.push( child );
}
}
return baseArray;
},
// Convert our internal representation of a list back to a DOM forest.
arrayToList : function( listArray, database, baseIndex, paragraphMode, dir )
{
if ( !baseIndex )
baseIndex = 0;
if ( !listArray || listArray.length < baseIndex + 1 )
return null;
var doc = listArray[ baseIndex ].parent.getDocument(),
retval = new CKEDITOR.dom.documentFragment( doc ),
rootNode = null,
currentIndex = baseIndex,
indentLevel = Math.max( listArray[ baseIndex ].indent, 0 ),
currentListItem = null,
paragraphName = ( paragraphMode == CKEDITOR.ENTER_P ? 'p' : 'div' );
while ( 1 )
{
var item = listArray[ currentIndex ];
if ( item.indent == indentLevel )
{
if ( !rootNode || listArray[ currentIndex ].parent.getName() != rootNode.getName() )
{
rootNode = listArray[ currentIndex ].parent.clone( false, 1 );
dir && rootNode.setAttribute( 'dir', dir );
retval.append( rootNode );
}
currentListItem = rootNode.append( item.element.clone( 0, 1 ) );
for ( var i = 0 ; i < item.contents.length ; i++ )
currentListItem.append( item.contents[i].clone( 1, 1 ) );
currentIndex++;
}
else if ( item.indent == Math.max( indentLevel, 0 ) + 1 )
{
var listData = CKEDITOR.plugins.list.arrayToList( listArray, null, currentIndex, paragraphMode );
currentListItem.append( listData.listNode );
currentIndex = listData.nextIndex;
}
else if ( item.indent == -1 && !baseIndex && item.grandparent )
{
currentListItem;
if ( listNodeNames[ item.grandparent.getName() ] )
currentListItem = item.element.clone( false, true );
else
{
// Create completely new blocks here.
if ( dir || item.element.hasAttributes() ||
( paragraphMode != CKEDITOR.ENTER_BR && item.grandparent.getName() != 'td' ) )
{
currentListItem = doc.createElement( paragraphName );
item.element.copyAttributes( currentListItem, { type:1, value:1 } );
dir && currentListItem.setAttribute( 'dir', dir );
// There might be a case where there are no attributes in the element after all
// (i.e. when "type" or "value" are the only attributes set). In this case, if enterMode = BR,
// the current item should be a fragment.
if ( !dir && paragraphMode == CKEDITOR.ENTER_BR && !currentListItem.hasAttributes() )
currentListItem = new CKEDITOR.dom.documentFragment( doc );
}
else
currentListItem = new CKEDITOR.dom.documentFragment( doc );
}
for ( i = 0 ; i < item.contents.length ; i++ )
currentListItem.append( item.contents[i].clone( 1, 1 ) );
if ( currentListItem.type == CKEDITOR.NODE_DOCUMENT_FRAGMENT
&& currentIndex != listArray.length - 1 )
{
var last = currentListItem.getLast();
if ( last && last.type == CKEDITOR.NODE_ELEMENT
&& last.getAttribute( 'type' ) == '_moz' )
{
last.remove();
}
if ( !( last = currentListItem.getLast( nonEmpty )
&& last.type == CKEDITOR.NODE_ELEMENT
&& last.getName() in CKEDITOR.dtd.$block ) )
{
currentListItem.append( doc.createElement( 'br' ) );
}
}
if ( currentListItem.type == CKEDITOR.NODE_ELEMENT &&
currentListItem.getName() == paragraphName &&
currentListItem.$.firstChild )
{
currentListItem.trim();
var firstChild = currentListItem.getFirst();
if ( firstChild.type == CKEDITOR.NODE_ELEMENT && firstChild.isBlockBoundary() )
{
var tmp = new CKEDITOR.dom.documentFragment( doc );
currentListItem.moveChildren( tmp );
currentListItem = tmp;
}
}
var currentListItemName = currentListItem.$.nodeName.toLowerCase();
if ( !CKEDITOR.env.ie && ( currentListItemName == 'div' || currentListItemName == 'p' ) )
currentListItem.appendBogus();
retval.append( currentListItem );
rootNode = null;
currentIndex++;
}
else
return null;
if ( listArray.length <= currentIndex || Math.max( listArray[ currentIndex ].indent, 0 ) < indentLevel )
break;
}
// Clear marker attributes for the new list tree made of cloned nodes, if any.
if ( database )
{
var currentNode = retval.getFirst();
while ( currentNode )
{
if ( currentNode.type == CKEDITOR.NODE_ELEMENT )
CKEDITOR.dom.element.clearMarkers( database, currentNode );
currentNode = currentNode.getNextSourceNode();
}
}
return { listNode : retval, nextIndex : currentIndex };
}
};
function setState( editor, state )
{
editor.getCommand( this.name ).setState( state );
}
function onSelectionChange( evt )
{
var path = evt.data.path,
blockLimit = path.blockLimit,
elements = path.elements,
element;
// Grouping should only happen under blockLimit.(#3940).
for ( var i = 0 ; i < elements.length && ( element = elements[ i ] )
&& !element.equals( blockLimit ); i++ )
{
if ( listNodeNames[ elements[i].getName() ] )
{
return setState.call( this, evt.editor,
this.type == elements[i].getName() ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF );
}
}
return setState.call( this, evt.editor, CKEDITOR.TRISTATE_OFF );
}
function changeListType( editor, groupObj, database, listsCreated )
{
// This case is easy...
// 1. Convert the whole list into a one-dimensional array.
// 2. Change the list type by modifying the array.
// 3. Recreate the whole list by converting the array to a list.
// 4. Replace the original list with the recreated list.
var listArray = CKEDITOR.plugins.list.listToArray( groupObj.root, database ),
selectedListItems = [];
for ( var i = 0 ; i < groupObj.contents.length ; i++ )
{
var itemNode = groupObj.contents[i];
itemNode = itemNode.getAscendant( 'li', true );
if ( !itemNode || itemNode.getCustomData( 'list_item_processed' ) )
continue;
selectedListItems.push( itemNode );
CKEDITOR.dom.element.setMarker( database, itemNode, 'list_item_processed', true );
}
var root = groupObj.root,
fakeParent = root.getDocument().createElement( this.type );
// Copy all attributes, except from 'start' and 'type'.
root.copyAttributes( fakeParent, { start : 1, type : 1 } );
// The list-style-type property should be ignored.
fakeParent.removeStyle( 'list-style-type' );
for ( i = 0 ; i < selectedListItems.length ; i++ )
{
var listIndex = selectedListItems[i].getCustomData( 'listarray_index' );
listArray[listIndex].parent = fakeParent;
}
var newList = CKEDITOR.plugins.list.arrayToList( listArray, database, null, editor.config.enterMode );
var child, length = newList.listNode.getChildCount();
for ( i = 0 ; i < length && ( child = newList.listNode.getChild( i ) ) ; i++ )
{
if ( child.getName() == this.type )
listsCreated.push( child );
}
newList.listNode.replace( groupObj.root );
}
var headerTagRegex = /^h[1-6]$/;
function createList( editor, groupObj, listsCreated )
{
var contents = groupObj.contents,
doc = groupObj.root.getDocument(),
listContents = [];
// It is possible to have the contents returned by DomRangeIterator to be the same as the root.
// e.g. when we're running into table cells.
// In such a case, enclose the childNodes of contents[0] into a <div>.
if ( contents.length == 1 && contents[0].equals( groupObj.root ) )
{
var divBlock = doc.createElement( 'div' );
contents[0].moveChildren && contents[0].moveChildren( divBlock );
contents[0].append( divBlock );
contents[0] = divBlock;
}
// Calculate the common parent node of all content blocks.
var commonParent = groupObj.contents[0].getParent();
for ( var i = 0 ; i < contents.length ; i++ )
commonParent = commonParent.getCommonAncestor( contents[i].getParent() );
var useComputedState = editor.config.useComputedState,
listDir, explicitDirection;
useComputedState = useComputedState === undefined || useComputedState;
// We want to insert things that are in the same tree level only, so calculate the contents again
// by expanding the selected blocks to the same tree level.
for ( i = 0 ; i < contents.length ; i++ )
{
var contentNode = contents[i],
parentNode;
while ( ( parentNode = contentNode.getParent() ) )
{
if ( parentNode.equals( commonParent ) )
{
listContents.push( contentNode );
// Determine the lists's direction.
if ( !explicitDirection && contentNode.getDirection() )
explicitDirection = 1;
var itemDir = contentNode.getDirection( useComputedState );
if ( listDir !== null )
{
// If at least one LI have a different direction than current listDir, we can't have listDir.
if ( listDir && listDir != itemDir )
listDir = null;
else
listDir = itemDir;
}
break;
}
contentNode = parentNode;
}
}
if ( listContents.length < 1 )
return;
// Insert the list to the DOM tree.
var insertAnchor = listContents[ listContents.length - 1 ].getNext(),
listNode = doc.createElement( this.type );
listsCreated.push( listNode );
var contentBlock, listItem;
while ( listContents.length )
{
contentBlock = listContents.shift();
listItem = doc.createElement( 'li' );
// Preserve preformat block and heading structure when converting to list item. (#5335) (#5271)
if ( contentBlock.is( 'pre' ) || headerTagRegex.test( contentBlock.getName() ) )
contentBlock.appendTo( listItem );
else
{
// Remove DIR attribute if it was merged into list root.
if ( listDir && contentBlock.getDirection() )
{
contentBlock.removeStyle( 'direction' );
contentBlock.removeAttribute( 'dir' );
}
contentBlock.copyAttributes( listItem );
contentBlock.moveChildren( listItem );
contentBlock.remove();
}
listItem.appendTo( listNode );
}
// Apply list root dir only if it has been explicitly declared.
if ( listDir && explicitDirection )
listNode.setAttribute( 'dir', listDir );
if ( insertAnchor )
listNode.insertBefore( insertAnchor );
else
listNode.appendTo( commonParent );
}
function removeList( editor, groupObj, database )
{
// This is very much like the change list type operation.
// Except that we're changing the selected items' indent to -1 in the list array.
var listArray = CKEDITOR.plugins.list.listToArray( groupObj.root, database ),
selectedListItems = [];
for ( var i = 0 ; i < groupObj.contents.length ; i++ )
{
var itemNode = groupObj.contents[i];
itemNode = itemNode.getAscendant( 'li', true );
if ( !itemNode || itemNode.getCustomData( 'list_item_processed' ) )
continue;
selectedListItems.push( itemNode );
CKEDITOR.dom.element.setMarker( database, itemNode, 'list_item_processed', true );
}
var lastListIndex = null;
for ( i = 0 ; i < selectedListItems.length ; i++ )
{
var listIndex = selectedListItems[i].getCustomData( 'listarray_index' );
listArray[listIndex].indent = -1;
lastListIndex = listIndex;
}
// After cutting parts of the list out with indent=-1, we still have to maintain the array list
// model's nextItem.indent <= currentItem.indent + 1 invariant. Otherwise the array model of the
// list cannot be converted back to a real DOM list.
for ( i = lastListIndex + 1 ; i < listArray.length ; i++ )
{
if ( listArray[i].indent > listArray[i-1].indent + 1 )
{
var indentOffset = listArray[i-1].indent + 1 - listArray[i].indent;
var oldIndent = listArray[i].indent;
while ( listArray[i] && listArray[i].indent >= oldIndent )
{
listArray[i].indent += indentOffset;
i++;
}
i--;
}
}
var newList = CKEDITOR.plugins.list.arrayToList( listArray, database, null, editor.config.enterMode,
groupObj.root.getAttribute( 'dir' ) );
// Compensate <br> before/after the list node if the surrounds are non-blocks.(#3836)
var docFragment = newList.listNode, boundaryNode, siblingNode;
function compensateBrs( isStart )
{
if ( ( boundaryNode = docFragment[ isStart ? 'getFirst' : 'getLast' ]() )
&& !( boundaryNode.is && boundaryNode.isBlockBoundary() )
&& ( siblingNode = groupObj.root[ isStart ? 'getPrevious' : 'getNext' ]
( CKEDITOR.dom.walker.whitespaces( true ) ) )
&& !( siblingNode.is && siblingNode.isBlockBoundary( { br : 1 } ) ) )
editor.document.createElement( 'br' )[ isStart ? 'insertBefore' : 'insertAfter' ]( boundaryNode );
}
compensateBrs( true );
compensateBrs();
docFragment.replace( groupObj.root );
}
function listCommand( name, type )
{
this.name = name;
this.type = type;
}
listCommand.prototype = {
exec : function( editor )
{
editor.focus();
var doc = editor.document,
selection = editor.getSelection(),
ranges = selection && selection.getRanges( true );
// There should be at least one selected range.
if ( !ranges || ranges.length < 1 )
return;
// Midas lists rule #1 says we can create a list even in an empty document.
// But DOM iterator wouldn't run if the document is really empty.
// So create a paragraph if the document is empty and we're going to create a list.
if ( this.state == CKEDITOR.TRISTATE_OFF )
{
var body = doc.getBody();
body.trim();
if ( !body.getFirst() )
{
var paragraph = doc.createElement( editor.config.enterMode == CKEDITOR.ENTER_P ? 'p' :
( editor.config.enterMode == CKEDITOR.ENTER_DIV ? 'div' : 'br' ) );
paragraph.appendTo( body );
ranges = new CKEDITOR.dom.rangeList( [ new CKEDITOR.dom.range( doc ) ] );
// IE exception on inserting anything when anchor inside <br>.
if ( paragraph.is( 'br' ) )
{
ranges[ 0 ].setStartBefore( paragraph );
ranges[ 0 ].setEndAfter( paragraph );
}
else
ranges[ 0 ].selectNodeContents( paragraph );
selection.selectRanges( ranges );
}
// Maybe a single range there enclosing the whole list,
// turn on the list state manually(#4129).
else
{
var range = ranges.length == 1 && ranges[ 0 ],
enclosedNode = range && range.getEnclosedNode();
if ( enclosedNode && enclosedNode.is
&& this.type == enclosedNode.getName() )
setState.call( this, editor, CKEDITOR.TRISTATE_ON );
}
}
var bookmarks = selection.createBookmarks( true );
// Group the blocks up because there are many cases where multiple lists have to be created,
// or multiple lists have to be cancelled.
var listGroups = [],
database = {},
rangeIterator = ranges.createIterator(),
index = 0;
while ( ( range = rangeIterator.getNextRange() ) && ++index )
{
var boundaryNodes = range.getBoundaryNodes(),
startNode = boundaryNodes.startNode,
endNode = boundaryNodes.endNode;
if ( startNode.type == CKEDITOR.NODE_ELEMENT && startNode.getName() == 'td' )
range.setStartAt( boundaryNodes.startNode, CKEDITOR.POSITION_AFTER_START );
if ( endNode.type == CKEDITOR.NODE_ELEMENT && endNode.getName() == 'td' )
range.setEndAt( boundaryNodes.endNode, CKEDITOR.POSITION_BEFORE_END );
var iterator = range.createIterator(),
block;
iterator.forceBrBreak = ( this.state == CKEDITOR.TRISTATE_OFF );
while ( ( block = iterator.getNextParagraph() ) )
{
// Avoid duplicate blocks get processed across ranges.
if( block.getCustomData( 'list_block' ) )
continue;
else
CKEDITOR.dom.element.setMarker( database, block, 'list_block', 1 );
var path = new CKEDITOR.dom.elementPath( block ),
pathElements = path.elements,
pathElementsCount = pathElements.length,
listNode = null,
processedFlag = 0,
blockLimit = path.blockLimit,
element;
// First, try to group by a list ancestor.
for ( var i = pathElementsCount - 1; i >= 0 && ( element = pathElements[ i ] ); i-- )
{
if ( listNodeNames[ element.getName() ]
&& blockLimit.contains( element ) ) // Don't leak outside block limit (#3940).
{
// If we've encountered a list inside a block limit
// The last group object of the block limit element should
// no longer be valid. Since paragraphs after the list
// should belong to a different group of paragraphs before
// the list. (Bug #1309)
blockLimit.removeCustomData( 'list_group_object_' + index );
var groupObj = element.getCustomData( 'list_group_object' );
if ( groupObj )
groupObj.contents.push( block );
else
{
groupObj = { root : element, contents : [ block ] };
listGroups.push( groupObj );
CKEDITOR.dom.element.setMarker( database, element, 'list_group_object', groupObj );
}
processedFlag = 1;
break;
}
}
if ( processedFlag )
continue;
// No list ancestor? Group by block limit, but don't mix contents from different ranges.
var root = blockLimit;
if ( root.getCustomData( 'list_group_object_' + index ) )
root.getCustomData( 'list_group_object_' + index ).contents.push( block );
else
{
groupObj = { root : root, contents : [ block ] };
CKEDITOR.dom.element.setMarker( database, root, 'list_group_object_' + index, groupObj );
listGroups.push( groupObj );
}
}
}
// Now we have two kinds of list groups, groups rooted at a list, and groups rooted at a block limit element.
// We either have to build lists or remove lists, for removing a list does not makes sense when we are looking
// at the group that's not rooted at lists. So we have three cases to handle.
var listsCreated = [];
while ( listGroups.length > 0 )
{
groupObj = listGroups.shift();
if ( this.state == CKEDITOR.TRISTATE_OFF )
{
if ( listNodeNames[ groupObj.root.getName() ] )
changeListType.call( this, editor, groupObj, database, listsCreated );
else
createList.call( this, editor, groupObj, listsCreated );
}
else if ( this.state == CKEDITOR.TRISTATE_ON && listNodeNames[ groupObj.root.getName() ] )
removeList.call( this, editor, groupObj, database );
}
// For all new lists created, merge adjacent, same type lists.
for ( i = 0 ; i < listsCreated.length ; i++ )
{
listNode = listsCreated[i];
var mergeSibling, listCommand = this;
( mergeSibling = function( rtl ){
var sibling = listNode[ rtl ?
'getPrevious' : 'getNext' ]( CKEDITOR.dom.walker.whitespaces( true ) );
if ( sibling && sibling.getName &&
sibling.getName() == listCommand.type )
{
sibling.remove();
// Move children order by merge direction.(#3820)
sibling.moveChildren( listNode, rtl );
}
} )();
mergeSibling( 1 );
}
// Clean up, restore selection and update toolbar button states.
CKEDITOR.dom.element.clearAllMarkers( database );
selection.selectBookmarks( bookmarks );
editor.focus();
}
};
var dtd = CKEDITOR.dtd;
var tailNbspRegex = /[\t\r\n ]*(?:&nbsp;|\xa0)$/;
function indexOfFirstChildElement( element, tagNameList )
{
var child,
children = element.children,
length = children.length;
for ( var i = 0 ; i < length ; i++ )
{
child = children[ i ];
if ( child.name && ( child.name in tagNameList ) )
return i;
}
return length;
}
function getExtendNestedListFilter( isHtmlFilter )
{
// An element filter function that corrects nested list start in an empty
// list item for better displaying/outputting. (#3165)
return function( listItem )
{
var children = listItem.children,
firstNestedListIndex = indexOfFirstChildElement( listItem, dtd.$list ),
firstNestedList = children[ firstNestedListIndex ],
nodeBefore = firstNestedList && firstNestedList.previous,
tailNbspmatch;
if ( nodeBefore
&& ( nodeBefore.name && nodeBefore.name == 'br'
|| nodeBefore.value && ( tailNbspmatch = nodeBefore.value.match( tailNbspRegex ) ) ) )
{
var fillerNode = nodeBefore;
// Always use 'nbsp' as filler node if we found a nested list appear
// in front of a list item.
if ( !( tailNbspmatch && tailNbspmatch.index ) && fillerNode == children[ 0 ] )
children[ 0 ] = ( isHtmlFilter || CKEDITOR.env.ie ) ?
new CKEDITOR.htmlParser.text( '\xa0' ) :
new CKEDITOR.htmlParser.element( 'br', {} );
// Otherwise the filler is not needed anymore.
else if ( fillerNode.name == 'br' )
children.splice( firstNestedListIndex - 1, 1 );
else
fillerNode.value = fillerNode.value.replace( tailNbspRegex, '' );
}
};
}
var defaultListDataFilterRules = { elements : {} };
for ( var i in dtd.$listItem )
defaultListDataFilterRules.elements[ i ] = getExtendNestedListFilter();
var defaultListHtmlFilterRules = { elements : {} };
for ( i in dtd.$listItem )
defaultListHtmlFilterRules.elements[ i ] = getExtendNestedListFilter( true );
CKEDITOR.plugins.add( 'list',
{
init : function( editor )
{
// Register commands.
var numberedListCommand = new listCommand( 'numberedlist', 'ol' ),
bulletedListCommand = new listCommand( 'bulletedlist', 'ul' );
editor.addCommand( 'numberedlist', numberedListCommand );
editor.addCommand( 'bulletedlist', bulletedListCommand );
// Register the toolbar button.
editor.ui.addButton( 'NumberedList',
{
label : editor.lang.numberedlist,
command : 'numberedlist'
} );
editor.ui.addButton( 'BulletedList',
{
label : editor.lang.bulletedlist,
command : 'bulletedlist'
} );
// Register the state changing handlers.
editor.on( 'selectionChange', CKEDITOR.tools.bind( onSelectionChange, numberedListCommand ) );
editor.on( 'selectionChange', CKEDITOR.tools.bind( onSelectionChange, bulletedListCommand ) );
},
afterInit : function ( editor )
{
var dataProcessor = editor.dataProcessor;
if ( dataProcessor )
{
dataProcessor.dataFilter.addRules( defaultListDataFilterRules );
dataProcessor.htmlFilter.addRules( defaultListHtmlFilterRules );
}
},
requires : [ 'domiterator' ]
} );
})();

View File

@@ -0,0 +1,257 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.add( 'listblock',
{
requires : [ 'panel' ],
onLoad : function()
{
CKEDITOR.ui.panel.prototype.addListBlock = function( name, definition )
{
return this.addBlock( name, new CKEDITOR.ui.listBlock( this.getHolderElement(), definition ) );
};
CKEDITOR.ui.listBlock = CKEDITOR.tools.createClass(
{
base : CKEDITOR.ui.panel.block,
$ : function( blockHolder, blockDefinition )
{
blockDefinition = blockDefinition || {};
var attribs = blockDefinition.attributes || ( blockDefinition.attributes = {} );
( this.multiSelect = !!blockDefinition.multiSelect ) &&
( attribs[ 'aria-multiselectable' ] = true );
// Provide default role of 'listbox'.
!attribs.role && ( attribs.role = 'listbox' );
// Call the base contructor.
this.base.apply( this, arguments );
var keys = this.keys;
keys[ 40 ] = 'next'; // ARROW-DOWN
keys[ 9 ] = 'next'; // TAB
keys[ 38 ] = 'prev'; // ARROW-UP
keys[ CKEDITOR.SHIFT + 9 ] = 'prev'; // SHIFT + TAB
keys[ 32 ] = 'click'; // SPACE
this._.pendingHtml = [];
this._.items = {};
this._.groups = {};
},
_ :
{
close : function()
{
if ( this._.started )
{
this._.pendingHtml.push( '</ul>' );
delete this._.started;
}
},
getClick : function()
{
if ( !this._.click )
{
this._.click = CKEDITOR.tools.addFunction( function( value )
{
var marked = true;
if ( this.multiSelect )
marked = this.toggle( value );
else
this.mark( value );
if ( this.onClick )
this.onClick( value, marked );
},
this );
}
return this._.click;
}
},
proto :
{
add : function( value, html, title )
{
var pendingHtml = this._.pendingHtml,
id = CKEDITOR.tools.getNextId();
if ( !this._.started )
{
pendingHtml.push( '<ul role="presentation" class=cke_panel_list>' );
this._.started = 1;
this._.size = this._.size || 0;
}
this._.items[ value ] = id;
pendingHtml.push(
'<li id=', id, ' class=cke_panel_listItem role=presentation>' +
'<a id="', id, '_option" _cke_focus=1 hidefocus=true' +
' title="', title || value, '"' +
' href="javascript:void(\'', value, '\')"' +
' onclick="CKEDITOR.tools.callFunction(', this._.getClick(), ',\'', value, '\'); return false;"',
' role="option"' +
' aria-posinset="' + ++this._.size + '">',
html || value,
'</a>' +
'</li>' );
},
startGroup : function( title )
{
this._.close();
var id = CKEDITOR.tools.getNextId();
this._.groups[ title ] = id;
this._.pendingHtml.push( '<h1 role="presentation" id=', id, ' class=cke_panel_grouptitle>', title, '</h1>' );
},
commit : function()
{
this._.close();
this.element.appendHtml( this._.pendingHtml.join( '' ) );
var items = this._.items,
doc = this.element.getDocument();
for ( var value in items )
doc.getById( items[ value ] + '_option' ).setAttribute( 'aria-setsize', this._.size );
delete this._.size;
this._.pendingHtml = [];
},
toggle : function( value )
{
var isMarked = this.isMarked( value );
if ( isMarked )
this.unmark( value );
else
this.mark( value );
return !isMarked;
},
hideGroup : function( groupTitle )
{
var group = this.element.getDocument().getById( this._.groups[ groupTitle ] ),
list = group && group.getNext();
if ( group )
{
group.setStyle( 'display', 'none' );
if ( list && list.getName() == 'ul' )
list.setStyle( 'display', 'none' );
}
},
hideItem : function( value )
{
this.element.getDocument().getById( this._.items[ value ] ).setStyle( 'display', 'none' );
},
showAll : function()
{
var items = this._.items,
groups = this._.groups,
doc = this.element.getDocument();
for ( var value in items )
{
doc.getById( items[ value ] ).setStyle( 'display', '' );
}
for ( var title in groups )
{
var group = doc.getById( groups[ title ] ),
list = group.getNext();
group.setStyle( 'display', '' );
if ( list && list.getName() == 'ul' )
list.setStyle( 'display', '' );
}
},
mark : function( value )
{
if ( !this.multiSelect )
this.unmarkAll();
var itemId = this._.items[ value ],
item = this.element.getDocument().getById( itemId );
item.addClass( 'cke_selected' );
this.element.getDocument().getById( itemId + '_option' ).setAttribute( 'aria-selected', true );
this.element.setAttribute( 'aria-activedescendant', itemId + '_option' );
this.onMark && this.onMark( item );
},
unmark : function( value )
{
this.element.getDocument().getById( this._.items[ value ] ).removeClass( 'cke_selected' );
this.onUnmark && this.onUnmark( this._.items[ value ] );
},
unmarkAll : function()
{
var items = this._.items,
doc = this.element.getDocument();
for ( var value in items )
{
doc.getById( items[ value ] ).removeClass( 'cke_selected' );
}
this.onUnmark && this.onUnmark();
},
isMarked : function( value )
{
return this.element.getDocument().getById( this._.items[ value ] ).hasClass( 'cke_selected' );
},
focus : function( value )
{
this._.focusIndex = -1;
if ( value )
{
var selected = this.element.getDocument().getById( this._.items[ value ] ).getFirst();
var links = this.element.getElementsByTag( 'a' ),
link,
i = -1;
while ( ( link = links.getItem( ++i ) ) )
{
if ( link.equals( selected ) )
{
this._.focusIndex = i;
break;
}
}
setTimeout( function()
{
selected.focus();
},
0 );
}
}
}
});
}
});

View File

@@ -0,0 +1,204 @@
/*
* Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.html or http://ckeditor.com/license
*/
(function()
{
function getListElement( editor, listTag )
{
var range;
try { range = editor.getSelection().getRanges()[ 0 ]; }
catch( e ) { return null; }
range.shrink( CKEDITOR.SHRINK_TEXT );
return range.getCommonAncestor().getAscendant( listTag, 1 );
}
var mapListStyle = {
'a' : 'lower-alpha',
'A' : 'upper-alpha',
'i' : 'lower-roman',
'I' : 'upper-roman',
'1' : 'decimal',
'disc' : 'disc',
'circle': 'circle',
'square' : 'square'
};
function listStyle( editor, startupPage )
{
var lang = editor.lang.list;
if ( startupPage == 'bulletedListStyle' )
{
return {
title : lang.bulletedTitle,
minWidth : 300,
minHeight : 50,
contents :
[
{
id : 'info',
accessKey : 'I',
elements :
[
{
type : 'select',
label : lang.type,
id : 'type',
style : 'width: 150px; margin: auto;',
items :
[
[ lang.notset, '' ],
[ lang.circle, 'circle' ],
[ lang.disc, 'disc' ],
[ lang.square, 'square' ]
],
setup : function( element )
{
var value = element.getStyle( 'list-style-type' )
|| mapListStyle[ element.getAttribute( 'type' ) ]
|| element.getAttribute( 'type' )
|| '';
this.setValue( value );
},
commit : function( element )
{
var value = this.getValue();
if ( value )
element.setStyle( 'list-style-type', value );
else
element.removeStyle( 'list-style-type' );
}
}
]
}
],
onShow: function()
{
var editor = this.getParentEditor(),
element = getListElement( editor, 'ul' );
element && this.setupContent( element );
},
onOk: function()
{
var editor = this.getParentEditor(),
element = getListElement( editor, 'ul' );
element && this.commitContent( element );
}
};
}
else if ( startupPage == 'numberedListStyle' )
{
var listStyleOptions =
[
[ lang.notset, '' ],
[ lang.lowerRoman, 'lower-roman' ],
[ lang.upperRoman, 'upper-roman' ],
[ lang.lowerAlpha, 'lower-alpha' ],
[ lang.upperAlpha, 'upper-alpha' ],
[ lang.decimal, 'decimal' ]
];
if ( !CKEDITOR.env.ie || CKEDITOR.env.version > 7 )
{
listStyleOptions.concat( [
[ lang.armenian, 'armenian' ],
[ lang.decimalLeadingZero, 'decimal-leading-zero' ],
[ lang.georgian, 'georgian' ],
[ lang.lowerGreek, 'lower-greek' ]
]);
}
return {
title : lang.numberedTitle,
minWidth : 300,
minHeight : 50,
contents :
[
{
id : 'info',
accessKey : 'I',
elements :
[
{
type : 'hbox',
widths : [ '25%', '75%' ],
children :
[
{
label : lang.start,
type : 'text',
id : 'start',
validate : CKEDITOR.dialog.validate.integer( lang.validateStartNumber ),
setup : function( element )
{
var value = element.getAttribute( 'start' ) || 1;
value && this.setValue( value );
},
commit : function( element )
{
element.setAttribute( 'start', this.getValue() );
}
},
{
type : 'select',
label : lang.type,
id : 'type',
style : 'width: 100%;',
items : listStyleOptions,
setup : function( element )
{
var value = element.getStyle( 'list-style-type' )
|| mapListStyle[ element.getAttribute( 'type' ) ]
|| element.getAttribute( 'type' )
|| '';
this.setValue( value );
},
commit : function( element )
{
var value = this.getValue();
if ( value )
element.setStyle( 'list-style-type', value );
else
element.removeStyle( 'list-style-type' );
}
}
]
}
]
}
],
onShow: function()
{
var editor = this.getParentEditor(),
element = getListElement( editor, 'ol' );
element && this.setupContent( element );
},
onOk: function()
{
var editor = this.getParentEditor(),
element = getListElement( editor, 'ol' );
element && this.commitContent( element );
}
};
}
}
CKEDITOR.dialog.add( 'numberedListStyle', function( editor )
{
return listStyle( editor, 'numberedListStyle' );
});
CKEDITOR.dialog.add( 'bulletedListStyle', function( editor )
{
return listStyle( editor, 'bulletedListStyle' );
});
})();

View File

@@ -0,0 +1,66 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
(function()
{
CKEDITOR.plugins.liststyle =
{
requires : [ 'dialog' ],
init : function( editor )
{
editor.addCommand( 'numberedListStyle', new CKEDITOR.dialogCommand( 'numberedListStyle' ) );
CKEDITOR.dialog.add( 'numberedListStyle', this.path + 'dialogs/liststyle.js' );
editor.addCommand( 'bulletedListStyle', new CKEDITOR.dialogCommand( 'bulletedListStyle' ) );
CKEDITOR.dialog.add( 'bulletedListStyle', this.path + 'dialogs/liststyle.js' );
// If the "menu" plugin is loaded, register the menu items.
if ( editor.addMenuItems )
{
//Register map group;
editor.addMenuGroup("list", 108);
editor.addMenuItems(
{
numberedlist :
{
label : editor.lang.list.numberedTitle,
group : 'list',
command: 'numberedListStyle'
},
bulletedlist :
{
label : editor.lang.list.bulletedTitle,
group : 'list',
command: 'bulletedListStyle'
}
});
}
// If the "contextmenu" plugin is loaded, register the listeners.
if ( editor.contextMenu )
{
editor.contextMenu.addListener( function( element, selection )
{
if ( !element || element.isReadOnly() )
return null;
while ( element )
{
var name = element.getName();
if ( name == 'ol' )
return { numberedlist: CKEDITOR.TRISTATE_OFF };
else if ( name == 'ul' )
return { bulletedlist: CKEDITOR.TRISTATE_OFF };
element = element.getParent();
}
return null;
});
}
}
};
CKEDITOR.plugins.add( 'liststyle', CKEDITOR.plugins.liststyle );
})();

View File

@@ -0,0 +1,340 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
(function()
{
function protectFormStyles( formElement )
{
if ( !formElement || formElement.type != CKEDITOR.NODE_ELEMENT || formElement.getName() != 'form' )
return [];
var hijackRecord = [],
hijackNames = [ 'style', 'className' ];
for ( var i = 0 ; i < hijackNames.length ; i++ )
{
var name = hijackNames[i];
var $node = formElement.$.elements.namedItem( name );
if ( $node )
{
var hijackNode = new CKEDITOR.dom.element( $node );
hijackRecord.push( [ hijackNode, hijackNode.nextSibling ] );
hijackNode.remove();
}
}
return hijackRecord;
}
function restoreFormStyles( formElement, hijackRecord )
{
if ( !formElement || formElement.type != CKEDITOR.NODE_ELEMENT || formElement.getName() != 'form' )
return;
if ( hijackRecord.length > 0 )
{
for ( var i = hijackRecord.length - 1 ; i >= 0 ; i-- )
{
var node = hijackRecord[i][0];
var sibling = hijackRecord[i][1];
if ( sibling )
node.insertBefore( sibling );
else
node.appendTo( formElement );
}
}
}
function saveStyles( element, isInsideEditor )
{
var data = protectFormStyles( element );
var retval = {};
var $element = element.$;
if ( !isInsideEditor )
{
retval[ 'class' ] = $element.className || '';
$element.className = '';
}
retval.inline = $element.style.cssText || '';
if ( !isInsideEditor ) // Reset any external styles that might interfere. (#2474)
$element.style.cssText = 'position: static; overflow: visible';
restoreFormStyles( data );
return retval;
}
function restoreStyles( element, savedStyles )
{
var data = protectFormStyles( element );
var $element = element.$;
if ( 'class' in savedStyles )
$element.className = savedStyles[ 'class' ];
if ( 'inline' in savedStyles )
$element.style.cssText = savedStyles.inline;
restoreFormStyles( data );
}
function refreshCursor( editor )
{
// Refresh all editor instances on the page (#5724).
var all = CKEDITOR.instances;
for ( var i in all )
{
var one = all[ i ];
if ( one.mode == 'wysiwyg' )
{
var body = one.document.getBody();
// Refresh 'contentEditable' otherwise
// DOM lifting breaks design mode. (#5560)
body.setAttribute( 'contentEditable', false );
body.setAttribute( 'contentEditable', true );
}
}
if ( editor.focusManager.hasFocus )
{
editor.toolbox.focus();
editor.focus();
}
}
/**
* Adding an iframe shim to this element, OR removing the existing one if already applied.
* Note: This will only affect IE version below 7.
*/
function createIframeShim( element )
{
if ( !CKEDITOR.env.ie || CKEDITOR.env.version > 6 )
return null;
var shim = CKEDITOR.dom.element.createFromHtml( '<iframe frameborder="0" tabindex="-1"' +
' src="javascript:' +
'void((function(){' +
'document.open();' +
( CKEDITOR.env.isCustomDomain() ? 'document.domain=\'' + this.getDocument().$.domain + '\';' : '' ) +
'document.close();' +
'})())"' +
' style="display:block;position:absolute;z-index:-1;' +
'progid:DXImageTransform.Microsoft.Alpha(opacity=0);' +
'"></iframe>' );
return element.append( shim, true );
}
CKEDITOR.plugins.add( 'maximize',
{
init : function( editor )
{
var lang = editor.lang;
var mainDocument = CKEDITOR.document,
mainWindow = mainDocument.getWindow();
// Saved selection and scroll position for the editing area.
var savedSelection,
savedScroll;
// Saved scroll position for the outer window.
var outerScroll;
var shim;
// Saved resize handler function.
function resizeHandler()
{
var viewPaneSize = mainWindow.getViewPaneSize();
shim && shim.setStyles( { width : viewPaneSize.width + 'px', height : viewPaneSize.height + 'px' } );
editor.resize( viewPaneSize.width, viewPaneSize.height, null, true );
}
// Retain state after mode switches.
var savedState = CKEDITOR.TRISTATE_OFF;
editor.addCommand( 'maximize',
{
modes : { wysiwyg : 1, source : 1 },
editorFocus : false,
exec : function()
{
var container = editor.container.getChild( 1 );
var contents = editor.getThemeSpace( 'contents' );
// Save current selection and scroll position in editing area.
if ( editor.mode == 'wysiwyg' )
{
var selection = editor.getSelection();
savedSelection = selection && selection.getRanges();
savedScroll = mainWindow.getScrollPosition();
}
else
{
var $textarea = editor.textarea.$;
savedSelection = !CKEDITOR.env.ie && [ $textarea.selectionStart, $textarea.selectionEnd ];
savedScroll = [ $textarea.scrollLeft, $textarea.scrollTop ];
}
if ( this.state == CKEDITOR.TRISTATE_OFF ) // Go fullscreen if the state is off.
{
// Add event handler for resizing.
mainWindow.on( 'resize', resizeHandler );
// Save the scroll bar position.
outerScroll = mainWindow.getScrollPosition();
// Save and reset the styles for the entire node tree.
var currentNode = editor.container;
while ( ( currentNode = currentNode.getParent() ) )
{
currentNode.setCustomData( 'maximize_saved_styles', saveStyles( currentNode ) );
currentNode.setStyle( 'z-index', editor.config.baseFloatZIndex - 1 );
}
contents.setCustomData( 'maximize_saved_styles', saveStyles( contents, true ) );
container.setCustomData( 'maximize_saved_styles', saveStyles( container, true ) );
// Hide scroll bars.
var viewPaneSize = mainWindow.getViewPaneSize();
var styles =
{
overflow : 'hidden',
width : 0,
height : 0
};
mainDocument.getDocumentElement().setStyles( styles );
!CKEDITOR.env.gecko && mainDocument.getDocumentElement().setStyle( 'position', 'fixed' );
mainDocument.getBody().setStyles( styles );
// Scroll to the top left (IE needs some time for it - #4923).
CKEDITOR.env.ie ?
setTimeout( function() { mainWindow.$.scrollTo( 0, 0 ); }, 0 ) :
mainWindow.$.scrollTo( 0, 0 );
// Resize and move to top left.
container.setStyle( 'position', 'absolute' );
container.$.offsetLeft; // SAFARI BUG: See #2066.
container.setStyles(
{
'z-index' : editor.config.baseFloatZIndex - 1,
left : '0px',
top : '0px'
} );
shim = createIframeShim( container ); // IE6 select element penetration when maximized. (#4459)
// Add cke_maximized class before resize handle since that will change things sizes (#5580)
container.addClass( 'cke_maximized' );
resizeHandler();
// Still not top left? Fix it. (Bug #174)
var offset = container.getDocumentPosition();
container.setStyles(
{
left : ( -1 * offset.x ) + 'px',
top : ( -1 * offset.y ) + 'px'
} );
// Fixing positioning editor chrome in Firefox break design mode. (#5149)
CKEDITOR.env.gecko && refreshCursor( editor );
}
else if ( this.state == CKEDITOR.TRISTATE_ON ) // Restore from fullscreen if the state is on.
{
// Remove event handler for resizing.
mainWindow.removeListener( 'resize', resizeHandler );
// Restore CSS styles for the entire node tree.
var editorElements = [ contents, container ];
for ( var i = 0 ; i < editorElements.length ; i++ )
{
restoreStyles( editorElements[i], editorElements[i].getCustomData( 'maximize_saved_styles' ) );
editorElements[i].removeCustomData( 'maximize_saved_styles' );
}
currentNode = editor.container;
while ( ( currentNode = currentNode.getParent() ) )
{
restoreStyles( currentNode, currentNode.getCustomData( 'maximize_saved_styles' ) );
currentNode.removeCustomData( 'maximize_saved_styles' );
}
// Restore the window scroll position.
CKEDITOR.env.ie ?
setTimeout( function() { mainWindow.$.scrollTo( outerScroll.x, outerScroll.y ); }, 0 ) :
mainWindow.$.scrollTo( outerScroll.x, outerScroll.y );
// Remove cke_maximized class.
container.removeClass( 'cke_maximized' );
if ( shim )
{
shim.remove();
shim = null;
}
// Emit a resize event, because this time the size is modified in
// restoreStyles.
editor.fire( 'resize' );
}
this.toggleState();
// Toggle button label.
var button = this.uiItems[ 0 ];
var label = ( this.state == CKEDITOR.TRISTATE_OFF )
? lang.maximize : lang.minimize;
var buttonNode = editor.element.getDocument().getById( button._.id );
buttonNode.getChild( 1 ).setHtml( label );
buttonNode.setAttribute( 'title', label );
buttonNode.setAttribute( 'href', 'javascript:void("' + label + '");' );
// Restore selection and scroll position in editing area.
if ( editor.mode == 'wysiwyg' )
{
if ( savedSelection )
{
// Fixing positioning editor chrome in Firefox break design mode. (#5149)
CKEDITOR.env.gecko && refreshCursor( editor );
editor.getSelection().selectRanges(savedSelection);
var element = editor.getSelection().getStartElement();
element && element.scrollIntoView( true );
}
else
mainWindow.$.scrollTo( savedScroll.x, savedScroll.y );
}
else
{
if ( savedSelection )
{
$textarea.selectionStart = savedSelection[0];
$textarea.selectionEnd = savedSelection[1];
}
$textarea.scrollLeft = savedScroll[0];
$textarea.scrollTop = savedScroll[1];
}
savedSelection = savedScroll = null;
savedState = this.state;
},
canUndo : false
} );
editor.ui.addButton( 'Maximize',
{
label : lang.maximize,
command : 'maximize'
} );
// Restore the command state after mode change, unless it has been changed to disabled (#6467)
editor.on( 'mode', function()
{
var command = editor.getCommand( 'maximize' );
command.setState( command.state == CKEDITOR.TRISTATE_DISABLED ? CKEDITOR.TRISTATE_DISABLED : savedState );
}, null, null, 100 );
}
} );
})();

View File

@@ -0,0 +1,505 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.add( 'menu',
{
beforeInit : function( editor )
{
var groups = editor.config.menu_groups.split( ',' ),
groupsOrder = editor._.menuGroups = {},
menuItems = editor._.menuItems = {};
for ( var i = 0 ; i < groups.length ; i++ )
groupsOrder[ groups[ i ] ] = i + 1;
editor.addMenuGroup = function( name, order )
{
groupsOrder[ name ] = order || 100;
};
editor.addMenuItem = function( name, definition )
{
if ( groupsOrder[ definition.group ] )
menuItems[ name ] = new CKEDITOR.menuItem( this, name, definition );
};
editor.addMenuItems = function( definitions )
{
for ( var itemName in definitions )
{
this.addMenuItem( itemName, definitions[ itemName ] );
}
};
editor.getMenuItem = function( name )
{
return menuItems[ name ];
};
},
requires : [ 'floatpanel' ]
});
(function()
{
CKEDITOR.menu = CKEDITOR.tools.createClass(
{
$ : function( editor, definition )
{
definition = this._.definition = definition || {};
this.id = 'cke_' + CKEDITOR.tools.getNextNumber();
this.editor = editor;
this.items = [];
this._.listeners = [];
this._.level = definition.level || 1;
var panelDefinition = CKEDITOR.tools.extend( {}, definition.panel,
{
css : editor.skin.editor.css,
level : this._.level - 1,
block : {}
} );
var attrs = panelDefinition.block.attributes = ( panelDefinition.attributes || {} );
// Provide default role of 'menu'.
!attrs.role && ( attrs.role = 'menu' );
this._.panelDefinition = panelDefinition;
},
_ :
{
onShow : function()
{
var selection = this.editor.getSelection();
// Selection will be unavailable after menu shows up
// in IE, lock it now.
if ( CKEDITOR.env.ie )
selection && selection.lock();
var element = selection && selection.getStartElement(),
listeners = this._.listeners,
includedItems = [];
this.removeAll();
// Call all listeners, filling the list of items to be displayed.
for ( var i = 0 ; i < listeners.length ; i++ )
{
var listenerItems = listeners[ i ]( element, selection );
if ( listenerItems )
{
for ( var itemName in listenerItems )
{
var item = this.editor.getMenuItem( itemName );
if ( item )
{
item.state = listenerItems[ itemName ];
this.add( item );
}
}
}
}
},
onClick : function( item )
{
this.hide();
if ( item.onClick )
item.onClick();
else if ( item.command )
this.editor.execCommand( item.command );
},
onEscape : function( keystroke )
{
var parent = this.parent;
// 1. If it's sub-menu, restore the last focused item
// of upper level menu.
// 2. In case of a top-menu, close it.
if ( parent )
{
parent._.panel.hideChild();
// Restore parent block item focus.
var parentBlock = parent._.panel._.panel._.currentBlock,
parentFocusIndex = parentBlock._.focusIndex;
parentBlock._.markItem( parentFocusIndex );
}
else if ( keystroke == 27 )
{
this.hide();
this.editor.focus();
}
return false;
},
onHide : function()
{
if ( CKEDITOR.env.ie )
{
var selection = this.editor.getSelection();
selection && selection.unlock();
}
this.onHide && this.onHide();
},
showSubMenu : function( index )
{
var menu = this._.subMenu,
item = this.items[ index ],
subItemDefs = item.getItems && item.getItems();
// If this item has no subitems, we just hide the submenu, if
// available, and return back.
if ( !subItemDefs )
{
this._.panel.hideChild();
return;
}
// Record parent menu focused item first (#3389).
var block = this._.panel.getBlock( this.id );
block._.focusIndex = index;
// Create the submenu, if not available, or clean the existing
// one.
if ( menu )
menu.removeAll();
else
{
menu = this._.subMenu = new CKEDITOR.menu( this.editor,
CKEDITOR.tools.extend( {}, this._.definition, { level : this._.level + 1 }, true ) );
menu.parent = this;
menu._.onClick = CKEDITOR.tools.bind( this._.onClick, this );
}
// Add all submenu items to the menu.
for ( var subItemName in subItemDefs )
{
var subItem = this.editor.getMenuItem( subItemName );
if ( subItem )
{
subItem.state = subItemDefs[ subItemName ];
menu.add( subItem );
}
}
// Get the element representing the current item.
var element = this._.panel.getBlock( this.id ).element.getDocument().getById( this.id + String( index ) );
// Show the submenu.
menu.show( element, 2 );
}
},
proto :
{
add : function( item )
{
// Later we may sort the items, but Array#sort is not stable in
// some browsers, here we're forcing the original sequence with
// 'order' attribute if it hasn't been assigned. (#3868)
if ( !item.order )
item.order = this.items.length;
this.items.push( item );
},
removeAll : function()
{
this.items = [];
},
show : function( offsetParent, corner, offsetX, offsetY )
{
// Not for sub menu.
if ( !this.parent )
{
this._.onShow();
// Don't menu with zero items.
if ( ! this.items.length )
return;
}
corner = corner || ( this.editor.lang.dir == 'rtl' ? 2 : 1 );
var items = this.items,
editor = this.editor,
panel = this._.panel,
element = this._.element;
// Create the floating panel for this menu.
if ( !panel )
{
panel = this._.panel = new CKEDITOR.ui.floatPanel( this.editor,
CKEDITOR.document.getBody(),
this._.panelDefinition,
this._.level );
panel.onEscape = CKEDITOR.tools.bind( function( keystroke )
{
if ( this._.onEscape( keystroke ) === false )
return false;
},
this );
panel.onHide = CKEDITOR.tools.bind( function()
{
this._.onHide && this._.onHide();
},
this );
// Create an autosize block inside the panel.
var block = panel.addBlock( this.id, this._.panelDefinition.block );
block.autoSize = true;
var keys = block.keys;
keys[ 40 ] = 'next'; // ARROW-DOWN
keys[ 9 ] = 'next'; // TAB
keys[ 38 ] = 'prev'; // ARROW-UP
keys[ CKEDITOR.SHIFT + 9 ] = 'prev'; // SHIFT + TAB
keys[ 32 ] = 'click'; // SPACE
keys[ ( editor.lang.dir == 'rtl' ? 37 : 39 ) ] = 'click'; // ARROW-RIGHT/ARROW-LEFT(rtl)
element = this._.element = block.element;
element.addClass( editor.skinClass );
var elementDoc = element.getDocument();
elementDoc.getBody().setStyle( 'overflow', 'hidden' );
elementDoc.getElementsByTag( 'html' ).getItem( 0 ).setStyle( 'overflow', 'hidden' );
this._.itemOverFn = CKEDITOR.tools.addFunction( function( index )
{
clearTimeout( this._.showSubTimeout );
this._.showSubTimeout = CKEDITOR.tools.setTimeout( this._.showSubMenu, editor.config.menu_subMenuDelay || 400, this, [ index ] );
},
this );
this._.itemOutFn = CKEDITOR.tools.addFunction( function( index )
{
clearTimeout( this._.showSubTimeout );
},
this );
this._.itemClickFn = CKEDITOR.tools.addFunction( function( index )
{
var item = this.items[ index ];
if ( item.state == CKEDITOR.TRISTATE_DISABLED )
{
this.hide();
return;
}
if ( item.getItems )
this._.showSubMenu( index );
else
this._.onClick( item );
},
this );
}
// Put the items in the right order.
sortItems( items );
var chromeRoot = editor.container.getChild( 1 ),
mixedContentClass = chromeRoot.hasClass( 'cke_mixed_dir_content' ) ? ' cke_mixed_dir_content' : '';
// Build the HTML that composes the menu and its items.
var output = [ '<div class="cke_menu' + mixedContentClass + '" role="presentation">' ];
var length = items.length,
lastGroup = length && items[ 0 ].group;
for ( var i = 0 ; i < length ; i++ )
{
var item = items[ i ];
if ( lastGroup != item.group )
{
output.push( '<div class="cke_menuseparator" role="separator"></div>' );
lastGroup = item.group;
}
item.render( this, i, output );
}
output.push( '</div>' );
// Inject the HTML inside the panel.
element.setHtml( output.join( '' ) );
CKEDITOR.ui.fire( 'ready', this );
// Show the panel.
if ( this.parent )
this.parent._.panel.showAsChild( panel, this.id, offsetParent, corner, offsetX, offsetY );
else
panel.showBlock( this.id, offsetParent, corner, offsetX, offsetY );
editor.fire( 'menuShow', [ panel ] );
},
addListener : function( listenerFn )
{
this._.listeners.push( listenerFn );
},
hide : function()
{
this._.onHide && this._.onHide();
this._.panel && this._.panel.hide();
}
}
});
function sortItems( items )
{
items.sort( function( itemA, itemB )
{
if ( itemA.group < itemB.group )
return -1;
else if ( itemA.group > itemB.group )
return 1;
return itemA.order < itemB.order ? -1 :
itemA.order > itemB.order ? 1 :
0;
});
}
CKEDITOR.menuItem = CKEDITOR.tools.createClass(
{
$ : function( editor, name, definition )
{
CKEDITOR.tools.extend( this, definition,
// Defaults
{
order : 0,
className : 'cke_button_' + name
});
// Transform the group name into its order number.
this.group = editor._.menuGroups[ this.group ];
this.editor = editor;
this.name = name;
},
proto :
{
render : function( menu, index, output )
{
var id = menu.id + String( index ),
state = ( typeof this.state == 'undefined' ) ? CKEDITOR.TRISTATE_OFF : this.state;
var classes = ' cke_' + (
state == CKEDITOR.TRISTATE_ON ? 'on' :
state == CKEDITOR.TRISTATE_DISABLED ? 'disabled' :
'off' );
var htmlLabel = this.label;
if ( this.className )
classes += ' ' + this.className;
var hasSubMenu = this.getItems;
output.push(
'<span class="cke_menuitem">' +
'<a id="', id, '"' +
' class="', classes, '" href="javascript:void(\'', ( this.label || '' ).replace( "'", '' ), '\')"' +
' title="', this.label, '"' +
' tabindex="-1"' +
'_cke_focus=1' +
' hidefocus="true"' +
' role="menuitem"' +
( hasSubMenu ? 'aria-haspopup="true"' : '' ) +
( state == CKEDITOR.TRISTATE_DISABLED ? 'aria-disabled="true"' : '' ) +
( state == CKEDITOR.TRISTATE_ON ? 'aria-pressed="true"' : '' ) );
// Some browsers don't cancel key events in the keydown but in the
// keypress.
// TODO: Check if really needed for Gecko+Mac.
if ( CKEDITOR.env.opera || ( CKEDITOR.env.gecko && CKEDITOR.env.mac ) )
{
output.push(
' onkeypress="return false;"' );
}
// With Firefox, we need to force the button to redraw, otherwise it
// will remain in the focus state.
if ( CKEDITOR.env.gecko )
{
output.push(
' onblur="this.style.cssText = this.style.cssText;"' );
}
var offset = ( this.iconOffset || 0 ) * -16;
output.push(
// ' onkeydown="return CKEDITOR.ui.button._.keydown(', index, ', event);"' +
' onmouseover="CKEDITOR.tools.callFunction(', menu._.itemOverFn, ',', index, ');"' +
' onmouseout="CKEDITOR.tools.callFunction(', menu._.itemOutFn, ',', index, ');"' +
' onclick="CKEDITOR.tools.callFunction(', menu._.itemClickFn, ',', index, '); return false;"' +
'>' +
'<span class="cke_icon_wrapper"><span class="cke_icon"' +
( this.icon ? ' style="background-image:url(' + CKEDITOR.getUrl( this.icon ) + ');background-position:0 ' + offset + 'px;"'
: '' ) +
'></span></span>' +
'<span class="cke_label">' );
if ( hasSubMenu )
{
output.push(
'<span class="cke_menuarrow">',
'<span>&#',
( this.editor.lang.dir == 'rtl' ?
'9668' : // BLACK LEFT-POINTING POINTER
'9658' ), // BLACK RIGHT-POINTING POINTER
';</span>',
'</span>' );
}
output.push(
htmlLabel,
'</span>' +
'</a>' +
'</span>' );
}
}
});
})();
/**
* The amount of time, in milliseconds, the editor waits before showing submenu
* options when moving the mouse over options that contains submenus, like the
* "Cell Properties" entry for tables.
* @type Number
* @default 400
* @example
* // Remove the submenu delay.
* config.menu_subMenuDelay = 0;
*/
/**
* A comma separated list of items group names to be displayed in the context
* menu. The items order will reflect the order in this list if no priority
* has been definted in the groups.
* @type String
* @default 'clipboard,form,tablecell,tablecellproperties,tablerow,tablecolumn,table,anchor,link,image,flash,checkbox,radio,textfield,hiddenfield,imagebutton,button,select,textarea'
* @example
* config.menu_groups = 'clipboard,table,anchor,link,image';
*/
CKEDITOR.config.menu_groups =
'clipboard,' +
'form,' +
'tablecell,tablecellproperties,tablerow,tablecolumn,table,'+
'anchor,link,image,flash,' +
'checkbox,radio,textfield,hiddenfield,imagebutton,button,select,textarea,div';

View File

@@ -0,0 +1,98 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.add( 'menubutton',
{
requires : [ 'button', 'menu' ],
beforeInit : function( editor )
{
editor.ui.addHandler( CKEDITOR.UI_MENUBUTTON, CKEDITOR.ui.menuButton.handler );
}
});
/**
* Button UI element.
* @constant
* @example
*/
CKEDITOR.UI_MENUBUTTON = 5;
(function()
{
var clickFn = function( editor )
{
var _ = this._;
// Do nothing if this button is disabled.
if ( _.state === CKEDITOR.TRISTATE_DISABLED )
return;
_.previousState = _.state;
// Check if we already have a menu for it, otherwise just create it.
var menu = _.menu;
if ( !menu )
{
menu = _.menu = new CKEDITOR.menu( editor,
{
panel:
{
className : editor.skinClass + ' cke_contextmenu',
attributes : { 'aria-label' : editor.lang.common.options }
}
});
menu.onHide = CKEDITOR.tools.bind( function()
{
this.setState( this.modes && this.modes[ editor.mode ] ? _.previousState : CKEDITOR.TRISTATE_DISABLED );
},
this );
// Initialize the menu items at this point.
if ( this.onMenu )
menu.addListener( this.onMenu );
}
if ( _.on )
{
menu.hide();
return;
}
this.setState( CKEDITOR.TRISTATE_ON );
menu.show( CKEDITOR.document.getById( this._.id ), 4 );
};
CKEDITOR.ui.menuButton = CKEDITOR.tools.createClass(
{
base : CKEDITOR.ui.button,
$ : function( definition )
{
// We don't want the panel definition in this object.
var panelDefinition = definition.panel;
delete definition.panel;
this.base( definition );
this.hasArrow = true;
this.click = clickFn;
},
statics :
{
handler :
{
create : function( definition )
{
return new CKEDITOR.ui.menuButton( definition );
}
}
}
});
})();

View File

@@ -0,0 +1,53 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
/**
* @file Horizontal Page Break
*/
// Register a plugin named "newpage".
CKEDITOR.plugins.add( 'newpage',
{
init : function( editor )
{
editor.addCommand( 'newpage',
{
modes : { wysiwyg:1, source:1 },
exec : function( editor )
{
var command = this;
editor.setData( editor.config.newpage_html || '', function()
{
// Save the undo snapshot after all document changes are affected. (#4889)
setTimeout( function ()
{
editor.fire( 'afterCommandExec',
{
name: command.name,
command: command
} );
}, 200 );
} );
editor.focus();
},
async : true
});
editor.ui.addButton( 'NewPage',
{
label : editor.lang.newPage,
command : 'newpage'
});
}
});
/**
* The HTML to load in the editor when the "new page" command is executed.
* @type String
* @default ''
* @example
* config.newpage_html = '&lt;p&gt;Type your text here.&lt;/p&gt;';
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 B

View File

@@ -0,0 +1,120 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
/**
* @file Horizontal Page Break
*/
// Register a plugin named "pagebreak".
CKEDITOR.plugins.add( 'pagebreak',
{
init : function( editor )
{
// Register the command.
editor.addCommand( 'pagebreak', CKEDITOR.plugins.pagebreakCmd );
// Register the toolbar button.
editor.ui.addButton( 'PageBreak',
{
label : editor.lang.pagebreak,
command : 'pagebreak'
});
// Add the style that renders our placeholder.
editor.addCss(
'img.cke_pagebreak' +
'{' +
'background-image: url(' + CKEDITOR.getUrl( this.path + 'images/pagebreak.gif' ) + ');' +
'background-position: center center;' +
'background-repeat: no-repeat;' +
'clear: both;' +
'display: block;' +
'float: none;' +
'width:100% !important; _width:99.9% !important;' +
'border-top: #999999 1px dotted;' +
'border-bottom: #999999 1px dotted;' +
'height: 5px !important;' +
'page-break-after: always;' +
'}' );
},
afterInit : function( editor )
{
// Register a filter to displaying placeholders after mode change.
var dataProcessor = editor.dataProcessor,
dataFilter = dataProcessor && dataProcessor.dataFilter;
if ( dataFilter )
{
dataFilter.addRules(
{
elements :
{
div : function( element )
{
var attributes = element.attributes,
style = attributes && attributes.style,
child = style && element.children.length == 1 && element.children[ 0 ],
childStyle = child && ( child.name == 'span' ) && child.attributes.style;
if ( childStyle && ( /page-break-after\s*:\s*always/i ).test( style ) && ( /display\s*:\s*none/i ).test( childStyle ) )
{
var fakeImg = editor.createFakeParserElement( element, 'cke_pagebreak', 'div' );
var label = editor.lang.pagebreakAlt;
fakeImg.attributes[ 'alt' ] = label;
fakeImg.attributes[ 'aria-label' ] = label;
return fakeImg;
}
}
}
});
}
},
requires : [ 'fakeobjects' ]
});
CKEDITOR.plugins.pagebreakCmd =
{
exec : function( editor )
{
// Create the element that represents a print break.
var label = editor.lang.pagebreakAlt;
var breakObject = CKEDITOR.dom.element.createFromHtml( '<div style="page-break-after: always;"><span style="display: none;">&nbsp;</span></div>' );
// Creates the fake image used for this element.
breakObject = editor.createFakeElement( breakObject, 'cke_pagebreak', 'div' );
breakObject.setAttributes( { alt : label, 'aria-label' : label, title : label } );
var ranges = editor.getSelection().getRanges( true );
editor.fire( 'saveSnapshot' );
for ( var range, i = ranges.length - 1 ; i >= 0; i-- )
{
range = ranges[ i ];
if ( i < ranges.length -1 )
breakObject = breakObject.clone( true );
range.splitBlock( 'p' );
range.insertNode( breakObject );
if ( i == ranges.length - 1 )
{
range.moveToPosition( breakObject, CKEDITOR.POSITION_AFTER_END );
range.select();
}
var previous = breakObject.getPrevious();
if ( previous && CKEDITOR.dtd[ previous.getName() ].div )
breakObject.move( previous );
}
editor.fire( 'saveSnapshot' );
}
};

View File

@@ -0,0 +1,395 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.add( 'panel',
{
beforeInit : function( editor )
{
editor.ui.addHandler( CKEDITOR.UI_PANEL, CKEDITOR.ui.panel.handler );
}
});
/**
* Panel UI element.
* @constant
* @example
*/
CKEDITOR.UI_PANEL = 2;
CKEDITOR.ui.panel = function( document, definition )
{
// Copy all definition properties to this object.
if ( definition )
CKEDITOR.tools.extend( this, definition );
// Set defaults.
CKEDITOR.tools.extend( this,
{
className : '',
css : []
});
this.id = CKEDITOR.tools.getNextId();
this.document = document;
this._ =
{
blocks : {}
};
};
/**
* Transforms a rich combo definition in a {@link CKEDITOR.ui.richCombo}
* instance.
* @type Object
* @example
*/
CKEDITOR.ui.panel.handler =
{
create : function( definition )
{
return new CKEDITOR.ui.panel( definition );
}
};
CKEDITOR.ui.panel.prototype =
{
renderHtml : function( editor )
{
var output = [];
this.render( editor, output );
return output.join( '' );
},
/**
* Renders the combo.
* @param {CKEDITOR.editor} editor The editor instance which this button is
* to be used by.
* @param {Array} output The output array to which append the HTML relative
* to this button.
* @example
*/
render : function( editor, output )
{
var id = this.id;
output.push(
'<div class="', editor.skinClass ,'"' +
' lang="', editor.langCode, '"' +
' role="presentation"' +
// iframe loading need sometime, keep the panel hidden(#4186).
' style="display:none;z-index:' + ( editor.config.baseFloatZIndex + 1 ) + '">' +
'<div' +
' id=', id,
' dir=', editor.lang.dir,
' role="presentation"' +
' class="cke_panel cke_', editor.lang.dir );
if ( this.className )
output.push( ' ', this.className );
output.push(
'">' );
if ( this.forceIFrame || this.css.length )
{
output.push(
'<iframe id="', id, '_frame"' +
' frameborder="0"' +
' role="application" src="javascript:void(' );
output.push(
// Support for custom document.domain in IE.
CKEDITOR.env.isCustomDomain() ?
'(function(){' +
'document.open();' +
'document.domain=\'' + document.domain + '\';' +
'document.close();' +
'})()'
:
'0' );
output.push(
')"></iframe>' );
}
output.push(
'</div>' +
'</div>' );
return id;
},
getHolderElement : function()
{
var holder = this._.holder;
if ( !holder )
{
if ( this.forceIFrame || this.css.length )
{
var iframe = this.document.getById( this.id + '_frame' ),
parentDiv = iframe.getParent(),
dir = parentDiv.getAttribute( 'dir' ),
className = parentDiv.getParent().getAttribute( 'class' ),
langCode = parentDiv.getParent().getAttribute( 'lang' ),
doc = iframe.getFrameDocument();
var onLoad = CKEDITOR.tools.addFunction( CKEDITOR.tools.bind( function( ev )
{
this.isLoaded = true;
if ( this.onLoad )
this.onLoad();
}, this ) );
var data =
'<!DOCTYPE html>' +
'<html dir="' + dir + '" class="' + className + '_container" lang="' + langCode + '">' +
'<head>' +
'<style>.' + className + '_container{visibility:hidden}</style>' +
'</head>' +
'<body class="cke_' + dir + ' cke_panel_frame ' + CKEDITOR.env.cssClass + '" style="margin:0;padding:0"' +
' onload="( window.CKEDITOR || window.parent.CKEDITOR ).tools.callFunction(' + onLoad + ');"></body>' +
// It looks strange, but for FF2, the styles must go
// after <body>, so it (body) becames immediatelly
// available. (#3031)
CKEDITOR.tools.buildStyleHtml( this.css ) +
'<\/html>';
doc.write( data );
var win = doc.getWindow();
// Register the CKEDITOR global.
win.$.CKEDITOR = CKEDITOR;
// Arrow keys for scrolling is only preventable with 'keypress' event in Opera (#4534).
doc.on( 'key' + ( CKEDITOR.env.opera? 'press':'down' ), function( evt )
{
var keystroke = evt.data.getKeystroke(),
dir = this.document.getById( this.id ).getAttribute( 'dir' );
// Delegate key processing to block.
if ( this._.onKeyDown && this._.onKeyDown( keystroke ) === false )
{
evt.data.preventDefault();
return;
}
// ESC/ARROW-LEFT(ltr) OR ARROW-RIGHT(rtl)
if ( keystroke == 27 || keystroke == ( dir == 'rtl' ? 39 : 37 ) )
{
if ( this.onEscape && this.onEscape( keystroke ) === false )
evt.data.preventDefault();
}
},
this );
holder = doc.getBody();
holder.unselectable();
CKEDITOR.env.air && CKEDITOR.tools.callFunction( onLoad );
}
else
holder = this.document.getById( this.id );
this._.holder = holder;
}
return holder;
},
addBlock : function( name, block )
{
block = this._.blocks[ name ] = block instanceof CKEDITOR.ui.panel.block ? block
: new CKEDITOR.ui.panel.block( this.getHolderElement(), block );
if ( !this._.currentBlock )
this.showBlock( name );
return block;
},
getBlock : function( name )
{
return this._.blocks[ name ];
},
showBlock : function( name )
{
var blocks = this._.blocks,
block = blocks[ name ],
current = this._.currentBlock,
holder = this.forceIFrame ?
this.document.getById( this.id + '_frame' )
: this._.holder;
// Disable context menu for block panel.
holder.getParent().getParent().disableContextMenu();
if ( current )
{
// Clean up the current block's effects on holder.
holder.removeAttributes( current.attributes );
current.hide();
}
this._.currentBlock = block;
holder.setAttributes( block.attributes );
CKEDITOR.fire( 'ariaWidget', holder );
// Reset the focus index, so it will always go into the first one.
block._.focusIndex = -1;
this._.onKeyDown = block.onKeyDown && CKEDITOR.tools.bind( block.onKeyDown, block );
block.onMark = function( item )
{
holder.setAttribute( 'aria-activedescendant', item.getId() + '_option' );
};
block.onUnmark = function()
{
holder.removeAttribute( 'aria-activedescendant' );
};
block.show();
return block;
},
destroy : function()
{
this.element && this.element.remove();
}
};
CKEDITOR.ui.panel.block = CKEDITOR.tools.createClass(
{
$ : function( blockHolder, blockDefinition )
{
this.element = blockHolder.append(
blockHolder.getDocument().createElement( 'div',
{
attributes :
{
'tabIndex' : -1,
'class' : 'cke_panel_block',
'role' : 'presentation'
},
styles :
{
display : 'none'
}
}) );
// Copy all definition properties to this object.
if ( blockDefinition )
CKEDITOR.tools.extend( this, blockDefinition );
if ( !this.attributes.title )
this.attributes.title = this.attributes[ 'aria-label' ];
this.keys = {};
this._.focusIndex = -1;
// Disable context menu for panels.
this.element.disableContextMenu();
},
_ : {
/**
* Mark the item specified by the index as current activated.
*/
markItem: function( index )
{
if ( index == -1 )
return;
var links = this.element.getElementsByTag( 'a' );
var item = links.getItem( this._.focusIndex = index );
// Safari need focus on the iframe window first(#3389), but we need
// lock the blur to avoid hiding the panel.
if ( CKEDITOR.env.webkit || CKEDITOR.env.opera )
item.getDocument().getWindow().focus();
item.focus();
this.onMark && this.onMark( item );
}
},
proto :
{
show : function()
{
this.element.setStyle( 'display', '' );
},
hide : function()
{
if ( !this.onHide || this.onHide.call( this ) !== true )
this.element.setStyle( 'display', 'none' );
},
onKeyDown : function( keystroke )
{
var keyAction = this.keys[ keystroke ];
switch ( keyAction )
{
// Move forward.
case 'next' :
var index = this._.focusIndex,
links = this.element.getElementsByTag( 'a' ),
link;
while ( ( link = links.getItem( ++index ) ) )
{
// Move the focus only if the element is marked with
// the _cke_focus and it it's visible (check if it has
// width).
if ( link.getAttribute( '_cke_focus' ) && link.$.offsetWidth )
{
this._.focusIndex = index;
link.focus();
break;
}
}
return false;
// Move backward.
case 'prev' :
index = this._.focusIndex;
links = this.element.getElementsByTag( 'a' );
while ( index > 0 && ( link = links.getItem( --index ) ) )
{
// Move the focus only if the element is marked with
// the _cke_focus and it it's visible (check if it has
// width).
if ( link.getAttribute( '_cke_focus' ) && link.$.offsetWidth )
{
this._.focusIndex = index;
link.focus();
break;
}
}
return false;
case 'click' :
index = this._.focusIndex;
link = index >= 0 && this.element.getElementsByTag( 'a' ).getItem( index );
if ( link )
link.$.click ? link.$.click() : link.$.onclick();
return false;
}
return true;
}
}
});

View File

@@ -0,0 +1,146 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.add( 'panelbutton',
{
requires : [ 'button' ],
beforeInit : function( editor )
{
editor.ui.addHandler( CKEDITOR.UI_PANELBUTTON, CKEDITOR.ui.panelButton.handler );
}
});
/**
* Button UI element.
* @constant
* @example
*/
CKEDITOR.UI_PANELBUTTON = 4;
(function()
{
var clickFn = function( editor )
{
var _ = this._;
if ( _.state == CKEDITOR.TRISTATE_DISABLED )
return;
this.createPanel( editor );
if ( _.on )
{
_.panel.hide();
return;
}
_.panel.showBlock( this._.id, this.document.getById( this._.id ), 4 );
};
CKEDITOR.ui.panelButton = CKEDITOR.tools.createClass(
{
base : CKEDITOR.ui.button,
$ : function( definition )
{
// We don't want the panel definition in this object.
var panelDefinition = definition.panel;
delete definition.panel;
this.base( definition );
this.document = ( panelDefinition
&& panelDefinition.parent
&& panelDefinition.parent.getDocument() )
|| CKEDITOR.document;
panelDefinition.block =
{
attributes : panelDefinition.attributes
};
this.hasArrow = true;
this.click = clickFn;
this._ =
{
panelDefinition : panelDefinition
};
},
statics :
{
handler :
{
create : function( definition )
{
return new CKEDITOR.ui.panelButton( definition );
}
}
},
proto :
{
createPanel : function( editor )
{
var _ = this._;
if ( _.panel )
return;
var panelDefinition = this._.panelDefinition || {},
panelBlockDefinition = this._.panelDefinition.block,
panelParentElement = panelDefinition.parent || CKEDITOR.document.getBody(),
panel = this._.panel = new CKEDITOR.ui.floatPanel( editor, panelParentElement, panelDefinition ),
block = panel.addBlock( _.id, panelBlockDefinition ),
me = this;
panel.onShow = function()
{
if ( me.className )
this.element.getFirst().addClass( me.className + '_panel' );
me.setState( CKEDITOR.TRISTATE_ON );
_.on = 1;
if ( me.onOpen )
me.onOpen();
};
panel.onHide = function( preventOnClose )
{
if ( me.className )
this.element.getFirst().removeClass( me.className + '_panel' );
me.setState( me.modes && me.modes[ editor.mode ] ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED );
_.on = 0;
if ( !preventOnClose && me.onClose )
me.onClose();
};
panel.onEscape = function()
{
panel.hide();
me.document.getById( _.id ).focus();
};
if ( this.onBlock )
this.onBlock( panel, block );
block.onHide = function()
{
_.on = 0;
me.setState( CKEDITOR.TRISTATE_OFF );
};
}
}
});
})();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,122 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
(function()
{
CKEDITOR.plugins.add( 'pastefromword',
{
init : function( editor )
{
// Flag indicate this command is actually been asked instead of a generic
// pasting.
var forceFromWord = 0;
var resetFromWord = function()
{
setTimeout( function() { forceFromWord = 0; }, 0 );
};
// Features bring by this command beside the normal process:
// 1. No more bothering of user about the clean-up.
// 2. Perform the clean-up even if content is not from MS-Word.
// (e.g. from a MS-Word similar application.)
editor.addCommand( 'pastefromword',
{
canUndo : false,
exec : function()
{
forceFromWord = 1;
if ( editor.execCommand( 'paste' ) === false )
{
editor.on( 'dialogHide', function ( evt )
{
evt.removeListener();
resetFromWord();
});
}
else
resetFromWord();
}
});
// Register the toolbar button.
editor.ui.addButton( 'PasteFromWord',
{
label : editor.lang.pastefromword.toolbar,
command : 'pastefromword'
});
editor.on( 'paste', function( evt )
{
var data = evt.data,
mswordHtml;
// MS-WORD format sniffing.
if ( ( mswordHtml = data[ 'html' ] )
&& ( forceFromWord || ( /(class=\"?Mso|style=\"[^\"]*\bmso\-|w:WordDocument)/ ).test( mswordHtml ) ) )
{
var isLazyLoad = this.loadFilterRules( function()
{
// Event continuation with the original data.
if ( isLazyLoad )
editor.fire( 'paste', data );
else if ( !editor.config.pasteFromWordPromptCleanup
|| ( forceFromWord || confirm( editor.lang.pastefromword.confirmCleanup ) ) )
{
data[ 'html' ] = CKEDITOR.cleanWord( mswordHtml, editor );
}
});
// The cleanup rules are to be loaded, we should just cancel
// this event.
isLazyLoad && evt.cancel();
}
}, this );
},
loadFilterRules : function( callback )
{
var isLoaded = CKEDITOR.cleanWord;
if ( isLoaded )
callback();
else
{
var filterFilePath = CKEDITOR.getUrl(
CKEDITOR.config.pasteFromWordCleanupFile
|| ( this.path + 'filter/default.js' ) );
// Load with busy indicator.
CKEDITOR.scriptLoader.load( filterFilePath, callback, null, false, true );
}
return !isLoaded;
}
});
})();
/**
* Whether to prompt the user about the clean up of content being pasted from
* MS Word.
* @name CKEDITOR.config.pasteFromWordPromptCleanup
* @since 3.1
* @type Boolean
* @default undefined
* @example
* config.pasteFromWordPromptCleanup = true;
*/
/**
* The file that provides the MS Word cleanup function for pasting operations.
* Note: This is a global configuration shared by all editor instances present
* in the page.
* @name CKEDITOR.config.pasteFromWordCleanupFile
* @since 3.1
* @type String
* @default 'default'
* @example
* // Load from 'pastefromword' plugin 'filter' sub folder (custom.js file).
* CKEDITOR.config.pasteFromWordCleanupFile = 'custom';
*/

View File

@@ -0,0 +1,70 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
(function()
{
CKEDITOR.dialog.add( 'pastetext', function( editor )
{
return {
title : editor.lang.pasteText.title,
minWidth : CKEDITOR.env.ie && CKEDITOR.env.quirks ? 368 : 350,
minHeight : 240,
onShow : function()
{
// Reset the textarea value.
this.getContentElement( 'general', 'content' ).getInputElement().setValue( '' );
},
onOk : function()
{
// Get the textarea value.
var text = this.getContentElement( 'general', 'content' ).getInputElement().getValue(),
editor = this.getParentEditor();
setTimeout( function()
{
editor.fire( 'paste', { 'text' : text } );
}, 0 );
},
contents :
[
{
label : editor.lang.common.generalTab,
id : 'general',
elements :
[
{
type : 'html',
id : 'pasteMsg',
html : '<div style="white-space:normal;width:340px;">' + editor.lang.clipboard.pasteMsg + '</div>'
},
{
type : 'textarea',
id : 'content',
className : 'cke_pastetext',
onLoad : function()
{
var label = this.getDialog().getContentElement( 'general', 'pasteMsg' ).getElement(),
input = this.getElement().getElementsByTag( 'textarea' ).getItem( 0 );
input.setAttribute( 'aria-labelledby', label.$.id );
input.setStyle( 'direction', editor.config.contentsLangDirection );
},
focus : function()
{
this.getElement().focus();
}
}
]
}
]
};
});
})();

View File

@@ -0,0 +1,85 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
/**
* @file Paste as plain text plugin
*/
(function()
{
// The pastetext command definition.
var pasteTextCmd =
{
exec : function( editor )
{
var clipboardText = CKEDITOR.tools.tryThese(
function()
{
var clipboardText = window.clipboardData.getData( 'Text' );
if ( !clipboardText )
throw 0;
return clipboardText;
}
// Any other approach that's working...
);
if ( !clipboardText ) // Clipboard access privilege is not granted.
{
editor.openDialog( 'pastetext' );
return false;
}
else
editor.fire( 'paste', { 'text' : clipboardText } );
return true;
}
};
// Register the plugin.
CKEDITOR.plugins.add( 'pastetext',
{
init : function( editor )
{
var commandName = 'pastetext',
command = editor.addCommand( commandName, pasteTextCmd );
editor.ui.addButton( 'PasteText',
{
label : editor.lang.pasteText.button,
command : commandName
});
CKEDITOR.dialog.add( commandName, CKEDITOR.getUrl( this.path + 'dialogs/pastetext.js' ) );
if ( editor.config.forcePasteAsPlainText )
{
// Intercept the default pasting process.
editor.on( 'beforeCommandExec', function ( evt )
{
if ( evt.data.name == 'paste' )
{
editor.execCommand( 'pastetext' );
evt.cancel();
}
}, null, null, 0 );
}
},
requires : [ 'clipboard' ]
});
})();
/**
* Whether to force all pasting operations to insert on plain text into the
* editor, loosing any formatting information possibly available in the source
* text.
* @name CKEDITOR.config.forcePasteAsPlainText
* @type Boolean
* @default false
* @example
* config.forcePasteAsPlainText = true;
*/

View File

@@ -0,0 +1,78 @@
/*
* Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.html or http://ckeditor.com/license
*/
(function()
{
function placeholderDialog( editor, isEdit )
{
var lang = editor.lang.placeholder,
generalLabel = editor.lang.common.generalTab;
return {
title : lang.title,
minWidth : 300,
minHeight : 80,
contents :
[
{
id : 'info',
label : generalLabel,
title : generalLabel,
elements :
[
{
id : 'text',
type : 'text',
style : 'width: 100%;',
label : lang.text,
'default' : '',
required : true,
validate : CKEDITOR.dialog.validate.notEmpty( lang.textMissing ),
setup : function( element )
{
if ( isEdit )
this.setValue( element.getText().slice( 2, -2 ) );
},
commit : function( element )
{
var text = '[[' + this.getValue() + ']]';
// The placeholder must be recreated.
CKEDITOR.plugins.placeholder.createPlaceholder( editor, element, text );
}
}
]
}
],
onShow : function()
{
if ( isEdit )
{
var range = editor.getSelection().getRanges()[0];
range.shrink( CKEDITOR.SHRINK_TEXT );
var node = range.startContainer;
while( node && !( node.type == CKEDITOR.NODE_ELEMENT && node.data( 'cke-placeholder' ) ) )
node = node.getParent();
this._element = node;
}
this.setupContent( this._element );
},
onOk : function()
{
this.commitContent( this._element );
delete this._element;
}
};
}
CKEDITOR.dialog.add( 'createplaceholder', function( editor )
{
return placeholderDialog( editor );
});
CKEDITOR.dialog.add( 'editplaceholder', function( editor )
{
return placeholderDialog( editor, 1 );
});
} )();

View File

@@ -0,0 +1,16 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.setLang( 'placeholder', 'en',
{
placeholder :
{
title : 'Placeholder Properties',
toolbar : 'Create Placeholder',
text : 'Placeholder Text',
edit : 'Edit Placeholder',
textMissing : 'The placeholder must contain text.'
}
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 B

View File

@@ -0,0 +1,160 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
/**
* @fileOverview The "placeholder" plugin.
*
*/
(function()
{
var placeholderReplaceRegex = /\[\[[^\]]+\]\]/g;
CKEDITOR.plugins.add( 'placeholder',
{
requires : [ 'dialog' ],
lang : [ 'en' ],
init : function( editor )
{
var lang = editor.lang.placeholder;
editor.addCommand( 'createplaceholder', new CKEDITOR.dialogCommand( 'createplaceholder' ) );
editor.addCommand( 'editplaceholder', new CKEDITOR.dialogCommand( 'editplaceholder' ) );
editor.ui.addButton( 'CreatePlaceholder',
{
label : lang.toolbar,
command :'createplaceholder',
icon : this.path + 'placeholder.gif'
});
if ( editor.addMenuItems )
{
editor.addMenuGroup( 'placeholder', 20 );
editor.addMenuItems(
{
editplaceholder :
{
label : lang.edit,
command : 'editplaceholder',
group : 'placeholder',
order : 1,
icon : this.path + 'placeholder.gif'
}
} );
if ( editor.contextMenu )
{
editor.contextMenu.addListener( function( element, selection )
{
if ( !element || !element.data( 'cke-placeholder' ) )
return null;
return { editplaceholder : CKEDITOR.TRISTATE_OFF };
} );
}
}
editor.on( 'doubleclick', function( evt )
{
var element = evt.data.element;
if ( element.data( 'cke-placeholder' ) )
evt.data.dialog = 'editplaceholder';
});
editor.addCss(
'.cke_placeholder' +
'{' +
'background-color: #ffff00;' +
( CKEDITOR.env.gecko ? 'cursor: default;' : '' ) +
'}'
);
editor.on( 'contentDom', function()
{
editor.document.getBody().on( 'resizestart', function( evt )
{
if ( editor.getSelection().getSelectedElement().data( 'cke-placeholder' ) )
evt.data.preventDefault();
});
});
CKEDITOR.dialog.add( 'createplaceholder', this.path + 'dialogs/placeholder.js' );
CKEDITOR.dialog.add( 'editplaceholder', this.path + 'dialogs/placeholder.js' );
},
afterInit : function( editor )
{
var dataProcessor = editor.dataProcessor,
dataFilter = dataProcessor && dataProcessor.dataFilter,
htmlFilter = dataProcessor && dataProcessor.htmlFilter;
if ( dataFilter )
{
dataFilter.addRules(
{
text : function( text )
{
return text.replace( placeholderReplaceRegex, function( match )
{
return CKEDITOR.plugins.placeholder.createPlaceholder( editor, null, match, 1 );
});
}
});
}
if ( htmlFilter )
{
htmlFilter.addRules(
{
elements :
{
'span' : function( element )
{
if ( element.attributes && element.attributes[ 'data-cke-placeholder' ] )
delete element.name;
}
}
});
}
}
});
})();
CKEDITOR.plugins.placeholder =
{
createPlaceholder : function( editor, oldElement, text, isGet )
{
var element = new CKEDITOR.dom.element( 'span', editor.document );
element.setAttributes(
{
contentEditable : 'false',
'data-cke-placeholder' : 1,
'class' : 'cke_placeholder'
}
);
text && element.setText( text );
if ( isGet )
return element.getOuterHtml();
if ( oldElement )
{
if ( CKEDITOR.env.ie )
{
element.insertAfter( oldElement );
// Some time is required for IE before the element is removed.
setTimeout( function()
{
oldElement.remove();
element.focus();
}, 10 );
}
else
element.replace( oldElement );
}
else
editor.insertElement( element );
}
};

View File

@@ -0,0 +1,64 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.add( 'popup' );
CKEDITOR.tools.extend( CKEDITOR.editor.prototype,
{
/**
* Opens Browser in a popup. The "width" and "height" parameters accept
* numbers (pixels) or percent (of screen size) values.
* @param {String} url The url of the external file browser.
* @param {String} width Popup window width.
* @param {String} height Popup window height.
* @param {String} options Popup window features.
*/
popup : function( url, width, height, options )
{
width = width || '80%';
height = height || '70%';
if ( typeof width == 'string' && width.length > 1 && width.substr( width.length - 1, 1 ) == '%' )
width = parseInt( window.screen.width * parseInt( width, 10 ) / 100, 10 );
if ( typeof height == 'string' && height.length > 1 && height.substr( height.length - 1, 1 ) == '%' )
height = parseInt( window.screen.height * parseInt( height, 10 ) / 100, 10 );
if ( width < 640 )
width = 640;
if ( height < 420 )
height = 420;
var top = parseInt( ( window.screen.height - height ) / 2, 10 ),
left = parseInt( ( window.screen.width - width ) / 2, 10 );
options = ( options || 'location=no,menubar=no,toolbar=no,dependent=yes,minimizable=no,modal=yes,alwaysRaised=yes,resizable=yes,scrollbars=yes' ) +
',width=' + width +
',height=' + height +
',top=' + top +
',left=' + left;
var popupWindow = window.open( '', null, options, true );
// Blocked by a popup blocker.
if ( !popupWindow )
return false;
try
{
popupWindow.moveTo( left, top );
popupWindow.resizeTo( width, height );
popupWindow.focus();
popupWindow.location.href = url;
}
catch ( e )
{
popupWindow = window.open( url, null, options, true );
}
return true;
}
});

View File

@@ -0,0 +1,108 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
/**
* @file Preview plugin.
*/
(function()
{
var previewCmd =
{
modes : { wysiwyg:1, source:1 },
canUndo : false,
exec : function( editor )
{
var sHTML,
config = editor.config,
baseTag = config.baseHref ? '<base href="' + config.baseHref + '"/>' : '',
isCustomDomain = CKEDITOR.env.isCustomDomain();
if ( config.fullPage )
{
sHTML = editor.getData()
.replace( /<head>/, '$&' + baseTag )
.replace( /[^>]*(?=<\/title>)/, editor.lang.preview );
}
else
{
var bodyHtml = '<body ',
body = editor.document && editor.document.getBody();
if ( body )
{
if ( body.getAttribute( 'id' ) )
bodyHtml += 'id="' + body.getAttribute( 'id' ) + '" ';
if ( body.getAttribute( 'class' ) )
bodyHtml += 'class="' + body.getAttribute( 'class' ) + '" ';
}
bodyHtml += '>';
sHTML =
editor.config.docType +
'<html dir="' + editor.config.contentsLangDirection + '">' +
'<head>' +
baseTag +
'<title>' + editor.lang.preview + '</title>' +
CKEDITOR.tools.buildStyleHtml( editor.config.contentsCss ) +
'</head>' + bodyHtml +
editor.getData() +
'</body></html>';
}
var iWidth = 640, // 800 * 0.8,
iHeight = 420, // 600 * 0.7,
iLeft = 80; // (800 - 0.8 * 800) /2 = 800 * 0.1.
try
{
var screen = window.screen;
iWidth = Math.round( screen.width * 0.8 );
iHeight = Math.round( screen.height * 0.7 );
iLeft = Math.round( screen.width * 0.1 );
}
catch ( e ){}
var sOpenUrl = '';
if ( isCustomDomain )
{
window._cke_htmlToLoad = sHTML;
sOpenUrl = 'javascript:void( (function(){' +
'document.open();' +
'document.domain="' + document.domain + '";' +
'document.write( window.opener._cke_htmlToLoad );' +
'document.close();' +
'window.opener._cke_htmlToLoad = null;' +
'})() )';
}
var oWindow = window.open( sOpenUrl, null, 'toolbar=yes,location=no,status=yes,menubar=yes,scrollbars=yes,resizable=yes,width=' +
iWidth + ',height=' + iHeight + ',left=' + iLeft );
if ( !isCustomDomain )
{
oWindow.document.open();
oWindow.document.write( sHTML );
oWindow.document.close();
}
}
};
var pluginName = 'preview';
// Register a plugin named "preview".
CKEDITOR.plugins.add( pluginName,
{
init : function( editor )
{
editor.addCommand( pluginName, previewCmd );
editor.ui.addButton( 'Preview',
{
label : editor.lang.preview,
command : pluginName
});
}
});
})();

View File

@@ -0,0 +1,41 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
/**
* @file Print Plugin
*/
CKEDITOR.plugins.add( 'print',
{
init : function( editor )
{
var pluginName = 'print';
// Register the command.
var command = editor.addCommand( pluginName, CKEDITOR.plugins.print );
// Register the toolbar button.
editor.ui.addButton( 'Print',
{
label : editor.lang.print,
command : pluginName
});
}
} );
CKEDITOR.plugins.print =
{
exec : function( editor )
{
if ( CKEDITOR.env.opera )
return;
else if ( CKEDITOR.env.gecko )
editor.window.$.print();
else
editor.document.$.execCommand( "Print" );
},
canUndo : false,
modes : { wysiwyg : !( CKEDITOR.env.opera ) } // It is imposible to print the inner document in Opera.
};

View File

@@ -0,0 +1,184 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.add( 'removeformat',
{
requires : [ 'selection' ],
init : function( editor )
{
editor.addCommand( 'removeFormat', CKEDITOR.plugins.removeformat.commands.removeformat );
editor.ui.addButton( 'RemoveFormat',
{
label : editor.lang.removeFormat,
command : 'removeFormat'
});
editor._.removeFormat = { filters: [] };
}
});
CKEDITOR.plugins.removeformat =
{
commands :
{
removeformat :
{
exec : function( editor )
{
var tagsRegex = editor._.removeFormatRegex ||
( editor._.removeFormatRegex = new RegExp( '^(?:' + editor.config.removeFormatTags.replace( /,/g,'|' ) + ')$', 'i' ) );
var removeAttributes = editor._.removeAttributes ||
( editor._.removeAttributes = editor.config.removeFormatAttributes.split( ',' ) );
var filter = CKEDITOR.plugins.removeformat.filter;
var ranges = editor.getSelection().getRanges( 1 ),
iterator = ranges.createIterator(),
range;
while ( ( range = iterator.getNextRange() ) )
{
if ( range.collapsed )
continue;
range.enlarge( CKEDITOR.ENLARGE_ELEMENT );
// Bookmark the range so we can re-select it after processing.
var bookmark = range.createBookmark();
// The style will be applied within the bookmark boundaries.
var startNode = bookmark.startNode,
endNode = bookmark.endNode;
// We need to check the selection boundaries (bookmark spans) to break
// the code in a way that we can properly remove partially selected nodes.
// For example, removing a <b> style from
// <b>This is [some text</b> to show <b>the] problem</b>
// ... where [ and ] represent the selection, must result:
// <b>This is </b>[some text to show the]<b> problem</b>
// The strategy is simple, we just break the partial nodes before the
// removal logic, having something that could be represented this way:
// <b>This is </b>[<b>some text</b> to show <b>the</b>]<b> problem</b>
var breakParent = function( node )
{
// Let's start checking the start boundary.
var path = new CKEDITOR.dom.elementPath( node ),
pathElements = path.elements;
for ( var i = 1, pathElement ; pathElement = pathElements[ i ] ; i++ )
{
if ( pathElement.equals( path.block ) || pathElement.equals( path.blockLimit ) )
break;
// If this element can be removed (even partially).
if ( tagsRegex.test( pathElement.getName() ) && filter( editor, pathElement ) )
node.breakParent( pathElement );
}
};
breakParent( startNode );
breakParent( endNode );
// Navigate through all nodes between the bookmarks.
var currentNode = startNode.getNextSourceNode( true, CKEDITOR.NODE_ELEMENT );
while ( currentNode )
{
// If we have reached the end of the selection, stop looping.
if ( currentNode.equals( endNode ) )
break;
// Cache the next node to be processed. Do it now, because
// currentNode may be removed.
var nextNode = currentNode.getNextSourceNode( false, CKEDITOR.NODE_ELEMENT );
// This node must not be a fake element.
if ( !( currentNode.getName() == 'img'
&& currentNode.data( 'cke-realelement' ) )
&& filter( editor, currentNode ) )
{
// Remove elements nodes that match with this style rules.
if ( tagsRegex.test( currentNode.getName() ) )
currentNode.remove( 1 );
else
{
currentNode.removeAttributes( removeAttributes );
editor.fire( 'removeFormatCleanup', currentNode );
}
}
currentNode = nextNode;
}
range.moveToBookmark( bookmark );
}
editor.getSelection().selectRanges( ranges );
}
}
},
/**
* Perform the remove format filters on the passed element.
* @param {CKEDITOR.editor} editor
* @param {CKEDITOR.dom.element} element
*/
filter : function ( editor, element )
{
var filters = editor._.removeFormat.filters;
for ( var i = 0; i < filters.length; i++ )
{
if ( filters[ i ]( element ) === false )
return false;
}
return true;
}
};
/**
* Add to a collection of functions to decide whether a specific
* element should be considered as formatting element and thus
* could be removed during <b>removeFormat</b> command,
* Note: Only available with the existence of 'removeformat' plugin.
* @since 3.3
* @param {Function} func The function to be called, which will be passed a {CKEDITOR.dom.element} element to test.
* @example
* // Don't remove empty span
* editor.addRemoveFormatFilter.push( function( element )
* {
* return !( element.is( 'span' ) && CKEDITOR.tools.isEmpty( element.getAttributes() ) );
* });
*/
CKEDITOR.editor.prototype.addRemoveFormatFilter = function( func )
{
this._.removeFormat.filters.push( func );
};
/**
* A comma separated list of elements to be removed when executing the "remove
" format" command. Note that only inline elements are allowed.
* @type String
* @default 'b,big,code,del,dfn,em,font,i,ins,kbd,q,samp,small,span,strike,strong,sub,sup,tt,u,var'
* @example
*/
CKEDITOR.config.removeFormatTags = 'b,big,code,del,dfn,em,font,i,ins,kbd,q,samp,small,span,strike,strong,sub,sup,tt,u,var';
/**
* A comma separated list of elements attributes to be removed when executing
* the "remove format" command.
* @type String
* @default 'class,style,lang,width,height,align,hspace,valign'
* @example
*/
CKEDITOR.config.removeFormatAttributes = 'class,style,lang,width,height,align,hspace,valign';
/**
* Fired after an element was cleaned by the removeFormat plugin.
* @name CKEDITOR#removeFormatCleanup
* @event
* @param {Object} data.element The element that was cleaned up.
*/

View File

@@ -0,0 +1,157 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.add( 'resize',
{
init : function( editor )
{
var config = editor.config;
!config.resize_dir && ( config.resize_dir = 'both' );
( config.resize_maxWidth == undefined ) && ( config.resize_maxWidth = 3000 );
( config.resize_maxHeight == undefined ) && ( config.resize_maxHeight = 3000 );
( config.resize_minWidth == undefined ) && ( config.resize_minWidth = 750 );
( config.resize_minHeight == undefined ) && ( config.resize_minHeight = 250 );
if ( config.resize_enabled !== false )
{
var container = null,
origin,
startSize,
resizeHorizontal = ( config.resize_dir == 'both' || config.resize_dir == 'horizontal' ) &&
( config.resize_minWidth != config.resize_maxWidth ),
resizeVertical = ( config.resize_dir == 'both' || config.resize_dir == 'vertical' ) &&
( config.resize_minHeight != config.resize_maxHeight );
function dragHandler( evt )
{
var dx = evt.data.$.screenX - origin.x,
dy = evt.data.$.screenY - origin.y,
width = startSize.width,
height = startSize.height,
internalWidth = width + dx * ( editor.lang.dir == 'rtl' ? -1 : 1 ),
internalHeight = height + dy;
if ( resizeHorizontal )
width = Math.max( config.resize_minWidth, Math.min( internalWidth, config.resize_maxWidth ) );
if ( resizeVertical )
height = Math.max( config.resize_minHeight, Math.min( internalHeight, config.resize_maxHeight ) );
editor.resize( width, height );
}
function dragEndHandler ( evt )
{
CKEDITOR.document.removeListener( 'mousemove', dragHandler );
CKEDITOR.document.removeListener( 'mouseup', dragEndHandler );
if ( editor.document )
{
editor.document.removeListener( 'mousemove', dragHandler );
editor.document.removeListener( 'mouseup', dragEndHandler );
}
}
var mouseDownFn = CKEDITOR.tools.addFunction( function( $event )
{
if ( !container )
container = editor.getResizable();
startSize = { width : container.$.offsetWidth || 0, height : container.$.offsetHeight || 0 };
origin = { x : $event.screenX, y : $event.screenY };
config.resize_minWidth > startSize.width && ( config.resize_minWidth = startSize.width );
config.resize_minHeight > startSize.height && ( config.resize_minHeight = startSize.height );
CKEDITOR.document.on( 'mousemove', dragHandler );
CKEDITOR.document.on( 'mouseup', dragEndHandler );
if ( editor.document )
{
editor.document.on( 'mousemove', dragHandler );
editor.document.on( 'mouseup', dragEndHandler );
}
});
editor.on( 'destroy', function() { CKEDITOR.tools.removeFunction( mouseDownFn ); } );
editor.on( 'themeSpace', function( event )
{
if ( event.data.space == 'bottom' )
{
var direction = '';
if ( resizeHorizontal && !resizeVertical )
direction = ' cke_resizer_horizontal';
if ( !resizeHorizontal && resizeVertical )
direction = ' cke_resizer_vertical';
event.data.html += '<div class="cke_resizer' + direction + '"' +
' title="' + CKEDITOR.tools.htmlEncode( editor.lang.resize ) + '"' +
' onmousedown="CKEDITOR.tools.callFunction(' + mouseDownFn + ', event)"' +
'></div>';
}
}, editor, null, 100 );
}
}
} );
/**
* The minimum editor width, in pixels, when resizing it with the resize handle.
* Note: It fallbacks to editor's actual width if that's smaller than the default value.
* @name CKEDITOR.config.resize_minWidth
* @type Number
* @default 750
* @example
* config.resize_minWidth = 500;
*/
/**
* The minimum editor height, in pixels, when resizing it with the resize handle.
* Note: It fallbacks to editor's actual height if that's smaller than the default value.
* @name CKEDITOR.config.resize_minHeight
* @type Number
* @default 250
* @example
* config.resize_minHeight = 600;
*/
/**
* The maximum editor width, in pixels, when resizing it with the resize handle.
* @name CKEDITOR.config.resize_maxWidth
* @type Number
* @default 3000
* @example
* config.resize_maxWidth = 750;
*/
/**
* The maximum editor height, in pixels, when resizing it with the resize handle.
* @name CKEDITOR.config.resize_maxHeight
* @type Number
* @default 3000
* @example
* config.resize_maxHeight = 600;
*/
/**
* Whether to enable the resizing feature. If disabled the resize handler will not be visible.
* @name CKEDITOR.config.resize_enabled
* @type Boolean
* @default true
* @example
* config.resize_enabled = false;
*/
/**
* The directions to which the editor resizing is enabled. Possible values
* are "both", "vertical" and "horizontal".
* @name CKEDITOR.config.resize_dir
* @type String
* @default 'both'
* @since 3.3
* @example
* config.resize_dir = 'vertical';
*/

View File

@@ -0,0 +1,371 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.add( 'richcombo',
{
requires : [ 'floatpanel', 'listblock', 'button' ],
beforeInit : function( editor )
{
editor.ui.addHandler( CKEDITOR.UI_RICHCOMBO, CKEDITOR.ui.richCombo.handler );
}
});
/**
* Button UI element.
* @constant
* @example
*/
CKEDITOR.UI_RICHCOMBO = 3;
CKEDITOR.ui.richCombo = CKEDITOR.tools.createClass(
{
$ : function( definition )
{
// Copy all definition properties to this object.
CKEDITOR.tools.extend( this, definition,
// Set defaults.
{
title : definition.label,
modes : { wysiwyg : 1 }
});
// We don't want the panel definition in this object.
var panelDefinition = this.panel || {};
delete this.panel;
this.id = CKEDITOR.tools.getNextNumber();
this.document = ( panelDefinition
&& panelDefinition.parent
&& panelDefinition.parent.getDocument() )
|| CKEDITOR.document;
panelDefinition.className = ( panelDefinition.className || '' ) + ' cke_rcombopanel';
panelDefinition.block =
{
multiSelect : panelDefinition.multiSelect,
attributes : panelDefinition.attributes
};
this._ =
{
panelDefinition : panelDefinition,
items : {},
state : CKEDITOR.TRISTATE_OFF
};
},
statics :
{
handler :
{
create : function( definition )
{
return new CKEDITOR.ui.richCombo( definition );
}
}
},
proto :
{
renderHtml : function( editor )
{
var output = [];
this.render( editor, output );
return output.join( '' );
},
/**
* Renders the combo.
* @param {CKEDITOR.editor} editor The editor instance which this button is
* to be used by.
* @param {Array} output The output array to which append the HTML relative
* to this button.
* @example
*/
render : function( editor, output )
{
var env = CKEDITOR.env;
var id = 'cke_' + this.id;
var clickFn = CKEDITOR.tools.addFunction( function( $element )
{
var _ = this._;
if ( _.state == CKEDITOR.TRISTATE_DISABLED )
return;
this.createPanel( editor );
if ( _.on )
{
_.panel.hide();
return;
}
this.commit();
var value = this.getValue();
if ( value )
_.list.mark( value );
else
_.list.unmarkAll();
_.panel.showBlock( this.id, new CKEDITOR.dom.element( $element ), 4 );
},
this );
var instance = {
id : id,
combo : this,
focus : function()
{
var element = CKEDITOR.document.getById( id ).getChild( 1 );
element.focus();
},
clickFn : clickFn
};
editor.on( 'mode', function()
{
this.setState( this.modes[ editor.mode ] ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED );
},
this );
var keyDownFn = CKEDITOR.tools.addFunction( function( ev, element )
{
ev = new CKEDITOR.dom.event( ev );
var keystroke = ev.getKeystroke();
switch ( keystroke )
{
case 13 : // ENTER
case 32 : // SPACE
case 40 : // ARROW-DOWN
// Show panel
CKEDITOR.tools.callFunction( clickFn, element );
break;
default :
// Delegate the default behavior to toolbar button key handling.
instance.onkey( instance, keystroke );
}
// Avoid subsequent focus grab on editor document.
ev.preventDefault();
});
// For clean up
instance.keyDownFn = keyDownFn;
output.push(
'<span class="cke_rcombo">',
'<span id=', id );
if ( this.className )
output.push( ' class="', this.className, ' cke_off"');
output.push(
'>',
'<span id="' + id+ '_label" class=cke_label>', this.label, '</span>',
'<a hidefocus=true title="', this.title, '" tabindex="-1"',
env.gecko && env.version >= 10900 && !env.hc ? '' : ' href="javascript:void(\'' + this.label + '\')"',
' role="button" aria-labelledby="', id , '_label" aria-describedby="', id, '_text" aria-haspopup="true"' );
// Some browsers don't cancel key events in the keydown but in the
// keypress.
// TODO: Check if really needed for Gecko+Mac.
if ( CKEDITOR.env.opera || ( CKEDITOR.env.gecko && CKEDITOR.env.mac ) )
{
output.push(
' onkeypress="return false;"' );
}
// With Firefox, we need to force it to redraw, otherwise it
// will remain in the focus state.
if ( CKEDITOR.env.gecko )
{
output.push(
' onblur="this.style.cssText = this.style.cssText;"' );
}
output.push(
' onkeydown="CKEDITOR.tools.callFunction( ', keyDownFn, ', event, this );"' +
' onclick="CKEDITOR.tools.callFunction(', clickFn, ', this); return false;">' +
'<span>' +
'<span id="' + id + '_text" class="cke_text cke_inline_label">' + this.label + '</span>' +
'</span>' +
'<span class=cke_openbutton>' + ( CKEDITOR.env.hc ? '<span>&#9660;</span>' : CKEDITOR.env.air ? '&nbsp;' : '' ) + '</span>' + // BLACK DOWN-POINTING TRIANGLE
'</a>' +
'</span>' +
'</span>' );
if ( this.onRender )
this.onRender();
return instance;
},
createPanel : function( editor )
{
if ( this._.panel )
return;
var panelDefinition = this._.panelDefinition,
panelBlockDefinition = this._.panelDefinition.block,
panelParentElement = panelDefinition.parent || CKEDITOR.document.getBody(),
panel = new CKEDITOR.ui.floatPanel( editor, panelParentElement, panelDefinition ),
list = panel.addListBlock( this.id, panelBlockDefinition ),
me = this;
panel.onShow = function()
{
if ( me.className )
this.element.getFirst().addClass( me.className + '_panel' );
me.setState( CKEDITOR.TRISTATE_ON );
list.focus( !me.multiSelect && me.getValue() );
me._.on = 1;
if ( me.onOpen )
me.onOpen();
};
panel.onHide = function( preventOnClose )
{
if ( me.className )
this.element.getFirst().removeClass( me.className + '_panel' );
me.setState( me.modes && me.modes[ editor.mode ] ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED );
me._.on = 0;
if ( !preventOnClose && me.onClose )
me.onClose();
};
panel.onEscape = function()
{
panel.hide();
me.document.getById( 'cke_' + me.id ).getFirst().getNext().focus();
};
list.onClick = function( value, marked )
{
// Move the focus to the main windows, otherwise it will stay
// into the floating panel, even if invisible, and Safari and
// Opera will go a bit crazy.
me.document.getWindow().focus();
if ( me.onClick )
me.onClick.call( me, value, marked );
if ( marked )
me.setValue( value, me._.items[ value ] );
else
me.setValue( '' );
panel.hide();
};
this._.panel = panel;
this._.list = list;
panel.getBlock( this.id ).onHide = function()
{
me._.on = 0;
me.setState( CKEDITOR.TRISTATE_OFF );
};
if ( this.init )
this.init();
},
setValue : function( value, text )
{
this._.value = value;
var textElement = this.document.getById( 'cke_' + this.id + '_text' );
if ( !( value || text ) )
{
text = this.label;
textElement.addClass( 'cke_inline_label' );
}
else
textElement.removeClass( 'cke_inline_label' );
textElement.setHtml( typeof text != 'undefined' ? text : value );
},
getValue : function()
{
return this._.value || '';
},
unmarkAll : function()
{
this._.list.unmarkAll();
},
mark : function( value )
{
this._.list.mark( value );
},
hideItem : function( value )
{
this._.list.hideItem( value );
},
hideGroup : function( groupTitle )
{
this._.list.hideGroup( groupTitle );
},
showAll : function()
{
this._.list.showAll();
},
add : function( value, html, text )
{
this._.items[ value ] = text || value;
this._.list.add( value, html, text );
},
startGroup : function( title )
{
this._.list.startGroup( title );
},
commit : function()
{
if ( !this._.committed )
{
this._.list.commit();
this._.committed = 1;
CKEDITOR.ui.fire( 'ready', this );
}
this._.committed = 1;
},
setState : function( state )
{
if ( this._.state == state )
return;
this.document.getById( 'cke_' + this.id ).setState( state );
this._.state = state;
}
}
});
CKEDITOR.ui.prototype.addRichCombo = function( name, definition )
{
this.add( name, CKEDITOR.UI_RICHCOMBO, definition );
};

View File

@@ -0,0 +1,55 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
/**
* @fileSave plugin.
*/
(function()
{
var saveCmd =
{
modes : { wysiwyg:1, source:1 },
exec : function( editor )
{
var $form = editor.element.$.form;
if ( $form )
{
try
{
$form.submit();
}
catch( e )
{
// If there's a button named "submit" then the form.submit
// function is masked and can't be called in IE/FF, so we
// call the click() method of that button.
if ( $form.submit.click )
$form.submit.click();
}
}
}
};
var pluginName = 'save';
// Register a plugin named "save".
CKEDITOR.plugins.add( pluginName,
{
init : function( editor )
{
var command = editor.addCommand( pluginName, saveCmd );
command.modes = { wysiwyg : !!( editor.element.$.form ) };
editor.ui.addButton( 'Save',
{
label : editor.lang.save,
command : pluginName
});
}
});
})();

View File

@@ -0,0 +1,534 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.dialog.add( 'scaytcheck', function( editor )
{
var firstLoad = true,
captions,
doc = CKEDITOR.document,
tags = [],
i,
contents = [],
userDicActive = 0,
dic_buttons = [
// [0] contains buttons for creating
"dic_create,dic_restore",
// [1] contains buton for manipulation
"dic_rename,dic_delete"
],
optionsIds = [ 'mixedCase', 'mixedWithDigits', 'allCaps', 'ignoreDomainNames' ];
// common operations
function getBOMAllOptions()
{
return document.forms.optionsbar["options"];
}
function getBOMAllLangs()
{
return document.forms.languagesbar["scayt_lang"];
}
function setCheckedValue( radioObj, newValue )
{
if ( !radioObj )
return;
var radioLength = radioObj.length;
if ( radioLength == undefined )
{
radioObj.checked = radioObj.value == newValue.toString();
return;
}
for ( var i = 0; i < radioLength; i++ )
{
radioObj[i].checked = false;
if ( radioObj[i].value == newValue.toString() )
radioObj[i].checked = true;
}
}
var lang = editor.lang.scayt;
var tags_contents = [
{
id : 'options',
label : lang.optionsTab,
elements : [
{
type : 'html',
id : 'options',
html : '<form name="optionsbar"><div class="inner_options">' +
' <div class="messagebox"></div>' +
' <div style="display:none;">' +
' <input type="checkbox" name="options" id="allCaps" />' +
' <label for="allCaps" id="label_allCaps"></label>' +
' </div>' +
' <div style="display:none;">' +
' <input name="options" type="checkbox" id="ignoreDomainNames" />' +
' <label for="ignoreDomainNames" id="label_ignoreDomainNames"></label>' +
' </div>' +
' <div style="display:none;">' +
' <input name="options" type="checkbox" id="mixedCase" />' +
' <label for="mixedCase" id="label_mixedCase"></label>' +
' </div>' +
' <div style="display:none;">' +
' <input name="options" type="checkbox" id="mixedWithDigits" />' +
' <label for="mixedWithDigits" id="label_mixedWithDigits"></label>' +
' </div>' +
'</div></form>'
}
]
},
{
id : 'langs',
label : lang.languagesTab,
elements : [
{
type : 'html',
id : 'langs',
html : '<form name="languagesbar"><div class="inner_langs">' +
' <div class="messagebox"></div> ' +
' <div style="float:left;width:45%;margin-left:5px;" id="scayt_lcol" ></div>' +
' <div style="float:left;width:45%;margin-left:15px;" id="scayt_rcol"></div>' +
'</div></form>'
}
]
},
{
id : 'dictionaries',
label : lang.dictionariesTab,
elements : [
{
type : 'html',
style: '',
id : 'dictionaries',
html : '<form name="dictionarybar"><div class="inner_dictionary" style="text-align:left; white-space:normal; width:320px; overflow: hidden;">' +
' <div style="margin:5px auto; width:80%;white-space:normal; overflow:hidden;" id="dic_message"> </div>' +
' <div style="margin:5px auto; width:80%;white-space:normal;"> ' +
' <span class="cke_dialog_ui_labeled_label" >Dictionary name</span><br>'+
' <span class="cke_dialog_ui_labeled_content" >'+
' <div class="cke_dialog_ui_input_text">'+
' <input id="dic_name" type="text" class="cke_dialog_ui_input_text"/>'+
' </div></span></div>'+
' <div style="margin:5px auto; width:80%;white-space:normal;">'+
' <a style="display:none;" class="cke_dialog_ui_button" href="javascript:void(0)" id="dic_create">'+
' </a>' +
' <a style="display:none;" class="cke_dialog_ui_button" href="javascript:void(0)" id="dic_delete">'+
' </a>' +
' <a style="display:none;" class="cke_dialog_ui_button" href="javascript:void(0)" id="dic_rename">'+
' </a>' +
' <a style="display:none;" class="cke_dialog_ui_button" href="javascript:void(0)" id="dic_restore">'+
' </a>' +
' </div>' +
' <div style="margin:5px auto; width:95%;white-space:normal;" id="dic_info"></div>' +
'</div></form>'
}
]
},
{
id : 'about',
label : lang.aboutTab,
elements : [
{
type : 'html',
id : 'about',
style : 'margin: 5px 5px;',
html : '<div id="scayt_about"></div>'
}
]
}
];
var dialogDefiniton = {
title : lang.title,
minWidth : 360,
minHeight : 220,
onShow : function()
{
var dialog = this;
dialog.data = editor.fire( 'scaytDialog', {} );
dialog.options = dialog.data.scayt_control.option();
dialog.sLang = dialog.data.scayt_control.sLang;
if ( !dialog.data || !dialog.data.scayt || !dialog.data.scayt_control )
{
alert( 'Error loading application service' );
dialog.hide();
return;
}
var stop = 0;
if ( firstLoad )
{
dialog.data.scayt.getCaption( editor.langCode || 'en', function( caps )
{
if ( stop++ > 0 ) // Once only
return;
captions = caps;
init_with_captions.apply( dialog );
reload.apply( dialog );
firstLoad = false;
});
}
else
reload.apply( dialog );
dialog.selectPage( dialog.data.tab );
},
onOk : function()
{
var scayt_control = this.data.scayt_control;
scayt_control.option( this.options );
// Setup languge if it was changed.
var csLang = this.chosed_lang;
scayt_control.setLang( csLang );
scayt_control.refresh();
},
onCancel: function()
{
var o = getBOMAllOptions();
for ( var i in o )
o[i].checked = false;
setCheckedValue( getBOMAllLangs(), "" );
},
contents : contents
};
var scayt_control = CKEDITOR.plugins.scayt.getScayt( editor );
tags = CKEDITOR.plugins.scayt.uiTabs;
for ( i in tags )
{
if ( tags[ i ] == 1 )
contents[ contents.length ] = tags_contents[ i ];
}
if ( tags[2] == 1 )
userDicActive = 1;
var init_with_captions = function()
{
var dialog = this,
lang_list = dialog.data.scayt.getLangList(),
buttons = [ 'dic_create', 'dic_delete', 'dic_rename', 'dic_restore' ],
labels = optionsIds,
i;
// Add buttons titles
if ( userDicActive )
{
for ( i = 0; i < buttons.length; i++ )
{
var button = buttons[ i ];
doc.getById( button ).setHtml( '<span class="cke_dialog_ui_button">' + captions[ 'button_' + button] +'</span>' );
}
doc.getById( 'dic_info' ).setHtml( captions[ 'dic_info' ] );
}
// Fill options and dictionary labels.
if ( tags[0] == 1 )
{
for ( i in labels )
{
var label = 'label_' + labels[ i ],
labelElement = doc.getById( label );
if ( 'undefined' != typeof labelElement
&& 'undefined' != typeof captions[ label ]
&& 'undefined' != typeof dialog.options[labels[ i ]] )
{
labelElement.setHtml( captions[ label ] );
var labelParent = labelElement.getParent();
labelParent.$.style.display = "block";
}
}
}
var about = '<p><img src="' + window.scayt.getAboutInfo().logoURL + '" /></p>' +
'<p>' + captions[ 'version' ] + window.scayt.getAboutInfo().version.toString() + '</p>' +
'<p>' + captions[ 'about_throwt_copy' ] + '</p>';
doc.getById( 'scayt_about' ).setHtml( about );
// Create languages tab.
var createOption = function( option, list )
{
var label = doc.createElement( 'label' );
label.setAttribute( 'for', 'cke_option' + option );
label.setHtml( list[ option ] );
if ( dialog.sLang == option ) // Current.
dialog.chosed_lang = option;
var div = doc.createElement( 'div' );
var radio = CKEDITOR.dom.element.createFromHtml( '<input id="cke_option' +
option + '" type="radio" ' +
( dialog.sLang == option ? 'checked="checked"' : '' ) +
' value="' + option + '" name="scayt_lang" />' );
radio.on( 'click', function()
{
this.$.checked = true;
dialog.chosed_lang = option;
});
div.append( radio );
div.append( label );
return {
lang : list[ option ],
code : option,
radio : div
};
};
var langList = [];
if ( tags[1] ==1 )
{
for ( i in lang_list.rtl )
langList[ langList.length ] = createOption( i, lang_list.ltr );
for ( i in lang_list.ltr )
langList[ langList.length ] = createOption( i, lang_list.ltr );
langList.sort( function( lang1, lang2 )
{
return ( lang2.lang > lang1.lang ) ? -1 : 1 ;
});
var fieldL = doc.getById( 'scayt_lcol' ),
fieldR = doc.getById( 'scayt_rcol' );
for ( i=0; i < langList.length; i++ )
{
var field = ( i < langList.length / 2 ) ? fieldL : fieldR;
field.append( langList[ i ].radio );
}
}
// user dictionary handlers
var dic = {};
dic.dic_create = function( el, dic_name , dic_buttons )
{
// comma separated button's ids include repeats if exists
var all_buttons = dic_buttons[0] + ',' + dic_buttons[1];
var err_massage = captions["err_dic_create"];
var suc_massage = captions["succ_dic_create"];
window.scayt.createUserDictionary( dic_name,
function( arg )
{
hide_dic_buttons ( all_buttons );
display_dic_buttons ( dic_buttons[1] );
suc_massage = suc_massage.replace("%s" , arg.dname );
dic_success_message (suc_massage);
},
function( arg )
{
err_massage = err_massage.replace("%s" ,arg.dname );
dic_error_message ( err_massage + "( "+ (arg.message || "") +")");
});
};
dic.dic_rename = function( el, dic_name )
{
//
// try to rename dictionary
var err_massage = captions["err_dic_rename"] || "";
var suc_massage = captions["succ_dic_rename"] || "";
window.scayt.renameUserDictionary( dic_name,
function( arg )
{
suc_massage = suc_massage.replace("%s" , arg.dname );
set_dic_name( dic_name );
dic_success_message ( suc_massage );
},
function( arg )
{
err_massage = err_massage.replace("%s" , arg.dname );
set_dic_name( dic_name );
dic_error_message( err_massage + "( " + ( arg.message || "" ) + " )" );
});
};
dic.dic_delete = function( el, dic_name , dic_buttons )
{
var all_buttons = dic_buttons[0] + ',' + dic_buttons[1];
var err_massage = captions["err_dic_delete"];
var suc_massage = captions["succ_dic_delete"];
// try to delete dictionary
window.scayt.deleteUserDictionary(
function( arg )
{
suc_massage = suc_massage.replace("%s" , arg.dname );
hide_dic_buttons ( all_buttons );
display_dic_buttons ( dic_buttons[0] );
set_dic_name( "" ); // empty input field
dic_success_message( suc_massage );
},
function( arg )
{
err_massage = err_massage.replace("%s" , arg.dname );
dic_error_message(err_massage);
});
};
dic.dic_restore = dialog.dic_restore || function( el, dic_name , dic_buttons )
{
// try to restore existing dictionary
var all_buttons = dic_buttons[0] + ',' + dic_buttons[1];
var err_massage = captions["err_dic_restore"];
var suc_massage = captions["succ_dic_restore"];
window.scayt.restoreUserDictionary(dic_name,
function( arg )
{
suc_massage = suc_massage.replace("%s" , arg.dname );
hide_dic_buttons ( all_buttons );
display_dic_buttons(dic_buttons[1]);
dic_success_message( suc_massage );
},
function( arg )
{
err_massage = err_massage.replace("%s" , arg.dname );
dic_error_message( err_massage );
});
};
function onDicButtonClick( ev )
{
var dic_name = doc.getById('dic_name').getValue();
if ( !dic_name )
{
dic_error_message(" Dictionary name should not be empty. ");
return false;
}
try{
var el = id = ev.data.getTarget().getParent();
var id = el.getId();
dic[ id ].apply( null, [ el, dic_name, dic_buttons ] );
}
catch(err)
{
dic_error_message(" Dictionary error. ");
}
return true;
}
// ** bind event listeners
var arr_buttons = ( dic_buttons[0] + ',' + dic_buttons[1] ).split( ',' ),
l;
for ( i = 0, l = arr_buttons.length ; i < l ; i += 1 )
{
var dic_button = doc.getById(arr_buttons[i]);
if ( dic_button )
dic_button.on( 'click', onDicButtonClick, this );
}
};
var reload = function()
{
var dialog = this;
// for enabled options tab
if ( tags[0] == 1 ){
var opto = getBOMAllOptions();
// Animate options.
for ( var k=0,l = opto.length; k<l;k++ )
{
var i = opto[k].id;
var checkbox = doc.getById( i );
if ( checkbox )
{
opto[k].checked = false;
//alert (opto[k].removeAttribute)
if ( dialog.options[ i ] == 1 )
{
opto[k].checked = true;
}
// Bind events. Do it only once.
if ( firstLoad )
{
checkbox.on( 'click', function()
{
dialog.options[ this.getId() ] = this.$.checked ? 1 : 0 ;
});
}
}
}
}
//for enabled languages tab
if ( tags[1] == 1 )
{
var domLang = doc.getById("cke_option" + dialog.sLang);
setCheckedValue( domLang.$,dialog.sLang );
}
// * user dictionary
if ( userDicActive )
{
window.scayt.getNameUserDictionary(
function( o )
{
var dic_name = o.dname;
hide_dic_buttons( dic_buttons[0] + ',' + dic_buttons[1] );
if ( dic_name )
{
doc.getById( 'dic_name' ).setValue(dic_name);
display_dic_buttons( dic_buttons[1] );
}
else
display_dic_buttons( dic_buttons[0] );
},
function()
{
doc.getById( 'dic_name' ).setValue("");
});
dic_success_message("");
}
};
function dic_error_message( m )
{
doc.getById('dic_message').setHtml('<span style="color:red;">' + m + '</span>' );
}
function dic_success_message( m )
{
doc.getById('dic_message').setHtml('<span style="color:blue;">' + m + '</span>') ;
}
function display_dic_buttons( sIds )
{
sIds = String( sIds );
var aIds = sIds.split(',');
for ( var i=0, l = aIds.length; i < l ; i+=1)
doc.getById( aIds[i] ).$.style.display = "inline";
}
function hide_dic_buttons( sIds )
{
sIds = String( sIds );
var aIds = sIds.split(',');
for ( var i = 0, l = aIds.length; i < l ; i += 1 )
doc.getById( aIds[i] ).$.style.display = "none";
}
function set_dic_name( dic_name )
{
doc.getById('dic_name').$.value= dic_name;
}
return dialogDefiniton;
});

View File

@@ -0,0 +1,71 @@
a
{
text-decoration:none;
padding: 2px 4px 4px 6px;
display : block;
border-width: 1px;
border-style: solid;
margin : 0px;
}
a.cke_scayt_toogle:hover,
a.cke_scayt_toogle:focus,
a.cke_scayt_toogle:active
{
border-color: #316ac5;
background-color: #dff1ff;
color : #000;
cursor: pointer;
margin : 0px;
}
a.cke_scayt_toogle {
color : #316ac5;
border-color: #fff;
}
.scayt_enabled a.cke_scayt_item {
color : #316ac5;
border-color: #fff;
margin : 0px;
}
.scayt_disabled a.cke_scayt_item {
color : gray;
border-color : #fff;
}
.scayt_enabled a.cke_scayt_item:hover,
.scayt_enabled a.cke_scayt_item:focus,
.scayt_enabled a.cke_scayt_item:active
{
border-color: #316ac5;
background-color: #dff1ff;
color : #000;
cursor: pointer;
}
.scayt_disabled a.cke_scayt_item:hover,
.scayt_disabled a.cke_scayt_item:focus,
.scayt_disabled a.cke_scayt_item:active
{
border-color: gray;
background-color: #dff1ff;
color : gray;
cursor: no-drop;
}
.cke_scayt_set_on, .cke_scayt_set_off
{
display: none;
}
.scayt_enabled .cke_scayt_set_on
{
display: none;
}
.scayt_disabled .cke_scayt_set_on
{
display: inline;
}
.scayt_disabled .cke_scayt_set_off
{
display: none;
}
.scayt_enabled .cke_scayt_set_off
{
display: inline;
}

View File

@@ -0,0 +1,947 @@
/*
Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
/**
* @fileOverview Spell Check As You Type (SCAYT).
* Button name : Scayt.
*/
(function()
{
var commandName = 'scaytcheck',
openPage = '';
// Checks if a value exists in an array
function in_array( needle, haystack )
{
var found = 0,
key;
for ( key in haystack )
{
if ( haystack[ key ] == needle )
{
found = 1;
break;
}
}
return found;
}
var onEngineLoad = function()
{
var editor = this;
var createInstance = function() // Create new instance every time Document is created.
{
var config = editor.config;
// Initialise Scayt instance.
var oParams = {};
// Get the iframe.
oParams.srcNodeRef = editor.document.getWindow().$.frameElement;
// syntax : AppName.AppVersion@AppRevision
oParams.assocApp = 'CKEDITOR.' + CKEDITOR.version + '@' + CKEDITOR.revision;
oParams.customerid = config.scayt_customerid || '1:WvF0D4-UtPqN1-43nkD4-NKvUm2-daQqk3-LmNiI-z7Ysb4-mwry24-T8YrS3-Q2tpq2';
oParams.customDictionaryIds = config.scayt_customDictionaryIds || '';
oParams.userDictionaryName = config.scayt_userDictionaryName || '';
oParams.sLang = config.scayt_sLang || 'en_US';
// Introduce SCAYT onLoad callback. (#5632)
oParams.onLoad = function()
{
// Draw down word marker to avoid being covered by background-color style.(#5466)
if ( !( CKEDITOR.env.ie && CKEDITOR.env.version < 8 ) )
this.addStyle( this.selectorCss(), 'padding-bottom: 2px !important;' );
// Call scayt_control.focus when SCAYT loaded
// and only if editor has focus and scayt control creates at first time (#5720)
if ( editor.focusManager.hasFocus && !plugin.isControlRestored( editor ) )
this.focus();
};
oParams.onBeforeChange = function()
{
if ( plugin.getScayt( editor ) && !editor.checkDirty() )
setTimeout( function(){ editor.resetDirty(); }, 0 );
};
var scayt_custom_params = window.scayt_custom_params;
if ( typeof scayt_custom_params == 'object' )
{
for ( var k in scayt_custom_params )
oParams[ k ] = scayt_custom_params[ k ];
}
// needs for restoring a specific scayt control settings
if ( plugin.getControlId( editor ) )
oParams.id = plugin.getControlId( editor );
var scayt_control = new window.scayt( oParams );
scayt_control.afterMarkupRemove.push( function( node )
{
( new CKEDITOR.dom.element( node, scayt_control.document ) ).mergeSiblings();
} );
// Copy config.
var lastInstance = plugin.instances[ editor.name ];
if ( lastInstance )
{
scayt_control.sLang = lastInstance.sLang;
scayt_control.option( lastInstance.option() );
scayt_control.paused = lastInstance.paused;
}
plugin.instances[ editor.name ] = scayt_control;
//window.scayt.uiTags
var menuGroup = 'scaytButton';
var uiTabs = window.scayt.uiTags;
var fTabs = [];
for ( var i = 0, l=4; i < l; i++ )
fTabs.push( uiTabs[i] && plugin.uiTabs[i] );
plugin.uiTabs = fTabs;
try {
scayt_control.setDisabled( plugin.isPaused( editor ) === false );
} catch (e) {}
editor.fire( 'showScaytState' );
};
editor.on( 'contentDom', createInstance );
editor.on( 'contentDomUnload', function()
{
// Remove scripts.
var scripts = CKEDITOR.document.getElementsByTag( 'script' ),
scaytIdRegex = /^dojoIoScript(\d+)$/i,
scaytSrcRegex = /^https?:\/\/svc\.spellchecker\.net\/spellcheck\/script\/ssrv\.cgi/i;
for ( var i=0; i < scripts.count(); i++ )
{
var script = scripts.getItem( i ),
id = script.getId(),
src = script.getAttribute( 'src' );
if ( id && src && id.match( scaytIdRegex ) && src.match( scaytSrcRegex ))
script.remove();
}
});
editor.on( 'beforeCommandExec', function( ev ) // Disable SCAYT before Source command execution.
{
if ( ( ev.data.name == 'source' || ev.data.name == 'newpage' ) && editor.mode == 'wysiwyg' )
{
var scayt_instance = plugin.getScayt( editor );
if ( scayt_instance )
{
plugin.setPaused( editor, !scayt_instance.disabled );
// store a control id for restore a specific scayt control settings
plugin.setControlId( editor, scayt_instance.id );
scayt_instance.destroy( true );
delete plugin.instances[ editor.name ];
}
}
// Catch on source mode switch off (#5720)
else if ( ev.data.name == 'source' && editor.mode == 'source' )
plugin.markControlRestore( editor );
});
editor.on( 'afterCommandExec', function( ev )
{
if ( !plugin.isScaytEnabled( editor ) )
return;
if ( editor.mode == 'wysiwyg' && ( ev.data.name == 'undo' || ev.data.name == 'redo' ) )
window.setTimeout( function() { plugin.getScayt( editor ).refresh(); }, 10 );
});
editor.on( 'destroy', function( ev )
{
var editor = ev.editor,
scayt_instance = plugin.getScayt( editor );
// SCAYT instance might already get destroyed by mode switch (#5744).
if ( !scayt_instance )
return;
delete plugin.instances[ editor.name ];
// store a control id for restore a specific scayt control settings
plugin.setControlId( editor, scayt_instance.id );
scayt_instance.destroy( true );
});
// Listen to data manipulation to reflect scayt markup.
editor.on( 'afterSetData', function()
{
if ( plugin.isScaytEnabled( editor ) ) {
window.setTimeout( function()
{
var instance = plugin.getScayt( editor );
instance && instance.refresh();
}, 10 );
}
});
// Reload spell-checking for current word after insertion completed.
editor.on( 'insertElement', function()
{
var scayt_instance = plugin.getScayt( editor );
if ( plugin.isScaytEnabled( editor ) )
{
// Unlock the selection before reload, SCAYT will take
// care selection update.
if ( CKEDITOR.env.ie )
editor.getSelection().unlock( true );
// Return focus to the editor and refresh SCAYT markup (#5573).
window.setTimeout( function()
{
scayt_instance.focus();
scayt_instance.refresh();
}, 10 );
}
}, this, null, 50 );
editor.on( 'insertHtml', function()
{
var scayt_instance = plugin.getScayt( editor );
if ( plugin.isScaytEnabled( editor ) )
{
// Unlock the selection before reload, SCAYT will take
// care selection update.
if ( CKEDITOR.env.ie )
editor.getSelection().unlock( true );
// Return focus to the editor (#5573)
// Refresh SCAYT markup
window.setTimeout( function()
{
scayt_instance.focus();
scayt_instance.refresh();
}, 10 );
}
}, this, null, 50 );
editor.on( 'scaytDialog', function( ev ) // Communication with dialog.
{
ev.data.djConfig = window.djConfig;
ev.data.scayt_control = plugin.getScayt( editor );
ev.data.tab = openPage;
ev.data.scayt = window.scayt;
});
var dataProcessor = editor.dataProcessor,
htmlFilter = dataProcessor && dataProcessor.htmlFilter;
if ( htmlFilter )
{
htmlFilter.addRules(
{
elements :
{
span : function( element )
{
if ( element.attributes[ 'data-scayt_word' ]
&& element.attributes[ 'data-scaytid' ] )
{
delete element.name; // Write children, but don't write this node.
return element;
}
}
}
}
);
}
// Override Image.equals method avoid CK snapshot module to add SCAYT markup to snapshots. (#5546)
var undoImagePrototype = CKEDITOR.plugins.undo.Image.prototype;
undoImagePrototype.equals = CKEDITOR.tools.override( undoImagePrototype.equals, function( org )
{
return function( otherImage )
{
var thisContents = this.contents,
otherContents = otherImage.contents;
var scayt_instance = plugin.getScayt( this.editor );
// Making the comparison based on content without SCAYT word markers.
if ( scayt_instance && plugin.isScaytReady( this.editor ) )
{
// scayt::reset might return value undefined. (#5742)
this.contents = scayt_instance.reset( thisContents ) || '';
otherImage.contents = scayt_instance.reset( otherContents ) || '';
}
var retval = org.apply( this, arguments );
this.contents = thisContents;
otherImage.contents = otherContents;
return retval;
};
});
if ( editor.document )
createInstance();
};
CKEDITOR.plugins.scayt =
{
engineLoaded : false,
instances : {},
// Data storage for SCAYT control, based on editor instances
controlInfo : {},
setControlInfo : function( editor, o )
{
if ( editor && editor.name && typeof ( this.controlInfo[ editor.name ] ) != 'object' )
this.controlInfo[ editor.name ] = {};
for ( var infoOpt in o )
this.controlInfo[ editor.name ][ infoOpt ] = o[ infoOpt ];
},
isControlRestored : function( editor )
{
if ( editor &&
editor.name &&
this.controlInfo[ editor.name ] )
{
return this.controlInfo[ editor.name ].restored ;
}
return false;
},
markControlRestore : function( editor )
{
this.setControlInfo( editor, { restored:true } );
},
setControlId: function( editor, id )
{
this.setControlInfo( editor, { id:id } );
},
getControlId: function( editor )
{
if ( editor &&
editor.name &&
this.controlInfo[ editor.name ] &&
this.controlInfo[ editor.name ].id )
{
return this.controlInfo[ editor.name ].id;
}
return null;
},
setPaused: function( editor , bool )
{
this.setControlInfo( editor, { paused:bool } );
},
isPaused: function( editor )
{
if ( editor &&
editor.name &&
this.controlInfo[editor.name] )
{
return this.controlInfo[editor.name].paused;
}
return undefined;
},
getScayt : function( editor )
{
return this.instances[ editor.name ];
},
isScaytReady : function( editor )
{
return this.engineLoaded === true &&
'undefined' !== typeof window.scayt && this.getScayt( editor );
},
isScaytEnabled : function( editor )
{
var scayt_instance = this.getScayt( editor );
return ( scayt_instance ) ? scayt_instance.disabled === false : false;
},
loadEngine : function( editor )
{
// SCAYT doesn't work with Firefox2, Opera and AIR.
if ( CKEDITOR.env.gecko && CKEDITOR.env.version < 10900 || CKEDITOR.env.opera || CKEDITOR.env.air )
return editor.fire( 'showScaytState' );
if ( this.engineLoaded === true )
return onEngineLoad.apply( editor ); // Add new instance.
else if ( this.engineLoaded == -1 ) // We are waiting.
return CKEDITOR.on( 'scaytReady', function(){ onEngineLoad.apply( editor ); } ); // Use function(){} to avoid rejection as duplicate.
CKEDITOR.on( 'scaytReady', onEngineLoad, editor );
CKEDITOR.on( 'scaytReady', function()
{
this.engineLoaded = true;
},
this,
null,
0
); // First to run.
this.engineLoaded = -1; // Loading in progress.
// compose scayt url
var protocol = document.location.protocol;
// Default to 'http' for unknown.
protocol = protocol.search( /https?:/) != -1? protocol : 'http:';
var baseUrl = 'svc.spellchecker.net/scayt26/loader__base.js';
var scaytUrl = editor.config.scayt_srcUrl || ( protocol + '//' + baseUrl );
var scaytConfigBaseUrl = plugin.parseUrl( scaytUrl ).path + '/';
if( window.scayt == undefined )
{
CKEDITOR._djScaytConfig =
{
baseUrl: scaytConfigBaseUrl,
addOnLoad:
[
function()
{
CKEDITOR.fireOnce( 'scaytReady' );
}
],
isDebug: false
};
// Append javascript code.
CKEDITOR.document.getHead().append(
CKEDITOR.document.createElement( 'script',
{
attributes :
{
type : 'text/javascript',
async : 'true',
src : scaytUrl
}
})
);
}
else
CKEDITOR.fireOnce( 'scaytReady' );
return null;
},
parseUrl : function ( data )
{
var match;
if ( data.match && ( match = data.match(/(.*)[\/\\](.*?\.\w+)$/) ) )
return { path: match[1], file: match[2] };
else
return data;
}
};
var plugin = CKEDITOR.plugins.scayt;
// Context menu constructing.
var addButtonCommand = function( editor, buttonName, buttonLabel, commandName, command, menugroup, menuOrder )
{
editor.addCommand( commandName, command );
// If the "menu" plugin is loaded, register the menu item.
editor.addMenuItem( commandName,
{
label : buttonLabel,
command : commandName,
group : menugroup,
order : menuOrder
});
};
var commandDefinition =
{
preserveState : true,
editorFocus : false,
canUndo : false,
exec: function( editor )
{
if ( plugin.isScaytReady( editor ) )
{
var isEnabled = plugin.isScaytEnabled( editor );
this.setState( isEnabled ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_ON );
var scayt_control = plugin.getScayt( editor );
// the place where the status of editor focus should be restored
// after there will be ability to store its state before SCAYT button click
// if (storedFocusState is focused )
// scayt_control.focus();
//
// now focus is set certainly
scayt_control.focus();
scayt_control.setDisabled( isEnabled );
}
else if ( !editor.config.scayt_autoStartup && plugin.engineLoaded >= 0 ) // Load first time
{
this.setState( CKEDITOR.TRISTATE_DISABLED );
plugin.loadEngine( editor );
}
}
};
// Add scayt plugin.
CKEDITOR.plugins.add( 'scayt',
{
requires : [ 'menubutton' ],
beforeInit : function( editor )
{
var items_order = editor.config.scayt_contextMenuItemsOrder
|| 'suggest|moresuggest|control',
items_order_str = "";
items_order = items_order.split( '|' );
if ( items_order && items_order.length )
{
for ( var pos = 0 ; pos < items_order.length ; pos++ )
items_order_str += 'scayt_' + items_order[ pos ] + ( items_order.length != parseInt( pos, 10 ) + 1 ? ',' : '' );
}
// Put it on top of all context menu items (#5717)
editor.config.menu_groups = items_order_str + ',' + editor.config.menu_groups;
},
init : function( editor )
{
var moreSuggestions = {},
mainSuggestions = {};
// Scayt command.
var command = editor.addCommand( commandName, commandDefinition );
// Add Options dialog.
CKEDITOR.dialog.add( commandName, CKEDITOR.getUrl( this.path + 'dialogs/options.js' ) );
// read ui tags
var confuiTabs = editor.config.scayt_uiTabs || '1,1,1';
var uiTabs =[];
// string to array convert
confuiTabs = confuiTabs.split( ',' );
// check array length ! always must be 3 filled with 1 or 0
for ( var i=0, l=3; i < l; i++ )
{
var flag = parseInt( confuiTabs[i] || '1', 10 );
uiTabs.push( flag );
}
var menuGroup = 'scaytButton';
editor.addMenuGroup( menuGroup );
// combine menu items to render
var uiMuneItems = {};
var lang = editor.lang.scayt;
// always added
uiMuneItems.scaytToggle =
{
label : lang.enable,
command : commandName,
group : menuGroup
};
if ( uiTabs[0] == 1 )
uiMuneItems.scaytOptions =
{
label : lang.options,
group : menuGroup,
onClick : function()
{
openPage = 'options';
editor.openDialog( commandName );
}
};
if ( uiTabs[1] == 1 )
uiMuneItems.scaytLangs =
{
label : lang.langs,
group : menuGroup,
onClick : function()
{
openPage = 'langs';
editor.openDialog( commandName );
}
};
if ( uiTabs[2] == 1 )
uiMuneItems.scaytDict =
{
label : lang.dictionariesTab,
group : menuGroup,
onClick : function()
{
openPage = 'dictionaries';
editor.openDialog( commandName );
}
};
// always added
uiMuneItems.scaytAbout =
{
label : editor.lang.scayt.about,
group : menuGroup,
onClick : function()
{
openPage = 'about';
editor.openDialog( commandName );
}
};
uiTabs[3] = 1; // about us tab is always on
plugin.uiTabs = uiTabs;
editor.addMenuItems( uiMuneItems );
editor.ui.add( 'Scayt', CKEDITOR.UI_MENUBUTTON,
{
label : lang.title,
title : CKEDITOR.env.opera ? lang.opera_title : lang.title,
className : 'cke_button_scayt',
modes : { wysiwyg : 1 },
onRender: function()
{
command.on( 'state', function()
{
this.setState( command.state );
},
this);
},
onMenu : function()
{
var isEnabled = plugin.isScaytEnabled( editor );
editor.getMenuItem( 'scaytToggle' ).label = lang[ isEnabled ? 'disable' : 'enable' ];
return {
scaytToggle : CKEDITOR.TRISTATE_OFF,
scaytOptions : isEnabled && plugin.uiTabs[0] ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
scaytLangs : isEnabled && plugin.uiTabs[1] ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
scaytDict : isEnabled && plugin.uiTabs[2] ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
scaytAbout : isEnabled && plugin.uiTabs[3] ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED
};
}
});
// If the "contextmenu" plugin is loaded, register the listeners.
if ( editor.contextMenu && editor.addMenuItems )
{
editor.contextMenu.addListener( function( element, selection )
{
if ( !plugin.isScaytEnabled( editor )
|| selection.getCommonAncestor().isReadOnly() )
return null;
var scayt_control = plugin.getScayt( editor ),
node = scayt_control.getScaytNode();
if ( !node )
return null;
var word = scayt_control.getWord( node );
if ( !word )
return null;
var sLang = scayt_control.getLang(),
_r = {},
items_suggestion = window.scayt.getSuggestion( word, sLang );
if ( !items_suggestion || !items_suggestion.length )
return null;
// Remove unused commands and menuitems
for ( i in moreSuggestions )
{
delete editor._.menuItems[ i ];
delete editor._.commands[ i ];
}
for ( i in mainSuggestions )
{
delete editor._.menuItems[ i ];
delete editor._.commands[ i ];
}
moreSuggestions = {}; // Reset items.
mainSuggestions = {};
var moreSuggestionsUnable = editor.config.scayt_moreSuggestions || 'on';
var moreSuggestionsUnableAdded = false;
var maxSuggestions = editor.config.scayt_maxSuggestions;
( typeof maxSuggestions != 'number' ) && ( maxSuggestions = 5 );
!maxSuggestions && ( maxSuggestions = items_suggestion.length );
var contextCommands = editor.config.scayt_contextCommands || 'all';
contextCommands = contextCommands.split( '|' );
for ( var i = 0, l = items_suggestion.length; i < l; i += 1 )
{
var commandName = 'scayt_suggestion_' + items_suggestion[i].replace( ' ', '_' );
var exec = ( function( el, s )
{
return {
exec: function()
{
scayt_control.replace( el, s );
}
};
})( node, items_suggestion[i] );
if ( i < maxSuggestions )
{
addButtonCommand( editor, 'button_' + commandName, items_suggestion[i],
commandName, exec, 'scayt_suggest', i + 1 );
_r[ commandName ] = CKEDITOR.TRISTATE_OFF;
mainSuggestions[ commandName ] = CKEDITOR.TRISTATE_OFF;
}
else if ( moreSuggestionsUnable == 'on' )
{
addButtonCommand( editor, 'button_' + commandName, items_suggestion[i],
commandName, exec, 'scayt_moresuggest', i + 1 );
moreSuggestions[ commandName ] = CKEDITOR.TRISTATE_OFF;
moreSuggestionsUnableAdded = true;
}
}
if ( moreSuggestionsUnableAdded )
{
// Register the More suggestions group;
editor.addMenuItem( 'scayt_moresuggest',
{
label : lang.moreSuggestions,
group : 'scayt_moresuggest',
order : 10,
getItems : function()
{
return moreSuggestions;
}
});
mainSuggestions[ 'scayt_moresuggest' ] = CKEDITOR.TRISTATE_OFF;
}
if ( in_array( 'all', contextCommands ) || in_array( 'ignore', contextCommands) )
{
var ignore_command = {
exec: function(){
scayt_control.ignore( node );
}
};
addButtonCommand( editor, 'ignore', lang.ignore, 'scayt_ignore', ignore_command, 'scayt_control', 1 );
mainSuggestions[ 'scayt_ignore' ] = CKEDITOR.TRISTATE_OFF;
}
if ( in_array( 'all', contextCommands ) || in_array( 'ignoreall', contextCommands ) )
{
var ignore_all_command = {
exec: function(){
scayt_control.ignoreAll( node );
}
};
addButtonCommand(editor, 'ignore_all', lang.ignoreAll, 'scayt_ignore_all', ignore_all_command, 'scayt_control', 2);
mainSuggestions['scayt_ignore_all'] = CKEDITOR.TRISTATE_OFF;
}
if ( in_array( 'all', contextCommands ) || in_array( 'add', contextCommands ) )
{
var addword_command = {
exec: function(){
window.scayt.addWordToUserDictionary( node );
}
};
addButtonCommand(editor, 'add_word', lang.addWord, 'scayt_add_word', addword_command, 'scayt_control', 3);
mainSuggestions['scayt_add_word'] = CKEDITOR.TRISTATE_OFF;
}
if ( scayt_control.fireOnContextMenu )
scayt_control.fireOnContextMenu( editor );
return mainSuggestions;
});
}
var showInitialState = function()
{
editor.removeListener( 'showScaytState', showInitialState );
if ( !CKEDITOR.env.opera && !CKEDITOR.env.air )
command.setState( plugin.isScaytEnabled( editor ) ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF );
else
command.setState( CKEDITOR.TRISTATE_DISABLED );
};
editor.on( 'showScaytState', showInitialState );
if ( CKEDITOR.env.opera || CKEDITOR.env.air )
{
editor.on( 'instanceReady', function()
{
showInitialState();
});
}
// Start plugin
if ( editor.config.scayt_autoStartup )
{
editor.on( 'instanceReady', function()
{
plugin.loadEngine( editor );
});
}
},
afterInit : function( editor )
{
// Prevent word marker line from displaying in elements path and been removed when cleaning format. (#3570) (#4125)
var elementsPathFilters,
scaytFilter = function( element )
{
if ( element.hasAttribute( 'data-scaytid' ) )
return false;
};
if ( editor._.elementsPath && ( elementsPathFilters = editor._.elementsPath.filters ) )
elementsPathFilters.push( scaytFilter );
editor.addRemoveFormatFilter && editor.addRemoveFormatFilter( scaytFilter );
}
});
})();
/**
* If enabled (true), turns on SCAYT automatically after loading the editor.
* @name CKEDITOR.config.scayt_autoStartup
* @type Boolean
* @default false
* @example
* config.scayt_autoStartup = true;
*/
/**
* Defines the number of SCAYT suggestions to show in the main context menu.
* The possible values are:
* <ul>
* <li>0 (zero): All suggestions are displayed in the main context menu.</li>
* <li>Positive number: The maximum number of suggestions to shown in context
* menu. Other entries will be shown in "More Suggestions" sub-menu.</li>
* <li>Negative number: No suggestions are shown in the main context menu. All
* entries will be listed in the "Suggestions" sub-menu.</li>
* </ul>
* @name CKEDITOR.config.scayt_maxSuggestions
* @type Number
* @default 5
* @example
* // Display only three suggestions in the main context menu.
* config.scayt_maxSuggestions = 3;
* @example
* // Do not show the suggestions directly.
* config.scayt_maxSuggestions = -1;
*/
/**
* Sets the customer ID for SCAYT. Required for migration from free version
* with banner to paid version.
* @name CKEDITOR.config.scayt_customerid
* @type String
* @default ''
* @example
* // Load SCAYT using my customer ID.
* config.scayt_customerid = 'your-encrypted-customer-id';
*/
/**
* Enables/disables the "More Suggestions" sub-menu in the context menu.
* The possible values are "on" or "off".
* @name CKEDITOR.config.scayt_moreSuggestions
* @type String
* @default 'on'
* @example
* // Disables the "More Suggestions" sub-menu.
* config.scayt_moreSuggestions = 'off';
*/
/**
* Customizes the display of SCAYT context menu commands ("Add Word", "Ignore"
* and "Ignore All"). It must be a string with one or more of the following
* words separated by a pipe ("|"):
* <ul>
* <li>"off": disables all options.</li>
* <li>"all": enables all options.</li>
* <li>"ignore": enables the "Ignore" option.</li>
* <li>"ignoreall": enables the "Ignore All" option.</li>
* <li>"add": enables the "Add Word" option.</li>
* </ul>
* @name CKEDITOR.config.scayt_contextCommands
* @type String
* @default 'all'
* @example
* // Show only "Add Word" and "Ignore All" in the context menu.
* config.scayt_contextCommands = 'add|ignoreall';
*/
/**
* Sets the default spellchecking language for SCAYT.
* @name CKEDITOR.config.scayt_sLang
* @type String
* @default 'en_US'
* @example
* // Sets SCAYT to German.
* config.scayt_sLang = 'de_DE';
*/
/**
* Sets the visibility of the SCAYT tabs in the settings dialog and toolbar
* button. The value must contain a "1" (enabled) or "0" (disabled) number for
* each of the following entries, in this precise order, separated by a
* comma (","): "Options", "Languages" and "Dictionary".
* @name CKEDITOR.config.scayt_uiTabs
* @type String
* @default '1,1,1'
* @example
* // Hide the "Languages" tab.
* config.scayt_uiTabs = '1,0,1';
*/
/**
* Set the URL to SCAYT core. Required to switch to licensed version of SCAYT application.
* Further details at http://wiki.spellchecker.net/doku.php?id=3rd:wysiwyg:fckeditor:wscckf3l .
* @name CKEDITOR.config.scayt_srcUrl
* @type String
* @default ''
* @example
* config.scayt_srcUrl = "http://my-host/spellcheck/lf/scayt/scayt.js";
*/
/**
* Links SCAYT to custom dictionaries. It's a string containing dictionary ids
* separared by commas (","). Available only for licensed version.
* Further details at http://wiki.spellchecker.net/doku.php?id=custom_dictionary_support .
* @name CKEDITOR.config.scayt_customDictionaryIds
* @type String
* @default ''
* @example
* config.scayt_customDictionaryIds = '3021,3456,3478"';
*/
/**
* Makes it possible to activate a custom dictionary on SCAYT. The user
* dictionary name must be used. Available only for licensed version.
* @name CKEDITOR.config.scayt_userDictionaryName
* @type String
* @default ''
* @example
* config.scayt_userDictionaryName = 'MyDictionary';
*/
/**
* Define order of placing of SCAYT context menu items by groups.
* It must be a string with one or more of the following
* words separated by a pipe ("|"):
* <ul>
* <li>'suggest' - main suggestion word list,</li>
* <li>'moresuggest' - more suggestions word list,</li>
* <li>'control' - SCAYT commands, such as 'Ignore' and 'Add Word'</li>
* </ul>
*
* @name CKEDITOR.config.scayt_contextMenuItemsOrder
* @type String
* @default 'suggest|moresuggest|control'
* @example
* config.scayt_contextMenuItemsOrder = 'moresuggest|control|suggest';
*/

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 B

Some files were not shown because too many files have changed in this diff Show More