first import

This commit is contained in:
Bachir Soussi Chiadmi
2015-04-08 11:40:19 +02:00
commit 1bc61b12ad
8435 changed files with 1582817 additions and 0 deletions

View File

@@ -0,0 +1,222 @@
/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.dialog.add( 'a11yHelp', function( editor )
{
var lang = editor.lang.accessibilityHelp,
id = CKEDITOR.tools.getNextId();
// CharCode <-> KeyChar.
var keyMap =
{
8 : "BACKSPACE",
9 : "TAB" ,
13 : "ENTER" ,
16 : "SHIFT" ,
17 : "CTRL" ,
18 : "ALT" ,
19 : "PAUSE" ,
20 : "CAPSLOCK" ,
27 : "ESCAPE" ,
33 : "PAGE UP" ,
34 : "PAGE DOWN" ,
35 : "END" ,
36 : "HOME" ,
37 : "LEFT ARROW" ,
38 : "UP ARROW" ,
39 : "RIGHT ARROW" ,
40 : "DOWN ARROW" ,
45 : "INSERT" ,
46 : "DELETE" ,
91 : "LEFT WINDOW KEY" ,
92 : "RIGHT WINDOW KEY" ,
93 : "SELECT KEY" ,
96 : "NUMPAD 0" ,
97 : "NUMPAD 1" ,
98 : "NUMPAD 2" ,
99 : "NUMPAD 3" ,
100 : "NUMPAD 4" ,
101 : "NUMPAD 5" ,
102 : "NUMPAD 6" ,
103 : "NUMPAD 7" ,
104 : "NUMPAD 8" ,
105 : "NUMPAD 9" ,
106 : "MULTIPLY" ,
107 : "ADD" ,
109 : "SUBTRACT" ,
110 : "DECIMAL POINT" ,
111 : "DIVIDE" ,
112 : "F1" ,
113 : "F2" ,
114 : "F3" ,
115 : "F4" ,
116 : "F5" ,
117 : "F6" ,
118 : "F7" ,
119 : "F8" ,
120 : "F9" ,
121 : "F10" ,
122 : "F11" ,
123 : "F12" ,
144 : "NUM LOCK" ,
145 : "SCROLL LOCK" ,
186 : "SEMI-COLON" ,
187 : "EQUAL SIGN" ,
188 : "COMMA" ,
189 : "DASH" ,
190 : "PERIOD" ,
191 : "FORWARD SLASH" ,
192 : "GRAVE ACCENT" ,
219 : "OPEN BRACKET" ,
220 : "BACK SLASH" ,
221 : "CLOSE BRAKET" ,
222 : "SINGLE QUOTE"
};
// Modifier keys override.
keyMap[ CKEDITOR.ALT ] = 'ALT';
keyMap[ CKEDITOR.SHIFT ] = 'SHIFT';
keyMap[ CKEDITOR.CTRL ] = 'CTRL';
// Sort in desc.
var modifiers = [ CKEDITOR.ALT, CKEDITOR.SHIFT, CKEDITOR.CTRL ];
function representKeyStroke( keystroke )
{
var quotient,
modifier,
presentation = [];
for ( var i = 0; i < modifiers.length; i++ )
{
modifier = modifiers[ i ];
quotient = keystroke / modifiers[ i ];
if ( quotient > 1 && quotient <= 2 )
{
keystroke -= modifier;
presentation.push( keyMap[ modifier ] );
}
}
presentation.push( keyMap[ keystroke ]
|| String.fromCharCode( keystroke ) );
return presentation.join( '+' );
}
var variablesPattern = /\$\{(.*?)\}/g;
function replaceVariables( match, name )
{
var keystrokes = editor.config.keystrokes,
definition,
length = keystrokes.length;
for ( var i = 0; i < length; i++ )
{
definition = keystrokes[ i ];
if ( definition[ 1 ] == name )
break;
}
return representKeyStroke( definition[ 0 ] );
}
// Create the help list directly from lang file entries.
function buildHelpContents()
{
var pageTpl = '<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',
style : 'white-space:normal;',
focus : function() {},
html : buildHelpContents() +
'<style type="text/css">' +
'.cke_accessibility_legend' +
'{' +
'width:600px;' +
'height:400px;' +
'padding-right:5px;' +
'overflow-y:auto;' +
'overflow-x:hidden;' +
'}' +
// Some adjustments are to be done for IE6 and Quirks to work "properly" (#5757)
'.cke_browser_quirks .cke_accessibility_legend,' +
'.cke_browser_ie6 .cke_accessibility_legend' +
'{' +
'height:390px' +
'}' +
// Override non-wrapping white-space rule in reset css.
'.cke_accessibility_legend *' +
'{' +
'white-space:normal;' +
'}' +
'.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' +
'{' +
'margin:10px' +
'}' +
'</style>'
}
]
}
],
buttons : [ CKEDITOR.dialog.cancelButton ]
};
});

View File

@@ -0,0 +1,108 @@
/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.setLang( 'a11yhelp', 'en',
{
accessibilityHelp :
{
title : 'Accessibility Instructions',
contents : 'Help Contents. To close this dialog press ESC.',
legend :
[
{
name : 'General',
items :
[
{
name : 'Editor Toolbar',
legend:
'Press ${toolbarFocus} to navigate to the toolbar. ' +
'Move to the next and previous toolbar group with TAB and SHIFT-TAB. ' +
'Move to the next and previous toolbar button with RIGHT ARROW or LEFT ARROW. ' +
'Press SPACE or ENTER to activate the toolbar button.'
},
{
name : 'Editor Dialog',
legend :
'Inside a dialog, press TAB to navigate to next dialog field, press SHIFT + TAB to move to previous field, press ENTER to submit dialog, press ESC to cancel dialog. ' +
'For dialogs that have multiple tab pages, press ALT + F10 to navigate to tab-list. ' +
'Then move to next tab with TAB OR RIGTH ARROW. ' +
'Move to previous tab with SHIFT + TAB or LEFT ARROW. ' +
'Press SPACE or ENTER to select the tab page.'
},
{
name : 'Editor Context Menu',
legend :
'Press ${contextMenu} or APPLICATION KEY to open context-menu. ' +
'Then move to next menu option with TAB or DOWN ARROW. ' +
'Move to previous option with SHIFT+TAB or UP ARROW. ' +
'Press SPACE or ENTER to select the menu option. ' +
'Open sub-menu of current option wtih SPACE or ENTER or RIGHT ARROW. ' +
'Go back to parent menu item with ESC or LEFT ARROW. ' +
'Close context menu with ESC.'
},
{
name : 'Editor List Box',
legend :
'Inside a list-box, move to next list item with TAB OR DOWN ARROW. ' +
'Move to previous list item with SHIFT + TAB or UP ARROW. ' +
'Press SPACE or ENTER to select the list option. ' +
'Press ESC to close the list-box.'
},
{
name : 'Editor Element Path Bar',
legend :
'Press ${elementsPathFocus} to navigate to the elements path bar. ' +
'Move to next element button with TAB or RIGHT ARROW. ' +
'Move to previous button with SHIFT+TAB or LEFT ARROW. ' +
'Press SPACE or ENTER to select the element in editor.'
}
]
},
{
name : 'Commands',
items :
[
{
name : ' Undo command',
legend : 'Press ${undo}'
},
{
name : ' Redo command',
legend : 'Press ${redo}'
},
{
name : ' Bold command',
legend : 'Press ${bold}'
},
{
name : ' Italic command',
legend : 'Press ${italic}'
},
{
name : ' Underline command',
legend : 'Press ${underline}'
},
{
name : ' Link command',
legend : 'Press ${link}'
},
{
name : ' Toolbar Collapse command',
legend : 'Press ${toolbarCollapse}'
},
{
name : ' Accessibility Help',
legend : 'Press ${a11yHelp}'
}
]
}
]
}
});

View File

@@ -0,0 +1,216 @@
/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.setLang( 'a11yhelp', 'he',
{
accessibilityHelp :
{
title : 'הוראות נגישות',
contents : 'הוראות נגישות. לסגירה לחץ אסקייפ (ESC).',
legend :
[
{
name : 'כללי',
items :
[
{
name : 'סרגל הכלים',
legend:
'לחץ על ${toolbarFocus} כדי לנווט לסרגל הכלים. ' +
'עבור לכפתור הבא עם מקש הטאב (TAB) או חץ שמאלי. ' +
'עבור לכפתור הקודם עם מקש השיפט (SHIFT) + טאב (TAB) או חץ ימני. ' +
'לחץ רווח או אנטר (ENTER) כדי להפעיל את הכפתור הנבחר.'
},
{
name : 'דיאלוגים (חלונות תשאול)',
legend :
'בתוך דיאלוג, לחץ טאב (TAB) כדי לנווט לשדה הבא, לחץ שיפט (SHIFT) + טאב (TAB) כדי לנווט לשדה הקודם, לחץ אנטר (ENTER) כדי לשלוח את הדיאלוג, לחץ אסקייפ (ESC) כדי לבטל. ' +
'בתוך דיאלוגים בעלי מספר טאבים (לשוניות), לחץ אלט (ALT) + F10 כדי לנווט לשורת הטאבים. ' +
'נווט לטאב הבא עם טאב (TAB) או חץ שמאלי. ' +
'עבור לטאב הקודם עם שיפט (SHIFT) + טאב (TAB) או חץ שמאלי. ' +
'לחץ רווח או אנטר (ENTER) כדי להיכנס לטאב.'
},
{
name : 'תפריט ההקשר (Context Menu)',
legend :
'לחץ ${contextMenu} או APPLICATION KEYכדי לפתוח את תפריט ההקשר. ' +
'עבור לאפשרות הבאה עם טאב (TAB) או חץ למטה. ' +
'עבור לאפשרות הקודמת עם שיפט (SHIFT) + טאב (TAB) או חץ למעלה. ' +
'לחץ רווח או אנטר (ENTER) כדי לבחור את האפשרות. ' +
'פתח את תת התפריט (Sub-menu) של האפשרות הנוכחית עם רווח או אנטר (ENTER) או חץ שמאלי. ' +
'חזור לתפריט האב עם אסקייפ (ESC) או חץ שמאלי. ' +
'סגור את תפריט ההקשר עם אסקייפ (ESC).'
},
{
name : 'תפריטים צפים (List boxes)',
legend :
'בתוך תפריט צף, עבור לפריט הבא עם טאב (TAB) או חץ למטה. ' +
'עבור לתפריט הקודם עם שיפט (SHIFT) + טאב (TAB) or חץ עליון. ' +
'Press SPACE or ENTER to select the list option. ' +
'Press ESC to close the list-box.'
},
{
name : 'עץ אלמנטים (Elements Path)',
legend :
'לחץ ${elementsPathFocus} כדי לנווט לעץ האלמנטים. ' +
'עבור לפריט הבא עם טאב (TAB) או חץ ימני. ' +
'עבור לפריט הקודם עם שיפט (SHIFT) + טאב (TAB) או חץ שמאלי. ' +
'לחץ רווח או אנטר (ENTER) כדי לבחור את האלמנט בעורך.'
}
]
},
{
name : 'פקודות',
items :
[
{
name : ' ביטול צעד אחרון',
legend : 'לחץ ${undo}'
},
{
name : ' חזרה על צעד אחרון',
legend : 'לחץ ${redo}'
},
{
name : ' הדגשה',
legend : 'לחץ ${bold}'
},
{
name : ' הטייה',
legend : 'לחץ ${italic}'
},
{
name : ' הוספת קו תחתון',
legend : 'לחץ ${underline}'
},
{
name : ' הוספת לינק',
legend : 'לחץ ${link}'
},
{
name : ' כיווץ סרגל הכלים',
legend : 'לחץ ${toolbarCollapse}'
},
{
name : ' הוראות נגישות',
legend : 'לחץ ${a11yHelp}'
}
]
}
]
}
});
/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.setLang( 'a11yhelp', 'he',
{
accessibilityHelp :
{
title : 'הוראות נגישות',
contents : 'הוראות נגישות. לסגירה לחץ אסקייפ (ESC).',
legend :
[
{
name : 'כללי',
items :
[
{
name : 'סרגל הכלים',
legend:
'לחץ על ${toolbarFocus} כדי לנווט לסרגל הכלים. ' +
'עבור לכפתור הבא עם מקש הטאב (TAB) או חץ שמאלי. ' +
'עבור לכפתור הקודם עם מקש השיפט (SHIFT) + טאב (TAB) או חץ ימני. ' +
'לחץ רווח או אנטר (ENTER) כדי להפעיל את הכפתור הנבחר.'
},
{
name : 'דיאלוגים (חלונות תשאול)',
legend :
'בתוך דיאלוג, לחץ טאב (TAB) כדי לנווט לשדה הבא, לחץ שיפט (SHIFT) + טאב (TAB) כדי לנווט לשדה הקודם, לחץ אנטר (ENTER) כדי לשלוח את הדיאלוג, לחץ אסקייפ (ESC) כדי לבטל. ' +
'בתוך דיאלוגים בעלי מספר טאבים (לשוניות), לחץ אלט (ALT) + F10 כדי לנווט לשורת הטאבים. ' +
'נווט לטאב הבא עם טאב (TAB) או חץ שמאלי. ' +
'עבור לטאב הקודם עם שיפט (SHIFT) + טאב (TAB) או חץ שמאלי. ' +
'לחץ רווח או אנטר (ENTER) כדי להיכנס לטאב.'
},
{
name : 'תפריט ההקשר (Context Menu)',
legend :
'לחץ ${contextMenu} או APPLICATION KEYכדי לפתוח את תפריט ההקשר. ' +
'עבור לאפשרות הבאה עם טאב (TAB) או חץ למטה. ' +
'עבור לאפשרות הקודמת עם שיפט (SHIFT) + טאב (TAB) או חץ למעלה. ' +
'לחץ רווח או אנטר (ENTER) כדי לבחור את האפשרות. ' +
'פתח את תת התפריט (Sub-menu) של האפשרות הנוכחית עם רווח או אנטר (ENTER) או חץ שמאלי. ' +
'חזור לתפריט האב עם אסקייפ (ESC) או חץ שמאלי. ' +
'סגור את תפריט ההקשר עם אסקייפ (ESC).'
},
{
name : 'תפריטים צפים (List boxes)',
legend :
'בתוך תפריט צף, עבור לפריט הבא עם טאב (TAB) או חץ למטה. ' +
'עבור לתפריט הקודם עם שיפט (SHIFT) + טאב (TAB) or חץ עליון. ' +
'Press SPACE or ENTER to select the list option. ' +
'Press ESC to close the list-box.'
},
{
name : 'עץ אלמנטים (Elements Path)',
legend :
'לחץ ${elementsPathFocus} כדי לנווט לעץ האלמנטים. ' +
'עבור לפריט הבא עם טאב (TAB) או חץ ימני. ' +
'עבור לפריט הקודם עם שיפט (SHIFT) + טאב (TAB) או חץ שמאלי. ' +
'לחץ רווח או אנטר (ENTER) כדי לבחור את האלמנט בעורך.'
}
]
},
{
name : 'פקודות',
items :
[
{
name : ' ביטול צעד אחרון',
legend : 'לחץ ${undo}'
},
{
name : ' חזרה על צעד אחרון',
legend : 'לחץ ${redo}'
},
{
name : ' הדגשה',
legend : 'לחץ ${bold}'
},
{
name : ' הטייה',
legend : 'לחץ ${italic}'
},
{
name : ' הוספת קו תחתון',
legend : 'לחץ ${underline}'
},
{
name : ' הוספת לינק',
legend : 'לחץ ${link}'
},
{
name : ' כיווץ סרגל הכלים',
legend : 'לחץ ${toolbarCollapse}'
},
{
name : ' הוראות נגישות',
legend : 'לחץ ${a11yHelp}'
}
]
}
]
}
});

View File

@@ -0,0 +1,47 @@
/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
/**
* @fileOverview Plugin definition for the a11yhelp, which provides a dialog
* with accessibility related help.
*/
(function()
{
var pluginName = 'a11yhelp',
commandName = 'a11yHelp';
CKEDITOR.plugins.add( pluginName,
{
// List of available localizations.
availableLangs : { en:1, he:1 },
init : function( editor )
{
var plugin = this;
editor.addCommand( commandName,
{
exec : function()
{
var langCode = editor.langCode;
langCode = plugin.availableLangs[ langCode ] ? langCode : 'en';
CKEDITOR.scriptLoader.load(
CKEDITOR.getUrl( plugin.path + 'lang/' + langCode + '.js' ),
function()
{
CKEDITOR.tools.extend( editor.lang, plugin.langEntries[ langCode ] );
editor.openDialog( commandName );
});
},
modes : { wysiwyg:1, source:1 },
readOnly : 1,
canUndo : false
});
CKEDITOR.dialog.add( commandName, this.path + 'dialogs/a11yhelp.js' );
}
});
})();

View File

@@ -0,0 +1,76 @@
/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.dialog.add( 'about', function( editor )
{
var lang = editor.lang.about;
return {
title : CKEDITOR.env.ie ? lang.dlgTitle : lang.title,
minWidth : 390,
minHeight : 230,
contents : [
{
id : 'tab1',
label : '',
title : '',
expand : true,
padding : 0,
elements :
[
{
type : 'html',
html :
'<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.help.replace( '$1', '<a href="http://docs.cksource.com/CKEditor_3.x/Users_Guide/Quick_Reference">' + lang.userGuide + '</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,24 @@
/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.add( 'about',
{
requires : [ 'dialog' ],
init : function( editor )
{
var command = editor.addCommand( 'about', new CKEDITOR.dialogCommand( 'about' ) );
command.modes = { wysiwyg:1, source:1 };
command.canUndo = false;
command.readOnly = 1;
editor.ui.addButton( 'About',
{
label : editor.lang.about.title,
command : 'about'
});
CKEDITOR.dialog.add( 'about', this.path + 'dialogs/about.js' );
}
});

View File

@@ -0,0 +1,228 @@
/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
(function()
{
var eventNameList = [ 'click', 'keydown', 'mousedown', 'keypress', 'mouseover', 'mouseout' ];
// Inline event callbacks assigned via innerHTML/outerHTML, such as
// onclick/onmouseover, are ignored in AIR.
// Use DOM2 event listeners to substitue inline handlers instead.
function convertInlineHandlers( container )
{
// TODO: document.querySelectorAll is not supported in AIR.
var children = container.getElementsByTag( '*' ),
count = children.count(),
child;
for ( var i = 0; i < count; i++ )
{
child = children.getItem( i );
(function( node )
{
for ( var j = 0; j < eventNameList.length; j++ )
{
(function( eventName )
{
var inlineEventHandler = node.getAttribute( 'on' + eventName );
if ( node.hasAttribute( 'on' + eventName ) )
{
node.removeAttribute( 'on' + eventName );
node.on( eventName, function( evt )
{
var callFunc = /(return\s*)?CKEDITOR\.tools\.callFunction\(([^)]+)\)/.exec( inlineEventHandler ),
hasReturn = callFunc && callFunc[ 1 ],
callFuncArgs = callFunc && callFunc[ 2 ].split( ',' ),
preventDefault = /return false;/.test( inlineEventHandler );
if ( callFuncArgs )
{
var nums = callFuncArgs.length,
argName;
for ( var i = 0; i < nums; i++ )
{
// Trim spaces around param.
callFuncArgs[ i ] = argName = CKEDITOR.tools.trim( callFuncArgs[ i ] );
// String form param.
var strPattern = argName.match( /^(["'])([^"']*?)\1$/ );
if ( strPattern )
{
callFuncArgs[ i ] = strPattern[ 2 ];
continue;
}
// Integer form param.
if ( argName.match( /\d+/ ) )
{
callFuncArgs[ i ] = parseInt( argName, 10 );
continue;
}
// Speical variables.
switch( argName )
{
case 'this' :
callFuncArgs[ i ] = node.$;
break;
case 'event' :
callFuncArgs[ i ] = evt.data.$;
break;
case 'null' :
callFuncArgs [ i ] = null;
break;
}
}
var retval = CKEDITOR.tools.callFunction.apply( window, callFuncArgs );
if ( hasReturn && retval === false )
preventDefault = 1;
}
if ( preventDefault )
evt.data.preventDefault();
});
}
})( eventNameList[ j ] );
}
})( child );
}
}
CKEDITOR.plugins.add( 'adobeair',
{
init : function( editor )
{
if ( !CKEDITOR.env.air )
return;
// Body doesn't get default margin on AIR.
editor.addCss( 'body { padding: 8px }' );
editor.on( 'uiReady', function()
{
convertInlineHandlers( editor.container );
if ( editor.sharedSpaces )
{
for ( var space in editor.sharedSpaces )
convertInlineHandlers( editor.sharedSpaces[ space ] );
}
editor.on( 'elementsPathUpdate', function( evt ) { convertInlineHandlers( evt.data.space ); } );
});
editor.on( 'contentDom', function()
{
// Hyperlinks are enabled in editable documents in Adobe
// AIR. Prevent their click behavior.
editor.document.on( 'click', function( ev )
{
ev.data.preventDefault( true );
});
});
}
});
CKEDITOR.ui.on( 'ready', function( evt )
{
var ui = evt.data;
// richcombo, panelbutton and menu
if ( ui._.panel )
{
var panel = ui._.panel._.panel,
holder;
( function()
{
// Adding dom event listeners off-line are not supported in AIR,
// waiting for panel iframe loaded.
if ( !panel.isLoaded )
{
setTimeout( arguments.callee, 30 );
return;
}
holder = panel._.holder;
convertInlineHandlers( holder );
})();
}
else if ( ui instanceof CKEDITOR.dialog )
convertInlineHandlers( ui._.element );
});
})();
CKEDITOR.dom.document.prototype.write = CKEDITOR.tools.override( CKEDITOR.dom.document.prototype.write,
function( original_write )
{
function appendElement( parent, tagName, fullTag, text )
{
var node = parent.append( tagName ),
attrs = CKEDITOR.htmlParser.fragment.fromHtml( fullTag ).children[ 0 ].attributes;
attrs && node.setAttributes( attrs );
text && node.append( parent.getDocument().createText( text ) );
}
return function( html, mode )
{
// document.write() or document.writeln() fail silently after
// the page load event in Adobe AIR.
// DOM manipulation could be used instead.
if ( this.getBody() )
{
// We're taking the below extra work only because innerHTML
// on <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,152 @@
/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.ajax} object, which holds ajax methods for
* data loading.
*/
(function()
{
CKEDITOR.plugins.add( 'ajax',
{
requires : [ 'xml' ]
});
/**
* Ajax methods for data loading.
* @namespace
* @example
*/
CKEDITOR.ajax = (function()
{
var createXMLHttpRequest = function()
{
// In IE, using the native XMLHttpRequest for local files may throw
// "Access is Denied" errors.
if ( !CKEDITOR.env.ie || location.protocol != 'file:' )
try { return new XMLHttpRequest(); } catch(e) {}
try { return new ActiveXObject( 'Msxml2.XMLHTTP' ); } catch (e) {}
try { return new ActiveXObject( 'Microsoft.XMLHTTP' ); } catch (e) {}
return null;
};
var checkStatus = function( xhr )
{
// HTTP Status Codes:
// 2xx : Success
// 304 : Not Modified
// 0 : Returned when running locally (file://)
// 1223 : IE may change 204 to 1223 (see http://dev.jquery.com/ticket/1450)
return ( xhr.readyState == 4 &&
( ( xhr.status >= 200 && xhr.status < 300 ) ||
xhr.status == 304 ||
xhr.status === 0 ||
xhr.status == 1223 ) );
};
var getResponseText = function( xhr )
{
if ( checkStatus( xhr ) )
return xhr.responseText;
return null;
};
var getResponseXml = function( xhr )
{
if ( checkStatus( xhr ) )
{
var xml = xhr.responseXML;
return new CKEDITOR.xml( xml && xml.firstChild ? xml : xhr.responseText );
}
return null;
};
var load = function( url, callback, getResponseFn )
{
var async = !!callback;
var xhr = createXMLHttpRequest();
if ( !xhr )
return null;
xhr.open( 'GET', url, async );
if ( async )
{
// TODO: perform leak checks on this closure.
/** @ignore */
xhr.onreadystatechange = function()
{
if ( xhr.readyState == 4 )
{
callback( getResponseFn( xhr ) );
xhr = null;
}
};
}
xhr.send(null);
return async ? '' : getResponseFn( xhr );
};
return /** @lends CKEDITOR.ajax */ {
/**
* Loads data from an URL as plain text.
* @param {String} url The URL from which load data.
* @param {Function} [callback] A callback function to be called on
* data load. If not provided, the data will be loaded
* synchronously.
* @returns {String} The loaded data. For asynchronous requests, an
* empty string. For invalid requests, null.
* @example
* // Load data synchronously.
* var data = CKEDITOR.ajax.load( 'somedata.txt' );
* alert( data );
* @example
* // Load data asynchronously.
* var data = CKEDITOR.ajax.load( 'somedata.txt', function( data )
* {
* alert( data );
* } );
*/
load : function( url, callback )
{
return load( url, callback, getResponseText );
},
/**
* Loads data from an URL as XML.
* @param {String} url The URL from which load data.
* @param {Function} [callback] A callback function to be called on
* data load. If not provided, the data will be loaded
* synchronously.
* @returns {CKEDITOR.xml} An XML object holding the loaded data. For asynchronous requests, an
* empty string. For invalid requests, null.
* @example
* // Load XML synchronously.
* var xml = CKEDITOR.ajax.loadXml( 'somedata.xml' );
* alert( xml.getInnerXml( '//' ) );
* @example
* // Load XML asynchronously.
* var data = CKEDITOR.ajax.loadXml( 'somedata.xml', function( xml )
* {
* alert( xml.getInnerXml( '//' ) );
* } );
*/
loadXml : function( url, callback )
{
return load( url, callback, getResponseXml );
}
};
})();
})();

View File

@@ -0,0 +1,141 @@
/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
/**
* @file AutoGrow plugin
*/
(function(){
// Actual content height, figured out by appending check the last element's document position.
function contentHeight( scrollable )
{
var overflowY = scrollable.getStyle( 'overflow-y' );
var doc = scrollable.getDocument();
// Create a temporary marker element.
var marker = CKEDITOR.dom.element.createFromHtml( '<span style="margin:0;padding:0;border:0;clear:both;width:1px;height:1px;display:block;">' + ( CKEDITOR.env.webkit ? '&nbsp;' : '' ) + '</span>', doc );
doc[ CKEDITOR.env.ie? 'getBody' : 'getDocumentElement']().append( marker );
var height = marker.getDocumentPosition( doc ).y + marker.$.offsetHeight;
marker.remove();
scrollable.setStyle( 'overflow-y', overflowY );
return height;
}
var resizeEditor = function( editor )
{
if ( !editor.window )
return;
var doc = editor.document,
iframe = new CKEDITOR.dom.element( doc.getWindow().$.frameElement ),
body = doc.getBody(),
htmlElement = doc.getDocumentElement(),
currentHeight = editor.window.getViewPaneSize().height,
// Quirks mode overflows body, standards overflows document element
scrollable = doc.$.compatMode == 'BackCompat' ? body : htmlElement,
newHeight = contentHeight( scrollable );
// Additional space specified by user.
newHeight += ( editor.config.autoGrow_bottomSpace || 0 );
var min = editor.config.autoGrow_minHeight != undefined ? editor.config.autoGrow_minHeight : 200,
max = editor.config.autoGrow_maxHeight || Infinity;
newHeight = Math.max( newHeight, min );
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 );
}
if ( scrollable.$.scrollHeight > scrollable.$.clientHeight && newHeight < max )
scrollable.setStyle( 'overflow-y', 'hidden' );
else
scrollable.removeStyle( 'overflow-y' );
};
CKEDITOR.plugins.add( 'autogrow',
{
init : function( editor )
{
editor.addCommand( 'autogrow', { exec : resizeEditor, modes : { wysiwyg:1 }, readOnly: 1, canUndo: false, editorFocus: false } );
var eventsList = { contentDom:1, key:1, selectionChange:1, insertElement:1 };
editor.config.autoGrow_onStartup && ( eventsList[ 'instanceReady' ] = 1 );
for ( var eventName in eventsList )
{
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 );
// Second pass to make correction upon
// the first resize, e.g. scrollbar.
resizeEditor( evt.editor );
}, 100 );
}
});
}
}
});
})();
/**
* The minimum height that the editor can reach using the AutoGrow feature.
* @name CKEDITOR.config.autoGrow_minHeight
* @type Number
* @default <code>200</code>
* @since 3.4
* @example
* config.autoGrow_minHeight = 300;
*/
/**
* The maximum height that the editor can reach using the AutoGrow feature. Zero means unlimited.
* @name CKEDITOR.config.autoGrow_maxHeight
* @type Number
* @default <code>0</code>
* @since 3.4
* @example
* config.autoGrow_maxHeight = 400;
*/
/**
* Whether to have the auto grow happen on editor creation.
* @name CKEDITOR.config.autoGrow_onStartup
* @type Boolean
* @default false
* @since 3.6.2
* @example
* config.autoGrow_onStartup = true;
*/
/**
* Fired when the AutoGrow plugin is about to change the size of the editor.
* @name CKEDITOR.editor#autogrow
* @event
* @param {Number} data.currentHeight The current height of the editor (before resizing).
* @param {Number} data.newHeight The new height of the editor (after resizing). It can be changed
* to determine a different height value to be used instead.
*/
/**
* Extra height in pixel to leave between the bottom boundary of content with document size when auto resizing.
* @name CKEDITOR.config.autoGrow_bottomSpace
* @type Number
* @default 0
* @since 3.6.2
*/

View File

@@ -0,0 +1,129 @@
/*
Copyright (c) 2003-2011, 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.readOnly && 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 that applies the <strong>bold</strong> style to the text.
* @type Object
* @default <code>{ element : 'strong', overrides : 'b' }</code>
* @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 that applies the <em>italics</em> style to the text.
* @type Object
* @default <code>{ element : 'em', overrides : 'i' }</code>
* @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 that applies the <u>underline</u> style to the text.
* @type Object
* @default <code>{ element : 'u' }</code>
* @example
* CKEDITOR.config.coreStyles_underline =
* {
* element : 'span',
* attributes : { 'class' : 'Underline' }
* };
*/
CKEDITOR.config.coreStyles_underline = { element : 'u' };
/**
* The style definition that applies the <strike>strike-through</strike> style to the text.
* @type Object
* @default <code>{ element : 'strike' }</code>
* @example
* CKEDITOR.config.coreStyles_strike =
* {
* element : 'span',
* attributes : { 'class' : 'StrikeThrough' },
* overrides : 'strike'
* };
*/
CKEDITOR.config.coreStyles_strike = { element : 'strike' };
/**
* The style definition that applies the subscript style to the text.
* @type Object
* @default <code>{ element : 'sub' }</code>
* @example
* CKEDITOR.config.coreStyles_subscript =
* {
* element : 'span',
* attributes : { 'class' : 'Subscript' },
* overrides : 'sub'
* };
*/
CKEDITOR.config.coreStyles_subscript = { element : 'sub' };
/**
* The style definition that applies the superscript style to the text.
* @type Object
* @default <code>{ element : 'sup' }</code>
* @example
* CKEDITOR.config.coreStyles_superscript =
* {
* element : 'span',
* attributes : { 'class' : 'Superscript' },
* overrides : 'sup'
* };
*/
CKEDITOR.config.coreStyles_superscript = { element : 'sup' };

View File

@@ -0,0 +1,931 @@
/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
(function()
{
CKEDITOR.on( 'dialogDefinition', function( ev )
{
var tab, name = ev.data.name,
definition = ev.data.definition;
if ( name == 'link' )
{
definition.removeContents( 'target' );
definition.removeContents( 'upload' );
definition.removeContents( 'advanced' );
tab = definition.getContents( 'info' );
tab.remove( 'emailSubject' );
tab.remove( 'emailBody' );
}
else if ( name == 'image' )
{
definition.removeContents( 'advanced' );
tab = definition.getContents( 'Link' );
tab.remove( 'cmbTarget' );
tab = definition.getContents( 'info' );
tab.remove( 'txtAlt' );
tab.remove( 'basic' );
}
});
var bbcodeMap = { 'b' : 'strong', 'u': 'u', 'i' : 'em', 'color' : 'span', 'size' : 'span', 'quote' : 'blockquote', 'code' : 'code', 'url' : 'a', 'email' : 'span', 'img' : 'span', '*' : 'li', 'list' : 'ol' },
convertMap = { 'strong' : 'b' , 'b' : 'b', 'u': 'u', 'em' : 'i', 'i': 'i', 'code' : 'code', 'li' : '*' },
tagnameMap = { 'strong' : 'b', 'em' : 'i', 'u' : 'u', 'li' : '*', 'ul' : 'list', 'ol' : 'list', 'code' : 'code', 'a' : 'link', 'img' : 'img', 'blockquote' : 'quote' },
stylesMap = { 'color' : 'color', 'size' : 'font-size' },
attributesMap = { 'url' : 'href', 'email' : 'mailhref', 'quote': 'cite', 'list' : 'listType' };
// List of block-like tags.
var dtd = CKEDITOR.dtd,
blockLikeTags = CKEDITOR.tools.extend( { table:1 }, dtd.$block, dtd.$listItem, dtd.$tableContent, dtd.$list );
var semicolonFixRegex = /\s*(?:;\s*|$)/;
function serializeStyleText( stylesObject )
{
var styleText = '';
for ( var style in stylesObject )
{
var styleVal = stylesObject[ style ],
text = ( style + ':' + styleVal ).replace( semicolonFixRegex, ';' );
styleText += text;
}
return styleText;
}
function parseStyleText( styleText )
{
var retval = {};
( styleText || '' )
.replace( /&quot;/g, '"' )
.replace( /\s*([^ :;]+)\s*:\s*([^;]+)\s*(?=;|$)/g, function( match, name, value )
{
retval[ name.toLowerCase() ] = value;
} );
return retval;
}
function RGBToHex( cssStyle )
{
return cssStyle.replace( /(?:rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\))/gi, function( match, red, green, blue )
{
red = parseInt( red, 10 ).toString( 16 );
green = parseInt( green, 10 ).toString( 16 );
blue = parseInt( blue, 10 ).toString( 16 );
var color = [red, green, blue] ;
// Add padding zeros if the hex value is less than 0x10.
for ( var i = 0 ; i < color.length ; i++ )
color[i] = String( '0' + color[i] ).slice( -2 ) ;
return '#' + color.join( '' ) ;
});
}
// Maintain the map of smiley-to-description.
var smileyMap = {"smiley":":)","sad":":(","wink":";)","laugh":":D","cheeky":":P","blush":":*)","surprise":":-o","indecision":":|","angry":">:(","angel":"o:)","cool":"8-)","devil":">:-)","crying":";(","kiss":":-*" },
smileyReverseMap = {},
smileyRegExp = [];
// Build regexp for the list of smiley text.
for ( var i in smileyMap )
{
smileyReverseMap[ smileyMap[ i ] ] = i;
smileyRegExp.push( smileyMap[ i ].replace( /\(|\)|\:|\/|\*|\-|\|/g, function( match ) { return '\\' + match; } ) );
}
smileyRegExp = new RegExp( smileyRegExp.join( '|' ), 'g' );
var decodeHtml = ( function ()
{
var regex = [],
entities =
{
nbsp : '\u00A0', // IE | FF
shy : '\u00AD', // IE
gt : '\u003E', // IE | FF | -- | Opera
lt : '\u003C' // IE | FF | Safari | Opera
};
for ( var entity in entities )
regex.push( entity );
regex = new RegExp( '&(' + regex.join( '|' ) + ');', 'g' );
return function( html )
{
return html.replace( regex, function( match, entity )
{
return entities[ entity ];
});
};
})();
CKEDITOR.BBCodeParser = function()
{
this._ =
{
bbcPartsRegex : /(?:\[([^\/\]=]*?)(?:=([^\]]*?))?\])|(?:\[\/([a-z]{1,16})\])/ig
};
};
CKEDITOR.BBCodeParser.prototype =
{
parse : function( bbcode )
{
var parts,
part,
lastIndex = 0;
while ( ( parts = this._.bbcPartsRegex.exec( bbcode ) ) )
{
var tagIndex = parts.index;
if ( tagIndex > lastIndex )
{
var text = bbcode.substring( lastIndex, tagIndex );
this.onText( text, 1 );
}
lastIndex = this._.bbcPartsRegex.lastIndex;
/*
"parts" is an array with the following items:
0 : The entire match for opening/closing tags and line-break;
1 : line-break;
2 : open of tag excludes option;
3 : tag option;
4 : close of tag;
*/
part = ( parts[ 1 ] || parts[ 3 ] || '' ).toLowerCase();
// Unrecognized tags should be delivered as a simple text (#7860).
if ( part && !bbcodeMap[ part ] )
{
this.onText( parts[ 0 ] );
continue;
}
// Opening tag
if ( parts[ 1 ] )
{
var tagName = bbcodeMap[ part ],
attribs = {},
styles = {},
optionPart = parts[ 2 ];
if ( optionPart )
{
if ( part == 'list' )
{
if ( !isNaN( optionPart ) )
optionPart = 'decimal';
else if ( /^[a-z]+$/.test( optionPart ) )
optionPart = 'lower-alpha';
else if ( /^[A-Z]+$/.test( optionPart ) )
optionPart = 'upper-alpha';
}
if ( stylesMap[ part ] )
{
// Font size represents percentage.
if ( part == 'size' )
optionPart += '%';
styles[ stylesMap[ part ] ] = optionPart;
attribs.style = serializeStyleText( styles );
}
else if ( attributesMap[ part ] )
attribs[ attributesMap[ part ] ] = optionPart;
}
// Two special handling - image and email, protect them
// as "span" with an attribute marker.
if ( part == 'email' || part == 'img' )
attribs[ 'bbcode' ] = part;
this.onTagOpen( tagName, attribs, CKEDITOR.dtd.$empty[ tagName ] );
}
// Closing tag
else if ( parts[ 3 ] )
this.onTagClose( bbcodeMap[ part ] );
}
if ( bbcode.length > lastIndex )
this.onText( bbcode.substring( lastIndex, bbcode.length ), 1 );
}
};
/**
* Creates a {@link CKEDITOR.htmlParser.fragment} from an HTML string.
* @param {String} source The HTML to be parsed, filling the fragment.
* @param {Number} [fixForBody=false] Wrap body with specified element if needed.
* @returns CKEDITOR.htmlParser.fragment The fragment created.
* @example
* var fragment = CKEDITOR.htmlParser.fragment.fromHtml( '<b>Sample</b> Text' );
* alert( fragment.children[0].name ); "b"
* alert( fragment.children[1].value ); " Text"
*/
CKEDITOR.htmlParser.fragment.fromBBCode = function( source )
{
var parser = new CKEDITOR.BBCodeParser(),
fragment = new CKEDITOR.htmlParser.fragment(),
pendingInline = [],
pendingBrs = 0,
currentNode = fragment,
returnPoint;
function checkPending( newTagName )
{
if ( pendingInline.length > 0 )
{
for ( var i = 0 ; i < pendingInline.length ; i++ )
{
var pendingElement = pendingInline[ i ],
pendingName = pendingElement.name,
pendingDtd = CKEDITOR.dtd[ pendingName ],
currentDtd = currentNode.name && CKEDITOR.dtd[ currentNode.name ];
if ( ( !currentDtd || currentDtd[ pendingName ] ) && ( !newTagName || !pendingDtd || pendingDtd[ newTagName ] || !CKEDITOR.dtd[ newTagName ] ) )
{
// Get a clone for the pending element.
pendingElement = pendingElement.clone();
// Add it to the current node and make it the current,
// so the new element will be added inside of it.
pendingElement.parent = currentNode;
currentNode = pendingElement;
// Remove the pending element (back the index by one
// to properly process the next entry).
pendingInline.splice( i, 1 );
i--;
}
}
}
}
function checkPendingBrs( tagName, closing )
{
var len = currentNode.children.length,
previous = len > 0 && currentNode.children[ len - 1 ],
lineBreakParent = !previous && BBCodeWriter.getRule( tagnameMap[ currentNode.name ], 'breakAfterOpen' ),
lineBreakPrevious = previous && previous.type == CKEDITOR.NODE_ELEMENT && BBCodeWriter.getRule( tagnameMap[ previous.name ], 'breakAfterClose' ),
lineBreakCurrent = tagName && BBCodeWriter.getRule( tagnameMap[ tagName ], closing ? 'breakBeforeClose' : 'breakBeforeOpen' );
if ( pendingBrs && ( lineBreakParent || lineBreakPrevious || lineBreakCurrent ) )
pendingBrs--;
// 1. Either we're at the end of block, where it requires us to compensate the br filler
// removing logic (from htmldataprocessor).
// 2. Or we're at the end of pseudo block, where it requires us to compensate
// the bogus br effect.
if ( pendingBrs && tagName in blockLikeTags )
pendingBrs++;
while ( pendingBrs && pendingBrs-- )
currentNode.children.push( previous = new CKEDITOR.htmlParser.element( 'br' ) );
}
function addElement( node, target )
{
checkPendingBrs( node.name, 1 );
target = target || currentNode || fragment;
var len = target.children.length,
previous = len > 0 && target.children[ len - 1 ] || null;
node.previous = previous;
node.parent = target;
target.children.push( node );
if ( node.returnPoint )
{
currentNode = node.returnPoint;
delete node.returnPoint;
}
}
parser.onTagOpen = function( tagName, attributes, selfClosing )
{
var element = new CKEDITOR.htmlParser.element( tagName, attributes );
// This is a tag to be removed if empty, so do not add it immediately.
if ( CKEDITOR.dtd.$removeEmpty[ tagName ] )
{
pendingInline.push( element );
return;
}
var currentName = currentNode.name;
var currentDtd = currentName
&& ( CKEDITOR.dtd[ currentName ]
|| ( currentNode._.isBlockLike ? CKEDITOR.dtd.div : CKEDITOR.dtd.span ) );
// If the element cannot be child of the current element.
if ( currentDtd && !currentDtd[ tagName ] )
{
var reApply = false,
addPoint; // New position to start adding nodes.
// If the element name is the same as the current element name,
// then just close the current one and append the new one to the
// parent. This situation usually happens with <p>, <li>, <dt> and
// <dd>, specially in IE. Do not enter in this if block in this case.
if ( tagName == currentName )
addElement( currentNode, currentNode.parent );
else if ( tagName in CKEDITOR.dtd.$listItem )
{
parser.onTagOpen( 'ul', {} );
addPoint = currentNode;
reApply = true;
}
else
{
addElement( currentNode, currentNode.parent );
// The current element is an inline element, which
// cannot hold the new one. Put it in the pending list,
// and try adding the new one after it.
pendingInline.unshift( currentNode );
reApply = true;
}
if ( addPoint )
currentNode = addPoint;
// Try adding it to the return point, or the parent element.
else
currentNode = currentNode.returnPoint || currentNode.parent;
if ( reApply )
{
parser.onTagOpen.apply( this, arguments );
return;
}
}
checkPending( tagName );
checkPendingBrs( tagName );
element.parent = currentNode;
element.returnPoint = returnPoint;
returnPoint = 0;
if ( element.isEmpty )
addElement( element );
else
currentNode = element;
};
parser.onTagClose = function( tagName )
{
// Check if there is any pending tag to be closed.
for ( var i = pendingInline.length - 1 ; i >= 0 ; i-- )
{
// If found, just remove it from the list.
if ( tagName == pendingInline[ i ].name )
{
pendingInline.splice( i, 1 );
return;
}
}
var pendingAdd = [],
newPendingInline = [],
candidate = currentNode;
while ( candidate.type && candidate.name != tagName )
{
// If this is an inline element, add it to the pending list, if we're
// really closing one of the parents element later, they will continue
// after it.
if ( !candidate._.isBlockLike )
newPendingInline.unshift( candidate );
// This node should be added to it's parent at this point. But,
// it should happen only if the closing tag is really closing
// one of the nodes. So, for now, we just cache it.
pendingAdd.push( candidate );
candidate = candidate.parent;
}
if ( candidate.type )
{
// Add all elements that have been found in the above loop.
for ( i = 0 ; i < pendingAdd.length ; i++ )
{
var node = pendingAdd[ i ];
addElement( node, node.parent );
}
currentNode = candidate;
addElement( candidate, candidate.parent );
// The parent should start receiving new nodes now, except if
// addElement changed the currentNode.
if ( candidate == currentNode )
currentNode = currentNode.parent;
pendingInline = pendingInline.concat( newPendingInline );
}
};
parser.onText = function( text )
{
var currentDtd = CKEDITOR.dtd[ currentNode.name ];
if ( !currentDtd || currentDtd[ '#' ] )
{
checkPendingBrs();
checkPending();
text.replace(/([\r\n])|[^\r\n]*/g, function( piece, lineBreak )
{
if ( lineBreak !== undefined && lineBreak.length )
pendingBrs++;
else if ( piece.length )
{
var lastIndex = 0;
// Create smiley from text emotion.
piece.replace( smileyRegExp, function( match, index )
{
addElement( new CKEDITOR.htmlParser.text( piece.substring( lastIndex, index ) ), currentNode );
addElement( new CKEDITOR.htmlParser.element( 'smiley', { 'desc': smileyReverseMap[ match ] } ), currentNode );
lastIndex = index + match.length;
});
if ( lastIndex != piece.length )
addElement( new CKEDITOR.htmlParser.text( piece.substring( lastIndex, piece.length ) ), currentNode );
}
});
}
};
// Parse it.
parser.parse( CKEDITOR.tools.htmlEncode( source ) );
// Close all hanging nodes.
while ( currentNode.type )
{
var parent = currentNode.parent,
node = currentNode;
addElement( node, parent );
currentNode = parent;
}
return fragment;
};
CKEDITOR.htmlParser.BBCodeWriter = CKEDITOR.tools.createClass(
{
$ : function()
{
this._ =
{
output : [],
rules : []
};
// List and list item.
this.setRules( 'list',
{
breakBeforeOpen : 1,
breakAfterOpen : 1,
breakBeforeClose : 1,
breakAfterClose : 1
} );
this.setRules( '*',
{
breakBeforeOpen : 1,
breakAfterOpen : 0,
breakBeforeClose : 1,
breakAfterClose : 0
} );
this.setRules( 'quote',
{
breakBeforeOpen : 1,
breakAfterOpen : 0,
breakBeforeClose : 0,
breakAfterClose : 1
} );
},
proto :
{
/**
* Sets formatting rules for a given tag. The possible rules are:
* <ul>
* <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.
*
* @param {String} tagName The tag 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( 'list',
* {
* breakBeforeOpen : true
* breakAfterOpen : true
* });
*/
setRules : function( tagName, rules )
{
var currentRules = this._.rules[ tagName ];
if ( currentRules )
CKEDITOR.tools.extend( currentRules, rules, true );
else
this._.rules[ tagName ] = rules;
},
getRule : function( tagName, ruleName )
{
return this._.rules[ tagName ] && this._.rules[ tagName ][ ruleName ];
},
openTag : function( tag, attributes )
{
if ( tag in bbcodeMap )
{
if ( this.getRule( tag, 'breakBeforeOpen' ) )
this.lineBreak( 1 );
this.write( '[', tag );
var option = attributes.option;
option && this.write( '=', option );
this.write( ']' );
if ( this.getRule( tag, 'breakAfterOpen' ) )
this.lineBreak( 1 );
}
else if ( tag == 'br' )
this._.output.push( '\n' );
},
openTagClose : function() { },
attribute : function() { },
closeTag : function( tag )
{
if ( tag in bbcodeMap )
{
if ( this.getRule( tag, 'breakBeforeClose' ) )
this.lineBreak( 1 );
tag != '*' && this.write( '[/', tag, ']' );
if ( this.getRule( tag, 'breakAfterClose' ) )
this.lineBreak( 1 );
}
},
text : function( text )
{
this.write( text );
},
/**
* Writes a comment.
* @param {String} comment The comment text.
* @example
* // Writes "&lt;!-- My comment --&gt;".
* writer.comment( ' My comment ' );
*/
comment : function() {},
/*
* Output line-break for formatting.
*/
lineBreak : function()
{
// Avoid line break when:
// 1) Previous tag already put one.
// 2) We're at output start.
if ( !this._.hasLineBreak && this._.output.length )
{
this.write( '\n' );
this._.hasLineBreak = 1;
}
},
write : function()
{
this._.hasLineBreak = 0;
var data = Array.prototype.join.call( arguments, '' );
this._.output.push( data );
},
reset : function()
{
this._.output = [];
this._.hasLineBreak = 0;
},
getHtml : function( reset )
{
var bbcode = this._.output.join( '' );
if ( reset )
this.reset();
return decodeHtml ( bbcode );
}
}
});
var BBCodeWriter = new CKEDITOR.htmlParser.BBCodeWriter();
CKEDITOR.plugins.add( 'bbcode',
{
requires : [ 'htmldataprocessor', 'entities' ],
beforeInit : function( editor )
{
// Adapt some critical editor configuration for better support
// of BBCode environment.
var config = editor.config;
CKEDITOR.tools.extend( config,
{
enterMode : CKEDITOR.ENTER_BR,
basicEntities: false,
entities : false,
fillEmptyBlocks : false
}, true );
},
init : function( editor )
{
var config = editor.config;
function BBCodeToHtml( code )
{
var fragment = CKEDITOR.htmlParser.fragment.fromBBCode( code ),
writer = new CKEDITOR.htmlParser.basicWriter();
fragment.writeHtml( writer, dataFilter );
return writer.getHtml( true );
}
var dataFilter = new CKEDITOR.htmlParser.filter();
dataFilter.addRules(
{
elements :
{
'blockquote' : function( element )
{
var quoted = new CKEDITOR.htmlParser.element( 'div' );
quoted.children = element.children;
element.children = [ quoted ];
var citeText = element.attributes.cite;
if ( citeText )
{
var cite = new CKEDITOR.htmlParser.element( 'cite' );
cite.add( new CKEDITOR.htmlParser.text( citeText.replace( /^"|"$/g, '' ) ) );
delete element.attributes.cite;
element.children.unshift( cite );
}
},
'span' : function( element )
{
var bbcode;
if ( ( bbcode = element.attributes.bbcode ) )
{
if ( bbcode == 'img' )
{
element.name = 'img';
element.attributes.src = element.children[ 0 ].value;
element.children = [];
}
else if ( bbcode == 'email' )
{
element.name = 'a';
element.attributes.href = 'mailto:' + element.children[ 0 ].value;
}
delete element.attributes.bbcode;
}
},
'ol' : function ( element )
{
if ( element.attributes.listType )
{
if ( element.attributes.listType != 'decimal' )
element.attributes.style = 'list-style-type:' + element.attributes.listType;
}
else
element.name = 'ul';
delete element.attributes.listType;
},
a : function( element )
{
if ( !element.attributes.href )
element.attributes.href = element.children[ 0 ].value;
},
'smiley' : function( element )
{
element.name = 'img';
var description = element.attributes.desc,
image = config.smiley_images[ CKEDITOR.tools.indexOf( config.smiley_descriptions, description ) ],
src = CKEDITOR.tools.htmlEncode( config.smiley_path + image );
element.attributes =
{
src : src,
'data-cke-saved-src' : src,
title : description,
alt : description
};
}
}
} );
editor.dataProcessor.htmlFilter.addRules(
{
elements :
{
$ : function( element )
{
var attributes = element.attributes,
style = parseStyleText( attributes.style ),
value;
var tagName = element.name;
if ( tagName in convertMap )
tagName = convertMap[ tagName ];
else if ( tagName == 'span' )
{
if ( ( value = style.color ) )
{
tagName = 'color';
value = RGBToHex( value );
}
else if ( ( value = style[ 'font-size' ] ) )
{
var percentValue = value.match( /(\d+)%$/ );
if ( percentValue )
{
value = percentValue[ 1 ];
tagName = 'size';
}
}
}
else if ( tagName == 'ol' || tagName == 'ul' )
{
if ( ( value = style[ 'list-style-type'] ) )
{
switch ( value )
{
case 'lower-alpha':
value = 'a';
break;
case 'upper-alpha':
value = 'A';
break;
}
}
else if ( tagName == 'ol' )
value = 1;
tagName = 'list';
}
else if ( tagName == 'blockquote' )
{
try
{
var cite = element.children[ 0 ],
quoted = element.children[ 1 ],
citeText = cite.name == 'cite' && cite.children[ 0 ].value;
if ( citeText )
{
value = '"' + citeText + '"';
element.children = quoted.children;
}
}
catch( er )
{
}
tagName = 'quote';
}
else if ( tagName == 'a' )
{
if ( ( value = attributes.href ) )
{
if ( value.indexOf( 'mailto:' ) !== -1 )
{
tagName = 'email';
// [email] should have a single text child with email address.
element.children = [ new CKEDITOR.htmlParser.text( value.replace( 'mailto:', '' ) ) ];
value = '';
}
else
{
var singleton = element.children.length == 1 && element.children[ 0 ];
if ( singleton
&& singleton.type == CKEDITOR.NODE_TEXT
&& singleton.value == value )
value = '';
tagName = 'url';
}
}
}
else if ( tagName == 'img' )
{
element.isEmpty = 0;
// Translate smiley (image) to text emotion.
var src = attributes[ 'data-cke-saved-src' ];
if ( src && src.indexOf( editor.config.smiley_path ) != -1 )
return new CKEDITOR.htmlParser.text( smileyMap[ attributes.alt ] );
else
element.children = [ new CKEDITOR.htmlParser.text( src ) ];
}
element.name = tagName;
value && ( element.attributes.option = value );
return null;
},
// Remove any bogus br from the end of a pseudo block,
// e.g. <div>some text<br /><p>paragraph</p></div>
br : function( element )
{
var next = element.next;
if ( next && next.name in blockLikeTags )
return false;
}
}
}, 1 );
editor.dataProcessor.writer = BBCodeWriter;
editor.on( 'beforeSetMode', function( evt )
{
evt.removeListener();
var wysiwyg = editor._.modes[ 'wysiwyg' ];
wysiwyg.loadData = CKEDITOR.tools.override( wysiwyg.loadData, function( org )
{
return function( data )
{
return ( org.call( this, BBCodeToHtml( data ) ) );
};
} );
} );
},
afterInit : function( editor )
{
var filters;
if ( editor._.elementsPath )
{
// Eliminate irrelevant elements from displaying, e.g body and p.
if ( ( filters = editor._.elementsPath.filters ) )
filters.push( function( element )
{
var htmlName = element.getName(),
name = tagnameMap[ htmlName ] || false;
// Specialized anchor presents as email.
if ( name == 'link' && element.getAttribute( 'href' ).indexOf( 'mailto:' ) === 0 )
name = 'email';
// Styled span could be either size or color.
else if ( htmlName == 'span' )
{
if ( element.getStyle( 'font-size' ) )
name = 'size';
else if ( element.getStyle( 'color' ) )
name = 'color';
}
else if ( name == 'img' )
{
var src = element.data( 'cke-saved-src' );
if ( src && src.indexOf( editor.config.smiley_path ) === 0 )
name = 'smiley';
}
return name;
});
}
}
} );
})();

View File

@@ -0,0 +1,334 @@
/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
(function()
{
var guardElements = { table:1, ul:1, ol:1, blockquote:1, div: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;
if ( editor.readOnly )
return;
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 we're having BODY here, user probably done CTRL+A, let's try to get the enclosed node, if any.
if ( selectedElement.is( 'body' ) )
{
var enclosedNode = editor.getSelection().getRanges()[ 0 ].getEnclosedNode();
enclosedNode && enclosedNode.type == CKEDITOR.NODE_ELEMENT && ( selectedElement = enclosedNode );
}
if ( !selectedElement )
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,
directionNode = evt.data.path.block || evt.data.path.blockLimit;
editor.fire( 'contentDirChanged', directionNode ? directionNode.getComputedStyle( 'direction' ) : editor.lang.dir );
}
/**
* 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 )
{
if ( element.isReadOnly() )
return;
// 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;
}
}
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;
// 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 );
editor.forceNextSelectionCheck();
return;
}
function getFullySelected( range, elements, enterMode )
{
var ancestor = range.getCommonAncestor( false, true );
range = range.clone();
range.enlarge( enterMode == CKEDITOR.ENTER_BR ?
CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS
: 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 ) )
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, enterMode );
selectedElement && 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' ) ) )
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 );
editor.on( 'contentDom', function()
{
editor.document.on( 'dirChanged', function( evt )
{
editor.fire( 'dirChanged',
{
node : evt.data,
dir : evt.data.getDirection( 1 )
} );
});
});
}
});
// 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)
function isOffline( el )
{
var html = el.getDocument().getBody().getParent();
while ( el )
{
if ( el.equals( html ) )
return false;
el = el.getParent();
}
return true;
}
function dirChangeNotifier( org )
{
var isAttribute = org == elementProto.setAttribute,
isRemoveAttribute = org == elementProto.removeAttribute,
dirStyleRegexp = /\bdirection\s*:\s*(.*?)\s*(:?$|;)/;
return function( name, val )
{
if ( !this.getDocument().equals( CKEDITOR.document ) )
{
var orgDir;
if ( ( name == ( isAttribute || isRemoveAttribute ? 'dir' : 'direction' ) ||
name == 'style' && ( isRemoveAttribute || dirStyleRegexp.test( val ) ) ) && !isOffline( this ) )
{
orgDir = this.getDirection( 1 );
var retval = org.apply( this, arguments );
if ( orgDir != this.getDirection( 1 ) )
{
this.getDocument().fire( 'dirChanged', this );
return retval;
}
}
}
return org.apply( this, arguments );
};
}
var elementProto = CKEDITOR.dom.element.prototype,
methods = [ 'setStyle', 'removeStyle', 'setAttribute', 'removeAttribute' ];
for ( var i = 0; i < methods.length; i++ )
elementProto[ methods[ i ] ] = CKEDITOR.tools.override( elementProto[ methods [ i ] ], dirChangeNotifier );
})();
/**
* Fired when the language direction of an element is changed
* @name CKEDITOR.editor#dirChanged
* @event
* @param {CKEDITOR.editor} editor This editor instance.
* @param {Object} eventData.node The element that is being changed.
* @param {String} eventData.dir The new direction.
*/
/**
* Fired when the language direction in the specific cursor position is changed
* @name CKEDITOR.editor#contentDirChanged
* @event
* @param {String} eventData The direction in the current position.
*/

View File

@@ -0,0 +1,305 @@
/*
Copyright (c) 2003-2011, 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;
if ( editor.readOnly )
return;
var 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;
iterator.enlargeBr = editor.config.enterMode != CKEDITOR.ENTER_BR;
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,290 @@
/*
Copyright (c) 2003-2011, 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 = 'button';
/**
* 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 );
}
};
( function()
{
CKEDITOR.ui.button.prototype =
{
/**
* 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;
this._.editor = editor;
var instance =
{
id : id,
button : this,
editor : editor,
focus : function()
{
var element = CKEDITOR.document.getById( id );
element.focus();
},
execute : function()
{
// IE 6 needs some time before execution (#7922)
if ( CKEDITOR.env.ie && CKEDITOR.env.version < 7 )
CKEDITOR.tools.setTimeout( function(){ this.button.click( editor ); }, 0, this );
else
this.button.click( editor );
}
};
var keydownFn = CKEDITOR.tools.addFunction( function( ev )
{
if ( instance.onkey )
{
ev = new CKEDITOR.dom.event( ev );
return ( instance.onkey( instance, ev.getKeystroke() ) !== false );
}
});
var focusFn = CKEDITOR.tools.addFunction( function( ev )
{
var 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;
});
instance.clickFn = clickFn = CKEDITOR.tools.addFunction( instance.execute, instance );
// Indicate a mode sensitive button.
if ( this.modes )
{
var modeStates = {};
function updateState()
{
// "this" is a CKEDITOR.ui.button instance.
var mode = editor.mode;
if ( mode )
{
// Restore saved button state.
var state = this.modes[ mode ] ? modeStates[ mode ] != undefined ? modeStates[ mode ] :
CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED;
this.setState( editor.readOnly && !this.readOnly ? CKEDITOR.TRISTATE_DISABLED : state );
}
}
editor.on( 'beforeModeUnload', function()
{
if ( editor.mode && this._.state != CKEDITOR.TRISTATE_DISABLED )
modeStates[ editor.mode ] = this._.state;
}, this );
editor.on( 'mode', updateState, this);
// If this button is sensitive to readOnly state, update it accordingly.
!this.readOnly && editor.on( 'readOnly', updateState, 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' + ( this.icon && this.icon.indexOf( '.png' ) == -1 ? ' cke_noalphafix' : '' ) + '">',
'<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, ', event);"' +
' onfocus="return CKEDITOR.tools.callFunction(', focusFn,', event);" ' +
( CKEDITOR.env.ie ? 'onclick="return false;" onmouseup' : 'onclick' ) + // #188
'="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} name The button name.
* @param {Object} definition 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 );
};

View File

@@ -0,0 +1,223 @@
/*
Copyright (c) 2003-2011, 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;
this.setupContent();
},
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()
{
this.commitContent();
},
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 );
},
setup : function()
{
var dialog = this.getDialog();
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, dialog ) + ', 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="' + dialog.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().$ );
}, dialog );
iframe.setCustomData( 'dialog', dialog );
var container = this.getElement();
container.setHtml( '' );
container.append( iframe );
// IE need a redirect on focus to make
// the cursor blinking inside iframe. (#5461)
if ( CKEDITOR.env.ie )
{
var focusGrabber = CKEDITOR.dom.element.createFromHtml( '<span tabindex="-1" style="position:absolute;" role="presentation"></span>' );
focusGrabber.on( 'focus', function()
{
iframe.$.contentWindow.focus();
});
container.append( focusGrabber );
// Override focus handler on field.
this.focus = function()
{
focusGrabber.focus();
this.fire( 'focus' );
};
}
this.getInputElement = function(){ return iframe; };
// Force container to scale in IE.
if ( CKEDITOR.env.ie )
{
container.setStyle( 'display', 'block' );
container.setStyle( 'height', ( iframe.$.offsetHeight + 2 ) + 'px' );
}
},
commit : function( data )
{
var container = this.getElement(),
editor = this.getDialog().getParentEditor(),
body = this.getInputElement().getFrameDocument().getBody(),
bogus = body.getBogus(),
html;
bogus && bogus.remove();
// Saving the contents so changes until paste is complete will not take place (#7500)
html = body.getHtml();
setTimeout( function(){
editor.fire( 'paste', { 'html' : html } );
}, 0 );
}
}
]
}
]
};
});

View File

@@ -0,0 +1,453 @@
/*
Copyright (c) 2003-2011, 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.
this.startDisabled = true;
};
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 );
}
};
function cancel( evt ) { evt.cancel(); }
// 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();
this.on( 'selectionChange', cancel, null, null, 0 );
// Turn off design mode temporarily before give focus to the paste bin.
if ( mode == 'text' )
pastebin.$.focus();
else
{
range.setStartAt( pastebin, CKEDITOR.POSITION_AFTER_START );
range.setEndAt( pastebin, CKEDITOR.POSITION_BEFORE_END );
range.select( true );
}
var editor = this;
// Wait a while and grab the pasted contents
window.setTimeout( function()
{
mode == 'text' && CKEDITOR.env.gecko && editor.focusGrabber.focus();
pastebin.remove();
editor.removeListener( 'selectionChange', cancel );
// 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 );
}
}
var depressBeforeEvent;
function stateFromNamedCommand( command, editor )
{
// 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 = CKEDITOR.TRISTATE_OFF;
try { retval = editor.document.$.queryCommandEnabled( command ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED; }catch( er ){}
depressBeforeEvent = 0;
return retval;
}
var inReadOnly;
function setToolbarStates()
{
if ( this.mode != 'wysiwyg' )
return;
this.getCommand( 'cut' ).setState( inReadOnly ? CKEDITOR.TRISTATE_DISABLED : stateFromNamedCommand( 'Cut', this ) );
this.getCommand( 'copy' ).setState( stateFromNamedCommand( 'Copy', this ) );
var pasteState = inReadOnly ? CKEDITOR.TRISTATE_DISABLED :
CKEDITOR.env.webkit ? CKEDITOR.TRISTATE_OFF : stateFromNamedCommand( 'Paste', this );
this.fire( 'pasteState', pasteState );
}
// 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' ] );
setTimeout( function () { editor.fire( 'afterPaste' ); }, 0 );
}, null, null, 1000 );
editor.on( 'pasteDialog', function( evt )
{
setTimeout( function()
{
// Open default paste dialog.
editor.openDialog( 'paste' );
}, 0 );
});
editor.on( 'pasteState', function( evt )
{
editor.getCommand( 'paste' ).setState( evt.data );
});
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 );
// 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( CKEDITOR.env.webkit ? 'paste' : 'beforepaste', function( evt )
{
if ( depressBeforeEvent )
return;
// Fire 'beforePaste' event so clipboard flavor get customized
// by other plugins.
var eventData = { mode : 'html' };
editor.fire( 'beforePaste', eventData );
getClipboardData.call( editor, evt, eventData.mode, function ( data )
{
// The very last guard to make sure the
// paste has successfully happened.
if ( !( data = CKEDITOR.tools.trim( data.replace( /<span[^>]+data-cke-bookmark[^<]*?<\/span>/ig,'' ) ) ) )
return;
var dataTransfer = {};
dataTransfer[ eventData.mode ] = data;
editor.fire( 'paste', dataTransfer );
} );
});
// Dismiss the (wrong) 'beforepaste' event fired on context menu open. (#7953)
body.on( 'contextmenu', function()
{
depressBeforeEvent = 1;
setTimeout( function() { depressBeforeEvent = 0; }, 10 );
});
body.on( 'beforecut', function() { !depressBeforeEvent && fixCut( editor ); } );
body.on( 'mouseup', function(){ setTimeout( function(){ setToolbarStates.call( editor ); }, 0 ); }, editor );
body.on( 'keyup', setToolbarStates, editor );
});
// For improved performance, we're checking the readOnly state on selectionChange instead of hooking a key event for that.
editor.on( 'selectionChange', function( evt )
{
inReadOnly = evt.data.selection.getRanges()[ 0 ].checkReadOnly();
setToolbarStates.call( editor );
});
// If the "contextmenu" plugin is loaded, register the listeners.
if ( editor.contextMenu )
{
editor.contextMenu.addListener( function( element, selection )
{
var readOnly = selection.getRanges()[ 0 ].checkReadOnly();
return {
cut : !readOnly && stateFromNamedCommand( 'Cut', editor ),
copy : stateFromNamedCommand( 'Copy', editor ),
paste : !readOnly && ( CKEDITOR.env.webkit ? CKEDITOR.TRISTATE_OFF : stateFromNamedCommand( 'Paste', editor ) )
};
});
}
}
});
})();
/**
* 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.
*/
/**
* Internal event to open the Paste dialog
* @name CKEDITOR.editor#pasteDialog
* @event
*/

View File

@@ -0,0 +1,301 @@
/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
/**
* @fileOverview The "colorbutton" plugin that makes it possible to assign
* text and background colors to editor contents.
*
*/
CKEDITOR.plugins.add( 'colorbutton',
{
requires : [ 'panelbutton', 'floatpanel', 'styles' ],
init : function( editor )
{
var config = editor.config,
lang = editor.lang.colorButton;
var clickFn;
if ( !CKEDITOR.env.hc )
{
addButton( 'TextColor', 'fore', lang.textColorTitle );
addButton( 'BGColor', 'back', lang.bgColorTitle );
}
function addButton( name, type, title )
{
var colorBoxId = CKEDITOR.tools.getNextId() + '_colorBox';
editor.ui.add( name, CKEDITOR.UI_PANELBUTTON,
{
label : title,
title : title,
className : 'cke_button_' + name.toLowerCase(),
modes : { wysiwyg : 1 },
panel :
{
css : editor.skin.editor.css,
attributes : { role : 'listbox', 'aria-label' : lang.panelTitle }
},
onBlock : function( panel, block )
{
block.autoSize = true;
block.element.addClass( 'cke_colorblock' );
block.element.setHtml( renderColors( panel, type, colorBoxId ) );
// The block should not have scrollbars (#5933, #6056)
block.element.getDocument().getBody().setStyle( 'overflow', 'hidden' );
CKEDITOR.ui.fire( 'ready', this );
var keys = block.keys;
var rtl = editor.lang.dir == 'rtl';
keys[ rtl ? 37 : 39 ] = 'next'; // ARROW-RIGHT
keys[ 40 ] = 'next'; // ARROW-DOWN
keys[ 9 ] = 'next'; // TAB
keys[ rtl ? 39 : 37 ] = 'prev'; // ARROW-LEFT
keys[ 38 ] = 'prev'; // ARROW-UP
keys[ CKEDITOR.SHIFT + 9 ] = 'prev'; // SHIFT + TAB
keys[ 32 ] = 'click'; // SPACE
},
// The automatic colorbox should represent the real color (#6010)
onOpen : function()
{
var selection = editor.getSelection(),
block = selection && selection.getStartElement(),
path = new CKEDITOR.dom.elementPath( block ),
color;
// Find the closest block element.
block = path.block || path.blockLimit || editor.document.getBody();
// The background color might be transparent. In that case, look up the color in the DOM tree.
do
{
color = block && block.getComputedStyle( type == 'back' ? 'background-color' : 'color' ) || 'transparent';
}
while ( type == 'back' && color == 'transparent' && block && ( block = block.getParent() ) );
// The box should never be transparent.
if ( !color || color == 'transparent' )
color = '#ffffff';
this._.panel._.iframe.getFrameDocument().getById( colorBoxId ).setStyle( 'background-color', color );
}
});
}
function renderColors( panel, type, colorBoxId )
{
var output = [],
colors = config.colorButton_colors.split( ',' ),
total = colors.length + ( config.colorButton_enableMore ? 2 : 1 );
var clickFn = CKEDITOR.tools.addFunction( function( color, type )
{
if ( color == '?' )
{
var applyColorStyle = arguments.callee;
function onColorDialogClose( evt )
{
this.removeListener( 'ok', onColorDialogClose );
this.removeListener( 'cancel', onColorDialogClose );
evt.name == 'ok' && applyColorStyle( this.getContentElement( 'picker', 'selectedColor' ).getValue(), type );
}
editor.openDialog( 'colordialog', function()
{
this.on( 'ok', onColorDialogClose );
this.on( 'cancel', onColorDialogClose );
} );
return;
}
editor.focus();
panel.hide( false );
editor.fire( 'saveSnapshot' );
// Clean up any conflicting style within the range.
new CKEDITOR.style( config['colorButton_' + type + 'Style'], { color : 'inherit' } ).remove( editor.document );
if ( color )
{
var colorStyle = config['colorButton_' + type + 'Style'];
colorStyle.childRule = type == 'back' ?
function( element )
{
// It's better to apply background color as the innermost style. (#3599)
// Except for "unstylable elements". (#6103)
return isUnstylable( element );
}
:
function( element )
{
// Fore color style must be applied inside links instead of around it. (#4772,#6908)
return !( element.is( 'a' ) || element.getElementsByTag( 'a' ).count() ) || isUnstylable( element );
};
new CKEDITOR.style( colorStyle, { color : color } ).apply( editor.document );
}
editor.fire( 'saveSnapshot' );
});
// Render the "Automatic" button.
output.push(
'<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-nostyle' );
}
}
});
/**
* Whether to enable the <strong>More Colors</strong> button in the color selectors.
* @name CKEDITOR.config.colorButton_enableMore
* @default <code>true</code>
* @type Boolean
* @example
* config.colorButton_enableMore = false;
*/
/**
* Defines the colors to be displayed in the color selectors. This is a string
* containing hexadecimal notation for HTML colors, without the "#" prefix.
* <br /><br />
* Since 3.3: A color name may optionally be defined by prefixing the entries with
* a name and the slash character. For example, "FontColor1/FF9900" will be
* displayed as the color #FF9900 in the selector, but will be output as "FontColor1".
* @name CKEDITOR.config.colorButton_colors
* @type String
* @default <code>'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'</code>
* @example
* // Brazil colors only.
* config.colorButton_colors = '00923E,F8C100,28166F';
* @example
* config.colorButton_colors = 'FontColor1/FF9900,FontColor2/0066CC,FontColor3/F00'
*/
CKEDITOR.config.colorButton_colors =
'000,800000,8B4513,2F4F4F,008080,000080,4B0082,696969,' +
'B22222,A52A2A,DAA520,006400,40E0D0,0000CD,800080,808080,' +
'F00,FF8C00,FFD700,008000,0FF,00F,EE82EE,A9A9A9,' +
'FFA07A,FFA500,FFFF00,00FF00,AFEEEE,ADD8E6,DDA0DD,D3D3D3,' +
'FFF0F5,FAEBD7,FFFFE0,F0FFF0,F0FFFF,F0F8FF,E6E6FA,FFF';
/**
* Stores the style definition that applies the text foreground color.
* @name CKEDITOR.config.colorButton_foreStyle
* @type Object
* @default (see example)
* @example
* // This is actually the default value.
* config.colorButton_foreStyle =
* {
* element : 'span',
* styles : { 'color' : '#(color)' }
* };
*/
CKEDITOR.config.colorButton_foreStyle =
{
element : 'span',
styles : { 'color' : '#(color)' },
overrides : [ { element : 'font', attributes : { 'color' : null } } ]
};
/**
* Stores the style definition that applies the text background color.
* @name CKEDITOR.config.colorButton_backStyle
* @type Object
* @default (see example)
* @example
* // This is actually the default value.
* config.colorButton_backStyle =
* {
* element : 'span',
* styles : { 'background-color' : '#(color)' }
* };
*/
CKEDITOR.config.colorButton_backStyle =
{
element : 'span',
styles : { 'background-color' : '#(color)' }
};

View File

@@ -0,0 +1,340 @@
/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.dialog.add( 'colordialog', function( editor )
{
// Define some shorthands.
var $el = CKEDITOR.dom.element,
$doc = CKEDITOR.document,
$tools = CKEDITOR.tools,
lang = editor.lang.colordialog;
// Reference the dialog.
var dialog;
var spacer =
{
type : 'html',
html : '&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,15 @@
/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.colordialog =
{
init : function( editor )
{
editor.addCommand( 'colordialog', new CKEDITOR.dialogCommand( 'colordialog' ) );
CKEDITOR.dialog.add( 'colordialog', this.path + 'dialogs/colordialog.js' );
}
};
CKEDITOR.plugins.add( 'colordialog', CKEDITOR.plugins.colordialog );

View File

@@ -0,0 +1,179 @@
/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.add( 'contextmenu',
{
requires : [ 'menu' ],
// Make sure the base class (CKEDITOR.menu) is loaded before it (#3318).
onLoad : function()
{
CKEDITOR.plugins.contextMenu = CKEDITOR.tools.createClass(
{
base : CKEDITOR.menu,
$ : function( editor )
{
this.base.call( this, editor,
{
panel:
{
className : editor.skinClass + ' cke_contextmenu',
attributes :
{
'aria-label' : editor.lang.contextmenu.options
}
}
});
},
proto :
{
addTarget : function( element, nativeContextMenuOnCtrl )
{
// Opera doesn't support 'contextmenu' event, we have duo approaches employed here:
// 1. Inherit the 'button override' hack we introduced in v2 (#4530), while this require the Opera browser
// option 'Allow script to detect context menu/right click events' to be always turned on.
// 2. Considering the fact that ctrl/meta key is not been occupied
// for multiple range selecting (like Gecko), we use this key
// combination as a fallback for triggering context-menu. (#4530)
if ( CKEDITOR.env.opera && !( 'oncontextmenu' in document.body ))
{
var contextMenuOverrideButton;
element.on( 'mousedown', function( evt )
{
evt = evt.data;
if ( evt.$.button != 2 )
{
if ( evt.getKeystroke() == CKEDITOR.CTRL + 1 )
element.fire( 'contextmenu', evt );
return;
}
if ( nativeContextMenuOnCtrl
&& ( CKEDITOR.env.mac ? evt.$.metaKey : evt.$.ctrlKey ) )
return;
var target = evt.getTarget();
if ( !contextMenuOverrideButton )
{
var ownerDoc = target.getDocument();
contextMenuOverrideButton = ownerDoc.createElement( 'input' ) ;
contextMenuOverrideButton.$.type = 'button' ;
ownerDoc.getBody().append( contextMenuOverrideButton ) ;
}
contextMenuOverrideButton.setAttribute( 'style', 'position:absolute;top:' + ( evt.$.clientY - 2 ) +
'px;left:' + ( evt.$.clientX - 2 ) +
'px;width:5px;height:5px;opacity:0.01' );
} );
element.on( 'mouseup', function ( evt )
{
if ( contextMenuOverrideButton )
{
contextMenuOverrideButton.remove();
contextMenuOverrideButton = undefined;
// Simulate 'contextmenu' event.
element.fire( 'contextmenu', evt.data );
}
} );
}
element.on( 'contextmenu', function( event )
{
var domEvent = event.data;
if ( nativeContextMenuOnCtrl &&
// Safari on Windows always show 'ctrlKey' as true in 'contextmenu' event,
// which make this property unreliable. (#4826)
( CKEDITOR.env.webkit ? holdCtrlKey : ( CKEDITOR.env.mac ? domEvent.$.metaKey : domEvent.$.ctrlKey ) ) )
return;
// Cancel the browser context menu.
domEvent.preventDefault();
var offsetParent = domEvent.getTarget().getDocument().getDocumentElement(),
offsetX = domEvent.$.clientX,
offsetY = domEvent.$.clientY;
CKEDITOR.tools.setTimeout( function()
{
this.open( offsetParent, null, offsetX, offsetY );
// IE needs a short while to allow selection change before opening menu. (#7908)
}, CKEDITOR.env.ie? 200 : 0, this );
},
this );
if ( CKEDITOR.env.opera )
{
// 'contextmenu' event triggered by Windows menu key is unpreventable,
// cancel the key event itself. (#6534)
element.on( 'keypress' , function ( evt )
{
var domEvent = evt.data;
if ( domEvent.$.keyCode === 0 )
domEvent.preventDefault();
});
}
if ( CKEDITOR.env.webkit )
{
var holdCtrlKey,
onKeyDown = function( event )
{
holdCtrlKey = CKEDITOR.env.mac ? event.data.$.metaKey : event.data.$.ctrlKey ;
},
resetOnKeyUp = function()
{
holdCtrlKey = 0;
};
element.on( 'keydown', onKeyDown );
element.on( 'keyup', resetOnKeyUp );
element.on( 'contextmenu', resetOnKeyUp );
}
},
open : function( offsetParent, corner, offsetX, offsetY )
{
this.editor.focus();
offsetParent = offsetParent || CKEDITOR.document.getDocumentElement();
this.show( offsetParent, corner, offsetX, offsetY );
}
}
});
},
beforeInit : function( editor )
{
editor.contextMenu = new CKEDITOR.plugins.contextMenu( editor );
editor.addCommand( 'contextMenu',
{
exec : function()
{
editor.contextMenu.open( editor.document.getBody() );
}
});
}
});
/**
* Whether to show the browser native context menu when the <em>Ctrl</em> or
* <em>Meta</em> (Mac) key is pressed on opening the context menu with the
* right mouse button click or the <em>Menu</em> key.
* @name CKEDITOR.config.browserContextMenuOnCtrl
* @since 3.0.2
* @type Boolean
* @default <code>true</code>
* @example
* config.browserContextMenuOnCtrl = false;
*/

View File

@@ -0,0 +1,16 @@
/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.setLang( 'devtools', 'en',
{
devTools :
{
title : 'Element Information',
dialogName : 'Dialog window name',
tabName : 'Tab name',
elementId : 'Element ID',
elementType : 'Element type'
}
});

View File

@@ -0,0 +1,173 @@
/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.add( 'devtools',
{
lang : [ 'en' ],
init : function( editor )
{
editor._.showDialogDefinitionTooltips = 1;
},
onLoad : function()
{
CKEDITOR.document.appendStyleText( CKEDITOR.config.devtools_styles ||
'#cke_tooltip { padding: 5px; border: 2px solid #333; background: #ffffff }' +
'#cke_tooltip h2 { font-size: 1.1em; border-bottom: 1px solid; margin: 0; padding: 1px; }' +
'#cke_tooltip ul { padding: 0pt; list-style-type: none; }' );
}
});
(function()
{
function defaultCallback( editor, dialog, element, tabName )
{
var lang = editor.lang.devTools,
link = '<a href="http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.dialog.definition.' +
( element ? ( element.type == 'text' ? 'textInput' : element.type ) : 'content' ) +
'.html" target="_blank">' + ( element ? element.type : 'content' ) + '</a>',
str =
'<h2>' + lang.title + '</h2>' +
'<ul>' +
'<li><strong>' + lang.dialogName + '</strong> : ' + dialog.getName() + '</li>' +
'<li><strong>' + lang.tabName + '</strong> : ' + tabName + '</li>';
if ( element )
str += '<li><strong>' + lang.elementId + '</strong> : ' + element.id + '</li>';
str += '<li><strong>' + lang.elementType + '</strong> : ' + link + '</li>';
return str + '</ul>';
}
function showTooltip( callback, el, editor, dialog, obj, tabName )
{
var pos = el.getDocumentPosition(),
styles = { 'z-index' : CKEDITOR.dialog._.currentZIndex + 10, top : ( pos.y + el.getSize( 'height' ) ) + 'px' };
tooltip.setHtml( callback( editor, dialog, obj, tabName ) );
tooltip.show();
// Translate coordinate for RTL.
if ( editor.lang.dir == 'rtl' )
{
var viewPaneSize = CKEDITOR.document.getWindow().getViewPaneSize();
styles.right = ( viewPaneSize.width - pos.x - el.getSize( 'width' ) ) + 'px';
}
else
styles.left = pos.x + 'px';
tooltip.setStyles( styles );
}
var tooltip;
CKEDITOR.on( 'reset', function()
{
tooltip && tooltip.remove();
tooltip = null;
});
CKEDITOR.on( 'dialogDefinition', function( evt )
{
var editor = evt.editor;
if ( editor._.showDialogDefinitionTooltips )
{
if ( !tooltip )
{
tooltip = CKEDITOR.dom.element.createFromHtml( '<div id="cke_tooltip" tabindex="-1" style="position: absolute"></div>', CKEDITOR.document );
tooltip.hide();
tooltip.on( 'mouseover', function(){ this.show(); } );
tooltip.on( 'mouseout', function(){ this.hide(); } );
tooltip.appendTo( CKEDITOR.document.getBody() );
}
var dialog = evt.data.definition.dialog,
callback = editor.config.devtools_textCallback || defaultCallback;
dialog.on( 'load', function()
{
var tabs = dialog.parts.tabs.getChildren(), tab;
for ( var i = 0, len = tabs.count(); i < len; i++ )
{
tab = tabs.getItem( i );
tab.on( 'mouseover', function()
{
var id = this.$.id;
showTooltip( callback, this, editor, dialog, null, id.substring( 4, id.lastIndexOf( '_' ) ) );
});
tab.on( 'mouseout', function()
{
tooltip.hide();
});
}
dialog.foreach( function( obj )
{
if ( obj.type in { hbox : 1, vbox : 1 } )
return;
var el = obj.getElement();
if ( el )
{
el.on( 'mouseover', function()
{
showTooltip( callback, this, editor, dialog, obj, dialog._.currentTabId );
});
el.on( 'mouseout', function()
{
tooltip.hide();
});
}
});
});
}
});
})();
/**
* A function that returns the text to be displayed inside the Developer Tools tooltip when hovering over a dialog UI element.
* There are 4 parameters that are being passed into the function: editor, dialog window, element, tab name.
* @name editor.config.devtools_textCallback
* @since 3.6
* @type Function
* @default (see example)
* @example
* // This is actually the default value.
* // Show dialog window name, tab ID, and element ID.
* config.devtools_textCallback = function( editor, dialog, element, tabName )
* {
* var lang = editor.lang.devTools,
* link = '<a href="http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.dialog.definition.' +
* ( element ? ( element.type == 'text' ? 'textInput' : element.type ) : 'content' ) +
* '.html" target="_blank">' + ( element ? element.type : 'content' ) + '</a>',
* str =
* '<h2>' + lang.title + '</h2>' +
* '<ul>' +
* '<li><strong>' + lang.dialogName + '</strong> : ' + dialog.getName() + '</li>' +
* '<li><strong>' + lang.tabName + '</strong> : ' + tabName + '</li>';
*
* if ( element )
* str += '<li><strong>' + lang.elementId + '</strong> : ' + element.id + '</li>';
*
* str += '<li><strong>' + lang.elementType + '</strong> : ' + link + '</li>';
*
* return str + '</ul>';
* }
*/
/**
* A setting that stores CSS rules to be injected into the page with styles to be applied to the tooltip element.
* @name CKEDITOR.config.devtools_styles
* @since 3.6
* @type String
* @default (see example)
* @example
* // This is actually the default value.
* CKEDITOR.config.devtools_styles = &quot;
* #cke_tooltip { padding: 5px; border: 2px solid #333; background: #ffffff }
* #cke_tooltip h2 { font-size: 1.1em; border-bottom: 1px solid; margin: 0; padding: 1px; }
* #cke_tooltip ul { padding: 0pt; list-style-type: none; }
* &quot;;
*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,208 @@
/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
(function()
{
function setupAdvParams( element )
{
var attrName = this.att;
var value = element && element.hasAttribute( attrName ) && element.getAttribute( attrName ) || '';
if ( value !== undefined )
this.setValue( value );
}
function commitAdvParams()
{
// Dialogs may use different parameters in the commit list, so, by
// definition, we take the first CKEDITOR.dom.element available.
var element;
for ( var i = 0 ; i < arguments.length ; i++ )
{
if ( arguments[ i ] instanceof CKEDITOR.dom.element )
{
element = arguments[ i ];
break;
}
}
if ( element )
{
var attrName = this.att,
value = this.getValue();
if ( value )
element.setAttribute( attrName, value );
else
element.removeAttribute( attrName, value );
}
}
CKEDITOR.plugins.add( 'dialogadvtab',
{
/**
*
* @param tabConfig
* id, dir, classes, styles
*/
createAdvancedTab : function( editor, tabConfig )
{
if ( !tabConfig )
tabConfig = { id:1, dir:1, classes:1, styles:1 };
var lang = editor.lang.common;
var result =
{
id : 'advanced',
label : lang.advancedTab,
title : lang.advancedTab,
elements :
[
{
type : 'vbox',
padding : 1,
children : []
}
]
};
var contents = [];
if ( tabConfig.id || tabConfig.dir )
{
if ( tabConfig.id )
{
contents.push(
{
id : 'advId',
att : 'id',
type : 'text',
label : lang.id,
setup : setupAdvParams,
commit : commitAdvParams
});
}
if ( tabConfig.dir )
{
contents.push(
{
id : 'advLangDir',
att : 'dir',
type : 'select',
label : lang.langDir,
'default' : '',
style : 'width:100%',
items :
[
[ lang.notSet, '' ],
[ lang.langDirLTR, 'ltr' ],
[ lang.langDirRTL, 'rtl' ]
],
setup : setupAdvParams,
commit : commitAdvParams
});
}
result.elements[ 0 ].children.push(
{
type : 'hbox',
widths : [ '50%', '50%' ],
children : [].concat( contents )
});
}
if ( tabConfig.styles || tabConfig.classes )
{
contents = [];
if ( tabConfig.styles )
{
contents.push(
{
id : 'advStyles',
att : 'style',
type : 'text',
label : lang.styles,
'default' : '',
validate : CKEDITOR.dialog.validate.inlineStyle( lang.invalidInlineStyle ),
onChange : function(){},
getStyle : function( name, defaultValue )
{
var match = this.getValue().match( new RegExp( name + '\\s*:\\s*([^;]*)', 'i') );
return match ? match[ 1 ] : defaultValue;
},
updateStyle : function( name, value )
{
var styles = this.getValue();
// Remove the current value.
if ( styles )
{
styles = styles
.replace( new RegExp( '\\s*' + name + '\s*:[^;]*(?:$|;\s*)', 'i' ), '' )
.replace( /^[;\s]+/, '' )
.replace( /\s+$/, '' );
}
if ( value )
{
styles && !(/;\s*$/).test( styles ) && ( styles += '; ' );
styles += name + ': ' + value;
}
this.setValue( styles, 1 );
},
setup : setupAdvParams,
commit : commitAdvParams
});
}
if ( tabConfig.classes )
{
contents.push(
{
type : 'hbox',
widths : [ '45%', '55%' ],
children :
[
{
id : 'advCSSClasses',
att : 'class',
type : 'text',
label : lang.cssClasses,
'default' : '',
setup : setupAdvParams,
commit : commitAdvParams
}
]
});
}
result.elements[ 0 ].children.push(
{
type : 'hbox',
widths : [ '50%', '50%' ],
children : [].concat( contents )
});
}
return result;
}
});
})();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,535 @@
/*
* Copyright (c) 2003-2011, 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-2011, 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,674 @@
/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.dialog.add( 'docProps', function( editor )
{
var lang = editor.lang.docprops,
langCommon = editor.lang.common,
metaHash = {};
function getDialogValue( dialogName, callback )
{
var onOk = function()
{
releaseHandlers( this );
callback( this, this._.parentDialog );
};
var releaseHandlers = function( dialog )
{
dialog.removeListener( 'ok', onOk );
dialog.removeListener( 'cancel', releaseHandlers );
};
var bindToDialog = function( dialog )
{
dialog.on( 'ok', onOk );
dialog.on( 'cancel', releaseHandlers );
};
editor.execCommand( dialogName );
if ( editor._.storedDialogs.colordialog )
bindToDialog( editor._.storedDialogs.colordialog );
else
{
CKEDITOR.on( 'dialogDefinition', function( e )
{
if ( e.data.name != dialogName )
return;
var definition = e.data.definition;
e.removeListener();
definition.onLoad = CKEDITOR.tools.override( definition.onLoad, function( orginal )
{
return function()
{
bindToDialog( this );
definition.onLoad = orginal;
if ( typeof orginal == 'function' )
orginal.call( this );
};
});
});
}
}
function handleOther()
{
var dialog = this.getDialog(),
other = dialog.getContentElement( 'general', this.id + 'Other' );
if ( !other )
return;
if ( this.getValue() == 'other' )
{
other.getInputElement().removeAttribute( 'readOnly' );
other.focus();
other.getElement().removeClass( 'cke_disabled' );
}
else
{
other.getInputElement().setAttribute( 'readOnly', true );
other.getElement().addClass( 'cke_disabled' );
}
}
function commitMeta( name, isHttp, value )
{
return function( doc, html, head )
{
var hash = metaHash,
val = typeof value != 'undefined' ? value : this.getValue();
if ( !val && ( name in hash ) )
hash[ name ].remove();
else if ( val && ( name in hash ) )
hash[ name ].setAttribute( 'content', val );
else if ( val )
{
var meta = new CKEDITOR.dom.element( 'meta', editor.document );
meta.setAttribute( isHttp ? 'http-equiv' : 'name', name );
meta.setAttribute( 'content', val );
head.append( meta );
}
};
}
function setupMeta( name, ret )
{
return function()
{
var hash = metaHash,
result = ( name in hash ) ? hash[ name ].getAttribute( 'content' ) || '' : '';
if ( ret )
return result;
this.setValue( result );
return null;
};
}
function commitMargin( name )
{
return function( doc, html, head, body )
{
body.removeAttribute( 'margin' + name );
var val = this.getValue();
if ( val !== '' )
body.setStyle( 'margin-' + name, CKEDITOR.tools.cssLength( val ) );
else
body.removeStyle( 'margin-' + name );
};
}
function createMetaHash( doc )
{
var hash = {},
metas = doc.getElementsByTag( 'meta' ),
count = metas.count();
for ( var i = 0; i < count; i++ )
{
var meta = metas.getItem( i );
hash[ meta.getAttribute( meta.hasAttribute( 'http-equiv' ) ? 'http-equiv' : 'name' ).toLowerCase() ] = meta;
}
return hash;
}
// We cannot just remove the style from the element, as it might be affected from non-inline stylesheets.
// To get the proper result, we should manually set the inline style to its default value.
function resetStyle( element, prop, resetVal )
{
element.removeStyle( prop );
if ( element.getComputedStyle( prop ) != resetVal )
element.setStyle( prop, resetVal );
}
// Utilty to shorten the creation of color fields in the dialog.
var colorField = function( id, label, fieldProps )
{
return {
type : 'hbox',
padding : 0,
widths : [ '60%', '40%' ],
children : [
CKEDITOR.tools.extend( {
type : 'text',
id : id,
label : lang[ label ]
}, fieldProps || {}, 1 ),
{
type : 'button',
id : id + 'Choose',
label : lang.chooseColor,
className : 'colorChooser',
onClick : function()
{
var self = this;
getDialogValue( 'colordialog', function( colorDialog )
{
var dialog = self.getDialog();
dialog.getContentElement( dialog._.currentTabId, id ).setValue( colorDialog.getContentElement( 'picker', 'selectedColor' ).getValue() );
});
}
}
]
};
};
var previewSrc = 'javascript:' +
'void((function(){' +
encodeURIComponent(
'document.open();' +
( CKEDITOR.env.isCustomDomain() ? 'document.domain=\'' + document.domain + '\';' : '' ) +
'document.write( \'<html style="background-color: #ffffff; height: 100%"><head></head><body style="width: 100%; height: 100%; margin: 0px">' + lang.previewHtml + '</body></html>\' );' +
'document.close();'
) +
'})())';
return {
title : lang.title,
minHeight: 330,
minWidth: 500,
onShow : function()
{
var doc = editor.document,
html = doc.getElementsByTag( 'html' ).getItem( 0 ),
head = doc.getHead(),
body = doc.getBody();
metaHash = createMetaHash( doc );
this.setupContent( doc, html, head, body );
},
onHide : function()
{
metaHash = {};
},
onOk : function()
{
var doc = editor.document,
html = doc.getElementsByTag( 'html' ).getItem( 0 ),
head = doc.getHead(),
body = doc.getBody();
this.commitContent( doc, html, head, body );
},
contents : [
{
id : 'general',
label : langCommon.generalTab,
elements : [
{
type : 'text',
id : 'title',
label : lang.docTitle,
setup : function( doc )
{
this.setValue( doc.getElementsByTag( 'title' ).getItem( 0 ).data( 'cke-title' ) );
},
commit : function( doc, html, head, body, isPreview )
{
if ( isPreview )
return;
doc.getElementsByTag( 'title' ).getItem( 0 ).data( 'cke-title', this.getValue() );
}
},
{
type : 'hbox',
children : [
{
type : 'select',
id : 'dir',
label : langCommon.langDir,
style : 'width: 100%',
items : [
[ langCommon.notSet , '' ],
[ langCommon.langDirLtr, 'ltr' ],
[ langCommon.langDirRtl, 'rtl' ]
],
setup : function( doc, html, head, body )
{
this.setValue( body.getDirection() || '' );
},
commit : function( doc, html, head, body )
{
var val = this.getValue();
if ( val )
body.setAttribute( 'dir', val );
else
body.removeAttribute( 'dir' );
body.removeStyle( 'direction' );
}
},
{
type : 'text',
id : 'langCode',
label : langCommon.langCode,
setup : function( doc, html )
{
this.setValue( html.getAttribute( 'xml:lang' ) || html.getAttribute( 'lang' ) || '' );
},
commit : function( doc, html, head, body, isPreview )
{
if ( isPreview )
return;
var val = this.getValue();
if ( val )
html.setAttributes( { 'xml:lang' : val, lang : val } );
else
html.removeAttributes( { 'xml:lang' : 1, lang : 1 } );
}
}
]
},
{
type : 'hbox',
children : [
{
type : 'select',
id : 'charset',
label : lang.charset,
style : 'width: 100%',
items : [
[ langCommon.notSet, '' ],
[ lang.charsetASCII, 'us-ascii' ],
[ lang.charsetCE, 'iso-8859-2' ],
[ lang.charsetCT, 'big5' ],
[ lang.charsetCR, 'iso-8859-5' ],
[ lang.charsetGR, 'iso-8859-7' ],
[ lang.charsetJP, 'iso-2022-jp' ],
[ lang.charsetKR, 'iso-2022-kr' ],
[ lang.charsetTR, 'iso-8859-9' ],
[ lang.charsetUN, 'utf-8' ],
[ lang.charsetWE, 'iso-8859-1' ],
[ lang.other, 'other' ]
],
'default' : '',
onChange : function()
{
this.getDialog().selectedCharset = this.getValue() != 'other' ? this.getValue() : '';
handleOther.call( this );
},
setup : function()
{
this.metaCharset = ( 'charset' in metaHash );
var func = setupMeta( this.metaCharset ? 'charset' : 'content-type', 1, 1 ),
val = func.call( this );
!this.metaCharset && val.match( /charset=[^=]+$/ ) && ( val = val.substring( val.indexOf( '=' ) + 1 ) );
if ( val )
{
this.setValue( val.toLowerCase() );
if ( !this.getValue() )
{
this.setValue( 'other' );
var other = this.getDialog().getContentElement( 'general', 'charsetOther' );
other && other.setValue( val );
}
this.getDialog().selectedCharset = val;
}
handleOther.call( this );
},
commit : function( doc, html, head, body, isPreview )
{
if ( isPreview )
return;
var value = this.getValue(),
other = this.getDialog().getContentElement( 'general', 'charsetOther' );
value == 'other' && ( value = other ? other.getValue() : '' );
value && !this.metaCharset && ( value = ( metaHash[ 'content-type' ] ? metaHash[ 'content-type' ].getAttribute( 'content' ).split( ';' )[0] : 'text/html' ) + '; charset=' + value );
var func = commitMeta( this.metaCharset ? 'charset' : 'content-type', 1, value );
func.call( this, doc, html, head );
}
},
{
type : 'text',
id : 'charsetOther',
label : lang.charsetOther,
onChange : function(){ this.getDialog().selectedCharset = this.getValue(); }
}
]
},
{
type : 'hbox',
children : [
{
type : 'select',
id : 'docType',
label : lang.docType,
style : 'width: 100%',
items : [
[ langCommon.notSet , '' ],
[ 'XHTML 1.1', '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">' ],
[ 'XHTML 1.0 Transitional', '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' ],
[ 'XHTML 1.0 Strict', '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' ],
[ 'XHTML 1.0 Frameset', '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">' ],
[ 'HTML 5', '<!DOCTYPE html>' ],
[ 'HTML 4.01 Transitional', '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">' ],
[ 'HTML 4.01 Strict', '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">' ],
[ 'HTML 4.01 Frameset', '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">' ],
[ 'HTML 3.2', '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">' ],
[ 'HTML 2.0', '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">' ],
[ lang.other, 'other' ]
],
onChange : handleOther,
setup : function()
{
if ( editor.docType )
{
this.setValue( editor.docType );
if ( !this.getValue() )
{
this.setValue( 'other' );
var other = this.getDialog().getContentElement( 'general', 'docTypeOther' );
other && other.setValue( editor.docType );
}
}
handleOther.call( this );
},
commit : function( doc, html, head, body, isPreview )
{
if ( isPreview )
return;
var value = this.getValue(),
other = this.getDialog().getContentElement( 'general', 'docTypeOther' );
editor.docType = value == 'other' ? ( other ? other.getValue() : '' ) : value;
}
},
{
type : 'text',
id : 'docTypeOther',
label : lang.docTypeOther
}
]
},
{
type : 'checkbox',
id : 'xhtmlDec',
label : lang.xhtmlDec,
setup : function()
{
this.setValue( !!editor.xmlDeclaration );
},
commit : function( doc, html, head, body, isPreview )
{
if ( isPreview )
return;
if ( this.getValue() )
{
editor.xmlDeclaration = '<?xml version="1.0" encoding="' + ( this.getDialog().selectedCharset || 'utf-8' )+ '"?>' ;
html.setAttribute( 'xmlns', 'http://www.w3.org/1999/xhtml' );
}
else
{
editor.xmlDeclaration = '';
html.removeAttribute( 'xmlns' );
}
}
}
]
},
{
id : 'design',
label : lang.design,
elements : [
{
type : 'hbox',
widths : [ '60%', '40%' ],
children : [
{
type : 'vbox',
children : [
colorField( 'txtColor', 'txtColor',
{
setup : function( doc, html, head, body )
{
this.setValue( body.getComputedStyle( 'color' ) );
},
commit : function( doc, html, head, body, isPreview )
{
if ( this.isChanged() || isPreview )
{
body.removeAttribute( 'text' );
var val = this.getValue();
if ( val )
body.setStyle( 'color', val );
else
body.removeStyle( 'color' );
}
}
}),
colorField( 'bgColor', 'bgColor', {
setup : function( doc, html, head, body )
{
var val = body.getComputedStyle( 'background-color' ) || '';
this.setValue( val == 'transparent' ? '' : val );
},
commit : function( doc, html, head, body, isPreview )
{
if ( this.isChanged() || isPreview )
{
body.removeAttribute( 'bgcolor' );
var val = this.getValue();
if ( val )
body.setStyle( 'background-color', val );
else
resetStyle( body, 'background-color', 'transparent' );
}
}
}),
{
type : 'hbox',
widths : [ '60%', '40%' ],
padding : 1,
children : [
{
type : 'text',
id : 'bgImage',
label : lang.bgImage,
setup : function( doc, html, head, body )
{
var val = body.getComputedStyle( 'background-image' ) || '';
if ( val == 'none' )
val = '';
else
{
val = val.replace( /url\(\s*(["']?)\s*([^\)]*)\s*\1\s*\)/i, function( match, quote, url )
{
return url;
});
}
this.setValue( val );
},
commit : function( doc, html, head, body )
{
body.removeAttribute( 'background' );
var val = this.getValue();
if ( val )
body.setStyle( 'background-image', 'url(' + val + ')' );
else
resetStyle( body, 'background-image', 'none' );
}
},
{
type : 'button',
id : 'bgImageChoose',
label : langCommon.browseServer,
style : 'display:inline-block;margin-top:10px;',
hidden : true,
filebrowser : 'design:bgImage'
}
]
},
{
type : 'checkbox',
id : 'bgFixed',
label : lang.bgFixed,
setup : function( doc, html, head, body )
{
this.setValue( body.getComputedStyle( 'background-attachment' ) == 'fixed' );
},
commit : function( doc, html, head, body )
{
if ( this.getValue() )
body.setStyle( 'background-attachment', 'fixed' );
else
resetStyle( body, 'background-attachment', 'scroll' );
}
}
]
},
{
type : 'vbox',
children : [
{
type : 'html',
id : 'marginTitle',
html : '<div style="text-align: center; margin: 0px auto; font-weight: bold">' + lang.margin + '</div>'
},
{
type : 'text',
id : 'marginTop',
label : lang.marginTop,
style : 'width: 80px; text-align: center',
align : 'center',
inputStyle : 'text-align: center',
setup : function( doc, html, head, body )
{
this.setValue( body.getStyle( 'margin-top' ) || body.getAttribute( 'margintop' ) || '' );
},
commit : commitMargin( 'top' )
},
{
type : 'hbox',
children : [
{
type : 'text',
id : 'marginLeft',
label : lang.marginLeft,
style : 'width: 80px; text-align: center',
align : 'center',
inputStyle : 'text-align: center',
setup : function( doc, html, head, body )
{
this.setValue( body.getStyle( 'margin-left' ) || body.getAttribute( 'marginleft' ) || '' );
},
commit : commitMargin( 'left' )
},
{
type : 'text',
id : 'marginRight',
label : lang.marginRight,
style : 'width: 80px; text-align: center',
align : 'center',
inputStyle : 'text-align: center',
setup : function( doc, html, head, body )
{
this.setValue( body.getStyle( 'margin-right' ) || body.getAttribute( 'marginright' ) || '' );
},
commit : commitMargin( 'right' )
}
]
},
{
type : 'text',
id : 'marginBottom',
label : lang.marginBottom,
style : 'width: 80px; text-align: center',
align : 'center',
inputStyle : 'text-align: center',
setup : function( doc, html, head, body )
{
this.setValue( body.getStyle( 'margin-bottom' ) || body.getAttribute( 'marginbottom' ) || '' );
},
commit : commitMargin( 'bottom' )
}
]
}
]
}
]
},
{
id : 'meta',
label : lang.meta,
elements : [
{
type : 'textarea',
id : 'metaKeywords',
label : lang.metaKeywords,
setup : setupMeta( 'keywords' ),
commit : commitMeta( 'keywords' )
},
{
type : 'textarea',
id : 'metaDescription',
label : lang.metaDescription,
setup : setupMeta( 'description' ),
commit : commitMeta( 'description' )
},
{
type : 'text',
id : 'metaAuthor',
label : lang.metaAuthor,
setup : setupMeta( 'author' ),
commit : commitMeta( 'author' )
},
{
type : 'text',
id : 'metaCopyright',
label : lang.metaCopyright,
setup : setupMeta( 'copyright' ),
commit : commitMeta( 'copyright' )
}
]
},
{
id : 'preview',
label : langCommon.preview,
elements : [
{
type : 'html',
id : 'previewHtml',
html : '<iframe src="' + previewSrc + '" style="width: 100%; height: 310px" hidefocus="true" frameborder="0" ' +
'id="cke_docProps_preview_iframe"></iframe>',
onLoad : function()
{
this.getDialog().on( 'selectPage', function( ev )
{
if ( ev.data.page == 'preview' )
{
var self = this;
setTimeout( function()
{
var doc = CKEDITOR.document.getById( 'cke_docProps_preview_iframe' ).getFrameDocument(),
html = doc.getElementsByTag( 'html' ).getItem( 0 ),
head = doc.getHead(),
body = doc.getBody();
self.commitContent( doc, html, head, body, 1 );
}, 50 );
}
});
CKEDITOR.document.getById( 'cke_docProps_preview_iframe' ).getAscendant( 'table' ).setStyle( 'height', '100%' );
}
}
]
}
]
};
});

View File

@@ -0,0 +1,22 @@
/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.add( 'docprops',
{
init : function( editor )
{
var cmd = new CKEDITOR.dialogCommand( 'docProps' );
// Only applicable on full page mode.
cmd.modes = { wysiwyg : editor.config.fullPage };
editor.addCommand( 'docProps', cmd );
CKEDITOR.dialog.add( 'docProps', this.path + 'dialogs/docprops.js' );
editor.ui.addButton( 'DocProps',
{
label : editor.lang.docprops.label,
command : 'docProps'
});
}
});

View File

@@ -0,0 +1,361 @@
/*
Copyright (c) 2003-2011, 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 ]+$/,
// Ignore bookmark nodes.(#3783)
bookmarkGuard = CKEDITOR.dom.walker.bookmark( false, true ),
whitespacesGuard = CKEDITOR.dom.walker.whitespaces( true ),
skipGuard = function( node ) { return bookmarkGuard( node ) && whitespacesGuard( node ); };
// Get a reference for the next element, bookmark nodes are skipped.
function getNextSourceNode( node, startFromSibling, lastNode )
{
var next = node.getNextSourceNode( startFromSibling, null, lastNode );
while ( !bookmarkGuard( next ) )
next = next.getNextSourceNode( startFromSibling, null, lastNode );
return next;
}
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( skipGuard ) && !isLast )
{
var parentNode = currentNode.getParent();
if ( parentNode.isBlockBoundary( this.forceBrBreak
&& !parentPre && { br : 1 } ) )
{
closeRange = 1;
includeNode = 0;
isLast = isLast || ( parentNode.equals( lastNode) );
// Make sure range includes bookmarks at the end of the block. (#7359)
range.setEndAt( parentNode, CKEDITOR.POSITION_BEFORE_END );
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 = getNextSourceNode ( currentNode, continueFromSibling, 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 : getNextSourceNode( range.getBoundaryNodes().endNode, 1, lastNode ) );
}
}
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 :
getNextSourceNode( block, 1, lastNode );
}
return block;
}
};
CKEDITOR.dom.range.prototype.createIterator = function()
{
return new iterator( this );
};
})();

View File

@@ -0,0 +1,278 @@
/*
Copyright (c) 2003-2011, 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()
{
// 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;
editor.getMode().loadData( editor.getData() );
isHandlingData = false;
}
if ( editor.mode )
setData();
else
{
editor.on( 'mode', function()
{
if ( editor.mode )
{
setData();
editor.removeListener( 'mode', arguments.callee );
}
});
}
}
});
editor.on( 'beforeGetData', function()
{
if ( !isHandlingData && editor.mode )
{
isHandlingData = true;
editor.setData( editor.getMode().getData(), null, 1 );
isHandlingData = false;
}
});
editor.on( 'getSnapshot', function( event )
{
if ( editor.mode )
event.data = editor.getMode().getSnapshotData();
});
editor.on( 'loadSnapshot', function( event )
{
if ( editor.mode )
editor.getMode().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 );
});
editor.on( 'destroy', function ()
{
// -> currentMode.unload( holderElement );
if ( this.mode )
this._.modes[ this.mode ].unload( this.getThemeSpace( 'contents' ) );
});
}
});
/**
* 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 )
{
this.fire( 'beforeSetMode', { newMode : mode } );
var data,
holderElement = this.getThemeSpace( 'contents' ),
isDirty = this.checkDirty();
// Unload the previous mode.
if ( this.mode )
{
if ( mode == this.mode )
return;
this._.previousMode = this.mode;
this.fire( 'beforeModeUnload' );
var currentMode = this.getMode();
data = currentMode.getData();
currentMode.unload( holderElement );
this.mode = '';
}
holderElement.setHtml( '' );
// Load required mode.
var modeEditor = this.getMode( 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 );
};
/**
* Gets the current or any of the objects that represent the editing
* area modes. The two most common editing modes are "wysiwyg" and "source".
* @param {String} [mode] The mode to be retrieved. If not specified, the
* current one is returned.
*/
CKEDITOR.editor.prototype.getMode = function( mode )
{
return this._.modes && this._.modes[ mode || this.mode ];
};
/**
* Moves the selection focus to the editing are space in the editor.
*/
CKEDITOR.editor.prototype.focus = function()
{
this.forceNextSelectionCheck();
var mode = this.getMode();
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.
* @name CKEDITOR.config.startupFocus
* @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.
*/
/**
* Fired when the CKEDITOR instance is created, fully initialized and ready for interaction.
* @name CKEDITOR.editor#instanceReady
* @event
*/
/**
* Fired before changing the editing mode. See also CKEDITOR.editor#beforeSetMode and CKEDITOR.editor#mode
* @name CKEDITOR.editor#beforeModeUnload
* @event
*/
/**
* Fired before the editor mode is set. See also CKEDITOR.editor#mode and CKEDITOR.editor#beforeModeUnload
* @name CKEDITOR.editor#beforeSetMode
* @event
* @since 3.5.3
* @param {String} newMode The name of the mode which is about to be set.
*/
/**
* Fired after setting the editing mode. See also CKEDITOR.editor#beforeSetMode and CKEDITOR.editor#beforeModeUnload
* @name CKEDITOR.editor#mode
* @event
* @param {String} previousMode The previous mode of the editor.
*/

View File

@@ -0,0 +1,218 @@
/*
Copyright (c) 2003-2011, 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 :
{
editorFocus : false,
readOnly : 1,
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,
name;
if ( element.data( 'cke-display-name' ) )
name = element.data( 'cke-display-name' );
else if ( element.data( 'cke-real-element-type' ) )
name = element.data( 'cke-real-element-type' );
else
name = element.getName();
for ( var i = 0; i < filters.length; i++ )
{
var ret = filters[ i ]( element, name );
if ( ret === false )
{
ignore = 1;
break;
}
name = ret || name;
}
if ( !ignore )
{
var index = elementsList.push( element ) - 1;
// 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 } );
});
function empty()
{
spaceElement && spaceElement.setHtml( emptyHtml );
delete editor._.elementsPath.list;
}
editor.on( 'readOnly', empty );
editor.on( 'contentDomUnload', empty );
editor.addCommand( 'elementsPathFocus', commands.toolbarFocus );
}
});
})();
/**
* Fired when the contents of the elementsPath are changed
* @name CKEDITOR.editor#elementsPathUpdate
* @event
* @param {Object} eventData.space The elementsPath container
*/

View File

@@ -0,0 +1,433 @@
/*
Copyright (c) 2003-2011, 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 )
{
editor.addCommand( 'enter', {
modes : { wysiwyg:1 },
editorFocus : false,
exec : function( editor ){ enter( editor ); }
});
editor.addCommand( 'shiftEnter', {
modes : { wysiwyg:1 },
editorFocus : false,
exec : function( editor ){ shiftEnter( editor ); }
});
var keystrokes = editor.keystrokeHandler.keystrokes;
keystrokes[ 13 ] = 'enter';
keystrokes[ 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;
var atBlockStart = range.checkStartOfBlock(),
atBlockEnd = range.checkEndOfBlock(),
path = new CKEDITOR.dom.elementPath( range.startContainer ),
block = path.block;
if ( atBlockStart && atBlockEnd )
{
// Exit the list when we're inside an empty list item block. (#5376)
if ( block && ( block.is( 'li' ) || block.getParent().is( 'li' ) ) )
{
editor.execCommand( 'outdent' );
return;
}
if ( block && block.getParent().is( 'blockquote' ) )
{
block.breakParent( block.getParent() );
// If we were at the start of <blockquote>, there will be an empty element before it now.
if ( !block.getPrevious().getFirst( CKEDITOR.dom.walker.invisible(1) ) )
block.getPrevious().remove();
// If we were at the end of <blockquote>, there will be an empty element after it now.
if ( !block.getNext().getFirst( CKEDITOR.dom.walker.invisible(1) ) )
block.getNext().remove();
range.moveToElementEditStart( block );
range.select();
return;
}
}
// Don't split <pre> if we're in the middle of it, act as shift enter key.
else if ( block && block.is( 'pre' ) )
{
if ( !atBlockEnd )
{
enterBr( editor, mode, range, forceMode );
return;
}
}
// Don't split caption blocks. (#7944)
else if ( block && CKEDITOR.dtd.$captionBlock[ block.getName() ] )
{
enterBr( editor, mode, range, forceMode );
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 );
node = previousBlock.getNext();
range.moveToElementEditStart( node );
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() ) || previousBlock.is( 'pre' ) ) )
{
// Otherwise, duplicate the previous block.
newBlock = previousBlock.clone();
}
}
else if ( nextBlock )
newBlock = nextBlock.clone();
if ( !newBlock )
{
// We have already created a new list item. (#6849)
if ( node && node.is( 'li' ) )
newBlock = node;
else
{
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();
if ( !newBlock.getParent() )
range.insertNode( newBlock );
// list item start number should not be duplicated (#7330), but we need
// to remove the attribute after it's onto the DOM tree because of old IEs (#7581).
newBlock.is( 'li' ) && newBlock.removeAttribute( 'value' );
// 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 );
// IE has different behavior regarding position.
if ( CKEDITOR.env.ie )
range.setStartAt( lineBreak, CKEDITOR.POSITION_AFTER_END );
else
{
// 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.
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 )
lineBreak.getParent().appendBogus();
// Now we can remove the text node contents, so the caret doesn't
// stop on it.
lineBreak.getNext().$.nodeValue = '';
range.setStartAt( lineBreak.getNext(), CKEDITOR.POSITION_AFTER_START );
// Scroll into view, for non 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)
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 )
enterBr( editor, mode, null, forceMode );
else
enterBlock( editor, mode, null, forceMode );
editor.fire( 'saveSnapshot' );
}, 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,250 @@
/*
Copyright (c) 2003-2011, 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,amp';
var entities =
// Latin-1 Entities
'quot,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 its entity form from a list of entity names.
* @param reverse {Boolean} Whether to create a reverse map from the entity string form to an 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
amp : '\u0026' // ALL
};
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 && entities )
{
// 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 = '';
if ( config.basicEntities !== false )
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, leave it matches nothing if entities are empty.
var entitiesRegex = entitiesTable.regex ? '[' + entitiesTable.regex + ']' : 'a^';
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 escape basic HTML entities in the document, including:
* <ul>
* <li><code>nbsp</code></li>
* <li><code>gt</code></li>
* <li><code>lt</code></li>
* <li><code>amp</code></li>
* </ul>
* <strong>Note:</strong> It should not be subject to change unless when outputting a non-HTML data format like BBCode.
* @type Boolean
* @default <code>true</code>
* @example
* config.basicEntities = false;
*/
CKEDITOR.config.basicEntities = true;
/**
* Whether to use HTML entities in the output.
* @name CKEDITOR.config.entities
* @type Boolean
* @default <code>true</code>
* @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 in 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>.
* @name CKEDITOR.config.entities_latin
* @type Boolean
* @default <code>true</code>
* @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 in 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>.
* @name CKEDITOR.config.entities_greek
* @type Boolean
* @default <code>true</code>
* @example
* config.entities_greek = false;
*/
CKEDITOR.config.entities_greek = true;
/**
* Whether to convert all remaining characters not included in the ASCII
* character table to their relative decimal numeric representation of HTML entity.
* When set to <code>force</code>, it will convert all entities into this format.
* For example the phrase "This is Chinese: &#27721;&#35821;." is output
* as "This is Chinese: &amp;#27721;&amp;#35821;."
* @name CKEDITOR.config.entities_processNumerical
* @type Boolean|String
* @default <code>false</code>
* @example
* config.entities_processNumerical = true;
* config.entities_processNumerical = 'force'; //Converts from "&nbsp;" into "&#160;";
*/
/**
* A comma separated list of additional entities to be used. Entity names
* or numbers must be used in a form that excludes the "&amp;" prefix and the ";" ending.
* @name CKEDITOR.config.entities_additional
* @default <code>'#39'</code> (The single quote (') character.)
* @type String
* @example
* config.entities_additional = '#1049'; // Adds Cyrillic capital letter Short I (&#1049;).
*/
CKEDITOR.config.entities_additional = '#39';

View File

@@ -0,0 +1,175 @@
/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
(function()
{
var cssStyle = CKEDITOR.htmlParser.cssStyle,
cssLength = CKEDITOR.tools.cssLength;
var cssLengthRegex = /^((?:\d*(?:\.\d+))|(?:\d+))(.*)?$/i;
/*
* Replacing the former CSS length value with the later one, with
* adjustment to the length unit.
*/
function replaceCssLength( length1, length2 )
{
var parts1 = cssLengthRegex.exec( length1 ),
parts2 = cssLengthRegex.exec( length2 );
// Omit pixel length unit when necessary,
// e.g. replaceCssLength( 10, '20px' ) -> 20
if ( parts1 )
{
if ( !parts1[ 2 ] && parts2[ 2 ] == 'px' )
return parts2[ 1 ];
if ( parts1[ 2 ] == 'px' && !parts2[ 2 ] )
return parts2[ 1 ] + 'px';
}
return length2;
}
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 ];
// Width/height in the fake object are subjected to clone into the real element.
if ( realElement && element.attributes[ 'data-cke-resizable' ] )
{
var styles = new cssStyle( element ).rules,
realAttrs = realElement.attributes,
width = styles.width,
height = styles.height;
width && ( realAttrs.width = replaceCssLength( realAttrs.width, width ) );
height && ( realAttrs.height = replaceCssLength( realAttrs.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;
var fakeStyle = new cssStyle();
var width = realElement.getAttribute( 'width' ),
height = realElement.getAttribute( 'height' );
width && ( fakeStyle.rules.width = cssLength( width ) );
height && ( fakeStyle.rules.height = cssLength( height ) );
fakeStyle.populate( attributes );
}
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;
var realAttrs = realElement.attributes,
fakeStyle = new cssStyle();
var width = realAttrs.width,
height = realAttrs.height;
width != undefined && ( fakeStyle.rules.width = cssLength( width ) );
height != undefined && ( fakeStyle.rules.height = cssLength ( height ) );
fakeStyle.populate( attributes );
}
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;
var element = CKEDITOR.dom.element.createFromHtml(
decodeURIComponent( fakeElement.data( 'cke-realelement' ) ),
this.document );
if ( fakeElement.data( 'cke-resizable') )
{
var width = fakeElement.getStyle( 'width' ),
height = fakeElement.getStyle( 'height' );
width && element.setAttribute( 'width', replaceCssLength( element.getAttribute( 'width' ), width ) );
height && element.setAttribute( 'height', replaceCssLength( element.getAttribute( 'height' ), height ) );
}
return element;
};
})();

View File

@@ -0,0 +1,534 @@
/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
/**
* @fileOverview The "filebrowser" plugin that adds support for file uploads and
* browsing.
*
* When a file is uploaded or selected inside the file browser, its URL is
* inserted automatically into a field defined in the <code>filebrowser</code>
* attribute. In order to specify a field that should be updated, pass the tab ID and
* the element ID, separated with a colon.<br /><br />
*
* <strong>Example 1: (Browse)</strong>
*
* <pre>
* {
* type : 'button',
* id : 'browse',
* filebrowser : 'tabId:elementId',
* label : editor.lang.common.browseServer
* }
* </pre>
*
* If you set the <code>filebrowser</code> attribute for an element other than
* the <code>fileButton</code>, the <code>Browse</code> action will be triggered.<br /><br />
*
* <strong>Example 2: (Quick Upload)</strong>
*
* <pre>
* {
* type : 'fileButton',
* id : 'uploadButton',
* filebrowser : 'tabId:elementId',
* label : editor.lang.common.uploadSubmit,
* 'for' : [ 'upload', 'upload' ]
* }
* </pre>
*
* If you set the <code>filebrowser</code> attribute for a <code>fileButton</code>
* element, the <code>QuickUpload</code> action will be executed.<br /><br />
*
* The filebrowser plugin also supports more advanced configuration performed through
* a JavaScript object.
*
* The following settings are supported:
*
* <ul>
* <li><code>action</code> &ndash; <code>Browse</code> or <code>QuickUpload</code>.</li>
* <li><code>target</code> &ndash; the field to update in the <code><em>tabId:elementId</em></code> format.</li>
* <li><code>params</code> &ndash; additional arguments to be passed to the server connector (optional).</li>
* <li><code>onSelect</code> &ndash; a function to execute when the file is selected/uploaded (optional).</li>
* <li><code>url</code> &ndash; the URL to be called (optional).</li>
* </ul>
*
* <strong>Example 3: (Quick Upload)</strong>
*
* <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 you have a file element with an ID of <code>myFile</code>, a text
* field with an ID of <code>elementId</code> and a <code>fileButton</code>.
* If the <code>filebowser.url</code> attribute is not specified explicitly,
* the form action will be set to <code>filebrowser[<em>DialogWindowName</em>]UploadUrl</code>
* or, if not specified, to <code>filebrowserUploadUrl</code>. Additional parameters
* from the <code>params</code> object will be added to the query string. It is
* possible to create your own <code>uploadHandler</code> and cancel the built-in
* <code>updateTargetElement</code> command.<br /><br />
*
* <strong>Example 4: (Browse)</strong>
*
* <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, when the button is pressed, the file browser will be opened in a
* popup window. If you do not specify the <code>filebrowser.url</code> attribute,
* <code>filebrowser[<em>DialogName</em>]BrowseUrl</code> or
* <code>filebrowserBrowseUrl</code> will be used. After selecting a file in the file
* browser, an element with an ID of <code>elementId</code> will be updated. Just
* like in the third example, a custom <code>onSelect</code> 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 );
// TODO: V4: Remove backward compatibility (#8163).
editor.popup( url, width, height, editor.config.filebrowserWindowFeatures || 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.definitionObject}
* definition Dialog definition.
* @param {Array}
* elements Array of {@link CKEDITOR.dialog.definition.content}
* 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.definitionObject}
* 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 );
editor.on( 'destroy', function () { CKEDITOR.tools.removeFunction( this._.filebrowserFn ); } );
}
} );
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 the <strong>Browse Server</strong>
* button is pressed. If configured, the <strong>Browse Server</strong> button will appear in the
* <strong>Link</strong>, <strong>Image</strong>, and <strong>Flash</strong> dialog windows.
* @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 <code>''</code> (empty string = disabled)
* @example
* config.filebrowserBrowseUrl = '/browser/browse.php';
*/
/**
* The location of the script that handles file uploads.
* If set, the <strong>Upload</strong> tab will appear in the <strong>Link</strong>, <strong>Image</strong>,
* and <strong>Flash</strong> dialog windows.
* @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 <code>''</code> (empty string = disabled)
* @example
* config.filebrowserUploadUrl = '/uploader/upload.php';
*/
/**
* The location of an external file browser that should be launched when the <strong>Browse Server</strong>
* button is pressed in the <strong>Image</strong> dialog window.
* If not set, CKEditor will use <code>{@link CKEDITOR.config.filebrowserBrowseUrl}</code>.
* @name CKEDITOR.config.filebrowserImageBrowseUrl
* @since 3.0
* @type String
* @default <code>''</code> (empty string = disabled)
* @example
* config.filebrowserImageBrowseUrl = '/browser/browse.php?type=Images';
*/
/**
* The location of an external file browser that should be launched when the <strong>Browse Server</strong>
* button is pressed in the <strong>Flash</strong> dialog window.
* If not set, CKEditor will use <code>{@link CKEDITOR.config.filebrowserBrowseUrl}</code>.
* @name CKEDITOR.config.filebrowserFlashBrowseUrl
* @since 3.0
* @type String
* @default <code>''</code> (empty string = disabled)
* @example
* config.filebrowserFlashBrowseUrl = '/browser/browse.php?type=Flash';
*/
/**
* The location of the script that handles file uploads in the <strong>Image</strong> dialog window.
* If not set, CKEditor will use <code>{@link CKEDITOR.config.filebrowserUploadUrl}</code>.
* @name CKEDITOR.config.filebrowserImageUploadUrl
* @since 3.0
* @type String
* @default <code>''</code> (empty string = disabled)
* @example
* config.filebrowserImageUploadUrl = '/uploader/upload.php?type=Images';
*/
/**
* The location of the script that handles file uploads in the <strong>Flash</strong> dialog window.
* If not set, CKEditor will use <code>{@link CKEDITOR.config.filebrowserUploadUrl}</code>.
* @name CKEDITOR.config.filebrowserFlashUploadUrl
* @since 3.0
* @type String
* @default <code>''</code> (empty string = disabled)
* @example
* config.filebrowserFlashUploadUrl = '/uploader/upload.php?type=Flash';
*/
/**
* The location of an external file browser that should be launched when the <strong>Browse Server</strong>
* button is pressed in the <strong>Link</strong> tab of the <strong>Image</strong> dialog window.
* If not set, CKEditor will use <code>{@link CKEDITOR.config.filebrowserBrowseUrl}</code>.
* @name CKEDITOR.config.filebrowserImageBrowseLinkUrl
* @since 3.2
* @type String
* @default <code>''</code> (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 <code>'location=no,menubar=no,toolbar=no,dependent=yes,minimizable=no,modal=yes,alwaysRaised=yes,resizable=yes,scrollbars=yes'</code>
* @example
* config.filebrowserWindowFeatures = 'resizable=yes,scrollbars=no';
*/
/**
* The width of the file browser popup window. It can be a number denoting a value in
* pixels or a percent string.
* @name CKEDITOR.config.filebrowserWindowWidth
* @type Number|String
* @default <code>'80%'</code>
* @example
* config.filebrowserWindowWidth = 750;
* @example
* config.filebrowserWindowWidth = '50%';
*/
/**
* The height of the file browser popup window. It can be a number denoting a value in
* pixels or a percent string.
* @name CKEDITOR.config.filebrowserWindowHeight
* @type Number|String
* @default <code>'70%'</code>
* @example
* config.filebrowserWindowHeight = 580;
* @example
* config.filebrowserWindowHeight = '50%';
*/

View File

@@ -0,0 +1,915 @@
/*
Copyright (c) 2003-2011, 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( { attributes : { 'data-cke-highlight': 1 }, fullMatch : 1, ignoreReadonly : 1, childRule : function(){ return 0; } },
editor.config.find_highlight, true ) );
/**
* 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 : 170,
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',
id : 'btnFind',
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 : 'fieldset',
label : CKEDITOR.tools.htmlEncode( lang.findOptions ),
style : 'margin-top:29px',
children :
[
{
type : 'vbox',
padding : 0,
children :
[
{
type : 'checkbox',
id : 'txtFindCaseChk',
isChanged : false,
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',
id : 'btnFindReplace',
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',
id : 'btnReplaceAll',
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 : 'fieldset',
label : CKEDITOR.tools.htmlEncode( lang.findOptions ),
children :
[
{
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();
// Fill in the find field with selected text.
var selectedText = this.getParentEditor().getSelection().getSelectedText(),
patternFieldId = ( startupPage == 'find' ? 'txtFindFind' : 'txtFindReplace' );
var field = this.getContentElement( startupPage, patternFieldId );
field.setValue( selectedText );
field.select();
this.selectPage( startupPage );
this[ ( startupPage == 'find' && this._.editor.readOnly? 'hide' : 'show' ) + 'Page' ]( 'replace');
},
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,47 @@
/*
Copyright (c) 2003-2011, 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;
findCommand.readOnly = 1;
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,674 @@
/*
Copyright (c) 2003-2011, 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' }, { type : ATTRTYPE_OBJECT, name : 'data' } ],
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;
var defaultToPixel = CKEDITOR.tools.cssLength;
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:
// Avoid applying the data attribute when not needed (#7733)
if ( !objectNode || ( attrDef.name == 'data' && embedNode && !objectNode.hasAttribute( 'data' ) ) )
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.htmlLength( editor.lang.common.invalidHtmlLength.replace( '%1', editor.lang.common.width ) ),
setup : loadValue,
commit : commitValue
},
{
type : 'text',
id : 'height',
style : 'width:95px',
label : editor.lang.common.height,
validate : CKEDITOR.dialog.validate.htmlLength( editor.lang.common.invalidHtmlLength.replace( '%1', editor.lang.common.height ) ),
setup : loadValue,
commit : commitValue
},
{
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',
validate : CKEDITOR.dialog.validate.inlineStyle( editor.lang.common.invalidInlineStyle ),
label : editor.lang.common.cssStyle,
setup : loadValue,
commit : commitValue
}
]
}
]
};
} );
})();

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 B

View File

@@ -0,0 +1,154 @@
/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
(function()
{
var flashFilenameRegex = /\.swf(?:$|\?)/i;
function isFlashEmbed( element )
{
var attributes = element.attributes;
return ( attributes.type == 'application/x-shockwave-flash' || flashFilenameRegex.test( attributes.src || '' ) );
}
function createFakeElement( editor, realElement )
{
return editor.createFakeParserElement( realElement, 'cke_flash', 'flash', true );
}
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 && !isFlashEmbed( element ) )
{
// 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,428 @@
/*
Copyright (c) 2003-2011, 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._ =
{
editor : editor,
// 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;
// Record from where the focus is when open panel.
this._.returnFocus = this._.editor.focusManager.hasFocus ? this._.editor : new CKEDITOR.dom.element( CKEDITOR.document.$.activeElement );
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 = ev.data.getTarget() ;
if ( target.getName && target.getName() != 'iframe' )
return;
if ( this.visible && !this._.activeChild && !isShowing )
{
// Panel close is caused by user's navigating away the focus, e.g. click outside the panel.
// DO NOT restore focus in this case.
delete this._.returnFocus;
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 ) + 3;
// 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 ) + 3;
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( returnFocus )
{
if ( this.visible && ( !this.onHide || this.onHide.call( this ) !== true ) )
{
this.hideChild();
// Blur previously focused element. (#6671)
CKEDITOR.env.gecko && this._.iframe.getFrameDocument().$.activeElement.blur();
this.element.setStyle( 'display', 'none' );
this.visible = 0;
this.element.getFirst().removeCustomData( 'activePanel' );
// Return focus properly. (#6247)
var focusReturn = returnFocus !== false && this._.returnFocus;
if ( focusReturn )
{
// Webkit requires focus moved out panel iframe first.
if ( CKEDITOR.env.webkit && focusReturn.type )
focusReturn.getWindow().$.focus();
focusReturn.focus();
}
}
},
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;
// Sub panels don't manage focus. (#7881)
delete activeChild._.returnFocus;
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-2011, 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,197 @@
/*
Copyright (c) 2003-2011, 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, styles[tag].buildPreview( label ), label );
}
},
onClick : function( value )
{
editor.focus();
editor.fire( 'saveSnapshot' );
var style = styles[ value ],
elementPath = new CKEDITOR.dom.elementPath( editor.getSelection().getStartElement() );
style[ style.checkActive( elementPath ) ? 'remove' : '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,118 @@
/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.dialog.add( 'button', function( editor )
{
function commitAttributes( element )
{
var val = this.getValue();
if ( val )
{
element.attributes[ this.id ] = val;
if ( this.id == 'name' )
element.attributes[ 'data-cke-saved-name' ] = val;
}
else
{
delete element.attributes[ this.id ];
if ( this.id == 'name' )
delete element.attributes[ 'data-cke-saved-name' ];
}
}
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 = this.getParentEditor(),
element = this.button,
isInsertMode = !element;
var fake = element ? CKEDITOR.htmlParser.fragment.fromHtml( element.getOuterHtml() ).children[ 0 ]
: new CKEDITOR.htmlParser.element( 'input' );
this.commitContent( fake );
var writer = new CKEDITOR.htmlParser.basicWriter();
fake.writeHtml( writer );
var newElement = CKEDITOR.dom.element.createFromHtml( writer.getHtml(), editor.document );
if ( isInsertMode )
editor.insertElement( newElement );
else
{
newElement.replace( element );
editor.getSelection().selectElement( newElement );
}
},
contents : [
{
id : 'info',
label : editor.lang.button.title,
title : editor.lang.button.title,
elements : [
{
id : 'name',
type : 'text',
label : editor.lang.common.name,
'default' : '',
setup : function( element )
{
this.setValue(
element.data( 'cke-saved-name' ) ||
element.getAttribute( 'name' ) ||
'' );
},
commit : commitAttributes
},
{
id : 'value',
type : 'text',
label : editor.lang.button.text,
accessKey : 'V',
'default' : '',
setup : function( element )
{
this.setValue( element.getAttribute( 'value' ) || '' );
},
commit : commitAttributes
},
{
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 : commitAttributes
}
]
}
]
};
});

View File

@@ -0,0 +1,153 @@
/*
Copyright (c) 2003-2011, 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-2011, 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' );
!CKEDITOR.env.ie && 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,100 @@
/*
Copyright (c) 2003-2011, 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 && !( CKEDITOR.document.$.documentMode >= 8 ) ?
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-2011, 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 || CKEDITOR.env.opera ) )
{
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,558 @@
/*
Copyright (c) 2003-2011, 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',
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' ],
children :
[
{
id : 'txtSize',
type : 'text',
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' ],
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',
id : 'btnAdd',
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',
id : 'btnModify',
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',
id : 'btnUp',
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',
id : 'btnDown',
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',
id : 'btnSetValue',
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',
id : 'btnDelete',
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,135 @@
/*
Copyright (c) 2003-2011, 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 : 220,
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' );
}
}
},
{
type : 'hbox',
widths:['50%','50%'],
children:[
{
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' );
}
}
]
},
{
id : 'value',
type : 'textarea',
label : editor.lang.textfield.value,
'default' : '',
setup : function( element )
{
this.setValue( element.$.defaultValue );
},
commit : function( element )
{
element.$.value = element.$.defaultValue = this.getValue() ;
}
}
]
}
]
};
});

View File

@@ -0,0 +1,199 @@
/*
Copyright (c) 2003-2011, 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,288 @@
/*
Copyright (c) 2003-2011, 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' )
{
switch( element.getAttribute( 'type' ) )
{
case 'button' :
case 'submit' :
case 'reset' :
return { button : CKEDITOR.TRISTATE_OFF };
case 'checkbox' :
return { checkbox : CKEDITOR.TRISTATE_OFF };
case 'radio' :
return { radio : CKEDITOR.TRISTATE_OFF };
case 'image' :
return { imagebutton : CKEDITOR.TRISTATE_OFF };
default :
return { textfield : 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' ) )
{
switch ( element.getAttribute( 'type' ) )
{
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;
default :
evt.data.dialog = 'textfield';
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;
// Old IEs don't provide type for Text inputs #5522
if ( !type )
attrs.type = 'text';
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 = CKEDITOR.tools.override( CKEDITOR.dom.element.prototype.hasAttribute,
function( original )
{
return 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' );
return type == 'checkbox' || type == 'radio' ? this.$.value != 'on' : this.$.value;
}
}
return original.apply( this, arguments );
};
});
}

View File

@@ -0,0 +1,48 @@
/*
Copyright (c) 2003-2011, 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 )
{
var hr = editor.document.createElement( 'hr' ),
range = new CKEDITOR.dom.range( editor.document );
editor.insertElement( hr );
// If there's nothing or a non-editable block followed by, establish a new paragraph
// to make sure cursor is not trapped.
range.moveToPosition( hr, CKEDITOR.POSITION_AFTER_END );
var next = hr.getNext();
if ( !next || next.type == CKEDITOR.NODE_ELEMENT && !next.isEditable() )
range.fixBlock( true, editor.config.enterMode == CKEDITOR.ENTER_DIV ? 'div' : 'p' );
range.select();
}
};
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,596 @@
/*
Copyright (c) 2003-2011, 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;
// Define orders of table elements.
var tableOrder = [ 'caption', 'colgroup', 'col', 'thead', 'tfoot', 'tbody' ];
// 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;
},
// The contents of table should be in correct order (#4809).
table : function( element )
{
var children = element.children;
children.sort( function ( node1, node2 )
{
return node1.type == CKEDITOR.NODE_ELEMENT && node2.type == node1.type ?
CKEDITOR.tools.indexOf( tableOrder, node1.name ) > CKEDITOR.tools.indexOf( tableOrder, node2.name ) ? 1 : -1 : 0;
} );
},
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;
},
// Empty <pre> in IE is reported with filler node (&nbsp;).
pre : function( element ) { CKEDITOR.env.ie && trimFillers( element ); },
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;
}
}
};
if ( CKEDITOR.env.ie )
{
// IE outputs style attribute in capital letters. We should convert
// them back to lower case, while not hurting the values (#5930)
defaultHtmlFilterRules.attributes.style = function( value, element )
{
return value.replace( /(^|;)([^\:]+)/g, function( match )
{
return match.toLowerCase();
});
};
}
function protectReadOnly( element )
{
var attrs = element.attributes;
// We should flag that the element was locked by our code so
// it'll be editable by the editor functions (#6046).
if ( attrs.contenteditable != "false" )
attrs[ 'data-cke-editable' ] = attrs.contenteditable ? 'true' : 1;
attrs.contenteditable = "false";
}
function unprotectReadyOnly( element )
{
var attrs = element.attributes;
switch( attrs[ 'data-cke-editable' ] )
{
case 'true': attrs.contenteditable = 'true'; break;
case '1': delete attrs.contenteditable; break;
}
}
// 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 protectElementRegex = /<(a|area|img|input)\b([^>]*)>/gi,
protectAttributeRegex = /\b(on\w+|href|src|name)\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|(?:[^ "'>]+))/gi;
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( protectElementRegex, function( element, tag, attributes )
{
return '<' + tag + attributes.replace( protectAttributeRegex, function( fullAttr, attrName )
{
// Avoid corrupting the inline event attributes (#7243).
// We should not rewrite the existed protected attributes, e.g. clipboard content from editor. (#5218)
if ( !( /^on/ ).test( attrName ) && attributes.indexOf( 'data-cke-saved-' + attrName ) == -1 )
return ' data-cke-saved-' + fullAttr + ' ' + fullAttr;
return fullAttr;
}) + '>';
});
}
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 unprotectSource( html, editor )
{
var store = editor._.dataStore;
return html.replace( /<!--\{cke_protected\}([\s\S]+?)-->/g, function( match, data )
{
return decodeURIComponent( data );
}).replace( /\{cke_protected_(\d+)\}/g, function( match, id )
{
return store && store[ id ] || '';
});
}
function protectSource( data, editor )
{
var protectedHtml = [],
protectRegexes = editor.config.protectedSource,
store = editor._.dataStore || ( editor._.dataStore = { id : 1 } ),
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 ];
}
);
// Avoid protecting over protected, e.g. /\{.*?\}/
return ( /cke_temp(comment)?/ ).test( match ) ? match
: '<!--{cke_temp}' + ( protectedHtml.push( match ) - 1 ) + '-->';
});
}
data = data.replace( tempRegex, function( $, isComment, id )
{
return '<!--' + protectedSourceMarker +
( isComment ? '{C}' : '' ) +
encodeURIComponent( protectedHtml[ id ] ).replace( /--/g, '%2D%2D' ) +
'-->';
}
);
// Different protection pattern is used for those that
// live in attributes to avoid from being HTML encoded.
return data.replace( /(['"]).*?\1/g, function ( match )
{
return match.replace( /<!--\{cke_protected\}([\s\S]+?)-->/g, function( match, data )
{
store[ store.id ] = decodeURIComponent( data );
return '{cke_protected_'+ ( store.id++ ) + '}';
});
});
}
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 );
// 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 );
var data = writer.getHtml( true );
// Restore those non-HTML protected source. (#4475,#4880)
data = unprotectRealComments( data );
data = unprotectSource( data, this.editor );
return data;
}
};
})();
/**
* 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-2011, 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,229 @@
/*
Copyright (c) 2003-2011, 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 );
}
},
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.htmlLength( commonLang.invalidHtmlLength.replace( '%1', commonLang.width ) ),
setup : loadValue,
commit : commitValue
},
{
id : 'height',
type : 'text',
style : 'width:100%',
labelLayout : 'vertical',
label : commonLang.height,
validate : CKEDITOR.dialog.validate.htmlLength( commonLang.invalidHtmlLength.replace( '%1', commonLang.height ) ),
setup : loadValue,
commit : commitValue
},
{
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,87 @@
/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
(function()
{
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 editor.createFakeParserElement( element, 'cke_iframe', 'iframe', true );
}
}
});
}
}
});
})();

View File

@@ -0,0 +1,188 @@
/*
Copyright (c) 2003-2011, 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()
{
/**
* An iframe base dialog.
* @param {String} name Name of the dialog
* @param {String} title Title of the dialog
* @param {Number} minWidth Minimum width of the dialog
* @param {Number} minHeight Minimum height of the dialog
* @param {Function} [onContentLoad] Function called when the iframe has been loaded.
* If it isn't specified, the inner frame is notified of the dialog events ('load',
* 'resize', 'ok' and 'cancel') on a function called 'onDialogEvent'
* @param {Object} [userDefinition] Additional properties for the dialog definition
* @example
*/
CKEDITOR.dialog.addIframe = function( name, title, src, minWidth, minHeight, onContentLoad, userDefinition )
{
var element =
{
type : 'iframe',
src : src,
width : '100%',
height : '100%'
};
if ( typeof( onContentLoad ) == 'function' )
element.onContentLoad = onContentLoad;
else
element.onContentLoad = function()
{
var element = this.getElement(),
childWindow = element.$.contentWindow;
// If the inner frame has defined a "onDialogEvent" function, setup listeners
if ( childWindow.onDialogEvent )
{
var dialog = this.getDialog(),
notifyEvent = function(e)
{
return childWindow.onDialogEvent(e);
};
dialog.on( 'ok', notifyEvent );
dialog.on( 'cancel', notifyEvent );
dialog.on( 'resize', notifyEvent );
// Clear listeners
dialog.on( 'hide', function(e)
{
dialog.removeListener( 'ok', notifyEvent );
dialog.removeListener( 'cancel', notifyEvent );
dialog.removeListener( 'resize', notifyEvent );
e.removeListener();
} );
// Notify child iframe of load:
childWindow.onDialogEvent( {
name : 'load',
sender : this,
editor : dialog._.editor
} );
}
};
var definition =
{
title : title,
minWidth : minWidth,
minHeight : minHeight,
contents :
[
{
id : 'iframe',
label : title,
expand : true,
elements : [ element ]
}
]
};
for ( var i in userDefinition )
definition[i] = userDefinition[i];
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.definition.uiElement} 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-2011, 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' ) && !element.isReadOnly() )
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,461 @@
/*
Copyright (c) 2003-2011, 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 },
isNotWhitespaces = CKEDITOR.dom.walker.whitespaces( true ),
isNotBookmark = CKEDITOR.dom.walker.bookmark( false, true );
function onSelectionChange( evt )
{
if ( evt.editor.readOnly )
return null;
var editor = evt.editor,
elementPath = evt.data.path,
list = elementPath && elementPath.contains( listNodeNames ),
firstBlock = elementPath.block || elementPath.blockLimit;
if ( list )
return this.setState( CKEDITOR.TRISTATE_OFF );
if ( !this.useIndentClasses && this.name == 'indent' )
return this.setState( CKEDITOR.TRISTATE_OFF );
if ( !firstBlock )
return this.setState( 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 this.setState( CKEDITOR.TRISTATE_DISABLED );
return this.setState( CKEDITOR.TRISTATE_OFF );
}
else
{
var indent = parseInt( firstBlock.getStyle( getIndentCssProperty( firstBlock ) ), 10 );
if ( isNaN( indent ) )
indent = 0;
if ( indent <= 0 )
return this.setState( CKEDITOR.TRISTATE_DISABLED );
return this.setState( 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( enterMode == CKEDITOR.ENTER_P ? 'p' : 'div' ) ) )
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 = editor.addCommand( 'indent', new indentCommand( editor, 'indent' ) ),
outdent = editor.addCommand( 'outdent', new indentCommand( editor, '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,253 @@
/*
Copyright (c) 2003-2011, 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 )
{
if ( evt.editor.readOnly )
return;
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( enterMode == CKEDITOR.ENTER_P ? 'p' : 'div' ) ) )
{
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' ]
});
})();
/**
* List of classes to use for aligning the contents. If it's null, no classes will be used
* and instead the corresponding CSS values will be used. The array should contain 4 members, in the following order: left, center, right, justify.
* @name CKEDITOR.config.justifyClasses
* @type Array
* @default null
* @example
* // Use the classes 'AlignLeft', 'AlignCenter', 'AlignRight', 'AlignJustify'
* config.justifyClasses = [ 'AlignLeft', 'AlignCenter', 'AlignRight', 'AlignJustify' ];
*/

View File

@@ -0,0 +1,225 @@
/*
Copyright (c) 2003-2011, 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 + ( CKEDITOR.env.ie || CKEDITOR.env.webkit ? 189 : 109 ) /*-*/, 'toolbarCollapse' ],
[ CKEDITOR.ALT + 48 /*0*/, 'a11yHelp' ]
];
/**
* Fired when any keyboard key (or combination) is pressed into the editing area.
* @name CKEDITOR.editor#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,144 @@
/*
Copyright (c) 2003-2011, 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( element )
{
this._.selectedElement = element;
var attributeValue = element.data( 'cke-saved-name' );
this.setValueOf( 'info','txtName', attributeValue || '' );
};
function createFakeAnchor( editor, anchor )
{
return editor.createFakeElement( anchor, 'cke_anchor', 'anchor' );
}
return {
title : editor.lang.anchor.title,
minWidth : 300,
minHeight : 60,
onOk : function()
{
var name = this.getValueOf( 'info', 'txtName' );
var attributes =
{
name : name,
'data-cke-saved-name' : name
};
if ( this._.selectedElement )
{
if ( this._.selectedElement.data( 'cke-realelement' ) )
{
var newFake = createFakeAnchor( editor, editor.document.createElement( 'a', { attributes: attributes } ) );
newFake.replace( this._.selectedElement );
}
else
this._.selectedElement.setAttributes( attributes );
}
else
{
var sel = editor.getSelection(),
range = sel && sel.getRanges()[ 0 ];
// Empty anchor
if ( range.collapsed )
{
if ( CKEDITOR.plugins.link.synAnchorSelector )
attributes[ 'class' ] = 'cke_anchor_empty';
if ( CKEDITOR.plugins.link.emptyAnchorFix )
{
attributes[ 'contenteditable' ] = 'false';
attributes[ 'data-cke-editable' ] = 1;
}
var anchor = editor.document.createElement( 'a', { attributes: attributes } );
// Transform the anchor into a fake element for browsers that need it.
if ( CKEDITOR.plugins.link.fakeAnchor )
anchor = createFakeAnchor( editor, anchor );
range.insertNode( anchor );
}
else
{
if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 )
attributes['class'] = 'cke_anchor';
// Apply style.
var style = new CKEDITOR.style( { element : 'a', attributes : attributes } );
style.type = CKEDITOR.STYLE_INLINE;
style.apply( editor.document );
}
}
},
onHide : function()
{
delete this._.selectedElement;
},
onShow : function()
{
var selection = editor.getSelection(),
fullySelected = selection.getSelectedElement(),
partialSelected;
// Detect the anchor under selection.
if ( fullySelected )
{
if ( CKEDITOR.plugins.link.fakeAnchor )
{
var realElement = CKEDITOR.plugins.link.tryRestoreFakeAnchor( editor, fullySelected );
realElement && loadElements.call( this, realElement );
this._.selectedElement = fullySelected;
}
else if ( fullySelected.is( 'a' ) && fullySelected.hasAttribute( 'name' ) )
loadElements.call( this, fullySelected );
}
else
{
partialSelected = CKEDITOR.plugins.link.getSelectedLink( editor );
if ( partialSelected )
{
loadElements.call( this, partialSelected );
selection.selectElement( partialSelected );
}
}
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,374 @@
/*
Copyright (c) 2003-2011, 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.addCommand( 'removeAnchor', new CKEDITOR.removeAnchorCommand() );
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.
var side = ( editor.lang.dir == 'rtl' ? 'right' : 'left' );
var basicCss =
'background:url(' + CKEDITOR.getUrl( this.path + 'images/anchor.gif' ) + ') no-repeat ' + side + ' center;' +
'border:1px dotted #00f;';
editor.addCss(
'a.cke_anchor,a.cke_anchor_empty' +
// IE6 breaks with the following selectors.
( ( CKEDITOR.env.ie && CKEDITOR.env.version < 7 ) ? '' :
',a[name],a[data-cke-saved-name]' ) +
'{' +
basicCss +
'padding-' + side + ':18px;' +
// Show the arrow cursor for the anchor image (FF at least).
'cursor:auto;' +
'}' +
( CKEDITOR.env.ie ? (
'a.cke_anchor_empty' +
'{' +
// Make empty anchor selectable on IE.
'display:inline-block;' +
'}'
) : '' ) +
'img.cke_anchor' +
'{' +
basicCss +
'width:16px;' +
'min-height:15px;' +
// The default line-height on IE.
'height:1.15em;' +
// Opera works better with "middle" (even if not perfect)
'vertical-align:' + ( CKEDITOR.env.opera ? 'middle' : 'text-bottom' ) + ';' +
'}');
// Register selection change handler for the unlink button.
editor.on( 'selectionChange', function( evt )
{
if ( editor.readOnly )
return;
/*
* 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' ) && element.getChildCount() )
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' ) || !element.getChildCount() ) ) ? 'anchor' : 'link';
editor.getSelection().selectElement( element );
}
else if ( CKEDITOR.plugins.link.tryRestoreFakeAnchor( editor, element ) )
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',
order : 1
},
removeAnchor :
{
label : editor.lang.anchor.remove,
command : 'removeAnchor',
group : 'anchor',
order : 5
},
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 anchor = CKEDITOR.plugins.link.tryRestoreFakeAnchor( editor, element );
if ( !anchor && !( anchor = CKEDITOR.plugins.link.getSelectedLink( editor ) ) )
return null;
var menu = {};
if ( anchor.getAttribute( 'href' ) && anchor.getChildCount() )
menu = { link : CKEDITOR.TRISTATE_OFF, unlink : CKEDITOR.TRISTATE_OFF };
if ( anchor && anchor.hasAttribute( 'name' ) )
menu.anchor = menu.removeAnchor = CKEDITOR.TRISTATE_OFF;
return menu;
});
}
},
afterInit : function( editor )
{
// Register a filter to displaying placeholders after mode change.
var dataProcessor = editor.dataProcessor,
dataFilter = dataProcessor && dataProcessor.dataFilter,
htmlFilter = dataProcessor && dataProcessor.htmlFilter,
pathFilters = editor._.elementsPath && editor._.elementsPath.filters;
if ( dataFilter )
{
dataFilter.addRules(
{
elements :
{
a : function( element )
{
var attributes = element.attributes;
if ( !attributes.name )
return null;
var isEmpty = !element.children.length;
if ( CKEDITOR.plugins.link.synAnchorSelector )
{
// IE needs a specific class name to be applied
// to the anchors, for appropriate styling.
var ieClass = isEmpty ? 'cke_anchor_empty' : 'cke_anchor';
var cls = attributes[ 'class' ];
if ( attributes.name && ( !cls || cls.indexOf( ieClass ) < 0 ) )
attributes[ 'class' ] = ( cls || '' ) + ' ' + ieClass;
if ( isEmpty && CKEDITOR.plugins.link.emptyAnchorFix )
{
attributes.contenteditable = 'false';
attributes[ 'data-cke-editable' ] = 1;
}
}
else if ( CKEDITOR.plugins.link.fakeAnchor && isEmpty )
return editor.createFakeParserElement( element, 'cke_anchor', 'anchor' );
return null;
}
}
});
}
if ( CKEDITOR.plugins.link.emptyAnchorFix && htmlFilter )
{
htmlFilter.addRules(
{
elements :
{
a : function( element )
{
delete element.attributes.contenteditable;
}
}
});
}
if ( pathFilters )
{
pathFilters.push( function( element, name )
{
if ( name == 'a' )
{
if ( CKEDITOR.plugins.link.tryRestoreFakeAnchor( editor, element ) ||
( element.getAttribute( 'name' ) && ( !element.getAttribute( 'href' ) || !element.getChildCount() ) ) )
{
return '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; }
},
// Opera and WebKit don't make it possible to select empty anchors. Fake
// elements must be used for them.
fakeAnchor : CKEDITOR.env.opera || CKEDITOR.env.webkit,
// For browsers that don't support CSS3 a[name]:empty(), note IE9 is included because of #7783.
synAnchorSelector : CKEDITOR.env.ie,
// For browsers that have editing issue with empty anchor.
emptyAnchorFix : CKEDITOR.env.ie && CKEDITOR.env.version < 8,
tryRestoreFakeAnchor : function( editor, element )
{
if ( element && element.data( 'cke-real-element-type' ) && element.data( 'cke-real-element-type' ) == 'anchor' )
{
var link = editor.restoreRealElement( element );
if ( link.data( 'cke-saved-name' ) )
return link;
}
}
};
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.removeAnchorCommand = function(){};
CKEDITOR.removeAnchorCommand.prototype =
{
/** @ignore */
exec : function( editor )
{
var sel = editor.getSelection(),
bms = sel.createBookmarks(),
anchor;
if ( sel && ( anchor = sel.getSelectedElement() ) && ( CKEDITOR.plugins.link.fakeAnchor && !anchor.getChildCount() ? CKEDITOR.plugins.link.tryRestoreFakeAnchor( editor, anchor ) : anchor.is( 'a' ) ) )
anchor.remove( 1 );
else
{
if ( ( anchor = CKEDITOR.plugins.link.getSelectedLink( editor ) ) )
{
if ( anchor.hasAttribute( 'href' ) )
{
anchor.removeAttributes( { name : 1, 'data-cke-saved-name' : 1 } );
anchor.removeClass( 'cke_anchor' );
}
else
anchor.remove( 1 );
}
}
sel.selectBookmarks( bms );
}
};
CKEDITOR.tools.extend( CKEDITOR.config,
{
linkShowAdvancedTab : true,
linkShowTargetTab : true
} );

View File

@@ -0,0 +1,774 @@
/*
Copyright (c) 2003-2011, 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 ) ); };
function cleanUpDirection( element )
{
var dir, parent, parentDir;
if ( ( dir = element.getDirection() ) )
{
parent = element.getParent();
while ( parent && !( parentDir = parent.getDirection() ) )
parent = parent.getParent();
if ( dir == parentDir )
element.removeAttribute( 'dir' );
}
}
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 );
// Fixing malformed nested lists by moving it into a previous list item. (#6236)
if( listItem.type == CKEDITOR.NODE_ELEMENT && listItem.getName() in CKEDITOR.dtd.$list )
CKEDITOR.plugins.list.listToArray( listItem, database, baseArray, baseIndentLevel + 1 );
// 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,
orgDir,
paragraphName = ( paragraphMode == CKEDITOR.ENTER_P ? 'p' : 'div' );
while ( 1 )
{
var item = listArray[ currentIndex ];
orgDir = item.element.getDirection( 1 );
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 ) );
if ( orgDir != rootNode.getDirection( 1 ) )
currentListItem.setAttribute( 'dir', orgDir );
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 )
{
// Maintain original direction (#6861).
var currDir = listArray[ currentIndex - 1 ].element.getDirection( 1 ),
listData = CKEDITOR.plugins.list.arrayToList( listArray, null, currentIndex, paragraphMode,
currDir != orgDir ? orgDir: null );
// 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. (#6724)
if ( !currentListItem.getChildCount() && CKEDITOR.env.ie && !( doc.$.documentMode > 7 ))
currentListItem.append( doc.createText( '\xa0' ) );
currentListItem.append( listData.listNode );
currentIndex = listData.nextIndex;
}
else if ( item.indent == -1 && !baseIndex && item.grandparent )
{
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 )
{
currentListItem = doc.createElement( paragraphName );
item.element.copyAttributes( currentListItem, { type:1, value:1 } );
// 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 );
}
if ( currentListItem.type == CKEDITOR.NODE_ELEMENT )
{
if ( item.grandparent.getDirection( 1 ) != orgDir )
currentListItem.setAttribute( 'dir', orgDir );
}
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;
}
if ( database )
{
var currentNode = retval.getFirst(),
listRoot = listArray[ 0 ].parent;
while ( currentNode )
{
if ( currentNode.type == CKEDITOR.NODE_ELEMENT )
{
// Clear marker attributes for the new list tree made of cloned nodes, if any.
CKEDITOR.dom.element.clearMarkers( database, currentNode );
// Clear redundant direction attribute specified on list items.
if ( currentNode.getName() in CKEDITOR.dtd.$listItem )
cleanUpDirection( currentNode );
}
currentNode = currentNode.getNextSourceNode();
}
}
return { listNode : retval, nextIndex : currentIndex };
}
};
function onSelectionChange( evt )
{
if ( evt.editor.readOnly )
return null;
var path = evt.data.path,
blockLimit = path.blockLimit,
elements = path.elements,
element,
i;
// Grouping should only happen under blockLimit.(#3940).
for ( i = 0 ; i < elements.length && ( element = elements[ i ] )
&& !element.equals( blockLimit ); i++ )
{
if ( listNodeNames[ elements[ i ].getName() ] )
return this.setState( this.type == elements[ i ].getName() ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF );
}
return this.setState( 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
{
contentBlock.copyAttributes( listItem );
// Remove direction attribute after it was merged into list root. (#7657)
if ( listDir && contentBlock.getDirection() )
{
listItem.removeStyle( 'direction' );
listItem.removeAttribute( 'dir' );
}
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;
}
// Move direction attribute from root to list items.
function dirToListItems( list )
{
var dir = list.getDirection();
if ( dir )
{
for ( var i = 0, children = list.getChildren(), child; child = children.getItem( i ), i < children.count(); i++ )
{
if ( child.type == CKEDITOR.NODE_ELEMENT && child.is( 'li' ) && !child.getDirection() )
child.setAttribute( 'dir', dir );
}
list.removeAttribute( 'dir' );
}
}
listCommand.prototype = {
exec : function( editor )
{
var doc = editor.document,
config = editor.config,
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();
if ( !body.getFirst( nonEmpty ) )
{
config.enterMode == CKEDITOR.ENTER_BR ?
body.appendBogus() :
ranges[ 0 ].fixBlock( 1, config.enterMode == CKEDITOR.ENTER_P ? 'p' : 'div' );
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() )
this.setState( 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 )
{
// In case to be merged lists have difference directions. (#7448)
if ( sibling.getDirection( 1 ) != listNode.getDirection( 1 ) )
dirToListItems( listNode.getDirection() ? listNode : sibling );
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 = editor.addCommand( 'numberedlist', new listCommand( 'numberedlist', 'ol' ) ),
bulletedListCommand = editor.addCommand( 'bulletedlist', new listCommand( 'bulletedlist', 'ul' ) );
// 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,266 @@
/*
Copyright (c) 2003-2011, 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 ] = CKEDITOR.env.ie ? 'mouseup' : 'click'; // SPACE
CKEDITOR.env.ie && ( keys[ 13 ] = 'mouseup' ); // Manage ENTER, since onclick is blocked in IE (#8041).
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, '\')" ' +
( CKEDITOR.env.ie ? 'onclick="return false;" onmouseup' : 'onclick' ) + // #188
'="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.onMark && this.onMark( item );
},
unmark : function( value )
{
var doc = this.element.getDocument(),
itemId = this._.items[ value ],
item = doc.getById( itemId );
item.removeClass( 'cke_selected' );
doc.getById( itemId + '_option' ).removeAttribute( 'aria-selected' );
this.onUnmark && this.onUnmark( item );
},
unmarkAll : function()
{
var items = this._.items,
doc = this.element.getDocument();
for ( var value in items )
{
var itemId = items[ value ];
doc.getById( itemId ).removeClass( 'cke_selected' );
doc.getById( itemId + '_option' ).removeAttribute( 'aria-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,225 @@
/*
* Copyright (c) 2003-2011, 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 listItem = function( node ) { return node.type == CKEDITOR.NODE_ELEMENT && node.is( 'li' ); };
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',
align : 'center',
style : 'width:150px',
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 )
{
// List item start number dominates.
var value = element.getFirst( listItem ).getAttribute( 'value' ) || element.getAttribute( 'start' ) || 1;
value && this.setValue( value );
},
commit : function( element )
{
var firstItem = element.getFirst( listItem );
var oldStart = firstItem.getAttribute( 'value' ) || element.getAttribute( 'start' ) || 1;
// Force start number on list root.
element.getFirst( listItem ).removeAttribute( 'value' );
var val = parseInt( this.getValue(), 10 );
if ( isNaN( val ) )
element.removeAttribute( 'start' );
else
element.setAttribute( 'start', val );
// Update consequent list item numbering.
var nextItem = firstItem, conseq = oldStart, startNumber = isNaN( val ) ? 1 : val;
while ( ( nextItem = nextItem.getNext( listItem ) ) && conseq++ )
{
if ( nextItem.getAttribute( 'value' ) == conseq )
nextItem.setAttribute( 'value', startNumber + conseq - oldStart );
}
}
},
{
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-2011, 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,353 @@
/*
Copyright (c) 2003-2011, 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' && !one.readOnly )
{
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',
{
// Disabled on iOS (#8307).
modes : { wysiwyg : !CKEDITOR.env.iOS, source : !CKEDITOR.env.iOS },
readOnly : 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 styles =
{
overflow : CKEDITOR.env.webkit ? '' : 'hidden', // #6896
width : 0,
height : 0
};
mainDocument.getDocumentElement().setStyles( styles );
!CKEDITOR.env.gecko && mainDocument.getDocumentElement().setStyle( 'position', 'fixed' );
!( CKEDITOR.env.gecko && CKEDITOR.env.quirks ) && 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.
// Special treatment for FF Quirks (#7284)
container.setStyle( 'position', CKEDITOR.env.gecko && CKEDITOR.env.quirks ? 'fixed' : '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' );
// Webkit requires a re-layout on editor chrome. (#6695)
if ( CKEDITOR.env.webkit )
{
container.setStyle( 'display', 'inline' );
setTimeout( function(){ container.setStyle( 'display', 'block' ); }, 0 );
}
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 ];
// Only try to change the button if it exists (#6166)
if( button )
{
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,541 @@
/*
Copyright (c) 2003-2011, 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;
/**
* Registers an item group to the editor context menu in order to make it
* possible to associate it with menu items later.
* @name CKEDITOR.editor.prototype.addMenuGroup
* @param {String} name Specify a group name.
* @param {Number} [order=100] Define the display sequence of this group
* inside the menu. A smaller value gets displayed first.
*/
editor.addMenuGroup = function( name, order )
{
groupsOrder[ name ] = order || 100;
};
/**
* Adds an item from the specified definition to the editor context menu.
* @name CKEDITOR.editor.prototype.addMenuItem
* @param {String} name The menu item name.
* @param {CKEDITOR.menu.definition} definition The menu item definition.
*/
editor.addMenuItem = function( name, definition )
{
if ( groupsOrder[ definition.group ] )
menuItems[ name ] = new CKEDITOR.menuItem( this, name, definition );
};
/**
* Adds one or more items from the specified definition array to the editor context menu.
* @name CKEDITOR.editor.prototype.addMenuItems
* @param {Array} definitions List of definitions for each menu item as if {@link CKEDITOR.editor.addMenuItem} is called.
*/
editor.addMenuItems = function( definitions )
{
for ( var itemName in definitions )
{
this.addMenuItem( itemName, definitions[ itemName ] );
}
};
/**
* Retrieves a particular menu item definition from the editor context menu.
* @name CKEDITOR.editor.prototype.getMenuItem
* @param {String} name The name of the desired menu item.
* @return {CKEDITOR.menu.definition}
*/
editor.getMenuItem = function( name )
{
return menuItems[ name ];
};
/**
* Removes a particular menu item added before from the editor context menu.
* @name CKEDITOR.editor.prototype.removeMenuItem
* @param {String} name The name of the desired menu item.
* @since 3.6.1
*/
editor.removeMenuItem = function( name )
{
delete menuItems[ name ];
};
},
requires : [ 'floatpanel' ]
});
(function()
{
CKEDITOR.menu = CKEDITOR.tools.createClass(
{
$ : function( editor, definition )
{
definition = this._.definition = definition || {};
this.id = CKEDITOR.tools.getNextId();
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.command || this.editor.getCommand( item.command ).state ) )
{
item.state = listenerItems[ itemName ];
this.add( item );
}
}
}
}
},
onClick : function( item )
{
this.hide( false );
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();
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[ ( editor.lang.dir == 'rtl' ? 37 : 39 ) ]= CKEDITOR.env.ie ? 'mouseup' : 'click'; // ARROW-RIGHT/ARROW-LEFT(rtl)
keys[ 32 ] = CKEDITOR.env.ie ? 'mouseup' : 'click'; // SPACE
CKEDITOR.env.ie && ( keys[ 13 ] = 'mouseup' ); // Manage ENTER, since onclick is blocked in IE (#8041).
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( returnFocus )
{
this._.onHide && this._.onHide();
this._.panel && this._.panel.hide( returnFocus );
}
}
});
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' + ( this.icon && this.icon.indexOf( '.png' ) == -1 ? ' cke_noalphafix' : '' ) + '">' +
'<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, ');" ' +
( CKEDITOR.env.ie ? 'onclick="return false;" onmouseup' : 'onclick' ) + // #188
'="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 displaying submenu
* options when moving the mouse over options that contain 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 order of items will reflect the order specified in this list if
* no priority was defined 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-2011, 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 = 'menubutton';
(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,55 @@
/*
Copyright (c) 2003-2011, 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
} );
editor.selectionChange();
}, 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.
* @name CKEDITOR.config.newpage_html
* @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,164 @@
/*
Copyright (c) 2003-2011, 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'
});
var cssStyles = [
'{' ,
'background: url(' + CKEDITOR.getUrl( this.path + 'images/pagebreak.gif' ) + ') no-repeat center center;' ,
'clear: both;' ,
'width:100%; _width:99.9%;' ,
'border-top: #999999 1px dotted;' ,
'border-bottom: #999999 1px dotted;' ,
'padding:0;' ,
'height: 5px;' ,
'cursor: default;' ,
'}'
].join( '' ).replace(/;/g, ' !important;' ); // Increase specificity to override other styles, e.g. block outline.
// Add the style that renders our placeholder.
editor.addCss( 'div.cke_pagebreak' + cssStyles );
// Opera needs help to select the page-break.
CKEDITOR.env.opera && editor.on( 'contentDom', function()
{
editor.document.on( 'click', function( evt )
{
var target = evt.data.getTarget();
if ( target.is( 'div' ) && target.hasClass( 'cke_pagebreak') )
editor.getSelection().selectElement( target );
});
});
},
afterInit : function( editor )
{
var label = editor.lang.pagebreakAlt;
// Register a filter to displaying placeholders after mode change.
var dataProcessor = editor.dataProcessor,
dataFilter = dataProcessor && dataProcessor.dataFilter,
htmlFilter = dataProcessor && dataProcessor.htmlFilter;
if ( htmlFilter )
{
htmlFilter.addRules(
{
attributes : {
'class' : function( value, element )
{
var className = value.replace( 'cke_pagebreak', '' );
if ( className != value )
{
var span = CKEDITOR.htmlParser.fragment.fromHtml( '<span style="display: none;">&nbsp;</span>' );
element.children.length = 0;
element.add( span );
var attrs = element.attributes;
delete attrs[ 'aria-label' ];
delete attrs.contenteditable;
delete attrs.title;
}
return className;
}
}
}, 5 );
}
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 ) )
{
attributes.contenteditable = "false";
attributes[ 'class' ] = "cke_pagebreak";
attributes[ 'data-cke-display-name' ] = "pagebreak";
attributes[ 'aria-label' ] = label;
attributes[ 'title' ] = label;
element.children.length = 0;
}
}
}
});
}
},
requires : [ 'fakeobjects' ]
});
CKEDITOR.plugins.pagebreakCmd =
{
exec : function( editor )
{
var label = editor.lang.pagebreakAlt;
// Create read-only element that represents a print break.
var pagebreak = CKEDITOR.dom.element.createFromHtml(
'<div style="' +
'page-break-after: always;"' +
'contenteditable="false" ' +
'title="'+ label + '" ' +
'aria-label="'+ label + '" ' +
'data-cke-display-name="pagebreak" ' +
'class="cke_pagebreak">' +
'</div>', editor.document );
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 )
pagebreak = pagebreak.clone( true );
range.splitBlock( 'p' );
range.insertNode( pagebreak );
if ( i == ranges.length - 1 )
{
var next = pagebreak.getNext();
range.moveToPosition( pagebreak, CKEDITOR.POSITION_AFTER_END );
// If there's nothing or a non-editable block followed by, establish a new paragraph
// to make sure cursor is not trapped.
if ( !next || next.type == CKEDITOR.NODE_ELEMENT && !next.isEditable() )
range.fixBlock( true, editor.config.enterMode == CKEDITOR.ENTER_DIV ? 'div' : 'p' );
range.select();
}
}
editor.fire( 'saveSnapshot' );
}
};

View File

@@ -0,0 +1,400 @@
/*
Copyright (c) 2003-2011, 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 = 'panel';
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();
// Make it scrollable on iOS. (#8308)
CKEDITOR.env.iOS && parentDiv.setStyles(
{
'overflow' : 'scroll',
'-webkit-overflow-scrolling' : 'touch'
});
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.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' :
case 'mouseup' :
index = this._.focusIndex;
link = index >= 0 && this.element.getElementsByTag( 'a' ).getItem( index );
if ( link )
link.$[ keyAction ] ? link.$[ keyAction ]() : link.$[ 'on' + keyAction ]();
return false;
}
return true;
}
}
});
/**
* Fired when a panel is added to the document
* @name CKEDITOR#ariaWidget
* @event
* @param {Object} holder The element wrapping the panel
*/

View File

@@ -0,0 +1,144 @@
/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.add( 'panelbutton',
{
requires : [ 'button' ],
onLoad : function()
{
function clickFn( 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 );
};
}
}
});
},
beforeInit : function( editor )
{
editor.ui.addHandler( CKEDITOR.UI_PANELBUTTON, CKEDITOR.ui.panelButton.handler );
}
});
/**
* Button UI element.
* @constant
* @example
*/
CKEDITOR.UI_PANELBUTTON = 'panelbutton';

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,141 @@
/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
(function()
{
function forceHtmlMode( evt ) { evt.data.mode = 'html'; }
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( evt )
{
evt && evt.removeListener();
editor.removeListener( 'beforePaste', forceHtmlMode );
forceFromWord && 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()
{
// Ensure the received data format is HTML and apply content filtering. (#6718)
forceFromWord = 1;
editor.on( 'beforePaste', forceHtmlMode );
if ( editor.execCommand( 'paste', 'html' ) === false )
{
editor.on( 'dialogShow', function ( evt )
{
evt.removeListener();
evt.data.on( 'cancel', resetFromWord );
});
editor.on( 'dialogHide', function( evt )
{
evt.data.removeListener( 'cancel', resetFromWord );
} );
}
editor.on( 'afterPaste', resetFromWord );
}
});
// Register the toolbar button.
editor.ui.addButton( 'PasteFromWord',
{
label : editor.lang.pastefromword.toolbar,
command : 'pastefromword'
});
editor.on( 'pasteState', function( evt )
{
editor.getCommand( 'pastefromword' ).setState( evt.data );
});
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, true );
}
return !isLoaded;
},
requires : [ 'clipboard' ]
});
})();
/**
* 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,67 @@
/*
Copyright (c) 2003-2011, 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(){ this.setupContent(); },
onOk : function(){ this.commitContent(); },
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();
},
setup : function()
{
this.setValue( '' );
},
commit : function()
{
var value = this.getValue();
setTimeout( function()
{
editor.fire( 'paste', { 'text' : value } );
}, 0 );
}
}
]
}
]
};
});
})();

View File

@@ -0,0 +1,98 @@
/*
Copyright (c) 2003-2011, 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 )
{
var mode = evt.data.commandData;
// Do NOT overwrite if HTML format is explicitly requested.
if ( evt.data.name == 'paste' && mode != 'html' )
{
editor.execCommand( 'pastetext' );
evt.cancel();
}
}, null, null, 0 );
editor.on( 'beforePaste', function( evt )
{
evt.data.mode = 'text';
});
}
editor.on( 'pasteState', function( evt )
{
editor.getCommand( 'pastetext' ).setState( evt.data );
});
},
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.
* <strong>Note:</strong> paste from word is not affected by this configuration.
* @name CKEDITOR.config.forcePasteAsPlainText
* @type Boolean
* @default false
* @example
* config.forcePasteAsPlainText = true;
*/

View File

@@ -0,0 +1,71 @@
/*
* Copyright (c) 2003-2011, 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 )
this._element = CKEDITOR.plugins.placeholder.getSelectedPlaceHoder( editor );
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-2011, 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.'
}
});

View File

@@ -0,0 +1,16 @@
/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.setLang( 'placeholder', 'he',
{
placeholder :
{
title : 'מאפייני שומר מקום',
toolbar : 'צור שומר מקום',
text : 'תוכן שומר המקום',
edit : 'ערוך שומר מקום',
textMissing : 'שומר המקום חייב להכיל טקסט.'
}
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 B

View File

@@ -0,0 +1,171 @@
/*
Copyright (c) 2003-2011, 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', 'he' ],
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 )
{
if ( CKEDITOR.plugins.placeholder.getSelectedPlaceHoder( editor ) )
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 );
return null;
},
getSelectedPlaceHoder : function( editor )
{
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();
return node;
}
};

View File

@@ -0,0 +1,64 @@
/*
Copyright (c) 2003-2011, 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,113 @@
/*
Copyright (c) 2003-2011, 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,
readOnly : 1,
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>)/, '$& &mdash; ' + 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 )
{
var doc = oWindow.document;
doc.open();
doc.write( sHTML );
doc.close();
// Chrome will need this to show the embedded. (#8016)
CKEDITOR.env.webkit && setTimeout( function() { doc.body.innerHTML += ''; }, 0 );
}
}
};
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,42 @@
/*
Copyright (c) 2003-2011, 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,
readOnly : 1,
modes : { wysiwyg : !( CKEDITOR.env.opera ) } // It is imposible to print the inner document in Opera.
};

View File

@@ -0,0 +1,185 @@
/*
Copyright (c) 2003-2011, 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 )
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.
startNode = bookmark.startNode,
endNode = bookmark.endNode,
currentNode;
// 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 );
if ( endNode )
{
breakParent( endNode );
// Navigate through all nodes between the bookmarks.
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.editor#removeFormatCleanup
* @event
* @param {Object} data.element The element that was cleaned up.
*/

View File

@@ -0,0 +1,168 @@
/*
Copyright (c) 2003-2011, 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;
// Resize in the same direction of chrome,
// which is identical to dir of editor element. (#6614)
var resizeDir = editor.element.getDirection( 1 );
!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 * ( resizeDir == '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';
var resizerHtml =
'<div' +
' class="cke_resizer' + direction + ' cke_resizer_' + resizeDir + '"' +
' title="' + CKEDITOR.tools.htmlEncode( editor.lang.resize ) + '"' +
' onmousedown="CKEDITOR.tools.callFunction(' + mouseDownFn + ', event)"' +
'></div>';
// Always sticks the corner of botttom space.
resizeDir == 'ltr' && direction == 'ltr' ?
event.data.html += resizerHtml :
event.data.html = resizerHtml + event.data.html;
}
}, editor, null, 100 );
}
}
} );
/**
* The minimum editor width, in pixels, when resizing the editor interface by using the resize handle.
* Note: It falls back to editor's actual width if it is 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 the editor interface by using the resize handle.
* Note: It falls back to editor's actual height if it is 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 the editor interface by using 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 the editor interface by using the resize handle.
* @name CKEDITOR.config.resize_maxHeight
* @type Number
* @default 3000
* @example
* config.resize_maxHeight = 600;
*/
/**
* Whether to enable the resizing feature. If this feature is disabled, the resize handle will not be visible.
* @name CKEDITOR.config.resize_enabled
* @type Boolean
* @default true
* @example
* config.resize_enabled = false;
*/
/**
* The dimensions for which the editor resizing is enabled. Possible values
* are <code>both</code>, <code>vertical</code>, and <code>horizontal</code>.
* @name CKEDITOR.config.resize_dir
* @type String
* @default 'both'
* @since 3.3
* @example
* config.resize_dir = 'vertical';
*/

View File

@@ -0,0 +1,381 @@
/*
Copyright (c) 2003-2011, 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 = 'richcombo';
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
};
function updateState()
{
var state = this.modes[ editor.mode ] ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED;
this.setState( editor.readOnly && !this.readOnly ? CKEDITOR.TRISTATE_DISABLED : state );
this.setValue( '' );
}
editor.on( 'mode', updateState, this );
// If this combo is sensitive to readOnly state, update it accordingly.
!this.readOnly && editor.on( 'readOnly', updateState, 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();
});
var focusFn = CKEDITOR.tools.addFunction( function() { instance.onfocus && instance.onfocus(); } );
// For clean up
instance.keyDownFn = keyDownFn;
output.push(
'<span class="cke_rcombo" role="presentation">',
'<span id=', id );
if ( this.className )
output.push( ' class="', this.className, ' cke_off"');
output.push(
' role="presentation">',
'<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 );"' +
' onfocus="return CKEDITOR.tools.callFunction(', focusFn, ', event);" ' +
( CKEDITOR.env.ie ? 'onclick="return false;" onmouseup' : 'onclick' ) + // #188
'="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><span class=cke_icon>' + ( CKEDITOR.env.hc ? '&#9660;' : CKEDITOR.env.air ? '&nbsp;' : '' ) + '</span></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();
};
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( false );
};
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 ( textElement )
{
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,56 @@
/*
Copyright (c) 2003-2011, 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 },
readOnly : 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
});
}
});
})();

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