diff options
Diffstat (limited to '_source/plugins')
254 files changed, 44185 insertions, 30038 deletions
diff --git a/_source/plugins/a11yhelp/dialogs/a11yhelp.js b/_source/plugins/a11yhelp/dialogs/a11yhelp.js index 3348f93..15bee48 100644 --- a/_source/plugins/a11yhelp/dialogs/a11yhelp.js +++ b/_source/plugins/a11yhelp/dialogs/a11yhelp.js @@ -1,12 +1,12 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, 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.getNextNumber();
+ id = CKEDITOR.tools.getNextId();
// CharCode <-> KeyChar.
var keyMap =
@@ -125,8 +125,8 @@ CKEDITOR.dialog.add( 'a11yHelp', function( editor ) // Create the help list directly from lang file entries.
function buildHelpContents()
{
- var pageTpl = '<div class="cke_accessibility_legend" role="document" aria-labelledby="cke_' + id + '_arialbl" tabIndex="-1">%1</div>' +
- '<span id="cke_' + id + '_arialbl" class="cke_voice_label">' + lang.contents + ' </span>',
+ 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>';
@@ -170,6 +170,7 @@ CKEDITOR.dialog.add( 'a11yHelp', function( editor ) {
type : 'html',
id : 'legends',
+ style : 'white-space:normal;',
focus : function() {},
html : buildHelpContents() +
'<style type="text/css">' +
@@ -181,6 +182,17 @@ CKEDITOR.dialog.add( 'a11yHelp', function( editor ) '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;' +
@@ -198,7 +210,6 @@ CKEDITOR.dialog.add( 'a11yHelp', function( editor ) '}' +
'.cke_accessibility_legend dd' +
'{' +
- 'white-space:normal;' +
'margin:10px' +
'}' +
'</style>'
diff --git a/_source/plugins/a11yhelp/lang/_translationstatus.txt b/_source/plugins/a11yhelp/lang/_translationstatus.txt new file mode 100644 index 0000000..c04d7fe --- /dev/null +++ b/_source/plugins/a11yhelp/lang/_translationstatus.txt @@ -0,0 +1,23 @@ +Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+
+cs.js Found: 30 Missing: 0
+cy.js Found: 30 Missing: 0
+da.js Found: 11 Missing: 19
+de.js Found: 30 Missing: 0
+el.js Found: 23 Missing: 7
+eo.js Found: 30 Missing: 0
+fa.js Found: 30 Missing: 0
+fi.js Found: 30 Missing: 0
+fr.js Found: 30 Missing: 0
+gu.js Found: 12 Missing: 18
+he.js Found: 30 Missing: 0
+it.js Found: 30 Missing: 0
+mk.js Found: 5 Missing: 25
+nb.js Found: 30 Missing: 0
+nl.js Found: 30 Missing: 0
+no.js Found: 30 Missing: 0
+tr.js Found: 30 Missing: 0
+ug.js Found: 27 Missing: 3
+vi.js Found: 6 Missing: 24
+zh-cn.js Found: 30 Missing: 0
diff --git a/_source/plugins/a11yhelp/lang/cs.js b/_source/plugins/a11yhelp/lang/cs.js new file mode 100644 index 0000000..711a675 --- /dev/null +++ b/_source/plugins/a11yhelp/lang/cs.js @@ -0,0 +1,89 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'a11yhelp', 'cs',
+{
+ accessibilityHelp :
+ {
+ title : 'Instrukce pro přístupnost',
+ contents : 'Obsah nápovědy. Pro uzavření tohoto dialogu stiskněte klávesu ESC.',
+ legend :
+ [
+ {
+ name : 'Obecné',
+ items :
+ [
+ {
+ name : 'Panel nástrojů editoru',
+ legend:
+ 'Stiskněte${toolbarFocus} k procházení panelu nástrojů. Přejděte na další a předchozí skupiny pomocí TAB a SHIFT-TAB. Přechod na další a předchozí tlačítko panelu nástrojů je pomocí ŠIPKA VPRAVO nebo ŠIPKA VLEVO. Stisknutím mezerníku nebo klávesy ENTER tlačítko aktivujete.'
+ },
+
+ {
+ name : 'Dialogové okno editoru',
+ legend :
+ 'Uvnitř dialogového okna stiskněte TAB pro přesunutí na další pole, stiskněte SHIFT + TAB pro přesun na předchozí pole, stiskněte ENTER pro odeslání dialogu, stiskněte ESC pro jeho zrušení. Pro dialogová okna, která mají mnoho karet stiskněte ALT + F10 pr oprocházení seznamu karet. Pak se přesuňte na další kartu pomocí TAB nebo ŠIPKA VPRAVO. Pro přesun na předchozí stiskněte SHIFT + TAB nebo ŠIPKA VLEVO. Stiskněte MEZERNÍK nebo ENTER pro vybrání stránky karet.'
+ },
+
+ {
+ name : 'Kontextové menu editoru',
+ legend :
+ 'Stiskněte ${contextMenu} nebo klávesu APPLICATION k otevření kontextového menu. Pak se přesuňte na další možnost menu pomocí TAB nebo ŠIPKY DOLŮ. Přesuňte se na předchozí možnost pomocí SHIFT+TAB nebo ŠIPKY NAHORU. Stiskněte MEZERNÍK nebo ENTER pro zvolení možnosti menu. Podmenu současné možnosti otevřete pomocí MEZERNÍKU nebo ENTER či ŠIPKY DOLEVA. Kontextové menu uzavřete stiskem ESC.'
+ },
+
+ {
+ name : 'Rámeček seznamu editoru',
+ legend :
+ 'Uvnitř rámečku seznamu se přesunete na další položku menu pomocí TAB nebo ŠIPKA DOLŮ. Na předchozí položku se přesunete SHIFT + TAB nebo ŠIPKA NAHORU. Stiskněte MEZERNÍK nebo ENTER pro zvolení možnosti seznamu. Stiskněte ESC pro uzavření seznamu.'
+ },
+
+ {
+ name : 'Lišta cesty prvku v editoru',
+ legend :
+ 'Stiskněte ${elementsPathFocus} pro procházení lišty cesty prvku. Na další tlačítko prvku se přesunete pomocí TAB nebo ŠIPKA VPRAVO. Na předchozí položku se přesunete pomocí SHIFT + TAB nebo ŠIPKA VLEVO. Stiskněte MEZERNÍK nebo ENTER pro vybrání prvku v editoru.'
+ }
+ ]
+ },
+ {
+ name : 'Příkazy',
+ items :
+ [
+ {
+ name : ' Příkaz Zpět',
+ legend : 'Stiskněte ${undo}'
+ },
+ {
+ name : ' Příkaz Znovu',
+ legend : 'Stiskněte ${redo}'
+ },
+ {
+ name : ' Příkaz Tučné',
+ legend : 'Stiskněte ${bold}'
+ },
+ {
+ name : ' Příkaz Kurzíva',
+ legend : 'Stiskněte ${italic}'
+ },
+ {
+ name : ' Příkaz Podtržení',
+ legend : 'Stiskněte ${underline}'
+ },
+ {
+ name : ' Příkaz Odkaz',
+ legend : 'Stiskněte ${link}'
+ },
+ {
+ name : ' Příkaz Skrýt panel nástrojů',
+ legend : 'Stiskněte ${toolbarCollapse}'
+ },
+ {
+ name : ' Nápověda přístupnosti',
+ legend : 'Stiskněte ${a11yHelp}'
+ }
+ ]
+ }
+ ]
+ }
+});
diff --git a/_source/plugins/a11yhelp/lang/cy.js b/_source/plugins/a11yhelp/lang/cy.js new file mode 100644 index 0000000..e9d6362 --- /dev/null +++ b/_source/plugins/a11yhelp/lang/cy.js @@ -0,0 +1,89 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'a11yhelp', 'cy',
+{
+ accessibilityHelp :
+ {
+ title : 'Canllawiau Hygyrchedd',
+ contents : 'Cynnwys Cymorth. I gau y deialog hwn, pwyswch ESC.',
+ legend :
+ [
+ {
+ name : 'Cyffredinol',
+ items :
+ [
+ {
+ name : 'Bar Offer y Golygydd',
+ legend:
+ 'Pwyswch $ {toolbarFocus} i fynd at y bar offer. Symudwch i\'r grŵp bar offer nesaf a blaenorol gyda TAB a SHIFT-TAB. Symudwch i\'r botwm bar offer nesaf a blaenorol gyda SAETH DDE neu SAETH CHWITH. Pwyswch SPACE neu ENTER i wneud botwm y bar offer yn weithredol.'
+ },
+
+ {
+ name : 'Deialog y Golygydd',
+ legend :
+ 'Tu mewn i\'r deialog, pwyswch TAB i fynd i\'r maes nesaf ar y deialog, pwyswch SHIFT + TAB i symud i faes blaenorol, pwyswch ENTER i gyflwyno\'r deialog, pwyswch ESC i ddiddymu\'r deialog. Ar gyfer deialogau sydd â thudalennau aml-tab, pwyswch ALT + F10 i lywio\'r tab-restr. Yna symudwch i\'r tab nesaf gyda TAB neu SAETH DDE. Symudwch i dab blaenorol gyda SHIFT + TAB neu\'r SAETH CHWITH. Pwyswch SPACE neu ENTER i ddewis y dudalen tab.'
+ },
+
+ {
+ name : 'Dewislen Cyd-destun y Golygydd',
+ legend :
+ 'Pwyswch $ {contextMenu} neu\'r ALLWEDD \'APPLICATION\' i agor y ddewislen cyd-destun. Yna symudwch i\'r opsiwn ddewislen nesaf gyda\'r TAB neu\'r SAETH I LAWR. Symudwch i\'r opsiwn blaenorol gyda SHIFT + TAB neu\'r SAETH I FYNY. Pwyswch SPACE neu ENTER i ddewis yr opsiwn ddewislen. Agorwch is-dewislen yr opsiwn cyfredol gyda SPACE neu ENTER neu SAETH DDE. Ewch yn ôl i\'r eitem ar y ddewislen uwch gydag ESC neu SAETH CHWITH. Ceuwch y ddewislen cyd-destun gydag ESC.'
+ },
+
+ {
+ name : 'Blwch Rhestr y Golygydd',
+ legend :
+ 'Tu mewn rhestr-bocs, ewch i\'r eitem rhestr nesaf gyda TAB neu\'r SAETH I LAWR. Symudwch i restr eitem flaenorol gyda SHIFT + TAB neu SAETH I FYNY. Pwyswch SPACE neu ENTER i ddewis yr opsiwn o\'r rhestr. Pwyswch ESC i gau\'r rhestr.'
+ },
+
+ {
+ name : 'Bar Llwybr Elfen y Golygydd',
+ legend :
+ 'Pwyswch $ {elementsPathFocus} i fynd i\'r elfennau llwybr bar. Symudwch i fotwm yr elfen nesaf gyda TAB neu SAETH DDE. Symudwch i fotwm blaenorol gyda SHIFT + TAB neu SAETH CHWITH. Pwyswch SPACE neu ENTER i ddewis yr elfen yn y golygydd.'
+ }
+ ]
+ },
+ {
+ name : 'Gorchmynion',
+ items :
+ [
+ {
+ name : 'Gorchymyn dadwneud',
+ legend : 'Pwyswch ${undo}'
+ },
+ {
+ name : 'Gorchymyn ailadrodd',
+ legend : 'Pwyswch ${redo}'
+ },
+ {
+ name : 'Gorchymyn Bras',
+ legend : 'Pwyswch ${bold}'
+ },
+ {
+ name : 'Gorchymyn italig',
+ legend : 'Pwyswch ${italig}'
+ },
+ {
+ name : 'Gorchymyn tanlinellu',
+ legend : 'Pwyso ${underline}'
+ },
+ {
+ name : 'Gorchymyn dolen',
+ legend : 'Pwyswch ${link}'
+ },
+ {
+ name : 'Gorchymyn Cwympo\'r Dewislen',
+ legend : 'Pwyswch ${toolbarCollapse}'
+ },
+ {
+ name : 'Cymorth Hygyrchedd',
+ legend : 'Pwyswch ${a11yHelp}'
+ }
+ ]
+ }
+ ]
+ }
+});
diff --git a/_source/plugins/a11yhelp/lang/da.js b/_source/plugins/a11yhelp/lang/da.js new file mode 100644 index 0000000..965824e --- /dev/null +++ b/_source/plugins/a11yhelp/lang/da.js @@ -0,0 +1,89 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'a11yhelp', 'da',
+{
+ accessibilityHelp :
+ {
+ title : 'Accessibility Instructions', // MISSING
+ contents : 'Help Contents. To close this dialog press ESC.', // MISSING
+ legend :
+ [
+ {
+ name : 'Generelt',
+ items :
+ [
+ {
+ name : 'Editor værktøjslinje',
+ 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.' // MISSING
+ },
+
+ {
+ name : 'Editor Dialog', // MISSING
+ 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.' // MISSING
+ },
+
+ {
+ name : 'Editor Context Menu', // MISSING
+ 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 with SPACE or ENTER or RIGHT ARROW. Go back to parent menu item with ESC or LEFT ARROW. Close context menu with ESC.' // MISSING
+ },
+
+ {
+ name : 'Editor List Box', // MISSING
+ 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.' // MISSING
+ },
+
+ {
+ name : 'Editor Element Path Bar', // MISSING
+ 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.' // MISSING
+ }
+ ]
+ },
+ {
+ name : 'Kommandoer',
+ items :
+ [
+ {
+ name : 'Fortryd kommando',
+ legend : 'Klik på ${undo}'
+ },
+ {
+ name : 'Gentag kommando',
+ legend : 'Klik ${redo}'
+ },
+ {
+ name : ' Bold command', // MISSING
+ legend : 'Klik ${bold}'
+ },
+ {
+ name : ' Italic command', // MISSING
+ legend : 'Press ${italic}' // MISSING
+ },
+ {
+ name : ' Underline command', // MISSING
+ legend : 'Klik ${underline}'
+ },
+ {
+ name : ' Link command', // MISSING
+ legend : 'Klik ${link}'
+ },
+ {
+ name : ' Toolbar Collapse command', // MISSING
+ legend : 'Press ${toolbarCollapse}' // MISSING
+ },
+ {
+ name : ' Accessibility Help', // MISSING
+ legend : 'Kilk ${a11yHelp}'
+ }
+ ]
+ }
+ ]
+ }
+});
diff --git a/_source/plugins/a11yhelp/lang/de.js b/_source/plugins/a11yhelp/lang/de.js new file mode 100644 index 0000000..2feea16 --- /dev/null +++ b/_source/plugins/a11yhelp/lang/de.js @@ -0,0 +1,89 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'a11yhelp', 'de',
+{
+ accessibilityHelp :
+ {
+ title : 'Barrierefreiheitinformationen',
+ contents : 'Hilfeinhalt. Um den Dialog zu schliessen die Taste \'ESC\' drücken.',
+ legend :
+ [
+ {
+ name : 'Allgemein',
+ items :
+ [
+ {
+ name : 'Editor Symbolleiste',
+ legend:
+ 'Drücken Sie ${toolbarFocus} auf der Symbolleiste. Gehen Sie zur nächsten oder vorherigen Symbolleistengruppe mit TAB und SHIFT-TAB. Gehen Sie zur nächsten oder vorherigen Symbolleiste auf die Schaltfläche mit dem RECHTS- oder LINKS-Pfeil. Drücken Sie die Leertaste oder Eingabetaste, um die Schaltfläche in der Symbolleiste aktivieren.'
+ },
+
+ {
+ name : 'Editor Dialog',
+ legend :
+ 'Innerhalb des Dialogs drücken Sie TAB um zum nächsten Dialogfeld zu gelangen, drücken Sie SHIFT-TAG um zum vorherigen Feld zu wechseln, drücken Sie ENTER um den Dialog abzusenden und ESC um den Dialog zu abzubrechen. Um zwischen den Reitern innerhalb eines Dialogs zu wechseln drücken sie ALT-F10. Um zum nächsten Reiter zu gelangen können Sie TAB oder die rechte Pfeiltaste. Zurück gelangt man mit SHIFT-TAB oder der linken Pfeiltaste. Mit der Leertaste oder Enter kann man den Reiter auswählen.'
+ },
+
+ {
+ name : 'Editor Kontextmenü',
+ legend :
+ 'Dürcken Sie ${contextMenu} oder die Anwendungstaste um das Kontextmenü zu öffnen. Man kann die Pfeiltasten zum Wechsel benutzen. Mit der Leertaste oder der Enter-Taste kann man den Menüpunkt aufrufen. Schliessen Sie das Kontextmenü mit der ESC-Taste.'
+ },
+
+ {
+ name : 'Editor Listen',
+ legend :
+ 'Innerhalb einer Listenbox kann man mit der TAB-Taste oder den Pfeilrunter-Taste den nächsten Menüeintrag wählen. Mit der Shift-TAB Tastenkombination oder der Pfeilhoch-Taste gelangt man zum vorherigen Menüpunkt. Mit der Leertaste oder Enter kann man den Menüpunkt auswählen. Drücken Sie ESC zum Verlassen des Menüs.'
+ },
+
+ {
+ name : 'Editor Elementpfadleiste',
+ legend :
+ 'Drücken Sie ${elementsPathFocus} um sich durch die Pfadleiste zu bewegen. Um zum nächsten Element zu gelangen drücken Sie TAB oder die Pfeilrechts-Taste. Zum vorherigen Element gelangen Sie mit der SHIFT-TAB oder der Pfeillinks-Taste. Drücken Sie die Leertaste oder Enter um das Element auszuwählen.'
+ }
+ ]
+ },
+ {
+ name : 'Befehle',
+ items :
+ [
+ {
+ name : 'Wiederholen Befehl',
+ legend : 'Drücken Sie ${undo}'
+ },
+ {
+ name : 'Rückgängig Befehl',
+ legend : 'Drücken Sie ${redo}'
+ },
+ {
+ name : 'Fettschrift Befehl',
+ legend : 'Drücken Sie ${bold}'
+ },
+ {
+ name : 'Italic Befehl',
+ legend : 'Drücken Sie ${italic}'
+ },
+ {
+ name : 'Unterstreichung Befehl',
+ legend : 'Drücken Sie ${underline}'
+ },
+ {
+ name : 'Link Befehl',
+ legend : 'Drücken Sie ${link}'
+ },
+ {
+ name : 'Symbolleiste zuammenklappen Befehl',
+ legend : 'Drücken Sie ${toolbarCollapse}'
+ },
+ {
+ name : 'Eingabehilfen',
+ legend : 'Drücken Sie ${a11yHelp}'
+ }
+ ]
+ }
+ ]
+ }
+});
diff --git a/_source/plugins/a11yhelp/lang/el.js b/_source/plugins/a11yhelp/lang/el.js new file mode 100644 index 0000000..1eecb22 --- /dev/null +++ b/_source/plugins/a11yhelp/lang/el.js @@ -0,0 +1,89 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'a11yhelp', 'el',
+{
+ accessibilityHelp :
+ {
+ title : 'Οδηγίες Προσβασιμότητας',
+ contents : 'Περιεχόμενα Βοήθειας. Πατήστε ESC για κλείσιμο.',
+ legend :
+ [
+ {
+ name : 'Γενικά',
+ items :
+ [
+ {
+ name : 'Εργαλειοθήκη Επεξεργαστή',
+ legend:
+ 'Πατήστε ${toolbarFocus} για να περιηγηθείτε στην γραμμή εργαλείων. Μετακινηθείτε ανάμεσα στις ομάδες της γραμμής εργαλείων με TAB και Shift-TAB. Μετακινηθείτε ανάμεσα στα κουμπία εργαλείων με ΔΕΞΙ και ΑΡΙΣΤΕΡΟ ΒΕΛΑΚΙ. Πατήστε ΚΕΝΟ ή ENTER για να ενεργοποιήσετε το ενεργό κουμπί εργαλείου.'
+ },
+
+ {
+ name : 'Παράθυρο Διαλόγου Επεξεργαστή',
+ 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.' // MISSING
+ },
+
+ {
+ name : 'Editor Context Menu', // MISSING
+ 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 with SPACE or ENTER or RIGHT ARROW. Go back to parent menu item with ESC or LEFT ARROW. Close context menu with ESC.' // MISSING
+ },
+
+ {
+ name : 'Editor List Box', // MISSING
+ 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.' // MISSING
+ },
+
+ {
+ name : 'Editor Element Path Bar', // MISSING
+ 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.' // MISSING
+ }
+ ]
+ },
+ {
+ 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}'
+ }
+ ]
+ }
+ ]
+ }
+});
diff --git a/_source/plugins/a11yhelp/lang/en.js b/_source/plugins/a11yhelp/lang/en.js index b01885a..7a78608 100644 --- a/_source/plugins/a11yhelp/lang/en.js +++ b/_source/plugins/a11yhelp/lang/en.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
@@ -19,8 +19,8 @@ CKEDITOR.plugins.setLang( 'a11yhelp', 'en', name : 'Editor Toolbar',
legend:
'Press ${toolbarFocus} to navigate to the toolbar. ' +
- 'Move to next toolbar button with TAB or RIGHT ARROW. ' +
- 'Move to previous button with SHIFT+TAB or LEFT ARROW. ' +
+ '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.'
},
@@ -39,9 +39,9 @@ CKEDITOR.plugins.setLang( 'a11yhelp', 'en', 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. ' +
+ '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. ' +
+ 'Open sub-menu of current option with SPACE or ENTER or RIGHT ARROW. ' +
'Go back to parent menu item with ESC or LEFT ARROW. ' +
'Close context menu with ESC.'
},
diff --git a/_source/plugins/a11yhelp/lang/eo.js b/_source/plugins/a11yhelp/lang/eo.js new file mode 100644 index 0000000..58af07e --- /dev/null +++ b/_source/plugins/a11yhelp/lang/eo.js @@ -0,0 +1,89 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'a11yhelp', 'eo',
+{
+ accessibilityHelp :
+ {
+ title : 'Uzindikoj pri atingeblo',
+ contents : 'Helpilenhavo. Por fermi tiun dialogon, premu la ESKAPAN klavon.',
+ legend :
+ [
+ {
+ name : 'Ĝeneralaĵoj',
+ items :
+ [
+ {
+ name : 'Ilbreto de la redaktilo',
+ legend:
+ 'Premu ${toolbarFocus} por atingi la ilbreton. Moviĝu al la sekva aŭ antaŭa grupoj de la ilbreto per la klavoj TABA kaj MAJUSKLIGA-TABA. Moviĝu al la sekva aŭ antaŭa butonoj de la ilbreto per la klavoj SAGO DEKSTREN kaj SAGO MALDEKSTREN. Premu la SPACETklavon aŭ la ENENklavon por aktivigi la ilbretbutonon.'
+ },
+
+ {
+ name : 'Redaktildialogo',
+ legend :
+ 'En dialogo, premu la TABAN klavon por navigi al la sekva dialogkampo, premu la MAJUSKLIGAN + TABAN klavojn por reveni al la antaŭa kampo, premu la ENENklavon por sendi la dialogon, premu la ESKAPAN klavon por nuligi la dialogon. Por dialogoj kun pluraj retpaĝoj sub langetoj, premu ALT + F10 por navigi al la langetlisto. Poste moviĝu al la sekva langeto per la klavo TABA aŭ SAGO DEKSTREN. Moviĝu al la antaŭa langeto per la klavoj MAJUSKLIGA + TABA aŭ SAGO MALDEKSTREN. Premu la SPACETklavon aŭ la ENENklavon por selekti la langetretpaĝon.'
+ },
+
+ {
+ name : 'Kunteksta menuo de la redaktilo',
+ legend :
+ 'Premu ${contextMenu} aŭ entajpu la KLAVKOMBINAĴON por malfermi la kuntekstan menuon. Poste moviĝu al la sekva opcio de la menuo per la klavoj TABA aŭ SAGO SUBEN. Moviĝu al la antaŭa opcio per la klavoj MAJUSKLGA + TABA aŭ SAGO SUPREN. Premu la SPACETklavon aŭ ENENklavon por selekti la menuopcion. Malfermu la submenuon de la kuranta opcio per la SPACETklavo aŭ la ENENklavo aŭ la SAGO DEKSTREN. Revenu al la elemento de la patra menuo per la klavoj ESKAPA aŭ SAGO MALDEKSTREN. Fermu la kuntekstan menuon per la ESKAPA klavo.'
+ },
+
+ {
+ name : 'Fallisto de la redaktilo',
+ legend :
+ 'En fallisto, moviĝu al la sekva listelemento per la klavoj TABA aŭ SAGO SUBEN. Moviĝu al la antaŭa listelemento per la klavoj MAJUSKLIGA + TABA aŭ SAGO SUPREN. Premu la SPACETklavon aŭ ENENklavon por selekti la opcion en la listo. Premu la ESKAPAN klavon por fermi la falmenuon.'
+ },
+
+ {
+ name : 'Breto indikanta la vojon al la redaktilelementoj',
+ legend :
+ 'Premu ${elementsPathFocus} por navigi al la breto indikanta la vojon al la redaktilelementoj. Moviĝu al la butono de la sekva elemento per la klavoj TABA aŭ SAGO DEKSTREN. Moviĝu al la butono de la antaŭa elemento per la klavoj MAJUSKLIGA + TABA aŭ SAGO MALDEKSTREN. Premu la SPACETklavon aŭ ENENklavon por selekti la elementon en la redaktilo.'
+ }
+ ]
+ },
+ {
+ name : 'Komandoj',
+ items :
+ [
+ {
+ name : 'Komando malfari',
+ legend : 'Premu ${undo}'
+ },
+ {
+ name : 'Komando refari',
+ legend : 'Premu ${redo}'
+ },
+ {
+ name : 'Komando grasa',
+ legend : 'Premu ${bold}'
+ },
+ {
+ name : 'Komando kursiva',
+ legend : 'Premu ${italic}'
+ },
+ {
+ name : 'Komando substreki',
+ legend : 'Premu ${underline}'
+ },
+ {
+ name : 'Komando ligilo',
+ legend : 'Premu ${link}'
+ },
+ {
+ name : 'Komando faldi la ilbreton',
+ legend : 'Premu ${toolbarCollapse}'
+ },
+ {
+ name : 'Helpilo pri atingeblo',
+ legend : 'Premu ${a11yHelp}'
+ }
+ ]
+ }
+ ]
+ }
+});
diff --git a/_source/plugins/a11yhelp/lang/fa.js b/_source/plugins/a11yhelp/lang/fa.js new file mode 100644 index 0000000..bbec6bf --- /dev/null +++ b/_source/plugins/a11yhelp/lang/fa.js @@ -0,0 +1,89 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'a11yhelp', 'fa',
+{
+ accessibilityHelp :
+ {
+ title : 'دستورالعملهای دسترسی',
+ contents : 'راهنمای فهرست مطالب. برای بستن این کادر محاورهای ESC را فشار دهید.',
+ legend :
+ [
+ {
+ name : 'عمومی',
+ items :
+ [
+ {
+ name : 'نوار ابزار ویرایشگر',
+ legend:
+ '${toolbarFocus} را برای باز کردن نوار ابزار بفشارید. با کلید Tab و Shif-Tab در مجموعه نوار ابزار بعدی و قبلی حرکت کنید. برای حرکت در کلید نوار ابزار قبلی و بعدی با کلید جهتنمای راست و چپ جابجا شوید. کلید Space یا Enter را برای فعال کردن کلید نوار ابزار بفشارید.'
+ },
+
+ {
+ name : 'پنجره محاورهای ویرایشگر',
+ legend :
+ 'در داخل یک پنجره محاورهای، کلید Tab را بفشارید تا به پنجرهی بعدی بروید، Shift+Tab برای حرکت به فیلد قبلی، فشردن Enter برای ثبت اطلاعات پنجره، فشردن Esc برای لغو پنجره محاورهای و برای پنجرههایی که چندین برگه دارند، فشردن Alt+F10 جهت رفتن به Tab-List. در نهایت حرکت به برگه بعدی با Tab یا کلید جهتنمای راست. حرکت به برگه قبلی با Shift+Tab یا کلید جهتنمای چپ. فشردن Space یا Enter برای انتخاب یک برگه.'
+ },
+
+ {
+ name : 'منوی متنی ویرایشگر',
+ legend :
+ '${contextMenu} یا کلید برنامههای کاربردی را برای باز کردن منوی متن را بفشارید. سپس میتوانید برای حرکت به گزینه بعدی منو با کلید Tab و یا کلید جهتنمای پایین جابجا شوید. حرکت به گزینه قبلی با Shift+Tab یا کلید جهتنمای بالا. فشردن Space یا Enter برای انتخاب یک گزینه از منو. باز کردن زیر شاخه گزینه منو جاری با کلید Space یا Enter و یا کلید جهتنمای راست و چپ. بازگشت به منوی والد با کلید Esc یا کلید جهتنمای چپ. بستن منوی متن با Esc.'
+ },
+
+ {
+ name : 'جعبه فهرست ویرایشگر',
+ legend :
+ 'در داخل جعبه لیست، قلم دوم از اقلام لیست بعدی را با TAB و یا Arrow Down حرکت دهید. انتقال به قلم دوم از اقلام لیست قبلی را با SHIFT + TAB یا UP ARROW. کلید Space یا ENTER را برای انتخاب گزینه لیست بفشارید. کلید ESC را برای بستن جعبه لیست بفشارید.'
+ },
+
+ {
+ name : 'ویرایشگر عنصر نوار راه',
+ legend :
+ 'برای رفتن به مسیر عناصر ${elementsPathFocus} را بفشارید. حرکت به کلید عنصر بعدی با کلید Tab یا کلید جهتنمای راست. برگشت به کلید قبلی با Shift+Tab یا کلید جهتنمای چپ. فشردن Space یا 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}'
+ }
+ ]
+ }
+ ]
+ }
+});
diff --git a/_source/plugins/a11yhelp/lang/fi.js b/_source/plugins/a11yhelp/lang/fi.js new file mode 100644 index 0000000..718b046 --- /dev/null +++ b/_source/plugins/a11yhelp/lang/fi.js @@ -0,0 +1,89 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'a11yhelp', 'fi',
+{
+ accessibilityHelp :
+ {
+ title : 'Saavutettavuus ohjeet',
+ contents : 'Ohjeen sisällöt. Sulkeaksesi tämän dialogin paina ESC.',
+ legend :
+ [
+ {
+ name : 'Yleinen',
+ items :
+ [
+ {
+ name : 'Editorin työkalupalkki',
+ legend:
+ 'Paina ${toolbarFocus} siirtyäksesi työkalupalkkiin. Siirry seuraavaan ja edelliseen työkalupalkin ryhmään TAB ja SHIFT-TAB näppäimillä. Siirry seuraavaan ja edelliseen työkalupainikkeeseen käyttämällä NUOLI OIKEALLE tai NUOLI VASEMMALLE näppäimillä. Paina VÄLILYÖNTI tai ENTER näppäintä aktivoidaksesi työkalupainikkeen.'
+ },
+
+ {
+ name : 'Editorin dialogi',
+ legend :
+ 'Dialogin sisällä, painamalla TAB siirryt seuraavaan dialogin kenttään, painamalla SHIFT+TAB siirryt aiempaan kenttään, painamalla ENTER lähetät dialogin, painamalla ESC peruutat dialogin. Dialogeille joissa on useita välilehtiä, paina ALT+F10 siirtyäksesi välillehtilistaan. Siirtyäksesi seuraavaan välilehteen paina TAB tai NUOLI OIKEALLE. Siirry edelliseen välilehteen painamalla SHIFT+TAB tai nuoli vasemmalle. Paina VÄLILYÖNTI tai ENTER valitaksesi välilehden.'
+ },
+
+ {
+ name : 'Editorin oheisvalikko',
+ legend :
+ 'Paina ${contextMenu} tai SOVELLUSPAINIKETTA avataksesi oheisvalikon. Liiku seuraavaan valikon vaihtoehtoon TAB tai NUOLI ALAS näppäimillä. Siirry edelliseen vaihtoehtoon SHIFT+TAB tai NUOLI YLÖS näppäimillä. Paina VÄLILYÖNTI tai ENTER valitaksesi valikon kohdan. Avataksesi nykyisen kohdan alivalikon paina VÄLILYÖNTI tai ENTER tai NUOLI OIKEALLE painiketta. Siirtyäksesi takaisin valikon ylemmälle tasolle paina ESC tai NUOLI vasemmalle. Oheisvalikko suljetaan ESC painikkeella.'
+ },
+
+ {
+ name : 'Editorin listalaatikko',
+ legend :
+ 'Listalaatikon sisällä siirry seuraavaan listan kohtaan TAB tai NUOLI ALAS painikkeilla. Siirry edelliseen listan kohtaan SHIFT+TAB tai NUOLI YLÖS painikkeilla. Paina VÄLILYÖNTI tai ENTER valitaksesi listan vaihtoehdon. Paina ESC sulkeaksesi listalaatikon.'
+ },
+
+ {
+ name : 'Editorin elementtipolun palkki',
+ legend :
+ 'Paina ${elementsPathFocus} siirtyäksesi elementtipolun palkkiin. Siirry seuraavaan elementtipainikkeeseen TAB tai NUOLI OIKEALLE painikkeilla. Siirry aiempaan painikkeeseen SHIFT+TAB tai NUOLI VASEMMALLE painikkeilla. Paina VÄLILYÖNTI tai ENTER valitaksesi elementin editorissa.'
+ }
+ ]
+ },
+ {
+ name : 'Komennot',
+ items :
+ [
+ {
+ name : 'Peruuta komento',
+ legend : 'Paina ${undo}'
+ },
+ {
+ name : 'Tee uudelleen komento',
+ legend : 'Paina ${redo}'
+ },
+ {
+ name : 'Lihavoi komento',
+ legend : 'Paina ${bold}'
+ },
+ {
+ name : 'Kursivoi komento',
+ legend : 'Paina ${italic}'
+ },
+ {
+ name : 'Alleviivaa komento',
+ legend : 'Paina ${underline}'
+ },
+ {
+ name : 'Linkki komento',
+ legend : 'Paina ${link}'
+ },
+ {
+ name : 'Pienennä työkalupalkki komento',
+ legend : 'Paina ${toolbarCollapse}'
+ },
+ {
+ name : 'Saavutettavuus ohjeet',
+ legend : 'Paina ${a11yHelp}'
+ }
+ ]
+ }
+ ]
+ }
+});
diff --git a/_source/plugins/a11yhelp/lang/fr.js b/_source/plugins/a11yhelp/lang/fr.js new file mode 100644 index 0000000..0525141 --- /dev/null +++ b/_source/plugins/a11yhelp/lang/fr.js @@ -0,0 +1,89 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'a11yhelp', 'fr',
+{
+ accessibilityHelp :
+ {
+ title : 'Instructions pour l\'accessibilité',
+ contents : 'Contenu de l\'aide. Pour fermer ce dialogue, appuyez sur la touche ESC (Echappement).',
+ legend :
+ [
+ {
+ name : 'Général',
+ items :
+ [
+ {
+ name : 'Barre d\'outils de l\'éditeur',
+ legend:
+ 'Appuyer sur ${toolbarFocus} pour accéder à la barre d\'outils. Se déplacer vers les groupes suivant ou précédent de la barre d\'outil avec les touches TAB et SHIFT-TAB. Se déplacer vers les boutons suivant ou précédent de la barre d\'outils avec les touches FLECHE DROITE et FLECHE GAUCHE. Appuyer sur la barre d\'espace ou la touche ENTRER pour activer le bouton de barre d\'outils.'
+ },
+
+ {
+ name : 'Dialogue de léditeur',
+ legend :
+ 'A l\'intérieur d\'un dialogue, appuyer sur la touche TAB pour naviguer jusqu\'au champ de dalogue suivant, appuyez sur les touches SHIFT + TAB pour revenir au champ précédent, appuyez sur la touche ENTRER pour soumettre le dialogue, appuyer sur la touche ESC pour annuler le dialogue. Pour les dialogues avec plusieurs pages d\'onglets, appuyer sur ALT + F10 pour naviguer jusqu\'à la liste des onglets. Puis se déplacer vers l\'onglet suivant avec la touche TAB ou FLECHE DROITE. Se déplacer vers l\'onglet précédent avec les touches SHIFT + TAB ou FLECHE GAUCHE. Appuyer sur la barre d\'espace ou la touche ENTRER pour sélectionner la page de l\'onglet.'
+ },
+
+ {
+ name : 'Menu contextuel de l\'éditeur',
+ legend :
+ 'Appuyer sur ${contextMenu} ou entrer le RACCOURCI CLAVIER pour ouvrir le menu contextuel. Puis se déplacer vers l\'option suivante du menu avec les touches TAB ou FLECHE BAS. Se déplacer vers l\'option précédente avec les touches SHIFT+TAB ou FLECHE HAUT. appuyer sur la BARRE D\'ESPACE ou la touche ENTREE pour sélectionner l\'option du menu. Oovrir le sous-menu de l\'option courante avec la BARRE D\'ESPACE ou les touches ENTREE ou FLECHE DROITE. Revenir à l\'élément de menu parent avec les touches ESC ou FLECHE GAUCHE. Fermer le menu contextuel avec ESC.'
+ },
+
+ {
+ name : 'Zone de liste en menu déroulant de l\'éditeur',
+ legend :
+ 'A l\'intérieur d\'une liste en menu déroulant, se déplacer vers l\'élément suivant de la liste avec les touches TAB ou FLECHE BAS. Se déplacer vers l\'élément précédent de la liste avec les touches SHIFT + TAB ou FLECHE HAUT. Appuyer sur la BARRE D\'ESPACE ou sur ENTREE pour sélectionner l\'option dans la liste. Appuyer sur ESC pour fermer le menu déroulant.'
+ },
+
+ {
+ name : 'Barre d\'emplacement des éléments de léditeur',
+ legend :
+ 'Appuyer sur ${elementsPathFocus} pour naviguer vers la barre d\'emplacement des éléments de léditeur. Se déplacer vers le bouton d\'élément suivant avec les touches TAB ou FLECHE DROITE. Se déplacer vers le bouton d\'élément précédent avec les touches SHIFT+TAB ou FLECHE GAUCHE. Appuyer sur la BARRE D\'ESPACE ou sur ENTREE pour sélectionner l\'élément dans l\'éditeur.'
+ }
+ ]
+ },
+ {
+ name : 'Commandes',
+ items :
+ [
+ {
+ name : ' Commande défaire',
+ legend : 'Appuyer sur ${undo}'
+ },
+ {
+ name : ' Commande refaire',
+ legend : 'Appuyer sur ${redo}'
+ },
+ {
+ name : ' Commande gras',
+ legend : 'Appuyer sur ${bold}'
+ },
+ {
+ name : ' Commande italique',
+ legend : 'Appuyer sur ${italic}'
+ },
+ {
+ name : ' Commande souligné',
+ legend : 'Appuyer sur ${underline}'
+ },
+ {
+ name : ' Commande lien',
+ legend : 'Appuyer sur ${link}'
+ },
+ {
+ name : ' Commande enrouler la barre d\'outils',
+ legend : 'Appuyer sur ${toolbarCollapse}'
+ },
+ {
+ name : ' Aide Accessibilité',
+ legend : 'Appuyer sur ${a11yHelp}'
+ }
+ ]
+ }
+ ]
+ }
+});
diff --git a/_source/plugins/a11yhelp/lang/gu.js b/_source/plugins/a11yhelp/lang/gu.js new file mode 100644 index 0000000..9fd170c --- /dev/null +++ b/_source/plugins/a11yhelp/lang/gu.js @@ -0,0 +1,89 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'a11yhelp', 'gu',
+{
+ accessibilityHelp :
+ {
+ title : 'એક્ક્ષેબિલિટી ની વિગતો',
+ contents : 'હેલ્પ. આ બંધ કરવા ESC દબાવો.',
+ legend :
+ [
+ {
+ name : 'જનરલ',
+ items :
+ [
+ {
+ name : 'એડિટર ટૂલબાર',
+ 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.' // MISSING
+ },
+
+ {
+ name : 'એડિટર ડાયલોગ',
+ 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.' // MISSING
+ },
+
+ {
+ name : 'Editor Context Menu', // MISSING
+ 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 with SPACE or ENTER or RIGHT ARROW. Go back to parent menu item with ESC or LEFT ARROW. Close context menu with ESC.' // MISSING
+ },
+
+ {
+ name : 'Editor List Box', // MISSING
+ 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.' // MISSING
+ },
+
+ {
+ name : 'Editor Element Path Bar', // MISSING
+ 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.' // MISSING
+ }
+ ]
+ },
+ {
+ name : 'કમાંડસ',
+ items :
+ [
+ {
+ name : 'અન્ડું કમાંડ',
+ legend : '$ દબાવો {undo}'
+ },
+ {
+ name : 'ફરી કરો કમાંડ',
+ legend : '$ દબાવો {redo}'
+ },
+ {
+ name : 'બોલ્દનો કમાંડ',
+ legend : '$ દબાવો {bold}'
+ },
+ {
+ name : ' Italic command', // MISSING
+ legend : 'Press ${italic}' // MISSING
+ },
+ {
+ name : ' Underline command', // MISSING
+ legend : 'Press ${underline}' // MISSING
+ },
+ {
+ name : ' Link command', // MISSING
+ legend : 'Press ${link}' // MISSING
+ },
+ {
+ name : ' Toolbar Collapse command', // MISSING
+ legend : 'Press ${toolbarCollapse}' // MISSING
+ },
+ {
+ name : ' Accessibility Help', // MISSING
+ legend : 'Press ${a11yHelp}' // MISSING
+ }
+ ]
+ }
+ ]
+ }
+});
diff --git a/_source/plugins/a11yhelp/lang/he.js b/_source/plugins/a11yhelp/lang/he.js index 1f77cf5..7161e4d 100644 --- a/_source/plugins/a11yhelp/lang/he.js +++ b/_source/plugins/a11yhelp/lang/he.js @@ -1,216 +1,89 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -CKEDITOR.plugins.setLang( 'a11yhelp', 'he', -{ - accessibilityHelp : - { - title : 'הוראות נגישות', - contents : 'הוראות נגישות. לסגירה לחץ אסקייפ (ESC).', - legend : - [ - { - name : 'כללי', - items : - [ - { - name : 'סרגל הכלים', - legend: - 'לחץ על ${toolbarFocus} כדי לנווט לסרגל הכלים. ' + - 'עבור לכפתור הבא עם מקש הטאב (TAB) או חץ שמאלי. ' + - 'עבור לכפתור הקודם עם מקש השיפט (SHIFT) + טאב (TAB) או חץ ימני. ' + - 'לחץ רווח או אנטר (ENTER) כדי להפעיל את הכפתור הנבחר.' - }, - - { - name : 'דיאלוגים (חלונות תשאול)', - legend : - 'בתוך דיאלוג, לחץ טאב (TAB) כדי לנווט לשדה הבא, לחץ שיפט (SHIFT) + טאב (TAB) כדי לנווט לשדה הקודם, לחץ אנטר (ENTER) כדי לשלוח את הדיאלוג, לחץ אסקייפ (ESC) כדי לבטל. ' + - 'בתוך דיאלוגים בעלי מספר טאבים (לשוניות), לחץ אלט (ALT) + F10 כדי לנווט לשורת הטאבים. ' + - 'נווט לטאב הבא עם טאב (TAB) או חץ שמאלי. ' + - 'עבור לטאב הקודם עם שיפט (SHIFT) + טאב (TAB) או חץ שמאלי. ' + - 'לחץ רווח או אנטר (ENTER) כדי להיכנס לטאב.' - }, - - { - name : 'תפריט ההקשר (Context Menu)', - legend : - 'לחץ ${contextMenu} או APPLICATION KEYכדי לפתוח את תפריט ההקשר. ' + - 'עבור לאפשרות הבאה עם טאב (TAB) או חץ למטה. ' + - 'עבור לאפשרות הקודמת עם שיפט (SHIFT) + טאב (TAB) או חץ למעלה. ' + - 'לחץ רווח או אנטר (ENTER) כדי לבחור את האפשרות. ' + - 'פתח את תת התפריט (Sub-menu) של האפשרות הנוכחית עם רווח או אנטר (ENTER) או חץ שמאלי. ' + - 'חזור לתפריט האב עם אסקייפ (ESC) או חץ שמאלי. ' + - 'סגור את תפריט ההקשר עם אסקייפ (ESC).' - }, - - { - name : 'תפריטים צפים (List boxes)', - legend : - 'בתוך תפריט צף, עבור לפריט הבא עם טאב (TAB) או חץ למטה. ' + - 'עבור לתפריט הקודם עם שיפט (SHIFT) + טאב (TAB) or חץ עליון. ' + - 'Press SPACE or ENTER to select the list option. ' + - 'Press ESC to close the list-box.' - }, - - { - name : 'עץ אלמנטים (Elements Path)', - legend : - 'לחץ ${elementsPathFocus} כדי לנווט לעץ האלמנטים. ' + - 'עבור לפריט הבא עם טאב (TAB) או חץ ימני. ' + - 'עבור לפריט הקודם עם שיפט (SHIFT) + טאב (TAB) או חץ שמאלי. ' + - 'לחץ רווח או אנטר (ENTER) כדי לבחור את האלמנט בעורך.' - } - ] - }, - { - name : 'פקודות', - items : - [ - { - name : ' ביטול צעד אחרון', - legend : 'לחץ ${undo}' - }, - { - name : ' חזרה על צעד אחרון', - legend : 'לחץ ${redo}' - }, - { - name : ' הדגשה', - legend : 'לחץ ${bold}' - }, - { - name : ' הטייה', - legend : 'לחץ ${italic}' - }, - { - name : ' הוספת קו תחתון', - legend : 'לחץ ${underline}' - }, - { - name : ' הוספת לינק', - legend : 'לחץ ${link}' - }, - { - name : ' כיווץ סרגל הכלים', - legend : 'לחץ ${toolbarCollapse}' - }, - { - name : ' הוראות נגישות', - legend : 'לחץ ${a11yHelp}' - } - ] - } - ] - } -}); -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -CKEDITOR.plugins.setLang( 'a11yhelp', 'he', -{ - accessibilityHelp : - { - title : 'הוראות נגישות', - contents : 'הוראות נגישות. לסגירה לחץ אסקייפ (ESC).', - legend : - [ - { - name : 'כללי', - items : - [ - { - name : 'סרגל הכלים', - legend: - 'לחץ על ${toolbarFocus} כדי לנווט לסרגל הכלים. ' + - 'עבור לכפתור הבא עם מקש הטאב (TAB) או חץ שמאלי. ' + - 'עבור לכפתור הקודם עם מקש השיפט (SHIFT) + טאב (TAB) או חץ ימני. ' + - 'לחץ רווח או אנטר (ENTER) כדי להפעיל את הכפתור הנבחר.' - }, - - { - name : 'דיאלוגים (חלונות תשאול)', - legend : - 'בתוך דיאלוג, לחץ טאב (TAB) כדי לנווט לשדה הבא, לחץ שיפט (SHIFT) + טאב (TAB) כדי לנווט לשדה הקודם, לחץ אנטר (ENTER) כדי לשלוח את הדיאלוג, לחץ אסקייפ (ESC) כדי לבטל. ' + - 'בתוך דיאלוגים בעלי מספר טאבים (לשוניות), לחץ אלט (ALT) + F10 כדי לנווט לשורת הטאבים. ' + - 'נווט לטאב הבא עם טאב (TAB) או חץ שמאלי. ' + - 'עבור לטאב הקודם עם שיפט (SHIFT) + טאב (TAB) או חץ שמאלי. ' + - 'לחץ רווח או אנטר (ENTER) כדי להיכנס לטאב.' - }, - - { - name : 'תפריט ההקשר (Context Menu)', - legend : - 'לחץ ${contextMenu} או APPLICATION KEYכדי לפתוח את תפריט ההקשר. ' + - 'עבור לאפשרות הבאה עם טאב (TAB) או חץ למטה. ' + - 'עבור לאפשרות הקודמת עם שיפט (SHIFT) + טאב (TAB) או חץ למעלה. ' + - 'לחץ רווח או אנטר (ENTER) כדי לבחור את האפשרות. ' + - 'פתח את תת התפריט (Sub-menu) של האפשרות הנוכחית עם רווח או אנטר (ENTER) או חץ שמאלי. ' + - 'חזור לתפריט האב עם אסקייפ (ESC) או חץ שמאלי. ' + - 'סגור את תפריט ההקשר עם אסקייפ (ESC).' - }, - - { - name : 'תפריטים צפים (List boxes)', - legend : - 'בתוך תפריט צף, עבור לפריט הבא עם טאב (TAB) או חץ למטה. ' + - 'עבור לתפריט הקודם עם שיפט (SHIFT) + טאב (TAB) or חץ עליון. ' + - 'Press SPACE or ENTER to select the list option. ' + - 'Press ESC to close the list-box.' - }, - - { - name : 'עץ אלמנטים (Elements Path)', - legend : - 'לחץ ${elementsPathFocus} כדי לנווט לעץ האלמנטים. ' + - 'עבור לפריט הבא עם טאב (TAB) או חץ ימני. ' + - 'עבור לפריט הקודם עם שיפט (SHIFT) + טאב (TAB) או חץ שמאלי. ' + - 'לחץ רווח או אנטר (ENTER) כדי לבחור את האלמנט בעורך.' - } - ] - }, - { - name : 'פקודות', - items : - [ - { - name : ' ביטול צעד אחרון', - legend : 'לחץ ${undo}' - }, - { - name : ' חזרה על צעד אחרון', - legend : 'לחץ ${redo}' - }, - { - name : ' הדגשה', - legend : 'לחץ ${bold}' - }, - { - name : ' הטייה', - legend : 'לחץ ${italic}' - }, - { - name : ' הוספת קו תחתון', - legend : 'לחץ ${underline}' - }, - { - name : ' הוספת לינק', - legend : 'לחץ ${link}' - }, - { - name : ' כיווץ סרגל הכלים', - legend : 'לחץ ${toolbarCollapse}' - }, - { - name : ' הוראות נגישות', - legend : 'לחץ ${a11yHelp}' - } - ] - } - ] - } -}); +/*
+Copyright (c) 2003-2012, 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}'
+ }
+ ]
+ }
+ ]
+ }
+});
diff --git a/_source/plugins/a11yhelp/lang/it.js b/_source/plugins/a11yhelp/lang/it.js new file mode 100644 index 0000000..848d03f --- /dev/null +++ b/_source/plugins/a11yhelp/lang/it.js @@ -0,0 +1,89 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'a11yhelp', 'it',
+{
+ accessibilityHelp :
+ {
+ title : 'Istruzioni di Accessibilità',
+ contents : 'Contenuti di Aiuto. Per chiudere questa finestra premi ESC.',
+ legend :
+ [
+ {
+ name : 'Generale',
+ items :
+ [
+ {
+ name : 'Barra degli strumenti Editor',
+ legend:
+ 'Premi ${toolbarFocus} per navigare fino alla barra degli strumenti. Muoviti tra i gruppi della barra degli strumenti con i tasti Tab e Maiusc-Tab. Spostati tra il successivo ed il precedente pulsante della barra degli strumenti usando le frecce direzionali Destra e Sinistra. Premi Spazio o Invio per attivare il pulsante della barra degli strumenti.'
+ },
+
+ {
+ name : 'Finestra Editor',
+ legend :
+ 'All\'interno di una finestra di dialogo, premi Tab per navigare fino al campo successivo della finestra di dialogo, premi Maiusc-Tab per tornare al campo precedente, premi Invio per inviare la finestra di dialogo, premi Esc per uscire. Per le finestre che hanno schede multiple, premi Alt+F10 per navigare nella lista delle schede. Quindi spostati alla scheda successiva con il tasto Tab oppure con la Freccia Destra. Torna alla scheda precedente con Maiusc+Tab oppure con la Freccia Sinistra. Premi Spazio o Invio per scegliere la scheda.'
+ },
+
+ {
+ name : 'Menù contestuale Editor',
+ legend :
+ 'Premi ${contextMenu} o TASTO APPLICAZIONE per aprire il menu contestuale. Dunque muoviti all\'opzione successiva del menu con il tasto TAB o con la Freccia Sotto. Muoviti all\'opzione precedente con MAIUSC+TAB o con Freccia Sopra. Premi SPAZIO o INVIO per scegliere l\'opzione di menu. Apri il sottomenu dell\'opzione corrente con SPAZIO o INVIO oppure con la Freccia Destra. Torna indietro al menu superiore con ESC oppure Freccia Sinistra. Chiudi il menu contestuale con ESC.'
+ },
+
+ {
+ name : 'Box Lista Editor',
+ legend :
+ 'Dentro un box-lista, muoviti al prossimo elemento della lista con TAB o con la Freccia direzionale giù. Spostati all\'elemento precedente con MAIUSC+TAB oppure con Freccia direzionale sopra. Premi SPAZIO o INVIO per scegliere l\'opzione della lista. Premi ESC per chiudere il box-lista.'
+ },
+
+ {
+ name : 'Barra percorso elementi editor',
+ legend :
+ 'Premi ${elementsPathFocus} per navigare tra gli elementi della barra percorso. Muoviti al prossimo pulsante di elemento con TAB o la Freccia direzionale destra. Muoviti al pulsante precedente con MAIUSC+TAB o la Freccia Direzionale Sinistra. Premi SPAZIO o INVIO per scegliere l\'elemento nell\'editor.'
+ }
+ ]
+ },
+ {
+ name : 'Comandi',
+ items :
+ [
+ {
+ name : ' Annulla comando',
+ legend : 'Premi ${undo}'
+ },
+ {
+ name : ' Ripeti comando',
+ legend : 'Premi ${redo}'
+ },
+ {
+ name : ' Comando Grassetto',
+ legend : 'Premi ${bold}'
+ },
+ {
+ name : ' Comando Corsivo',
+ legend : 'Premi ${italic}'
+ },
+ {
+ name : ' Comando Sottolineato',
+ legend : 'Premi ${underline}'
+ },
+ {
+ name : ' Comando Link',
+ legend : 'Premi ${link}'
+ },
+ {
+ name : ' Comando riduci barra degli strumenti',
+ legend : 'Premi ${toolbarCollapse}'
+ },
+ {
+ name : ' Aiuto Accessibilità',
+ legend : 'Premi ${a11yHelp}'
+ }
+ ]
+ }
+ ]
+ }
+});
diff --git a/_source/plugins/a11yhelp/lang/mk.js b/_source/plugins/a11yhelp/lang/mk.js new file mode 100644 index 0000000..36d1aba --- /dev/null +++ b/_source/plugins/a11yhelp/lang/mk.js @@ -0,0 +1,89 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'a11yhelp', 'mk',
+{
+ accessibilityHelp :
+ {
+ title : 'Инструкции за пристапност',
+ contents : 'Содржина на делот за помош. За да го затворите овој дијалот притиснете ESC.',
+ legend :
+ [
+ {
+ name : 'Општо',
+ items :
+ [
+ {
+ name : 'Мени за едиторот',
+ 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.' // MISSING
+ },
+
+ {
+ name : 'Дијалот за едиторот',
+ 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.' // MISSING
+ },
+
+ {
+ name : 'Editor Context Menu', // MISSING
+ 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 with SPACE or ENTER or RIGHT ARROW. Go back to parent menu item with ESC or LEFT ARROW. Close context menu with ESC.' // MISSING
+ },
+
+ {
+ name : 'Editor List Box', // MISSING
+ 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.' // MISSING
+ },
+
+ {
+ name : 'Editor Element Path Bar', // MISSING
+ 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.' // MISSING
+ }
+ ]
+ },
+ {
+ name : 'Commands', // MISSING
+ items :
+ [
+ {
+ name : ' Undo command', // MISSING
+ legend : 'Press ${undo}' // MISSING
+ },
+ {
+ name : ' Redo command', // MISSING
+ legend : 'Press ${redo}' // MISSING
+ },
+ {
+ name : ' Bold command', // MISSING
+ legend : 'Press ${bold}' // MISSING
+ },
+ {
+ name : ' Italic command', // MISSING
+ legend : 'Press ${italic}' // MISSING
+ },
+ {
+ name : ' Underline command', // MISSING
+ legend : 'Press ${underline}' // MISSING
+ },
+ {
+ name : ' Link command', // MISSING
+ legend : 'Press ${link}' // MISSING
+ },
+ {
+ name : ' Toolbar Collapse command', // MISSING
+ legend : 'Press ${toolbarCollapse}' // MISSING
+ },
+ {
+ name : ' Accessibility Help', // MISSING
+ legend : 'Press ${a11yHelp}' // MISSING
+ }
+ ]
+ }
+ ]
+ }
+});
diff --git a/_source/plugins/a11yhelp/lang/nb.js b/_source/plugins/a11yhelp/lang/nb.js new file mode 100644 index 0000000..dc4966b --- /dev/null +++ b/_source/plugins/a11yhelp/lang/nb.js @@ -0,0 +1,89 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'a11yhelp', 'nb',
+{
+ accessibilityHelp :
+ {
+ title : 'Instruksjoner for tilgjengelighet',
+ contents : 'Innhold for hjelp. Trykk ESC for å lukke denne dialogen.',
+ legend :
+ [
+ {
+ name : 'Generelt',
+ items :
+ [
+ {
+ name : 'Verktøylinje for editor',
+ legend:
+ 'Trykk ${toolbarFocus} for å navigere til verktøylinjen. Flytt til neste og forrige verktøylinjegruppe med TAB og SHIFT-TAB. Flytt til neste og forrige verktøylinjeknapp med HØYRE PILTAST og VENSTRE PILTAST. Trykk MELLOMROM eller ENTER for å aktivere verktøylinjeknappen.'
+ },
+
+ {
+ name : 'Dialog for editor',
+ legend :
+ 'Mens du er i en dialog, trykk TAB for å navigere til neste dialogfelt, press SHIFT + TAB for å flytte til forrige felt, trykk ENTER for å akseptere dialogen, trykk ESC for å avbryte dialogen. For dialoger med flere faner, trykk ALT + F10 for å navigere til listen over faner. Gå til neste fane med TAB eller HØYRE PILTAST. Gå til forrige fane med SHIFT + TAB eller VENSTRE PILTAST. Trykk MELLOMROM eller ENTER for å velge fanen.'
+ },
+
+ {
+ name : 'Kontekstmeny for editor',
+ legend :
+ 'Trykk ${contextMenu} eller MENYKNAPP for å åpne kontekstmeny. Gå til neste alternativ i menyen med TAB eller PILTAST NED. Gå til forrige alternativ med SHIFT+TAB eller PILTAST OPP. Trykk MELLOMROM eller ENTER for å velge menyalternativet. Åpne undermenyen på valgt alternativ med MELLOMROM eller ENTER eller HØYRE PILTAST. Gå tilbake til overordnet menyelement med ESC eller VENSTRE PILTAST. Lukk kontekstmenyen med ESC.'
+ },
+
+ {
+ name : 'Listeboks for editor',
+ legend :
+ 'I en listeboks, gå til neste alternativ i listen med TAB eller PILTAST NED. Gå til forrige alternativ i listen med SHIFT + TAB eller PILTAST OPP. Trykk MELLOMROM eller ENTER for å velge alternativet i listen. Trykk ESC for å lukke listeboksen.'
+ },
+
+ {
+ name : 'Verktøylinje for elementsti',
+ legend :
+ 'Trykk ${elementsPathFocus} for å navigere til verktøylinjen som viser elementsti. Gå til neste elementknapp med TAB eller HØYRE PILTAST. Gå til forrige elementknapp med SHIFT+TAB eller VENSTRE PILTAST. Trykk MELLOMROM eller ENTER for å velge elementet i editoren.'
+ }
+ ]
+ },
+ {
+ name : 'Kommandoer',
+ items :
+ [
+ {
+ name : 'Angre',
+ legend : 'Trykk ${undo}'
+ },
+ {
+ name : 'Gjør om',
+ legend : 'Trykk ${redo}'
+ },
+ {
+ name : 'Fet tekst',
+ legend : 'Trykk ${bold}'
+ },
+ {
+ name : 'Kursiv tekst',
+ legend : 'Trykk ${italic}'
+ },
+ {
+ name : 'Understreking',
+ legend : 'Trykk ${underline}'
+ },
+ {
+ name : 'Link',
+ legend : 'Trykk ${link}'
+ },
+ {
+ name : 'Skjul verktøylinje',
+ legend : 'Trykk ${toolbarCollapse}'
+ },
+ {
+ name : 'Hjelp for tilgjengelighet',
+ legend : 'Trykk ${a11yHelp}'
+ }
+ ]
+ }
+ ]
+ }
+});
diff --git a/_source/plugins/a11yhelp/lang/nl.js b/_source/plugins/a11yhelp/lang/nl.js new file mode 100644 index 0000000..61b2989 --- /dev/null +++ b/_source/plugins/a11yhelp/lang/nl.js @@ -0,0 +1,89 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'a11yhelp', 'nl',
+{
+ accessibilityHelp :
+ {
+ title : 'Toegankelijkheidsinstructies',
+ contents : 'Help inhoud. Druk op ESC om dit dialoog te sluiten.',
+ legend :
+ [
+ {
+ name : 'Algemeen',
+ items :
+ [
+ {
+ name : 'Werkbalk tekstverwerker',
+ legend:
+ 'Druk op ${toolbarFocus} om naar de werkbalk te navigeren. Om te schakelen naar de volgende en vorige werkbalkgroep, gebruik TAB en SHIFT+TAB. Om te schakelen naar de volgende en vorige werkbalkknop, gebruik de PIJL RECHTS en PIJL LINKS. Druk op SPATIE of ENTER om een werkbalkknop te activeren.'
+ },
+
+ {
+ name : 'Dialoog tekstverwerker',
+ legend :
+ 'In een dialoogvenster, druk op TAB om te navigeren naar het volgende veld. Druk op SHIFT+TAB om naar het vorige veld te navigeren. Druk op ENTER om het dialoogvenster te verzenden. Druk op ESC om het dialoogvenster te sluiten. Voor dialoogvensters met meerdere tabbladen, druk op ALT+F10 om naar de tabset te navigeren. Schakel naar het volgende tabblad met TAB of PIJL RECHTS. Schakel naar het vorige tabblad met SHIFT+TAB of PIJL LINKS. Druk op SPATIE of ENTER om het tabblad te selecteren.'
+ },
+
+ {
+ name : 'Contextmenu tekstverwerker',
+ legend :
+ 'Druk op ${contextMenu} of APPLICATION KEY om het contextmenu te openen. Schakel naar de volgende menuoptie met TAB of PIJL OMLAAG. Schakel naar de vorige menuoptie met SHIFT+TAB of PIJL OMHOOG. Druk op SPATIE of ENTER om een menuoptie te selecteren. Op een submenu van de huidige optie met SPATIE, ENTER of PIJL RECHTS. Ga terug naar de bovenliggende menuoptie met ESC of PIJL LINKS. Sluit het contextmenu met ESC.'
+ },
+
+ {
+ name : 'Keuzelijst tekstverwerker',
+ legend :
+ 'In een keuzelijst, schakel naar het volgende item met TAB of PIJL OMLAAG. Schakel naar het vorige item met SHIFT+TAB of PIJL OMHOOG. Druk op SPATIE of ENTER om het item te selecteren. Druk op ESC om de keuzelijst te sluiten.'
+ },
+
+ {
+ name : 'Elementenpad werkbalk tekstverwerker',
+ legend :
+ 'Druk op ${elementsPathFocus} om naar het elementenpad te navigeren. Om te schakelen naar het volgende element, gebruik TAB of PIJL RECHTS. Om te schakelen naar het vorige element, gebruik SHIFT+TAB or PIJL LINKS. Druk op SPATIE of ENTER om een element te selecteren in de tekstverwerker.'
+ }
+ ]
+ },
+ {
+ name : 'Opdrachten',
+ items :
+ [
+ {
+ name : 'Ongedaan maken opdracht',
+ legend : 'Druk op ${undo}'
+ },
+ {
+ name : 'Opnieuw uitvoeren opdracht',
+ legend : 'Druk op ${redo}'
+ },
+ {
+ name : 'Vetgedrukt opdracht',
+ legend : 'Druk up ${bold}'
+ },
+ {
+ name : 'Cursief opdracht',
+ legend : 'Druk op ${italic}'
+ },
+ {
+ name : 'Onderstrepen opdracht',
+ legend : 'Druk op ${underline}'
+ },
+ {
+ name : 'Link opdracht',
+ legend : 'Druk op ${link}'
+ },
+ {
+ name : 'Werkbalk inklappen opdracht',
+ legend : 'Druk op ${toolbarCollapse}'
+ },
+ {
+ name : 'Toegankelijkheidshulp',
+ legend : 'Druk op ${a11yHelp}'
+ }
+ ]
+ }
+ ]
+ }
+});
diff --git a/_source/plugins/a11yhelp/lang/no.js b/_source/plugins/a11yhelp/lang/no.js new file mode 100644 index 0000000..8fe719e --- /dev/null +++ b/_source/plugins/a11yhelp/lang/no.js @@ -0,0 +1,89 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'a11yhelp', 'no',
+{
+ accessibilityHelp :
+ {
+ title : 'Instruksjoner for tilgjengelighet',
+ contents : 'Innhold for hjelp. Trykk ESC for å lukke denne dialogen.',
+ legend :
+ [
+ {
+ name : 'Generelt',
+ items :
+ [
+ {
+ name : 'Verktøylinje for editor',
+ legend:
+ 'Trykk ${toolbarFocus} for å navigere til verktøylinjen. Flytt til neste og forrige verktøylinjegruppe med TAB og SHIFT-TAB. Flytt til neste og forrige verktøylinjeknapp med HØYRE PILTAST og VENSTRE PILTAST. Trykk MELLOMROM eller ENTER for å aktivere verktøylinjeknappen.'
+ },
+
+ {
+ name : 'Dialog for editor',
+ legend :
+ 'Mens du er i en dialog, trykk TAB for å navigere til neste dialogfelt, press SHIFT + TAB for å flytte til forrige felt, trykk ENTER for å akseptere dialogen, trykk ESC for å avbryte dialogen. For dialoger med flere faner, trykk ALT + F10 for å navigere til listen over faner. Gå til neste fane med TAB eller HØYRE PILTAST. Gå til forrige fane med SHIFT + TAB eller VENSTRE PILTAST. Trykk MELLOMROM eller ENTER for å velge fanen.'
+ },
+
+ {
+ name : 'Kontekstmeny for editor',
+ legend :
+ 'Trykk ${contextMenu} eller MENYKNAPP for å åpne kontekstmeny. Gå til neste alternativ i menyen med TAB eller PILTAST NED. Gå til forrige alternativ med SHIFT+TAB eller PILTAST OPP. Trykk MELLOMROM eller ENTER for å velge menyalternativet. Åpne undermenyen på valgt alternativ med MELLOMROM eller ENTER eller HØYRE PILTAST. Gå tilbake til overordnet menyelement med ESC eller VENSTRE PILTAST. Lukk kontekstmenyen med ESC.'
+ },
+
+ {
+ name : 'Listeboks for editor',
+ legend :
+ 'I en listeboks, gå til neste alternativ i listen med TAB eller PILTAST NED. Gå til forrige alternativ i listen med SHIFT + TAB eller PILTAST OPP. Trykk MELLOMROM eller ENTER for å velge alternativet i listen. Trykk ESC for å lukke listeboksen.'
+ },
+
+ {
+ name : 'Verktøylinje for elementsti',
+ legend :
+ 'Trykk ${elementsPathFocus} for å navigere til verktøylinjen som viser elementsti. Gå til neste elementknapp med TAB eller HØYRE PILTAST. Gå til forrige elementknapp med SHIFT+TAB eller VENSTRE PILTAST. Trykk MELLOMROM eller ENTER for å velge elementet i editoren.'
+ }
+ ]
+ },
+ {
+ name : 'Kommandoer',
+ items :
+ [
+ {
+ name : 'Angre',
+ legend : 'Trykk ${undo}'
+ },
+ {
+ name : 'Gjør om',
+ legend : 'Trykk ${redo}'
+ },
+ {
+ name : 'Fet tekst',
+ legend : 'Trykk ${bold}'
+ },
+ {
+ name : 'Kursiv tekst',
+ legend : 'Trykk ${italic}'
+ },
+ {
+ name : 'Understreking',
+ legend : 'Trykk ${underline}'
+ },
+ {
+ name : 'Link',
+ legend : 'Trykk ${link}'
+ },
+ {
+ name : 'Skjul verktøylinje',
+ legend : 'Trykk ${toolbarCollapse}'
+ },
+ {
+ name : 'Hjelp for tilgjengelighet',
+ legend : 'Trykk ${a11yHelp}'
+ }
+ ]
+ }
+ ]
+ }
+});
diff --git a/_source/plugins/a11yhelp/lang/tr.js b/_source/plugins/a11yhelp/lang/tr.js new file mode 100644 index 0000000..34c1a46 --- /dev/null +++ b/_source/plugins/a11yhelp/lang/tr.js @@ -0,0 +1,89 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'a11yhelp', 'tr',
+{
+ accessibilityHelp :
+ {
+ title : 'Erişilebilirlik Talimatları',
+ contents : 'Yardım içeriği. Bu pencereyi kapatmak için ESC tuşuna basın.',
+ legend :
+ [
+ {
+ name : 'Genel',
+ items :
+ [
+ {
+ name : 'Araç Çubuğu Editörü',
+ legend:
+ 'Araç çubuğunda gezinmek için ${toolbarFocus} basın. TAB ve SHIFT-TAB ile önceki ve sonraki araç çubuğu grubuna taşıyın. SAĞ OK veya SOL OK ile önceki ve sonraki bir araç çubuğu düğmesini hareket ettirin. SPACE tuşuna basın veya araç çubuğu düğmesini etkinleştirmek için ENTER tuşna basın.'
+ },
+
+ {
+ name : 'Dialog Editörü',
+ legend :
+ 'Dialog penceresi içinde, sonraki iletişim alanına gitmek için SEKME tuşuna basın, önceki alana geçmek için SHIFT + TAB tuşuna basın, pencereyi göndermek için ENTER tuşuna basın, dialog penceresini iptal etmek için ESC tuşuna basın. Birden çok sekme sayfaları olan diyalogların, sekme listesine gitmek için ALT + F10 tuşlarına basın. Sonra TAB veya SAĞ OK sonraki sekmeye taşıyın. SHIFT + TAB veya SOL OK ile önceki sekmeye geçin. Sekme sayfayı seçmek için SPACE veya ENTER tuşuna basın.'
+ },
+
+ {
+ name : 'İçerik Menü Editörü',
+ legend :
+ 'İçerik menüsünü açmak için ${contextMenu} veya UYGULAMA TUŞU\'na basın. Daha sonra SEKME veya AŞAĞI OK ile bir sonraki menü seçeneği taşıyın. SHIFT + TAB veya YUKARI OK ile önceki seçeneğe gider. Menü seçeneğini seçmek için SPACE veya ENTER tuşuna basın. Seçili seçeneğin alt menüsünü SPACE ya da ENTER veya SAĞ OK açın. Üst menü öğesini geçmek için ESC veya SOL OK ile geri dönün. ESC ile bağlam menüsünü kapatın.'
+ },
+
+ {
+ name : 'Liste Kutusu Editörü',
+ legend :
+ 'Liste kutusu içinde, bir sonraki liste öğesine SEKME VEYA AŞAĞI OK ile taşıyın. SHIFT + TAB veya YUKARI önceki liste öğesi taşıyın. Liste seçeneği seçmek için SPACE veya ENTER tuşuna basın. Liste kutusunu kapatmak için ESC tuşuna basın.'
+ },
+
+ {
+ name : 'Element Yol Çubuğu Editörü',
+ legend :
+ 'Elementlerin yol çubuğunda gezinmek için ${ElementsPathFocus} basın. SEKME veya SAĞ OK ile sonraki element düğmesine taşıyın. SHIFT + TAB veya SOL OK önceki düğmeye hareket ettirin. Editör içindeki elementi seçmek için ENTER veya SPACE tuşuna basın.'
+ }
+ ]
+ },
+ {
+ name : 'Komutlar',
+ items :
+ [
+ {
+ name : 'Komutu geri al',
+ legend : '${undo} basın'
+ },
+ {
+ name : ' Tekrar komutu uygula',
+ legend : '${redo} basın'
+ },
+ {
+ name : ' Kalın komut',
+ legend : '${bold} basın'
+ },
+ {
+ name : ' İtalik komutu',
+ legend : '${italic} basın'
+ },
+ {
+ name : ' Alttan çizgi komutu',
+ legend : '${underline} basın'
+ },
+ {
+ name : ' Bağlantı komutu',
+ legend : '${link} basın'
+ },
+ {
+ name : ' Araç çubuğu Toplama komutu',
+ legend : '${toolbarCollapse} basın'
+ },
+ {
+ name : 'Erişilebilirlik Yardımı',
+ legend : '${a11yHelp} basın'
+ }
+ ]
+ }
+ ]
+ }
+});
diff --git a/_source/plugins/a11yhelp/lang/ug.js b/_source/plugins/a11yhelp/lang/ug.js new file mode 100644 index 0000000..6e9a84a --- /dev/null +++ b/_source/plugins/a11yhelp/lang/ug.js @@ -0,0 +1,89 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'a11yhelp', 'ug',
+{
+ accessibilityHelp :
+ {
+ title : 'قوشۇمچە چۈشەندۈرۈش',
+ contents : 'ياردەم مەزمۇنى. بۇ سۆزلەشكۈنى ياپماقچى بولسىڭىز ESC نى بېسىڭ.',
+ legend :
+ [
+ {
+ name : 'ئادەتتىكى',
+ items :
+ [
+ {
+ name : 'قورال بالداق تەھرىر',
+ legend:
+ '${toolbarFocus} بېسىلسا قورال بالداققا يېتەكلەيدۇ، TAB ياكى SHIFT+TAB ئارقىلىق قورال بالداق گۇرۇپپىسى تاللىنىدۇ، ئوڭ سول يا ئوقتا توپچا تاللىنىدۇ، بوشلۇق ياكى Enter كۇنۇپكىسىدا تاللانغان توپچىنى قوللىنىدۇ.'
+ },
+
+ {
+ name : 'تەھرىرلىگۈچ سۆزلەشكۈسى',
+ 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.' // MISSING
+ },
+
+ {
+ name : 'تەھرىرلىگۈچ تىل مۇھىت تىزىملىكى',
+ 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 with SPACE or ENTER or RIGHT ARROW. Go back to parent menu item with ESC or LEFT ARROW. Close context menu with ESC.' // MISSING
+ },
+
+ {
+ name : 'تەھرىرلىگۈچ تىزىمى',
+ 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.' // MISSING
+ },
+
+ {
+ name : 'تەھرىرلىگۈچ ئېلېمېنت يول بالداق',
+ 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} نى بېسىڭ'
+ }
+ ]
+ }
+ ]
+ }
+});
diff --git a/_source/plugins/a11yhelp/lang/vi.js b/_source/plugins/a11yhelp/lang/vi.js new file mode 100644 index 0000000..c045369 --- /dev/null +++ b/_source/plugins/a11yhelp/lang/vi.js @@ -0,0 +1,89 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'a11yhelp', 'vi',
+{
+ accessibilityHelp :
+ {
+ title : 'Accessibility Instructions', // MISSING
+ contents : 'Nội dung Hỗ trợ. Nhấn ESC để đóng hộp thoại.',
+ legend :
+ [
+ {
+ name : 'Chung',
+ items :
+ [
+ {
+ name : 'Thanh công cụ soạn th',
+ legend:
+ 'Nhấn ${toolbarFocus} để điều hướng đến thanh công cụ. Nhấn TAB và SHIFT-TAB để chuyển đến nhóm thanh công cụ khác. Nhấn MŨI TÊN PHẢI hoặc MŨI TÊN TRÁI để chuyển sang nút khác trên thanh công cụ. Nhấn PHÍM CÁCH hoặc ENTER để kích hoạt nút trên thanh công c.'
+ },
+
+ {
+ name : 'Hộp thoại Biên t',
+ 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.' // MISSING
+ },
+
+ {
+ name : 'Trình đơn Ngữ cảnh cBộ soạn thảo',
+ 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 with SPACE or ENTER or RIGHT ARROW. Go back to parent menu item with ESC or LEFT ARROW. Close context menu with ESC.' // MISSING
+ },
+
+ {
+ name : 'Editor List Box', // MISSING
+ 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.' // MISSING
+ },
+
+ {
+ name : 'Editor Element Path Bar', // MISSING
+ 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.' // MISSING
+ }
+ ]
+ },
+ {
+ name : 'Commands', // MISSING
+ items :
+ [
+ {
+ name : ' Undo command', // MISSING
+ legend : 'Press ${undo}' // MISSING
+ },
+ {
+ name : ' Redo command', // MISSING
+ legend : 'Press ${redo}' // MISSING
+ },
+ {
+ name : ' Bold command', // MISSING
+ legend : 'Press ${bold}' // MISSING
+ },
+ {
+ name : ' Italic command', // MISSING
+ legend : 'Press ${italic}' // MISSING
+ },
+ {
+ name : ' Underline command', // MISSING
+ legend : 'Press ${underline}' // MISSING
+ },
+ {
+ name : ' Link command', // MISSING
+ legend : 'Press ${link}' // MISSING
+ },
+ {
+ name : ' Toolbar Collapse command', // MISSING
+ legend : 'Press ${toolbarCollapse}' // MISSING
+ },
+ {
+ name : ' Accessibility Help', // MISSING
+ legend : 'Press ${a11yHelp}' // MISSING
+ }
+ ]
+ }
+ ]
+ }
+});
diff --git a/_source/plugins/a11yhelp/lang/zh-cn.js b/_source/plugins/a11yhelp/lang/zh-cn.js new file mode 100644 index 0000000..a595f6f --- /dev/null +++ b/_source/plugins/a11yhelp/lang/zh-cn.js @@ -0,0 +1,89 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'a11yhelp', 'zh-cn',
+{
+ accessibilityHelp :
+ {
+ title : '辅助说明',
+ contents : '帮助内容。要关闭此对话框请按 ESC 键。',
+ legend :
+ [
+ {
+ name : '常规',
+ items :
+ [
+ {
+ name : '编辑器工具栏',
+ legend:
+ '按 ${toolbarFocus} 以导航到工具栏,使用 TAB 键或 SHIFT+TAB 组合键以选择工具栏组,使用左右箭头键以选择按钮,按空格键或回车键以应用选中的按钮。'
+ },
+
+ {
+ name : '编辑器对话框',
+ legend :
+ '在对话框内,TAB键移动到下一个字段,SHIFT + TAB 移动到上一个字段,ENTER键提交对话框,ESC键取消对话框。对于有多标签的对话框,用ALT + F10来移到标签列表。然后用TAB键或者向右箭头来移动到下一个标签;SHIFT + TAB或者向左箭头移动到上一个标签。用SPACE或者ENTER选择标签。'
+ },
+
+ {
+ name : '编辑器上下文菜单',
+ legend :
+ '用 ${contextMenu}或者 应用程序键 打开上下文菜单。然后用TAB键或者向下箭头来移动到下一个菜单项;SHIFT + TAB或者向上箭头移动到上一个菜单项。用SPACE或者ENTER选择菜单项。用SPACE,ENTER或者向右箭头打开子菜单。返回菜单用ESC键或者向左箭头。用ESC关闭上下文菜单。'
+ },
+
+ {
+ name : '编辑器列表框',
+ legend :
+ '在列表框中,移到下一列表项用TAB键或者向下箭头。移到上一列表项用SHIFT + TAB或者向上箭头,用SPACE或者ENTER选择列表项。用ESC收起列表框。'
+ },
+
+ {
+ name : '编辑器元素路径栏',
+ legend :
+ '按 ${elementsPathFocus} 以导航到元素路径栏,使用 TAB 键或右箭头键选择下一个元素,使用 SHIFT+TAB 组合键或左箭头键选择上一个元素,按空格键或回车键以选定编辑器里的元素。'
+ }
+ ]
+ },
+ {
+ 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}'
+ }
+ ]
+ }
+ ]
+ }
+});
diff --git a/_source/plugins/a11yhelp/plugin.js b/_source/plugins/a11yhelp/plugin.js index 9409b37..31996a1 100644 --- a/_source/plugins/a11yhelp/plugin.js +++ b/_source/plugins/a11yhelp/plugin.js @@ -1,46 +1,47 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -/** - * @fileOverview Plugin definition for the a11yhelp, which provides a dialog - * with accessibility related help. - */ - -(function() -{ - var pluginName = 'a11yhelp', - commandName = 'a11yHelp'; - - CKEDITOR.plugins.add( pluginName, - { - // List of available localizations. - availableLangs : { en:1, he:1 }, - - init : function( editor ) - { - var plugin = this; - editor.addCommand( commandName, - { - exec : function() - { - var langCode = editor.langCode; - langCode = plugin.availableLangs[ langCode ] ? langCode : 'en'; - - CKEDITOR.scriptLoader.load( - CKEDITOR.getUrl( plugin.path + 'lang/' + langCode + '.js' ), - function() - { - CKEDITOR.tools.extend( editor.lang, plugin.lang[ langCode ] ); - editor.openDialog( commandName ); - }); - }, - modes : { wysiwyg:1, source:1 }, - canUndo : false - }); - - CKEDITOR.dialog.add( commandName, this.path + 'dialogs/a11yhelp.js' ); - } - }); -})(); +/*
+Copyright (c) 2003-2012, 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 : { cs:1, cy:1, da:1, de:1, el:1, en:1, eo:1, fa:1, fi:1, fr:1, gu:1, he:1, it:1, mk:1, nb:1, nl:1, no:1, tr:1, ug:1, vi:1, 'zh-cn':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' );
+ }
+ });
+})();
diff --git a/_source/plugins/about/dialogs/about.js b/_source/plugins/about/dialogs/about.js index 509762e..53b4b0a 100644 --- a/_source/plugins/about/dialogs/about.js +++ b/_source/plugins/about/dialogs/about.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
@@ -57,6 +57,9 @@ CKEDITOR.dialog.add( 'about', function( editor ) '<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>' +
diff --git a/_source/plugins/about/plugin.js b/_source/plugins/about/plugin.js index 71c172f..40eae7e 100644 --- a/_source/plugins/about/plugin.js +++ b/_source/plugins/about/plugin.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
@@ -11,6 +11,7 @@ CKEDITOR.plugins.add( 'about', 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',
{
diff --git a/_source/plugins/adobeair/plugin.js b/_source/plugins/adobeair/plugin.js new file mode 100644 index 0000000..065cde0 --- /dev/null +++ b/_source/plugins/adobeair/plugin.js @@ -0,0 +1,228 @@ +/*
+Copyright (c) 2003-2012, 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 );
+ };
+ });
diff --git a/_source/plugins/ajax/plugin.js b/_source/plugins/ajax/plugin.js new file mode 100644 index 0000000..1c032a9 --- /dev/null +++ b/_source/plugins/ajax/plugin.js @@ -0,0 +1,152 @@ +/*
+Copyright (c) 2003-2012, 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 );
+ }
+ };
+ })();
+
+})();
diff --git a/_source/plugins/autogrow/plugin.js b/_source/plugins/autogrow/plugin.js new file mode 100644 index 0000000..fdbb578 --- /dev/null +++ b/_source/plugins/autogrow/plugin.js @@ -0,0 +1,141 @@ +/*
+Copyright (c) 2003-2012, 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 ? ' ' : '' ) + '</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, mode: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
+ */
diff --git a/_source/plugins/basicstyles/plugin.js b/_source/plugins/basicstyles/plugin.js index b1bc5bc..b3f64fb 100644 --- a/_source/plugins/basicstyles/plugin.js +++ b/_source/plugins/basicstyles/plugin.js @@ -1,101 +1,129 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -CKEDITOR.plugins.add( 'basicstyles', -{ - requires : [ 'styles', 'button' ], - - init : function( editor ) - { - // All buttons use the same code to register. So, to avoid - // duplications, let's use this tool function. - var addButtonCommand = function( buttonName, buttonLabel, commandName, styleDefiniton ) - { - var style = new CKEDITOR.style( styleDefiniton ); - - editor.attachStyleStateChange( style, function( state ) - { - editor.getCommand( commandName ).setState( state ); - }); - - editor.addCommand( commandName, new CKEDITOR.styleCommand( style ) ); - - editor.ui.addButton( buttonName, - { - label : buttonLabel, - command : commandName - }); - }; - - var config = editor.config; - var lang = editor.lang; - - addButtonCommand( 'Bold' , lang.bold , 'bold' , config.coreStyles_bold ); - addButtonCommand( 'Italic' , lang.italic , 'italic' , config.coreStyles_italic ); - addButtonCommand( 'Underline' , lang.underline , 'underline' , config.coreStyles_underline ); - addButtonCommand( 'Strike' , lang.strike , 'strike' , config.coreStyles_strike ); - addButtonCommand( 'Subscript' , lang.subscript , 'subscript' , config.coreStyles_subscript ); - addButtonCommand( 'Superscript' , lang.superscript , 'superscript' , config.coreStyles_superscript ); - } -}); - -// Basic Inline Styles. - -/** - * The style definition to be used to apply the bold style in the text. - * @type Object - * @example - * config.coreStyles_bold = { element : 'b', overrides : 'strong' }; - * @example - * config.coreStyles_bold = { element : 'span', attributes : {'class': 'Bold'} }; - */ -CKEDITOR.config.coreStyles_bold = { element : 'strong', overrides : 'b' }; - -/** - * The style definition to be used to apply the italic style in the text. - * @type Object - * @default { element : 'em', overrides : 'i' } - * @example - * config.coreStyles_bold = { element : 'i', overrides : 'em' }; - * @example - * CKEDITOR.config.coreStyles_italic = { element : 'span', attributes : {'class': 'Italic'} }; - */ -CKEDITOR.config.coreStyles_italic = { element : 'em', overrides : 'i' }; - -/** - * The style definition to be used to apply the underline style in the text. - * @type Object - * @default { element : 'u' } - * @example - * CKEDITOR.config.coreStyles_underline = { element : 'span', attributes : {'class': 'Underline'}}; - */ -CKEDITOR.config.coreStyles_underline = { element : 'u' }; - -/** - * The style definition to be used to apply the strike style in the text. - * @type Object - * @default { element : 'strike' } - * @example - * CKEDITOR.config.coreStyles_strike = { element : 'span', attributes : {'class': 'StrikeThrough'}, overrides : 'strike' }; - */ -CKEDITOR.config.coreStyles_strike = { element : 'strike' }; - -/** - * The style definition to be used to apply the subscript style in the text. - * @type Object - * @default { element : 'sub' } - * @example - * CKEDITOR.config.coreStyles_subscript = { element : 'span', attributes : {'class': 'Subscript'}, overrides : 'sub' }; - */ -CKEDITOR.config.coreStyles_subscript = { element : 'sub' }; - -/** - * The style definition to be used to apply the superscript style in the text. - * @type Object - * @default { element : 'sup' } - * @example - * CKEDITOR.config.coreStyles_superscript = { element : 'span', attributes : {'class': 'Superscript'}, overrides : 'sup' }; - */ -CKEDITOR.config.coreStyles_superscript = { element : 'sup' }; +/*
+Copyright (c) 2003-2012, 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' };
diff --git a/_source/plugins/bbcode/plugin.js b/_source/plugins/bbcode/plugin.js new file mode 100644 index 0000000..fa8bc1a --- /dev/null +++ b/_source/plugins/bbcode/plugin.js @@ -0,0 +1,931 @@ +/*
+Copyright (c) 2003-2012, 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( /"/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 "<!-- My comment -->".
+ * 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;
+ });
+ }
+ }
+ } );
+
+})();
diff --git a/_source/plugins/bidi/plugin.js b/_source/plugins/bidi/plugin.js new file mode 100644 index 0000000..72f1041 --- /dev/null +++ b/_source/plugins/bidi/plugin.js @@ -0,0 +1,334 @@ +/*
+Copyright (c) 2003-2012, 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.
+ */
diff --git a/_source/plugins/blockquote/plugin.js b/_source/plugins/blockquote/plugin.js index a25c1a1..f17aec2 100644 --- a/_source/plugins/blockquote/plugin.js +++ b/_source/plugins/blockquote/plugin.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
@@ -25,8 +25,11 @@ For licensing, see LICENSE.html or http://ckeditor.com/license function onSelectionChange( evt )
{
- var editor = evt.editor,
- command = editor.getCommand( 'blockquote' );
+ var editor = evt.editor;
+ if ( editor.readOnly )
+ return;
+
+ var command = editor.getCommand( 'blockquote' );
command.state = getState( editor, evt.data.path );
command.fire( 'state' );
}
@@ -47,7 +50,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license {
var state = editor.getCommand( 'blockquote' ).state,
selection = editor.getSelection(),
- range = selection && selection.getRanges()[0];
+ range = selection && selection.getRanges( true )[0];
if ( !range )
return;
@@ -95,6 +98,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license var iterator = range.createIterator(),
block;
+ iterator.enlargeBr = editor.config.enterMode != CKEDITOR.ENTER_BR;
if ( state == CKEDITOR.TRISTATE_OFF )
{
diff --git a/_source/plugins/button/plugin.js b/_source/plugins/button/plugin.js index 26909bc..af95b69 100644 --- a/_source/plugins/button/plugin.js +++ b/_source/plugins/button/plugin.js @@ -1,277 +1,290 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -CKEDITOR.plugins.add( 'button', -{ - beforeInit : function( editor ) - { - editor.ui.addHandler( CKEDITOR.UI_BUTTON, CKEDITOR.ui.button.handler ); - } -}); - -/** - * Button UI element. - * @constant - * @example - */ -CKEDITOR.UI_BUTTON = 1; - -/** - * Represents a button UI element. This class should not be called directly. To - * create new buttons use {@link CKEDITOR.ui.prototype.addButton} instead. - * @constructor - * @param {Object} definition The button definition. - * @example - */ -CKEDITOR.ui.button = function( definition ) -{ - // Copy all definition properties to this object. - CKEDITOR.tools.extend( this, definition, - // Set defaults. - { - title : definition.label, - className : definition.className || ( definition.command && 'cke_button_' + definition.command ) || '', - click : definition.click || function( editor ) - { - editor.execCommand( definition.command ); - } - }); - - this._ = {}; -}; - -/** - * Transforms a button definition in a {@link CKEDITOR.ui.button} instance. - * @type Object - * @example - */ -CKEDITOR.ui.button.handler = -{ - create : function( definition ) - { - return new CKEDITOR.ui.button( definition ); - } -}; - -CKEDITOR.ui.button.prototype = -{ - canGroup : true, - - /** - * Renders the button. - * @param {CKEDITOR.editor} editor The editor instance which this button is - * to be used by. - * @param {Array} output The output array to which append the HTML relative - * to this button. - * @example - */ - render : function( editor, output ) - { - var env = CKEDITOR.env, - id = this._.id = 'cke_' + CKEDITOR.tools.getNextNumber(), - classes = '', - command = this.command, // Get the command name. - clickFn, - index; - - this._.editor = editor; - - var instance = - { - id : id, - button : this, - editor : editor, - focus : function() - { - var element = CKEDITOR.document.getById( id ); - element.focus(); - }, - execute : function() - { - this.button.click( editor ); - } - }; - - instance.clickFn = clickFn = CKEDITOR.tools.addFunction( instance.execute, instance ); - - instance.index = index = CKEDITOR.ui.button._.instances.push( instance ) - 1; - - if ( this.modes ) - { - editor.on( 'mode', function() - { - this.setState( this.modes[ editor.mode ] ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED ); - }, this); - } - else if ( command ) - { - // Get the command instance. - command = editor.getCommand( command ); - - if ( command ) - { - command.on( 'state', function() - { - this.setState( command.state ); - }, this); - - classes += 'cke_' + ( - command.state == CKEDITOR.TRISTATE_ON ? 'on' : - command.state == CKEDITOR.TRISTATE_DISABLED ? 'disabled' : - 'off' ); - } - } - - if ( !command ) - classes += 'cke_off'; - - if ( this.className ) - classes += ' ' + this.className; - - output.push( - '<span class="cke_button">', - '<a id="', id, '"' + - ' class="', classes, '"', - env.gecko && env.version >= 10900 && !env.hc ? '' : '" href="javascript:void(\''+ ( this.title || '' ).replace( "'"+ '' )+ '\')"', - ' title="', this.title, '"' + - ' tabindex="-1"' + - ' hidefocus="true"' + - ' role="button"' + - ' aria-labelledby="' + id + '_label"' + - ( this.hasArrow ? ' aria-haspopup="true"' : '' ) ); - - // Some browsers don't cancel key events in the keydown but in the - // keypress. - // TODO: Check if really needed for Gecko+Mac. - if ( env.opera || ( env.gecko && env.mac ) ) - { - output.push( - ' onkeypress="return false;"' ); - } - - // With Firefox, we need to force the button to redraw, otherwise it - // will remain in the focus state. - if ( env.gecko ) - { - output.push( - ' onblur="this.style.cssText = this.style.cssText;"' ); - } - - output.push( - ' onkeydown="return CKEDITOR.ui.button._.keydown(', index, ', event);"' + - ' onfocus="return CKEDITOR.ui.button._.focus(', index, ', event);"' + - ' onclick="CKEDITOR.tools.callFunction(', clickFn, ', this); return false;">' + - '<span class="cke_icon"' ); - - if ( this.icon ) - { - var offset = ( this.iconOffset || 0 ) * -16; - output.push( ' style="background-image:url(', CKEDITOR.getUrl( this.icon ), ');background-position:0 ' + offset + 'px;"' ); - } - - output.push( - '> </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 ? '▼' : ' ' ) - + '</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; - } -}; - -/** - * Handles a button click. - * @private - */ -CKEDITOR.ui.button._ = -{ - instances : [], - - keydown : function( index, ev ) - { - var instance = CKEDITOR.ui.button._.instances[ index ]; - - if ( instance.onkey ) - { - ev = new CKEDITOR.dom.event( ev ); - return ( instance.onkey( instance, ev.getKeystroke() ) !== false ); - } - }, - - focus : function( index, ev ) - { - var instance = CKEDITOR.ui.button._.instances[ index ], - retVal; - - if ( instance.onfocus ) - retVal = ( instance.onfocus( instance, new CKEDITOR.dom.event( ev ) ) !== false ); - - // FF2: prevent focus event been bubbled up to editor container, which caused unexpected editor focus. - if ( CKEDITOR.env.gecko && CKEDITOR.env.version < 10900 ) - ev.preventBubble(); - return retVal; - } -}; - -/** - * Adds a button definition to the UI elements list. - * @param {String} The button name. - * @param {Object} The button definition. - * @example - * editorInstance.ui.addButton( 'MyBold', - * { - * label : 'My Bold', - * command : 'bold' - * }); - */ -CKEDITOR.ui.prototype.addButton = function( name, definition ) -{ - this.add( name, CKEDITOR.UI_BUTTON, definition ); -}; - -CKEDITOR.on( 'reset', function() - { - CKEDITOR.ui.button._.instances = []; - }); +/*
+Copyright (c) 2003-2012, 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(
+ '> </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 ? '▼' : ' ' )
+ + '</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 );
+};
diff --git a/_source/plugins/clipboard/dialogs/paste.js b/_source/plugins/clipboard/dialogs/paste.js index 42401d1..cec04d3 100644 --- a/_source/plugins/clipboard/dialogs/paste.js +++ b/_source/plugins/clipboard/dialogs/paste.js @@ -1,211 +1,223 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -CKEDITOR.dialog.add( 'paste', function( editor ) -{ - var lang = editor.lang.clipboard; - var isCustomDomain = CKEDITOR.env.isCustomDomain(); - - function onPasteFrameLoad( win ) - { - var doc = new CKEDITOR.dom.document( win.document ), - docElement = doc.$; - - doc.getById( "cke_actscrpt" ).remove(); - - CKEDITOR.env.ie ? - docElement.body.contentEditable = "true" : - docElement.designMode = "on"; - - // IE before version 8 will leave cursor blinking inside the document after - // editor blurred unless we clean up the selection. (#4716) - if ( CKEDITOR.env.ie && CKEDITOR.env.version < 8 ) - { - doc.getWindow().on( 'blur', function() - { - docElement.selection.empty(); - } ); - } - - doc.on( "keydown", function( e ) - { - var domEvent = e.data, - key = domEvent.getKeystroke(), - processed; - - switch( key ) - { - case 27 : - this.hide(); - processed = 1; - break; - - case 9 : - case CKEDITOR.SHIFT + 9 : - this.changeFocus( true ); - processed = 1; - } - - processed && domEvent.preventDefault(); - }, this ); - - editor.fire( 'ariaWidget', new CKEDITOR.dom.element( win.frameElement ) ); - } - - return { - title : lang.title, - - minWidth : CKEDITOR.env.ie && CKEDITOR.env.quirks ? 370 : 350, - minHeight : CKEDITOR.env.quirks ? 250 : 245, - onShow : function() - { - // FIREFOX BUG: Force the browser to render the dialog to make the to-be- - // inserted iframe editable. (#3366) - this.parts.dialog.$.offsetHeight; - - var htmlToLoad = - '<html dir="' + editor.config.contentsLangDirection + '"' + - ' lang="' + ( editor.config.contentsLanguage || editor.langCode ) + '">' + - '<head><style>body { margin: 3px; height: 95%; } </style></head><body>' + - '<script id="cke_actscrpt" type="text/javascript">' + - 'window.parent.CKEDITOR.tools.callFunction( ' + CKEDITOR.tools.addFunction( onPasteFrameLoad, this ) + ', this );' + - '</script></body>' + - '</html>'; - - var iframe = CKEDITOR.dom.element.createFromHtml( - '<iframe' + - ' frameborder="0" ' + - ' allowTransparency="true"' + - // Support for custom document.domain in IE. - ( isCustomDomain ? - ' src="javascript:void((function(){' + - 'document.open();' + - 'document.domain=\'' + document.domain + '\';' + - 'document.close();' + - '})())"' : '' ) + - ' role="region"' + - ' aria-label="' + lang.pasteArea + '"' + - ' aria-describedby="' + this.getContentElement( 'general', 'pasteMsg' ).domId + '"' + - ' aria-multiple="true"' + - '></iframe>' ); - - iframe.on( 'load', function( e ) - { - e.removeListener(); - var doc = iframe.getFrameDocument().$; - // Custom domain handling is needed after each document.open(). - doc.open(); - if ( isCustomDomain ) - doc.domain = document.domain; - doc.write( htmlToLoad ); - doc.close(); - }, this ); - - iframe.setStyles( - { - width : '346px', - height : '130px', - 'background-color' : 'white', - border : '1px solid black' - } ); - iframe.setCustomData( 'dialog', this ); - - var field = this.getContentElement( 'general', 'editing_area' ), - container = field.getElement(); - container.setHtml( '' ); - container.append( iframe ); - - // IE need a redirect on focus to make - // the cursor blinking inside iframe. (#5461) - if ( CKEDITOR.env.ie ) - { - var focusGrabber = CKEDITOR.dom.element.createFromHtml( '<span tabindex="-1" style="position:absolute;" role="presentation"></span>' ); - focusGrabber.on( 'focus', function() - { - iframe.$.contentWindow.focus(); - }); - container.append( focusGrabber ); - - // Override focus handler on field. - field.focus = function() - { - focusGrabber.focus(); - this.fire( 'focus' ); - }; - } - - field.getInputElement = function(){ return iframe; }; - - // Force container to scale in IE. - if ( CKEDITOR.env.ie ) - { - container.setStyle( 'display', 'block' ); - container.setStyle( 'height', ( iframe.$.offsetHeight + 2 ) + 'px' ); - } - }, - - onHide : function() - { - if ( CKEDITOR.env.ie ) - this.getParentEditor().document.getBody().$.contentEditable = 'true'; - }, - - onLoad : function() - { - if ( ( CKEDITOR.env.ie7Compat || CKEDITOR.env.ie6Compat ) && editor.lang.dir == 'rtl' ) - this.parts.contents.setStyle( 'overflow', 'hidden' ); - }, - - onOk : function() - { - var container = this.getContentElement( 'general', 'editing_area' ).getElement(), - iframe = container.getElementsByTag( 'iframe' ).getItem( 0 ), - editor = this.getParentEditor(), - html = iframe.$.contentWindow.document.body.innerHTML; - - setTimeout( function(){ - editor.fire( 'paste', { 'html' : html } ); - }, 0 ); - - }, - - contents : [ - { - id : 'general', - label : editor.lang.common.generalTab, - elements : [ - { - type : 'html', - id : 'securityMsg', - html : '<div style="white-space:normal;width:340px;">' + lang.securityMsg + '</div>' - }, - { - type : 'html', - id : 'pasteMsg', - html : '<div style="white-space:normal;width:340px;">'+lang.pasteMsg +'</div>' - }, - { - type : 'html', - id : 'editing_area', - style : 'width: 100%; height: 100%;', - html : '', - focus : function() - { - var win = this.getInputElement().$.contentWindow; - - // #3291 : JAWS needs the 500ms delay to detect that the editor iframe - // iframe is no longer editable. So that it will put the focus into the - // Paste from Word dialog's editable area instead. - setTimeout( function() - { - win.focus(); - }, 500 ); - } - } - ] - } - ] - }; -}); +/*
+Copyright (c) 2003-2012, 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( 1 );
+ 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 );
+ }
+ }
+ ]
+ }
+ ]
+ };
+});
diff --git a/_source/plugins/clipboard/plugin.js b/_source/plugins/clipboard/plugin.js index b679a3f..a285954 100644 --- a/_source/plugins/clipboard/plugin.js +++ b/_source/plugins/clipboard/plugin.js @@ -1,413 +1,479 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -/** - * @file Clipboard support - */ - -(function() -{ - // Tries to execute any of the paste, cut or copy commands in IE. Returns a - // boolean indicating that the operation succeeded. - var execIECommand = function( editor, command ) - { - var doc = editor.document, - body = doc.getBody(); - - var enabled = false; - var onExec = function() - { - enabled = true; - }; - - // 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 ); - } - catch( e ) - { - return false; - } - }; - - // A class that represents one of the cut or copy commands. - var cutCopyCmd = function( type ) - { - this.type = type; - this.canUndo = ( this.type == 'cut' ); // We can't undo copy to clipboard. - }; - - cutCopyCmd.prototype = - { - exec : function( editor, data ) - { - this.type == 'cut' && fixCut( editor ); - - var success = tryToCutCopy( editor, this.type ); - - if ( !success ) - alert( editor.lang.clipboard[ this.type + 'Error' ] ); // Show cutError or copyError. - - return success; - } - }; - - // Paste command. - var pasteCmd = - { - canUndo : false, - - exec : - CKEDITOR.env.ie ? - function( editor ) - { - // Prevent IE from pasting at the begining of the document. - editor.focus(); - - if ( !editor.document.getBody().fire( 'beforepaste' ) - && !execIECommand( editor, 'paste' ) ) - { - editor.fire( 'pasteDialog' ); - return false; - } - } - : - function( editor ) - { - try - { - if ( !editor.document.getBody().fire( 'beforepaste' ) - && !editor.document.$.execCommand( 'Paste', false, null ) ) - { - throw 0; - } - } - catch ( e ) - { - setTimeout( function() - { - editor.fire( 'pasteDialog' ); - }, 0 ); - return false; - } - } - }; - - // Listens for some clipboard related keystrokes, so they get customized. - var onKey = function( event ) - { - if ( this.mode != 'wysiwyg' ) - return; - - switch ( event.data.keyCode ) - { - // Paste - case CKEDITOR.CTRL + 86 : // CTRL+V - case CKEDITOR.SHIFT + 45 : // SHIFT+INS - - var body = this.document.getBody(); - - // Simulate 'beforepaste' event for all none-IEs. - if ( !CKEDITOR.env.ie && body.fire( 'beforepaste' ) ) - event.cancel(); - // Simulate 'paste' event for Opera/Firefox2. - else if ( CKEDITOR.env.opera - || CKEDITOR.env.gecko && CKEDITOR.env.version < 10900 ) - body.fire( 'paste' ); - return; - - // Cut - case CKEDITOR.CTRL + 88 : // CTRL+X - case CKEDITOR.SHIFT + 46 : // SHIFT+DEL - - // Save Undo snapshot. - var editor = this; - this.fire( 'saveSnapshot' ); // Save before paste - setTimeout( function() - { - editor.fire( 'saveSnapshot' ); // Save after paste - }, 0 ); - } - }; - - // Allow to peek clipboard content by redirecting the - // pasting content into a temporary bin and grab the content of it. - function getClipboardData( evt, mode, callback ) - { - var doc = this.document; - - // Avoid recursions on 'paste' event for IE. - if ( CKEDITOR.env.ie && doc.getById( 'cke_pastebin' ) ) - return; - - // If the browser supports it, get the data directly - if (mode == 'text' && evt.data && evt.data.$.clipboardData) - { - // evt.data.$.clipboardData.types contains all the flavours in Mac's Safari, but not on windows. - var plain = evt.data.$.clipboardData.getData( 'text/plain' ); - if (plain) - { - evt.data.preventDefault(); - callback( plain ); - return; - } - } - - var sel = this.getSelection(), - range = new CKEDITOR.dom.range( doc ); - - // Create container to paste into - var pastebin = new CKEDITOR.dom.element( mode == 'text' ? 'textarea' : CKEDITOR.env.webkit ? 'body' : 'div', doc ); - pastebin.setAttribute( 'id', 'cke_pastebin' ); - // Safari requires a filler node inside the div to have the content pasted into it. (#4882) - CKEDITOR.env.webkit && pastebin.append( doc.createText( '\xa0' ) ); - doc.getBody().append( pastebin ); - - pastebin.setStyles( - { - position : 'absolute', - // Position the bin exactly at the position of the selected element - // to avoid any subsequent document scroll. - top : sel.getStartElement().getDocumentPosition().y + 'px', - width : '1px', - height : '1px', - overflow : 'hidden' - }); - - // It's definitely a better user experience if we make the paste-bin pretty unnoticed - // by pulling it off the screen. - pastebin.setStyle( this.config.contentsLangDirection == 'ltr' ? 'left' : 'right', '-1000px' ); - - var bms = sel.createBookmarks(); - - // Turn off design mode temporarily before give focus to the paste bin. - if ( mode == 'text' ) - { - if ( CKEDITOR.env.ie ) - { - var ieRange = doc.getBody().$.createTextRange(); - ieRange.moveToElementText( pastebin.$ ); - ieRange.execCommand( 'Paste' ); - evt.data.preventDefault(); - } - else - { - doc.$.designMode = 'off'; - pastebin.$.focus(); - } - } - else - { - range.setStartAt( pastebin, CKEDITOR.POSITION_AFTER_START ); - range.setEndAt( pastebin, CKEDITOR.POSITION_BEFORE_END ); - range.select( true ); - } - - // Wait a while and grab the pasted contents - window.setTimeout( function() - { - mode == 'text' && !CKEDITOR.env.ie && ( doc.$.designMode = 'on' ); - pastebin.remove(); - - // Grab the HTML contents. - // We need to look for a apple style wrapper on webkit it also adds - // a div wrapper if you copy/paste the body of the editor. - // Remove hidden div and restore selection. - var bogusSpan; - pastebin = ( CKEDITOR.env.webkit - && ( bogusSpan = pastebin.getFirst() ) - && ( bogusSpan.is && bogusSpan.hasClass( 'Apple-style-span' ) ) ? - bogusSpan : pastebin ); - - sel.selectBookmarks( bms ); - callback( pastebin[ 'get' + ( mode == 'text' ? 'Value' : 'Html' ) ]() ); - }, 0 ); - } - - // Cutting off control type element in IE standards breaks the selection entirely. (#4881) - function fixCut( editor ) - { - if ( !CKEDITOR.env.ie || editor.document.$.compatMode == 'BackCompat' ) - return; - - var sel = editor.getSelection(); - var control; - if( ( sel.getType() == CKEDITOR.SELECTION_ELEMENT ) && ( control = sel.getSelectedElement() ) ) - { - var range = sel.getRanges()[ 0 ]; - var dummy = editor.document.createText( '' ); - dummy.insertBefore( control ); - range.setStartBefore( dummy ); - range.setEndAfter( control ); - sel.selectRanges( [ range ] ); - - // Clear up the fix if the paste wasn't succeeded. - setTimeout( function() - { - // Element still online? - if ( control.getParent() ) - { - dummy.remove(); - sel.selectElement( control ); - } - }, 0 ); - } - } - - // Register the plugin. - CKEDITOR.plugins.add( 'clipboard', - { - requires : [ 'dialog', 'htmldataprocessor' ], - init : function( editor ) - { - // Inserts processed data into the editor at the end of the - // events chain. - editor.on( 'paste', function( evt ) - { - var data = evt.data; - if ( data[ 'html' ] ) - editor.insertHtml( data[ 'html' ] ); - else if ( data[ 'text' ] ) - editor.insertText( data[ 'text' ] ); - - }, null, null, 1000 ); - - editor.on( 'pasteDialog', function( evt ) - { - setTimeout( function() - { - // Open default paste dialog. - editor.openDialog( 'paste' ); - }, 0 ); - }); - - function addButtonCommand( buttonName, commandName, command, ctxMenuOrder ) - { - var lang = editor.lang[ commandName ]; - - editor.addCommand( commandName, command ); - editor.ui.addButton( buttonName, - { - label : lang, - command : commandName - }); - - // If the "menu" plugin is loaded, register the menu item. - if ( editor.addMenuItems ) - { - editor.addMenuItem( commandName, - { - label : lang, - command : commandName, - group : 'clipboard', - order : ctxMenuOrder - }); - } - } - - addButtonCommand( 'Cut', 'cut', new cutCopyCmd( 'cut' ), 1 ); - addButtonCommand( 'Copy', 'copy', new cutCopyCmd( 'copy' ), 4 ); - addButtonCommand( 'Paste', 'paste', pasteCmd, 8 ); - - CKEDITOR.dialog.add( 'paste', CKEDITOR.getUrl( this.path + 'dialogs/paste.js' ) ); - - editor.on( 'key', onKey, editor ); - - var mode = editor.config.forcePasteAsPlainText ? 'text' : 'html'; - - // We'll be catching all pasted content in one line, regardless of whether the - // it's introduced by a document command execution (e.g. toolbar buttons) or - // user paste behaviors. (e.g. Ctrl-V) - editor.on( 'contentDom', function() - { - var body = editor.document.getBody(); - body.on( ( (mode == 'text' && CKEDITOR.env.ie) || CKEDITOR.env.webkit ) ? 'paste' : 'beforepaste', - function( evt ) - { - if ( depressBeforeEvent ) - return; - - getClipboardData.call( editor, evt, mode, function ( data ) - { - // The very last guard to make sure the - // paste has successfully happened. - if ( !data ) - return; - - var dataTransfer = {}; - dataTransfer[ mode ] = data; - editor.fire( 'paste', dataTransfer ); - } ); - }); - - body.on( 'beforecut', function() { !depressBeforeEvent && fixCut( editor ); } ); - }); - - // If the "contextmenu" plugin is loaded, register the listeners. - if ( editor.contextMenu ) - { - var depressBeforeEvent; - function stateFromNamedCommand( command ) - { - // IE Bug: queryCommandEnabled('paste') fires also 'beforepaste(copy/cut)', - // guard to distinguish from the ordinary sources( either - // keyboard paste or execCommand ) (#4874). - CKEDITOR.env.ie && ( depressBeforeEvent = 1 ); - - var retval = editor.document.$.queryCommandEnabled( command ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED; - depressBeforeEvent = 0; - return retval; - } - - editor.contextMenu.addListener( function() - { - return { - cut : stateFromNamedCommand( 'Cut' ), - - // Browser bug: 'Cut' has the correct states for both Copy and Cut. - copy : stateFromNamedCommand( 'Cut' ), - paste : CKEDITOR.env.webkit ? CKEDITOR.TRISTATE_OFF : stateFromNamedCommand( 'Paste' ) - }; - }); - } - } - }); -})(); - -/** - * Fired when a clipboard operation is about to be taken into the editor. - * Listeners can manipulate the data to be pasted before having it effectively - * inserted into the document. - * @name CKEDITOR.editor#paste - * @since 3.1 - * @event - * @param {String} [data.html] The HTML data to be pasted. If not available, e.data.text will be defined. - * @param {String} [data.text] The plain text data to be pasted, available when plain text operations are to used. If not available, e.data.html will be defined. - */ +/*
+Copyright (c) 2003-2012, 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 = false;
+ var onExec = function()
+ {
+ enabled = true;
+ };
+
+ // 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 'paste' event for Opera/Firefox2.
+ 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()
+ {
+ // Restore properly the document focus. (#5684, #8849)
+ editor.document.getBody().focus();
+
+ 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 );
+
+ // IE7: selection must go before removing paste. (#8691)
+ sel.selectBookmarks( bms );
+ pastebin.remove();
+ 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();
+
+ // Intercept the paste before it actually takes place.
+ body.on( !CKEDITOR.env.ie ? 'paste' : 'beforepaste', function( evt )
+ {
+ if ( depressBeforeEvent )
+ return;
+
+ // Dismiss the (wrong) 'beforepaste' event fired on toolbar menu open.
+ var domEvent = evt.data && evt.data.$;
+ if ( CKEDITOR.env.ie && domEvent && !domEvent.ctrlKey )
+ 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 );
+ } );
+ });
+
+ if ( CKEDITOR.env.ie )
+ {
+ // Dismiss the (wrong) 'beforepaste' event fired on context menu open. (#7953)
+ body.on( 'contextmenu', function()
+ {
+ depressBeforeEvent = 1;
+ // Important: The following timeout will be called only after menu closed.
+ setTimeout( function() { depressBeforeEvent = 0; }, 0 );
+ } );
+
+ // Handle IE's late coming "paste" event when pasting from
+ // browser toolbar/context menu.
+ body.on( 'paste', function( evt )
+ {
+ if ( !editor.document.getById( 'cke_pastebin' ) )
+ {
+ // Prevent native paste.
+ evt.data.preventDefault();
+
+ depressBeforeEvent = 0;
+ // Resort to the paste command.
+ pasteCmd.exec( editor );
+ }
+ } );
+ }
+
+ 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
+ */
diff --git a/_source/plugins/colorbutton/plugin.js b/_source/plugins/colorbutton/plugin.js index dd127f9..7cc0604 100644 --- a/_source/plugins/colorbutton/plugin.js +++ b/_source/plugins/colorbutton/plugin.js @@ -1,248 +1,300 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -CKEDITOR.plugins.add( 'colorbutton', -{ - requires : [ 'panelbutton', 'floatpanel', 'styles' ], - - init : function( editor ) - { - var config = editor.config, - lang = editor.lang.colorButton; - - var clickFn; - - if ( !CKEDITOR.env.hc ) - { - addButton( 'TextColor', 'fore', lang.textColorTitle ); - addButton( 'BGColor', 'back', lang.bgColorTitle ); - } - - function addButton( name, type, title ) - { - 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 ) ); - - var keys = block.keys; - keys[ 39 ] = 'next'; // ARROW-RIGHT - keys[ 40 ] = 'next'; // ARROW-DOWN - keys[ 9 ] = 'next'; // TAB - keys[ 37 ] = 'prev'; // ARROW-LEFT - keys[ 38 ] = 'prev'; // ARROW-UP - keys[ CKEDITOR.SHIFT + 9 ] = 'prev'; // SHIFT + TAB - keys[ 32 ] = 'click'; // SPACE - } - }); - } - - - function renderColors( panel, type ) - { - var output = [], - colors = config.colorButton_colors.split( ',' ), - total = colors.length + ( config.colorButton_enableMore ? 2 : 1 ); - - var clickFn = CKEDITOR.tools.addFunction( function( color, type ) - { - if ( color == '?' ) - { - var applyColorStyle = arguments.callee; - function onColorDialogClose( evt ) - { - this.removeListener( 'ok', onColorDialogClose ); - this.removeListener( 'cancel', onColorDialogClose ); - - evt.name == 'ok' && applyColorStyle( this.getContentElement( 'picker', 'selectedColor' ).getValue(), type ); - } - - editor.openDialog( 'colordialog', function() - { - this.on( 'ok', onColorDialogClose ); - this.on( 'cancel', onColorDialogClose ); - } ); - - return; - } - - editor.focus(); - - panel.hide(); - - - editor.fire( 'saveSnapshot' ); - - // Clean up any conflicting style within the range. - new CKEDITOR.style( config['colorButton_' + type + 'Style'], { color : 'inherit' } ).remove( editor.document ); - - if ( color ) - { - var colorStyle = config['colorButton_' + type + 'Style']; - - colorStyle.childRule = type == 'back' ? - // It's better to apply background color as the innermost style. (#3599) - function(){ return false; } : - // Fore color style must be applied inside links instead of around it. - function( element ){ return element.getName() != 'a'; }; - - 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" style="background-color:#000"></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 ) - { - 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>' ); // It is later in the code. - } - - output.push( '</tr></table>' ); - - return output.join( '' ); - } - } -}); - -/** - * Whether to enable the "More Colors..." button in the color selectors. - * @default false - * @type Boolean - * @example - * config.colorButton_enableMore = false; - */ -CKEDITOR.config.colorButton_enableMore = true; - -/** - * Defines the colors to be displayed in the color selectors. It's a string - * containing the hexadecimal notation for HTML colors, without the "#" prefix. - * - * Since 3.3: A name may be optionally defined by prefixing the entries with the - * name and the slash character. For example, "FontColor1/FF9900" will be - * displayed as the color #FF9900 in the selector, but will be outputted as "FontColor1". - * @type String - * @default '000,800000,8B4513,2F4F4F,008080,000080,4B0082,696969,B22222,A52A2A,DAA520,006400,40E0D0,0000CD,800080,808080,F00,FF8C00,FFD700,008000,0FF,00F,EE82EE,A9A9A9,FFA07A,FFA500,FFFF00,00FF00,AFEEEE,ADD8E6,DDA0DD,D3D3D3,FFF0F5,FAEBD7,FFFFE0,F0FFF0,F0FFFF,F0F8FF,E6E6FA,FFF' - * @example - * // Brazil colors only. - * config.colorButton_colors = '00923E,F8C100,28166F'; - * @example - * config.colorButton_colors = 'FontColor1/FF9900,FontColor2/0066CC,FontColor3/F00' - */ -CKEDITOR.config.colorButton_colors = - '000,800000,8B4513,2F4F4F,008080,000080,4B0082,696969,' + - 'B22222,A52A2A,DAA520,006400,40E0D0,0000CD,800080,808080,' + - 'F00,FF8C00,FFD700,008000,0FF,00F,EE82EE,A9A9A9,' + - 'FFA07A,FFA500,FFFF00,00FF00,AFEEEE,ADD8E6,DDA0DD,D3D3D3,' + - 'FFF0F5,FAEBD7,FFFFE0,F0FFF0,F0FFFF,F0F8FF,E6E6FA,FFF'; - -/** - * Holds the style definition to be used to apply the text foreground color. - * @type Object - * @example - * // This is basically the default setting value. - * config.colorButton_foreStyle = - * { - * element : 'span', - * styles : { 'color' : '#(color)' } - * }; - */ -CKEDITOR.config.colorButton_foreStyle = - { - element : 'span', - styles : { 'color' : '#(color)' }, - overrides : [ { element : 'font', attributes : { 'color' : null } } ] - }; - -/** - * Holds the style definition to be used to apply the text background color. - * @type Object - * @example - * // This is basically the default setting value. - * config.colorButton_backStyle = - * { - * element : 'span', - * styles : { 'background-color' : '#(color)' } - * }; - */ -CKEDITOR.config.colorButton_backStyle = - { - element : 'span', - styles : { 'background-color' : '#(color)' } - }; +/*
+Copyright (c) 2003-2012, 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( ',' );
+
+ 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">' +
+ '<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">' +
+ '<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">',
+ 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)' }
+ };
diff --git a/_source/plugins/colordialog/dialogs/colordialog.js b/_source/plugins/colordialog/dialogs/colordialog.js index 9eb2261..a8e83f5 100644 --- a/_source/plugins/colordialog/dialogs/colordialog.js +++ b/_source/plugins/colordialog/dialogs/colordialog.js @@ -1,339 +1,387 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -CKEDITOR.dialog.add( 'colordialog', function( editor ) - { - // Define some shorthands. - var $el = CKEDITOR.dom.element, - $doc = CKEDITOR.document, - $tools = CKEDITOR.tools, - lang = editor.lang.colordialog; - - // Reference the dialog. - var dialog; - - function spacer() - { - return { - type : 'html', - html : ' ' - }; - } - - function clearSelected() - { - $doc.getById( selHiColorId ).removeStyle( 'background-color' ); - dialog.getContentElement( 'picker', 'selectedColor' ).setValue( '' ); - } - - function updateSelected( evt ) - { - if ( ! (evt instanceof CKEDITOR.dom.event ) ) - evt = new CKEDITOR.dom.event( evt ); - - var target = evt.getTarget(), - color; - - if ( target.getName() == 'a' && ( color = target.getChild( 0 ).getHtml() ) ) - dialog.getContentElement( 'picker', 'selectedColor' ).setValue( color ); - } - - function updateHighlight( event ) - { - if ( ! (event instanceof CKEDITOR.dom.event ) ) - event = event.data; - - var target = event.getTarget(), - color; - - if ( target.getName() == 'a' && ( color = target.getChild( 0 ).getHtml() ) ) - { - $doc.getById( hicolorId ).setStyle( 'background-color', color ); - $doc.getById( hicolorTextId ).setHtml( color ); - } - } - - function clearHighlight() - { - $doc.getById( hicolorId ).removeStyle( 'background-color' ); - $doc.getById( hicolorTextId ).setHtml( ' ' ); - } - - var onMouseout = $tools.addFunction( clearHighlight ); - - var onClick = updateSelected, - onClickHandler = CKEDITOR.tools.addFunction( onClick ); - - var 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(); - var 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> </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 numbering = function( id ) - { - return id + CKEDITOR.tools.getNextNumber(); - }, - hicolorId = numbering( 'hicolor' ), - hicolorTextId = numbering( 'hicolortext' ), - selHiColorId = numbering( 'selhicolor' ); - - 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="color_table_label" onmouseout="CKEDITOR.tools.callFunction( ' + onMouseout + ' );">' + table.getHtml() + '</table>' + - '<span id="color_table_label" class="cke_voice_label">' + lang.options +'</span>', - onLoad : function() - { - var table = CKEDITOR.document.getById( this.domId ); - table.on( 'mouseover', updateHighlight ); - }, - 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 + '"> </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 - } - ] - } - ] - } - ] - } - ] - }; - } - ); +/*
+Copyright (c) 2003-2012, 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,
+ lang = editor.lang.colordialog;
+
+ // Reference the dialog.
+ var dialog;
+
+ var spacer =
+ {
+ type : 'html',
+ html : ' '
+ };
+
+ var selected;
+
+ function clearSelected()
+ {
+ $doc.getById( selHiColorId ).removeStyle( 'background-color' );
+ dialog.getContentElement( 'picker', 'selectedColor' ).setValue( '' );
+ selected && selected.removeAttribute( 'aria-selected' );
+ selected = null;
+ }
+
+ function updateSelected( evt )
+ {
+ var target = evt.data.getTarget(),
+ color;
+
+ if ( target.getName() == 'td' &&
+ ( color = target.getChild( 0 ).getHtml() ) )
+ {
+ selected = target;
+ selected.setAttribute( 'aria-selected', true );
+ dialog.getContentElement( 'picker', 'selectedColor' ).setValue( color );
+ }
+ }
+
+ // Basing black-white decision off of luma scheme using the Rec. 709 version
+ function whiteOrBlack( color )
+ {
+ color = color.replace( /^#/, '' );
+ for ( var i = 0, rgb = []; i <= 2; i++ )
+ rgb[i] = parseInt( color.substr( i * 2, 2 ), 16 );
+ var luma = (0.2126 * rgb[0]) + (0.7152 * rgb[1]) + (0.0722 * rgb[2]);
+ return '#' + ( luma >= 165 ? '000' : 'fff' );
+ }
+
+ // Distinguish focused and hover states.
+ var focused, hovered;
+
+ // Apply highlight style.
+ function updateHighlight( event )
+ {
+ // Convert to event.
+ !event.name && ( event = new CKEDITOR.event( event ) );
+
+ var isFocus = !(/mouse/).test( event.name ),
+ target = event.data.getTarget(),
+ color;
+
+ if ( target.getName() == 'td' && ( color = target.getChild( 0 ).getHtml() ) )
+ {
+ removeHighlight( event );
+
+ isFocus ? focused = target : hovered = target;
+
+ // Apply outline style to show focus.
+ if ( isFocus )
+ {
+ target.setStyle( 'border-color', whiteOrBlack( color ) );
+ target.setStyle( 'border-style', 'dotted' );
+ }
+
+ $doc.getById( hicolorId ).setStyle( 'background-color', color );
+ $doc.getById( hicolorTextId ).setHtml( color );
+ }
+ }
+
+ function clearHighlight()
+ {
+ var color = focused.getChild( 0 ).getHtml();
+ focused.setStyle( 'border-color', color );
+ focused.setStyle( 'border-style', 'solid' );
+ $doc.getById( hicolorId ).removeStyle( 'background-color' );
+ $doc.getById( hicolorTextId ).setHtml( ' ' );
+ focused = null;
+ }
+
+ // Remove previously focused style.
+ function removeHighlight( event )
+ {
+ var isFocus = !(/mouse/).test( event.name ),
+ target = isFocus && focused;
+
+ if ( target )
+ {
+ var color = target.getChild( 0 ).getHtml();
+ target.setStyle( 'border-color', color );
+ target.setStyle( 'border-style', 'solid' );
+ }
+
+ if ( ! ( focused || hovered ) )
+ {
+ $doc.getById( hicolorId ).removeStyle( 'background-color' );
+ $doc.getById( hicolorTextId ).setHtml( ' ' );
+ }
+ }
+
+ function onKeyStrokes( evt )
+ {
+ var domEvt = evt.data;
+
+ var element = domEvt.getTarget();
+ var relative, nodeToMove;
+ var keystroke = domEvt.getKeystroke(),
+ rtl = editor.lang.dir == 'rtl';
+
+ switch ( keystroke )
+ {
+ // UP-ARROW
+ case 38 :
+ // relative is TR
+ if ( ( relative = element.getParent().getPrevious() ) )
+ {
+ nodeToMove = relative.getChild( [ element.getIndex() ] );
+ nodeToMove.focus();
+ }
+ domEvt.preventDefault();
+ break;
+ // DOWN-ARROW
+ case 40 :
+ // relative is TR
+ if ( ( relative = element.getParent().getNext() ) )
+ {
+ nodeToMove = relative.getChild( [ element.getIndex() ] );
+ if ( nodeToMove && nodeToMove.type == 1 )
+ {
+ nodeToMove.focus();
+ }
+ }
+ domEvt.preventDefault();
+ break;
+
+ // SPACE
+ // ENTER
+ case 32 :
+ case 13 :
+ updateSelected( evt );
+ domEvt.preventDefault();
+ break;
+
+ // RIGHT-ARROW
+ case rtl ? 37 : 39 :
+ // relative is TD
+ if ( ( nodeToMove = element.getNext() ) )
+ {
+ if ( nodeToMove.type == 1 )
+ {
+ nodeToMove.focus();
+ domEvt.preventDefault( true );
+ }
+ }
+ // relative is TR
+ else if ( ( relative = element.getParent().getNext() ) )
+ {
+ nodeToMove = relative.getChild( [ 0 ] );
+ if ( nodeToMove && nodeToMove.type == 1 )
+ {
+ nodeToMove.focus();
+ domEvt.preventDefault( true );
+ }
+ }
+ break;
+
+ // LEFT-ARROW
+ case rtl ? 39 : 37 :
+ // relative is TD
+ if ( ( nodeToMove = element.getPrevious() ) )
+ {
+ nodeToMove.focus();
+ domEvt.preventDefault( true );
+ }
+ // relative is TR
+ else if ( ( relative = element.getParent().getPrevious() ) )
+ {
+ nodeToMove = relative.getLast();
+ nodeToMove.focus();
+ domEvt.preventDefault( true );
+ }
+ break;
+ default :
+ // Do not stop not handled events.
+ return;
+ }
+ }
+
+ function createColorTable()
+ {
+ table = CKEDITOR.dom.element.createFromHtml
+ (
+ '<table tabIndex="-1" aria-label="' + lang.options + '"' +
+ ' role="grid" style="border-collapse:separate;" cellspacing="0">' +
+ '<caption class="cke_voice_label">' + lang.options + '</caption>' +
+ '<tbody role="presentation"></tbody></table>'
+ );
+
+ table.on( 'mouseover', updateHighlight );
+ table.on( 'mouseout', removeHighlight );
+
+ // 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 = new $el( table.$.insertRow( -1 ) );
+ row.setAttribute( 'role', 'row' );
+
+ 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.setAttribute( 'tabIndex', -1 );
+ cell.setAttribute( 'role', 'gridcell' );
+
+ cell.on( 'keydown', onKeyStrokes );
+ cell.on( 'click', updateSelected );
+ cell.on( 'focus', updateHighlight );
+ cell.on( 'blur', removeHighlight );
+
+ cell.setStyle( 'background-color', color );
+ cell.setStyle( 'border', '1px solid ' + color );
+
+ cell.setStyle( 'width', '14px' );
+ cell.setStyle( 'height', '14px' );
+
+ var colorLabel = numbering( 'color_table_cell' );
+ cell.setAttribute( 'aria-labelledby',colorLabel );
+ cell.append( CKEDITOR.dom.element.createFromHtml( '<span id="' + colorLabel + '" class="cke_voice_label">' + color + '</span>', CKEDITOR.document ) );
+ }
+
+ appendColorRow( 0, 0 );
+ appendColorRow( 3, 0 );
+ appendColorRow( 0, 3 );
+ appendColorRow( 3, 3 );
+
+ // Create the last row.
+ var oRow = new $el( table.$.insertRow( -1 ) ) ;
+ oRow.setAttribute( 'role', 'row' );
+
+ // 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 numbering = function( id )
+ {
+ return CKEDITOR.tools.getNextId() + '_' + id;
+ },
+ hicolorId = numbering( 'hicolor' ),
+ hicolorTextId = numbering( 'hicolortext' ),
+ selHiColorId = numbering( 'selhicolor' ),
+ table;
+
+ createColorTable();
+
+ return {
+ title : lang.title,
+ minWidth : 360,
+ minHeight : 220,
+ onLoad : function()
+ {
+ // Update reference.
+ dialog = this;
+ },
+ onHide : function()
+ {
+ clearSelected();
+ clearHighlight();
+ },
+ contents : [
+ {
+ id : 'picker',
+ label : lang.title,
+ accessKey : 'I',
+ elements :
+ [
+ {
+ type : 'hbox',
+ padding : 0,
+ widths : [ '70%', '10%', '30%' ],
+ children :
+ [
+ {
+ type : 'html',
+ html : '<div></div>',
+ onLoad : function()
+ {
+ CKEDITOR.document.getById( this.domId ).append( table );
+ },
+ focus : function()
+ {
+ // Restore the previously focused cell,
+ // otherwise put the initial focus on the first table cell.
+ ( focused || this.getElement().getElementsByTag( 'td' ).getItem( 0 ) ).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 + '"> </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
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ };
+ }
+ );
diff --git a/_source/plugins/colordialog/plugin.js b/_source/plugins/colordialog/plugin.js index 7006d68..5687740 100644 --- a/_source/plugins/colordialog/plugin.js +++ b/_source/plugins/colordialog/plugin.js @@ -1,13 +1,15 @@ -( function()
+/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.colordialog =
{
- CKEDITOR.plugins.colordialog =
+ init : function( editor )
{
- init : function( editor )
- {
- editor.addCommand( 'colordialog', new CKEDITOR.dialogCommand( 'colordialog' ) );
- CKEDITOR.dialog.add( 'colordialog', this.path + 'dialogs/colordialog.js' );
- }
- };
+ editor.addCommand( 'colordialog', new CKEDITOR.dialogCommand( 'colordialog' ) );
+ CKEDITOR.dialog.add( 'colordialog', this.path + 'dialogs/colordialog.js' );
+ }
+};
- CKEDITOR.plugins.add( 'colordialog', CKEDITOR.plugins.colordialog );
-} )();
+CKEDITOR.plugins.add( 'colordialog', CKEDITOR.plugins.colordialog );
diff --git a/_source/plugins/contextmenu/plugin.js b/_source/plugins/contextmenu/plugin.js index 4516167..5d95080 100644 --- a/_source/plugins/contextmenu/plugin.js +++ b/_source/plugins/contextmenu/plugin.js @@ -1,276 +1,179 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -CKEDITOR.plugins.add( 'contextmenu', -{ - requires : [ 'menu' ], - - beforeInit : function( editor ) - { - editor.contextMenu = new CKEDITOR.plugins.contextMenu( editor ); - - editor.addCommand( 'contextMenu', - { - exec : function() - { - editor.contextMenu.show( editor.document.getBody() ); - } - }); - } -}); - -CKEDITOR.plugins.contextMenu = CKEDITOR.tools.createClass( -{ - $ : function( editor ) - { - this.id = 'cke_' + CKEDITOR.tools.getNextNumber(); - this.editor = editor; - this._.listeners = []; - this._.functionId = CKEDITOR.tools.addFunction( function( commandName ) - { - this._.panel.hide(); - editor.focus(); - editor.execCommand( commandName ); - }, - this); - - this.definition = - { - panel: - { - className : editor.skinClass + ' cke_contextmenu', - attributes : - { - 'aria-label' : editor.lang.contextmenu.options - } - } - }; - }, - - _ : - { - onMenu : function( offsetParent, corner, offsetX, offsetY ) - { - var menu = this._.menu, - editor = this.editor; - - if ( menu ) - { - menu.hide(); - menu.removeAll(); - } - else - { - menu = this._.menu = new CKEDITOR.menu( editor, this.definition ); - menu.onClick = CKEDITOR.tools.bind( function( item ) - { - menu.hide(); - - if ( item.onClick ) - item.onClick(); - else if ( item.command ) - editor.execCommand( item.command ); - - }, this ); - - menu.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(); - editor.focus(); - } - return false; - }; - } - - var listeners = this._.listeners, - includedItems = []; - - var selection = this.editor.getSelection(), - element = selection && selection.getStartElement(); - - menu.onHide = CKEDITOR.tools.bind( function() - { - menu.onHide = null; - - if ( CKEDITOR.env.ie ) - { - var selection = editor.getSelection(); - selection && selection.unlock(); - } - - this.onHide && this.onHide(); - }, - this ); - - // Call all listeners, filling the list of items to be displayed. - for ( var i = 0 ; i < listeners.length ; i++ ) - { - var listenerItems = listeners[ i ]( element, selection ); - - if ( listenerItems ) - { - for ( var itemName in listenerItems ) - { - var item = this.editor.getMenuItem( itemName ); - - if ( item ) - { - item.state = listenerItems[ itemName ]; - menu.add( item ); - } - } - } - } - - // Don't show context menu with zero items. - menu.items.length && menu.show( offsetParent, corner || ( editor.lang.dir == 'rtl' ? 2 : 1 ), offsetX, offsetY ); - } - }, - - 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 ) - { - 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.show( offsetParent, null, offsetX, offsetY ); - }, - 0, this ); - }, - this ); - - 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 ); - } - }, - - addListener : function( listenerFn ) - { - this._.listeners.push( listenerFn ); - }, - - show : function( offsetParent, corner, offsetX, offsetY ) - { - this.editor.focus(); - - // Selection will be unavailable after context menu shows up - // in IE, lock it now. - if ( CKEDITOR.env.ie ) - { - var selection = this.editor.getSelection(); - selection && selection.lock(); - } - - this._.onMenu( offsetParent || CKEDITOR.document.getDocumentElement(), corner, offsetX || 0, offsetY || 0 ); - } - } -}); - -/** - * Whether to show the browser native context menu when the CTRL or the - * META (Mac) key is pressed while opening the context menu. - * @name CKEDITOR.config.browserContextMenuOnCtrl - * @since 3.0.2 - * @type Boolean - * @default true - * @example - * config.browserContextMenuOnCtrl = false; - */ +/*
+Copyright (c) 2003-2012, 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;
+ */
diff --git a/_source/plugins/devtools/lang/_translationstatus.txt b/_source/plugins/devtools/lang/_translationstatus.txt new file mode 100644 index 0000000..fc6f642 --- /dev/null +++ b/_source/plugins/devtools/lang/_translationstatus.txt @@ -0,0 +1,27 @@ +Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+
+bg.js Found: 5 Missing: 0
+cs.js Found: 5 Missing: 0
+cy.js Found: 5 Missing: 0
+da.js Found: 5 Missing: 0
+de.js Found: 5 Missing: 0
+el.js Found: 5 Missing: 0
+eo.js Found: 5 Missing: 0
+et.js Found: 5 Missing: 0
+fa.js Found: 5 Missing: 0
+fi.js Found: 5 Missing: 0
+fr.js Found: 5 Missing: 0
+gu.js Found: 5 Missing: 0
+he.js Found: 5 Missing: 0
+hr.js Found: 5 Missing: 0
+it.js Found: 5 Missing: 0
+nb.js Found: 5 Missing: 0
+nl.js Found: 5 Missing: 0
+no.js Found: 5 Missing: 0
+pl.js Found: 5 Missing: 0
+tr.js Found: 5 Missing: 0
+ug.js Found: 5 Missing: 0
+uk.js Found: 5 Missing: 0
+vi.js Found: 5 Missing: 0
+zh-cn.js Found: 5 Missing: 0
diff --git a/_source/plugins/devtools/lang/bg.js b/_source/plugins/devtools/lang/bg.js new file mode 100644 index 0000000..c524ded --- /dev/null +++ b/_source/plugins/devtools/lang/bg.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'devtools', 'bg',
+{
+ devTools :
+ {
+ title : 'Информация за елемента',
+ dialogName : 'Име на диалоговия прозорец',
+ tabName : 'Име на таб',
+ elementId : 'ID на елемента',
+ elementType : 'Тип на елемента'
+ }
+});
diff --git a/_source/plugins/devtools/lang/cs.js b/_source/plugins/devtools/lang/cs.js new file mode 100644 index 0000000..41050c8 --- /dev/null +++ b/_source/plugins/devtools/lang/cs.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'devtools', 'cs',
+{
+ devTools :
+ {
+ title : 'Informace o prvku',
+ dialogName : 'Název dialogového okna',
+ tabName : 'Název karty',
+ elementId : 'ID prvku',
+ elementType : 'Typ prvku'
+ }
+});
diff --git a/_source/plugins/devtools/lang/cy.js b/_source/plugins/devtools/lang/cy.js new file mode 100644 index 0000000..eb65ec7 --- /dev/null +++ b/_source/plugins/devtools/lang/cy.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'devtools', 'cy',
+{
+ devTools :
+ {
+ title : 'Gwybodaeth am yr Elfen',
+ dialogName : 'Enw ffenestr y deialog',
+ tabName : 'Enw\'r tab',
+ elementId : 'ID yr Elfen',
+ elementType : 'Math yr elfen'
+ }
+});
diff --git a/_source/plugins/devtools/lang/da.js b/_source/plugins/devtools/lang/da.js new file mode 100644 index 0000000..90279c3 --- /dev/null +++ b/_source/plugins/devtools/lang/da.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'devtools', 'da',
+{
+ devTools :
+ {
+ title : 'Information på elementet',
+ dialogName : 'Dialogboks',
+ tabName : 'Tab beskrivelse',
+ elementId : 'ID på element',
+ elementType : 'Type af element'
+ }
+});
diff --git a/_source/plugins/devtools/lang/de.js b/_source/plugins/devtools/lang/de.js new file mode 100644 index 0000000..c958ef9 --- /dev/null +++ b/_source/plugins/devtools/lang/de.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'devtools', 'de',
+{
+ devTools :
+ {
+ title : 'Elementinformation',
+ dialogName : 'Dialogfenstername',
+ tabName : 'Reitername',
+ elementId : 'Element ID',
+ elementType : 'Elementtyp'
+ }
+});
diff --git a/_source/plugins/devtools/lang/el.js b/_source/plugins/devtools/lang/el.js new file mode 100644 index 0000000..9e7223c --- /dev/null +++ b/_source/plugins/devtools/lang/el.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'devtools', 'el',
+{
+ devTools :
+ {
+ title : 'Πληροφορίες Στοιχείου',
+ dialogName : 'Όνομα παραθύρου διαλόγου',
+ tabName : 'Όνομα καρτέλας',
+ elementId : 'ID Στοιχείου',
+ elementType : 'Τύπος στοιχείου'
+ }
+});
diff --git a/_source/plugins/devtools/lang/en.js b/_source/plugins/devtools/lang/en.js new file mode 100644 index 0000000..ec2771a --- /dev/null +++ b/_source/plugins/devtools/lang/en.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, 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'
+ }
+});
diff --git a/_source/plugins/devtools/lang/eo.js b/_source/plugins/devtools/lang/eo.js new file mode 100644 index 0000000..15bceba --- /dev/null +++ b/_source/plugins/devtools/lang/eo.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'devtools', 'eo',
+{
+ devTools :
+ {
+ title : 'Informo pri la elemento',
+ dialogName : 'Nomo de la dialogfenestro',
+ tabName : 'Langetnomo',
+ elementId : 'ID de la elemento',
+ elementType : 'Tipo de la elemento'
+ }
+});
diff --git a/_source/plugins/devtools/lang/et.js b/_source/plugins/devtools/lang/et.js new file mode 100644 index 0000000..6ca4ef9 --- /dev/null +++ b/_source/plugins/devtools/lang/et.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'devtools', 'et',
+{
+ devTools :
+ {
+ title : 'Elemendi andmed',
+ dialogName : 'Dialoogiakna nimi',
+ tabName : 'Saki nimi',
+ elementId : 'Elemendi ID',
+ elementType : 'Elemendi liik'
+ }
+});
diff --git a/_source/plugins/devtools/lang/fa.js b/_source/plugins/devtools/lang/fa.js new file mode 100644 index 0000000..4184d14 --- /dev/null +++ b/_source/plugins/devtools/lang/fa.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'devtools', 'fa',
+{
+ devTools :
+ {
+ title : 'اطلاعات عنصر',
+ dialogName : 'نام پنجره محاورهای',
+ tabName : 'نام برگه',
+ elementId : 'ID عنصر',
+ elementType : 'نوع عنصر'
+ }
+});
diff --git a/_source/plugins/devtools/lang/fi.js b/_source/plugins/devtools/lang/fi.js new file mode 100644 index 0000000..c415660 --- /dev/null +++ b/_source/plugins/devtools/lang/fi.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'devtools', 'fi',
+{
+ devTools :
+ {
+ title : 'Elementin tiedot',
+ dialogName : 'Dialogi-ikkunan nimi',
+ tabName : 'Välilehden nimi',
+ elementId : 'Elementin ID',
+ elementType : 'Elementin tyyppi'
+ }
+});
diff --git a/_source/plugins/devtools/lang/fr.js b/_source/plugins/devtools/lang/fr.js new file mode 100644 index 0000000..2eab122 --- /dev/null +++ b/_source/plugins/devtools/lang/fr.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'devtools', 'fr',
+{
+ devTools :
+ {
+ title : 'Information sur l\'élément',
+ dialogName : 'Nom de la fenêtre de dialogue',
+ tabName : 'Nom de l\'onglet',
+ elementId : 'ID de l\'élément',
+ elementType : 'Type de l\'élément'
+ }
+});
diff --git a/_source/plugins/devtools/lang/gu.js b/_source/plugins/devtools/lang/gu.js new file mode 100644 index 0000000..cea9282 --- /dev/null +++ b/_source/plugins/devtools/lang/gu.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'devtools', 'gu',
+{
+ devTools :
+ {
+ title : 'પ્રાથમિક માહિતી',
+ dialogName : 'વિન્ડોનું નામ',
+ tabName : 'ટેબનું નામ',
+ elementId : 'પ્રાથમિક આઈડી',
+ elementType : 'પ્રાથમિક પ્રકાર'
+ }
+});
diff --git a/_source/plugins/devtools/lang/he.js b/_source/plugins/devtools/lang/he.js new file mode 100644 index 0000000..b61f3c7 --- /dev/null +++ b/_source/plugins/devtools/lang/he.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'devtools', 'he',
+{
+ devTools :
+ {
+ title : 'מידע על האלמנט',
+ dialogName : 'שם הדיאלוג',
+ tabName : 'שם הטאב',
+ elementId : 'ID של האלמנט',
+ elementType : 'סוג האלמנט'
+ }
+});
diff --git a/_source/plugins/devtools/lang/hr.js b/_source/plugins/devtools/lang/hr.js new file mode 100644 index 0000000..a4e5d1b --- /dev/null +++ b/_source/plugins/devtools/lang/hr.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'devtools', 'hr',
+{
+ devTools :
+ {
+ title : 'Informacije elementa',
+ dialogName : 'Naziv prozora za dijalog',
+ tabName : 'Naziva jahača',
+ elementId : 'ID elementa',
+ elementType : 'Vrsta elementa'
+ }
+});
diff --git a/_source/plugins/devtools/lang/it.js b/_source/plugins/devtools/lang/it.js new file mode 100644 index 0000000..27c01ed --- /dev/null +++ b/_source/plugins/devtools/lang/it.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'devtools', 'it',
+{
+ devTools :
+ {
+ title : 'Informazioni elemento',
+ dialogName : 'Nome finestra di dialogo',
+ tabName : 'Nome Tab',
+ elementId : 'ID Elemento',
+ elementType : 'Tipo elemento'
+ }
+});
diff --git a/_source/plugins/devtools/lang/nb.js b/_source/plugins/devtools/lang/nb.js new file mode 100644 index 0000000..1df7af1 --- /dev/null +++ b/_source/plugins/devtools/lang/nb.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'devtools', 'nb',
+{
+ devTools :
+ {
+ title : 'Elementinformasjon',
+ dialogName : 'Navn på dialogvindu',
+ tabName : 'Navn på fane',
+ elementId : 'Element-ID',
+ elementType : 'Elementtype'
+ }
+});
diff --git a/_source/plugins/devtools/lang/nl.js b/_source/plugins/devtools/lang/nl.js new file mode 100644 index 0000000..2c674fc --- /dev/null +++ b/_source/plugins/devtools/lang/nl.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'devtools', 'nl',
+{
+ devTools :
+ {
+ title : 'Elementinformatie',
+ dialogName : 'Naam dialoogvenster',
+ tabName : 'Tabnaam',
+ elementId : 'Element ID',
+ elementType : 'Elementtype'
+ }
+});
diff --git a/_source/plugins/devtools/lang/no.js b/_source/plugins/devtools/lang/no.js new file mode 100644 index 0000000..0d51dbf --- /dev/null +++ b/_source/plugins/devtools/lang/no.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'devtools', 'no',
+{
+ devTools :
+ {
+ title : 'Elementinformasjon',
+ dialogName : 'Navn på dialogvindu',
+ tabName : 'Navn på fane',
+ elementId : 'Element-ID',
+ elementType : 'Elementtype'
+ }
+});
diff --git a/_source/plugins/devtools/lang/pl.js b/_source/plugins/devtools/lang/pl.js new file mode 100644 index 0000000..1099684 --- /dev/null +++ b/_source/plugins/devtools/lang/pl.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'devtools', 'pl',
+{
+ devTools :
+ {
+ title : 'Informacja o elemencie',
+ dialogName : 'Nazwa okna dialogowego',
+ tabName : 'Nazwa zakładki',
+ elementId : 'ID elementu',
+ elementType : 'Typ elementu'
+ }
+});
diff --git a/_source/plugins/devtools/lang/tr.js b/_source/plugins/devtools/lang/tr.js new file mode 100644 index 0000000..9940638 --- /dev/null +++ b/_source/plugins/devtools/lang/tr.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'devtools', 'tr',
+{
+ devTools :
+ {
+ title : 'Eleman Bilgisi',
+ dialogName : 'İletişim pencere ismi',
+ tabName : 'Sekme adı',
+ elementId : 'Eleman ID',
+ elementType : 'Eleman türü'
+ }
+});
diff --git a/_source/plugins/devtools/lang/ug.js b/_source/plugins/devtools/lang/ug.js new file mode 100644 index 0000000..5b1be62 --- /dev/null +++ b/_source/plugins/devtools/lang/ug.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'devtools', 'ug',
+{
+ devTools :
+ {
+ title : 'ئېلېمېنت ئۇچۇرى',
+ dialogName : 'سۆزلەشكۈ كۆزنەك ئاتى',
+ tabName : 'Tab ئاتى',
+ elementId : 'ئېلېمېنت كىملىكى',
+ elementType : 'ئېلېمېنت تىپى'
+ }
+});
diff --git a/_source/plugins/devtools/lang/uk.js b/_source/plugins/devtools/lang/uk.js new file mode 100644 index 0000000..21c4f29 --- /dev/null +++ b/_source/plugins/devtools/lang/uk.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'devtools', 'uk',
+{
+ devTools :
+ {
+ title : 'Відомості про Елемент',
+ dialogName : 'Заголовок діалогового вікна',
+ tabName : 'Назва вкладки',
+ elementId : 'Ідентифікатор Елемента',
+ elementType : 'Тип Елемента'
+ }
+});
diff --git a/_source/plugins/devtools/lang/vi.js b/_source/plugins/devtools/lang/vi.js new file mode 100644 index 0000000..9470587 --- /dev/null +++ b/_source/plugins/devtools/lang/vi.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'devtools', 'vi',
+{
+ devTools :
+ {
+ title : 'Thông tin thành ph',
+ dialogName : 'Tên hộp tho',
+ tabName : 'Tên th',
+ elementId : 'Mã thành ph',
+ elementType : 'Loại thành ph'
+ }
+});
diff --git a/_source/plugins/devtools/lang/zh-cn.js b/_source/plugins/devtools/lang/zh-cn.js new file mode 100644 index 0000000..d06ceb2 --- /dev/null +++ b/_source/plugins/devtools/lang/zh-cn.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'devtools', 'zh-cn',
+{
+ devTools :
+ {
+ title : '元素信息',
+ dialogName : '对话框窗口名称',
+ tabName : 'Tab 名称',
+ elementId : '元素 ID',
+ elementType : '元素类型'
+ }
+});
diff --git a/_source/plugins/devtools/plugin.js b/_source/plugins/devtools/plugin.js new file mode 100644 index 0000000..255b1c3 --- /dev/null +++ b/_source/plugins/devtools/plugin.js @@ -0,0 +1,173 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.add( 'devtools',
+{
+ lang : [ 'bg', 'cs', 'cy', 'da', 'de', 'el', 'en', 'eo', 'et', 'fa', 'fi', 'fr', 'gu', 'he', 'hr', 'it', 'nb', 'nl', 'no', 'pl', 'tr', 'ug', 'uk', 'vi', 'zh-cn' ],
+
+ 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 = "
+ * #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; }
+ * ";
+ */
diff --git a/_source/plugins/dialog/dialogDefinition.js b/_source/plugins/dialog/dialogDefinition.js index a3094f6..3d36d04 100644 --- a/_source/plugins/dialog/dialogDefinition.js +++ b/_source/plugins/dialog/dialogDefinition.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
@@ -9,9 +9,12 @@ For licensing, see LICENSE.html or http://ckeditor.com/license */
/**
+ * The definition of a dialog window.
+ * <div class="notapi">
* This class is not really part of the API. It just illustrates the properties
* that developers can use to define and create dialogs.
- * @name CKEDITOR.dialog.dialogDefinition
+ * </div>
+ * @name CKEDITOR.dialog.definition
* @constructor
* @example
* // There is no constructor for this class, the user just has to define an
@@ -46,7 +49,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license /**
* The dialog title, displayed in the dialog's header. Required.
- * @name CKEDITOR.dialog.dialogDefinition.prototype.title
+ * @name CKEDITOR.dialog.definition.prototype.title
* @field
* @type String
* @example
@@ -59,7 +62,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license * <strong>CKEDITOR.DIALOG_RESIZE_WIDTH</strong><br />
* <strong>CKEDITOR.DIALOG_RESIZE_HEIGHT</strong><br />
* <strong>CKEDITOR.DIALOG_RESIZE_BOTH</strong><br />
- * @name CKEDITOR.dialog.dialogDefinition.prototype.resizable
+ * @name CKEDITOR.dialog.definition.prototype.resizable
* @field
* @type Number
* @default CKEDITOR.DIALOG_RESIZE_NONE
@@ -68,7 +71,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license /**
* The minimum width of the dialog, in pixels.
- * @name CKEDITOR.dialog.dialogDefinition.prototype.minWidth
+ * @name CKEDITOR.dialog.definition.prototype.minWidth
* @field
* @type Number
* @default 600
@@ -77,17 +80,38 @@ For licensing, see LICENSE.html or http://ckeditor.com/license /**
* The minimum height of the dialog, in pixels.
- * @name CKEDITOR.dialog.dialogDefinition.prototype.minHeight
+ * @name CKEDITOR.dialog.definition.prototype.minHeight
* @field
* @type Number
* @default 400
* @example
*/
+
+/**
+ * The initial width of the dialog, in pixels.
+ * @name CKEDITOR.dialog.definition.prototype.width
+ * @field
+ * @type Number
+ * @default @CKEDITOR.dialog.definition.prototype.minWidth
+ * @since 3.5.3
+ * @example
+ */
+
+/**
+ * The initial height of the dialog, in pixels.
+ * @name CKEDITOR.dialog.definition.prototype.height
+ * @field
+ * @type Number
+ * @default @CKEDITOR.dialog.definition.prototype.minHeight
+ * @since 3.5.3
+ * @example
+ */
+
/**
* The buttons in the dialog, defined as an array of
- * {@link CKEDITOR.dialog.buttonDefinition} objects.
- * @name CKEDITOR.dialog.dialogDefinition.prototype.buttons
+ * {@link CKEDITOR.dialog.definition.button} objects.
+ * @name CKEDITOR.dialog.definition.prototype.buttons
* @field
* @type Array
* @default [ CKEDITOR.dialog.okButton, CKEDITOR.dialog.cancelButton ]
@@ -96,8 +120,8 @@ For licensing, see LICENSE.html or http://ckeditor.com/license /**
* The contents in the dialog, defined as an array of
- * {@link CKEDITOR.dialog.contentDefinition} objects. Required.
- * @name CKEDITOR.dialog.dialogDefinition.prototype.contents
+ * {@link CKEDITOR.dialog.definition.content} objects. Required.
+ * @name CKEDITOR.dialog.definition.prototype.contents
* @field
* @type Array
* @example
@@ -105,7 +129,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license /**
* The function to execute when OK is pressed.
- * @name CKEDITOR.dialog.dialogDefinition.prototype.onOk
+ * @name CKEDITOR.dialog.definition.prototype.onOk
* @field
* @type Function
* @example
@@ -113,7 +137,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license /**
* The function to execute when Cancel is pressed.
- * @name CKEDITOR.dialog.dialogDefinition.prototype.onCancel
+ * @name CKEDITOR.dialog.definition.prototype.onCancel
* @field
* @type Function
* @example
@@ -121,16 +145,24 @@ For licensing, see LICENSE.html or http://ckeditor.com/license /**
* The function to execute when the dialog is displayed for the first time.
- * @name CKEDITOR.dialog.dialogDefinition.prototype.onLoad
+ * @name CKEDITOR.dialog.definition.prototype.onLoad
* @field
* @type Function
* @example
*/
/**
- * This class is not really part of the API. It just illustrates the properties
- * that developers can use to define and create dialog content pages.
- * @name CKEDITOR.dialog.contentDefinition
+ * The function to execute when the dialog is loaded (executed every time the dialog is opened).
+ * @name CKEDITOR.dialog.definition.prototype.onShow
+ * @field
+ * @type Function
+ * @example
+ */
+
+/**
+ * <div class="notapi">This class is not really part of the API. It just illustrates the properties
+ * that developers can use to define and create dialog content pages.</div>
+ * @name CKEDITOR.dialog.definition.content
* @constructor
* @example
* // There is no constructor for this class, the user just has to define an
@@ -139,7 +171,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license /**
* The id of the content page.
- * @name CKEDITOR.dialog.contentDefinition.prototype.id
+ * @name CKEDITOR.dialog.definition.content.prototype.id
* @field
* @type String
* @example
@@ -147,7 +179,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license /**
* The tab label of the content page.
- * @name CKEDITOR.dialog.contentDefinition.prototype.label
+ * @name CKEDITOR.dialog.definition.content.prototype.label
* @field
* @type String
* @example
@@ -155,7 +187,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license /**
* The popup message of the tab label.
- * @name CKEDITOR.dialog.contentDefinition.prototype.title
+ * @name CKEDITOR.dialog.definition.content.prototype.title
* @field
* @type String
* @example
@@ -163,7 +195,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license /**
* The CTRL hotkey for switching to the tab.
- * @name CKEDITOR.dialog.contentDefinition.prototype.accessKey
+ * @name CKEDITOR.dialog.definition.content.prototype.accessKey
* @field
* @type String
* @example
@@ -172,144 +204,963 @@ For licensing, see LICENSE.html or http://ckeditor.com/license /**
* The UI elements contained in this content page, defined as an array of
- * {@link CKEDITOR.dialog.uiElementDefinition} objects.
- * @name CKEDITOR.dialog.contentDefinition.prototype.elements
+ * {@link CKEDITOR.dialog.definition.uiElement} objects.
+ * @name CKEDITOR.dialog.definition.content.prototype.elements
* @field
* @type Array
* @example
*/
/**
+ * The definition of user interface element (textarea, radio etc).
+ * <div class="notapi">This class is not really part of the API. It just illustrates the properties
+ * that developers can use to define and create dialog UI elements.</div>
+ * @name CKEDITOR.dialog.definition.uiElement
+ * @constructor
+ * @see CKEDITOR.ui.dialog.uiElement
+ * @example
+ * // There is no constructor for this class, the user just has to define an
+ * // object with the appropriate properties.
+ */
+
+/**
+ * The id of the UI element.
+ * @name CKEDITOR.dialog.definition.uiElement.prototype.id
+ * @field
+ * @type String
+ * @example
+ */
+
+/**
+ * The type of the UI element. Required.
+ * @name CKEDITOR.dialog.definition.uiElement.prototype.type
+ * @field
+ * @type String
+ * @example
+ */
+
+/**
+ * The popup label of the UI element.
+ * @name CKEDITOR.dialog.definition.uiElement.prototype.title
+ * @field
+ * @type String
+ * @example
+ */
+
+/**
+ * CSS class names to append to the UI element.
+ * @name CKEDITOR.dialog.definition.uiElement.prototype.className
+ * @field
+ * @type String
+ * @example
+ */
+
+/**
+ * Inline CSS classes to append to the UI element.
+ * @name CKEDITOR.dialog.definition.uiElement.prototype.style
+ * @field
+ * @type String
+ * @example
+ */
+
+/**
+ * Horizontal alignment (in container) of the UI element.
+ * @name CKEDITOR.dialog.definition.uiElement.prototype.align
+ * @field
+ * @type String
+ * @example
+ */
+
+/**
+ * Function to execute the first time the UI element is displayed.
+ * @name CKEDITOR.dialog.definition.uiElement.prototype.onLoad
+ * @field
+ * @type Function
+ * @example
+ */
+
+/**
+ * Function to execute whenever the UI element's parent dialog is displayed.
+ * @name CKEDITOR.dialog.definition.uiElement.prototype.onShow
+ * @field
+ * @type Function
+ * @example
+ */
+
+/**
+ * Function to execute whenever the UI element's parent dialog is closed.
+ * @name CKEDITOR.dialog.definition.uiElement.prototype.onHide
+ * @field
+ * @type Function
+ * @example
+ */
+
+/**
+ * Function to execute whenever the UI element's parent dialog's {@link CKEDITOR.dialog.definition.setupContent} method is executed.
+ * It usually takes care of the respective UI element as a standalone element.
+ * @name CKEDITOR.dialog.definition.uiElement.prototype.setup
+ * @field
+ * @type Function
+ * @example
+ */
+
+/**
+ * Function to execute whenever the UI element's parent dialog's {@link CKEDITOR.dialog.definition.commitContent} method is executed.
+ * It usually takes care of the respective UI element as a standalone element.
+ * @name CKEDITOR.dialog.definition.uiElement.prototype.commit
+ * @field
+ * @type Function
+ * @example
+ */
+
+// ----- hbox -----
+
+/**
+ * Horizontal layout box for dialog UI elements, auto-expends to available width of container.
+ * <div class="notapi">
* This class is not really part of the API. It just illustrates the properties
- * that developers can use to define and create dialog buttons.
- * @name CKEDITOR.dialog.buttonDefinition
+ * that developers can use to define and create horizontal layouts.
+ * <br /><br />Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.hbox} object and can be accessed with {@link CKEDITOR.dialog#getContentElement}.
+ * </div>
+ * @name CKEDITOR.dialog.definition.hbox
+ * @extends CKEDITOR.dialog.definition.uiElement
* @constructor
* @example
* // There is no constructor for this class, the user just has to define an
* // object with the appropriate properties.
+ *
+ * // Example:
+ * {
+ * <b>type : 'hbox',</b>
+ * widths : [ '25%', '25%', '50%' ],
+ * children :
+ * [
+ * {
+ * type : 'text',
+ * id : 'id1',
+ * width : '40px',
+ * },
+ * {
+ * type : 'text',
+ * id : 'id2',
+ * width : '40px',
+ * },
+ * {
+ * type : 'text',
+ * id : 'id3'
+ * }
+ * ]
+ * }
*/
/**
- * The id of the dialog button. Required.
- * @name CKEDITOR.dialog.buttonDefinition.prototype.id
+ * Array of {@link CKEDITOR.ui.dialog.uiElement} objects inside this container.
+ * @name CKEDITOR.dialog.definition.hbox.prototype.children
+ * @field
+ * @type Array
+ * @example
+ */
+
+/**
+ * (Optional) The widths of child cells.
+ * @name CKEDITOR.dialog.definition.hbox.prototype.widths
+ * @field
+ * @type Array
+ * @example
+ */
+
+/**
+ * (Optional) The height of the layout.
+ * @name CKEDITOR.dialog.definition.hbox.prototype.height
+ * @field
+ * @type Number
+ * @example
+ */
+
+/**
+ * The CSS styles to apply to this element.
+ * @name CKEDITOR.dialog.definition.hbox.prototype.styles
+ * @field
* @type String
+ * @example
+ */
+
+/**
+ * (Optional) The padding width inside child cells. Example: 0, 1.
+ * @name CKEDITOR.dialog.definition.hbox.prototype.padding
+ * @field
+ * @type Number
+ * @example
+ */
+
+/**
+ * (Optional) The alignment of the whole layout. Example: center, top.
+ * @name CKEDITOR.dialog.definition.hbox.prototype.align
+ * @field
+ * @type String
+ * @example
+ */
+
+// ----- vbox -----
+
+/**
+ * Vertical layout box for dialog UI elements.
+ * <div class="notapi">
+ * This class is not really part of the API. It just illustrates the properties
+ * that developers can use to define and create vertical layouts.
+ * <br /><br />Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.vbox} object and can be accessed with {@link CKEDITOR.dialog#getContentElement}.
+ * </div>
+ * <style type="text/css">.details .detailList {display:none;} </style>
+ * @name CKEDITOR.dialog.definition.vbox
+ * @extends CKEDITOR.dialog.definition.uiElement
+ * @constructor
+ * @example
+ * // There is no constructor for this class, the user just has to define an
+ * // object with the appropriate properties.
+ *
+ * // Example:
+ * {
+ * <b>type : 'vbox',</b>
+ * align : 'right',
+ * width : '200px',
+ * children :
+ * [
+ * {
+ * type : 'text',
+ * id : 'age',
+ * label : 'Age'
+ * },
+ * {
+ * type : 'text',
+ * id : 'sex',
+ * label : 'Sex'
+ * },
+ * {
+ * type : 'text',
+ * id : 'nationality',
+ * label : 'Nationality'
+ * }
+ * ]
+ * }
+ */
+
+/**
+ * Array of {@link CKEDITOR.ui.dialog.uiElement} objects inside this container.
+ * @name CKEDITOR.dialog.definition.vbox.prototype.children
+ * @field
+ * @type Array
+ * @example
+ */
+
+/**
+ * (Optional) The width of the layout.
+ * @name CKEDITOR.dialog.definition.vbox.prototype.width
+ * @field
+ * @type Array
+ * @example
+ */
+
+/**
+ * (Optional) The heights of individual cells.
+ * @name CKEDITOR.dialog.definition.vbox.prototype.heights
+ * @field
+ * @type Number
+ * @example
+ */
+
+/**
+ * The CSS styles to apply to this element.
+ * @name CKEDITOR.dialog.definition.vbox.prototype.styles
+ * @field
+ * @type String
+ * @example
+ */
+
+/**
+ * (Optional) The padding width inside child cells. Example: 0, 1.
+ * @name CKEDITOR.dialog.definition.vbox.prototype.padding
+ * @field
+ * @type Number
+ * @example
+ */
+
+/**
+ * (Optional) The alignment of the whole layout. Example: center, top.
+ * @name CKEDITOR.dialog.definition.vbox.prototype.align
+ * @field
+ * @type String
+ * @example
+ */
+
+/**
+ * (Optional) Whether the layout should expand vertically to fill its container.
+ * @name CKEDITOR.dialog.definition.vbox.prototype.expand
+ * @field
+ * @type Boolean
+ * @example
+ */
+
+// ----- labeled element ------
+
+/**
+ * The definition of labeled user interface element (textarea, textInput etc).
+ * <div class="notapi">This class is not really part of the API. It just illustrates the properties
+ * that developers can use to define and create dialog UI elements.</div>
+ * @name CKEDITOR.dialog.definition.labeledElement
+ * @extends CKEDITOR.dialog.definition.uiElement
+ * @constructor
+ * @see CKEDITOR.ui.dialog.labeledElement
+ * @example
+ * // There is no constructor for this class, the user just has to define an
+ * // object with the appropriate properties.
+ */
+
+/**
+ * The label of the UI element.
+ * @name CKEDITOR.dialog.definition.labeledElement.prototype.label
+ * @type String
+ * @field
+ * @example
+ * {
+ * type : 'text',
+ * label : 'My Label '
+ * }
+ */
+
+/**
+ * (Optional) Specify the layout of the label. Set to 'horizontal' for horizontal layout.
+ * The default layout is vertical.
+ * @name CKEDITOR.dialog.definition.labeledElement.prototype.labelLayout
+ * @type String
+ * @field
+ * @example
+ * {
+ * type : 'text',
+ * label : 'My Label ',
+ * <strong> labelLayout : 'horizontal',</strong>
+ * }
+ */
+
+/**
+ * (Optional) Applies only to horizontal layouts: a two elements array of lengths to specify the widths of the
+* label and the content element. See also {@link CKEDITOR.dialog.definition.labeledElement#labelLayout}.
+ * @name CKEDITOR.dialog.definition.labeledElement.prototype.widths
+ * @type Array
* @field
* @example
+ * {
+ * type : 'text',
+ * label : 'My Label ',
+ * labelLayout : 'horizontal',
+ * <strong> widths : [100, 200],</strong>
+ * }
*/
/**
- * The label of the dialog button. Required.
- * @name CKEDITOR.dialog.buttonDefinition.prototype.label
+ * Specify the inline style of the uiElement label.
+ * @name CKEDITOR.dialog.definition.labeledElement.prototype.labelStyle
* @type String
* @field
* @example
+ * {
+ * type : 'text',
+ * label : 'My Label ',
+ * <strong> labelStyle : 'color: red',</strong>
+ * }
*/
+
/**
- * The popup message of the dialog button.
- * @name CKEDITOR.dialog.buttonDefinition.prototype.title
+ * Specify the inline style of the input element.
+ * @name CKEDITOR.dialog.definition.labeledElement.prototype.inputStyle
* @type String
+ * @since 3.6.1
* @field
* @example
+ * {
+ * type : 'text',
+ * label : 'My Label ',
+ * <strong> inputStyle : 'text-align:center',</strong>
+ * }
*/
/**
- * The CTRL hotkey for the button.
- * @name CKEDITOR.dialog.buttonDefinition.prototype.accessKey
+ * Specify the inline style of the input element container .
+ * @name CKEDITOR.dialog.definition.labeledElement.prototype.controlStyle
* @type String
+ * @since 3.6.1
* @field
* @example
- * exitButton.accessKey = 'X'; // Button will be pressed when user presses CTRL-X
+ * {
+ * type : 'text',
+ * label : 'My Label ',
+ * <strong> controlStyle : 'width:3em',</strong>
+ * }
+ */
+
+
+// ----- button ------
+
+/**
+ * The definition of a button.
+ * <div class="notapi">
+ * This class is not really part of the API. It just illustrates the properties
+ * that developers can use to define and create buttons.
+ * <br /><br />Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.button} object and can be accessed with {@link CKEDITOR.dialog#getContentElement}.
+ * </div>
+ * For a complete example of dialog definition, please check {@link CKEDITOR.dialog.add}.
+ * @name CKEDITOR.dialog.definition.button
+ * @extends CKEDITOR.dialog.definition.uiElement
+ * @constructor
+ * @example
+ * // There is no constructor for this class, the user just has to define an
+ * // object with the appropriate properties.
+ *
+ * // Example:
+ * {
+ * <b>type : 'button',</b>
+ * id : 'buttonId',
+ * label : 'Click me',
+ * title : 'My title',
+ * onClick : function() {
+ * // this = CKEDITOR.ui.dialog.button
+ * alert( 'Clicked: ' + this.id );
+ * }
+ * }
*/
/**
* Whether the button is disabled.
- * @name CKEDITOR.dialog.buttonDefinition.prototype.disabled
+ * @name CKEDITOR.dialog.definition.button.prototype.disabled
* @type Boolean
* @field
- * @default false
* @example
*/
/**
- * The function to execute when the button is clicked.
- * @name CKEDITOR.dialog.buttonDefinition.prototype.onClick
+ * The label of the UI element.
+ * @name CKEDITOR.dialog.definition.button.prototype.label
+ * @type String
+ * @field
+ * @example
+ */
+
+// ----- checkbox ------
+
+/**
+ * The definition of a checkbox element.
+ * <div class="notapi">
+ * This class is not really part of the API. It just illustrates the properties
+ * that developers can use to define and create groups of checkbox buttons.
+ * <br /><br />Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.checkbox} object and can be accessed with {@link CKEDITOR.dialog#getContentElement}.
+ * </div>
+ * For a complete example of dialog definition, please check {@link CKEDITOR.dialog.add}.
+ * @name CKEDITOR.dialog.definition.checkbox
+ * @extends CKEDITOR.dialog.definition.uiElement
+ * @constructor
+ * @example
+ * // There is no constructor for this class, the user just has to define an
+ * // object with the appropriate properties.
+ *
+ * // Example:
+ * {
+ * <b>type : 'checkbox',</b>
+ * id : 'agree',
+ * label : 'I agree',
+ * 'default' : 'checked',
+ * onClick : function() {
+ * // this = CKEDITOR.ui.dialog.checkbox
+ * alert( 'Checked: ' + this.getValue() );
+ * }
+ * }
+ */
+
+/**
+ * (Optional) The validation function.
+ * @name CKEDITOR.dialog.definition.checkbox.prototype.validate
+ * @field
* @type Function
+ * @example
+ */
+
+/**
+ * The label of the UI element.
+ * @name CKEDITOR.dialog.definition.checkbox.prototype.label
+ * @type String
+ * @field
+ * @example
+ */
+
+/**
+ * The default state.
+ * @name CKEDITOR.dialog.definition.checkbox.prototype.default
+ * @type String
* @field
+ * @default
+ * '' (unchecked)
* @example
*/
+// ----- file -----
+
/**
+ * The definition of a file upload input.
+ * <div class="notapi">
* This class is not really part of the API. It just illustrates the properties
- * that developers can use to define and create dialog UI elements.
- * @name CKEDITOR.dialog.uiElementDefinition
+ * that developers can use to define and create file upload elements.
+ * <br /><br />Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.file} object and can be accessed with {@link CKEDITOR.dialog#getContentElement}.
+ * </div>
+ * For a complete example of dialog definition, please check {@link CKEDITOR.dialog.add}.
+ * @name CKEDITOR.dialog.definition.file
+ * @extends CKEDITOR.dialog.definition.labeledElement
* @constructor
- * @see CKEDITOR.ui.dialog.uiElement
* @example
* // There is no constructor for this class, the user just has to define an
* // object with the appropriate properties.
+ *
+ * // Example:
+ * {
+ * <b>type : 'file'</b>,
+ * id : 'upload',
+ * label : 'Select file from your computer',
+ * size : 38
+ * },
+ * {
+ * type : 'fileButton',
+ * id : 'fileId',
+ * label : 'Upload file',
+ * 'for' : [ 'tab1', 'upload' ]
+ * filebrowser : {
+ * onSelect : function( fileUrl, data ) {
+ * alert( 'Successfully uploaded: ' + fileUrl );
+ * }
+ * }
+ * }
*/
/**
- * The id of the UI element.
- * @name CKEDITOR.dialog.uiElementDefinition.prototype.id
+ * (Optional) The validation function.
+ * @name CKEDITOR.dialog.definition.file.prototype.validate
* @field
+ * @type Function
+ * @example
+ */
+
+/**
+ * (Optional) The action attribute of the form element associated with this file upload input.
+ * If empty, CKEditor will use path to server connector for currently opened folder.
+ * @name CKEDITOR.dialog.definition.file.prototype.action
* @type String
+ * @field
* @example
*/
/**
- * The type of the UI element. Required.
- * @name CKEDITOR.dialog.uiElementDefinition.prototype.type
+ * The size of the UI element.
+ * @name CKEDITOR.dialog.definition.file.prototype.size
+ * @type Number
* @field
+ * @example
+ */
+
+// ----- fileButton -----
+
+/**
+ * The definition of a button for submitting the file in a file upload input.
+ * <div class="notapi">
+ * This class is not really part of the API. It just illustrates the properties
+ * that developers can use to define and create a button for submitting the file in a file upload input.
+ * <br /><br />Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.fileButton} object and can be accessed with {@link CKEDITOR.dialog#getContentElement}.
+ * </div>
+ * For a complete example of dialog definition, please check {@link CKEDITOR.dialog.add}.
+ * @name CKEDITOR.dialog.definition.fileButton
+ * @extends CKEDITOR.dialog.definition.uiElement
+ * @constructor
+ * @example
+ * // There is no constructor for this class, the user just has to define an
+ * // object with the appropriate properties.
+ *
+ * // Example:
+ * {
+ * type : 'file',
+ * id : 'upload',
+ * label : 'Select file from your computer',
+ * size : 38
+ * },
+ * {
+ * <b>type : 'fileButton'</b>,
+ * id : 'fileId',
+ * label : 'Upload file',
+ * 'for' : [ 'tab1', 'upload' ]
+ * filebrowser : {
+ * onSelect : function( fileUrl, data ) {
+ * alert( 'Successfully uploaded: ' + fileUrl );
+ * }
+ * }
+ * }
+ */
+
+/**
+ * (Optional) The validation function.
+ * @name CKEDITOR.dialog.definition.fileButton.prototype.validate
+ * @field
+ * @type Function
+ * @example
+ */
+
+/**
+ * The label of the UI element.
+ * @name CKEDITOR.dialog.definition.fileButton.prototype.label
* @type String
+ * @field
* @example
*/
/**
- * The popup label of the UI element.
- * @name CKEDITOR.dialog.uiElementDefinition.prototype.title
+ * The instruction for CKEditor how to deal with file upload.
+ * By default, the file and fileButton elements will not work "as expected" if this attribute is not set.
+ * @name CKEDITOR.dialog.definition.fileButton.prototype.filebrowser
+ * @type String|Object
* @field
+ * @example
+ * // Update field with id 'txtUrl' in the 'tab1' tab when file is uploaded.
+ * filebrowser : 'tab1:txtUrl'
+ *
+ * // Call custom onSelect function when file is successfully uploaded.
+ * filebrowser : {
+ * onSelect : function( fileUrl, data ) {
+ * alert( 'Successfully uploaded: ' + fileUrl );
+ * }
+ * }
+ */
+
+/**
+ * An array that contains pageId and elementId of the file upload input element for which this button is created.
+ * @name CKEDITOR.dialog.definition.fileButton.prototype.for
* @type String
+ * @field
* @example
+ * [ pageId, elementId ]
*/
+// ----- html -----
+
/**
- * CSS class names to append to the UI element.
- * @name CKEDITOR.dialog.uiElementDefinition.prototype.className
+ * The definition of a raw HTML element.
+ * <div class="notapi">
+ * This class is not really part of the API. It just illustrates the properties
+ * that developers can use to define and create elements made from raw HTML code.
+ * <br /><br />Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.html} object and can be accessed with {@link CKEDITOR.dialog#getContentElement}.
+ * </div>
+ * For a complete example of dialog definition, please check {@link CKEDITOR.dialog.add}.<br />
+ * To access HTML elements use {@link CKEDITOR.dom.document#getById}
+ * @name CKEDITOR.dialog.definition.html
+ * @extends CKEDITOR.dialog.definition.uiElement
+ * @constructor
+ * @example
+ * // There is no constructor for this class, the user just has to define an
+ * // object with the appropriate properties.
+ *
+ * // Example 1:
+ * {
+ * <b>type : 'html',</b>
+ * html : '<h3>This is some sample HTML content.</h3>'
+ * }
+ * @example
+ * // Example 2:
+ * // Complete sample with document.getById() call when the "Ok" button is clicked.
+ * var dialogDefinition =
+ * {
+ * title : 'Sample dialog',
+ * minWidth : 300,
+ * minHeight : 200,
+ * onOk : function() {
+ * // "this" is now a CKEDITOR.dialog object.
+ * var document = this.getElement().getDocument();
+ * // document = CKEDITOR.dom.document
+ * var element = <b>document.getById( 'myDiv' );</b>
+ * if ( element )
+ * alert( element.getHtml() );
+ * },
+ * contents : [
+ * {
+ * id : 'tab1',
+ * label : '',
+ * title : '',
+ * elements :
+ * [
+ * {
+ * <b>type : 'html',</b>
+ * html : '<b><div id="myDiv">Sample <b>text</b>.</div></b><div id="otherId">Another div.</div>'
+ * },
+ * ]
+ * }
+ * ],
+ * buttons : [ CKEDITOR.dialog.cancelButton, CKEDITOR.dialog.okButton ]
+ * };
+ */
+
+/**
+ * (Required) HTML code of this element.
+ * @name CKEDITOR.dialog.definition.html.prototype.html
+ * @type String
* @field
+ * @example
+ */
+
+// ----- radio ------
+
+/**
+ * The definition of a radio group.
+ * <div class="notapi">
+ * This class is not really part of the API. It just illustrates the properties
+ * that developers can use to define and create groups of radio buttons.
+ * <br /><br />Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.radio} object and can be accessed with {@link CKEDITOR.dialog#getContentElement}.
+ * </div>
+ * For a complete example of dialog definition, please check {@link CKEDITOR.dialog.add}.
+ * @name CKEDITOR.dialog.definition.radio
+ * @extends CKEDITOR.dialog.definition.labeledElement
+ * @constructor
+ * @example
+ * // There is no constructor for this class, the user just has to define an
+ * // object with the appropriate properties.
+ *
+ * // Example:
+ * {
+ * <b>type : 'radio',</b>
+ * id : 'country',
+ * label : 'Which country is bigger',
+ * items : [ [ 'France', 'FR' ], [ 'Germany', 'DE' ] ] ,
+ * style : 'color:green',
+ * 'default' : 'DE',
+ * onClick : function() {
+ * // this = CKEDITOR.ui.dialog.radio
+ * alert( 'Current value: ' + this.getValue() );
+ * }
+ * }
+ */
+
+/**
+ * The default value.
+ * @name CKEDITOR.dialog.definition.radio.prototype.default
* @type String
+ * @field
* @example
*/
/**
- * Inline CSS classes to append to the UI element.
- * @name CKEDITOR.dialog.uiElementDefinition.prototype.style
+ * (Optional) The validation function.
+ * @name CKEDITOR.dialog.definition.radio.prototype.validate
+ * @field
+ * @type Function
+ * @example
+ */
+
+/**
+ * An array of options. Each option is a 1- or 2-item array of format [ 'Description', 'Value' ]. If 'Value' is missing, then the value would be assumed to be the same as the description.
+ * @name CKEDITOR.dialog.definition.radio.prototype.items
* @field
+ * @type Array
+ * @example
+ */
+
+// ----- selectElement ------
+
+/**
+ * The definition of a select element.
+ * <div class="notapi">
+ * This class is not really part of the API. It just illustrates the properties
+ * that developers can use to define and create select elements.
+ * <br /><br />Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.select} object and can be accessed with {@link CKEDITOR.dialog#getContentElement}.
+ * </div>
+ * For a complete example of dialog definition, please check {@link CKEDITOR.dialog.add}.
+ * @name CKEDITOR.dialog.definition.select
+ * @extends CKEDITOR.dialog.definition.labeledElement
+ * @constructor
+ * @example
+ * // There is no constructor for this class, the user just has to define an
+ * // object with the appropriate properties.
+ *
+ * // Example:
+ * {
+ * <b>type : 'select',</b>
+ * id : 'sport',
+ * label : 'Select your favourite sport',
+ * items : [ [ 'Basketball' ], [ 'Baseball' ], [ 'Hockey' ], [ 'Football' ] ],
+ * 'default' : 'Football',
+ * onChange : function( api ) {
+ * // this = CKEDITOR.ui.dialog.select
+ * alert( 'Current value: ' + this.getValue() );
+ * }
+ * }
+ */
+
+/**
+ * The default value.
+ * @name CKEDITOR.dialog.definition.select.prototype.default
* @type String
+ * @field
* @example
*/
/**
- * Function to execute the first time the UI element is displayed.
- * @name CKEDITOR.dialog.uiElementDefinition.prototype.onLoad
+ * (Optional) The validation function.
+ * @name CKEDITOR.dialog.definition.select.prototype.validate
* @field
* @type Function
* @example
*/
/**
- * Function to execute whenever the UI element's parent dialog is displayed.
- * @name CKEDITOR.dialog.uiElementDefinition.prototype.onShow
+ * An array of options. Each option is a 1- or 2-item array of format [ 'Description', 'Value' ]. If 'Value' is missing, then the value would be assumed to be the same as the description.
+ * @name CKEDITOR.dialog.definition.select.prototype.items
+ * @field
+ * @type Array
+ * @example
+ */
+
+/**
+ * (Optional) Set this to true if you'd like to have a multiple-choice select box.
+ * @name CKEDITOR.dialog.definition.select.prototype.multiple
+ * @type Boolean
+ * @field
+ * @example
+ * @default false
+ */
+
+/**
+ * (Optional) The number of items to display in the select box.
+ * @name CKEDITOR.dialog.definition.select.prototype.size
+ * @type Number
+ * @field
+ * @example
+ */
+
+// ----- textInput -----
+
+/**
+ * The definition of a text field (single line).
+ * <div class="notapi">
+ * This class is not really part of the API. It just illustrates the properties
+ * that developers can use to define and create text fields.
+ * <br /><br />Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.textInput} object and can be accessed with {@link CKEDITOR.dialog#getContentElement}.
+ * </div>
+ * For a complete example of dialog definition, please check {@link CKEDITOR.dialog.add}.
+ * @name CKEDITOR.dialog.definition.textInput
+ * @extends CKEDITOR.dialog.definition.labeledElement
+ * @constructor
+ * @example
+ * // There is no constructor for this class, the user just has to define an
+ * // object with the appropriate properties.
+ *
+ * {
+ * <b>type : 'text',</b>
+ * id : 'name',
+ * label : 'Your name',
+ * 'default' : '',
+ * validate : function() {
+ * if ( !this.getValue() )
+ * {
+ * api.openMsgDialog( '', 'Name cannot be empty.' );
+ * return false;
+ * }
+ * }
+ * }
+ */
+
+/**
+ * The default value.
+ * @name CKEDITOR.dialog.definition.textInput.prototype.default
+ * @type String
+ * @field
+ * @example
+ */
+
+/**
+ * (Optional) The maximum length.
+ * @name CKEDITOR.dialog.definition.textInput.prototype.maxLength
+ * @type Number
+ * @field
+ * @example
+ */
+
+/**
+ * (Optional) The size of the input field.
+ * @name CKEDITOR.dialog.definition.textInput.prototype.size
+ * @type Number
+ * @field
+ * @example
+ */
+
+/**
+ * (Optional) The validation function.
+ * @name CKEDITOR.dialog.definition.textInput.prototype.validate
* @field
* @type Function
* @example
*/
+// ----- textarea ------
+
/**
- * Function to execute whenever the UI element's parent dialog is closed.
- * @name CKEDITOR.dialog.uiElementDefinition.prototype.onHide
+ * The definition of a text field (multiple lines).
+ * <div class="notapi">
+ * This class is not really part of the API. It just illustrates the properties
+ * that developers can use to define and create textarea.
+ * <br /><br />Once the dialog is opened, the created element becomes a {@link CKEDITOR.ui.dialog.textarea} object and can be accessed with {@link CKEDITOR.dialog#getContentElement}.
+ * </div>
+ * For a complete example of dialog definition, please check {@link CKEDITOR.dialog.add}.
+ * @name CKEDITOR.dialog.definition.textarea
+ * @extends CKEDITOR.dialog.definition.labeledElement
+ * @constructor
+ * @example
+ * // There is no constructor for this class, the user just has to define an
+ * // object with the appropriate properties.
+ *
+ * // Example:
+ * {
+ * <b>type : 'textarea',</b>
+ * id : 'message',
+ * label : 'Your comment',
+ * 'default' : '',
+ * validate : function() {
+ * if ( this.getValue().length < 5 )
+ * {
+ * api.openMsgDialog( 'The comment is too short.' );
+ * return false;
+ * }
+ * }
+ * }
+ */
+
+/**
+ * The number of rows.
+ * @name CKEDITOR.dialog.definition.textarea.prototype.rows
+ * @type Number
+ * @field
+ * @example
+ */
+
+/**
+ * The number of columns.
+ * @name CKEDITOR.dialog.definition.textarea.prototype.cols
+ * @type Number
+ * @field
+ * @example
+ */
+
+/**
+ * (Optional) The validation function.
+ * @name CKEDITOR.dialog.definition.textarea.prototype.validate
* @field
* @type Function
* @example
*/
+
+/**
+ * The default value.
+ * @name CKEDITOR.dialog.definition.textarea.prototype.default
+ * @type String
+ * @field
+ * @example
+ */
diff --git a/_source/plugins/dialog/plugin.js b/_source/plugins/dialog/plugin.js index 8f48a2e..eee4916 100644 --- a/_source/plugins/dialog/plugin.js +++ b/_source/plugins/dialog/plugin.js @@ -1,2950 +1,3319 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -/** - * @fileOverview The floating dialog plugin. - */ - -/** - * No resize for this dialog. - * @constant - */ -CKEDITOR.DIALOG_RESIZE_NONE = 0; - -/** - * Only allow horizontal resizing for this dialog, disable vertical resizing. - * @constant - */ -CKEDITOR.DIALOG_RESIZE_WIDTH = 1; - -/** - * Only allow vertical resizing for this dialog, disable horizontal resizing. - * @constant - */ -CKEDITOR.DIALOG_RESIZE_HEIGHT = 2; - -/* - * Allow the dialog to be resized in both directions. - * @constant - */ -CKEDITOR.DIALOG_RESIZE_BOTH = 3; - -(function() -{ - function isTabVisible( tabId ) - { - return !!this._.tabs[ tabId ][ 0 ].$.offsetHeight; - } - - function getPreviousVisibleTab() - { - var tabId = this._.currentTabId, - length = this._.tabIdList.length, - tabIndex = CKEDITOR.tools.indexOf( this._.tabIdList, tabId ) + length; - - for ( var i = tabIndex - 1 ; i > tabIndex - length ; i-- ) - { - if ( isTabVisible.call( this, this._.tabIdList[ i % length ] ) ) - return this._.tabIdList[ i % length ]; - } - - return null; - } - - function getNextVisibleTab() - { - var tabId = this._.currentTabId, - length = this._.tabIdList.length, - tabIndex = CKEDITOR.tools.indexOf( this._.tabIdList, tabId ); - - for ( var i = tabIndex + 1 ; i < tabIndex + length ; i++ ) - { - if ( isTabVisible.call( this, this._.tabIdList[ i % length ] ) ) - return this._.tabIdList[ i % length ]; - } - - return null; - } - - /** - * This is the base class for runtime dialog objects. An instance of this - * class represents a single named dialog for a single editor instance. - * @param {Object} editor The editor which created the dialog. - * @param {String} dialogName The dialog's registered name. - * @constructor - * @example - * var dialogObj = new CKEDITOR.dialog( editor, 'smiley' ); - */ - CKEDITOR.dialog = function( editor, dialogName ) - { - // Load the dialog definition. - var definition = CKEDITOR.dialog._.dialogDefinitions[ dialogName ]; - - // Completes the definition with the default values. - definition = CKEDITOR.tools.extend( definition( editor ), defaultDialogDefinition ); - - // Clone a functionally independent copy for this dialog. - definition = CKEDITOR.tools.clone( definition ); - - // Create a complex definition object, extending it with the API - // functions. - definition = new definitionObject( this, definition ); - - - var doc = CKEDITOR.document; - - var themeBuilt = editor.theme.buildDialog( editor ); - - // Initialize some basic parameters. - this._ = - { - editor : editor, - element : themeBuilt.element, - name : dialogName, - contentSize : { width : 0, height : 0 }, - size : { width : 0, height : 0 }, - updateSize : false, - contents : {}, - buttons : {}, - accessKeyMap : {}, - - // Initialize the tab and page map. - tabs : {}, - tabIdList : [], - currentTabId : null, - currentTabIndex : null, - pageCount : 0, - lastTab : null, - tabBarMode : false, - - // Initialize the tab order array for input widgets. - focusList : [], - currentFocusIndex : 0, - hasFocus : false - }; - - this.parts = themeBuilt.parts; - - CKEDITOR.tools.setTimeout( function() - { - editor.fire( 'ariaWidget', this.parts.contents ); - }, - 0, this ); - - // Set the startup styles for the dialog, avoiding it enlarging the - // page size on the dialog creation. - this.parts.dialog.setStyles( - { - position : CKEDITOR.env.ie6Compat ? 'absolute' : 'fixed', - top : 0, - left: 0, - visibility : 'hidden' - }); - - // Call the CKEDITOR.event constructor to initialize this instance. - CKEDITOR.event.call( this ); - - // Fire the "dialogDefinition" event, making it possible to customize - // the dialog definition. - this.definition = definition = CKEDITOR.fire( 'dialogDefinition', - { - name : dialogName, - definition : definition - } - , editor ).definition; - // Initialize load, show, hide, ok and cancel events. - if ( definition.onLoad ) - this.on( 'load', definition.onLoad ); - - if ( definition.onShow ) - this.on( 'show', definition.onShow ); - - if ( definition.onHide ) - this.on( 'hide', definition.onHide ); - - if ( definition.onOk ) - { - this.on( 'ok', function( evt ) - { - if ( definition.onOk.call( this, evt ) === false ) - evt.data.hide = false; - }); - } - - if ( definition.onCancel ) - { - this.on( 'cancel', function( evt ) - { - if ( definition.onCancel.call( this, evt ) === false ) - evt.data.hide = false; - }); - } - - var me = this; - - // Iterates over all items inside all content in the dialog, calling a - // function for each of them. - var iterContents = function( func ) - { - var contents = me._.contents, - stop = false; - - for ( var i in contents ) - { - for ( var j in contents[i] ) - { - stop = func.call( this, contents[i][j] ); - if ( stop ) - return; - } - } - }; - - this.on( 'ok', function( evt ) - { - iterContents( function( item ) - { - if ( item.validate ) - { - var isValid = item.validate( this ); - - if ( typeof isValid == 'string' ) - { - alert( isValid ); - isValid = false; - } - - if ( isValid === false ) - { - if ( item.select ) - item.select(); - else - item.focus(); - - evt.data.hide = false; - evt.stop(); - return true; - } - } - }); - }, this, null, 0 ); - - this.on( 'cancel', function( evt ) - { - iterContents( function( item ) - { - if ( item.isChanged() ) - { - if ( !confirm( editor.lang.common.confirmCancel ) ) - evt.data.hide = false; - return true; - } - }); - }, this, null, 0 ); - - this.parts.close.on( 'click', function( evt ) - { - if ( this.fire( 'cancel', { hide : true } ).hide !== false ) - this.hide(); - evt.data.preventDefault(); - }, this ); - - // Sort focus list according to tab order definitions. - function setupFocus() - { - var focusList = me._.focusList; - focusList.sort( function( a, b ) - { - // Mimics browser tab order logics; - if ( a.tabIndex != b.tabIndex ) - return b.tabIndex - a.tabIndex; - // Sort is not stable in some browsers, - // fall-back the comparator to 'focusIndex'; - else - return a.focusIndex - b.focusIndex; - }); - - var size = focusList.length; - for ( var i = 0; i < size; i++ ) - focusList[ i ].focusIndex = i; - } - - function changeFocus( forward ) - { - var focusList = me._.focusList, - offset = forward ? 1 : -1; - if ( focusList.length < 1 ) - return; - - var current = me._.currentFocusIndex; - - // Trigger the 'blur' event of any input element before anything, - // since certain UI updates may depend on it. - try - { - focusList[ current ].getInputElement().$.blur(); - } - catch( e ){} - - var startIndex = ( current + offset + focusList.length ) % focusList.length, - currentIndex = startIndex; - while ( !focusList[ currentIndex ].isFocusable() ) - { - currentIndex = ( currentIndex + offset + focusList.length ) % focusList.length; - if ( currentIndex == startIndex ) - break; - } - focusList[ currentIndex ].focus(); - - // Select whole field content. - if ( focusList[ currentIndex ].type == 'text' ) - focusList[ currentIndex ].select(); - } - - this.changeFocus = changeFocus; - - var processed; - - function focusKeydownHandler( evt ) - { - // If I'm not the top dialog, ignore. - if ( me != CKEDITOR.dialog._.currentTop ) - return; - - var keystroke = evt.data.getKeystroke(), - rtl = editor.lang.dir == 'rtl'; - - processed = 0; - if ( keystroke == 9 || keystroke == CKEDITOR.SHIFT + 9 ) - { - var shiftPressed = ( keystroke == CKEDITOR.SHIFT + 9 ); - - // Handling Tab and Shift-Tab. - if ( me._.tabBarMode ) - { - // Change tabs. - var nextId = shiftPressed ? getPreviousVisibleTab.call( me ) : getNextVisibleTab.call( me ); - me.selectPage( nextId ); - me._.tabs[ nextId ][ 0 ].focus(); - } - else - { - // Change the focus of inputs. - changeFocus( !shiftPressed ); - } - - processed = 1; - } - else if ( keystroke == CKEDITOR.ALT + 121 && !me._.tabBarMode && me.getPageCount() > 1 ) - { - // Alt-F10 puts focus into the current tab item in the tab bar. - me._.tabBarMode = true; - me._.tabs[ me._.currentTabId ][ 0 ].focus(); - processed = 1; - } - else if ( ( keystroke == 37 || keystroke == 39 ) && me._.tabBarMode ) - { - // Arrow keys - used for changing tabs. - nextId = ( keystroke == ( rtl ? 39 : 37 ) ? getPreviousVisibleTab.call( me ) : getNextVisibleTab.call( me ) ); - me.selectPage( nextId ); - me._.tabs[ nextId ][ 0 ].focus(); - processed = 1; - } - else if ( ( keystroke == 13 || keystroke == 32 ) && me._.tabBarMode ) - { - this.selectPage( this._.currentTabId ); - this._.tabBarMode = false; - this._.currentFocusIndex = -1; - changeFocus( true ); - processed = 1; - } - - if ( processed ) - { - evt.stop(); - evt.data.preventDefault(); - } - } - - function focusKeyPressHandler( evt ) - { - processed && evt.data.preventDefault(); - } - - var dialogElement = this._.element; - // Add the dialog keyboard handlers. - this.on( 'show', function() - { - dialogElement.on( 'keydown', focusKeydownHandler, this, null, 0 ); - // Some browsers instead, don't cancel key events in the keydown, but in the - // keypress. So we must do a longer trip in those cases. (#4531) - if ( CKEDITOR.env.opera || ( CKEDITOR.env.gecko && CKEDITOR.env.mac ) ) - dialogElement.on( 'keypress', focusKeyPressHandler, this ); - - } ); - this.on( 'hide', function() - { - dialogElement.removeListener( 'keydown', focusKeydownHandler ); - if ( CKEDITOR.env.opera || ( CKEDITOR.env.gecko && CKEDITOR.env.mac ) ) - dialogElement.removeListener( 'keypress', focusKeyPressHandler ); - } ); - this.on( 'iframeAdded', function( evt ) - { - var doc = new CKEDITOR.dom.document( evt.data.iframe.$.contentWindow.document ); - doc.on( 'keydown', focusKeydownHandler, this, null, 0 ); - } ); - - // Auto-focus logic in dialog. - this.on( 'show', function() - { - // Setup tabIndex on showing the dialog instead of on loading - // to allow dynamic tab order happen in dialog definition. - setupFocus(); - - if ( editor.config.dialog_startupFocusTab - && me._.tabIdList.length > 1 ) - { - me._.tabBarMode = true; - me._.tabs[ me._.currentTabId ][ 0 ].focus(); - } - else if ( !this._.hasFocus ) - { - this._.currentFocusIndex = -1; - - // Decide where to put the initial focus. - if ( definition.onFocus ) - { - var initialFocus = definition.onFocus.call( this ); - // Focus the field that the user specified. - initialFocus && initialFocus.focus(); - } - // Focus the first field in layout order. - else - changeFocus( true ); - - /* - * IE BUG: If the initial focus went into a non-text element (e.g. button), - * then IE would still leave the caret inside the editing area. - */ - if ( this._.editor.mode == 'wysiwyg' && CKEDITOR.env.ie ) - { - var $selection = editor.document.$.selection, - $range = $selection.createRange(); - - if ( $range ) - { - if ( $range.parentElement && $range.parentElement().ownerDocument == editor.document.$ - || $range.item && $range.item( 0 ).ownerDocument == editor.document.$ ) - { - var $myRange = document.body.createTextRange(); - $myRange.moveToElementText( this.getElement().getFirst().$ ); - $myRange.collapse( true ); - $myRange.select(); - } - } - } - } - }, this, null, 0xffffffff ); - - // IE6 BUG: Text fields and text areas are only half-rendered the first time the dialog appears in IE6 (#2661). - // This is still needed after [2708] and [2709] because text fields in hidden TR tags are still broken. - if ( CKEDITOR.env.ie6Compat ) - { - this.on( 'load', function( evt ) - { - var outer = this.getElement(), - inner = outer.getFirst(); - inner.remove(); - inner.appendTo( outer ); - }, this ); - } - - initDragAndDrop( this ); - initResizeHandles( this ); - - // Insert the title. - ( new CKEDITOR.dom.text( definition.title, CKEDITOR.document ) ).appendTo( this.parts.title ); - - // Insert the tabs and contents. - for ( var i = 0 ; i < definition.contents.length ; i++ ) - this.addPage( definition.contents[i] ); - - this.parts['tabs'].on( 'click', function( evt ) - { - var target = evt.data.getTarget(); - // If we aren't inside a tab, bail out. - if ( target.hasClass( 'cke_dialog_tab' ) ) - { - var id = target.$.id; - this.selectPage( id.substr( 0, id.lastIndexOf( '_' ) ) ); - if ( this._.tabBarMode ) - { - this._.tabBarMode = false; - this._.currentFocusIndex = -1; - changeFocus( true ); - } - evt.data.preventDefault(); - } - }, this ); - - // Insert buttons. - var buttonsHtml = [], - buttons = CKEDITOR.dialog._.uiElementBuilders.hbox.build( this, - { - type : 'hbox', - className : 'cke_dialog_footer_buttons', - widths : [], - children : definition.buttons - }, buttonsHtml ).getChild(); - this.parts.footer.setHtml( buttonsHtml.join( '' ) ); - - for ( i = 0 ; i < buttons.length ; i++ ) - this._.buttons[ buttons[i].id ] = buttons[i]; - }; - - // Focusable interface. Use it via dialog.addFocusable. - function Focusable( dialog, element, index ) - { - this.element = element; - this.focusIndex = index; - // TODO: support tabIndex for focusables. - this.tabIndex = 0; - this.isFocusable = function() - { - return !element.getAttribute( 'disabled' ) && element.isVisible(); - }; - this.focus = function() - { - dialog._.currentFocusIndex = this.focusIndex; - this.element.focus(); - }; - // Bind events - element.on( 'keydown', function( e ) - { - if ( e.data.getKeystroke() in { 32:1, 13:1 } ) - this.fire( 'click' ); - } ); - element.on( 'focus', function() - { - this.fire( 'mouseover' ); - } ); - element.on( 'blur', function() - { - this.fire( 'mouseout' ); - } ); - } - - CKEDITOR.dialog.prototype = - { - destroy : function() - { - this.hide(); - this._.element.remove(); - }, - - /** - * Resizes the dialog. - * @param {Number} width The width of the dialog in pixels. - * @param {Number} height The height of the dialog in pixels. - * @function - * @example - * dialogObj.resize( 800, 640 ); - */ - resize : (function() - { - return function( width, height ) - { - if ( this._.contentSize && this._.contentSize.width == width && this._.contentSize.height == height ) - return; - - CKEDITOR.dialog.fire( 'resize', - { - dialog : this, - skin : this._.editor.skinName, - width : width, - height : height - }, this._.editor ); - - this._.contentSize = { width : width, height : height }; - this._.updateSize = true; - }; - })(), - - /** - * Gets the current size of the dialog in pixels. - * @returns {Object} An object with "width" and "height" properties. - * @example - * var width = dialogObj.getSize().width; - */ - getSize : function() - { - if ( !this._.updateSize ) - return this._.size; - var element = this._.element.getFirst(); - var size = this._.size = { width : element.$.offsetWidth || 0, height : element.$.offsetHeight || 0}; - - // If either the offsetWidth or offsetHeight is 0, the element isn't visible. - this._.updateSize = !size.width || !size.height; - - return size; - }, - - /** - * Moves the dialog to an (x, y) coordinate relative to the window. - * @function - * @param {Number} x The target x-coordinate. - * @param {Number} y The target y-coordinate. - * @example - * dialogObj.move( 10, 40 ); - */ - move : (function() - { - var isFixed; - return function( x, y ) - { - // The dialog may be fixed positioned or absolute positioned. Ask the - // browser what is the current situation first. - var element = this._.element.getFirst(); - if ( isFixed === undefined ) - isFixed = element.getComputedStyle( 'position' ) == 'fixed'; - - if ( isFixed && this._.position && this._.position.x == x && this._.position.y == y ) - return; - - // Save the current position. - this._.position = { x : x, y : y }; - - // If not fixed positioned, add scroll position to the coordinates. - if ( !isFixed ) - { - var scrollPosition = CKEDITOR.document.getWindow().getScrollPosition(); - x += scrollPosition.x; - y += scrollPosition.y; - } - - element.setStyles( - { - 'left' : ( x > 0 ? x : 0 ) + 'px', - 'top' : ( y > 0 ? y : 0 ) + 'px' - }); - }; - })(), - - /** - * Gets the dialog's position in the window. - * @returns {Object} An object with "x" and "y" properties. - * @example - * var dialogX = dialogObj.getPosition().x; - */ - getPosition : function(){ return CKEDITOR.tools.extend( {}, this._.position ); }, - - /** - * Shows the dialog box. - * @example - * dialogObj.show(); - */ - show : function() - { - var editor = this._.editor; - if ( editor.mode == 'wysiwyg' && CKEDITOR.env.ie ) - { - var selection = editor.getSelection(); - selection && selection.lock(); - } - - // Insert the dialog's element to the root document. - var element = this._.element; - var definition = this.definition; - if ( !( element.getParent() && element.getParent().equals( CKEDITOR.document.getBody() ) ) ) - element.appendTo( CKEDITOR.document.getBody() ); - else - element.setStyle( 'display', 'block' ); - - // FIREFOX BUG: Fix vanishing caret for Firefox 2 or Gecko 1.8. - if ( CKEDITOR.env.gecko && CKEDITOR.env.version < 10900 ) - { - var dialogElement = this.parts.dialog; - dialogElement.setStyle( 'position', 'absolute' ); - setTimeout( function() - { - dialogElement.setStyle( 'position', 'fixed' ); - }, 0 ); - } - - - // First, set the dialog to an appropriate size. - this.resize( definition.minWidth, definition.minHeight ); - - // Select the first tab by default. - this.selectPage( this.definition.contents[0].id ); - - // Reset all inputs back to their default value. - this.reset(); - - // Set z-index. - if ( CKEDITOR.dialog._.currentZIndex === null ) - CKEDITOR.dialog._.currentZIndex = this._.editor.config.baseFloatZIndex; - this._.element.getFirst().setStyle( 'z-index', CKEDITOR.dialog._.currentZIndex += 10 ); - - // Maintain the dialog ordering and dialog cover. - // Also register key handlers if first dialog. - if ( CKEDITOR.dialog._.currentTop === null ) - { - CKEDITOR.dialog._.currentTop = this; - this._.parentDialog = null; - showCover( this._.editor ); - - element.on( 'keydown', accessKeyDownHandler ); - element.on( CKEDITOR.env.opera ? 'keypress' : 'keyup', accessKeyUpHandler ); - - // Prevent some keys from bubbling up. (#4269) - for ( var event in { keyup :1, keydown :1, keypress :1 } ) - element.on( event, preventKeyBubbling ); - } - else - { - this._.parentDialog = CKEDITOR.dialog._.currentTop; - var parentElement = this._.parentDialog.getElement().getFirst(); - parentElement.$.style.zIndex -= Math.floor( this._.editor.config.baseFloatZIndex / 2 ); - CKEDITOR.dialog._.currentTop = this; - } - - // Register the Esc hotkeys. - registerAccessKey( this, this, '\x1b', null, function() - { - this.getButton( 'cancel' ) && this.getButton( 'cancel' ).click(); - } ); - - // Reset the hasFocus state. - this._.hasFocus = false; - - // Rearrange the dialog to the middle of the window. - CKEDITOR.tools.setTimeout( function() - { - var viewSize = CKEDITOR.document.getWindow().getViewPaneSize(); - var dialogSize = this.getSize(); - - // We're using definition size for initial position because of - // offten corrupted data in offsetWidth at this point. (#4084) - this.move( ( viewSize.width - definition.minWidth ) / 2, ( viewSize.height - dialogSize.height ) / 2 ); - - this.parts.dialog.setStyle( 'visibility', '' ); - - // Execute onLoad for the first show. - this.fireOnce( 'load', {} ); - this.fire( 'show', {} ); - this._.editor.fire( 'dialogShow', this ); - - // Save the initial values of the dialog. - this.foreach( function( contentObj ) { contentObj.setInitValue && contentObj.setInitValue(); } ); - - }, - 100, this ); - }, - - /** - * Executes a function for each UI element. - * @param {Function} fn Function to execute for each UI element. - * @returns {CKEDITOR.dialog} The current dialog object. - */ - foreach : function( fn ) - { - for ( var i in this._.contents ) - { - for ( var j in this._.contents[i] ) - fn( this._.contents[i][j]); - } - return this; - }, - - /** - * Resets all input values in the dialog. - * @example - * dialogObj.reset(); - * @returns {CKEDITOR.dialog} The current dialog object. - */ - reset : (function() - { - var fn = function( widget ){ if ( widget.reset ) widget.reset(); }; - return function(){ this.foreach( fn ); return this; }; - })(), - - setupContent : function() - { - var args = arguments; - this.foreach( function( widget ) - { - if ( widget.setup ) - widget.setup.apply( widget, args ); - }); - }, - - commitContent : function() - { - var args = arguments; - this.foreach( function( widget ) - { - if ( widget.commit ) - widget.commit.apply( widget, args ); - }); - }, - - /** - * Hides the dialog box. - * @example - * dialogObj.hide(); - */ - hide : function() - { - if ( !this.parts.dialog.isVisible() ) - return; - - this.fire( 'hide', {} ); - this._.editor.fire( 'dialogHide', this ); - var element = this._.element; - element.setStyle( 'display', 'none' ); - this.parts.dialog.setStyle( 'visibility', 'hidden' ); - // Unregister all access keys associated with this dialog. - unregisterAccessKey( this ); - - // Close any child(top) dialogs first. - while( CKEDITOR.dialog._.currentTop != this ) - CKEDITOR.dialog._.currentTop.hide(); - - // Maintain dialog ordering and remove cover if needed. - if ( !this._.parentDialog ) - hideCover(); - else - { - var parentElement = this._.parentDialog.getElement().getFirst(); - parentElement.setStyle( 'z-index', parseInt( parentElement.$.style.zIndex, 10 ) + Math.floor( this._.editor.config.baseFloatZIndex / 2 ) ); - } - CKEDITOR.dialog._.currentTop = this._.parentDialog; - - // Deduct or clear the z-index. - if ( !this._.parentDialog ) - { - CKEDITOR.dialog._.currentZIndex = null; - - // Remove access key handlers. - element.removeListener( 'keydown', accessKeyDownHandler ); - element.removeListener( CKEDITOR.env.opera ? 'keypress' : 'keyup', accessKeyUpHandler ); - - // Remove bubbling-prevention handler. (#4269) - for ( var event in { keyup :1, keydown :1, keypress :1 } ) - element.removeListener( event, preventKeyBubbling ); - - var editor = this._.editor; - editor.focus(); - - if ( editor.mode == 'wysiwyg' && CKEDITOR.env.ie ) - { - var selection = editor.getSelection(); - selection && selection.unlock( true ); - } - } - else - CKEDITOR.dialog._.currentZIndex -= 10; - - delete this._.parentDialog; - // Reset the initial values of the dialog. - this.foreach( function( contentObj ) { contentObj.resetInitValue && contentObj.resetInitValue(); } ); - }, - - /** - * Adds a tabbed page into the dialog. - * @param {Object} contents Content definition. - * @example - */ - addPage : function( contents ) - { - var pageHtml = [], - titleHtml = contents.label ? ' title="' + CKEDITOR.tools.htmlEncode( contents.label ) + '"' : '', - elements = contents.elements, - vbox = CKEDITOR.dialog._.uiElementBuilders.vbox.build( this, - { - type : 'vbox', - className : 'cke_dialog_page_contents', - children : contents.elements, - expand : !!contents.expand, - padding : contents.padding, - style : contents.style || 'width: 100%; height: 100%;' - }, pageHtml ); - - // Create the HTML for the tab and the content block. - var page = CKEDITOR.dom.element.createFromHtml( pageHtml.join( '' ) ); - page.setAttribute( 'role', 'tabpanel' ); - - var env = CKEDITOR.env; - var tabId = contents.id + '_' + CKEDITOR.tools.getNextNumber(), - tab = CKEDITOR.dom.element.createFromHtml( [ - '<a class="cke_dialog_tab"', - ( this._.pageCount > 0 ? ' cke_last' : 'cke_first' ), - titleHtml, - ( !!contents.hidden ? ' style="display:none"' : '' ), - ' id="', tabId, '"', - env.gecko && env.version >= 10900 && !env.hc ? '' : ' href="javascript:void(0)"', - ' tabIndex="-1"', - ' hidefocus="true"', - ' role="tab">', - contents.label, - '</a>' - ].join( '' ) ); - - page.setAttribute( 'aria-labelledby', tabId ); - - // Take records for the tabs and elements created. - this._.tabs[ contents.id ] = [ tab, page ]; - this._.tabIdList.push( contents.id ); - !contents.hidden && this._.pageCount++; - this._.lastTab = tab; - this.updateStyle(); - - var contentMap = this._.contents[ contents.id ] = {}, - cursor, - children = vbox.getChild(); - - while ( ( cursor = children.shift() ) ) - { - contentMap[ cursor.id ] = cursor; - if ( typeof( cursor.getChild ) == 'function' ) - children.push.apply( children, cursor.getChild() ); - } - - // Attach the DOM nodes. - - page.setAttribute( 'name', contents.id ); - page.appendTo( this.parts.contents ); - - tab.unselectable(); - this.parts.tabs.append( tab ); - - // Add access key handlers if access key is defined. - if ( contents.accessKey ) - { - registerAccessKey( this, this, 'CTRL+' + contents.accessKey, - tabAccessKeyDown, tabAccessKeyUp ); - this._.accessKeyMap[ 'CTRL+' + contents.accessKey ] = contents.id; - } - }, - - /** - * Activates a tab page in the dialog by its id. - * @param {String} id The id of the dialog tab to be activated. - * @example - * dialogObj.selectPage( 'tab_1' ); - */ - selectPage : function( id ) - { - // Hide the non-selected tabs and pages. - for ( var i in this._.tabs ) - { - var tab = this._.tabs[i][0], - page = this._.tabs[i][1]; - if ( i != id ) - { - tab.removeClass( 'cke_dialog_tab_selected' ); - page.hide(); - } - page.setAttribute( 'aria-hidden', i != id ); - } - - var selected = this._.tabs[id]; - selected[0].addClass( 'cke_dialog_tab_selected' ); - selected[1].show(); - this._.currentTabId = id; - this._.currentTabIndex = CKEDITOR.tools.indexOf( this._.tabIdList, id ); - }, - - // Dialog state-specific style updates. - updateStyle : function() - { - // If only a single page shown, a different style is used in the central pane. - this.parts.dialog[ ( this._.pageCount === 1 ? 'add' : 'remove' ) + 'Class' ]( 'cke_single_page' ); - }, - - /** - * Hides a page's tab away from the dialog. - * @param {String} id The page's Id. - * @example - * dialog.hidePage( 'tab_3' ); - */ - hidePage : function( id ) - { - var tab = this._.tabs[id] && this._.tabs[id][0]; - if ( !tab || this._.pageCount == 1 ) - return; - // Switch to other tab first when we're hiding the active tab. - else if ( id == this._.currentTabId ) - this.selectPage( getPreviousVisibleTab.call( this ) ); - - tab.hide(); - this._.pageCount--; - this.updateStyle(); - }, - - /** - * Unhides a page's tab. - * @param {String} id The page's Id. - * @example - * dialog.showPage( 'tab_2' ); - */ - showPage : function( id ) - { - var tab = this._.tabs[id] && this._.tabs[id][0]; - if ( !tab ) - return; - tab.show(); - this._.pageCount++; - this.updateStyle(); - }, - - /** - * Gets the root DOM element of the dialog. - * @returns {CKEDITOR.dom.element} The <span> element containing this dialog. - * @example - * var dialogElement = dialogObj.getElement().getFirst(); - * dialogElement.setStyle( 'padding', '5px' ); - */ - getElement : function() - { - return this._.element; - }, - - /** - * Gets the name of the dialog. - * @returns {String} The name of this dialog. - * @example - * var dialogName = dialogObj.getName(); - */ - getName : function() - { - return this._.name; - }, - - /** - * Gets a dialog UI element object from a dialog page. - * @param {String} pageId id of dialog page. - * @param {String} elementId id of UI element. - * @example - * @returns {CKEDITOR.ui.dialog.uiElement} The dialog UI element. - */ - getContentElement : function( pageId, elementId ) - { - var page = this._.contents[ pageId ]; - return page && page[ elementId ]; - }, - - /** - * Gets the value of a dialog UI element. - * @param {String} pageId id of dialog page. - * @param {String} elementId id of UI element. - * @example - * @returns {Object} The value of the UI element. - */ - getValueOf : function( pageId, elementId ) - { - return this.getContentElement( pageId, elementId ).getValue(); - }, - - /** - * Sets the value of a dialog UI element. - * @param {String} pageId id of the dialog page. - * @param {String} elementId id of the UI element. - * @param {Object} value The new value of the UI element. - * @example - */ - setValueOf : function( pageId, elementId, value ) - { - return this.getContentElement( pageId, elementId ).setValue( value ); - }, - - /** - * Gets the UI element of a button in the dialog's button row. - * @param {String} id The id of the button. - * @example - * @returns {CKEDITOR.ui.dialog.button} The button object. - */ - getButton : function( id ) - { - return this._.buttons[ id ]; - }, - - /** - * Simulates a click to a dialog button in the dialog's button row. - * @param {String} id The id of the button. - * @example - * @returns The return value of the dialog's "click" event. - */ - click : function( id ) - { - return this._.buttons[ id ].click(); - }, - - /** - * Disables a dialog button. - * @param {String} id The id of the button. - * @example - */ - disableButton : function( id ) - { - return this._.buttons[ id ].disable(); - }, - - /** - * Enables a dialog button. - * @param {String} id The id of the button. - * @example - */ - enableButton : function( id ) - { - return this._.buttons[ id ].enable(); - }, - - /** - * Gets the number of pages in the dialog. - * @returns {Number} Page count. - */ - getPageCount : function() - { - return this._.pageCount; - }, - - /** - * Gets the editor instance which opened this dialog. - * @returns {CKEDITOR.editor} Parent editor instances. - */ - getParentEditor : function() - { - return this._.editor; - }, - - /** - * Gets the element that was selected when opening the dialog, if any. - * @returns {CKEDITOR.dom.element} The element that was selected, or null. - */ - getSelectedElement : function() - { - return this.getParentEditor().getSelection().getSelectedElement(); - }, - - /** - * Adds element to dialog's focusable list. - * - * @param {CKEDITOR.dom.element} element - * @param {Number} [index] - */ - addFocusable: function( element, index ) { - if ( typeof index == 'undefined' ) - { - index = this._.focusList.length; - this._.focusList.push( new Focusable( this, element, index ) ); - } - else - { - this._.focusList.splice( index, 0, new Focusable( this, element, index ) ); - for ( var i = index + 1 ; i < this._.focusList.length ; i++ ) - this._.focusList[ i ].focusIndex++; - } - } - }; - - CKEDITOR.tools.extend( CKEDITOR.dialog, - /** - * @lends CKEDITOR.dialog - */ - { - /** - * Registers a dialog. - * @param {String} name The dialog's name. - * @param {Function|String} dialogDefinition - * A function returning the dialog's definition, or the URL to the .js file holding the function. - * The function should accept an argument "editor" which is the current editor instance, and - * return an object conforming to {@link CKEDITOR.dialog.dialogDefinition}. - * @example - * @see CKEDITOR.dialog.dialogDefinition - */ - add : function( name, dialogDefinition ) - { - // Avoid path registration from multiple instances override definition. - if ( !this._.dialogDefinitions[name] - || typeof dialogDefinition == 'function' ) - this._.dialogDefinitions[name] = dialogDefinition; - }, - - exists : function( name ) - { - return !!this._.dialogDefinitions[ name ]; - }, - - getCurrent : function() - { - return CKEDITOR.dialog._.currentTop; - }, - - /** - * The default OK button for dialogs. Fires the "ok" event and closes the dialog if the event succeeds. - * @static - * @field - * @example - * @type Function - */ - okButton : (function() - { - var retval = function( editor, override ) - { - override = override || {}; - return CKEDITOR.tools.extend( { - id : 'ok', - type : 'button', - label : editor.lang.common.ok, - 'class' : 'cke_dialog_ui_button_ok', - onClick : function( evt ) - { - var dialog = evt.data.dialog; - if ( dialog.fire( 'ok', { hide : true } ).hide !== false ) - dialog.hide(); - } - }, override, true ); - }; - retval.type = 'button'; - retval.override = function( override ) - { - return CKEDITOR.tools.extend( function( editor ){ return retval( editor, override ); }, - { type : 'button' }, true ); - }; - return retval; - })(), - - /** - * The default cancel button for dialogs. Fires the "cancel" event and closes the dialog if no UI element value changed. - * @static - * @field - * @example - * @type Function - */ - cancelButton : (function() - { - var retval = function( editor, override ) - { - override = override || {}; - return CKEDITOR.tools.extend( { - id : 'cancel', - type : 'button', - label : editor.lang.common.cancel, - 'class' : 'cke_dialog_ui_button_cancel', - onClick : function( evt ) - { - var dialog = evt.data.dialog; - if ( dialog.fire( 'cancel', { hide : true } ).hide !== false ) - dialog.hide(); - } - }, override, true ); - }; - retval.type = 'button'; - retval.override = function( override ) - { - return CKEDITOR.tools.extend( function( editor ){ return retval( editor, override ); }, - { type : 'button' }, true ); - }; - return retval; - })(), - - /** - * Registers a dialog UI element. - * @param {String} typeName The name of the UI element. - * @param {Function} builder The function to build the UI element. - * @example - */ - addUIElement : function( typeName, builder ) - { - this._.uiElementBuilders[ typeName ] = builder; - } - }); - - CKEDITOR.dialog._ = - { - uiElementBuilders : {}, - - dialogDefinitions : {}, - - currentTop : null, - - currentZIndex : null - }; - - // "Inherit" (copy actually) from CKEDITOR.event. - CKEDITOR.event.implementOn( CKEDITOR.dialog ); - CKEDITOR.event.implementOn( CKEDITOR.dialog.prototype, true ); - - var defaultDialogDefinition = - { - resizable : CKEDITOR.DIALOG_RESIZE_BOTH, - minWidth : 600, - minHeight : 400, - buttons : [ CKEDITOR.dialog.okButton, CKEDITOR.dialog.cancelButton ] - }; - - // The buttons in MacOS Apps are in reverse order #4750 - CKEDITOR.env.mac && defaultDialogDefinition.buttons.reverse(); - - // Tool function used to return an item from an array based on its id - // property. - var getById = function( array, id, recurse ) - { - for ( var i = 0, item ; ( item = array[ i ] ) ; i++ ) - { - if ( item.id == id ) - return item; - if ( recurse && item[ recurse ] ) - { - var retval = getById( item[ recurse ], id, recurse ) ; - if ( retval ) - return retval; - } - } - return null; - }; - - // Tool function used to add an item into an array. - var addById = function( array, newItem, nextSiblingId, recurse, nullIfNotFound ) - { - if ( nextSiblingId ) - { - for ( var i = 0, item ; ( item = array[ i ] ) ; i++ ) - { - if ( item.id == nextSiblingId ) - { - array.splice( i, 0, newItem ); - return newItem; - } - - if ( recurse && item[ recurse ] ) - { - var retval = addById( item[ recurse ], newItem, nextSiblingId, recurse, true ); - if ( retval ) - return retval; - } - } - - if ( nullIfNotFound ) - return null; - } - - array.push( newItem ); - return newItem; - }; - - // Tool function used to remove an item from an array based on its id. - var removeById = function( array, id, recurse ) - { - for ( var i = 0, item ; ( item = array[ i ] ) ; i++ ) - { - if ( item.id == id ) - return array.splice( i, 1 ); - if ( recurse && item[ recurse ] ) - { - var retval = removeById( item[ recurse ], id, recurse ); - if ( retval ) - return retval; - } - } - return null; - }; - - /** - * This class is not really part of the API. It is the "definition" property value - * passed to "dialogDefinition" event handlers. - * @constructor - * @name CKEDITOR.dialog.dialogDefinitionObject - * @extends CKEDITOR.dialog.dialogDefinition - * @example - * CKEDITOR.on( 'dialogDefinition', function( evt ) - * { - * var definition = evt.data.definition; - * var content = definition.getContents( 'page1' ); - * ... - * } ); - */ - var definitionObject = function( dialog, dialogDefinition ) - { - // TODO : Check if needed. - this.dialog = dialog; - - // Transform the contents entries in contentObjects. - var contents = dialogDefinition.contents; - for ( var i = 0, content ; ( content = contents[i] ) ; i++ ) - contents[ i ] = new contentObject( dialog, content ); - - CKEDITOR.tools.extend( this, dialogDefinition ); - }; - - definitionObject.prototype = - /** @lends CKEDITOR.dialog.dialogDefinitionObject.prototype */ - { - /** - * Gets a content definition. - * @param {String} id The id of the content definition. - * @returns {CKEDITOR.dialog.contentDefinition} The content definition - * matching id. - */ - getContents : function( id ) - { - return getById( this.contents, id ); - }, - - /** - * Gets a button definition. - * @param {String} id The id of the button definition. - * @returns {CKEDITOR.dialog.buttonDefinition} The button definition - * matching id. - */ - getButton : function( id ) - { - return getById( this.buttons, id ); - }, - - /** - * Adds a content definition object under this dialog definition. - * @param {CKEDITOR.dialog.contentDefinition} contentDefinition The - * content definition. - * @param {String} [nextSiblingId] The id of an existing content - * definition which the new content definition will be inserted - * before. Omit if the new content definition is to be inserted as - * the last item. - * @returns {CKEDITOR.dialog.contentDefinition} The inserted content - * definition. - */ - addContents : function( contentDefinition, nextSiblingId ) - { - return addById( this.contents, contentDefinition, nextSiblingId ); - }, - - /** - * Adds a button definition object under this dialog definition. - * @param {CKEDITOR.dialog.buttonDefinition} buttonDefinition The - * button definition. - * @param {String} [nextSiblingId] The id of an existing button - * definition which the new button definition will be inserted - * before. Omit if the new button definition is to be inserted as - * the last item. - * @returns {CKEDITOR.dialog.buttonDefinition} The inserted button - * definition. - */ - addButton : function( buttonDefinition, nextSiblingId ) - { - return addById( this.buttons, buttonDefinition, nextSiblingId ); - }, - - /** - * Removes a content definition from this dialog definition. - * @param {String} id The id of the content definition to be removed. - * @returns {CKEDITOR.dialog.contentDefinition} The removed content - * definition. - */ - removeContents : function( id ) - { - removeById( this.contents, id ); - }, - - /** - * Removes a button definition from the dialog definition. - * @param {String} id The id of the button definition to be removed. - * @returns {CKEDITOR.dialog.buttonDefinition} The removed button - * definition. - */ - removeButton : function( id ) - { - removeById( this.buttons, id ); - } - }; - - /** - * This class is not really part of the API. It is the template of the - * objects representing content pages inside the - * CKEDITOR.dialog.dialogDefinitionObject. - * @constructor - * @name CKEDITOR.dialog.contentDefinitionObject - * @example - * CKEDITOR.on( 'dialogDefinition', function( evt ) - * { - * var definition = evt.data.definition; - * var content = definition.getContents( 'page1' ); - * content.remove( 'textInput1' ); - * ... - * } ); - */ - function contentObject( dialog, contentDefinition ) - { - this._ = - { - dialog : dialog - }; - - CKEDITOR.tools.extend( this, contentDefinition ); - } - - contentObject.prototype = - /** @lends CKEDITOR.dialog.contentDefinitionObject.prototype */ - { - /** - * Gets a UI element definition under the content definition. - * @param {String} id The id of the UI element definition. - * @returns {CKEDITOR.dialog.uiElementDefinition} - */ - get : function( id ) - { - return getById( this.elements, id, 'children' ); - }, - - /** - * Adds a UI element definition to the content definition. - * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition The - * UI elemnet definition to be added. - * @param {String} nextSiblingId The id of an existing UI element - * definition which the new UI element definition will be inserted - * before. Omit if the new button definition is to be inserted as - * the last item. - * @returns {CKEDITOR.dialog.uiElementDefinition} The element - * definition inserted. - */ - add : function( elementDefinition, nextSiblingId ) - { - return addById( this.elements, elementDefinition, nextSiblingId, 'children' ); - }, - - /** - * Removes a UI element definition from the content definition. - * @param {String} id The id of the UI element definition to be - * removed. - * @returns {CKEDITOR.dialog.uiElementDefinition} The element - * definition removed. - * @example - */ - remove : function( id ) - { - removeById( this.elements, id, 'children' ); - } - }; - - function initDragAndDrop( dialog ) - { - var lastCoords = null, - abstractDialogCoords = null, - element = dialog.getElement().getFirst(), - editor = dialog.getParentEditor(), - magnetDistance = editor.config.dialog_magnetDistance, - margins = editor.skin.margins || [ 0, 0, 0, 0 ]; - - if ( typeof magnetDistance == 'undefined' ) - magnetDistance = 20; - - function mouseMoveHandler( evt ) - { - var dialogSize = dialog.getSize(), - viewPaneSize = CKEDITOR.document.getWindow().getViewPaneSize(), - x = evt.data.$.screenX, - y = evt.data.$.screenY, - dx = x - lastCoords.x, - dy = y - lastCoords.y, - realX, realY; - - lastCoords = { x : x, y : y }; - abstractDialogCoords.x += dx; - abstractDialogCoords.y += dy; - - if ( abstractDialogCoords.x + margins[3] < magnetDistance ) - realX = - margins[3]; - else if ( abstractDialogCoords.x - margins[1] > viewPaneSize.width - dialogSize.width - magnetDistance ) - realX = viewPaneSize.width - dialogSize.width + margins[1]; - else - realX = abstractDialogCoords.x; - - if ( abstractDialogCoords.y + margins[0] < magnetDistance ) - realY = - margins[0]; - else if ( abstractDialogCoords.y - margins[2] > viewPaneSize.height - dialogSize.height - magnetDistance ) - realY = viewPaneSize.height - dialogSize.height + margins[2]; - else - realY = abstractDialogCoords.y; - - dialog.move( realX, realY ); - - evt.data.preventDefault(); - } - - function mouseUpHandler( evt ) - { - CKEDITOR.document.removeListener( 'mousemove', mouseMoveHandler ); - CKEDITOR.document.removeListener( 'mouseup', mouseUpHandler ); - - if ( CKEDITOR.env.ie6Compat ) - { - var coverDoc = currentCover.getChild( 0 ).getFrameDocument(); - coverDoc.removeListener( 'mousemove', mouseMoveHandler ); - coverDoc.removeListener( 'mouseup', mouseUpHandler ); - } - } - - dialog.parts.title.on( 'mousedown', function( evt ) - { - dialog._.updateSize = true; - - lastCoords = { x : evt.data.$.screenX, y : evt.data.$.screenY }; - - CKEDITOR.document.on( 'mousemove', mouseMoveHandler ); - CKEDITOR.document.on( 'mouseup', mouseUpHandler ); - abstractDialogCoords = dialog.getPosition(); - - if ( CKEDITOR.env.ie6Compat ) - { - var coverDoc = currentCover.getChild( 0 ).getFrameDocument(); - coverDoc.on( 'mousemove', mouseMoveHandler ); - coverDoc.on( 'mouseup', mouseUpHandler ); - } - - evt.data.preventDefault(); - }, dialog ); - } - - function initResizeHandles( dialog ) - { - var definition = dialog.definition, - minWidth = definition.minWidth || 0, - minHeight = definition.minHeight || 0, - resizable = definition.resizable, - margins = dialog.getParentEditor().skin.margins || [ 0, 0, 0, 0 ]; - - function topSizer( coords, dy ) - { - coords.y += dy; - } - - function rightSizer( coords, dx ) - { - coords.x2 += dx; - } - - function bottomSizer( coords, dy ) - { - coords.y2 += dy; - } - - function leftSizer( coords, dx ) - { - coords.x += dx; - } - - var lastCoords = null, - abstractDialogCoords = null, - magnetDistance = dialog._.editor.config.magnetDistance, - parts = [ 'tl', 't', 'tr', 'l', 'r', 'bl', 'b', 'br' ]; - - function mouseDownHandler( evt ) - { - var partName = evt.listenerData.part, size = dialog.getSize(); - abstractDialogCoords = dialog.getPosition(); - CKEDITOR.tools.extend( abstractDialogCoords, - { - x2 : abstractDialogCoords.x + size.width, - y2 : abstractDialogCoords.y + size.height - } ); - lastCoords = { x : evt.data.$.screenX, y : evt.data.$.screenY }; - - CKEDITOR.document.on( 'mousemove', mouseMoveHandler, dialog, { part : partName } ); - CKEDITOR.document.on( 'mouseup', mouseUpHandler, dialog, { part : partName } ); - - if ( CKEDITOR.env.ie6Compat ) - { - var coverDoc = currentCover.getChild( 0 ).getFrameDocument(); - coverDoc.on( 'mousemove', mouseMoveHandler, dialog, { part : partName } ); - coverDoc.on( 'mouseup', mouseUpHandler, dialog, { part : partName } ); - } - - evt.data.preventDefault(); - } - - function mouseMoveHandler( evt ) - { - var x = evt.data.$.screenX, - y = evt.data.$.screenY, - dx = x - lastCoords.x, - dy = y - lastCoords.y, - viewPaneSize = CKEDITOR.document.getWindow().getViewPaneSize(), - partName = evt.listenerData.part; - - if ( partName.search( 't' ) != -1 ) - topSizer( abstractDialogCoords, dy ); - if ( partName.search( 'l' ) != -1 ) - leftSizer( abstractDialogCoords, dx ); - if ( partName.search( 'b' ) != -1 ) - bottomSizer( abstractDialogCoords, dy ); - if ( partName.search( 'r' ) != -1 ) - rightSizer( abstractDialogCoords, dx ); - - lastCoords = { x : x, y : y }; - - var realX, realY, realX2, realY2; - - if ( abstractDialogCoords.x + margins[3] < magnetDistance ) - realX = - margins[3]; - else if ( partName.search( 'l' ) != -1 && abstractDialogCoords.x2 - abstractDialogCoords.x < minWidth + magnetDistance ) - realX = abstractDialogCoords.x2 - minWidth; - else - realX = abstractDialogCoords.x; - - if ( abstractDialogCoords.y + margins[0] < magnetDistance ) - realY = - margins[0]; - else if ( partName.search( 't' ) != -1 && abstractDialogCoords.y2 - abstractDialogCoords.y < minHeight + magnetDistance ) - realY = abstractDialogCoords.y2 - minHeight; - else - realY = abstractDialogCoords.y; - - if ( abstractDialogCoords.x2 - margins[1] > viewPaneSize.width - magnetDistance ) - realX2 = viewPaneSize.width + margins[1] ; - else if ( partName.search( 'r' ) != -1 && abstractDialogCoords.x2 - abstractDialogCoords.x < minWidth + magnetDistance ) - realX2 = abstractDialogCoords.x + minWidth; - else - realX2 = abstractDialogCoords.x2; - - if ( abstractDialogCoords.y2 - margins[2] > viewPaneSize.height - magnetDistance ) - realY2= viewPaneSize.height + margins[2] ; - else if ( partName.search( 'b' ) != -1 && abstractDialogCoords.y2 - abstractDialogCoords.y < minHeight + magnetDistance ) - realY2 = abstractDialogCoords.y + minHeight; - else - realY2 = abstractDialogCoords.y2 ; - - dialog.move( realX, realY ); - dialog.resize( realX2 - realX, realY2 - realY ); - - evt.data.preventDefault(); - } - - function mouseUpHandler( evt ) - { - CKEDITOR.document.removeListener( 'mouseup', mouseUpHandler ); - CKEDITOR.document.removeListener( 'mousemove', mouseMoveHandler ); - - if ( CKEDITOR.env.ie6Compat ) - { - var coverDoc = currentCover.getChild( 0 ).getFrameDocument(); - coverDoc.removeListener( 'mouseup', mouseUpHandler ); - coverDoc.removeListener( 'mousemove', mouseMoveHandler ); - } - } - -// TODO : Simplify the resize logic, having just a single resize grip <div>. -// var widthTest = /[lr]/, -// heightTest = /[tb]/; -// for ( var i = 0 ; i < parts.length ; i++ ) -// { -// var element = dialog.parts[ parts[i] + '_resize' ]; -// if ( resizable == CKEDITOR.DIALOG_RESIZE_NONE || -// resizable == CKEDITOR.DIALOG_RESIZE_HEIGHT && widthTest.test( parts[i] ) || -// resizable == CKEDITOR.DIALOG_RESIZE_WIDTH && heightTest.test( parts[i] ) ) -// { -// element.hide(); -// continue; -// } -// element.on( 'mousedown', mouseDownHandler, dialog, { part : parts[i] } ); -// } - } - - var resizeCover; - // Caching resuable covers and allowing only one cover - // on screen. - var covers = {}, - currentCover; - - function showCover( editor ) - { - var win = CKEDITOR.document.getWindow(); - var backgroundColorStyle = editor.config.dialog_backgroundCoverColor || 'white', - backgroundCoverOpacity = editor.config.dialog_backgroundCoverOpacity, - baseFloatZIndex = editor.config.baseFloatZIndex, - coverKey = CKEDITOR.tools.genKey( - backgroundColorStyle, - backgroundCoverOpacity, - baseFloatZIndex ), - coverElement = covers[ coverKey ]; - - if ( !coverElement ) - { - var html = [ - '<div style="position: ', ( CKEDITOR.env.ie6Compat ? 'absolute' : 'fixed' ), - '; z-index: ', baseFloatZIndex, - '; top: 0px; left: 0px; ', - ( !CKEDITOR.env.ie6Compat ? 'background-color: ' + backgroundColorStyle : '' ), - '" class="cke_dialog_background_cover">' - ]; - - if ( CKEDITOR.env.ie6Compat ) - { - // Support for custom document.domain in IE. - var isCustomDomain = CKEDITOR.env.isCustomDomain(), - iframeHtml = '<html><body style=\\\'background-color:' + backgroundColorStyle + ';\\\'></body></html>'; - - html.push( - '<iframe' + - ' hidefocus="true"' + - ' frameborder="0"' + - ' id="cke_dialog_background_iframe"' + - ' src="javascript:' ); - - html.push( 'void((function(){' + - 'document.open();' + - ( isCustomDomain ? 'document.domain=\'' + document.domain + '\';' : '' ) + - 'document.write( \'' + iframeHtml + '\' );' + - 'document.close();' + - '})())' ); - - html.push( - '"' + - ' style="' + - 'position:absolute;' + - 'left:0;' + - 'top:0;' + - 'width:100%;' + - 'height: 100%;' + - 'progid:DXImageTransform.Microsoft.Alpha(opacity=0)">' + - '</iframe>' ); - } - - html.push( '</div>' ); - - coverElement = CKEDITOR.dom.element.createFromHtml( html.join( '' ) ); - coverElement.setOpacity( backgroundCoverOpacity != undefined ? backgroundCoverOpacity : 0.5 ); - - coverElement.appendTo( CKEDITOR.document.getBody() ); - covers[ coverKey ] = coverElement; - } - else - coverElement. show(); - - currentCover = coverElement; - var resizeFunc = function() - { - var size = win.getViewPaneSize(); - coverElement.setStyles( - { - width : size.width + 'px', - height : size.height + 'px' - } ); - }; - - var scrollFunc = function() - { - var pos = win.getScrollPosition(), - cursor = CKEDITOR.dialog._.currentTop; - coverElement.setStyles( - { - left : pos.x + 'px', - top : pos.y + 'px' - }); - - do - { - var dialogPos = cursor.getPosition(); - cursor.move( dialogPos.x, dialogPos.y ); - } while ( ( cursor = cursor._.parentDialog ) ); - }; - - resizeCover = resizeFunc; - win.on( 'resize', resizeFunc ); - resizeFunc(); - if ( CKEDITOR.env.ie6Compat ) - { - // IE BUG: win.$.onscroll assignment doesn't work.. it must be window.onscroll. - // So we need to invent a really funny way to make it work. - var myScrollHandler = function() - { - scrollFunc(); - arguments.callee.prevScrollHandler.apply( this, arguments ); - }; - win.$.setTimeout( function() - { - myScrollHandler.prevScrollHandler = window.onscroll || function(){}; - window.onscroll = myScrollHandler; - }, 0 ); - scrollFunc(); - } - } - - function hideCover() - { - if ( !currentCover ) - return; - - var win = CKEDITOR.document.getWindow(); - currentCover.hide(); - win.removeListener( 'resize', resizeCover ); - - if ( CKEDITOR.env.ie6Compat ) - { - win.$.setTimeout( function() - { - var prevScrollHandler = window.onscroll && window.onscroll.prevScrollHandler; - window.onscroll = prevScrollHandler || null; - }, 0 ); - } - resizeCover = null; - } - - function removeCovers() - { - for ( var coverId in covers ) - covers[ coverId ].remove(); - covers = {}; - } - - var accessKeyProcessors = {}; - - var accessKeyDownHandler = function( evt ) - { - var ctrl = evt.data.$.ctrlKey || evt.data.$.metaKey, - alt = evt.data.$.altKey, - shift = evt.data.$.shiftKey, - key = String.fromCharCode( evt.data.$.keyCode ), - keyProcessor = accessKeyProcessors[( ctrl ? 'CTRL+' : '' ) + ( alt ? 'ALT+' : '') + ( shift ? 'SHIFT+' : '' ) + key]; - - if ( !keyProcessor || !keyProcessor.length ) - return; - - keyProcessor = keyProcessor[keyProcessor.length - 1]; - keyProcessor.keydown && keyProcessor.keydown.call( keyProcessor.uiElement, keyProcessor.dialog, keyProcessor.key ); - evt.data.preventDefault(); - }; - - var accessKeyUpHandler = function( evt ) - { - var ctrl = evt.data.$.ctrlKey || evt.data.$.metaKey, - alt = evt.data.$.altKey, - shift = evt.data.$.shiftKey, - key = String.fromCharCode( evt.data.$.keyCode ), - keyProcessor = accessKeyProcessors[( ctrl ? 'CTRL+' : '' ) + ( alt ? 'ALT+' : '') + ( shift ? 'SHIFT+' : '' ) + key]; - - if ( !keyProcessor || !keyProcessor.length ) - return; - - keyProcessor = keyProcessor[keyProcessor.length - 1]; - if ( keyProcessor.keyup ) - { - keyProcessor.keyup.call( keyProcessor.uiElement, keyProcessor.dialog, keyProcessor.key ); - evt.data.preventDefault(); - } - }; - - var registerAccessKey = function( uiElement, dialog, key, downFunc, upFunc ) - { - var procList = accessKeyProcessors[key] || ( accessKeyProcessors[key] = [] ); - procList.push( { - uiElement : uiElement, - dialog : dialog, - key : key, - keyup : upFunc || uiElement.accessKeyUp, - keydown : downFunc || uiElement.accessKeyDown - } ); - }; - - var unregisterAccessKey = function( obj ) - { - for ( var i in accessKeyProcessors ) - { - var list = accessKeyProcessors[i]; - for ( var j = list.length - 1 ; j >= 0 ; j-- ) - { - if ( list[j].dialog == obj || list[j].uiElement == obj ) - list.splice( j, 1 ); - } - if ( list.length === 0 ) - delete accessKeyProcessors[i]; - } - }; - - var tabAccessKeyUp = function( dialog, key ) - { - if ( dialog._.accessKeyMap[key] ) - dialog.selectPage( dialog._.accessKeyMap[key] ); - }; - - var tabAccessKeyDown = function( dialog, key ) - { - }; - - // ESC, ENTER - var preventKeyBubblingKeys = { 27 :1, 13 :1 }; - var preventKeyBubbling = function( e ) - { - if ( e.data.getKeystroke() in preventKeyBubblingKeys ) - e.data.stopPropagation(); - }; - - (function() - { - CKEDITOR.ui.dialog = - { - /** - * The base class of all dialog UI elements. - * @constructor - * @param {CKEDITOR.dialog} dialog Parent dialog object. - * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition Element - * definition. Accepted fields: - * <ul> - * <li><strong>id</strong> (Required) The id of the UI element. See {@link - * CKEDITOR.dialog#getContentElement}</li> - * <li><strong>type</strong> (Required) The type of the UI element. The - * value to this field specifies which UI element class will be used to - * generate the final widget.</li> - * <li><strong>title</strong> (Optional) The popup tooltip for the UI - * element.</li> - * <li><strong>hidden</strong> (Optional) A flag that tells if the element - * should be initially visible.</li> - * <li><strong>className</strong> (Optional) Additional CSS class names - * to add to the UI element. Separated by space.</li> - * <li><strong>style</strong> (Optional) Additional CSS inline styles - * to add to the UI element. A semicolon (;) is required after the last - * style declaration.</li> - * <li><strong>accessKey</strong> (Optional) The alphanumeric access key - * for this element. Access keys are automatically prefixed by CTRL.</li> - * <li><strong>on*</strong> (Optional) Any UI element definition field that - * starts with <em>on</em> followed immediately by a capital letter and - * probably more letters is an event handler. Event handlers may be further - * divided into registered event handlers and DOM event handlers. Please - * refer to {@link CKEDITOR.ui.dialog.uiElement#registerEvents} and - * {@link CKEDITOR.ui.dialog.uiElement#eventProcessors} for more - * information.</li> - * </ul> - * @param {Array} htmlList - * List of HTML code to be added to the dialog's content area. - * @param {Function|String} nodeNameArg - * A function returning a string, or a simple string for the node name for - * the root DOM node. Default is 'div'. - * @param {Function|Object} stylesArg - * A function returning an object, or a simple object for CSS styles applied - * to the DOM node. Default is empty object. - * @param {Function|Object} attributesArg - * A fucntion returning an object, or a simple object for attributes applied - * to the DOM node. Default is empty object. - * @param {Function|String} contentsArg - * A function returning a string, or a simple string for the HTML code inside - * the root DOM node. Default is empty string. - * @example - */ - uiElement : function( dialog, elementDefinition, htmlList, nodeNameArg, stylesArg, attributesArg, contentsArg ) - { - if ( arguments.length < 4 ) - return; - - var nodeName = ( nodeNameArg.call ? nodeNameArg( elementDefinition ) : nodeNameArg ) || 'div', - html = [ '<', nodeName, ' ' ], - styles = ( stylesArg && stylesArg.call ? stylesArg( elementDefinition ) : stylesArg ) || {}, - attributes = ( attributesArg && attributesArg.call ? attributesArg( elementDefinition ) : attributesArg ) || {}, - innerHTML = ( contentsArg && contentsArg.call ? contentsArg.call( this, dialog, elementDefinition ) : contentsArg ) || '', - domId = this.domId = attributes.id || CKEDITOR.tools.getNextNumber() + '_uiElement', - id = this.id = elementDefinition.id, - i; - - // Set the id, a unique id is required for getElement() to work. - attributes.id = domId; - - // Set the type and definition CSS class names. - var classes = {}; - if ( elementDefinition.type ) - classes[ 'cke_dialog_ui_' + elementDefinition.type ] = 1; - if ( elementDefinition.className ) - classes[ elementDefinition.className ] = 1; - var attributeClasses = ( attributes['class'] && attributes['class'].split ) ? attributes['class'].split( ' ' ) : []; - for ( i = 0 ; i < attributeClasses.length ; i++ ) - { - if ( attributeClasses[i] ) - classes[ attributeClasses[i] ] = 1; - } - var finalClasses = []; - for ( i in classes ) - finalClasses.push( i ); - attributes['class'] = finalClasses.join( ' ' ); - - // Set the popup tooltop. - if ( elementDefinition.title ) - attributes.title = elementDefinition.title; - - // Write the inline CSS styles. - var styleStr = ( elementDefinition.style || '' ).split( ';' ); - for ( i in styles ) - styleStr.push( i + ':' + styles[i] ); - if ( elementDefinition.hidden ) - styleStr.push( 'display:none' ); - for ( i = styleStr.length - 1 ; i >= 0 ; i-- ) - { - if ( styleStr[i] === '' ) - styleStr.splice( i, 1 ); - } - if ( styleStr.length > 0 ) - attributes.style = ( attributes.style ? ( attributes.style + '; ' ) : '' ) + styleStr.join( '; ' ); - - // Write the attributes. - for ( i in attributes ) - html.push( i + '="' + CKEDITOR.tools.htmlEncode( attributes[i] ) + '" '); - - // Write the content HTML. - html.push( '>', innerHTML, '</', nodeName, '>' ); - - // Add contents to the parent HTML array. - htmlList.push( html.join( '' ) ); - - ( this._ || ( this._ = {} ) ).dialog = dialog; - - // Override isChanged if it is defined in element definition. - if ( typeof( elementDefinition.isChanged ) == 'boolean' ) - this.isChanged = function(){ return elementDefinition.isChanged; }; - if ( typeof( elementDefinition.isChanged ) == 'function' ) - this.isChanged = elementDefinition.isChanged; - - // Add events. - CKEDITOR.event.implementOn( this ); - - this.registerEvents( elementDefinition ); - if ( this.accessKeyUp && this.accessKeyDown && elementDefinition.accessKey ) - registerAccessKey( this, dialog, 'CTRL+' + elementDefinition.accessKey ); - - var me = this; - dialog.on( 'load', function() - { - if ( me.getInputElement() ) - { - me.getInputElement().on( 'focus', function() - { - dialog._.tabBarMode = false; - dialog._.hasFocus = true; - me.fire( 'focus' ); - }, me ); - } - } ); - - // Register the object as a tab focus if it can be included. - if ( this.keyboardFocusable ) - { - this.tabIndex = elementDefinition.tabIndex || 0; - - this.focusIndex = dialog._.focusList.push( this ) - 1; - this.on( 'focus', function() - { - dialog._.currentFocusIndex = me.focusIndex; - } ); - } - - // Completes this object with everything we have in the - // definition. - CKEDITOR.tools.extend( this, elementDefinition ); - }, - - /** - * Horizontal layout box for dialog UI elements, auto-expends to available width of container. - * @constructor - * @extends CKEDITOR.ui.dialog.uiElement - * @param {CKEDITOR.dialog} dialog - * Parent dialog object. - * @param {Array} childObjList - * Array of {@link CKEDITOR.ui.dialog.uiElement} objects inside this - * container. - * @param {Array} childHtmlList - * Array of HTML code that correspond to the HTML output of all the - * objects in childObjList. - * @param {Array} htmlList - * Array of HTML code that this element will output to. - * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition - * The element definition. Accepted fields: - * <ul> - * <li><strong>widths</strong> (Optional) The widths of child cells.</li> - * <li><strong>height</strong> (Optional) The height of the layout.</li> - * <li><strong>padding</strong> (Optional) The padding width inside child - * cells.</li> - * <li><strong>align</strong> (Optional) The alignment of the whole layout - * </li> - * </ul> - * @example - */ - hbox : function( dialog, childObjList, childHtmlList, htmlList, elementDefinition ) - { - if ( arguments.length < 4 ) - return; - - this._ || ( this._ = {} ); - - var children = this._.children = childObjList, - widths = elementDefinition && elementDefinition.widths || null, - height = elementDefinition && elementDefinition.height || null, - styles = {}, - i; - /** @ignore */ - var innerHTML = function() - { - var html = [ '<tbody><tr class="cke_dialog_ui_hbox">' ]; - for ( i = 0 ; i < childHtmlList.length ; i++ ) - { - var className = 'cke_dialog_ui_hbox_child', - styles = []; - if ( i === 0 ) - className = 'cke_dialog_ui_hbox_first'; - if ( i == childHtmlList.length - 1 ) - className = 'cke_dialog_ui_hbox_last'; - html.push( '<td class="', className, '" role="presentation" ' ); - if ( widths ) - { - if ( widths[i] ) - styles.push( 'width:' + CKEDITOR.tools.cssLength( widths[i] ) ); - } - else - styles.push( 'width:' + Math.floor( 100 / childHtmlList.length ) + '%' ); - if ( height ) - styles.push( 'height:' + CKEDITOR.tools.cssLength( height ) ); - if ( elementDefinition && elementDefinition.padding != undefined ) - styles.push( 'padding:' + CKEDITOR.tools.cssLength( elementDefinition.padding ) ); - if ( styles.length > 0 ) - html.push( 'style="' + styles.join('; ') + '" ' ); - html.push( '>', childHtmlList[i], '</td>' ); - } - html.push( '</tr></tbody>' ); - return html.join( '' ); - }; - - var attribs = { role : 'presentation' }; - elementDefinition && elementDefinition.align && ( attribs.align = elementDefinition.align ); - - CKEDITOR.ui.dialog.uiElement.call( - this, - dialog, - elementDefinition || { type : 'hbox' }, - htmlList, - 'table', - styles, - attribs, - innerHTML ); - }, - - /** - * Vertical layout box for dialog UI elements. - * @constructor - * @extends CKEDITOR.ui.dialog.hbox - * @param {CKEDITOR.dialog} dialog - * Parent dialog object. - * @param {Array} childObjList - * Array of {@link CKEDITOR.ui.dialog.uiElement} objects inside this - * container. - * @param {Array} childHtmlList - * Array of HTML code that correspond to the HTML output of all the - * objects in childObjList. - * @param {Array} htmlList - * Array of HTML code that this element will output to. - * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition - * The element definition. Accepted fields: - * <ul> - * <li><strong>width</strong> (Optional) The width of the layout.</li> - * <li><strong>heights</strong> (Optional) The heights of individual cells. - * </li> - * <li><strong>align</strong> (Optional) The alignment of the layout.</li> - * <li><strong>padding</strong> (Optional) The padding width inside child - * cells.</li> - * <li><strong>expand</strong> (Optional) Whether the layout should expand - * vertically to fill its container.</li> - * </ul> - * @example - */ - vbox : function( dialog, childObjList, childHtmlList, htmlList, elementDefinition ) - { - if (arguments.length < 3 ) - return; - - this._ || ( this._ = {} ); - - var children = this._.children = childObjList, - width = elementDefinition && elementDefinition.width || null, - heights = elementDefinition && elementDefinition.heights || null; - /** @ignore */ - var innerHTML = function() - { - var html = [ '<table role="presentation" cellspacing="0" border="0" ' ]; - html.push( 'style="' ); - if ( elementDefinition && elementDefinition.expand ) - html.push( 'height:100%;' ); - html.push( 'width:' + CKEDITOR.tools.cssLength( width || '100%' ), ';' ); - html.push( '"' ); - html.push( 'align="', CKEDITOR.tools.htmlEncode( - ( elementDefinition && elementDefinition.align ) || ( dialog.getParentEditor().lang.dir == 'ltr' ? 'left' : 'right' ) ), '" ' ); - - html.push( '><tbody>' ); - for ( var i = 0 ; i < childHtmlList.length ; i++ ) - { - var styles = []; - html.push( '<tr><td role="presentation" ' ); - if ( width ) - styles.push( 'width:' + CKEDITOR.tools.cssLength( width || '100%' ) ); - if ( heights ) - styles.push( 'height:' + CKEDITOR.tools.cssLength( heights[i] ) ); - else if ( elementDefinition && elementDefinition.expand ) - styles.push( 'height:' + Math.floor( 100 / childHtmlList.length ) + '%' ); - if ( elementDefinition && elementDefinition.padding != undefined ) - styles.push( 'padding:' + CKEDITOR.tools.cssLength( elementDefinition.padding ) ); - if ( styles.length > 0 ) - html.push( 'style="', styles.join( '; ' ), '" ' ); - html.push( ' class="cke_dialog_ui_vbox_child">', childHtmlList[i], '</td></tr>' ); - } - html.push( '</tbody></table>' ); - return html.join( '' ); - }; - CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition || { type : 'vbox' }, htmlList, 'div', null, { role : 'presentation' }, innerHTML ); - } - }; - })(); - - CKEDITOR.ui.dialog.uiElement.prototype = - { - /** - * Gets the root DOM element of this dialog UI object. - * @returns {CKEDITOR.dom.element} Root DOM element of UI object. - * @example - * uiElement.getElement().hide(); - */ - getElement : function() - { - return CKEDITOR.document.getById( this.domId ); - }, - - /** - * Gets the DOM element that the user inputs values. - * This function is used by setValue(), getValue() and focus(). It should - * be overrided in child classes where the input element isn't the root - * element. - * @returns {CKEDITOR.dom.element} The element where the user input values. - * @example - * var rawValue = textInput.getInputElement().$.value; - */ - getInputElement : function() - { - return this.getElement(); - }, - - /** - * Gets the parent dialog object containing this UI element. - * @returns {CKEDITOR.dialog} Parent dialog object. - * @example - * var dialog = uiElement.getDialog(); - */ - getDialog : function() - { - return this._.dialog; - }, - - /** - * Sets the value of this dialog UI object. - * @param {Object} value The new value. - * @returns {CKEDITOR.dialog.uiElement} The current UI element. - * @example - * uiElement.setValue( 'Dingo' ); - */ - setValue : function( value ) - { - this.getInputElement().setValue( value ); - this.fire( 'change', { value : value } ); - return this; - }, - - /** - * Gets the current value of this dialog UI object. - * @returns {Object} The current value. - * @example - * var myValue = uiElement.getValue(); - */ - getValue : function() - { - return this.getInputElement().getValue(); - }, - - /** - * Tells whether the UI object's value has changed. - * @returns {Boolean} true if changed, false if not changed. - * @example - * if ( uiElement.isChanged() ) - * confirm( 'Value changed! Continue?' ); - */ - isChanged : function() - { - // Override in input classes. - return false; - }, - - /** - * Selects the parent tab of this element. Usually called by focus() or overridden focus() methods. - * @returns {CKEDITOR.dialog.uiElement} The current UI element. - * @example - * focus : function() - * { - * this.selectParentTab(); - * // do something else. - * } - */ - selectParentTab : function() - { - var element = this.getInputElement(), - cursor = element, - tabId; - while ( ( cursor = cursor.getParent() ) && cursor.$.className.search( 'cke_dialog_page_contents' ) == -1 ) - { /*jsl:pass*/ } - - // Some widgets don't have parent tabs (e.g. OK and Cancel buttons). - if ( !cursor ) - return this; - - tabId = cursor.getAttribute( 'name' ); - // Avoid duplicate select. - if ( this._.dialog._.currentTabId != tabId ) - this._.dialog.selectPage( tabId ); - return this; - }, - - /** - * Puts the focus to the UI object. Switches tabs if the UI object isn't in the active tab page. - * @returns {CKEDITOR.dialog.uiElement} The current UI element. - * @example - * uiElement.focus(); - */ - focus : function() - { - this.selectParentTab().getInputElement().focus(); - return this; - }, - - /** - * Registers the on* event handlers defined in the element definition. - * The default behavior of this function is: - * <ol> - * <li> - * If the on* event is defined in the class's eventProcesors list, - * then the registration is delegated to the corresponding function - * in the eventProcessors list. - * </li> - * <li> - * If the on* event is not defined in the eventProcessors list, then - * register the event handler under the corresponding DOM event of - * the UI element's input DOM element (as defined by the return value - * of {@link CKEDITOR.ui.dialog.uiElement#getInputElement}). - * </li> - * </ol> - * This function is only called at UI element instantiation, but can - * be overridded in child classes if they require more flexibility. - * @param {CKEDITOR.dialog.uiElementDefinition} definition The UI element - * definition. - * @returns {CKEDITOR.dialog.uiElement} The current UI element. - * @example - */ - registerEvents : function( definition ) - { - var regex = /^on([A-Z]\w+)/, - match; - - var registerDomEvent = function( uiElement, dialog, eventName, func ) - { - dialog.on( 'load', function() - { - uiElement.getInputElement().on( eventName, func, uiElement ); - }); - }; - - for ( var i in definition ) - { - if ( !( match = i.match( regex ) ) ) - continue; - if ( this.eventProcessors[i] ) - this.eventProcessors[i].call( this, this._.dialog, definition[i] ); - else - registerDomEvent( this, this._.dialog, match[1].toLowerCase(), definition[i] ); - } - - return this; - }, - - /** - * The event processor list used by - * {@link CKEDITOR.ui.dialog.uiElement#getInputElement} at UI element - * instantiation. The default list defines three on* events: - * <ol> - * <li>onLoad - Called when the element's parent dialog opens for the - * first time</li> - * <li>onShow - Called whenever the element's parent dialog opens.</li> - * <li>onHide - Called whenever the element's parent dialog closes.</li> - * </ol> - * @field - * @type Object - * @example - * // This connects the 'click' event in CKEDITOR.ui.dialog.button to onClick - * // handlers in the UI element's definitions. - * CKEDITOR.ui.dialog.button.eventProcessors = CKEDITOR.tools.extend( {}, - * CKEDITOR.ui.dialog.uiElement.prototype.eventProcessors, - * { onClick : function( dialog, func ) { this.on( 'click', func ); } }, - * true ); - */ - eventProcessors : - { - onLoad : function( dialog, func ) - { - dialog.on( 'load', func, this ); - }, - - onShow : function( dialog, func ) - { - dialog.on( 'show', func, this ); - }, - - onHide : function( dialog, func ) - { - dialog.on( 'hide', func, this ); - } - }, - - /** - * The default handler for a UI element's access key down event, which - * tries to put focus to the UI element.<br /> - * Can be overridded in child classes for more sophisticaed behavior. - * @param {CKEDITOR.dialog} dialog The parent dialog object. - * @param {String} key The key combination pressed. Since access keys - * are defined to always include the CTRL key, its value should always - * include a 'CTRL+' prefix. - * @example - */ - accessKeyDown : function( dialog, key ) - { - this.focus(); - }, - - /** - * The default handler for a UI element's access key up event, which - * does nothing.<br /> - * Can be overridded in child classes for more sophisticated behavior. - * @param {CKEDITOR.dialog} dialog The parent dialog object. - * @param {String} key The key combination pressed. Since access keys - * are defined to always include the CTRL key, its value should always - * include a 'CTRL+' prefix. - * @example - */ - accessKeyUp : function( dialog, key ) - { - }, - - /** - * Disables a UI element. - * @example - */ - disable : function() - { - var element = this.getInputElement(); - element.setAttribute( 'disabled', 'true' ); - element.addClass( 'cke_disabled' ); - }, - - /** - * Enables a UI element. - * @example - */ - enable : function() - { - var element = this.getInputElement(); - element.removeAttribute( 'disabled' ); - element.removeClass( 'cke_disabled' ); - }, - - /** - * Determines whether an UI element is enabled or not. - * @returns {Boolean} Whether the UI element is enabled. - * @example - */ - isEnabled : function() - { - return !this.getInputElement().getAttribute( 'disabled' ); - }, - - /** - * Determines whether an UI element is visible or not. - * @returns {Boolean} Whether the UI element is visible. - * @example - */ - isVisible : function() - { - return this.getInputElement().isVisible(); - }, - - /** - * Determines whether an UI element is focus-able or not. - * Focus-able is defined as being both visible and enabled. - * @returns {Boolean} Whether the UI element can be focused. - * @example - */ - isFocusable : function() - { - if ( !this.isEnabled() || !this.isVisible() ) - return false; - return true; - } - }; - - CKEDITOR.ui.dialog.hbox.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.uiElement, - /** - * @lends CKEDITOR.ui.dialog.hbox.prototype - */ - { - /** - * Gets a child UI element inside this container. - * @param {Array|Number} indices An array or a single number to indicate the child's - * position in the container's descendant tree. Omit to get all the children in an array. - * @returns {Array|CKEDITOR.ui.dialog.uiElement} Array of all UI elements in the container - * if no argument given, or the specified UI element if indices is given. - * @example - * var checkbox = hbox.getChild( [0,1] ); - * checkbox.setValue( true ); - */ - getChild : function( indices ) - { - // If no arguments, return a clone of the children array. - if ( arguments.length < 1 ) - return this._.children.concat(); - - // If indices isn't array, make it one. - if ( !indices.splice ) - indices = [ indices ]; - - // Retrieve the child element according to tree position. - if ( indices.length < 2 ) - return this._.children[ indices[0] ]; - else - return ( this._.children[ indices[0] ] && this._.children[ indices[0] ].getChild ) ? - this._.children[ indices[0] ].getChild( indices.slice( 1, indices.length ) ) : - null; - } - }, true ); - - CKEDITOR.ui.dialog.vbox.prototype = new CKEDITOR.ui.dialog.hbox(); - - - - (function() - { - var commonBuilder = { - build : function( dialog, elementDefinition, output ) - { - var children = elementDefinition.children, - child, - childHtmlList = [], - childObjList = []; - for ( var i = 0 ; ( i < children.length && ( child = children[i] ) ) ; i++ ) - { - var childHtml = []; - childHtmlList.push( childHtml ); - childObjList.push( CKEDITOR.dialog._.uiElementBuilders[ child.type ].build( dialog, child, childHtml ) ); - } - return new CKEDITOR.ui.dialog[elementDefinition.type]( dialog, childObjList, childHtmlList, output, elementDefinition ); - } - }; - - CKEDITOR.dialog.addUIElement( 'hbox', commonBuilder ); - CKEDITOR.dialog.addUIElement( 'vbox', commonBuilder ); - })(); - - /** - * Generic dialog command. It opens a specific dialog when executed. - * @constructor - * @augments CKEDITOR.commandDefinition - * @param {string} dialogName The name of the dialog to open when executing - * this command. - * @example - * // Register the "link" command, which opens the "link" dialog. - * editor.addCommand( 'link', <b>new CKEDITOR.dialogCommand( 'link' )</b> ); - */ - CKEDITOR.dialogCommand = function( dialogName ) - { - this.dialogName = dialogName; - }; - - CKEDITOR.dialogCommand.prototype = - { - /** @ignore */ - exec : function( editor ) - { - editor.openDialog( this.dialogName ); - }, - - // Dialog commands just open a dialog ui, thus require no undo logic, - // undo support should dedicate to specific dialog implementation. - canUndo: false, - - editorFocus : CKEDITOR.env.ie || CKEDITOR.env.webkit - }; - - (function() - { - var notEmptyRegex = /^([a]|[^a])+$/, - integerRegex = /^\d*$/, - numberRegex = /^\d*(?:\.\d+)?$/; - - CKEDITOR.VALIDATE_OR = 1; - CKEDITOR.VALIDATE_AND = 2; - - CKEDITOR.dialog.validate = - { - functions : function() - { - return function() - { - /** - * It's important for validate functions to be able to accept the value - * as argument in addition to this.getValue(), so that it is possible to - * combine validate functions together to make more sophisticated - * validators. - */ - var value = this && this.getValue ? this.getValue() : arguments[0]; - - var msg = undefined, - relation = CKEDITOR.VALIDATE_AND, - functions = [], i; - - for ( i = 0 ; i < arguments.length ; i++ ) - { - if ( typeof( arguments[i] ) == 'function' ) - functions.push( arguments[i] ); - else - break; - } - - if ( i < arguments.length && typeof( arguments[i] ) == 'string' ) - { - msg = arguments[i]; - i++; - } - - if ( i < arguments.length && typeof( arguments[i]) == 'number' ) - relation = arguments[i]; - - var passed = ( relation == CKEDITOR.VALIDATE_AND ? true : false ); - for ( i = 0 ; i < functions.length ; i++ ) - { - if ( relation == CKEDITOR.VALIDATE_AND ) - passed = passed && functions[i]( value ); - else - passed = passed || functions[i]( value ); - } - - if ( !passed ) - { - if ( msg !== undefined ) - alert( msg ); - if ( this && ( this.select || this.focus ) ) - ( this.select || this.focus )(); - return false; - } - - return true; - }; - }, - - regex : function( regex, msg ) - { - /* - * Can be greatly shortened by deriving from functions validator if code size - * turns out to be more important than performance. - */ - return function() - { - var value = this && this.getValue ? this.getValue() : arguments[0]; - if ( !regex.test( value ) ) - { - if ( msg !== undefined ) - alert( msg ); - if ( this && ( this.select || this.focus ) ) - { - if ( this.select ) - this.select(); - else - this.focus(); - } - return false; - } - return true; - }; - }, - - notEmpty : function( msg ) - { - return this.regex( notEmptyRegex, msg ); - }, - - integer : function( msg ) - { - return this.regex( integerRegex, msg ); - }, - - 'number' : function( msg ) - { - return this.regex( numberRegex, msg ); - }, - - equals : function( value, msg ) - { - return this.functions( function( val ){ return val == value; }, msg ); - }, - - notEqual : function( value, msg ) - { - return this.functions( function( val ){ return val != value; }, msg ); - } - }; - - CKEDITOR.on( 'instanceDestroyed', function( evt ) - { - // Remove dialog cover on last instance destroy. - if ( CKEDITOR.tools.isEmpty( CKEDITOR.instances ) ) - { - var currentTopDialog; - while ( ( currentTopDialog = CKEDITOR.dialog._.currentTop ) ) - currentTopDialog.hide(); - removeCovers(); - } - - var dialogs = evt.editor._.storedDialogs; - for ( var name in dialogs ) - dialogs[ name ].destroy(); - - }); - - })(); -})(); - -// Extend the CKEDITOR.editor class with dialog specific functions. -CKEDITOR.tools.extend( CKEDITOR.editor.prototype, - /** @lends CKEDITOR.editor.prototype */ - { - /** - * Loads and opens a registered dialog. - * @param {String} dialogName The registered name of the dialog. - * @param {Function} callback The function to be invoked after dialog instance created. - * @see CKEDITOR.dialog.add - * @example - * CKEDITOR.instances.editor1.openDialog( 'smiley' ); - * @returns {CKEDITOR.dialog} The dialog object corresponding to the dialog displayed. null if the dialog name is not registered. - */ - openDialog : function( dialogName, callback ) - { - var dialogDefinitions = CKEDITOR.dialog._.dialogDefinitions[ dialogName ], - dialogSkin = this.skin.dialog; - - // If the dialogDefinition is already loaded, open it immediately. - if ( typeof dialogDefinitions == 'function' && dialogSkin._isLoaded ) - { - var storedDialogs = this._.storedDialogs || - ( this._.storedDialogs = {} ); - - var dialog = storedDialogs[ dialogName ] || - ( storedDialogs[ dialogName ] = new CKEDITOR.dialog( this, dialogName ) ); - - callback && callback.call( dialog, dialog ); - dialog.show(); - - return dialog; - } - else if ( dialogDefinitions == 'failed' ) - throw new Error( '[CKEDITOR.dialog.openDialog] Dialog "' + dialogName + '" failed when loading definition.' ); - - // Not loaded? Load the .js file first. - var body = CKEDITOR.document.getBody(), - cursor = body.$.style.cursor, - me = this; - - body.setStyle( 'cursor', 'wait' ); - - function onDialogFileLoaded( success ) - { - var dialogDefinition = CKEDITOR.dialog._.dialogDefinitions[ dialogName ], - skin = me.skin.dialog; - - // Check if both skin part and definition is loaded. - if ( !skin._isLoaded || loadDefinition && typeof success == 'undefined' ) - return; - - // In case of plugin error, mark it as loading failed. - if ( typeof dialogDefinition != 'function' ) - CKEDITOR.dialog._.dialogDefinitions[ dialogName ] = 'failed'; - - me.openDialog( dialogName, callback ); - body.setStyle( 'cursor', cursor ); - } - - if ( typeof dialogDefinitions == 'string' ) - { - var loadDefinition = 1; - CKEDITOR.scriptLoader.load( CKEDITOR.getUrl( dialogDefinitions ), onDialogFileLoaded ); - } - - CKEDITOR.skins.load( this, 'dialog', onDialogFileLoaded ); - - return null; - } - }); - -CKEDITOR.plugins.add( 'dialog', - { - requires : [ 'dialogui' ] - }); - -// Dialog related configurations. - -/** - * The color of the dialog background cover. It should be a valid CSS color - * string. - * @name CKEDITOR.config.dialog_backgroundCoverColor - * @type String - * @default 'white' - * @example - * config.dialog_backgroundCoverColor = 'rgb(255, 254, 253)'; - */ - -/** - * The opacity of the dialog background cover. It should be a number within the - * range [0.0, 1.0]. - * @name CKEDITOR.config.dialog_backgroundCoverOpacity - * @type Number - * @default 0.5 - * @example - * config.dialog_backgroundCoverOpacity = 0.7; - */ - -/** - * If the dialog has more than one tab, put focus into the first tab as soon as dialog is opened. - * @name CKEDITOR.config.dialog_startupFocusTab - * @type Boolean - * @default false - * @example - * config.dialog_startupFocusTab = true; - */ - -/** - * The distance of magnetic borders used in moving and resizing dialogs, - * measured in pixels. - * @name CKEDITOR.config.dialog_magnetDistance - * @type Number - * @default 20 - * @example - * config.dialog_magnetDistance = 30; - */ - -/** - * Fired when a dialog definition is about to be used to create a dialog into - * an editor instance. This event makes it possible to customize the definition - * before creating it. - * <p>Note that this event is called only the first time a specific dialog is - * opened. Successive openings will use the cached dialog, and this event will - * not get fired.</p> - * @name CKEDITOR#dialogDefinition - * @event - * @param {CKEDITOR.dialog.dialogDefinition} data The dialog defination that - * is being loaded. - * @param {CKEDITOR.editor} editor The editor instance that will use the - * dialog. - */ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview The floating dialog plugin.
+ */
+
+/**
+ * No resize for this dialog.
+ * @constant
+ */
+CKEDITOR.DIALOG_RESIZE_NONE = 0;
+
+/**
+ * Only allow horizontal resizing for this dialog, disable vertical resizing.
+ * @constant
+ */
+CKEDITOR.DIALOG_RESIZE_WIDTH = 1;
+
+/**
+ * Only allow vertical resizing for this dialog, disable horizontal resizing.
+ * @constant
+ */
+CKEDITOR.DIALOG_RESIZE_HEIGHT = 2;
+
+/*
+ * Allow the dialog to be resized in both directions.
+ * @constant
+ */
+CKEDITOR.DIALOG_RESIZE_BOTH = 3;
+
+(function()
+{
+ var cssLength = CKEDITOR.tools.cssLength;
+ function isTabVisible( tabId )
+ {
+ return !!this._.tabs[ tabId ][ 0 ].$.offsetHeight;
+ }
+
+ function getPreviousVisibleTab()
+ {
+ var tabId = this._.currentTabId,
+ length = this._.tabIdList.length,
+ tabIndex = CKEDITOR.tools.indexOf( this._.tabIdList, tabId ) + length;
+
+ for ( var i = tabIndex - 1 ; i > tabIndex - length ; i-- )
+ {
+ if ( isTabVisible.call( this, this._.tabIdList[ i % length ] ) )
+ return this._.tabIdList[ i % length ];
+ }
+
+ return null;
+ }
+
+ function getNextVisibleTab()
+ {
+ var tabId = this._.currentTabId,
+ length = this._.tabIdList.length,
+ tabIndex = CKEDITOR.tools.indexOf( this._.tabIdList, tabId );
+
+ for ( var i = tabIndex + 1 ; i < tabIndex + length ; i++ )
+ {
+ if ( isTabVisible.call( this, this._.tabIdList[ i % length ] ) )
+ return this._.tabIdList[ i % length ];
+ }
+
+ return null;
+ }
+
+
+ function clearOrRecoverTextInputValue( container, isRecover )
+ {
+ var inputs = container.$.getElementsByTagName( 'input' );
+ for ( var i = 0, length = inputs.length; i < length ; i++ )
+ {
+ var item = new CKEDITOR.dom.element( inputs[ i ] );
+
+ if ( item.getAttribute( 'type' ).toLowerCase() == 'text' )
+ {
+ if ( isRecover )
+ {
+ item.setAttribute( 'value', item.getCustomData( 'fake_value' ) || '' );
+ item.removeCustomData( 'fake_value' );
+ }
+ else
+ {
+ item.setCustomData( 'fake_value', item.getAttribute( 'value' ) );
+ item.setAttribute( 'value', '' );
+ }
+ }
+ }
+ }
+
+ // Handle dialog element validation state UI changes.
+ function handleFieldValidated( isValid, msg )
+ {
+ var input = this.getInputElement();
+ if ( input )
+ {
+ isValid ? input.removeAttribute( 'aria-invalid' )
+ : input.setAttribute( 'aria-invalid', true );
+ }
+
+ if ( !isValid )
+ {
+ if ( this.select )
+ this.select();
+ else
+ this.focus();
+ }
+
+ msg && alert( msg );
+
+ this.fire( 'validated', { valid : isValid, msg : msg } );
+ }
+
+ function resetField()
+ {
+ var input = this.getInputElement();
+ input && input.removeAttribute( 'aria-invalid' );
+ }
+
+
+ /**
+ * This is the base class for runtime dialog objects. An instance of this
+ * class represents a single named dialog for a single editor instance.
+ * @param {Object} editor The editor which created the dialog.
+ * @param {String} dialogName The dialog's registered name.
+ * @constructor
+ * @example
+ * var dialogObj = new CKEDITOR.dialog( editor, 'smiley' );
+ */
+ CKEDITOR.dialog = function( editor, dialogName )
+ {
+ // Load the dialog definition.
+ var definition = CKEDITOR.dialog._.dialogDefinitions[ dialogName ],
+ defaultDefinition = CKEDITOR.tools.clone( defaultDialogDefinition ),
+ buttonsOrder = editor.config.dialog_buttonsOrder || 'OS',
+ dir = editor.lang.dir,
+ tabsToRemove = {},
+ i,
+ processed;
+
+ if ( ( buttonsOrder == 'OS' && CKEDITOR.env.mac ) || // The buttons in MacOS Apps are in reverse order (#4750)
+ ( buttonsOrder == 'rtl' && dir == 'ltr' ) ||
+ ( buttonsOrder == 'ltr' && dir == 'rtl' ) )
+ defaultDefinition.buttons.reverse();
+
+
+ // Completes the definition with the default values.
+ definition = CKEDITOR.tools.extend( definition( editor ), defaultDefinition );
+
+ // Clone a functionally independent copy for this dialog.
+ definition = CKEDITOR.tools.clone( definition );
+
+ // Create a complex definition object, extending it with the API
+ // functions.
+ definition = new definitionObject( this, definition );
+
+ var doc = CKEDITOR.document;
+
+ var themeBuilt = editor.theme.buildDialog( editor );
+
+ // Initialize some basic parameters.
+ this._ =
+ {
+ editor : editor,
+ element : themeBuilt.element,
+ name : dialogName,
+ contentSize : { width : 0, height : 0 },
+ size : { width : 0, height : 0 },
+ contents : {},
+ buttons : {},
+ accessKeyMap : {},
+
+ // Initialize the tab and page map.
+ tabs : {},
+ tabIdList : [],
+ currentTabId : null,
+ currentTabIndex : null,
+ pageCount : 0,
+ lastTab : null,
+ tabBarMode : false,
+
+ // Initialize the tab order array for input widgets.
+ focusList : [],
+ currentFocusIndex : 0,
+ hasFocus : false
+ };
+
+ this.parts = themeBuilt.parts;
+
+ CKEDITOR.tools.setTimeout( function()
+ {
+ editor.fire( 'ariaWidget', this.parts.contents );
+ },
+ 0, this );
+
+ // Set the startup styles for the dialog, avoiding it enlarging the
+ // page size on the dialog creation.
+ var startStyles = {
+ position : CKEDITOR.env.ie6Compat ? 'absolute' : 'fixed',
+ top : 0,
+ visibility : 'hidden'
+ };
+
+ startStyles[ dir == 'rtl' ? 'right' : 'left' ] = 0;
+ this.parts.dialog.setStyles( startStyles );
+
+
+ // Call the CKEDITOR.event constructor to initialize this instance.
+ CKEDITOR.event.call( this );
+
+ // Fire the "dialogDefinition" event, making it possible to customize
+ // the dialog definition.
+ this.definition = definition = CKEDITOR.fire( 'dialogDefinition',
+ {
+ name : dialogName,
+ definition : definition
+ }
+ , editor ).definition;
+
+ // Cache tabs that should be removed.
+ if ( !( 'removeDialogTabs' in editor._ ) && editor.config.removeDialogTabs )
+ {
+ var removeContents = editor.config.removeDialogTabs.split( ';' );
+
+ for ( i = 0; i < removeContents.length; i++ )
+ {
+ var parts = removeContents[ i ].split( ':' );
+ if ( parts.length == 2 )
+ {
+ var removeDialogName = parts[ 0 ];
+ if ( !tabsToRemove[ removeDialogName ] )
+ tabsToRemove[ removeDialogName ] = [];
+ tabsToRemove[ removeDialogName ].push( parts[ 1 ] );
+ }
+ }
+ editor._.removeDialogTabs = tabsToRemove;
+ }
+
+ // Remove tabs of this dialog.
+ if ( editor._.removeDialogTabs && ( tabsToRemove = editor._.removeDialogTabs[ dialogName ] ) )
+ {
+ for ( i = 0; i < tabsToRemove.length; i++ )
+ definition.removeContents( tabsToRemove[ i ] );
+ }
+
+ // Initialize load, show, hide, ok and cancel events.
+ if ( definition.onLoad )
+ this.on( 'load', definition.onLoad );
+
+ if ( definition.onShow )
+ this.on( 'show', definition.onShow );
+
+ if ( definition.onHide )
+ this.on( 'hide', definition.onHide );
+
+ if ( definition.onOk )
+ {
+ this.on( 'ok', function( evt )
+ {
+ // Dialog confirm might probably introduce content changes (#5415).
+ editor.fire( 'saveSnapshot' );
+ setTimeout( function () { editor.fire( 'saveSnapshot' ); }, 0 );
+ if ( definition.onOk.call( this, evt ) === false )
+ evt.data.hide = false;
+ });
+ }
+
+ if ( definition.onCancel )
+ {
+ this.on( 'cancel', function( evt )
+ {
+ if ( definition.onCancel.call( this, evt ) === false )
+ evt.data.hide = false;
+ });
+ }
+
+ var me = this;
+
+ // Iterates over all items inside all content in the dialog, calling a
+ // function for each of them.
+ var iterContents = function( func )
+ {
+ var contents = me._.contents,
+ stop = false;
+
+ for ( var i in contents )
+ {
+ for ( var j in contents[i] )
+ {
+ stop = func.call( this, contents[i][j] );
+ if ( stop )
+ return;
+ }
+ }
+ };
+
+ this.on( 'ok', function( evt )
+ {
+ iterContents( function( item )
+ {
+ if ( item.validate )
+ {
+ var retval = item.validate( this ),
+ invalid = typeof ( retval ) == 'string' || retval === false;
+
+ if ( invalid )
+ {
+ evt.data.hide = false;
+ evt.stop();
+ }
+
+ handleFieldValidated.call( item, !invalid, typeof retval == 'string' ? retval : undefined );
+ return invalid;
+ }
+ });
+ }, this, null, 0 );
+
+ this.on( 'cancel', function( evt )
+ {
+ iterContents( function( item )
+ {
+ if ( item.isChanged() )
+ {
+ if ( !confirm( editor.lang.common.confirmCancel ) )
+ evt.data.hide = false;
+ return true;
+ }
+ });
+ }, this, null, 0 );
+
+ this.parts.close.on( 'click', function( evt )
+ {
+ if ( this.fire( 'cancel', { hide : true } ).hide !== false )
+ this.hide();
+ evt.data.preventDefault();
+ }, this );
+
+ // Sort focus list according to tab order definitions.
+ function setupFocus()
+ {
+ var focusList = me._.focusList;
+ focusList.sort( function( a, b )
+ {
+ // Mimics browser tab order logics;
+ if ( a.tabIndex != b.tabIndex )
+ return b.tabIndex - a.tabIndex;
+ // Sort is not stable in some browsers,
+ // fall-back the comparator to 'focusIndex';
+ else
+ return a.focusIndex - b.focusIndex;
+ });
+
+ var size = focusList.length;
+ for ( var i = 0; i < size; i++ )
+ focusList[ i ].focusIndex = i;
+ }
+
+ function changeFocus( offset )
+ {
+ var focusList = me._.focusList;
+ offset = offset || 0;
+
+ if ( focusList.length < 1 )
+ return;
+
+ var current = me._.currentFocusIndex;
+
+ // Trigger the 'blur' event of any input element before anything,
+ // since certain UI updates may depend on it.
+ try
+ {
+ focusList[ current ].getInputElement().$.blur();
+ }
+ catch( e ){}
+
+ var startIndex = ( current + offset + focusList.length ) % focusList.length,
+ currentIndex = startIndex;
+ while ( offset && !focusList[ currentIndex ].isFocusable() )
+ {
+ currentIndex = ( currentIndex + offset + focusList.length ) % focusList.length;
+ if ( currentIndex == startIndex )
+ break;
+ }
+
+ focusList[ currentIndex ].focus();
+
+ // Select whole field content.
+ if ( focusList[ currentIndex ].type == 'text' )
+ focusList[ currentIndex ].select();
+ }
+
+ this.changeFocus = changeFocus;
+
+
+ function focusKeydownHandler( evt )
+ {
+ // If I'm not the top dialog, ignore.
+ if ( me != CKEDITOR.dialog._.currentTop )
+ return;
+
+ var keystroke = evt.data.getKeystroke(),
+ rtl = editor.lang.dir == 'rtl';
+
+ processed = 0;
+ if ( keystroke == 9 || keystroke == CKEDITOR.SHIFT + 9 )
+ {
+ var shiftPressed = ( keystroke == CKEDITOR.SHIFT + 9 );
+
+ // Handling Tab and Shift-Tab.
+ if ( me._.tabBarMode )
+ {
+ // Change tabs.
+ var nextId = shiftPressed ? getPreviousVisibleTab.call( me ) : getNextVisibleTab.call( me );
+ me.selectPage( nextId );
+ me._.tabs[ nextId ][ 0 ].focus();
+ }
+ else
+ {
+ // Change the focus of inputs.
+ changeFocus( shiftPressed ? -1 : 1 );
+ }
+
+ processed = 1;
+ }
+ else if ( keystroke == CKEDITOR.ALT + 121 && !me._.tabBarMode && me.getPageCount() > 1 )
+ {
+ // Alt-F10 puts focus into the current tab item in the tab bar.
+ me._.tabBarMode = true;
+ me._.tabs[ me._.currentTabId ][ 0 ].focus();
+ processed = 1;
+ }
+ else if ( ( keystroke == 37 || keystroke == 39 ) && me._.tabBarMode )
+ {
+ // Arrow keys - used for changing tabs.
+ nextId = ( keystroke == ( rtl ? 39 : 37 ) ? getPreviousVisibleTab.call( me ) : getNextVisibleTab.call( me ) );
+ me.selectPage( nextId );
+ me._.tabs[ nextId ][ 0 ].focus();
+ processed = 1;
+ }
+ else if ( ( keystroke == 13 || keystroke == 32 ) && me._.tabBarMode )
+ {
+ this.selectPage( this._.currentTabId );
+ this._.tabBarMode = false;
+ this._.currentFocusIndex = -1;
+ changeFocus( 1 );
+ processed = 1;
+ }
+
+ if ( processed )
+ {
+ evt.stop();
+ evt.data.preventDefault();
+ }
+ }
+
+ function focusKeyPressHandler( evt )
+ {
+ processed && evt.data.preventDefault();
+ }
+
+ var dialogElement = this._.element;
+ // Add the dialog keyboard handlers.
+ this.on( 'show', function()
+ {
+ dialogElement.on( 'keydown', focusKeydownHandler, this, null, 0 );
+ // Some browsers instead, don't cancel key events in the keydown, but in the
+ // keypress. So we must do a longer trip in those cases. (#4531)
+ if ( CKEDITOR.env.opera || ( CKEDITOR.env.gecko && CKEDITOR.env.mac ) )
+ dialogElement.on( 'keypress', focusKeyPressHandler, this );
+
+ } );
+ this.on( 'hide', function()
+ {
+ dialogElement.removeListener( 'keydown', focusKeydownHandler );
+ if ( CKEDITOR.env.opera || ( CKEDITOR.env.gecko && CKEDITOR.env.mac ) )
+ dialogElement.removeListener( 'keypress', focusKeyPressHandler );
+
+ // Reset fields state when closing dialog.
+ iterContents( function( item ) { resetField.apply( item ); } );
+ } );
+ this.on( 'iframeAdded', function( evt )
+ {
+ var doc = new CKEDITOR.dom.document( evt.data.iframe.$.contentWindow.document );
+ doc.on( 'keydown', focusKeydownHandler, this, null, 0 );
+ } );
+
+ // Auto-focus logic in dialog.
+ this.on( 'show', function()
+ {
+ // Setup tabIndex on showing the dialog instead of on loading
+ // to allow dynamic tab order happen in dialog definition.
+ setupFocus();
+
+ if ( editor.config.dialog_startupFocusTab
+ && me._.pageCount > 1 )
+ {
+ me._.tabBarMode = true;
+ me._.tabs[ me._.currentTabId ][ 0 ].focus();
+ }
+ else if ( !this._.hasFocus )
+ {
+ this._.currentFocusIndex = -1;
+
+ // Decide where to put the initial focus.
+ if ( definition.onFocus )
+ {
+ var initialFocus = definition.onFocus.call( this );
+ // Focus the field that the user specified.
+ initialFocus && initialFocus.focus();
+ }
+ // Focus the first field in layout order.
+ else
+ changeFocus( 1 );
+
+ /*
+ * IE BUG: If the initial focus went into a non-text element (e.g. button),
+ * then IE would still leave the caret inside the editing area.
+ */
+ if ( this._.editor.mode == 'wysiwyg' && CKEDITOR.env.ie )
+ {
+ var $selection = editor.document.$.selection,
+ $range = $selection.createRange();
+
+ if ( $range )
+ {
+ if ( $range.parentElement && $range.parentElement().ownerDocument == editor.document.$
+ || $range.item && $range.item( 0 ).ownerDocument == editor.document.$ )
+ {
+ var $myRange = document.body.createTextRange();
+ $myRange.moveToElementText( this.getElement().getFirst().$ );
+ $myRange.collapse( true );
+ $myRange.select();
+ }
+ }
+ }
+ }
+ }, this, null, 0xffffffff );
+
+ // IE6 BUG: Text fields and text areas are only half-rendered the first time the dialog appears in IE6 (#2661).
+ // This is still needed after [2708] and [2709] because text fields in hidden TR tags are still broken.
+ if ( CKEDITOR.env.ie6Compat )
+ {
+ this.on( 'load', function( evt )
+ {
+ var outer = this.getElement(),
+ inner = outer.getFirst();
+ inner.remove();
+ inner.appendTo( outer );
+ }, this );
+ }
+
+ initDragAndDrop( this );
+ initResizeHandles( this );
+
+ // Insert the title.
+ ( new CKEDITOR.dom.text( definition.title, CKEDITOR.document ) ).appendTo( this.parts.title );
+
+ // Insert the tabs and contents.
+ for ( i = 0 ; i < definition.contents.length ; i++ )
+ {
+ var page = definition.contents[i];
+ page && this.addPage( page );
+ }
+
+ this.parts[ 'tabs' ].on( 'click', function( evt )
+ {
+ var target = evt.data.getTarget();
+ // If we aren't inside a tab, bail out.
+ if ( target.hasClass( 'cke_dialog_tab' ) )
+ {
+ // Get the ID of the tab, without the 'cke_' prefix and the unique number suffix.
+ var id = target.$.id;
+ this.selectPage( id.substring( 4, id.lastIndexOf( '_' ) ) );
+
+ if ( this._.tabBarMode )
+ {
+ this._.tabBarMode = false;
+ this._.currentFocusIndex = -1;
+ changeFocus( 1 );
+ }
+ evt.data.preventDefault();
+ }
+ }, this );
+
+ // Insert buttons.
+ var buttonsHtml = [],
+ buttons = CKEDITOR.dialog._.uiElementBuilders.hbox.build( this,
+ {
+ type : 'hbox',
+ className : 'cke_dialog_footer_buttons',
+ widths : [],
+ children : definition.buttons
+ }, buttonsHtml ).getChild();
+ this.parts.footer.setHtml( buttonsHtml.join( '' ) );
+
+ for ( i = 0 ; i < buttons.length ; i++ )
+ this._.buttons[ buttons[i].id ] = buttons[i];
+ };
+
+ // Focusable interface. Use it via dialog.addFocusable.
+ function Focusable( dialog, element, index )
+ {
+ this.element = element;
+ this.focusIndex = index;
+ // TODO: support tabIndex for focusables.
+ this.tabIndex = 0;
+ this.isFocusable = function()
+ {
+ return !element.getAttribute( 'disabled' ) && element.isVisible();
+ };
+ this.focus = function()
+ {
+ dialog._.currentFocusIndex = this.focusIndex;
+ this.element.focus();
+ };
+ // Bind events
+ element.on( 'keydown', function( e )
+ {
+ if ( e.data.getKeystroke() in { 32:1, 13:1 } )
+ this.fire( 'click' );
+ } );
+ element.on( 'focus', function()
+ {
+ this.fire( 'mouseover' );
+ } );
+ element.on( 'blur', function()
+ {
+ this.fire( 'mouseout' );
+ } );
+ }
+
+ CKEDITOR.dialog.prototype =
+ {
+ destroy : function()
+ {
+ this.hide();
+ this._.element.remove();
+ },
+
+ /**
+ * Resizes the dialog.
+ * @param {Number} width The width of the dialog in pixels.
+ * @param {Number} height The height of the dialog in pixels.
+ * @function
+ * @example
+ * dialogObj.resize( 800, 640 );
+ */
+ resize : (function()
+ {
+ return function( width, height )
+ {
+ if ( this._.contentSize && this._.contentSize.width == width && this._.contentSize.height == height )
+ return;
+
+ CKEDITOR.dialog.fire( 'resize',
+ {
+ dialog : this,
+ skin : this._.editor.skinName,
+ width : width,
+ height : height
+ }, this._.editor );
+
+ this.fire( 'resize',
+ {
+ skin : this._.editor.skinName,
+ width : width,
+ height : height
+ }, this._.editor );
+
+ // Update dialog position when dimension get changed in RTL.
+ if ( this._.editor.lang.dir == 'rtl' && this._.position )
+ this._.position.x = CKEDITOR.document.getWindow().getViewPaneSize().width -
+ this._.contentSize.width - parseInt( this._.element.getFirst().getStyle( 'right' ), 10 );
+
+ this._.contentSize = { width : width, height : height };
+ };
+ })(),
+
+ /**
+ * Gets the current size of the dialog in pixels.
+ * @returns {Object} An object with "width" and "height" properties.
+ * @example
+ * var width = dialogObj.getSize().width;
+ */
+ getSize : function()
+ {
+ var element = this._.element.getFirst();
+ return { width : element.$.offsetWidth || 0, height : element.$.offsetHeight || 0};
+ },
+
+ /**
+ * Moves the dialog to an (x, y) coordinate relative to the window.
+ * @function
+ * @param {Number} x The target x-coordinate.
+ * @param {Number} y The target y-coordinate.
+ * @param {Boolean} save Flag indicate whether the dialog position should be remembered on next open up.
+ * @example
+ * dialogObj.move( 10, 40 );
+ */
+ move : (function()
+ {
+ var isFixed;
+ return function( x, y, save )
+ {
+ // The dialog may be fixed positioned or absolute positioned. Ask the
+ // browser what is the current situation first.
+ var element = this._.element.getFirst(),
+ rtl = this._.editor.lang.dir == 'rtl';
+
+ if ( isFixed === undefined )
+ isFixed = element.getComputedStyle( 'position' ) == 'fixed';
+
+ if ( isFixed && this._.position && this._.position.x == x && this._.position.y == y )
+ return;
+
+ // Save the current position.
+ this._.position = { x : x, y : y };
+
+ // If not fixed positioned, add scroll position to the coordinates.
+ if ( !isFixed )
+ {
+ var scrollPosition = CKEDITOR.document.getWindow().getScrollPosition();
+ x += scrollPosition.x;
+ y += scrollPosition.y;
+ }
+
+ // Translate coordinate for RTL.
+ if ( rtl )
+ {
+ var dialogSize = this.getSize(),
+ viewPaneSize = CKEDITOR.document.getWindow().getViewPaneSize();
+ x = viewPaneSize.width - dialogSize.width - x;
+ }
+
+ var styles = { 'top' : ( y > 0 ? y : 0 ) + 'px' };
+ styles[ rtl ? 'right' : 'left' ] = ( x > 0 ? x : 0 ) + 'px';
+
+ element.setStyles( styles );
+
+ save && ( this._.moved = 1 );
+ };
+ })(),
+
+ /**
+ * Gets the dialog's position in the window.
+ * @returns {Object} An object with "x" and "y" properties.
+ * @example
+ * var dialogX = dialogObj.getPosition().x;
+ */
+ getPosition : function(){ return CKEDITOR.tools.extend( {}, this._.position ); },
+
+ /**
+ * Shows the dialog box.
+ * @example
+ * dialogObj.show();
+ */
+ show : function()
+ {
+ // Insert the dialog's element to the root document.
+ var element = this._.element;
+ var definition = this.definition;
+ if ( !( element.getParent() && element.getParent().equals( CKEDITOR.document.getBody() ) ) )
+ element.appendTo( CKEDITOR.document.getBody() );
+ else
+ element.setStyle( 'display', 'block' );
+
+ // FIREFOX BUG: Fix vanishing caret for Firefox 2 or Gecko 1.8.
+ if ( CKEDITOR.env.gecko && CKEDITOR.env.version < 10900 )
+ {
+ var dialogElement = this.parts.dialog;
+ dialogElement.setStyle( 'position', 'absolute' );
+ setTimeout( function()
+ {
+ dialogElement.setStyle( 'position', 'fixed' );
+ }, 0 );
+ }
+
+
+ // First, set the dialog to an appropriate size.
+ this.resize( this._.contentSize && this._.contentSize.width || definition.width || definition.minWidth,
+ this._.contentSize && this._.contentSize.height || definition.height || definition.minHeight );
+
+ // Reset all inputs back to their default value.
+ this.reset();
+
+ // Select the first tab by default.
+ this.selectPage( this.definition.contents[0].id );
+
+ // Set z-index.
+ if ( CKEDITOR.dialog._.currentZIndex === null )
+ CKEDITOR.dialog._.currentZIndex = this._.editor.config.baseFloatZIndex;
+ this._.element.getFirst().setStyle( 'z-index', CKEDITOR.dialog._.currentZIndex += 10 );
+
+ // Maintain the dialog ordering and dialog cover.
+ if ( CKEDITOR.dialog._.currentTop === null )
+ {
+ CKEDITOR.dialog._.currentTop = this;
+ this._.parentDialog = null;
+ showCover( this._.editor );
+
+ }
+ else
+ {
+ this._.parentDialog = CKEDITOR.dialog._.currentTop;
+ var parentElement = this._.parentDialog.getElement().getFirst();
+ parentElement.$.style.zIndex -= Math.floor( this._.editor.config.baseFloatZIndex / 2 );
+ CKEDITOR.dialog._.currentTop = this;
+ }
+
+ element.on( 'keydown', accessKeyDownHandler );
+ element.on( CKEDITOR.env.opera ? 'keypress' : 'keyup', accessKeyUpHandler );
+
+ // Prevent some keys from bubbling up. (#4269)
+ for ( var event in { keyup :1, keydown :1, keypress :1 } )
+ element.on( event, preventKeyBubbling );
+
+ // Register the Esc hotkeys.
+ registerAccessKey( this, this, '\x1b', null, function()
+ {
+ var button = this.getButton( 'cancel' );
+ // If there's a Cancel button, click it, else just fire the cancel event and hide the dialog
+ if ( button )
+ button.click();
+ else
+ {
+ if ( this.fire( 'cancel', { hide : true } ).hide !== false )
+ this.hide();
+ }
+ } );
+
+ // Reset the hasFocus state.
+ this._.hasFocus = false;
+
+ CKEDITOR.tools.setTimeout( function()
+ {
+ this.layout();
+ this.parts.dialog.setStyle( 'visibility', '' );
+
+ // Execute onLoad for the first show.
+ this.fireOnce( 'load', {} );
+ CKEDITOR.ui.fire( 'ready', this );
+
+ this.fire( 'show', {} );
+ this._.editor.fire( 'dialogShow', this );
+
+ // Save the initial values of the dialog.
+ this.foreach( function( contentObj ) { contentObj.setInitValue && contentObj.setInitValue(); } );
+
+ },
+ 100, this );
+ },
+
+ /**
+ * Rearrange the dialog to its previous position or the middle of the window.
+ * @since 3.5
+ */
+ layout : function()
+ {
+ var viewSize = CKEDITOR.document.getWindow().getViewPaneSize(),
+ dialogSize = this.getSize();
+
+ this.move( this._.moved ? this._.position.x : ( viewSize.width - dialogSize.width ) / 2,
+ this._.moved ? this._.position.y : ( viewSize.height - dialogSize.height ) / 2 );
+ },
+
+ /**
+ * Executes a function for each UI element.
+ * @param {Function} fn Function to execute for each UI element.
+ * @returns {CKEDITOR.dialog} The current dialog object.
+ */
+ foreach : function( fn )
+ {
+ for ( var i in this._.contents )
+ {
+ for ( var j in this._.contents[i] )
+ fn.call( this, this._.contents[i][j] );
+ }
+ return this;
+ },
+
+ /**
+ * Resets all input values in the dialog.
+ * @example
+ * dialogObj.reset();
+ * @returns {CKEDITOR.dialog} The current dialog object.
+ */
+ reset : (function()
+ {
+ var fn = function( widget ){ if ( widget.reset ) widget.reset( 1 ); };
+ return function(){ this.foreach( fn ); return this; };
+ })(),
+
+
+ /**
+ * Calls the {@link CKEDITOR.dialog.definition.uiElement#setup} method of each of the UI elements, with the arguments passed through it.
+ * It is usually being called when the dialog is opened, to put the initial value inside the field.
+ * @example
+ * dialogObj.setupContent();
+ * @example
+ * var timestamp = ( new Date() ).valueOf();
+ * dialogObj.setupContent( timestamp );
+ */
+ setupContent : function()
+ {
+ var args = arguments;
+ this.foreach( function( widget )
+ {
+ if ( widget.setup )
+ widget.setup.apply( widget, args );
+ });
+ },
+
+ /**
+ * Calls the {@link CKEDITOR.dialog.definition.uiElement#commit} method of each of the UI elements, with the arguments passed through it.
+ * It is usually being called when the user confirms the dialog, to process the values.
+ * @example
+ * dialogObj.commitContent();
+ * @example
+ * var timestamp = ( new Date() ).valueOf();
+ * dialogObj.commitContent( timestamp );
+ */
+ commitContent : function()
+ {
+ var args = arguments;
+ this.foreach( function( widget )
+ {
+ // Make sure IE triggers "change" event on last focused input before closing the dialog. (#7915)
+ if ( CKEDITOR.env.ie && this._.currentFocusIndex == widget.focusIndex )
+ widget.getInputElement().$.blur();
+
+ if ( widget.commit )
+ widget.commit.apply( widget, args );
+ });
+ },
+
+ /**
+ * Hides the dialog box.
+ * @example
+ * dialogObj.hide();
+ */
+ hide : function()
+ {
+ if ( !this.parts.dialog.isVisible() )
+ return;
+
+ this.fire( 'hide', {} );
+ this._.editor.fire( 'dialogHide', this );
+ var element = this._.element;
+ element.setStyle( 'display', 'none' );
+ this.parts.dialog.setStyle( 'visibility', 'hidden' );
+ // Unregister all access keys associated with this dialog.
+ unregisterAccessKey( this );
+
+ // Close any child(top) dialogs first.
+ while( CKEDITOR.dialog._.currentTop != this )
+ CKEDITOR.dialog._.currentTop.hide();
+
+ // Maintain dialog ordering and remove cover if needed.
+ if ( !this._.parentDialog )
+ hideCover();
+ else
+ {
+ var parentElement = this._.parentDialog.getElement().getFirst();
+ parentElement.setStyle( 'z-index', parseInt( parentElement.$.style.zIndex, 10 ) + Math.floor( this._.editor.config.baseFloatZIndex / 2 ) );
+ }
+ CKEDITOR.dialog._.currentTop = this._.parentDialog;
+
+ // Deduct or clear the z-index.
+ if ( !this._.parentDialog )
+ {
+ CKEDITOR.dialog._.currentZIndex = null;
+
+ // Remove access key handlers.
+ element.removeListener( 'keydown', accessKeyDownHandler );
+ element.removeListener( CKEDITOR.env.opera ? 'keypress' : 'keyup', accessKeyUpHandler );
+
+ // Remove bubbling-prevention handler. (#4269)
+ for ( var event in { keyup :1, keydown :1, keypress :1 } )
+ element.removeListener( event, preventKeyBubbling );
+
+ var editor = this._.editor;
+ editor.focus();
+
+ if ( editor.mode == 'wysiwyg' && CKEDITOR.env.ie )
+ {
+ var selection = editor.getSelection();
+ selection && selection.unlock( true );
+ }
+ }
+ else
+ CKEDITOR.dialog._.currentZIndex -= 10;
+
+ delete this._.parentDialog;
+ // Reset the initial values of the dialog.
+ this.foreach( function( contentObj ) { contentObj.resetInitValue && contentObj.resetInitValue(); } );
+ },
+
+ /**
+ * Adds a tabbed page into the dialog.
+ * @param {Object} contents Content definition.
+ * @example
+ */
+ addPage : function( contents )
+ {
+ var pageHtml = [],
+ titleHtml = contents.label ? ' title="' + CKEDITOR.tools.htmlEncode( contents.label ) + '"' : '',
+ elements = contents.elements,
+ vbox = CKEDITOR.dialog._.uiElementBuilders.vbox.build( this,
+ {
+ type : 'vbox',
+ className : 'cke_dialog_page_contents',
+ children : contents.elements,
+ expand : !!contents.expand,
+ padding : contents.padding,
+ style : contents.style || 'width: 100%;height:100%'
+ }, pageHtml );
+
+ // Create the HTML for the tab and the content block.
+ var page = CKEDITOR.dom.element.createFromHtml( pageHtml.join( '' ) );
+ page.setAttribute( 'role', 'tabpanel' );
+
+ var env = CKEDITOR.env;
+ var tabId = 'cke_' + contents.id + '_' + CKEDITOR.tools.getNextNumber(),
+ tab = CKEDITOR.dom.element.createFromHtml( [
+ '<a class="cke_dialog_tab"',
+ ( this._.pageCount > 0 ? ' cke_last' : 'cke_first' ),
+ titleHtml,
+ ( !!contents.hidden ? ' style="display:none"' : '' ),
+ ' id="', tabId, '"',
+ env.gecko && env.version >= 10900 && !env.hc ? '' : ' href="javascript:void(0)"',
+ ' tabIndex="-1"',
+ ' hidefocus="true"',
+ ' role="tab">',
+ contents.label,
+ '</a>'
+ ].join( '' ) );
+
+ page.setAttribute( 'aria-labelledby', tabId );
+
+ // Take records for the tabs and elements created.
+ this._.tabs[ contents.id ] = [ tab, page ];
+ this._.tabIdList.push( contents.id );
+ !contents.hidden && this._.pageCount++;
+ this._.lastTab = tab;
+ this.updateStyle();
+
+ var contentMap = this._.contents[ contents.id ] = {},
+ cursor,
+ children = vbox.getChild();
+
+ while ( ( cursor = children.shift() ) )
+ {
+ contentMap[ cursor.id ] = cursor;
+ if ( typeof( cursor.getChild ) == 'function' )
+ children.push.apply( children, cursor.getChild() );
+ }
+
+ // Attach the DOM nodes.
+
+ page.setAttribute( 'name', contents.id );
+ page.appendTo( this.parts.contents );
+
+ tab.unselectable();
+ this.parts.tabs.append( tab );
+
+ // Add access key handlers if access key is defined.
+ if ( contents.accessKey )
+ {
+ registerAccessKey( this, this, 'CTRL+' + contents.accessKey,
+ tabAccessKeyDown, tabAccessKeyUp );
+ this._.accessKeyMap[ 'CTRL+' + contents.accessKey ] = contents.id;
+ }
+ },
+
+ /**
+ * Activates a tab page in the dialog by its id.
+ * @param {String} id The id of the dialog tab to be activated.
+ * @example
+ * dialogObj.selectPage( 'tab_1' );
+ */
+ selectPage : function( id )
+ {
+ if ( this._.currentTabId == id )
+ return;
+
+ // Returning true means that the event has been canceled
+ if ( this.fire( 'selectPage', { page : id, currentPage : this._.currentTabId } ) === true )
+ return;
+
+ // Hide the non-selected tabs and pages.
+ for ( var i in this._.tabs )
+ {
+ var tab = this._.tabs[i][0],
+ page = this._.tabs[i][1];
+ if ( i != id )
+ {
+ tab.removeClass( 'cke_dialog_tab_selected' );
+ page.hide();
+ }
+ page.setAttribute( 'aria-hidden', i != id );
+ }
+
+ var selected = this._.tabs[ id ];
+ selected[ 0 ].addClass( 'cke_dialog_tab_selected' );
+
+ // [IE] an invisible input[type='text'] will enlarge it's width
+ // if it's value is long when it shows, so we clear it's value
+ // before it shows and then recover it (#5649)
+ if ( CKEDITOR.env.ie6Compat || CKEDITOR.env.ie7Compat )
+ {
+ clearOrRecoverTextInputValue( selected[ 1 ] );
+ selected[ 1 ].show();
+ setTimeout( function()
+ {
+ clearOrRecoverTextInputValue( selected[ 1 ], 1 );
+ }, 0 );
+ }
+ else
+ selected[ 1 ].show();
+
+ this._.currentTabId = id;
+ this._.currentTabIndex = CKEDITOR.tools.indexOf( this._.tabIdList, id );
+ },
+
+ // Dialog state-specific style updates.
+ updateStyle : function()
+ {
+ // If only a single page shown, a different style is used in the central pane.
+ this.parts.dialog[ ( this._.pageCount === 1 ? 'add' : 'remove' ) + 'Class' ]( 'cke_single_page' );
+ },
+
+ /**
+ * Hides a page's tab away from the dialog.
+ * @param {String} id The page's Id.
+ * @example
+ * dialog.hidePage( 'tab_3' );
+ */
+ hidePage : function( id )
+ {
+ var tab = this._.tabs[id] && this._.tabs[id][0];
+ if ( !tab || this._.pageCount == 1 || !tab.isVisible() )
+ return;
+ // Switch to other tab first when we're hiding the active tab.
+ else if ( id == this._.currentTabId )
+ this.selectPage( getPreviousVisibleTab.call( this ) );
+
+ tab.hide();
+ this._.pageCount--;
+ this.updateStyle();
+ },
+
+ /**
+ * Unhides a page's tab.
+ * @param {String} id The page's Id.
+ * @example
+ * dialog.showPage( 'tab_2' );
+ */
+ showPage : function( id )
+ {
+ var tab = this._.tabs[id] && this._.tabs[id][0];
+ if ( !tab )
+ return;
+ tab.show();
+ this._.pageCount++;
+ this.updateStyle();
+ },
+
+ /**
+ * Gets the root DOM element of the dialog.
+ * @returns {CKEDITOR.dom.element} The <span> element containing this dialog.
+ * @example
+ * var dialogElement = dialogObj.getElement().getFirst();
+ * dialogElement.setStyle( 'padding', '5px' );
+ */
+ getElement : function()
+ {
+ return this._.element;
+ },
+
+ /**
+ * Gets the name of the dialog.
+ * @returns {String} The name of this dialog.
+ * @example
+ * var dialogName = dialogObj.getName();
+ */
+ getName : function()
+ {
+ return this._.name;
+ },
+
+ /**
+ * Gets a dialog UI element object from a dialog page.
+ * @param {String} pageId id of dialog page.
+ * @param {String} elementId id of UI element.
+ * @example
+ * dialogObj.getContentElement( 'tabId', 'elementId' ).setValue( 'Example' );
+ * @returns {CKEDITOR.ui.dialog.uiElement} The dialog UI element.
+ */
+ getContentElement : function( pageId, elementId )
+ {
+ var page = this._.contents[ pageId ];
+ return page && page[ elementId ];
+ },
+
+ /**
+ * Gets the value of a dialog UI element.
+ * @param {String} pageId id of dialog page.
+ * @param {String} elementId id of UI element.
+ * @example
+ * alert( dialogObj.getValueOf( 'tabId', 'elementId' ) );
+ * @returns {Object} The value of the UI element.
+ */
+ getValueOf : function( pageId, elementId )
+ {
+ return this.getContentElement( pageId, elementId ).getValue();
+ },
+
+ /**
+ * Sets the value of a dialog UI element.
+ * @param {String} pageId id of the dialog page.
+ * @param {String} elementId id of the UI element.
+ * @param {Object} value The new value of the UI element.
+ * @example
+ * dialogObj.setValueOf( 'tabId', 'elementId', 'Example' );
+ */
+ setValueOf : function( pageId, elementId, value )
+ {
+ return this.getContentElement( pageId, elementId ).setValue( value );
+ },
+
+ /**
+ * Gets the UI element of a button in the dialog's button row.
+ * @param {String} id The id of the button.
+ * @example
+ * @returns {CKEDITOR.ui.dialog.button} The button object.
+ */
+ getButton : function( id )
+ {
+ return this._.buttons[ id ];
+ },
+
+ /**
+ * Simulates a click to a dialog button in the dialog's button row.
+ * @param {String} id The id of the button.
+ * @example
+ * @returns The return value of the dialog's "click" event.
+ */
+ click : function( id )
+ {
+ return this._.buttons[ id ].click();
+ },
+
+ /**
+ * Disables a dialog button.
+ * @param {String} id The id of the button.
+ * @example
+ */
+ disableButton : function( id )
+ {
+ return this._.buttons[ id ].disable();
+ },
+
+ /**
+ * Enables a dialog button.
+ * @param {String} id The id of the button.
+ * @example
+ */
+ enableButton : function( id )
+ {
+ return this._.buttons[ id ].enable();
+ },
+
+ /**
+ * Gets the number of pages in the dialog.
+ * @returns {Number} Page count.
+ */
+ getPageCount : function()
+ {
+ return this._.pageCount;
+ },
+
+ /**
+ * Gets the editor instance which opened this dialog.
+ * @returns {CKEDITOR.editor} Parent editor instances.
+ */
+ getParentEditor : function()
+ {
+ return this._.editor;
+ },
+
+ /**
+ * Gets the element that was selected when opening the dialog, if any.
+ * @returns {CKEDITOR.dom.element} The element that was selected, or null.
+ */
+ getSelectedElement : function()
+ {
+ return this.getParentEditor().getSelection().getSelectedElement();
+ },
+
+ /**
+ * Adds element to dialog's focusable list.
+ *
+ * @param {CKEDITOR.dom.element} element
+ * @param {Number} [index]
+ */
+ addFocusable: function( element, index ) {
+ if ( typeof index == 'undefined' )
+ {
+ index = this._.focusList.length;
+ this._.focusList.push( new Focusable( this, element, index ) );
+ }
+ else
+ {
+ this._.focusList.splice( index, 0, new Focusable( this, element, index ) );
+ for ( var i = index + 1 ; i < this._.focusList.length ; i++ )
+ this._.focusList[ i ].focusIndex++;
+ }
+ }
+ };
+
+ CKEDITOR.tools.extend( CKEDITOR.dialog,
+ /**
+ * @lends CKEDITOR.dialog
+ */
+ {
+ /**
+ * Registers a dialog.
+ * @param {String} name The dialog's name.
+ * @param {Function|String} dialogDefinition
+ * A function returning the dialog's definition, or the URL to the .js file holding the function.
+ * The function should accept an argument "editor" which is the current editor instance, and
+ * return an object conforming to {@link CKEDITOR.dialog.definition}.
+ * @see CKEDITOR.dialog.definition
+ * @example
+ * // Full sample plugin, which does not only register a dialog window but also adds an item to the context menu.
+ * // To open the dialog window, choose "Open dialog" in the context menu.
+ * CKEDITOR.plugins.add( 'myplugin',
+ * {
+ * init: function( editor )
+ * {
+ * editor.addCommand( 'mydialog',new CKEDITOR.dialogCommand( 'mydialog' ) );
+ *
+ * if ( editor.contextMenu )
+ * {
+ * editor.addMenuGroup( 'mygroup', 10 );
+ * editor.addMenuItem( 'My Dialog',
+ * {
+ * label : 'Open dialog',
+ * command : 'mydialog',
+ * group : 'mygroup'
+ * });
+ * editor.contextMenu.addListener( function( element )
+ * {
+ * return { 'My Dialog' : CKEDITOR.TRISTATE_OFF };
+ * });
+ * }
+ *
+ * <strong>CKEDITOR.dialog.add</strong>( 'mydialog', function( api )
+ * {
+ * // CKEDITOR.dialog.definition
+ * var <strong>dialogDefinition</strong> =
+ * {
+ * title : 'Sample dialog',
+ * minWidth : 390,
+ * minHeight : 130,
+ * contents : [
+ * {
+ * id : 'tab1',
+ * label : 'Label',
+ * title : 'Title',
+ * expand : true,
+ * padding : 0,
+ * elements :
+ * [
+ * {
+ * type : 'html',
+ * html : '<p>This is some sample HTML content.</p>'
+ * },
+ * {
+ * type : 'textarea',
+ * id : 'textareaId',
+ * rows : 4,
+ * cols : 40
+ * }
+ * ]
+ * }
+ * ],
+ * buttons : [ CKEDITOR.dialog.okButton, CKEDITOR.dialog.cancelButton ],
+ * onOk : function() {
+ * // "this" is now a CKEDITOR.dialog object.
+ * // Accessing dialog elements:
+ * var textareaObj = this.<strong>getContentElement</strong>( 'tab1', 'textareaId' );
+ * alert( "You have entered: " + textareaObj.getValue() );
+ * }
+ * };
+ *
+ * return dialogDefinition;
+ * } );
+ * }
+ * } );
+ *
+ * CKEDITOR.replace( 'editor1', { extraPlugins : 'myplugin' } );
+ */
+ add : function( name, dialogDefinition )
+ {
+ // Avoid path registration from multiple instances override definition.
+ if ( !this._.dialogDefinitions[name]
+ || typeof dialogDefinition == 'function' )
+ this._.dialogDefinitions[name] = dialogDefinition;
+ },
+
+ exists : function( name )
+ {
+ return !!this._.dialogDefinitions[ name ];
+ },
+
+ getCurrent : function()
+ {
+ return CKEDITOR.dialog._.currentTop;
+ },
+
+ /**
+ * The default OK button for dialogs. Fires the "ok" event and closes the dialog if the event succeeds.
+ * @static
+ * @field
+ * @example
+ * @type Function
+ */
+ okButton : (function()
+ {
+ var retval = function( editor, override )
+ {
+ override = override || {};
+ return CKEDITOR.tools.extend( {
+ id : 'ok',
+ type : 'button',
+ label : editor.lang.common.ok,
+ 'class' : 'cke_dialog_ui_button_ok',
+ onClick : function( evt )
+ {
+ var dialog = evt.data.dialog;
+ if ( dialog.fire( 'ok', { hide : true } ).hide !== false )
+ dialog.hide();
+ }
+ }, override, true );
+ };
+ retval.type = 'button';
+ retval.override = function( override )
+ {
+ return CKEDITOR.tools.extend( function( editor ){ return retval( editor, override ); },
+ { type : 'button' }, true );
+ };
+ return retval;
+ })(),
+
+ /**
+ * The default cancel button for dialogs. Fires the "cancel" event and closes the dialog if no UI element value changed.
+ * @static
+ * @field
+ * @example
+ * @type Function
+ */
+ cancelButton : (function()
+ {
+ var retval = function( editor, override )
+ {
+ override = override || {};
+ return CKEDITOR.tools.extend( {
+ id : 'cancel',
+ type : 'button',
+ label : editor.lang.common.cancel,
+ 'class' : 'cke_dialog_ui_button_cancel',
+ onClick : function( evt )
+ {
+ var dialog = evt.data.dialog;
+ if ( dialog.fire( 'cancel', { hide : true } ).hide !== false )
+ dialog.hide();
+ }
+ }, override, true );
+ };
+ retval.type = 'button';
+ retval.override = function( override )
+ {
+ return CKEDITOR.tools.extend( function( editor ){ return retval( editor, override ); },
+ { type : 'button' }, true );
+ };
+ return retval;
+ })(),
+
+ /**
+ * Registers a dialog UI element.
+ * @param {String} typeName The name of the UI element.
+ * @param {Function} builder The function to build the UI element.
+ * @example
+ */
+ addUIElement : function( typeName, builder )
+ {
+ this._.uiElementBuilders[ typeName ] = builder;
+ }
+ });
+
+ CKEDITOR.dialog._ =
+ {
+ uiElementBuilders : {},
+
+ dialogDefinitions : {},
+
+ currentTop : null,
+
+ currentZIndex : null
+ };
+
+ // "Inherit" (copy actually) from CKEDITOR.event.
+ CKEDITOR.event.implementOn( CKEDITOR.dialog );
+ CKEDITOR.event.implementOn( CKEDITOR.dialog.prototype, true );
+
+ var defaultDialogDefinition =
+ {
+ resizable : CKEDITOR.DIALOG_RESIZE_BOTH,
+ minWidth : 600,
+ minHeight : 400,
+ buttons : [ CKEDITOR.dialog.okButton, CKEDITOR.dialog.cancelButton ]
+ };
+
+ // Tool function used to return an item from an array based on its id
+ // property.
+ var getById = function( array, id, recurse )
+ {
+ for ( var i = 0, item ; ( item = array[ i ] ) ; i++ )
+ {
+ if ( item.id == id )
+ return item;
+ if ( recurse && item[ recurse ] )
+ {
+ var retval = getById( item[ recurse ], id, recurse ) ;
+ if ( retval )
+ return retval;
+ }
+ }
+ return null;
+ };
+
+ // Tool function used to add an item into an array.
+ var addById = function( array, newItem, nextSiblingId, recurse, nullIfNotFound )
+ {
+ if ( nextSiblingId )
+ {
+ for ( var i = 0, item ; ( item = array[ i ] ) ; i++ )
+ {
+ if ( item.id == nextSiblingId )
+ {
+ array.splice( i, 0, newItem );
+ return newItem;
+ }
+
+ if ( recurse && item[ recurse ] )
+ {
+ var retval = addById( item[ recurse ], newItem, nextSiblingId, recurse, true );
+ if ( retval )
+ return retval;
+ }
+ }
+
+ if ( nullIfNotFound )
+ return null;
+ }
+
+ array.push( newItem );
+ return newItem;
+ };
+
+ // Tool function used to remove an item from an array based on its id.
+ var removeById = function( array, id, recurse )
+ {
+ for ( var i = 0, item ; ( item = array[ i ] ) ; i++ )
+ {
+ if ( item.id == id )
+ return array.splice( i, 1 );
+ if ( recurse && item[ recurse ] )
+ {
+ var retval = removeById( item[ recurse ], id, recurse );
+ if ( retval )
+ return retval;
+ }
+ }
+ return null;
+ };
+
+ /**
+ * This class is not really part of the API. It is the "definition" property value
+ * passed to "dialogDefinition" event handlers.
+ * @constructor
+ * @name CKEDITOR.dialog.definitionObject
+ * @extends CKEDITOR.dialog.definition
+ * @example
+ * CKEDITOR.on( 'dialogDefinition', function( evt )
+ * {
+ * var definition = evt.data.definition;
+ * var content = definition.getContents( 'page1' );
+ * ...
+ * } );
+ */
+ var definitionObject = function( dialog, dialogDefinition )
+ {
+ // TODO : Check if needed.
+ this.dialog = dialog;
+
+ // Transform the contents entries in contentObjects.
+ var contents = dialogDefinition.contents;
+ for ( var i = 0, content ; ( content = contents[i] ) ; i++ )
+ contents[ i ] = content && new contentObject( dialog, content );
+
+ CKEDITOR.tools.extend( this, dialogDefinition );
+ };
+
+ definitionObject.prototype =
+ /** @lends CKEDITOR.dialog.definitionObject.prototype */
+ {
+ /**
+ * Gets a content definition.
+ * @param {String} id The id of the content definition.
+ * @returns {CKEDITOR.dialog.definition.content} The content definition
+ * matching id.
+ */
+ getContents : function( id )
+ {
+ return getById( this.contents, id );
+ },
+
+ /**
+ * Gets a button definition.
+ * @param {String} id The id of the button definition.
+ * @returns {CKEDITOR.dialog.definition.button} The button definition
+ * matching id.
+ */
+ getButton : function( id )
+ {
+ return getById( this.buttons, id );
+ },
+
+ /**
+ * Adds a content definition object under this dialog definition.
+ * @param {CKEDITOR.dialog.definition.content} contentDefinition The
+ * content definition.
+ * @param {String} [nextSiblingId] The id of an existing content
+ * definition which the new content definition will be inserted
+ * before. Omit if the new content definition is to be inserted as
+ * the last item.
+ * @returns {CKEDITOR.dialog.definition.content} The inserted content
+ * definition.
+ */
+ addContents : function( contentDefinition, nextSiblingId )
+ {
+ return addById( this.contents, contentDefinition, nextSiblingId );
+ },
+
+ /**
+ * Adds a button definition object under this dialog definition.
+ * @param {CKEDITOR.dialog.definition.button} buttonDefinition The
+ * button definition.
+ * @param {String} [nextSiblingId] The id of an existing button
+ * definition which the new button definition will be inserted
+ * before. Omit if the new button definition is to be inserted as
+ * the last item.
+ * @returns {CKEDITOR.dialog.definition.button} The inserted button
+ * definition.
+ */
+ addButton : function( buttonDefinition, nextSiblingId )
+ {
+ return addById( this.buttons, buttonDefinition, nextSiblingId );
+ },
+
+ /**
+ * Removes a content definition from this dialog definition.
+ * @param {String} id The id of the content definition to be removed.
+ * @returns {CKEDITOR.dialog.definition.content} The removed content
+ * definition.
+ */
+ removeContents : function( id )
+ {
+ removeById( this.contents, id );
+ },
+
+ /**
+ * Removes a button definition from the dialog definition.
+ * @param {String} id The id of the button definition to be removed.
+ * @returns {CKEDITOR.dialog.definition.button} The removed button
+ * definition.
+ */
+ removeButton : function( id )
+ {
+ removeById( this.buttons, id );
+ }
+ };
+
+ /**
+ * This class is not really part of the API. It is the template of the
+ * objects representing content pages inside the
+ * CKEDITOR.dialog.definitionObject.
+ * @constructor
+ * @name CKEDITOR.dialog.definition.contentObject
+ * @example
+ * CKEDITOR.on( 'dialogDefinition', function( evt )
+ * {
+ * var definition = evt.data.definition;
+ * var content = definition.getContents( 'page1' );
+ * content.remove( 'textInput1' );
+ * ...
+ * } );
+ */
+ function contentObject( dialog, contentDefinition )
+ {
+ this._ =
+ {
+ dialog : dialog
+ };
+
+ CKEDITOR.tools.extend( this, contentDefinition );
+ }
+
+ contentObject.prototype =
+ /** @lends CKEDITOR.dialog.definition.contentObject.prototype */
+ {
+ /**
+ * Gets a UI element definition under the content definition.
+ * @param {String} id The id of the UI element definition.
+ * @returns {CKEDITOR.dialog.definition.uiElement}
+ */
+ get : function( id )
+ {
+ return getById( this.elements, id, 'children' );
+ },
+
+ /**
+ * Adds a UI element definition to the content definition.
+ * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition The
+ * UI elemnet definition to be added.
+ * @param {String} nextSiblingId The id of an existing UI element
+ * definition which the new UI element definition will be inserted
+ * before. Omit if the new button definition is to be inserted as
+ * the last item.
+ * @returns {CKEDITOR.dialog.definition.uiElement} The element
+ * definition inserted.
+ */
+ add : function( elementDefinition, nextSiblingId )
+ {
+ return addById( this.elements, elementDefinition, nextSiblingId, 'children' );
+ },
+
+ /**
+ * Removes a UI element definition from the content definition.
+ * @param {String} id The id of the UI element definition to be
+ * removed.
+ * @returns {CKEDITOR.dialog.definition.uiElement} The element
+ * definition removed.
+ * @example
+ */
+ remove : function( id )
+ {
+ removeById( this.elements, id, 'children' );
+ }
+ };
+
+ function initDragAndDrop( dialog )
+ {
+ var lastCoords = null,
+ abstractDialogCoords = null,
+ element = dialog.getElement().getFirst(),
+ editor = dialog.getParentEditor(),
+ magnetDistance = editor.config.dialog_magnetDistance,
+ margins = editor.skin.margins || [ 0, 0, 0, 0 ];
+
+ if ( typeof magnetDistance == 'undefined' )
+ magnetDistance = 20;
+
+ function mouseMoveHandler( evt )
+ {
+ var dialogSize = dialog.getSize(),
+ viewPaneSize = CKEDITOR.document.getWindow().getViewPaneSize(),
+ x = evt.data.$.screenX,
+ y = evt.data.$.screenY,
+ dx = x - lastCoords.x,
+ dy = y - lastCoords.y,
+ realX, realY;
+
+ lastCoords = { x : x, y : y };
+ abstractDialogCoords.x += dx;
+ abstractDialogCoords.y += dy;
+
+ if ( abstractDialogCoords.x + margins[3] < magnetDistance )
+ realX = - margins[3];
+ else if ( abstractDialogCoords.x - margins[1] > viewPaneSize.width - dialogSize.width - magnetDistance )
+ realX = viewPaneSize.width - dialogSize.width + ( editor.lang.dir == 'rtl' ? 0 : margins[1] );
+ else
+ realX = abstractDialogCoords.x;
+
+ if ( abstractDialogCoords.y + margins[0] < magnetDistance )
+ realY = - margins[0];
+ else if ( abstractDialogCoords.y - margins[2] > viewPaneSize.height - dialogSize.height - magnetDistance )
+ realY = viewPaneSize.height - dialogSize.height + margins[2];
+ else
+ realY = abstractDialogCoords.y;
+
+ dialog.move( realX, realY, 1 );
+
+ evt.data.preventDefault();
+ }
+
+ function mouseUpHandler( evt )
+ {
+ CKEDITOR.document.removeListener( 'mousemove', mouseMoveHandler );
+ CKEDITOR.document.removeListener( 'mouseup', mouseUpHandler );
+
+ if ( CKEDITOR.env.ie6Compat )
+ {
+ var coverDoc = currentCover.getChild( 0 ).getFrameDocument();
+ coverDoc.removeListener( 'mousemove', mouseMoveHandler );
+ coverDoc.removeListener( 'mouseup', mouseUpHandler );
+ }
+ }
+
+ dialog.parts.title.on( 'mousedown', function( evt )
+ {
+ lastCoords = { x : evt.data.$.screenX, y : evt.data.$.screenY };
+
+ CKEDITOR.document.on( 'mousemove', mouseMoveHandler );
+ CKEDITOR.document.on( 'mouseup', mouseUpHandler );
+ abstractDialogCoords = dialog.getPosition();
+
+ if ( CKEDITOR.env.ie6Compat )
+ {
+ var coverDoc = currentCover.getChild( 0 ).getFrameDocument();
+ coverDoc.on( 'mousemove', mouseMoveHandler );
+ coverDoc.on( 'mouseup', mouseUpHandler );
+ }
+
+ evt.data.preventDefault();
+ }, dialog );
+ }
+
+ function initResizeHandles( dialog )
+ {
+ var def = dialog.definition,
+ resizable = def.resizable;
+
+ if ( resizable == CKEDITOR.DIALOG_RESIZE_NONE )
+ return;
+
+ var editor = dialog.getParentEditor();
+ var wrapperWidth, wrapperHeight,
+ viewSize, origin, startSize,
+ dialogCover;
+
+ var mouseDownFn = CKEDITOR.tools.addFunction( function( $event )
+ {
+ startSize = dialog.getSize();
+
+ var content = dialog.parts.contents,
+ iframeDialog = content.$.getElementsByTagName( 'iframe' ).length;
+
+ // Shim to help capturing "mousemove" over iframe.
+ if ( iframeDialog )
+ {
+ dialogCover = CKEDITOR.dom.element.createFromHtml( '<div class="cke_dialog_resize_cover" style="height: 100%; position: absolute; width: 100%;"></div>' );
+ content.append( dialogCover );
+ }
+
+ // Calculate the offset between content and chrome size.
+ wrapperHeight = startSize.height - dialog.parts.contents.getSize( 'height', ! ( CKEDITOR.env.gecko || CKEDITOR.env.opera || CKEDITOR.env.ie && CKEDITOR.env.quirks ) );
+ wrapperWidth = startSize.width - dialog.parts.contents.getSize( 'width', 1 );
+
+ origin = { x : $event.screenX, y : $event.screenY };
+
+ viewSize = CKEDITOR.document.getWindow().getViewPaneSize();
+
+ CKEDITOR.document.on( 'mousemove', mouseMoveHandler );
+ CKEDITOR.document.on( 'mouseup', mouseUpHandler );
+
+ if ( CKEDITOR.env.ie6Compat )
+ {
+ var coverDoc = currentCover.getChild( 0 ).getFrameDocument();
+ coverDoc.on( 'mousemove', mouseMoveHandler );
+ coverDoc.on( 'mouseup', mouseUpHandler );
+ }
+
+ $event.preventDefault && $event.preventDefault();
+ });
+
+ // Prepend the grip to the dialog.
+ dialog.on( 'load', function()
+ {
+ var direction = '';
+ if ( resizable == CKEDITOR.DIALOG_RESIZE_WIDTH )
+ direction = ' cke_resizer_horizontal';
+ else if ( resizable == CKEDITOR.DIALOG_RESIZE_HEIGHT )
+ direction = ' cke_resizer_vertical';
+ var resizer = CKEDITOR.dom.element.createFromHtml( '<div' +
+ ' class="cke_resizer' + direction + ' cke_resizer_' + editor.lang.dir + '"' +
+ ' title="' + CKEDITOR.tools.htmlEncode( editor.lang.resize ) + '"' +
+ ' onmousedown="CKEDITOR.tools.callFunction(' + mouseDownFn + ', event )"></div>' );
+ dialog.parts.footer.append( resizer, 1 );
+ });
+ editor.on( 'destroy', function() { CKEDITOR.tools.removeFunction( mouseDownFn ); } );
+
+ function mouseMoveHandler( evt )
+ {
+ var rtl = editor.lang.dir == 'rtl',
+ dx = ( evt.data.$.screenX - origin.x ) * ( rtl ? -1 : 1 ),
+ dy = evt.data.$.screenY - origin.y,
+ width = startSize.width,
+ height = startSize.height,
+ internalWidth = width + dx * ( dialog._.moved ? 1 : 2 ),
+ internalHeight = height + dy * ( dialog._.moved ? 1 : 2 ),
+ element = dialog._.element.getFirst(),
+ right = rtl && element.getComputedStyle( 'right' ),
+ position = dialog.getPosition();
+
+ if ( position.y + internalHeight > viewSize.height )
+ internalHeight = viewSize.height - position.y;
+
+ if ( ( rtl ? right : position.x ) + internalWidth > viewSize.width )
+ internalWidth = viewSize.width - ( rtl ? right : position.x );
+
+ // Make sure the dialog will not be resized to the wrong side when it's in the leftmost position for RTL.
+ if ( ( resizable == CKEDITOR.DIALOG_RESIZE_WIDTH || resizable == CKEDITOR.DIALOG_RESIZE_BOTH ) )
+ width = Math.max( def.minWidth || 0, internalWidth - wrapperWidth );
+
+ if ( resizable == CKEDITOR.DIALOG_RESIZE_HEIGHT || resizable == CKEDITOR.DIALOG_RESIZE_BOTH )
+ height = Math.max( def.minHeight || 0, internalHeight - wrapperHeight );
+
+ dialog.resize( width, height );
+
+ if ( !dialog._.moved )
+ dialog.layout();
+
+ evt.data.preventDefault();
+ }
+
+ function mouseUpHandler()
+ {
+ CKEDITOR.document.removeListener( 'mouseup', mouseUpHandler );
+ CKEDITOR.document.removeListener( 'mousemove', mouseMoveHandler );
+
+ if ( dialogCover )
+ {
+ dialogCover.remove();
+ dialogCover = null;
+ }
+
+ if ( CKEDITOR.env.ie6Compat )
+ {
+ var coverDoc = currentCover.getChild( 0 ).getFrameDocument();
+ coverDoc.removeListener( 'mouseup', mouseUpHandler );
+ coverDoc.removeListener( 'mousemove', mouseMoveHandler );
+ }
+ }
+ }
+
+ var resizeCover;
+ // Caching resuable covers and allowing only one cover
+ // on screen.
+ var covers = {},
+ currentCover;
+
+ function cancelEvent( ev )
+ {
+ ev.data.preventDefault(1);
+ }
+
+ function showCover( editor )
+ {
+ var win = CKEDITOR.document.getWindow();
+ var config = editor.config,
+ backgroundColorStyle = config.dialog_backgroundCoverColor || 'white',
+ backgroundCoverOpacity = config.dialog_backgroundCoverOpacity,
+ baseFloatZIndex = config.baseFloatZIndex,
+ coverKey = CKEDITOR.tools.genKey(
+ backgroundColorStyle,
+ backgroundCoverOpacity,
+ baseFloatZIndex ),
+ coverElement = covers[ coverKey ];
+
+ if ( !coverElement )
+ {
+ var html = [
+ '<div tabIndex="-1" style="position: ', ( CKEDITOR.env.ie6Compat ? 'absolute' : 'fixed' ),
+ '; z-index: ', baseFloatZIndex,
+ '; top: 0px; left: 0px; ',
+ ( !CKEDITOR.env.ie6Compat ? 'background-color: ' + backgroundColorStyle : '' ),
+ '" class="cke_dialog_background_cover">'
+ ];
+
+ if ( CKEDITOR.env.ie6Compat )
+ {
+ // Support for custom document.domain in IE.
+ var isCustomDomain = CKEDITOR.env.isCustomDomain(),
+ iframeHtml = '<html><body style=\\\'background-color:' + backgroundColorStyle + ';\\\'></body></html>';
+
+ html.push(
+ '<iframe' +
+ ' hidefocus="true"' +
+ ' frameborder="0"' +
+ ' id="cke_dialog_background_iframe"' +
+ ' src="javascript:' );
+
+ html.push( 'void((function(){' +
+ 'document.open();' +
+ ( isCustomDomain ? 'document.domain=\'' + document.domain + '\';' : '' ) +
+ 'document.write( \'' + iframeHtml + '\' );' +
+ 'document.close();' +
+ '})())' );
+
+ html.push(
+ '"' +
+ ' style="' +
+ 'position:absolute;' +
+ 'left:0;' +
+ 'top:0;' +
+ 'width:100%;' +
+ 'height: 100%;' +
+ 'progid:DXImageTransform.Microsoft.Alpha(opacity=0)">' +
+ '</iframe>' );
+ }
+
+ html.push( '</div>' );
+
+ coverElement = CKEDITOR.dom.element.createFromHtml( html.join( '' ) );
+ coverElement.setOpacity( backgroundCoverOpacity != undefined ? backgroundCoverOpacity : 0.5 );
+
+ coverElement.on( 'keydown', cancelEvent );
+ coverElement.on( 'keypress', cancelEvent );
+ coverElement.on( 'keyup', cancelEvent );
+
+ coverElement.appendTo( CKEDITOR.document.getBody() );
+ covers[ coverKey ] = coverElement;
+ }
+ else
+ coverElement. show();
+
+ currentCover = coverElement;
+ var resizeFunc = function()
+ {
+ var size = win.getViewPaneSize();
+ coverElement.setStyles(
+ {
+ width : size.width + 'px',
+ height : size.height + 'px'
+ } );
+ };
+
+ var scrollFunc = function()
+ {
+ var pos = win.getScrollPosition(),
+ cursor = CKEDITOR.dialog._.currentTop;
+ coverElement.setStyles(
+ {
+ left : pos.x + 'px',
+ top : pos.y + 'px'
+ });
+
+ if ( cursor )
+ {
+ do
+ {
+ var dialogPos = cursor.getPosition();
+ cursor.move( dialogPos.x, dialogPos.y );
+ } while ( ( cursor = cursor._.parentDialog ) );
+ }
+ };
+
+ resizeCover = resizeFunc;
+ win.on( 'resize', resizeFunc );
+ resizeFunc();
+ // Using Safari/Mac, focus must be kept where it is (#7027)
+ if ( !( CKEDITOR.env.mac && CKEDITOR.env.webkit ) )
+ coverElement.focus();
+
+ if ( CKEDITOR.env.ie6Compat )
+ {
+ // IE BUG: win.$.onscroll assignment doesn't work.. it must be window.onscroll.
+ // So we need to invent a really funny way to make it work.
+ var myScrollHandler = function()
+ {
+ scrollFunc();
+ arguments.callee.prevScrollHandler.apply( this, arguments );
+ };
+ win.$.setTimeout( function()
+ {
+ myScrollHandler.prevScrollHandler = window.onscroll || function(){};
+ window.onscroll = myScrollHandler;
+ }, 0 );
+ scrollFunc();
+ }
+ }
+
+ function hideCover()
+ {
+ if ( !currentCover )
+ return;
+
+ var win = CKEDITOR.document.getWindow();
+ currentCover.hide();
+ win.removeListener( 'resize', resizeCover );
+
+ if ( CKEDITOR.env.ie6Compat )
+ {
+ win.$.setTimeout( function()
+ {
+ var prevScrollHandler = window.onscroll && window.onscroll.prevScrollHandler;
+ window.onscroll = prevScrollHandler || null;
+ }, 0 );
+ }
+ resizeCover = null;
+ }
+
+ function removeCovers()
+ {
+ for ( var coverId in covers )
+ covers[ coverId ].remove();
+ covers = {};
+ }
+
+ var accessKeyProcessors = {};
+
+ var accessKeyDownHandler = function( evt )
+ {
+ var ctrl = evt.data.$.ctrlKey || evt.data.$.metaKey,
+ alt = evt.data.$.altKey,
+ shift = evt.data.$.shiftKey,
+ key = String.fromCharCode( evt.data.$.keyCode ),
+ keyProcessor = accessKeyProcessors[( ctrl ? 'CTRL+' : '' ) + ( alt ? 'ALT+' : '') + ( shift ? 'SHIFT+' : '' ) + key];
+
+ if ( !keyProcessor || !keyProcessor.length )
+ return;
+
+ keyProcessor = keyProcessor[keyProcessor.length - 1];
+ keyProcessor.keydown && keyProcessor.keydown.call( keyProcessor.uiElement, keyProcessor.dialog, keyProcessor.key );
+ evt.data.preventDefault();
+ };
+
+ var accessKeyUpHandler = function( evt )
+ {
+ var ctrl = evt.data.$.ctrlKey || evt.data.$.metaKey,
+ alt = evt.data.$.altKey,
+ shift = evt.data.$.shiftKey,
+ key = String.fromCharCode( evt.data.$.keyCode ),
+ keyProcessor = accessKeyProcessors[( ctrl ? 'CTRL+' : '' ) + ( alt ? 'ALT+' : '') + ( shift ? 'SHIFT+' : '' ) + key];
+
+ if ( !keyProcessor || !keyProcessor.length )
+ return;
+
+ keyProcessor = keyProcessor[keyProcessor.length - 1];
+ if ( keyProcessor.keyup )
+ {
+ keyProcessor.keyup.call( keyProcessor.uiElement, keyProcessor.dialog, keyProcessor.key );
+ evt.data.preventDefault();
+ }
+ };
+
+ var registerAccessKey = function( uiElement, dialog, key, downFunc, upFunc )
+ {
+ var procList = accessKeyProcessors[key] || ( accessKeyProcessors[key] = [] );
+ procList.push( {
+ uiElement : uiElement,
+ dialog : dialog,
+ key : key,
+ keyup : upFunc || uiElement.accessKeyUp,
+ keydown : downFunc || uiElement.accessKeyDown
+ } );
+ };
+
+ var unregisterAccessKey = function( obj )
+ {
+ for ( var i in accessKeyProcessors )
+ {
+ var list = accessKeyProcessors[i];
+ for ( var j = list.length - 1 ; j >= 0 ; j-- )
+ {
+ if ( list[j].dialog == obj || list[j].uiElement == obj )
+ list.splice( j, 1 );
+ }
+ if ( list.length === 0 )
+ delete accessKeyProcessors[i];
+ }
+ };
+
+ var tabAccessKeyUp = function( dialog, key )
+ {
+ if ( dialog._.accessKeyMap[key] )
+ dialog.selectPage( dialog._.accessKeyMap[key] );
+ };
+
+ var tabAccessKeyDown = function( dialog, key )
+ {
+ };
+
+ // ESC, ENTER
+ var preventKeyBubblingKeys = { 27 :1, 13 :1 };
+ var preventKeyBubbling = function( e )
+ {
+ if ( e.data.getKeystroke() in preventKeyBubblingKeys )
+ e.data.stopPropagation();
+ };
+
+ (function()
+ {
+ CKEDITOR.ui.dialog =
+ {
+ /**
+ * The base class of all dialog UI elements.
+ * @constructor
+ * @param {CKEDITOR.dialog} dialog Parent dialog object.
+ * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition Element
+ * definition. Accepted fields:
+ * <ul>
+ * <li><strong>id</strong> (Required) The id of the UI element. See {@link
+ * CKEDITOR.dialog#getContentElement}</li>
+ * <li><strong>type</strong> (Required) The type of the UI element. The
+ * value to this field specifies which UI element class will be used to
+ * generate the final widget.</li>
+ * <li><strong>title</strong> (Optional) The popup tooltip for the UI
+ * element.</li>
+ * <li><strong>hidden</strong> (Optional) A flag that tells if the element
+ * should be initially visible.</li>
+ * <li><strong>className</strong> (Optional) Additional CSS class names
+ * to add to the UI element. Separated by space.</li>
+ * <li><strong>style</strong> (Optional) Additional CSS inline styles
+ * to add to the UI element. A semicolon (;) is required after the last
+ * style declaration.</li>
+ * <li><strong>accessKey</strong> (Optional) The alphanumeric access key
+ * for this element. Access keys are automatically prefixed by CTRL.</li>
+ * <li><strong>on*</strong> (Optional) Any UI element definition field that
+ * starts with <em>on</em> followed immediately by a capital letter and
+ * probably more letters is an event handler. Event handlers may be further
+ * divided into registered event handlers and DOM event handlers. Please
+ * refer to {@link CKEDITOR.ui.dialog.uiElement#registerEvents} and
+ * {@link CKEDITOR.ui.dialog.uiElement#eventProcessors} for more
+ * information.</li>
+ * </ul>
+ * @param {Array} htmlList
+ * List of HTML code to be added to the dialog's content area.
+ * @param {Function|String} nodeNameArg
+ * A function returning a string, or a simple string for the node name for
+ * the root DOM node. Default is 'div'.
+ * @param {Function|Object} stylesArg
+ * A function returning an object, or a simple object for CSS styles applied
+ * to the DOM node. Default is empty object.
+ * @param {Function|Object} attributesArg
+ * A fucntion returning an object, or a simple object for attributes applied
+ * to the DOM node. Default is empty object.
+ * @param {Function|String} contentsArg
+ * A function returning a string, or a simple string for the HTML code inside
+ * the root DOM node. Default is empty string.
+ * @example
+ */
+ uiElement : function( dialog, elementDefinition, htmlList, nodeNameArg, stylesArg, attributesArg, contentsArg )
+ {
+ if ( arguments.length < 4 )
+ return;
+
+ var nodeName = ( nodeNameArg.call ? nodeNameArg( elementDefinition ) : nodeNameArg ) || 'div',
+ html = [ '<', nodeName, ' ' ],
+ styles = ( stylesArg && stylesArg.call ? stylesArg( elementDefinition ) : stylesArg ) || {},
+ attributes = ( attributesArg && attributesArg.call ? attributesArg( elementDefinition ) : attributesArg ) || {},
+ innerHTML = ( contentsArg && contentsArg.call ? contentsArg.call( this, dialog, elementDefinition ) : contentsArg ) || '',
+ domId = this.domId = attributes.id || CKEDITOR.tools.getNextId() + '_uiElement',
+ id = this.id = elementDefinition.id,
+ i;
+
+ // Set the id, a unique id is required for getElement() to work.
+ attributes.id = domId;
+
+ // Set the type and definition CSS class names.
+ var classes = {};
+ if ( elementDefinition.type )
+ classes[ 'cke_dialog_ui_' + elementDefinition.type ] = 1;
+ if ( elementDefinition.className )
+ classes[ elementDefinition.className ] = 1;
+ if ( elementDefinition.disabled )
+ classes[ 'cke_disabled' ] = 1;
+
+ var attributeClasses = ( attributes['class'] && attributes['class'].split ) ? attributes['class'].split( ' ' ) : [];
+ for ( i = 0 ; i < attributeClasses.length ; i++ )
+ {
+ if ( attributeClasses[i] )
+ classes[ attributeClasses[i] ] = 1;
+ }
+ var finalClasses = [];
+ for ( i in classes )
+ finalClasses.push( i );
+ attributes['class'] = finalClasses.join( ' ' );
+
+ // Set the popup tooltop.
+ if ( elementDefinition.title )
+ attributes.title = elementDefinition.title;
+
+ // Write the inline CSS styles.
+ var styleStr = ( elementDefinition.style || '' ).split( ';' );
+
+ // Element alignment support.
+ if ( elementDefinition.align )
+ {
+ var align = elementDefinition.align;
+ styles[ 'margin-left' ] = align == 'left' ? 0 : 'auto';
+ styles[ 'margin-right' ] = align == 'right' ? 0 : 'auto';
+ }
+
+ for ( i in styles )
+ styleStr.push( i + ':' + styles[i] );
+ if ( elementDefinition.hidden )
+ styleStr.push( 'display:none' );
+ for ( i = styleStr.length - 1 ; i >= 0 ; i-- )
+ {
+ if ( styleStr[i] === '' )
+ styleStr.splice( i, 1 );
+ }
+ if ( styleStr.length > 0 )
+ attributes.style = ( attributes.style ? ( attributes.style + '; ' ) : '' ) + styleStr.join( '; ' );
+
+ // Write the attributes.
+ for ( i in attributes )
+ html.push( i + '="' + CKEDITOR.tools.htmlEncode( attributes[i] ) + '" ');
+
+ // Write the content HTML.
+ html.push( '>', innerHTML, '</', nodeName, '>' );
+
+ // Add contents to the parent HTML array.
+ htmlList.push( html.join( '' ) );
+
+ ( this._ || ( this._ = {} ) ).dialog = dialog;
+
+ // Override isChanged if it is defined in element definition.
+ if ( typeof( elementDefinition.isChanged ) == 'boolean' )
+ this.isChanged = function(){ return elementDefinition.isChanged; };
+ if ( typeof( elementDefinition.isChanged ) == 'function' )
+ this.isChanged = elementDefinition.isChanged;
+
+ // Overload 'get(set)Value' on definition.
+ if ( typeof( elementDefinition.setValue ) == 'function' )
+ {
+ this.setValue = CKEDITOR.tools.override( this.setValue, function( org )
+ {
+ return function( val ){ org.call( this, elementDefinition.setValue.call( this, val ) ); };
+ } );
+ }
+
+ if ( typeof( elementDefinition.getValue ) == 'function' )
+ {
+ this.getValue = CKEDITOR.tools.override( this.getValue, function( org )
+ {
+ return function(){ return elementDefinition.getValue.call( this, org.call( this ) ); };
+ } );
+ }
+
+ // Add events.
+ CKEDITOR.event.implementOn( this );
+
+ this.registerEvents( elementDefinition );
+ if ( this.accessKeyUp && this.accessKeyDown && elementDefinition.accessKey )
+ registerAccessKey( this, dialog, 'CTRL+' + elementDefinition.accessKey );
+
+ var me = this;
+ dialog.on( 'load', function()
+ {
+ var input = me.getInputElement();
+ if ( input )
+ {
+ var focusClass = me.type in { 'checkbox' : 1, 'ratio' : 1 } && CKEDITOR.env.ie && CKEDITOR.env.version < 8 ? 'cke_dialog_ui_focused' : '';
+ input.on( 'focus', function()
+ {
+ dialog._.tabBarMode = false;
+ dialog._.hasFocus = true;
+ me.fire( 'focus' );
+ focusClass && this.addClass( focusClass );
+
+ });
+
+ input.on( 'blur', function()
+ {
+ me.fire( 'blur' );
+ focusClass && this.removeClass( focusClass );
+ });
+ }
+ } );
+
+ // Register the object as a tab focus if it can be included.
+ if ( this.keyboardFocusable )
+ {
+ this.tabIndex = elementDefinition.tabIndex || 0;
+
+ this.focusIndex = dialog._.focusList.push( this ) - 1;
+ this.on( 'focus', function()
+ {
+ dialog._.currentFocusIndex = me.focusIndex;
+ } );
+ }
+
+ // Completes this object with everything we have in the
+ // definition.
+ CKEDITOR.tools.extend( this, elementDefinition );
+ },
+
+ /**
+ * Horizontal layout box for dialog UI elements, auto-expends to available width of container.
+ * @constructor
+ * @extends CKEDITOR.ui.dialog.uiElement
+ * @param {CKEDITOR.dialog} dialog
+ * Parent dialog object.
+ * @param {Array} childObjList
+ * Array of {@link CKEDITOR.ui.dialog.uiElement} objects inside this
+ * container.
+ * @param {Array} childHtmlList
+ * Array of HTML code that correspond to the HTML output of all the
+ * objects in childObjList.
+ * @param {Array} htmlList
+ * Array of HTML code that this element will output to.
+ * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition
+ * The element definition. Accepted fields:
+ * <ul>
+ * <li><strong>widths</strong> (Optional) The widths of child cells.</li>
+ * <li><strong>height</strong> (Optional) The height of the layout.</li>
+ * <li><strong>padding</strong> (Optional) The padding width inside child
+ * cells.</li>
+ * <li><strong>align</strong> (Optional) The alignment of the whole layout
+ * </li>
+ * </ul>
+ * @example
+ */
+ hbox : function( dialog, childObjList, childHtmlList, htmlList, elementDefinition )
+ {
+ if ( arguments.length < 4 )
+ return;
+
+ this._ || ( this._ = {} );
+
+ var children = this._.children = childObjList,
+ widths = elementDefinition && elementDefinition.widths || null,
+ height = elementDefinition && elementDefinition.height || null,
+ styles = {},
+ i;
+ /** @ignore */
+ var innerHTML = function()
+ {
+ var html = [ '<tbody><tr class="cke_dialog_ui_hbox">' ];
+ for ( i = 0 ; i < childHtmlList.length ; i++ )
+ {
+ var className = 'cke_dialog_ui_hbox_child',
+ styles = [];
+ if ( i === 0 )
+ className = 'cke_dialog_ui_hbox_first';
+ if ( i == childHtmlList.length - 1 )
+ className = 'cke_dialog_ui_hbox_last';
+ html.push( '<td class="', className, '" role="presentation" ' );
+ if ( widths )
+ {
+ if ( widths[i] )
+ styles.push( 'width:' + cssLength( widths[i] ) );
+ }
+ else
+ styles.push( 'width:' + Math.floor( 100 / childHtmlList.length ) + '%' );
+ if ( height )
+ styles.push( 'height:' + cssLength( height ) );
+ if ( elementDefinition && elementDefinition.padding != undefined )
+ styles.push( 'padding:' + cssLength( elementDefinition.padding ) );
+ // In IE Quirks alignment has to be done on table cells. (#7324)
+ if ( CKEDITOR.env.ie && CKEDITOR.env.quirks && children[ i ].align )
+ styles.push( 'text-align:' + children[ i ].align );
+ if ( styles.length > 0 )
+ html.push( 'style="' + styles.join('; ') + '" ' );
+ html.push( '>', childHtmlList[i], '</td>' );
+ }
+ html.push( '</tr></tbody>' );
+ return html.join( '' );
+ };
+
+ var attribs = { role : 'presentation' };
+ elementDefinition && elementDefinition.align && ( attribs.align = elementDefinition.align );
+
+ CKEDITOR.ui.dialog.uiElement.call(
+ this,
+ dialog,
+ elementDefinition || { type : 'hbox' },
+ htmlList,
+ 'table',
+ styles,
+ attribs,
+ innerHTML );
+ },
+
+ /**
+ * Vertical layout box for dialog UI elements.
+ * @constructor
+ * @extends CKEDITOR.ui.dialog.hbox
+ * @param {CKEDITOR.dialog} dialog
+ * Parent dialog object.
+ * @param {Array} childObjList
+ * Array of {@link CKEDITOR.ui.dialog.uiElement} objects inside this
+ * container.
+ * @param {Array} childHtmlList
+ * Array of HTML code that correspond to the HTML output of all the
+ * objects in childObjList.
+ * @param {Array} htmlList
+ * Array of HTML code that this element will output to.
+ * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition
+ * The element definition. Accepted fields:
+ * <ul>
+ * <li><strong>width</strong> (Optional) The width of the layout.</li>
+ * <li><strong>heights</strong> (Optional) The heights of individual cells.
+ * </li>
+ * <li><strong>align</strong> (Optional) The alignment of the layout.</li>
+ * <li><strong>padding</strong> (Optional) The padding width inside child
+ * cells.</li>
+ * <li><strong>expand</strong> (Optional) Whether the layout should expand
+ * vertically to fill its container.</li>
+ * </ul>
+ * @example
+ */
+ vbox : function( dialog, childObjList, childHtmlList, htmlList, elementDefinition )
+ {
+ if ( arguments.length < 3 )
+ return;
+
+ this._ || ( this._ = {} );
+
+ var children = this._.children = childObjList,
+ width = elementDefinition && elementDefinition.width || null,
+ heights = elementDefinition && elementDefinition.heights || null;
+ /** @ignore */
+ var innerHTML = function()
+ {
+ var html = [ '<table role="presentation" cellspacing="0" border="0" ' ];
+ html.push( 'style="' );
+ if ( elementDefinition && elementDefinition.expand )
+ html.push( 'height:100%;' );
+ html.push( 'width:' + cssLength( width || '100%' ), ';' );
+ html.push( '"' );
+ html.push( 'align="', CKEDITOR.tools.htmlEncode(
+ ( elementDefinition && elementDefinition.align ) || ( dialog.getParentEditor().lang.dir == 'ltr' ? 'left' : 'right' ) ), '" ' );
+
+ html.push( '><tbody>' );
+ for ( var i = 0 ; i < childHtmlList.length ; i++ )
+ {
+ var styles = [];
+ html.push( '<tr><td role="presentation" ' );
+ if ( width )
+ styles.push( 'width:' + cssLength( width || '100%' ) );
+ if ( heights )
+ styles.push( 'height:' + cssLength( heights[i] ) );
+ else if ( elementDefinition && elementDefinition.expand )
+ styles.push( 'height:' + Math.floor( 100 / childHtmlList.length ) + '%' );
+ if ( elementDefinition && elementDefinition.padding != undefined )
+ styles.push( 'padding:' + cssLength( elementDefinition.padding ) );
+ // In IE Quirks alignment has to be done on table cells. (#7324)
+ if ( CKEDITOR.env.ie && CKEDITOR.env.quirks && children[ i ].align )
+ styles.push( 'text-align:' + children[ i ].align );
+ if ( styles.length > 0 )
+ html.push( 'style="', styles.join( '; ' ), '" ' );
+ html.push( ' class="cke_dialog_ui_vbox_child">', childHtmlList[i], '</td></tr>' );
+ }
+ html.push( '</tbody></table>' );
+ return html.join( '' );
+ };
+ CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition || { type : 'vbox' }, htmlList, 'div', null, { role : 'presentation' }, innerHTML );
+ }
+ };
+ })();
+
+ CKEDITOR.ui.dialog.uiElement.prototype =
+ {
+ /**
+ * Gets the root DOM element of this dialog UI object.
+ * @returns {CKEDITOR.dom.element} Root DOM element of UI object.
+ * @example
+ * uiElement.getElement().hide();
+ */
+ getElement : function()
+ {
+ return CKEDITOR.document.getById( this.domId );
+ },
+
+ /**
+ * Gets the DOM element that the user inputs values.
+ * This function is used by setValue(), getValue() and focus(). It should
+ * be overrided in child classes where the input element isn't the root
+ * element.
+ * @returns {CKEDITOR.dom.element} The element where the user input values.
+ * @example
+ * var rawValue = textInput.getInputElement().$.value;
+ */
+ getInputElement : function()
+ {
+ return this.getElement();
+ },
+
+ /**
+ * Gets the parent dialog object containing this UI element.
+ * @returns {CKEDITOR.dialog} Parent dialog object.
+ * @example
+ * var dialog = uiElement.getDialog();
+ */
+ getDialog : function()
+ {
+ return this._.dialog;
+ },
+
+ /**
+ * Sets the value of this dialog UI object.
+ * @param {Object} value The new value.
+ * @param {Boolean} noChangeEvent Internal commit, to supress 'change' event on this element.
+ * @returns {CKEDITOR.dialog.uiElement} The current UI element.
+ * @example
+ * uiElement.setValue( 'Dingo' );
+ */
+ setValue : function( value, noChangeEvent )
+ {
+ this.getInputElement().setValue( value );
+ !noChangeEvent && this.fire( 'change', { value : value } );
+ return this;
+ },
+
+ /**
+ * Gets the current value of this dialog UI object.
+ * @returns {Object} The current value.
+ * @example
+ * var myValue = uiElement.getValue();
+ */
+ getValue : function()
+ {
+ return this.getInputElement().getValue();
+ },
+
+ /**
+ * Tells whether the UI object's value has changed.
+ * @returns {Boolean} true if changed, false if not changed.
+ * @example
+ * if ( uiElement.isChanged() )
+ * confirm( 'Value changed! Continue?' );
+ */
+ isChanged : function()
+ {
+ // Override in input classes.
+ return false;
+ },
+
+ /**
+ * Selects the parent tab of this element. Usually called by focus() or overridden focus() methods.
+ * @returns {CKEDITOR.dialog.uiElement} The current UI element.
+ * @example
+ * focus : function()
+ * {
+ * this.selectParentTab();
+ * // do something else.
+ * }
+ */
+ selectParentTab : function()
+ {
+ var element = this.getInputElement(),
+ cursor = element,
+ tabId;
+ while ( ( cursor = cursor.getParent() ) && cursor.$.className.search( 'cke_dialog_page_contents' ) == -1 )
+ { /*jsl:pass*/ }
+
+ // Some widgets don't have parent tabs (e.g. OK and Cancel buttons).
+ if ( !cursor )
+ return this;
+
+ tabId = cursor.getAttribute( 'name' );
+ // Avoid duplicate select.
+ if ( this._.dialog._.currentTabId != tabId )
+ this._.dialog.selectPage( tabId );
+ return this;
+ },
+
+ /**
+ * Puts the focus to the UI object. Switches tabs if the UI object isn't in the active tab page.
+ * @returns {CKEDITOR.dialog.uiElement} The current UI element.
+ * @example
+ * uiElement.focus();
+ */
+ focus : function()
+ {
+ this.selectParentTab().getInputElement().focus();
+ return this;
+ },
+
+ /**
+ * Registers the on* event handlers defined in the element definition.
+ * The default behavior of this function is:
+ * <ol>
+ * <li>
+ * If the on* event is defined in the class's eventProcesors list,
+ * then the registration is delegated to the corresponding function
+ * in the eventProcessors list.
+ * </li>
+ * <li>
+ * If the on* event is not defined in the eventProcessors list, then
+ * register the event handler under the corresponding DOM event of
+ * the UI element's input DOM element (as defined by the return value
+ * of {@link CKEDITOR.ui.dialog.uiElement#getInputElement}).
+ * </li>
+ * </ol>
+ * This function is only called at UI element instantiation, but can
+ * be overridded in child classes if they require more flexibility.
+ * @param {CKEDITOR.dialog.definition.uiElement} definition The UI element
+ * definition.
+ * @returns {CKEDITOR.dialog.uiElement} The current UI element.
+ * @example
+ */
+ registerEvents : function( definition )
+ {
+ var regex = /^on([A-Z]\w+)/,
+ match;
+
+ var registerDomEvent = function( uiElement, dialog, eventName, func )
+ {
+ dialog.on( 'load', function()
+ {
+ uiElement.getInputElement().on( eventName, func, uiElement );
+ });
+ };
+
+ for ( var i in definition )
+ {
+ if ( !( match = i.match( regex ) ) )
+ continue;
+ if ( this.eventProcessors[i] )
+ this.eventProcessors[i].call( this, this._.dialog, definition[i] );
+ else
+ registerDomEvent( this, this._.dialog, match[1].toLowerCase(), definition[i] );
+ }
+
+ return this;
+ },
+
+ /**
+ * The event processor list used by
+ * {@link CKEDITOR.ui.dialog.uiElement#getInputElement} at UI element
+ * instantiation. The default list defines three on* events:
+ * <ol>
+ * <li>onLoad - Called when the element's parent dialog opens for the
+ * first time</li>
+ * <li>onShow - Called whenever the element's parent dialog opens.</li>
+ * <li>onHide - Called whenever the element's parent dialog closes.</li>
+ * </ol>
+ * @field
+ * @type Object
+ * @example
+ * // This connects the 'click' event in CKEDITOR.ui.dialog.button to onClick
+ * // handlers in the UI element's definitions.
+ * CKEDITOR.ui.dialog.button.eventProcessors = CKEDITOR.tools.extend( {},
+ * CKEDITOR.ui.dialog.uiElement.prototype.eventProcessors,
+ * { onClick : function( dialog, func ) { this.on( 'click', func ); } },
+ * true );
+ */
+ eventProcessors :
+ {
+ onLoad : function( dialog, func )
+ {
+ dialog.on( 'load', func, this );
+ },
+
+ onShow : function( dialog, func )
+ {
+ dialog.on( 'show', func, this );
+ },
+
+ onHide : function( dialog, func )
+ {
+ dialog.on( 'hide', func, this );
+ }
+ },
+
+ /**
+ * The default handler for a UI element's access key down event, which
+ * tries to put focus to the UI element.<br />
+ * Can be overridded in child classes for more sophisticaed behavior.
+ * @param {CKEDITOR.dialog} dialog The parent dialog object.
+ * @param {String} key The key combination pressed. Since access keys
+ * are defined to always include the CTRL key, its value should always
+ * include a 'CTRL+' prefix.
+ * @example
+ */
+ accessKeyDown : function( dialog, key )
+ {
+ this.focus();
+ },
+
+ /**
+ * The default handler for a UI element's access key up event, which
+ * does nothing.<br />
+ * Can be overridded in child classes for more sophisticated behavior.
+ * @param {CKEDITOR.dialog} dialog The parent dialog object.
+ * @param {String} key The key combination pressed. Since access keys
+ * are defined to always include the CTRL key, its value should always
+ * include a 'CTRL+' prefix.
+ * @example
+ */
+ accessKeyUp : function( dialog, key )
+ {
+ },
+
+ /**
+ * Disables a UI element.
+ * @example
+ */
+ disable : function()
+ {
+ var element = this.getElement(),
+ input = this.getInputElement();
+ input.setAttribute( 'disabled', 'true' );
+ element.addClass( 'cke_disabled' );
+ },
+
+ /**
+ * Enables a UI element.
+ * @example
+ */
+ enable : function()
+ {
+ var element = this.getElement(),
+ input = this.getInputElement();
+ input.removeAttribute( 'disabled' );
+ element.removeClass( 'cke_disabled' );
+ },
+
+ /**
+ * Determines whether an UI element is enabled or not.
+ * @returns {Boolean} Whether the UI element is enabled.
+ * @example
+ */
+ isEnabled : function()
+ {
+ return !this.getElement().hasClass( 'cke_disabled' );
+ },
+
+ /**
+ * Determines whether an UI element is visible or not.
+ * @returns {Boolean} Whether the UI element is visible.
+ * @example
+ */
+ isVisible : function()
+ {
+ return this.getInputElement().isVisible();
+ },
+
+ /**
+ * Determines whether an UI element is focus-able or not.
+ * Focus-able is defined as being both visible and enabled.
+ * @returns {Boolean} Whether the UI element can be focused.
+ * @example
+ */
+ isFocusable : function()
+ {
+ if ( !this.isEnabled() || !this.isVisible() )
+ return false;
+ return true;
+ }
+ };
+
+ CKEDITOR.ui.dialog.hbox.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.uiElement,
+ /**
+ * @lends CKEDITOR.ui.dialog.hbox.prototype
+ */
+ {
+ /**
+ * Gets a child UI element inside this container.
+ * @param {Array|Number} indices An array or a single number to indicate the child's
+ * position in the container's descendant tree. Omit to get all the children in an array.
+ * @returns {Array|CKEDITOR.ui.dialog.uiElement} Array of all UI elements in the container
+ * if no argument given, or the specified UI element if indices is given.
+ * @example
+ * var checkbox = hbox.getChild( [0,1] );
+ * checkbox.setValue( true );
+ */
+ getChild : function( indices )
+ {
+ // If no arguments, return a clone of the children array.
+ if ( arguments.length < 1 )
+ return this._.children.concat();
+
+ // If indices isn't array, make it one.
+ if ( !indices.splice )
+ indices = [ indices ];
+
+ // Retrieve the child element according to tree position.
+ if ( indices.length < 2 )
+ return this._.children[ indices[0] ];
+ else
+ return ( this._.children[ indices[0] ] && this._.children[ indices[0] ].getChild ) ?
+ this._.children[ indices[0] ].getChild( indices.slice( 1, indices.length ) ) :
+ null;
+ }
+ }, true );
+
+ CKEDITOR.ui.dialog.vbox.prototype = new CKEDITOR.ui.dialog.hbox();
+
+
+
+ (function()
+ {
+ var commonBuilder = {
+ build : function( dialog, elementDefinition, output )
+ {
+ var children = elementDefinition.children,
+ child,
+ childHtmlList = [],
+ childObjList = [];
+ for ( var i = 0 ; ( i < children.length && ( child = children[i] ) ) ; i++ )
+ {
+ var childHtml = [];
+ childHtmlList.push( childHtml );
+ childObjList.push( CKEDITOR.dialog._.uiElementBuilders[ child.type ].build( dialog, child, childHtml ) );
+ }
+ return new CKEDITOR.ui.dialog[elementDefinition.type]( dialog, childObjList, childHtmlList, output, elementDefinition );
+ }
+ };
+
+ CKEDITOR.dialog.addUIElement( 'hbox', commonBuilder );
+ CKEDITOR.dialog.addUIElement( 'vbox', commonBuilder );
+ })();
+
+ /**
+ * Generic dialog command. It opens a specific dialog when executed.
+ * @constructor
+ * @augments CKEDITOR.commandDefinition
+ * @param {string} dialogName The name of the dialog to open when executing
+ * this command.
+ * @example
+ * // Register the "link" command, which opens the "link" dialog.
+ * editor.addCommand( 'link', <b>new CKEDITOR.dialogCommand( 'link' )</b> );
+ */
+ CKEDITOR.dialogCommand = function( dialogName )
+ {
+ this.dialogName = dialogName;
+ };
+
+ CKEDITOR.dialogCommand.prototype =
+ {
+ /** @ignore */
+ exec : function( editor )
+ {
+ // Special treatment for Opera. (#8031)
+ CKEDITOR.env.opera ?
+ CKEDITOR.tools.setTimeout( function() { editor.openDialog( this.dialogName ); }, 0, this )
+ : editor.openDialog( this.dialogName );
+ },
+
+ // Dialog commands just open a dialog ui, thus require no undo logic,
+ // undo support should dedicate to specific dialog implementation.
+ canUndo: false,
+
+ editorFocus : CKEDITOR.env.ie || CKEDITOR.env.webkit
+ };
+
+ (function()
+ {
+ var notEmptyRegex = /^([a]|[^a])+$/,
+ integerRegex = /^\d*$/,
+ numberRegex = /^\d*(?:\.\d+)?$/,
+ htmlLengthRegex = /^(((\d*(\.\d+))|(\d*))(px|\%)?)?$/,
+ cssLengthRegex = /^(((\d*(\.\d+))|(\d*))(px|em|ex|in|cm|mm|pt|pc|\%)?)?$/i,
+ inlineStyleRegex = /^(\s*[\w-]+\s*:\s*[^:;]+(?:;|$))*$/;
+
+ CKEDITOR.VALIDATE_OR = 1;
+ CKEDITOR.VALIDATE_AND = 2;
+
+ CKEDITOR.dialog.validate =
+ {
+ functions : function()
+ {
+ var args = arguments;
+ return function()
+ {
+ /**
+ * It's important for validate functions to be able to accept the value
+ * as argument in addition to this.getValue(), so that it is possible to
+ * combine validate functions together to make more sophisticated
+ * validators.
+ */
+ var value = this && this.getValue ? this.getValue() : args[ 0 ];
+
+ var msg = undefined,
+ relation = CKEDITOR.VALIDATE_AND,
+ functions = [], i;
+
+ for ( i = 0 ; i < args.length ; i++ )
+ {
+ if ( typeof( args[i] ) == 'function' )
+ functions.push( args[i] );
+ else
+ break;
+ }
+
+ if ( i < args.length && typeof( args[i] ) == 'string' )
+ {
+ msg = args[i];
+ i++;
+ }
+
+ if ( i < args.length && typeof( args[i]) == 'number' )
+ relation = args[i];
+
+ var passed = ( relation == CKEDITOR.VALIDATE_AND ? true : false );
+ for ( i = 0 ; i < functions.length ; i++ )
+ {
+ if ( relation == CKEDITOR.VALIDATE_AND )
+ passed = passed && functions[i]( value );
+ else
+ passed = passed || functions[i]( value );
+ }
+
+ return !passed ? msg : true;
+ };
+ },
+
+ regex : function( regex, msg )
+ {
+ /*
+ * Can be greatly shortened by deriving from functions validator if code size
+ * turns out to be more important than performance.
+ */
+ return function()
+ {
+ var value = this && this.getValue ? this.getValue() : arguments[0];
+ return !regex.test( value ) ? msg : true;
+ };
+ },
+
+ notEmpty : function( msg )
+ {
+ return this.regex( notEmptyRegex, msg );
+ },
+
+ integer : function( msg )
+ {
+ return this.regex( integerRegex, msg );
+ },
+
+ 'number' : function( msg )
+ {
+ return this.regex( numberRegex, msg );
+ },
+
+ 'cssLength' : function( msg )
+ {
+ return this.functions( function( val ){ return cssLengthRegex.test( CKEDITOR.tools.trim( val ) ); }, msg );
+ },
+
+ 'htmlLength' : function( msg )
+ {
+ return this.functions( function( val ){ return htmlLengthRegex.test( CKEDITOR.tools.trim( val ) ); }, msg );
+ },
+
+ 'inlineStyle' : function( msg )
+ {
+ return this.functions( function( val ){ return inlineStyleRegex.test( CKEDITOR.tools.trim( val ) ); }, msg );
+ },
+
+ equals : function( value, msg )
+ {
+ return this.functions( function( val ){ return val == value; }, msg );
+ },
+
+ notEqual : function( value, msg )
+ {
+ return this.functions( function( val ){ return val != value; }, msg );
+ }
+ };
+
+ CKEDITOR.on( 'instanceDestroyed', function( evt )
+ {
+ // Remove dialog cover on last instance destroy.
+ if ( CKEDITOR.tools.isEmpty( CKEDITOR.instances ) )
+ {
+ var currentTopDialog;
+ while ( ( currentTopDialog = CKEDITOR.dialog._.currentTop ) )
+ currentTopDialog.hide();
+ removeCovers();
+ }
+
+ var dialogs = evt.editor._.storedDialogs;
+ for ( var name in dialogs )
+ dialogs[ name ].destroy();
+
+ });
+
+ })();
+
+ // Extend the CKEDITOR.editor class with dialog specific functions.
+ CKEDITOR.tools.extend( CKEDITOR.editor.prototype,
+ /** @lends CKEDITOR.editor.prototype */
+ {
+ /**
+ * Loads and opens a registered dialog.
+ * @param {String} dialogName The registered name of the dialog.
+ * @param {Function} callback The function to be invoked after dialog instance created.
+ * @see CKEDITOR.dialog.add
+ * @example
+ * CKEDITOR.instances.editor1.openDialog( 'smiley' );
+ * @returns {CKEDITOR.dialog} The dialog object corresponding to the dialog displayed. null if the dialog name is not registered.
+ */
+ openDialog : function( dialogName, callback )
+ {
+ if ( this.mode == 'wysiwyg' && CKEDITOR.env.ie )
+ {
+ var selection = this.getSelection();
+ selection && selection.lock();
+ }
+
+ var dialogDefinitions = CKEDITOR.dialog._.dialogDefinitions[ dialogName ],
+ dialogSkin = this.skin.dialog;
+
+ if ( CKEDITOR.dialog._.currentTop === null )
+ showCover( this );
+
+ // If the dialogDefinition is already loaded, open it immediately.
+ if ( typeof dialogDefinitions == 'function' && dialogSkin._isLoaded )
+ {
+ var storedDialogs = this._.storedDialogs ||
+ ( this._.storedDialogs = {} );
+
+ var dialog = storedDialogs[ dialogName ] ||
+ ( storedDialogs[ dialogName ] = new CKEDITOR.dialog( this, dialogName ) );
+
+ callback && callback.call( dialog, dialog );
+ dialog.show();
+
+ return dialog;
+ }
+ else if ( dialogDefinitions == 'failed' )
+ {
+ hideCover();
+ throw new Error( '[CKEDITOR.dialog.openDialog] Dialog "' + dialogName + '" failed when loading definition.' );
+ }
+
+ var me = this;
+
+ function onDialogFileLoaded( success )
+ {
+ var dialogDefinition = CKEDITOR.dialog._.dialogDefinitions[ dialogName ],
+ skin = me.skin.dialog;
+
+ // Check if both skin part and definition is loaded.
+ if ( !skin._isLoaded || loadDefinition && typeof success == 'undefined' )
+ return;
+
+ // In case of plugin error, mark it as loading failed.
+ if ( typeof dialogDefinition != 'function' )
+ CKEDITOR.dialog._.dialogDefinitions[ dialogName ] = 'failed';
+
+ me.openDialog( dialogName, callback );
+ }
+
+ if ( typeof dialogDefinitions == 'string' )
+ {
+ var loadDefinition = 1;
+ CKEDITOR.scriptLoader.load( CKEDITOR.getUrl( dialogDefinitions ), onDialogFileLoaded, null, 0, 1 );
+ }
+
+ CKEDITOR.skins.load( this, 'dialog', onDialogFileLoaded );
+
+ return null;
+ }
+ });
+})();
+
+CKEDITOR.plugins.add( 'dialog',
+ {
+ requires : [ 'dialogui' ]
+ });
+
+// Dialog related configurations.
+
+/**
+ * The color of the dialog background cover. It should be a valid CSS color
+ * string.
+ * @name CKEDITOR.config.dialog_backgroundCoverColor
+ * @type String
+ * @default 'white'
+ * @example
+ * config.dialog_backgroundCoverColor = 'rgb(255, 254, 253)';
+ */
+
+/**
+ * The opacity of the dialog background cover. It should be a number within the
+ * range [0.0, 1.0].
+ * @name CKEDITOR.config.dialog_backgroundCoverOpacity
+ * @type Number
+ * @default 0.5
+ * @example
+ * config.dialog_backgroundCoverOpacity = 0.7;
+ */
+
+/**
+ * If the dialog has more than one tab, put focus into the first tab as soon as dialog is opened.
+ * @name CKEDITOR.config.dialog_startupFocusTab
+ * @type Boolean
+ * @default false
+ * @example
+ * config.dialog_startupFocusTab = true;
+ */
+
+/**
+ * The distance of magnetic borders used in moving and resizing dialogs,
+ * measured in pixels.
+ * @name CKEDITOR.config.dialog_magnetDistance
+ * @type Number
+ * @default 20
+ * @example
+ * config.dialog_magnetDistance = 30;
+ */
+
+/**
+ * The guideline to follow when generating the dialog buttons. There are 3 possible options:
+ * <ul>
+ * <li>'OS' - the buttons will be displayed in the default order of the user's OS;</li>
+ * <li>'ltr' - for Left-To-Right order;</li>
+ * <li>'rtl' - for Right-To-Left order.</li>
+ * </ul>
+ * @name CKEDITOR.config.dialog_buttonsOrder
+ * @type String
+ * @default 'OS'
+ * @since 3.5
+ * @example
+ * config.dialog_buttonsOrder = 'rtl';
+ */
+
+/**
+ * The dialog contents to removed. It's a string composed by dialog name and tab name with a colon between them.
+ * Separate each pair with semicolon (see example).
+ * <b>Note: All names are case-sensitive.</b>
+ * <b>Note: Be cautious when specifying dialog tabs that are mandatory, like "info", dialog functionality might be broken because of this!</b>
+ * @name CKEDITOR.config.removeDialogTabs
+ * @type String
+ * @since 3.5
+ * @default ''
+ * @example
+ * config.removeDialogTabs = 'flash:advanced;image:Link';
+ */
+
+/**
+ * Fired when a dialog definition is about to be used to create a dialog into
+ * an editor instance. This event makes it possible to customize the definition
+ * before creating it.
+ * <p>Note that this event is called only the first time a specific dialog is
+ * opened. Successive openings will use the cached dialog, and this event will
+ * not get fired.</p>
+ * @name CKEDITOR#dialogDefinition
+ * @event
+ * @param {CKEDITOR.dialog.definition} data The dialog defination that
+ * is being loaded.
+ * @param {CKEDITOR.editor} editor The editor instance that will use the
+ * dialog.
+ */
+
+/**
+ * Fired when a tab is going to be selected in a dialog
+ * @name CKEDITOR.dialog#selectPage
+ * @event
+ * @param {String} page The id of the page that it's gonna be selected.
+ * @param {String} currentPage The id of the current page.
+ */
+
+/**
+ * Fired when the user tries to dismiss a dialog
+ * @name CKEDITOR.dialog#cancel
+ * @event
+ * @param {Boolean} hide Whether the event should proceed or not.
+ */
+
+/**
+ * Fired when the user tries to confirm a dialog
+ * @name CKEDITOR.dialog#ok
+ * @event
+ * @param {Boolean} hide Whether the event should proceed or not.
+ */
+
+/**
+ * Fired when a dialog is shown
+ * @name CKEDITOR.dialog#show
+ * @event
+ */
+
+/**
+ * Fired when a dialog is shown
+ * @name CKEDITOR.editor#dialogShow
+ * @event
+ */
+
+/**
+ * Fired when a dialog is hidden
+ * @name CKEDITOR.dialog#hide
+ * @event
+ */
+
+/**
+ * Fired when a dialog is hidden
+ * @name CKEDITOR.editor#dialogHide
+ * @event
+ */
+
+/**
+ * Fired when a dialog is being resized. The event is fired on
+ * both the 'CKEDITOR.dialog' object and the dialog instance
+ * since 3.5.3, previously it's available only in the global object.
+ * @name CKEDITOR.dialog#resize
+ * @since 3.5
+ * @event
+ * @param {CKEDITOR.dialog} dialog The dialog being resized (if
+ * it's fired on the dialog itself, this parameter isn't sent).
+ * @param {String} skin The skin name.
+ * @param {Number} width The new width.
+ * @param {Number} height The new height.
+ */
diff --git a/_source/plugins/dialogadvtab/plugin.js b/_source/plugins/dialogadvtab/plugin.js new file mode 100644 index 0000000..d369be7 --- /dev/null +++ b/_source/plugins/dialogadvtab/plugin.js @@ -0,0 +1,208 @@ +/*
+Copyright (c) 2003-2012, 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;
+ }
+});
+
+})();
diff --git a/_source/plugins/dialogui/plugin.js b/_source/plugins/dialogui/plugin.js index fd4df91..963c80c 100644 --- a/_source/plugins/dialogui/plugin.js +++ b/_source/plugins/dialogui/plugin.js @@ -1,1415 +1,1549 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -/** @fileoverview The "dialogui" plugin. */ - -CKEDITOR.plugins.add( 'dialogui' ); - -(function() -{ - var initPrivateObject = function( elementDefinition ) - { - this._ || ( this._ = {} ); - this._['default'] = this._.initValue = elementDefinition['default'] || ''; - this._.required = elementDefinition[ 'required' ] || false; - var args = [ this._ ]; - for ( var i = 1 ; i < arguments.length ; i++ ) - args.push( arguments[i] ); - args.push( true ); - CKEDITOR.tools.extend.apply( CKEDITOR.tools, args ); - return this._; - }, - textBuilder = - { - build : function( dialog, elementDefinition, output ) - { - return new CKEDITOR.ui.dialog.textInput( dialog, elementDefinition, output ); - } - }, - commonBuilder = - { - build : function( dialog, elementDefinition, output ) - { - return new CKEDITOR.ui.dialog[elementDefinition.type]( dialog, elementDefinition, output ); - } - }, - containerBuilder = - { - build : function( dialog, elementDefinition, output ) - { - var children = elementDefinition.children, - child, - childHtmlList = [], - childObjList = []; - for ( var i = 0 ; ( i < children.length && ( child = children[i] ) ) ; i++ ) - { - var childHtml = []; - childHtmlList.push( childHtml ); - childObjList.push( CKEDITOR.dialog._.uiElementBuilders[ child.type ].build( dialog, child, childHtml ) ); - } - return new CKEDITOR.ui.dialog[ elementDefinition.type ]( dialog, childObjList, childHtmlList, output, elementDefinition ); - } - }, - commonPrototype = - { - isChanged : function() - { - return this.getValue() != this.getInitValue(); - }, - - reset : function() - { - this.setValue( this.getInitValue() ); - }, - - setInitValue : function() - { - this._.initValue = this.getValue(); - }, - - resetInitValue : function() - { - this._.initValue = this._['default']; - }, - - getInitValue : function() - { - return this._.initValue; - } - }, - commonEventProcessors = CKEDITOR.tools.extend( {}, CKEDITOR.ui.dialog.uiElement.prototype.eventProcessors, - { - onChange : function( dialog, func ) - { - if ( !this._.domOnChangeRegistered ) - { - dialog.on( 'load', function() - { - this.getInputElement().on( 'change', function() - { - // Make sure 'onchange' doesn't get fired after dialog closed. (#5719) - if ( !dialog.parts.dialog.isVisible() ) - return; - - this.fire( 'change', { value : this.getValue() } ); - }, this ); - }, this ); - this._.domOnChangeRegistered = true; - } - - this.on( 'change', func ); - } - }, true ), - eventRegex = /^on([A-Z]\w+)/, - cleanInnerDefinition = function( def ) - { - // An inner UI element should not have the parent's type, title or events. - for ( var i in def ) - { - if ( eventRegex.test( i ) || i == 'title' || i == 'type' ) - delete def[i]; - } - return def; - }; - - CKEDITOR.tools.extend( CKEDITOR.ui.dialog, - /** @lends CKEDITOR.ui.dialog */ - { - /** - * Base class for all dialog elements with a textual label on the left. - * @constructor - * @example - * @extends CKEDITOR.ui.dialog.uiElement - * @param {CKEDITOR.dialog} dialog - * Parent dialog object. - * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition - * The element definition. Accepted fields: - * <ul> - * <li><strong>label</strong> (Required) The label string.</li> - * <li><strong>labelLayout</strong> (Optional) Put 'horizontal' here if the - * label element is to be layed out horizontally. Otherwise a vertical - * layout will be used.</li> - * <li><strong>widths</strong> (Optional) This applies only for horizontal - * layouts - an 2-element array of lengths to specify the widths of the - * label and the content element.</li> - * </ul> - * @param {Array} htmlList - * List of HTML code to output to. - * @param {Function} contentHtml - * A function returning the HTML code string to be added inside the content - * cell. - */ - labeledElement : function( dialog, elementDefinition, htmlList, contentHtml ) - { - if ( arguments.length < 4 ) - return; - - var _ = initPrivateObject.call( this, elementDefinition ); - _.labelId = CKEDITOR.tools.getNextNumber() + '_label'; - var children = this._.children = []; - /** @ignore */ - var innerHTML = function() - { - var html = []; - if ( elementDefinition.labelLayout != 'horizontal' ) - html.push( '<label class="cke_dialog_ui_labeled_label" ', - ' id="'+ _.labelId + '"', - ' for="' + _.inputId + '"', - ' style="' + elementDefinition.labelStyle + '">', - elementDefinition.label, - '</label>', - '<div class="cke_dialog_ui_labeled_content" role="presentation">', - contentHtml.call( this, dialog, elementDefinition ), - '</div>' ); - else - { - var hboxDefinition = { - type : 'hbox', - widths : elementDefinition.widths, - padding : 0, - children : - [ - { - type : 'html', - html : '<label class="cke_dialog_ui_labeled_label"' + - ' id="' + _.labelId + '"' + - ' for="' + _.inputId + '"' + - ' style="' + elementDefinition.labelStyle + '">' + - CKEDITOR.tools.htmlEncode( elementDefinition.label ) + - '</span>' - }, - { - type : 'html', - html : '<span class="cke_dialog_ui_labeled_content">' + - contentHtml.call( this, dialog, elementDefinition ) + - '</span>' - } - ] - }; - CKEDITOR.dialog._.uiElementBuilders.hbox.build( dialog, hboxDefinition, html ); - } - return html.join( '' ); - }; - CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition, htmlList, 'div', null, { role : 'presentation' }, innerHTML ); - }, - - /** - * A text input with a label. This UI element class represents both the - * single-line text inputs and password inputs in dialog boxes. - * @constructor - * @example - * @extends CKEDITOR.ui.dialog.labeledElement - * @param {CKEDITOR.dialog} dialog - * Parent dialog object. - * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition - * The element definition. Accepted fields: - * <ul> - * <li><strong>default</strong> (Optional) The default value.</li> - * <li><strong>validate</strong> (Optional) The validation function. </li> - * <li><strong>maxLength</strong> (Optional) The maximum length of text box - * contents.</li> - * <li><strong>size</strong> (Optional) The size of the text box. This is - * usually overridden by the size defined by the skin, however.</li> - * </ul> - * @param {Array} htmlList - * List of HTML code to output to. - */ - textInput : function( dialog, elementDefinition, htmlList ) - { - if ( arguments.length < 3 ) - return; - - initPrivateObject.call( this, elementDefinition ); - var domId = this._.inputId = CKEDITOR.tools.getNextNumber() + '_textInput', - attributes = { 'class' : 'cke_dialog_ui_input_' + elementDefinition.type, id : domId, type : 'text' }, - i; - - // Set the validator, if any. - if ( elementDefinition.validate ) - this.validate = elementDefinition.validate; - - // Set the max length and size. - if ( elementDefinition.maxLength ) - attributes.maxlength = elementDefinition.maxLength; - if ( elementDefinition.size ) - attributes.size = elementDefinition.size; - - // If user presses Enter in a text box, it implies clicking OK for the dialog. - var me = this, keyPressedOnMe = false; - dialog.on( 'load', function() - { - me.getInputElement().on( 'keydown', function( evt ) - { - if ( evt.data.getKeystroke() == 13 ) - keyPressedOnMe = true; - } ); - - // Lower the priority this 'keyup' since 'ok' will close the dialog.(#3749) - me.getInputElement().on( 'keyup', function( evt ) - { - if ( evt.data.getKeystroke() == 13 && keyPressedOnMe ) - { - dialog.getButton( 'ok' ) && setTimeout( function () - { - dialog.getButton( 'ok' ).click(); - }, 0 ); - keyPressedOnMe = false; - } - }, null, null, 1000 ); - } ); - - /** @ignore */ - var innerHTML = function() - { - // IE BUG: Text input fields in IE at 100% would exceed a <td> or inline - // container's width, so need to wrap it inside a <div>. - var html = [ '<div class="cke_dialog_ui_input_', elementDefinition.type, '" role="presentation"' ]; - - if ( elementDefinition.width ) - html.push( 'style="width:'+ elementDefinition.width +'" ' ); - - html.push( '><input ' ); - - attributes[ 'aria-labelledby' ] = this._.labelId; - this._.required && ( attributes[ 'aria-required' ] = this._.required ); - for ( var i in attributes ) - html.push( i + '="' + attributes[i] + '" ' ); - html.push( ' /></div>' ); - return html.join( '' ); - }; - CKEDITOR.ui.dialog.labeledElement.call( this, dialog, elementDefinition, htmlList, innerHTML ); - }, - - /** - * A text area with a label on the top or left. - * @constructor - * @extends CKEDITOR.ui.dialog.labeledElement - * @example - * @param {CKEDITOR.dialog} dialog - * Parent dialog object. - * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition - * The element definition. Accepted fields: - * <ul> - * <li><strong>rows</strong> (Optional) The number of rows displayed. - * Defaults to 5 if not defined.</li> - * <li><strong>cols</strong> (Optional) The number of cols displayed. - * Defaults to 20 if not defined. Usually overridden by skins.</li> - * <li><strong>default</strong> (Optional) The default value.</li> - * <li><strong>validate</strong> (Optional) The validation function. </li> - * </ul> - * @param {Array} htmlList - * List of HTML code to output to. - */ - textarea : function( dialog, elementDefinition, htmlList ) - { - if ( arguments.length < 3 ) - return; - - initPrivateObject.call( this, elementDefinition ); - var me = this, - domId = this._.inputId = CKEDITOR.tools.getNextNumber() + '_textarea', - attributes = {}; - - if ( elementDefinition.validate ) - this.validate = elementDefinition.validate; - - // Generates the essential attributes for the textarea tag. - attributes.rows = elementDefinition.rows || 5; - attributes.cols = elementDefinition.cols || 20; - - /** @ignore */ - var innerHTML = function() - { - attributes[ 'aria-labelledby' ] = this._.labelId; - this._.required && ( attributes[ 'aria-required' ] = this._.required ); - var html = [ '<div class="cke_dialog_ui_input_textarea" role="presentation"><textarea class="cke_dialog_ui_input_textarea" id="', domId, '" ' ]; - for ( var i in attributes ) - html.push( i + '="' + CKEDITOR.tools.htmlEncode( attributes[i] ) + '" ' ); - html.push( '>', CKEDITOR.tools.htmlEncode( me._['default'] ), '</textarea></div>' ); - return html.join( '' ); - }; - CKEDITOR.ui.dialog.labeledElement.call( this, dialog, elementDefinition, htmlList, innerHTML ); - }, - - /** - * A single checkbox with a label on the right. - * @constructor - * @extends CKEDITOR.ui.dialog.uiElement - * @example - * @param {CKEDITOR.dialog} dialog - * Parent dialog object. - * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition - * The element definition. Accepted fields: - * <ul> - * <li><strong>checked</strong> (Optional) Whether the checkbox is checked - * on instantiation. Defaults to false.</li> - * <li><strong>validate</strong> (Optional) The validation function.</li> - * <li><strong>label</strong> (Optional) The checkbox label.</li> - * </ul> - * @param {Array} htmlList - * List of HTML code to output to. - */ - checkbox : function( dialog, elementDefinition, htmlList ) - { - if ( arguments.length < 3 ) - return; - - var _ = initPrivateObject.call( this, elementDefinition, { 'default' : !!elementDefinition[ 'default' ] } ); - - if ( elementDefinition.validate ) - this.validate = elementDefinition.validate; - - /** @ignore */ - var innerHTML = function() - { - var myDefinition = CKEDITOR.tools.extend( {}, elementDefinition, - { - id : elementDefinition.id ? elementDefinition.id + '_checkbox' : CKEDITOR.tools.getNextNumber() + '_checkbox' - }, true ), - html = []; - - var labelId = CKEDITOR.tools.getNextNumber() + '_label'; - var attributes = { 'class' : 'cke_dialog_ui_checkbox_input', type : 'checkbox', 'aria-labelledby' : labelId }; - cleanInnerDefinition( myDefinition ); - if ( elementDefinition[ 'default' ] ) - attributes.checked = 'checked'; - _.checkbox = new CKEDITOR.ui.dialog.uiElement( dialog, myDefinition, html, 'input', null, attributes ); - html.push( ' <label id="', labelId, '" for="', attributes.id, '">', - CKEDITOR.tools.htmlEncode( elementDefinition.label ), - '</label>' ); - return html.join( '' ); - }; - - CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition, htmlList, 'span', null, null, innerHTML ); - }, - - /** - * A group of radio buttons. - * @constructor - * @example - * @extends CKEDITOR.ui.dialog.labeledElement - * @param {CKEDITOR.dialog} dialog - * Parent dialog object. - * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition - * The element definition. Accepted fields: - * <ul> - * <li><strong>default</strong> (Required) The default value.</li> - * <li><strong>validate</strong> (Optional) The validation function.</li> - * <li><strong>items</strong> (Required) An array of options. Each option - * is a 1- or 2-item array of format [ 'Description', 'Value' ]. If 'Value' - * is missing, then the value would be assumed to be the same as the - * description.</li> - * </ul> - * @param {Array} htmlList - * List of HTML code to output to. - */ - radio : function( dialog, elementDefinition, htmlList ) - { - if ( arguments.length < 3) - return; - - initPrivateObject.call( this, elementDefinition ); - if ( !this._['default'] ) - this._['default'] = this._.initValue = elementDefinition.items[0][1]; - if ( elementDefinition.validate ) - this.validate = elementDefinition.valdiate; - var children = [], me = this; - - /** @ignore */ - var innerHTML = function() - { - var inputHtmlList = [], html = [], - commonAttributes = { 'class' : 'cke_dialog_ui_radio_item', 'aria-labelledby' : this._.labelId }, - commonName = elementDefinition.id ? elementDefinition.id + '_radio' : CKEDITOR.tools.getNextNumber() + '_radio'; - for ( var i = 0 ; i < elementDefinition.items.length ; i++ ) - { - var item = elementDefinition.items[i], - title = item[2] !== undefined ? item[2] : item[0], - value = item[1] !== undefined ? item[1] : item[0], - inputId = CKEDITOR.tools.getNextNumber() + '_radio_input', - labelId = inputId + '_label', - inputDefinition = CKEDITOR.tools.extend( {}, elementDefinition, - { - id : inputId, - title : null, - type : null - }, true ), - labelDefinition = CKEDITOR.tools.extend( {}, inputDefinition, - { - title : title - }, true ), - inputAttributes = - { - type : 'radio', - 'class' : 'cke_dialog_ui_radio_input', - name : commonName, - value : value, - 'aria-labelledby' : labelId - }, - inputHtml = []; - if ( me._['default'] == value ) - inputAttributes.checked = 'checked'; - cleanInnerDefinition( inputDefinition ); - cleanInnerDefinition( labelDefinition ); - children.push( new CKEDITOR.ui.dialog.uiElement( dialog, inputDefinition, inputHtml, 'input', null, inputAttributes ) ); - inputHtml.push( ' ' ); - new CKEDITOR.ui.dialog.uiElement( dialog, labelDefinition, inputHtml, 'label', null, { id : labelId, 'for' : inputAttributes.id }, - item[0] ); - inputHtmlList.push( inputHtml.join( '' ) ); - } - new CKEDITOR.ui.dialog.hbox( dialog, [], inputHtmlList, html ); - return html.join( '' ); - }; - - CKEDITOR.ui.dialog.labeledElement.call( this, dialog, elementDefinition, htmlList, innerHTML ); - this._.children = children; - }, - - /** - * A button with a label inside. - * @constructor - * @example - * @extends CKEDITOR.ui.dialog.uiElement - * @param {CKEDITOR.dialog} dialog - * Parent dialog object. - * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition - * The element definition. Accepted fields: - * <ul> - * <li><strong>label</strong> (Required) The button label.</li> - * <li><strong>disabled</strong> (Optional) Set to true if you want the - * button to appear in disabled state.</li> - * </ul> - * @param {Array} htmlList - * List of HTML code to output to. - */ - button : function( dialog, elementDefinition, htmlList ) - { - if ( !arguments.length ) - return; - - if ( typeof elementDefinition == 'function' ) - elementDefinition = elementDefinition( dialog.getParentEditor() ); - - initPrivateObject.call( this, elementDefinition, { disabled : elementDefinition.disabled || false } ); - - // Add OnClick event to this input. - CKEDITOR.event.implementOn( this ); - - var me = this; - - // Register an event handler for processing button clicks. - dialog.on( 'load', function( eventInfo ) - { - var element = this.getElement(); - - (function() - { - element.on( 'click', function( evt ) - { - me.fire( 'click', { dialog : me.getDialog() } ); - evt.data.preventDefault(); - } ); - - element.on( 'keydown', function( evt ) - { - if ( evt.data.getKeystroke() in { 32:1 } ) - { - me.click(); - evt.data.preventDefault(); - } - } ); - })(); - - element.unselectable(); - }, this ); - - var outerDefinition = CKEDITOR.tools.extend( {}, elementDefinition ); - delete outerDefinition.style; - - var labelId = CKEDITOR.tools.getNextNumber() + '_label'; - CKEDITOR.ui.dialog.uiElement.call( - this, - dialog, - outerDefinition, - htmlList, - 'a', - null, - { - style : elementDefinition.style, - href : 'javascript:void(0)', - title : elementDefinition.label, - hidefocus : 'true', - 'class' : elementDefinition['class'], - role : 'button', - 'aria-labelledby' : labelId - }, - '<span id="' + labelId + '" class="cke_dialog_ui_button">' + - CKEDITOR.tools.htmlEncode( elementDefinition.label ) + - '</span>' ); - }, - - /** - * A select box. - * @extends CKEDITOR.ui.dialog.uiElement - * @example - * @constructor - * @param {CKEDITOR.dialog} dialog - * Parent dialog object. - * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition - * The element definition. Accepted fields: - * <ul> - * <li><strong>default</strong> (Required) The default value.</li> - * <li><strong>validate</strong> (Optional) The validation function.</li> - * <li><strong>items</strong> (Required) An array of options. Each option - * is a 1- or 2-item array of format [ 'Description', 'Value' ]. If 'Value' - * is missing, then the value would be assumed to be the same as the - * description.</li> - * <li><strong>multiple</strong> (Optional) Set this to true if you'd like - * to have a multiple-choice select box.</li> - * <li><strong>size</strong> (Optional) The number of items to display in - * the select box.</li> - * </ul> - * @param {Array} htmlList - * List of HTML code to output to. - */ - select : function( dialog, elementDefinition, htmlList ) - { - if ( arguments.length < 3 ) - return; - - var _ = initPrivateObject.call( this, elementDefinition ); - - if ( elementDefinition.validate ) - this.validate = elementDefinition.validate; - - _.inputId = CKEDITOR.tools.getNextNumber() + '_select'; - /** @ignore */ - var innerHTML = function() - { - var myDefinition = CKEDITOR.tools.extend( {}, elementDefinition, - { - id : elementDefinition.id ? elementDefinition.id + '_select' : CKEDITOR.tools.getNextNumber() + '_select' - }, true ), - html = [], - innerHTML = [], - attributes = { 'id' : _.inputId, 'class' : 'cke_dialog_ui_input_select', 'aria-labelledby' : this._.labelId }; - - // Add multiple and size attributes from element definition. - if ( elementDefinition.size != undefined ) - attributes.size = elementDefinition.size; - if ( elementDefinition.multiple != undefined ) - attributes.multiple = elementDefinition.multiple; - - cleanInnerDefinition( myDefinition ); - for ( var i = 0, item ; i < elementDefinition.items.length && ( item = elementDefinition.items[i] ) ; i++ ) - { - innerHTML.push( '<option value="', - CKEDITOR.tools.htmlEncode( item[1] !== undefined ? item[1] : item[0] ), '" /> ', - CKEDITOR.tools.htmlEncode( item[0] ) ); - } - - _.select = new CKEDITOR.ui.dialog.uiElement( dialog, myDefinition, html, 'select', null, attributes, innerHTML.join( '' ) ); - return html.join( '' ); - }; - - CKEDITOR.ui.dialog.labeledElement.call( this, dialog, elementDefinition, htmlList, innerHTML ); - }, - - /** - * A file upload input. - * @extends CKEDITOR.ui.dialog.labeledElement - * @example - * @constructor - * @param {CKEDITOR.dialog} dialog - * Parent dialog object. - * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition - * The element definition. Accepted fields: - * <ul> - * <li><strong>validate</strong> (Optional) The validation function.</li> - * </ul> - * @param {Array} htmlList - * List of HTML code to output to. - */ - file : function( dialog, elementDefinition, htmlList ) - { - if ( arguments.length < 3 ) - return; - - if ( elementDefinition['default'] === undefined ) - elementDefinition['default'] = ''; - - var _ = CKEDITOR.tools.extend( initPrivateObject.call( this, elementDefinition ), { definition : elementDefinition, buttons : [] } ); - - if ( elementDefinition.validate ) - this.validate = elementDefinition.validate; - - /** @ignore */ - var innerHTML = function() - { - _.frameId = CKEDITOR.tools.getNextNumber() + '_fileInput'; - - // Support for custom document.domain in IE. - var isCustomDomain = CKEDITOR.env.isCustomDomain(); - - var html = [ - '<iframe' + - ' frameborder="0"' + - ' allowtransparency="0"' + - ' class="cke_dialog_ui_input_file"' + - ' id="', _.frameId, '"' + - ' title="', elementDefinition.label, '"' + - ' src="javascript:void(' ]; - - html.push( - isCustomDomain ? - '(function(){' + - 'document.open();' + - 'document.domain=\'' + document.domain + '\';' + - 'document.close();' + - '})()' - : - '0' ); - - html.push( - ')">' + - '</iframe>' ); - - return html.join( '' ); - }; - - // IE BUG: Parent container does not resize to contain the iframe automatically. - dialog.on( 'load', function() - { - var iframe = CKEDITOR.document.getById( _.frameId ), - contentDiv = iframe.getParent(); - contentDiv.addClass( 'cke_dialog_ui_input_file' ); - } ); - - CKEDITOR.ui.dialog.labeledElement.call( this, dialog, elementDefinition, htmlList, innerHTML ); - }, - - /** - * A button for submitting the file in a file upload input. - * @extends CKEDITOR.ui.dialog.button - * @example - * @constructor - * @param {CKEDITOR.dialog} dialog - * Parent dialog object. - * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition - * The element definition. Accepted fields: - * <ul> - * <li><strong>for</strong> (Required) The file input's page and element Id - * to associate to, in a 2-item array format: [ 'page_id', 'element_id' ]. - * </li> - * <li><strong>validate</strong> (Optional) The validation function.</li> - * </ul> - * @param {Array} htmlList - * List of HTML code to output to. - */ - fileButton : function( dialog, elementDefinition, htmlList ) - { - if ( arguments.length < 3 ) - return; - - var _ = initPrivateObject.call( this, elementDefinition ), - me = this; - - if ( elementDefinition.validate ) - this.validate = elementDefinition.validate; - - var myDefinition = CKEDITOR.tools.extend( {}, elementDefinition ); - var onClick = myDefinition.onClick; - myDefinition.className = ( myDefinition.className ? myDefinition.className + ' ' : '' ) + 'cke_dialog_ui_button'; - myDefinition.onClick = function( evt ) - { - var target = elementDefinition[ 'for' ]; // [ pageId, elementId ] - if ( !onClick || onClick.call( this, evt ) !== false ) - { - dialog.getContentElement( target[0], target[1] ).submit(); - this.disable(); - } - }; - - dialog.on( 'load', function() - { - dialog.getContentElement( elementDefinition[ 'for' ][0], elementDefinition[ 'for' ][1] )._.buttons.push( me ); - } ); - - CKEDITOR.ui.dialog.button.call( this, dialog, myDefinition, htmlList ); - }, - - html : (function() - { - var myHtmlRe = /^\s*<[\w:]+\s+([^>]*)?>/, - theirHtmlRe = /^(\s*<[\w:]+(?:\s+[^>]*)?)((?:.|\r|\n)+)$/, - emptyTagRe = /\/$/; - /** - * A dialog element made from raw HTML code. - * @extends CKEDITOR.ui.dialog.uiElement - * @name CKEDITOR.ui.dialog.html - * @param {CKEDITOR.dialog} dialog Parent dialog object. - * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition Element definition. - * Accepted fields: - * <ul> - * <li><strong>html</strong> (Required) HTML code of this element.</li> - * </ul> - * @param {Array} htmlList List of HTML code to be added to the dialog's content area. - * @example - * @constructor - */ - return function( dialog, elementDefinition, htmlList ) - { - if ( arguments.length < 3 ) - return; - - var myHtmlList = [], - myHtml, - theirHtml = elementDefinition.html, - myMatch, theirMatch; - - // If the HTML input doesn't contain any tags at the beginning, add a <span> tag around it. - if ( theirHtml.charAt( 0 ) != '<' ) - theirHtml = '<span>' + theirHtml + '</span>'; - - // Look for focus function in definition. - var focus = elementDefinition.focus; - if ( focus ) - { - var oldFocus = this.focus; - this.focus = function() - { - oldFocus.call( this ); - typeof focus == 'function' && focus.call( this ); - this.fire( 'focus' ); - }; - if ( elementDefinition.isFocusable ) - { - var oldIsFocusable = this.isFocusable; - this.isFocusable = oldIsFocusable; - } - this.keyboardFocusable = true; - } - - CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition, myHtmlList, 'span', null, null, '' ); - - // Append the attributes created by the uiElement call to the real HTML. - myHtml = myHtmlList.join( '' ); - myMatch = myHtml.match( myHtmlRe ); - theirMatch = theirHtml.match( theirHtmlRe ) || [ '', '', '' ]; - - if ( emptyTagRe.test( theirMatch[1] ) ) - { - theirMatch[1] = theirMatch[1].slice( 0, -1 ); - theirMatch[2] = '/' + theirMatch[2]; - } - - htmlList.push( [ theirMatch[1], ' ', myMatch[1] || '', theirMatch[2] ].join( '' ) ); - }; - })(), - - /** - * Form fieldset for grouping dialog UI elements. - * @constructor - * @extends CKEDITOR.ui.dialog.uiElement - * @param {CKEDITOR.dialog} dialog Parent dialog object. - * @param {Array} childObjList - * Array of {@link CKEDITOR.ui.dialog.uiElement} objects inside this - * container. - * @param {Array} childHtmlList - * Array of HTML code that correspond to the HTML output of all the - * objects in childObjList. - * @param {Array} htmlList - * Array of HTML code that this element will output to. - * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition - * The element definition. Accepted fields: - * <ul> - * <li><strong>label</strong> (Optional) The legend of the this fieldset.</li> - * <li><strong>children</strong> (Required) An array of dialog field definitions which will be grouped inside this fieldset. </li> - * </ul> - */ - fieldset : function( dialog, childObjList, childHtmlList, htmlList, elementDefinition ) - { - var legendLabel = elementDefinition.label; - /** @ignore */ - var innerHTML = function() - { - var html = []; - legendLabel && html.push( '<legend>' + legendLabel + '</legend>' ); - for ( var i = 0; i < childHtmlList.length; i++ ) - html.push( childHtmlList[ i ] ); - return html.join( '' ); - }; - - this._ = { children : childObjList }; - CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition, htmlList, 'fieldset', null, null, innerHTML ); - } - - }, true ); - - CKEDITOR.ui.dialog.html.prototype = new CKEDITOR.ui.dialog.uiElement; - - CKEDITOR.ui.dialog.labeledElement.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.uiElement, - /** @lends CKEDITOR.ui.dialog.labeledElement.prototype */ - { - /** - * Sets the label text of the element. - * @param {String} label The new label text. - * @returns {CKEDITOR.ui.dialog.labeledElement} The current labeled element. - * @example - */ - setLabel : function( label ) - { - var node = CKEDITOR.document.getById( this._.labelId ); - if ( node.getChildCount() < 1 ) - ( new CKEDITOR.dom.text( label, CKEDITOR.document ) ).appendTo( node ); - else - node.getChild( 0 ).$.nodeValue = label; - return this; - }, - - /** - * Retrieves the current label text of the elment. - * @returns {String} The current label text. - * @example - */ - getLabel : function() - { - var node = CKEDITOR.document.getById( this._.labelId ); - if ( !node || node.getChildCount() < 1 ) - return ''; - else - return node.getChild( 0 ).getText(); - }, - - /** - * Defines the onChange event for UI element definitions. - * @field - * @type Object - * @example - */ - eventProcessors : commonEventProcessors - }, true ); - - CKEDITOR.ui.dialog.button.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.uiElement, - /** @lends CKEDITOR.ui.dialog.button.prototype */ - { - /** - * Simulates a click to the button. - * @example - * @returns {Object} Return value of the 'click' event. - */ - click : function() - { - if ( !this._.disabled ) - return this.fire( 'click', { dialog : this._.dialog } ); - this.getElement().$.blur(); - return false; - }, - - /** - * Enables the button. - * @example - */ - enable : function() - { - this._.disabled = false; - var element = this.getElement(); - element && element.removeClass( 'disabled' ); - }, - - /** - * Disables the button. - * @example - */ - disable : function() - { - this._.disabled = true; - this.getElement().addClass( 'disabled' ); - }, - - isVisible : function() - { - return this.getElement().getFirst().isVisible(); - }, - - isEnabled : function() - { - return !this._.disabled; - }, - - /** - * Defines the onChange event and onClick for button element definitions. - * @field - * @type Object - * @example - */ - eventProcessors : CKEDITOR.tools.extend( {}, CKEDITOR.ui.dialog.uiElement.prototype.eventProcessors, - { - /** @ignore */ - onClick : function( dialog, func ) - { - this.on( 'click', func ); - } - }, true ), - - /** - * Handler for the element's access key up event. Simulates a click to - * the button. - * @example - */ - accessKeyUp : function() - { - this.click(); - }, - - /** - * Handler for the element's access key down event. Simulates a mouse - * down to the button. - * @example - */ - accessKeyDown : function() - { - this.focus(); - }, - - keyboardFocusable : true - }, true ); - - CKEDITOR.ui.dialog.textInput.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.labeledElement, - /** @lends CKEDITOR.ui.dialog.textInput.prototype */ - { - /** - * Gets the text input DOM element under this UI object. - * @example - * @returns {CKEDITOR.dom.element} The DOM element of the text input. - */ - getInputElement : function() - { - return CKEDITOR.document.getById( this._.inputId ); - }, - - /** - * Puts focus into the text input. - * @example - */ - focus : function() - { - var me = this.selectParentTab(); - - // GECKO BUG: setTimeout() is needed to workaround invisible selections. - setTimeout( function() - { - var element = me.getInputElement(); - element && element.$.focus(); - }, 0 ); - }, - - /** - * Selects all the text in the text input. - * @example - */ - select : function() - { - var me = this.selectParentTab(); - - // GECKO BUG: setTimeout() is needed to workaround invisible selections. - setTimeout( function() - { - var e = me.getInputElement(); - if ( e ) - { - e.$.focus(); - e.$.select(); - } - }, 0 ); - }, - - /** - * Handler for the text input's access key up event. Makes a select() - * call to the text input. - * @example - */ - accessKeyUp : function() - { - this.select(); - }, - - /** - * Sets the value of this text input object. - * @param {Object} value The new value. - * @returns {CKEDITOR.ui.dialog.textInput} The current UI element. - * @example - * uiElement.setValue( 'Blamo' ); - */ - setValue : function( value ) - { - !value && ( value = '' ); - return CKEDITOR.ui.dialog.uiElement.prototype.setValue.call( this, value ); - }, - - keyboardFocusable : true - }, commonPrototype, true ); - - CKEDITOR.ui.dialog.textarea.prototype = new CKEDITOR.ui.dialog.textInput(); - - CKEDITOR.ui.dialog.select.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.labeledElement, - /** @lends CKEDITOR.ui.dialog.select.prototype */ - { - /** - * Gets the DOM element of the select box. - * @returns {CKEDITOR.dom.element} The <select> element of this UI - * element. - * @example - */ - getInputElement : function() - { - return this._.select.getElement(); - }, - - /** - * Adds an option to the select box. - * @param {String} label Option label. - * @param {String} value (Optional) Option value, if not defined it'll be - * assumed to be the same as the label. - * @param {Number} index (Optional) Position of the option to be inserted - * to. If not defined the new option will be inserted to the end of list. - * @example - * @returns {CKEDITOR.ui.dialog.select} The current select UI element. - */ - add : function( label, value, index ) - { - var option = new CKEDITOR.dom.element( 'option', this.getDialog().getParentEditor().document ), - selectElement = this.getInputElement().$; - option.$.text = label; - option.$.value = ( value === undefined || value === null ) ? label : value; - if ( index === undefined || index === null ) - { - if ( CKEDITOR.env.ie ) - selectElement.add( option.$ ); - else - selectElement.add( option.$, null ); - } - else - selectElement.add( option.$, index ); - return this; - }, - - /** - * Removes an option from the selection list. - * @param {Number} index Index of the option to be removed. - * @example - * @returns {CKEDITOR.ui.dialog.select} The current select UI element. - */ - remove : function( index ) - { - var selectElement = this.getInputElement().$; - selectElement.remove( index ); - return this; - }, - - /** - * Clears all options out of the selection list. - * @returns {CKEDITOR.ui.dialog.select} The current select UI element. - */ - clear : function() - { - var selectElement = this.getInputElement().$; - while ( selectElement.length > 0 ) - selectElement.remove( 0 ); - return this; - }, - - keyboardFocusable : true - }, commonPrototype, true ); - - CKEDITOR.ui.dialog.checkbox.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.uiElement, - /** @lends CKEDITOR.ui.dialog.checkbox.prototype */ - { - /** - * Gets the checkbox DOM element. - * @example - * @returns {CKEDITOR.dom.element} The DOM element of the checkbox. - */ - getInputElement : function() - { - return this._.checkbox.getElement(); - }, - - /** - * Sets the state of the checkbox. - * @example - * @param {Boolean} true to tick the checkbox, false to untick it. - */ - setValue : function( checked ) - { - this.getInputElement().$.checked = checked; - this.fire( 'change', { value : checked } ); - }, - - /** - * Gets the state of the checkbox. - * @example - * @returns {Boolean} true means the checkbox is ticked, false means it's not ticked. - */ - getValue : function() - { - return this.getInputElement().$.checked; - }, - - /** - * Handler for the access key up event. Toggles the checkbox. - * @example - */ - accessKeyUp : function() - { - this.setValue( !this.getValue() ); - }, - - /** - * Defines the onChange event for UI element definitions. - * @field - * @type Object - * @example - */ - eventProcessors : - { - onChange : function( dialog, func ) - { - if ( !CKEDITOR.env.ie ) - return commonEventProcessors.onChange.apply( this, arguments ); - else - { - dialog.on( 'load', function() - { - var element = this._.checkbox.getElement(); - element.on( 'propertychange', function( evt ) - { - evt = evt.data.$; - if ( evt.propertyName == 'checked' ) - this.fire( 'change', { value : element.$.checked } ); - }, this ); - }, this ); - this.on( 'change', func ); - } - return null; - } - }, - - keyboardFocusable : true - }, commonPrototype, true ); - - CKEDITOR.ui.dialog.radio.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.uiElement, - /** @lends CKEDITOR.ui.dialog.radio.prototype */ - { - /** - * Checks one of the radio buttons in this button group. - * @example - * @param {String} value The value of the button to be chcked. - */ - setValue : function( value ) - { - var children = this._.children, - item; - for ( var i = 0 ; ( i < children.length ) && ( item = children[i] ) ; i++ ) - item.getElement().$.checked = ( item.getValue() == value ); - this.fire( 'change', { value : value } ); - }, - - /** - * Gets the value of the currently checked radio button. - * @example - * @returns {String} The currently checked button's value. - */ - getValue : function() - { - var children = this._.children; - for ( var i = 0 ; i < children.length ; i++ ) - { - if ( children[i].getElement().$.checked ) - return children[i].getValue(); - } - return null; - }, - - /** - * Handler for the access key up event. Focuses the currently - * selected radio button, or the first radio button if none is - * selected. - * @example - */ - accessKeyUp : function() - { - var children = this._.children, i; - for ( i = 0 ; i < children.length ; i++ ) - { - if ( children[i].getElement().$.checked ) - { - children[i].getElement().focus(); - return; - } - } - children[0].getElement().focus(); - }, - - /** - * Defines the onChange event for UI element definitions. - * @field - * @type Object - * @example - */ - eventProcessors : - { - onChange : function( dialog, func ) - { - if ( !CKEDITOR.env.ie ) - return commonEventProcessors.onChange.apply( this, arguments ); - else - { - dialog.on( 'load', function() - { - var children = this._.children, me = this; - for ( var i = 0 ; i < children.length ; i++ ) - { - var element = children[i].getElement(); - element.on( 'propertychange', function( evt ) - { - evt = evt.data.$; - if ( evt.propertyName == 'checked' && this.$.checked ) - me.fire( 'change', { value : this.getAttribute( 'value' ) } ); - } ); - } - }, this ); - this.on( 'change', func ); - } - return null; - } - }, - - keyboardFocusable : true - }, commonPrototype, true ); - - CKEDITOR.ui.dialog.file.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.labeledElement, - commonPrototype, - /** @lends CKEDITOR.ui.dialog.file.prototype */ - { - /** - * Gets the <input> element of this file input. - * @returns {CKEDITOR.dom.element} The file input element. - * @example - */ - getInputElement : function() - { - var frameDocument = CKEDITOR.document.getById( this._.frameId ).getFrameDocument(); - return frameDocument.$.forms.length > 0 ? - new CKEDITOR.dom.element( frameDocument.$.forms[0].elements[0] ) : - this.getElement(); - }, - - /** - * Uploads the file in the file input. - * @returns {CKEDITOR.ui.dialog.file} This object. - * @example - */ - submit : function() - { - this.getInputElement().getParent().$.submit(); - return this; - }, - - /** - * Get the action assigned to the form. - * @returns {String} The value of the action. - * @example - */ - getAction : function( action ) - { - return this.getInputElement().getParent().$.action; - }, - - /** - * Redraws the file input and resets the file path in the file input. - * The redraw logic is necessary because non-IE browsers tend to clear - * the <iframe> containing the file input after closing the dialog. - * @example - */ - reset : function() - { - var frameElement = CKEDITOR.document.getById( this._.frameId ), - frameDocument = frameElement.getFrameDocument(), - elementDefinition = this._.definition, - buttons = this._.buttons; - - function generateFormField() - { - frameDocument.$.open(); - - // Support for custom document.domain in IE. - if ( CKEDITOR.env.isCustomDomain() ) - frameDocument.$.domain = document.domain; - - var size = ''; - if ( elementDefinition.size ) - size = elementDefinition.size - ( CKEDITOR.env.ie ? 7 : 0 ); // "Browse" button is bigger in IE. - - frameDocument.$.write( [ '<html><head><title></title></head><body style="margin: 0; overflow: hidden; background: transparent;">', - '<form enctype="multipart/form-data" method="POST" action="', - CKEDITOR.tools.htmlEncode( elementDefinition.action ), - '">', - '<input type="file" name="', - CKEDITOR.tools.htmlEncode( elementDefinition.id || 'cke_upload' ), - '" size="', - CKEDITOR.tools.htmlEncode( size > 0 ? size : "" ), - '" />', - '</form>', - '</body></html>' ].join( '' ) ); - - frameDocument.$.close(); - - for ( var i = 0 ; i < buttons.length ; i++ ) - buttons[i].enable(); - } - - // #3465: Wait for the browser to finish rendering the dialog first. - if ( CKEDITOR.env.gecko ) - setTimeout( generateFormField, 500 ); - else - generateFormField(); - }, - - getValue : function() - { - // The file path returned from the input tag is incomplete anyway, so it's - // safe to ignore it and prevent the confirmation dialog from appearing. - // (Part of #3465) - return ''; - }, - - /** - * Defines the onChange event for UI element definitions. - * @field - * @type Object - * @example - */ - eventProcessors : commonEventProcessors, - - keyboardFocusable : true - }, true ); - - CKEDITOR.ui.dialog.fileButton.prototype = new CKEDITOR.ui.dialog.button; - - CKEDITOR.ui.dialog.fieldset.prototype = CKEDITOR.tools.clone( CKEDITOR.ui.dialog.hbox.prototype ); - - CKEDITOR.dialog.addUIElement( 'text', textBuilder ); - CKEDITOR.dialog.addUIElement( 'password', textBuilder ); - CKEDITOR.dialog.addUIElement( 'textarea', commonBuilder ); - CKEDITOR.dialog.addUIElement( 'checkbox', commonBuilder ); - CKEDITOR.dialog.addUIElement( 'radio', commonBuilder ); - CKEDITOR.dialog.addUIElement( 'button', commonBuilder ); - CKEDITOR.dialog.addUIElement( 'select', commonBuilder ); - CKEDITOR.dialog.addUIElement( 'file', commonBuilder ); - CKEDITOR.dialog.addUIElement( 'fileButton', commonBuilder ); - CKEDITOR.dialog.addUIElement( 'html', commonBuilder ); - CKEDITOR.dialog.addUIElement( 'fieldset', containerBuilder ); -})(); +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/** @fileoverview The "dialogui" plugin. */
+
+CKEDITOR.plugins.add( 'dialogui' );
+
+(function()
+{
+ var initPrivateObject = function( elementDefinition )
+ {
+ this._ || ( this._ = {} );
+ this._['default'] = this._.initValue = elementDefinition['default'] || '';
+ this._.required = elementDefinition[ 'required' ] || false;
+ var args = [ this._ ];
+ for ( var i = 1 ; i < arguments.length ; i++ )
+ args.push( arguments[i] );
+ args.push( true );
+ CKEDITOR.tools.extend.apply( CKEDITOR.tools, args );
+ return this._;
+ },
+ textBuilder =
+ {
+ build : function( dialog, elementDefinition, output )
+ {
+ return new CKEDITOR.ui.dialog.textInput( dialog, elementDefinition, output );
+ }
+ },
+ commonBuilder =
+ {
+ build : function( dialog, elementDefinition, output )
+ {
+ return new CKEDITOR.ui.dialog[elementDefinition.type]( dialog, elementDefinition, output );
+ }
+ },
+ containerBuilder =
+ {
+ build : function( dialog, elementDefinition, output )
+ {
+ var children = elementDefinition.children,
+ child,
+ childHtmlList = [],
+ childObjList = [];
+ for ( var i = 0 ; ( i < children.length && ( child = children[i] ) ) ; i++ )
+ {
+ var childHtml = [];
+ childHtmlList.push( childHtml );
+ childObjList.push( CKEDITOR.dialog._.uiElementBuilders[ child.type ].build( dialog, child, childHtml ) );
+ }
+ return new CKEDITOR.ui.dialog[ elementDefinition.type ]( dialog, childObjList, childHtmlList, output, elementDefinition );
+ }
+ },
+ commonPrototype =
+ {
+ isChanged : function()
+ {
+ return this.getValue() != this.getInitValue();
+ },
+
+ reset : function( noChangeEvent )
+ {
+ this.setValue( this.getInitValue(), noChangeEvent );
+ },
+
+ setInitValue : function()
+ {
+ this._.initValue = this.getValue();
+ },
+
+ resetInitValue : function()
+ {
+ this._.initValue = this._['default'];
+ },
+
+ getInitValue : function()
+ {
+ return this._.initValue;
+ }
+ },
+ commonEventProcessors = CKEDITOR.tools.extend( {}, CKEDITOR.ui.dialog.uiElement.prototype.eventProcessors,
+ {
+ onChange : function( dialog, func )
+ {
+ if ( !this._.domOnChangeRegistered )
+ {
+ dialog.on( 'load', function()
+ {
+ this.getInputElement().on( 'change', function()
+ {
+ // Make sure 'onchange' doesn't get fired after dialog closed. (#5719)
+ if ( !dialog.parts.dialog.isVisible() )
+ return;
+
+ this.fire( 'change', { value : this.getValue() } );
+ }, this );
+ }, this );
+ this._.domOnChangeRegistered = true;
+ }
+
+ this.on( 'change', func );
+ }
+ }, true ),
+ eventRegex = /^on([A-Z]\w+)/,
+ cleanInnerDefinition = function( def )
+ {
+ // An inner UI element should not have the parent's type, title or events.
+ for ( var i in def )
+ {
+ if ( eventRegex.test( i ) || i == 'title' || i == 'type' )
+ delete def[i];
+ }
+ return def;
+ };
+
+ CKEDITOR.tools.extend( CKEDITOR.ui.dialog,
+ /** @lends CKEDITOR.ui.dialog */
+ {
+ /**
+ * Base class for all dialog elements with a textual label on the left.
+ * @constructor
+ * @example
+ * @extends CKEDITOR.ui.dialog.uiElement
+ * @param {CKEDITOR.dialog} dialog
+ * Parent dialog object.
+ * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition
+ * The element definition. Accepted fields:
+ * <ul>
+ * <li><strong>label</strong> (Required) The label string.</li>
+ * <li><strong>labelLayout</strong> (Optional) Put 'horizontal' here if the
+ * label element is to be layed out horizontally. Otherwise a vertical
+ * layout will be used.</li>
+ * <li><strong>widths</strong> (Optional) This applies only for horizontal
+ * layouts - an 2-element array of lengths to specify the widths of the
+ * label and the content element.</li>
+ * </ul>
+ * @param {Array} htmlList
+ * List of HTML code to output to.
+ * @param {Function} contentHtml
+ * A function returning the HTML code string to be added inside the content
+ * cell.
+ */
+ labeledElement : function( dialog, elementDefinition, htmlList, contentHtml )
+ {
+ if ( arguments.length < 4 )
+ return;
+
+ var _ = initPrivateObject.call( this, elementDefinition );
+ _.labelId = CKEDITOR.tools.getNextId() + '_label';
+ var children = this._.children = [];
+ /** @ignore */
+ var innerHTML = function()
+ {
+ var html = [],
+ requiredClass = elementDefinition.required ? ' cke_required' : '' ;
+ if ( elementDefinition.labelLayout != 'horizontal' )
+ html.push( '<label class="cke_dialog_ui_labeled_label' + requiredClass + '" ',
+ ' id="'+ _.labelId + '"',
+ ' for="' + _.inputId + '"',
+ ( elementDefinition.labelStyle ? ' style="' + elementDefinition.labelStyle + '"' : '' ) +'>',
+ elementDefinition.label,
+ '</label>',
+ '<div class="cke_dialog_ui_labeled_content"' + ( elementDefinition.controlStyle ? ' style="' + elementDefinition.controlStyle + '"' : '' ) + ' role="presentation">',
+ contentHtml.call( this, dialog, elementDefinition ),
+ '</div>' );
+ else
+ {
+ var hboxDefinition = {
+ type : 'hbox',
+ widths : elementDefinition.widths,
+ padding : 0,
+ children :
+ [
+ {
+ type : 'html',
+ html : '<label class="cke_dialog_ui_labeled_label' + requiredClass + '"' +
+ ' id="' + _.labelId + '"' +
+ ' for="' + _.inputId + '"' +
+ ( elementDefinition.labelStyle ? ' style="' + elementDefinition.labelStyle + '"' : '' ) +'>' +
+ CKEDITOR.tools.htmlEncode( elementDefinition.label ) +
+ '</span>'
+ },
+ {
+ type : 'html',
+ html : '<span class="cke_dialog_ui_labeled_content"' + ( elementDefinition.controlStyle ? ' style="' + elementDefinition.controlStyle + '"' : '' ) + '>' +
+ contentHtml.call( this, dialog, elementDefinition ) +
+ '</span>'
+ }
+ ]
+ };
+ CKEDITOR.dialog._.uiElementBuilders.hbox.build( dialog, hboxDefinition, html );
+ }
+ return html.join( '' );
+ };
+ CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition, htmlList, 'div', null, { role : 'presentation' }, innerHTML );
+ },
+
+ /**
+ * A text input with a label. This UI element class represents both the
+ * single-line text inputs and password inputs in dialog boxes.
+ * @constructor
+ * @example
+ * @extends CKEDITOR.ui.dialog.labeledElement
+ * @param {CKEDITOR.dialog} dialog
+ * Parent dialog object.
+ * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition
+ * The element definition. Accepted fields:
+ * <ul>
+ * <li><strong>default</strong> (Optional) The default value.</li>
+ * <li><strong>validate</strong> (Optional) The validation function. </li>
+ * <li><strong>maxLength</strong> (Optional) The maximum length of text box
+ * contents.</li>
+ * <li><strong>size</strong> (Optional) The size of the text box. This is
+ * usually overridden by the size defined by the skin, however.</li>
+ * </ul>
+ * @param {Array} htmlList
+ * List of HTML code to output to.
+ */
+ textInput : function( dialog, elementDefinition, htmlList )
+ {
+ if ( arguments.length < 3 )
+ return;
+
+ initPrivateObject.call( this, elementDefinition );
+ var domId = this._.inputId = CKEDITOR.tools.getNextId() + '_textInput',
+ attributes = { 'class' : 'cke_dialog_ui_input_' + elementDefinition.type, id : domId, type : elementDefinition.type },
+ i;
+
+ // Set the validator, if any.
+ if ( elementDefinition.validate )
+ this.validate = elementDefinition.validate;
+
+ // Set the max length and size.
+ if ( elementDefinition.maxLength )
+ attributes.maxlength = elementDefinition.maxLength;
+ if ( elementDefinition.size )
+ attributes.size = elementDefinition.size;
+
+ if ( elementDefinition.inputStyle )
+ attributes.style = elementDefinition.inputStyle;
+
+ // If user presses Enter in a text box, it implies clicking OK for the dialog.
+ var me = this, keyPressedOnMe = false;
+ dialog.on( 'load', function()
+ {
+ me.getInputElement().on( 'keydown', function( evt )
+ {
+ if ( evt.data.getKeystroke() == 13 )
+ keyPressedOnMe = true;
+ } );
+
+ // Lower the priority this 'keyup' since 'ok' will close the dialog.(#3749)
+ me.getInputElement().on( 'keyup', function( evt )
+ {
+ if ( evt.data.getKeystroke() == 13 && keyPressedOnMe )
+ {
+ dialog.getButton( 'ok' ) && setTimeout( function ()
+ {
+ dialog.getButton( 'ok' ).click();
+ }, 0 );
+ keyPressedOnMe = false;
+ }
+ }, null, null, 1000 );
+ } );
+
+ /** @ignore */
+ var innerHTML = function()
+ {
+ // IE BUG: Text input fields in IE at 100% would exceed a <td> or inline
+ // container's width, so need to wrap it inside a <div>.
+ var html = [ '<div class="cke_dialog_ui_input_', elementDefinition.type, '" role="presentation"' ];
+
+ if ( elementDefinition.width )
+ html.push( 'style="width:'+ elementDefinition.width +'" ' );
+
+ html.push( '><input ' );
+
+ attributes[ 'aria-labelledby' ] = this._.labelId;
+ this._.required && ( attributes[ 'aria-required' ] = this._.required );
+ for ( var i in attributes )
+ html.push( i + '="' + attributes[i] + '" ' );
+ html.push( ' /></div>' );
+ return html.join( '' );
+ };
+ CKEDITOR.ui.dialog.labeledElement.call( this, dialog, elementDefinition, htmlList, innerHTML );
+ },
+
+ /**
+ * A text area with a label on the top or left.
+ * @constructor
+ * @extends CKEDITOR.ui.dialog.labeledElement
+ * @example
+ * @param {CKEDITOR.dialog} dialog
+ * Parent dialog object.
+ * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition
+ * The element definition. Accepted fields:
+ * <ul>
+ * <li><strong>rows</strong> (Optional) The number of rows displayed.
+ * Defaults to 5 if not defined.</li>
+ * <li><strong>cols</strong> (Optional) The number of cols displayed.
+ * Defaults to 20 if not defined. Usually overridden by skins.</li>
+ * <li><strong>default</strong> (Optional) The default value.</li>
+ * <li><strong>validate</strong> (Optional) The validation function. </li>
+ * </ul>
+ * @param {Array} htmlList
+ * List of HTML code to output to.
+ */
+ textarea : function( dialog, elementDefinition, htmlList )
+ {
+ if ( arguments.length < 3 )
+ return;
+
+ initPrivateObject.call( this, elementDefinition );
+ var me = this,
+ domId = this._.inputId = CKEDITOR.tools.getNextId() + '_textarea',
+ attributes = {};
+
+ if ( elementDefinition.validate )
+ this.validate = elementDefinition.validate;
+
+ // Generates the essential attributes for the textarea tag.
+ attributes.rows = elementDefinition.rows || 5;
+ attributes.cols = elementDefinition.cols || 20;
+
+ if ( typeof elementDefinition.inputStyle != 'undefined' )
+ attributes.style = elementDefinition.inputStyle;
+
+
+ /** @ignore */
+ var innerHTML = function()
+ {
+ attributes[ 'aria-labelledby' ] = this._.labelId;
+ this._.required && ( attributes[ 'aria-required' ] = this._.required );
+ var html = [ '<div class="cke_dialog_ui_input_textarea" role="presentation"><textarea class="cke_dialog_ui_input_textarea" id="', domId, '" ' ];
+ for ( var i in attributes )
+ html.push( i + '="' + CKEDITOR.tools.htmlEncode( attributes[i] ) + '" ' );
+ html.push( '>', CKEDITOR.tools.htmlEncode( me._['default'] ), '</textarea></div>' );
+ return html.join( '' );
+ };
+ CKEDITOR.ui.dialog.labeledElement.call( this, dialog, elementDefinition, htmlList, innerHTML );
+ },
+
+ /**
+ * A single checkbox with a label on the right.
+ * @constructor
+ * @extends CKEDITOR.ui.dialog.uiElement
+ * @example
+ * @param {CKEDITOR.dialog} dialog
+ * Parent dialog object.
+ * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition
+ * The element definition. Accepted fields:
+ * <ul>
+ * <li><strong>checked</strong> (Optional) Whether the checkbox is checked
+ * on instantiation. Defaults to false.</li>
+ * <li><strong>validate</strong> (Optional) The validation function.</li>
+ * <li><strong>label</strong> (Optional) The checkbox label.</li>
+ * </ul>
+ * @param {Array} htmlList
+ * List of HTML code to output to.
+ */
+ checkbox : function( dialog, elementDefinition, htmlList )
+ {
+ if ( arguments.length < 3 )
+ return;
+
+ var _ = initPrivateObject.call( this, elementDefinition, { 'default' : !!elementDefinition[ 'default' ] } );
+
+ if ( elementDefinition.validate )
+ this.validate = elementDefinition.validate;
+
+ /** @ignore */
+ var innerHTML = function()
+ {
+ var myDefinition = CKEDITOR.tools.extend( {}, elementDefinition,
+ {
+ id : elementDefinition.id ? elementDefinition.id + '_checkbox' : CKEDITOR.tools.getNextId() + '_checkbox'
+ }, true ),
+ html = [];
+
+ var labelId = CKEDITOR.tools.getNextId() + '_label';
+ var attributes = { 'class' : 'cke_dialog_ui_checkbox_input', type : 'checkbox', 'aria-labelledby' : labelId };
+ cleanInnerDefinition( myDefinition );
+ if ( elementDefinition[ 'default' ] )
+ attributes.checked = 'checked';
+
+ if ( typeof myDefinition.inputStyle != 'undefined' )
+ myDefinition.style = myDefinition.inputStyle;
+
+ _.checkbox = new CKEDITOR.ui.dialog.uiElement( dialog, myDefinition, html, 'input', null, attributes );
+ html.push( ' <label id="', labelId, '" for="', attributes.id, '"' + ( elementDefinition.labelStyle ? ' style="' + elementDefinition.labelStyle + '"' : '' ) + '>',
+ CKEDITOR.tools.htmlEncode( elementDefinition.label ),
+ '</label>' );
+ return html.join( '' );
+ };
+
+ CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition, htmlList, 'span', null, null, innerHTML );
+ },
+
+ /**
+ * A group of radio buttons.
+ * @constructor
+ * @example
+ * @extends CKEDITOR.ui.dialog.labeledElement
+ * @param {CKEDITOR.dialog} dialog
+ * Parent dialog object.
+ * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition
+ * The element definition. Accepted fields:
+ * <ul>
+ * <li><strong>default</strong> (Required) The default value.</li>
+ * <li><strong>validate</strong> (Optional) The validation function.</li>
+ * <li><strong>items</strong> (Required) An array of options. Each option
+ * is a 1- or 2-item array of format [ 'Description', 'Value' ]. If 'Value'
+ * is missing, then the value would be assumed to be the same as the
+ * description.</li>
+ * </ul>
+ * @param {Array} htmlList
+ * List of HTML code to output to.
+ */
+ radio : function( dialog, elementDefinition, htmlList )
+ {
+ if ( arguments.length < 3)
+ return;
+
+ initPrivateObject.call( this, elementDefinition );
+ if ( !this._['default'] )
+ this._['default'] = this._.initValue = elementDefinition.items[0][1];
+ if ( elementDefinition.validate )
+ this.validate = elementDefinition.valdiate;
+ var children = [], me = this;
+
+ /** @ignore */
+ var innerHTML = function()
+ {
+ var inputHtmlList = [], html = [],
+ commonAttributes = { 'class' : 'cke_dialog_ui_radio_item', 'aria-labelledby' : this._.labelId },
+ commonName = elementDefinition.id ? elementDefinition.id + '_radio' : CKEDITOR.tools.getNextId() + '_radio';
+ for ( var i = 0 ; i < elementDefinition.items.length ; i++ )
+ {
+ var item = elementDefinition.items[i],
+ title = item[2] !== undefined ? item[2] : item[0],
+ value = item[1] !== undefined ? item[1] : item[0],
+ inputId = CKEDITOR.tools.getNextId() + '_radio_input',
+ labelId = inputId + '_label',
+ inputDefinition = CKEDITOR.tools.extend( {}, elementDefinition,
+ {
+ id : inputId,
+ title : null,
+ type : null
+ }, true ),
+ labelDefinition = CKEDITOR.tools.extend( {}, inputDefinition,
+ {
+ title : title
+ }, true ),
+ inputAttributes =
+ {
+ type : 'radio',
+ 'class' : 'cke_dialog_ui_radio_input',
+ name : commonName,
+ value : value,
+ 'aria-labelledby' : labelId
+ },
+ inputHtml = [];
+ if ( me._['default'] == value )
+ inputAttributes.checked = 'checked';
+ cleanInnerDefinition( inputDefinition );
+ cleanInnerDefinition( labelDefinition );
+
+ if ( typeof inputDefinition.inputStyle != 'undefined' )
+ inputDefinition.style = inputDefinition.inputStyle;
+
+ children.push( new CKEDITOR.ui.dialog.uiElement( dialog, inputDefinition, inputHtml, 'input', null, inputAttributes ) );
+ inputHtml.push( ' ' );
+ new CKEDITOR.ui.dialog.uiElement( dialog, labelDefinition, inputHtml, 'label', null, { id : labelId, 'for' : inputAttributes.id },
+ item[0] );
+ inputHtmlList.push( inputHtml.join( '' ) );
+ }
+ new CKEDITOR.ui.dialog.hbox( dialog, children, inputHtmlList, html );
+ return html.join( '' );
+ };
+
+ CKEDITOR.ui.dialog.labeledElement.call( this, dialog, elementDefinition, htmlList, innerHTML );
+ this._.children = children;
+ },
+
+ /**
+ * A button with a label inside.
+ * @constructor
+ * @example
+ * @extends CKEDITOR.ui.dialog.uiElement
+ * @param {CKEDITOR.dialog} dialog
+ * Parent dialog object.
+ * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition
+ * The element definition. Accepted fields:
+ * <ul>
+ * <li><strong>label</strong> (Required) The button label.</li>
+ * <li><strong>disabled</strong> (Optional) Set to true if you want the
+ * button to appear in disabled state.</li>
+ * </ul>
+ * @param {Array} htmlList
+ * List of HTML code to output to.
+ */
+ button : function( dialog, elementDefinition, htmlList )
+ {
+ if ( !arguments.length )
+ return;
+
+ if ( typeof elementDefinition == 'function' )
+ elementDefinition = elementDefinition( dialog.getParentEditor() );
+
+ initPrivateObject.call( this, elementDefinition, { disabled : elementDefinition.disabled || false } );
+
+ // Add OnClick event to this input.
+ CKEDITOR.event.implementOn( this );
+
+ var me = this;
+
+ // Register an event handler for processing button clicks.
+ dialog.on( 'load', function( eventInfo )
+ {
+ var element = this.getElement();
+
+ (function()
+ {
+ element.on( 'click', function( evt )
+ {
+ me.fire( 'click', { dialog : me.getDialog() } );
+ evt.data.preventDefault();
+ } );
+
+ element.on( 'keydown', function( evt )
+ {
+ if ( evt.data.getKeystroke() in { 32:1 } )
+ {
+ me.click();
+ evt.data.preventDefault();
+ }
+ } );
+ })();
+
+ element.unselectable();
+ }, this );
+
+ var outerDefinition = CKEDITOR.tools.extend( {}, elementDefinition );
+ delete outerDefinition.style;
+
+ var labelId = CKEDITOR.tools.getNextId() + '_label';
+ CKEDITOR.ui.dialog.uiElement.call(
+ this,
+ dialog,
+ outerDefinition,
+ htmlList,
+ 'a',
+ null,
+ {
+ style : elementDefinition.style,
+ href : 'javascript:void(0)',
+ title : elementDefinition.label,
+ hidefocus : 'true',
+ 'class' : elementDefinition['class'],
+ role : 'button',
+ 'aria-labelledby' : labelId
+ },
+ '<span id="' + labelId + '" class="cke_dialog_ui_button">' +
+ CKEDITOR.tools.htmlEncode( elementDefinition.label ) +
+ '</span>' );
+ },
+
+ /**
+ * A select box.
+ * @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>default</strong> (Required) The default value.</li>
+ * <li><strong>validate</strong> (Optional) The validation function.</li>
+ * <li><strong>items</strong> (Required) An array of options. Each option
+ * is a 1- or 2-item array of format [ 'Description', 'Value' ]. If 'Value'
+ * is missing, then the value would be assumed to be the same as the
+ * description.</li>
+ * <li><strong>multiple</strong> (Optional) Set this to true if you'd like
+ * to have a multiple-choice select box.</li>
+ * <li><strong>size</strong> (Optional) The number of items to display in
+ * the select box.</li>
+ * </ul>
+ * @param {Array} htmlList
+ * List of HTML code to output to.
+ */
+ select : function( dialog, elementDefinition, htmlList )
+ {
+ if ( arguments.length < 3 )
+ return;
+
+ var _ = initPrivateObject.call( this, elementDefinition );
+
+ if ( elementDefinition.validate )
+ this.validate = elementDefinition.validate;
+
+ _.inputId = CKEDITOR.tools.getNextId() + '_select';
+ /** @ignore */
+ var innerHTML = function()
+ {
+ var myDefinition = CKEDITOR.tools.extend( {}, elementDefinition,
+ {
+ id : elementDefinition.id ? elementDefinition.id + '_select' : CKEDITOR.tools.getNextId() + '_select'
+ }, true ),
+ html = [],
+ innerHTML = [],
+ attributes = { 'id' : _.inputId, 'class' : 'cke_dialog_ui_input_select', 'aria-labelledby' : this._.labelId };
+
+ // Add multiple and size attributes from element definition.
+ if ( elementDefinition.size != undefined )
+ attributes.size = elementDefinition.size;
+ if ( elementDefinition.multiple != undefined )
+ attributes.multiple = elementDefinition.multiple;
+
+ cleanInnerDefinition( myDefinition );
+ for ( var i = 0, item ; i < elementDefinition.items.length && ( item = elementDefinition.items[i] ) ; i++ )
+ {
+ innerHTML.push( '<option value="',
+ CKEDITOR.tools.htmlEncode( item[1] !== undefined ? item[1] : item[0] ).replace( /"/g, '"' ), '" /> ',
+ CKEDITOR.tools.htmlEncode( item[0] ) );
+ }
+
+ if ( typeof myDefinition.inputStyle != 'undefined' )
+ myDefinition.style = myDefinition.inputStyle;
+
+ _.select = new CKEDITOR.ui.dialog.uiElement( dialog, myDefinition, html, 'select', null, attributes, innerHTML.join( '' ) );
+ return html.join( '' );
+ };
+
+ CKEDITOR.ui.dialog.labeledElement.call( this, dialog, elementDefinition, htmlList, innerHTML );
+ },
+
+ /**
+ * A file upload input.
+ * @extends CKEDITOR.ui.dialog.labeledElement
+ * @example
+ * @constructor
+ * @param {CKEDITOR.dialog} dialog
+ * Parent dialog object.
+ * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition
+ * The element definition. Accepted fields:
+ * <ul>
+ * <li><strong>validate</strong> (Optional) The validation function.</li>
+ * </ul>
+ * @param {Array} htmlList
+ * List of HTML code to output to.
+ */
+ file : function( dialog, elementDefinition, htmlList )
+ {
+ if ( arguments.length < 3 )
+ return;
+
+ if ( elementDefinition['default'] === undefined )
+ elementDefinition['default'] = '';
+
+ var _ = CKEDITOR.tools.extend( initPrivateObject.call( this, elementDefinition ), { definition : elementDefinition, buttons : [] } );
+
+ if ( elementDefinition.validate )
+ this.validate = elementDefinition.validate;
+
+ /** @ignore */
+ var innerHTML = function()
+ {
+ _.frameId = CKEDITOR.tools.getNextId() + '_fileInput';
+
+ // Support for custom document.domain in IE.
+ var isCustomDomain = CKEDITOR.env.isCustomDomain();
+
+ var html = [
+ '<iframe' +
+ ' frameborder="0"' +
+ ' allowtransparency="0"' +
+ ' class="cke_dialog_ui_input_file"' +
+ ' id="', _.frameId, '"' +
+ ' title="', elementDefinition.label, '"' +
+ ' src="javascript:void(' ];
+
+ html.push(
+ isCustomDomain ?
+ '(function(){' +
+ 'document.open();' +
+ 'document.domain=\'' + document.domain + '\';' +
+ 'document.close();' +
+ '})()'
+ :
+ '0' );
+
+ html.push(
+ ')">' +
+ '</iframe>' );
+
+ return html.join( '' );
+ };
+
+ // IE BUG: Parent container does not resize to contain the iframe automatically.
+ dialog.on( 'load', function()
+ {
+ var iframe = CKEDITOR.document.getById( _.frameId ),
+ contentDiv = iframe.getParent();
+ contentDiv.addClass( 'cke_dialog_ui_input_file' );
+ } );
+
+ CKEDITOR.ui.dialog.labeledElement.call( this, dialog, elementDefinition, htmlList, innerHTML );
+ },
+
+ /**
+ * A button for submitting the file in a file upload input.
+ * @extends CKEDITOR.ui.dialog.button
+ * @example
+ * @constructor
+ * @param {CKEDITOR.dialog} dialog
+ * Parent dialog object.
+ * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition
+ * The element definition. Accepted fields:
+ * <ul>
+ * <li><strong>for</strong> (Required) The file input's page and element Id
+ * to associate to, in a 2-item array format: [ 'page_id', 'element_id' ].
+ * </li>
+ * <li><strong>validate</strong> (Optional) The validation function.</li>
+ * </ul>
+ * @param {Array} htmlList
+ * List of HTML code to output to.
+ */
+ fileButton : function( dialog, elementDefinition, htmlList )
+ {
+ if ( arguments.length < 3 )
+ return;
+
+ var _ = initPrivateObject.call( this, elementDefinition ),
+ me = this;
+
+ if ( elementDefinition.validate )
+ this.validate = elementDefinition.validate;
+
+ var myDefinition = CKEDITOR.tools.extend( {}, elementDefinition );
+ var onClick = myDefinition.onClick;
+ myDefinition.className = ( myDefinition.className ? myDefinition.className + ' ' : '' ) + 'cke_dialog_ui_button';
+ myDefinition.onClick = function( evt )
+ {
+ var target = elementDefinition[ 'for' ]; // [ pageId, elementId ]
+ if ( !onClick || onClick.call( this, evt ) !== false )
+ {
+ dialog.getContentElement( target[0], target[1] ).submit();
+ this.disable();
+ }
+ };
+
+ dialog.on( 'load', function()
+ {
+ dialog.getContentElement( elementDefinition[ 'for' ][0], elementDefinition[ 'for' ][1] )._.buttons.push( me );
+ } );
+
+ CKEDITOR.ui.dialog.button.call( this, dialog, myDefinition, htmlList );
+ },
+
+ html : (function()
+ {
+ var myHtmlRe = /^\s*<[\w:]+\s+([^>]*)?>/,
+ theirHtmlRe = /^(\s*<[\w:]+(?:\s+[^>]*)?)((?:.|\r|\n)+)$/,
+ emptyTagRe = /\/$/;
+ /**
+ * A dialog element made from raw HTML code.
+ * @extends CKEDITOR.ui.dialog.uiElement
+ * @name CKEDITOR.ui.dialog.html
+ * @param {CKEDITOR.dialog} dialog Parent dialog object.
+ * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition Element definition.
+ * Accepted fields:
+ * <ul>
+ * <li><strong>html</strong> (Required) HTML code of this element.</li>
+ * </ul>
+ * @param {Array} htmlList List of HTML code to be added to the dialog's content area.
+ * @example
+ * @constructor
+ */
+ return function( dialog, elementDefinition, htmlList )
+ {
+ if ( arguments.length < 3 )
+ return;
+
+ var myHtmlList = [],
+ myHtml,
+ theirHtml = elementDefinition.html,
+ myMatch, theirMatch;
+
+ // If the HTML input doesn't contain any tags at the beginning, add a <span> tag around it.
+ if ( theirHtml.charAt( 0 ) != '<' )
+ theirHtml = '<span>' + theirHtml + '</span>';
+
+ // Look for focus function in definition.
+ var focus = elementDefinition.focus;
+ if ( focus )
+ {
+ var oldFocus = this.focus;
+ this.focus = function()
+ {
+ oldFocus.call( this );
+ typeof focus == 'function' && focus.call( this );
+ this.fire( 'focus' );
+ };
+ if ( elementDefinition.isFocusable )
+ {
+ var oldIsFocusable = this.isFocusable;
+ this.isFocusable = oldIsFocusable;
+ }
+ this.keyboardFocusable = true;
+ }
+
+ CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition, myHtmlList, 'span', null, null, '' );
+
+ // Append the attributes created by the uiElement call to the real HTML.
+ myHtml = myHtmlList.join( '' );
+ myMatch = myHtml.match( myHtmlRe );
+ theirMatch = theirHtml.match( theirHtmlRe ) || [ '', '', '' ];
+
+ if ( emptyTagRe.test( theirMatch[1] ) )
+ {
+ theirMatch[1] = theirMatch[1].slice( 0, -1 );
+ theirMatch[2] = '/' + theirMatch[2];
+ }
+
+ htmlList.push( [ theirMatch[1], ' ', myMatch[1] || '', theirMatch[2] ].join( '' ) );
+ };
+ })(),
+
+ /**
+ * Form fieldset for grouping dialog UI elements.
+ * @constructor
+ * @extends CKEDITOR.ui.dialog.uiElement
+ * @param {CKEDITOR.dialog} dialog Parent dialog object.
+ * @param {Array} childObjList
+ * Array of {@link CKEDITOR.ui.dialog.uiElement} objects inside this
+ * container.
+ * @param {Array} childHtmlList
+ * Array of HTML code that correspond to the HTML output of all the
+ * objects in childObjList.
+ * @param {Array} htmlList
+ * Array of HTML code that this element will output to.
+ * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition
+ * The element definition. Accepted fields:
+ * <ul>
+ * <li><strong>label</strong> (Optional) The legend of the this fieldset.</li>
+ * <li><strong>children</strong> (Required) An array of dialog field definitions which will be grouped inside this fieldset. </li>
+ * </ul>
+ */
+ fieldset : function( dialog, childObjList, childHtmlList, htmlList, elementDefinition )
+ {
+ var legendLabel = elementDefinition.label;
+ /** @ignore */
+ var innerHTML = function()
+ {
+ var html = [];
+ legendLabel && html.push( '<legend' +
+ ( elementDefinition.labelStyle ? ' style="' + elementDefinition.labelStyle + '"' : '' ) +
+ '>' + legendLabel + '</legend>' );
+ for ( var i = 0; i < childHtmlList.length; i++ )
+ html.push( childHtmlList[ i ] );
+ return html.join( '' );
+ };
+
+ this._ = { children : childObjList };
+ CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition, htmlList, 'fieldset', null, null, innerHTML );
+ }
+
+ }, true );
+
+ CKEDITOR.ui.dialog.html.prototype = new CKEDITOR.ui.dialog.uiElement;
+
+ CKEDITOR.ui.dialog.labeledElement.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.uiElement,
+ /** @lends CKEDITOR.ui.dialog.labeledElement.prototype */
+ {
+ /**
+ * Sets the label text of the element.
+ * @param {String} label The new label text.
+ * @returns {CKEDITOR.ui.dialog.labeledElement} The current labeled element.
+ * @example
+ */
+ setLabel : function( label )
+ {
+ var node = CKEDITOR.document.getById( this._.labelId );
+ if ( node.getChildCount() < 1 )
+ ( new CKEDITOR.dom.text( label, CKEDITOR.document ) ).appendTo( node );
+ else
+ node.getChild( 0 ).$.nodeValue = label;
+ return this;
+ },
+
+ /**
+ * Retrieves the current label text of the elment.
+ * @returns {String} The current label text.
+ * @example
+ */
+ getLabel : function()
+ {
+ var node = CKEDITOR.document.getById( this._.labelId );
+ if ( !node || node.getChildCount() < 1 )
+ return '';
+ else
+ return node.getChild( 0 ).getText();
+ },
+
+ /**
+ * Defines the onChange event for UI element definitions.
+ * @field
+ * @type Object
+ * @example
+ */
+ eventProcessors : commonEventProcessors
+ }, true );
+
+ CKEDITOR.ui.dialog.button.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.uiElement,
+ /** @lends CKEDITOR.ui.dialog.button.prototype */
+ {
+ /**
+ * Simulates a click to the button.
+ * @example
+ * @returns {Object} Return value of the 'click' event.
+ */
+ click : function()
+ {
+ if ( !this._.disabled )
+ return this.fire( 'click', { dialog : this._.dialog } );
+ this.getElement().$.blur();
+ return false;
+ },
+
+ /**
+ * Enables the button.
+ * @example
+ */
+ enable : function()
+ {
+ this._.disabled = false;
+ var element = this.getElement();
+ element && element.removeClass( 'cke_disabled' );
+ },
+
+ /**
+ * Disables the button.
+ * @example
+ */
+ disable : function()
+ {
+ this._.disabled = true;
+ this.getElement().addClass( 'cke_disabled' );
+ },
+
+ isVisible : function()
+ {
+ return this.getElement().getFirst().isVisible();
+ },
+
+ isEnabled : function()
+ {
+ return !this._.disabled;
+ },
+
+ /**
+ * Defines the onChange event and onClick for button element definitions.
+ * @field
+ * @type Object
+ * @example
+ */
+ eventProcessors : CKEDITOR.tools.extend( {}, CKEDITOR.ui.dialog.uiElement.prototype.eventProcessors,
+ {
+ /** @ignore */
+ onClick : function( dialog, func )
+ {
+ this.on( 'click', function()
+ {
+ // Some browsers (Chrome, IE8, IE7 compat mode) don't move
+ // focus to clicked button. Force this.
+ this.getElement().focus();
+ func.apply( this, arguments );
+ });
+ }
+ }, true ),
+
+ /**
+ * Handler for the element's access key up event. Simulates a click to
+ * the button.
+ * @example
+ */
+ accessKeyUp : function()
+ {
+ this.click();
+ },
+
+ /**
+ * Handler for the element's access key down event. Simulates a mouse
+ * down to the button.
+ * @example
+ */
+ accessKeyDown : function()
+ {
+ this.focus();
+ },
+
+ keyboardFocusable : true
+ }, true );
+
+ CKEDITOR.ui.dialog.textInput.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.labeledElement,
+ /** @lends CKEDITOR.ui.dialog.textInput.prototype */
+ {
+ /**
+ * Gets the text input DOM element under this UI object.
+ * @example
+ * @returns {CKEDITOR.dom.element} The DOM element of the text input.
+ */
+ getInputElement : function()
+ {
+ return CKEDITOR.document.getById( this._.inputId );
+ },
+
+ /**
+ * Puts focus into the text input.
+ * @example
+ */
+ focus : function()
+ {
+ var me = this.selectParentTab();
+
+ // GECKO BUG: setTimeout() is needed to workaround invisible selections.
+ setTimeout( function()
+ {
+ var element = me.getInputElement();
+ element && element.$.focus();
+ }, 0 );
+ },
+
+ /**
+ * Selects all the text in the text input.
+ * @example
+ */
+ select : function()
+ {
+ var me = this.selectParentTab();
+
+ // GECKO BUG: setTimeout() is needed to workaround invisible selections.
+ setTimeout( function()
+ {
+ var e = me.getInputElement();
+ if ( e )
+ {
+ e.$.focus();
+ e.$.select();
+ }
+ }, 0 );
+ },
+
+ /**
+ * Handler for the text input's access key up event. Makes a select()
+ * call to the text input.
+ * @example
+ */
+ accessKeyUp : function()
+ {
+ this.select();
+ },
+
+ /**
+ * Sets the value of this text input object.
+ * @param {Object} value The new value.
+ * @returns {CKEDITOR.ui.dialog.textInput} The current UI element.
+ * @example
+ * uiElement.setValue( 'Blamo' );
+ */
+ setValue : function( value )
+ {
+ !value && ( value = '' );
+ return CKEDITOR.ui.dialog.uiElement.prototype.setValue.apply( this, arguments );
+ },
+
+ keyboardFocusable : true
+ }, commonPrototype, true );
+
+ CKEDITOR.ui.dialog.textarea.prototype = new CKEDITOR.ui.dialog.textInput();
+
+ CKEDITOR.ui.dialog.select.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.labeledElement,
+ /** @lends CKEDITOR.ui.dialog.select.prototype */
+ {
+ /**
+ * Gets the DOM element of the select box.
+ * @returns {CKEDITOR.dom.element} The <select> element of this UI
+ * element.
+ * @example
+ */
+ getInputElement : function()
+ {
+ return this._.select.getElement();
+ },
+
+ /**
+ * Adds an option to the select box.
+ * @param {String} label Option label.
+ * @param {String} value (Optional) Option value, if not defined it'll be
+ * assumed to be the same as the label.
+ * @param {Number} index (Optional) Position of the option to be inserted
+ * to. If not defined the new option will be inserted to the end of list.
+ * @example
+ * @returns {CKEDITOR.ui.dialog.select} The current select UI element.
+ */
+ add : function( label, value, index )
+ {
+ var option = new CKEDITOR.dom.element( 'option', this.getDialog().getParentEditor().document ),
+ selectElement = this.getInputElement().$;
+ option.$.text = label;
+ option.$.value = ( value === undefined || value === null ) ? label : value;
+ if ( index === undefined || index === null )
+ {
+ if ( CKEDITOR.env.ie )
+ selectElement.add( option.$ );
+ else
+ selectElement.add( option.$, null );
+ }
+ else
+ selectElement.add( option.$, index );
+ return this;
+ },
+
+ /**
+ * Removes an option from the selection list.
+ * @param {Number} index Index of the option to be removed.
+ * @example
+ * @returns {CKEDITOR.ui.dialog.select} The current select UI element.
+ */
+ remove : function( index )
+ {
+ var selectElement = this.getInputElement().$;
+ selectElement.remove( index );
+ return this;
+ },
+
+ /**
+ * Clears all options out of the selection list.
+ * @returns {CKEDITOR.ui.dialog.select} The current select UI element.
+ */
+ clear : function()
+ {
+ var selectElement = this.getInputElement().$;
+ while ( selectElement.length > 0 )
+ selectElement.remove( 0 );
+ return this;
+ },
+
+ keyboardFocusable : true
+ }, commonPrototype, true );
+
+ CKEDITOR.ui.dialog.checkbox.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.uiElement,
+ /** @lends CKEDITOR.ui.dialog.checkbox.prototype */
+ {
+ /**
+ * Gets the checkbox DOM element.
+ * @example
+ * @returns {CKEDITOR.dom.element} The DOM element of the checkbox.
+ */
+ getInputElement : function()
+ {
+ return this._.checkbox.getElement();
+ },
+
+ /**
+ * Sets the state of the checkbox.
+ * @example
+ * @param {Boolean} true to tick the checkbox, false to untick it.
+ * @param {Boolean} noChangeEvent Internal commit, to supress 'change' event on this element.
+ */
+ setValue : function( checked, noChangeEvent )
+ {
+ this.getInputElement().$.checked = checked;
+ !noChangeEvent && this.fire( 'change', { value : checked } );
+ },
+
+ /**
+ * Gets the state of the checkbox.
+ * @example
+ * @returns {Boolean} true means the checkbox is ticked, false means it's not ticked.
+ */
+ getValue : function()
+ {
+ return this.getInputElement().$.checked;
+ },
+
+ /**
+ * Handler for the access key up event. Toggles the checkbox.
+ * @example
+ */
+ accessKeyUp : function()
+ {
+ this.setValue( !this.getValue() );
+ },
+
+ /**
+ * Defines the onChange event for UI element definitions.
+ * @field
+ * @type Object
+ * @example
+ */
+ eventProcessors :
+ {
+ onChange : function( dialog, func )
+ {
+ if ( !CKEDITOR.env.ie )
+ return commonEventProcessors.onChange.apply( this, arguments );
+ else
+ {
+ dialog.on( 'load', function()
+ {
+ var element = this._.checkbox.getElement();
+ element.on( 'propertychange', function( evt )
+ {
+ evt = evt.data.$;
+ if ( evt.propertyName == 'checked' )
+ this.fire( 'change', { value : element.$.checked } );
+ }, this );
+ }, this );
+ this.on( 'change', func );
+ }
+ return null;
+ }
+ },
+
+ keyboardFocusable : true
+ }, commonPrototype, true );
+
+ CKEDITOR.ui.dialog.radio.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.uiElement,
+ /** @lends CKEDITOR.ui.dialog.radio.prototype */
+ {
+ /**
+ * Checks one of the radio buttons in this button group.
+ * @example
+ * @param {String} value The value of the button to be chcked.
+ * @param {Boolean} noChangeEvent Internal commit, to supress 'change' event on this element.
+ */
+ setValue : function( value, noChangeEvent )
+ {
+ var children = this._.children,
+ item;
+ for ( var i = 0 ; ( i < children.length ) && ( item = children[i] ) ; i++ )
+ item.getElement().$.checked = ( item.getValue() == value );
+ !noChangeEvent && this.fire( 'change', { value : value } );
+ },
+
+ /**
+ * Gets the value of the currently checked radio button.
+ * @example
+ * @returns {String} The currently checked button's value.
+ */
+ getValue : function()
+ {
+ var children = this._.children;
+ for ( var i = 0 ; i < children.length ; i++ )
+ {
+ if ( children[i].getElement().$.checked )
+ return children[i].getValue();
+ }
+ return null;
+ },
+
+ /**
+ * Handler for the access key up event. Focuses the currently
+ * selected radio button, or the first radio button if none is
+ * selected.
+ * @example
+ */
+ accessKeyUp : function()
+ {
+ var children = this._.children, i;
+ for ( i = 0 ; i < children.length ; i++ )
+ {
+ if ( children[i].getElement().$.checked )
+ {
+ children[i].getElement().focus();
+ return;
+ }
+ }
+ children[0].getElement().focus();
+ },
+
+ /**
+ * Defines the onChange event for UI element definitions.
+ * @field
+ * @type Object
+ * @example
+ */
+ eventProcessors :
+ {
+ onChange : function( dialog, func )
+ {
+ if ( !CKEDITOR.env.ie )
+ return commonEventProcessors.onChange.apply( this, arguments );
+ else
+ {
+ dialog.on( 'load', function()
+ {
+ var children = this._.children, me = this;
+ for ( var i = 0 ; i < children.length ; i++ )
+ {
+ var element = children[i].getElement();
+ element.on( 'propertychange', function( evt )
+ {
+ evt = evt.data.$;
+ if ( evt.propertyName == 'checked' && this.$.checked )
+ me.fire( 'change', { value : this.getAttribute( 'value' ) } );
+ } );
+ }
+ }, this );
+ this.on( 'change', func );
+ }
+ return null;
+ }
+ },
+
+ keyboardFocusable : true
+ }, commonPrototype, true );
+
+ CKEDITOR.ui.dialog.file.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.labeledElement,
+ commonPrototype,
+ /** @lends CKEDITOR.ui.dialog.file.prototype */
+ {
+ /**
+ * Gets the <input> element of this file input.
+ * @returns {CKEDITOR.dom.element} The file input element.
+ * @example
+ */
+ getInputElement : function()
+ {
+ var frameDocument = CKEDITOR.document.getById( this._.frameId ).getFrameDocument();
+ return frameDocument.$.forms.length > 0 ?
+ new CKEDITOR.dom.element( frameDocument.$.forms[0].elements[0] ) :
+ this.getElement();
+ },
+
+ /**
+ * Uploads the file in the file input.
+ * @returns {CKEDITOR.ui.dialog.file} This object.
+ * @example
+ */
+ submit : function()
+ {
+ this.getInputElement().getParent().$.submit();
+ return this;
+ },
+
+ /**
+ * Get the action assigned to the form.
+ * @returns {String} The value of the action.
+ * @example
+ */
+ getAction : function()
+ {
+ return this.getInputElement().getParent().$.action;
+ },
+
+ /**
+ * The events must be applied on the inner input element, and
+ * that must be done when the iframe & form has been loaded
+ */
+ registerEvents : function( definition )
+ {
+ var regex = /^on([A-Z]\w+)/,
+ match;
+
+ var registerDomEvent = function( uiElement, dialog, eventName, func )
+ {
+ uiElement.on( 'formLoaded', function()
+ {
+ uiElement.getInputElement().on( eventName, func, uiElement );
+ });
+ };
+
+ for ( var i in definition )
+ {
+ if ( !( match = i.match( regex ) ) )
+ continue;
+
+ if ( this.eventProcessors[i] )
+ this.eventProcessors[i].call( this, this._.dialog, definition[i] );
+ else
+ registerDomEvent( this, this._.dialog, match[1].toLowerCase(), definition[i] );
+ }
+
+ return this;
+ },
+
+ /**
+ * Redraws the file input and resets the file path in the file input.
+ * The redraw logic is necessary because non-IE browsers tend to clear
+ * the <iframe> containing the file input after closing the dialog.
+ * @example
+ */
+ reset : function()
+ {
+ var _ = this._,
+ frameElement = CKEDITOR.document.getById( _.frameId ),
+ frameDocument = frameElement.getFrameDocument(),
+ elementDefinition = _.definition,
+ buttons = _.buttons,
+ callNumber = this.formLoadedNumber,
+ unloadNumber = this.formUnloadNumber,
+ langDir = _.dialog._.editor.lang.dir,
+ langCode = _.dialog._.editor.langCode;
+
+ // The callback function for the iframe, but we must call tools.addFunction only once
+ // so we store the function number in this.formLoadedNumber
+ if ( !callNumber )
+ {
+ callNumber = this.formLoadedNumber = CKEDITOR.tools.addFunction(
+ function()
+ {
+ // Now we can apply the events to the input type=file
+ this.fire( 'formLoaded' ) ;
+ }, this ) ;
+
+ // Remove listeners attached to the content of the iframe (the file input)
+ unloadNumber = this.formUnloadNumber = CKEDITOR.tools.addFunction(
+ function()
+ {
+ this.getInputElement().clearCustomData();
+ }, this ) ;
+
+ this.getDialog()._.editor.on( 'destroy', function()
+ {
+ CKEDITOR.tools.removeFunction( callNumber );
+ CKEDITOR.tools.removeFunction( unloadNumber );
+ } );
+ }
+
+ function generateFormField()
+ {
+ frameDocument.$.open();
+
+ // Support for custom document.domain in IE.
+ if ( CKEDITOR.env.isCustomDomain() )
+ frameDocument.$.domain = document.domain;
+
+ var size = '';
+ if ( elementDefinition.size )
+ size = elementDefinition.size - ( CKEDITOR.env.ie ? 7 : 0 ); // "Browse" button is bigger in IE.
+
+ frameDocument.$.write( [ '<html dir="' + langDir + '" lang="' + langCode + '"><head><title></title></head><body style="margin: 0; overflow: hidden; background: transparent;">',
+ '<form enctype="multipart/form-data" method="POST" dir="' + langDir + '" lang="' + langCode + '" action="',
+ CKEDITOR.tools.htmlEncode( elementDefinition.action ),
+ '">',
+ '<input type="file" name="',
+ CKEDITOR.tools.htmlEncode( elementDefinition.id || 'cke_upload' ),
+ '" size="',
+ CKEDITOR.tools.htmlEncode( size > 0 ? size : "" ),
+ '" />',
+ '</form>',
+ '</body></html>',
+ '<script>window.parent.CKEDITOR.tools.callFunction(' + callNumber + ');',
+ 'window.onbeforeunload = function() {window.parent.CKEDITOR.tools.callFunction(' + unloadNumber + ')}</script>' ].join( '' ) );
+
+ frameDocument.$.close();
+
+ for ( var i = 0 ; i < buttons.length ; i++ )
+ buttons[i].enable();
+ }
+
+ // #3465: Wait for the browser to finish rendering the dialog first.
+ if ( CKEDITOR.env.gecko )
+ setTimeout( generateFormField, 500 );
+ else
+ generateFormField();
+ },
+
+ getValue : function()
+ {
+ return this.getInputElement().$.value || '';
+ },
+
+ /***
+ * The default value of input type="file" is an empty string, but during initialization
+ * of this UI element, the iframe still isn't ready so it can't be read from that object
+ * Setting it manually prevents later issues about the current value ("") being different
+ * of the initial value (undefined as it asked for .value of a div)
+ */
+ setInitValue : function()
+ {
+ this._.initValue = '';
+ },
+
+ /**
+ * Defines the onChange event for UI element definitions.
+ * @field
+ * @type Object
+ * @example
+ */
+ eventProcessors :
+ {
+ onChange : function( dialog, func )
+ {
+ // If this method is called several times (I'm not sure about how this can happen but the default
+ // onChange processor includes this protection)
+ // In order to reapply to the new element, the property is deleted at the beggining of the registerEvents method
+ if ( !this._.domOnChangeRegistered )
+ {
+ // By listening for the formLoaded event, this handler will get reapplied when a new
+ // form is created
+ this.on( 'formLoaded', function()
+ {
+ this.getInputElement().on( 'change', function(){ this.fire( 'change', { value : this.getValue() } ); }, this );
+ }, this );
+ this._.domOnChangeRegistered = true;
+ }
+
+ this.on( 'change', func );
+ }
+ },
+
+ keyboardFocusable : true
+ }, true );
+
+ CKEDITOR.ui.dialog.fileButton.prototype = new CKEDITOR.ui.dialog.button;
+
+ CKEDITOR.ui.dialog.fieldset.prototype = CKEDITOR.tools.clone( CKEDITOR.ui.dialog.hbox.prototype );
+
+ CKEDITOR.dialog.addUIElement( 'text', textBuilder );
+ CKEDITOR.dialog.addUIElement( 'password', textBuilder );
+ CKEDITOR.dialog.addUIElement( 'textarea', commonBuilder );
+ CKEDITOR.dialog.addUIElement( 'checkbox', commonBuilder );
+ CKEDITOR.dialog.addUIElement( 'radio', commonBuilder );
+ CKEDITOR.dialog.addUIElement( 'button', commonBuilder );
+ CKEDITOR.dialog.addUIElement( 'select', commonBuilder );
+ CKEDITOR.dialog.addUIElement( 'file', commonBuilder );
+ CKEDITOR.dialog.addUIElement( 'fileButton', commonBuilder );
+ CKEDITOR.dialog.addUIElement( 'html', commonBuilder );
+ CKEDITOR.dialog.addUIElement( 'fieldset', containerBuilder );
+})();
+
+/**
+ * Fired when the value of the uiElement is changed
+ * @name CKEDITOR.ui.dialog.uiElement#change
+ * @event
+ */
+
+/**
+ * Fired when the inner frame created by the element is ready.
+ * Each time the button is used or the dialog is loaded a new
+ * form might be created.
+ * @name CKEDITOR.ui.dialog.fileButton#formLoaded
+ * @event
+ */
diff --git a/_source/plugins/div/dialogs/div.js b/_source/plugins/div/dialogs/div.js index 55b1f70..37e1e97 100644 --- a/_source/plugins/div/dialogs/div.js +++ b/_source/plugins/div/dialogs/div.js @@ -1,529 +1,535 @@ -/* - * Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. - * For licensing, see LICENSE.html or http://ckeditor.com/license - */ - -(function() -{ - - /** - * Add to collection with DUP examination. - * @param {Object} collection - * @param {Object} element - * @param {Object} database - */ - function addSafely( collection, element, database ) - { - // 1. IE doesn't support customData on text nodes; - // 2. Text nodes never get chance to appear twice; - if ( !element.is || !element.getCustomData( 'block_processed' ) ) - { - element.is && CKEDITOR.dom.element.setMarker( database, element, 'block_processed', true ); - collection.push( element ); - } - } - - function getNonEmptyChildren( element ) - { - var retval = []; - var children = element.getChildren(); - for ( var i = 0 ; i < children.count() ; i++ ) - { - var child = children.getItem( i ); - if ( ! ( child.type === CKEDITOR.NODE_TEXT - && ( /^[ \t\n\r]+$/ ).test( child.getText() ) ) ) - retval.push( child ); - } - return retval; - } - - - /** - * Dialog reused by both 'creatediv' and 'editdiv' commands. - * @param {Object} editor - * @param {String} command The command name which indicate what the current command is. - */ - function divDialog( editor, command ) - { - // Definition of elements at which div operation should stopped. - var divLimitDefinition = ( function(){ - - // Customzie from specialize blockLimit elements - var definition = CKEDITOR.tools.extend( {}, CKEDITOR.dtd.$blockLimit ); - - // Exclude 'div' itself. - delete definition.div; - - // Exclude 'td' and 'th' when 'wrapping table' - if ( editor.config.div_wrapTable ) - { - delete definition.td; - delete definition.th; - } - return definition; - })(); - - // DTD of 'div' element - var dtd = CKEDITOR.dtd.div; - - /** - * Get the first div limit element on the element's path. - * @param {Object} element - */ - function getDivLimitElement( element ) - { - var pathElements = new CKEDITOR.dom.elementPath( element ).elements; - var divLimit; - for ( var i = 0; i < pathElements.length ; i++ ) - { - if ( pathElements[ i ].getName() in divLimitDefinition ) - { - divLimit = pathElements[ i ]; - break; - } - } - return divLimit; - } - - /** - * Init all fields' setup/commit function. - * @memberof divDialog - */ - function setupFields() - { - this.foreach( function( field ) - { - // Exclude layout container elements - if ( /^(?!vbox|hbox)/.test( field.type ) ) - { - if ( !field.setup ) - { - // Read the dialog fields values from the specified - // element attributes. - field.setup = function( element ) - { - field.setValue( element.getAttribute( field.id ) || '' ); - }; - } - if ( !field.commit ) - { - // Set element attributes assigned by the dialog - // fields. - field.commit = function( element ) - { - var fieldValue = this.getValue(); - // ignore default element attribute values - if ( 'dir' == field.id && element.getComputedStyle( 'direction' ) == fieldValue ) - return; - - if ( fieldValue ) - element.setAttribute( field.id, fieldValue ); - else - element.removeAttribute( field.id ); - }; - } - } - } ); - } - - /** - * Wrapping 'div' element around appropriate blocks among the selected ranges. - * @param {Object} editor - */ - function createDiv( editor ) - { - // new adding containers OR detected pre-existed containers. - var containers = []; - // node markers store. - var database = {}; - // All block level elements which contained by the ranges. - var containedBlocks = [], block; - - // Get all ranges from the selection. - var selection = editor.document.getSelection(); - var 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() ) ) - styles[ styleName ].applyToObject( element ); - } - }, - { - 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 value = this.getValue(), - merged = [ value, element.getAttribute( 'style' ) ].join( ';' ); - value && 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() - { - editor.fire( 'saveSnapshot' ); - 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' ); - } - editor.fire( 'saveSnapshot' ); - - this.hide(); - }, - onHide : function() - { - 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; - */ +/*
+ * Copyright (c) 2003-2012, 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;
+ */
diff --git a/_source/plugins/div/plugin.js b/_source/plugins/div/plugin.js index 0714dee..4cddea7 100644 --- a/_source/plugins/div/plugin.js +++ b/_source/plugins/div/plugin.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
@@ -37,10 +37,10 @@ For licensing, see LICENSE.html or http://ckeditor.com/license blockLimit = path.blockLimit,
div = blockLimit.is( 'div' ) && blockLimit;
- if ( div && !div.getAttribute( '_cke_div_added' ) )
+ if ( div && !div.data( 'cke-div-added' ) )
{
toRemove.push( div );
- div.setAttribute( '_cke_div_added' );
+ div.data( 'cke-div-added' );
}
}
@@ -95,7 +95,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license {
editor.contextMenu.addListener( function( element, selection )
{
- if ( !element )
+ if ( !element || element.isReadOnly() )
return null;
var elementPath = new CKEDITOR.dom.elementPath( element ),
diff --git a/_source/plugins/docprops/dialogs/docprops.js b/_source/plugins/docprops/dialogs/docprops.js new file mode 100644 index 0000000..d2012ab --- /dev/null +++ b/_source/plugins/docprops/dialogs/docprops.js @@ -0,0 +1,674 @@ +/*
+Copyright (c) 2003-2012, 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%' );
+ }
+ }
+ ]
+ }
+ ]
+ };
+});
diff --git a/_source/plugins/docprops/plugin.js b/_source/plugins/docprops/plugin.js new file mode 100644 index 0000000..b9e0315 --- /dev/null +++ b/_source/plugins/docprops/plugin.js @@ -0,0 +1,22 @@ +/*
+Copyright (c) 2003-2012, 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'
+ });
+ }
+});
diff --git a/_source/plugins/domiterator/plugin.js b/_source/plugins/domiterator/plugin.js index b12e345..f68f7c0 100644 --- a/_source/plugins/domiterator/plugin.js +++ b/_source/plugins/domiterator/plugin.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
@@ -20,17 +20,29 @@ CKEDITOR.plugins.add( 'domiterator' ); return;
this.range = range;
- this.forceBrBreak = false;
+ this.forceBrBreak = 0;
// Whether include <br>s into the enlarged range.(#3730).
- this.enlargeBr = true;
- this.enforceRealBlocks = false;
+ this.enlargeBr = 1;
+ this.enforceRealBlocks = 0;
this._ || ( this._ = {} );
}
var beginWhitespaceRegex = /^[\r\n\t ]+$/,
- isBookmark = CKEDITOR.dom.walker.bookmark();
+ // 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 )
@@ -44,54 +56,69 @@ CKEDITOR.plugins.add( 'domiterator' ); // 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 )
+ if ( !this._.started )
{
range = this.range.clone();
- range.enlarge( this.forceBrBreak || !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 );
+ // 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 );
- // 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() )
+ if ( !range.collapsed )
{
- var testRange = new CKEDITOR.dom.range( range.document );
- testRange.moveToPosition( this._.lastNode, CKEDITOR.POSITION_AFTER_END );
- if ( testRange.checkEndOfBlock() )
+ var walker = new CKEDITOR.dom.walker( range.clone() ),
+ 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.clone() );
+ 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 path = new CKEDITOR.dom.elementPath( testRange.endContainer );
- var lastBlock = path.block || path.blockLimit;
- this._.lastNode = lastBlock.getNextSourceNode( true );
+ 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 );
+ // 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;
}
- // Let's reuse this variable.
- range = null;
+ this._.started = 1;
}
var currentNode = this._.nextNode;
@@ -102,12 +129,13 @@ CKEDITOR.plugins.add( 'domiterator' ); {
// closeRange indicates that a paragraph boundary has been found,
// so the range can be closed.
- var closeRange = false;
+ 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 = false;
+ continueFromSibling = 0;
// If it is an element node, let's check if it can be part of the
// range.
@@ -115,12 +143,13 @@ CKEDITOR.plugins.add( 'domiterator' ); {
var nodeName = currentNode.getName();
- if ( currentNode.isBlockBoundary( this.forceBrBreak && { br : 1 } ) )
+ 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 = true;
+ includeNode = 1;
else if ( !range && !currentNode.getChildCount() && nodeName != 'hr' )
{
// If we have found an empty block, and haven't started
@@ -142,7 +171,7 @@ CKEDITOR.plugins.add( 'domiterator' ); this._.nextNode = currentNode;
}
- closeRange = true;
+ closeRange = 1;
}
else
{
@@ -159,7 +188,7 @@ CKEDITOR.plugins.add( 'domiterator' ); currentNode = currentNode.getFirst();
continue;
}
- includeNode = true;
+ includeNode = 1;
}
}
else if ( currentNode.type == CKEDITOR.NODE_TEXT )
@@ -167,7 +196,7 @@ CKEDITOR.plugins.add( 'domiterator' ); // Ignore normal whitespaces (i.e. not including or
// other unicode whitespaces) before/after a block node.
if ( beginWhitespaceRegex.test( currentNode.getText() ) )
- includeNode = false;
+ includeNode = 0;
}
// The current node is good to be part of the range and we are
@@ -185,21 +214,25 @@ CKEDITOR.plugins.add( 'domiterator' ); // to close the range, otherwise we include the parent within it.
if ( range && !closeRange )
{
- while ( !currentNode.getNext() && !isLast )
+ while ( !currentNode.getNext( skipGuard ) && !isLast )
{
var parentNode = currentNode.getParent();
- if ( parentNode.isBlockBoundary( this.forceBrBreak && { br : 1 } ) )
+ if ( parentNode.isBlockBoundary( this.forceBrBreak
+ && !parentPre && { br : 1 } ) )
{
- closeRange = true;
+ 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 = true;
+ includeNode = 1;
isLast = ( currentNode.equals( lastNode ) );
- continueFromSibling = true;
+ continueFromSibling = 1;
}
}
@@ -207,31 +240,13 @@ CKEDITOR.plugins.add( 'domiterator' ); if ( includeNode )
range.setEndAt( currentNode, CKEDITOR.POSITION_AFTER_END );
- currentNode = currentNode.getNextSourceNode( continueFromSibling, null, lastNode );
+ 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 ( ( closeRange || isLast ) && range )
- {
- var boundaryNodes = range.getBoundaryNodes(),
- startPath = new CKEDITOR.dom.elementPath( range.startContainer );
-
- // Drop the range if it only contains bookmark nodes, and is
- // not because of the original collapsed range. (#4087,#4450)
- if ( boundaryNodes.startNode.getParent().equals( startPath.blockLimit )
- && isBookmark( boundaryNodes.startNode ) && isBookmark( boundaryNodes.endNode ) )
- {
- range = null;
- this._.nextNode = null;
- }
- else
+ if ( isLast || ( closeRange && range ) )
break;
- }
-
- if ( isLast )
- break;
-
}
// Now, based on the processed range, look for (or create) the block to be returned.
@@ -245,7 +260,7 @@ CKEDITOR.plugins.add( 'domiterator' ); return null;
}
- startPath = new CKEDITOR.dom.elementPath( range.startContainer );
+ var startPath = new CKEDITOR.dom.elementPath( range.startContainer );
var startBlockLimit = startPath.blockLimit,
checkLimits = { div : 1, th : 1, td : 1 };
block = startPath.block;
@@ -261,14 +276,14 @@ CKEDITOR.plugins.add( 'domiterator' ); // 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();
+ // 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 );
+ // Insert the fixed block into the DOM.
+ range.insertNode( block );
- removePreviousBr = removeLastBr = true;
+ removePreviousBr = removeLastBr = true;
}
else if ( block.getName() != 'li' )
{
@@ -302,8 +317,7 @@ CKEDITOR.plugins.add( 'domiterator' ); // the current range, which could be an <li> child (nested
// lists) or the next sibling <li>.
- this._.nextNode = ( block.equals( lastNode ) ? null :
- range.getBoundaryNodes().endNode.getNextSourceNode( true, null, lastNode ) );
+ this._.nextNode = ( block.equals( lastNode ) ? null : getNextSourceNode( range.getBoundaryNodes().endNode, 1, lastNode ) );
}
}
@@ -321,9 +335,6 @@ CKEDITOR.plugins.add( 'domiterator' ); if ( removeLastBr )
{
- // Ignore bookmark nodes.(#3783)
- var bookmarkGuard = CKEDITOR.dom.walker.bookmark( false, true );
-
var lastChild = block.getLast();
if ( lastChild && lastChild.type == CKEDITOR.NODE_ELEMENT && lastChild.getName() == 'br' )
{
@@ -340,8 +351,8 @@ CKEDITOR.plugins.add( 'domiterator' ); // next interation.
if ( !this._.nextNode )
{
- this._.nextNode = ( isLast || block.equals( lastNode ) ) ? null :
- block.getNextSourceNode( true, null, lastNode );
+ this._.nextNode = ( isLast || block.equals( lastNode ) || !lastNode ) ? null :
+ getNextSourceNode( block, 1, lastNode );
}
return block;
diff --git a/_source/plugins/editingblock/plugin.js b/_source/plugins/editingblock/plugin.js index 30e929d..abe0b71 100644 --- a/_source/plugins/editingblock/plugin.js +++ b/_source/plugins/editingblock/plugin.js @@ -1,230 +1,278 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -/** - * @fileOverview The default editing block plugin, which holds the editing area - * and source view. - */ - -(function() -{ - var getMode = function( editor, mode ) - { - return editor._.modes && editor._.modes[ mode || editor.mode ]; - }; - - // This is a semaphore used to avoid recursive calls between - // the following data handling functions. - var isHandlingData; - - CKEDITOR.plugins.add( 'editingblock', - { - init : function( editor ) - { - if ( !editor.config.editingBlock ) - return; - - editor.on( 'themeSpace', function( event ) - { - if ( event.data.space == 'contents' ) - event.data.html += '<br>'; - }); - - editor.on( 'themeLoaded', function() - { - editor.fireOnce( 'editingBlockReady' ); - }); - - editor.on( 'uiReady', function() - { - editor.setMode( editor.config.startupMode ); - }); - - editor.on( 'afterSetData', function() - { - if ( !isHandlingData ) - { - function setData() - { - isHandlingData = true; - getMode( editor ).loadData( editor.getData() ); - isHandlingData = false; - } - - if ( editor.mode ) - setData(); - else - { - editor.on( 'mode', function() - { - setData(); - editor.removeListener( 'mode', arguments.callee ); - }); - } - } - }); - - editor.on( 'beforeGetData', function() - { - if ( !isHandlingData && editor.mode ) - { - isHandlingData = true; - editor.setData( getMode( editor ).getData() ); - isHandlingData = false; - } - }); - - editor.on( 'getSnapshot', function( event ) - { - if ( editor.mode ) - event.data = getMode( editor ).getSnapshotData(); - }); - - editor.on( 'loadSnapshot', function( event ) - { - if ( editor.mode ) - getMode( editor ).loadSnapshotData( event.data ); - }); - - // For the first "mode" call, we'll also fire the "instanceReady" - // event. - editor.on( 'mode', function( event ) - { - // Do that once only. - event.removeListener(); - - // Redirect the focus into editor for webkit. (#5713) - CKEDITOR.env.webkit && editor.container.on( 'focus', function() - { - editor.focus(); - }); - - if ( editor.config.startupFocus ) - editor.focus(); - - // Fire instanceReady for both the editor and CKEDITOR, but - // defer this until the whole execution has completed - // to guarantee the editor is fully responsible. - setTimeout( function(){ - editor.fireOnce( 'instanceReady' ); - CKEDITOR.fire( 'instanceReady', null, editor ); - } ); - }); - } - }); - - /** - * The current editing mode. An editing mode is basically a viewport for - * editing or content viewing. By default the possible values for this - * property are "wysiwyg" and "source". - * @type String - * @example - * alert( CKEDITOR.instances.editor1.mode ); // "wysiwyg" (e.g.) - */ - CKEDITOR.editor.prototype.mode = ''; - - /** - * Registers an editing mode. This function is to be used mainly by plugins. - * @param {String} mode The mode name. - * @param {Object} modeEditor The mode editor definition. - * @example - */ - CKEDITOR.editor.prototype.addMode = function( mode, modeEditor ) - { - modeEditor.name = mode; - ( this._.modes || ( this._.modes = {} ) )[ mode ] = modeEditor; - }; - - /** - * Sets the current editing mode in this editor instance. - * @param {String} mode A registered mode name. - * @example - * // Switch to "source" view. - * CKEDITOR.instances.editor1.setMode( 'source' ); - */ - CKEDITOR.editor.prototype.setMode = function( mode ) - { - var data, - holderElement = this.getThemeSpace( 'contents' ), - isDirty = this.checkDirty(); - - // Unload the previous mode. - if ( this.mode ) - { - if ( mode == this.mode ) - return; - - this.fire( 'beforeModeUnload' ); - - var currentMode = getMode( this ); - data = currentMode.getData(); - currentMode.unload( holderElement ); - this.mode = ''; - } - - holderElement.setHtml( '' ); - - // Load required mode. - var modeEditor = getMode( this, mode ); - if ( !modeEditor ) - throw '[CKEDITOR.editor.setMode] Unknown mode "' + mode + '".'; - - if ( !isDirty ) - { - this.on( 'mode', function() - { - this.resetDirty(); - this.removeListener( 'mode', arguments.callee ); - }); - } - - modeEditor.load( holderElement, ( typeof data ) != 'string' ? this.getData() : data); - }; - - /** - * Moves the selection focus to the editing are space in the editor. - */ - CKEDITOR.editor.prototype.focus = function() - { - var mode = getMode( this ); - if ( mode ) - mode.focus(); - }; -})(); - -/** - * The mode to load at the editor startup. It depends on the plugins - * loaded. By default, the "wysiwyg" and "source" modes are available. - * @type String - * @default 'wysiwyg' - * @example - * config.startupMode = 'source'; - */ -CKEDITOR.config.startupMode = 'wysiwyg'; - -/** - * Sets whether the editor should have the focus when the page loads. - * @type Boolean - * @default false - * @example - * config.startupFocus = true; - */ -CKEDITOR.config.startupFocus = false; - -/** - * 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. - */ +/*
+Copyright (c) 2003-2012, 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.
+ */
diff --git a/_source/plugins/elementspath/plugin.js b/_source/plugins/elementspath/plugin.js index 095c35c..1fb5435 100644 --- a/_source/plugins/elementspath/plugin.js +++ b/_source/plugins/elementspath/plugin.js @@ -1,204 +1,218 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -/** - * @fileOverview The "elementspath" plugin. It shows all elements in the DOM - * parent tree relative to the current selection in the editing area. - */ - -(function() -{ - var commands = - { - toolbarFocus : - { - exec : function( editor ) - { - var idBase = editor._.elementsPath.idBase; - var element = CKEDITOR.document.getById( idBase + '0' ); - - if ( element ) - element.focus(); - } - } - }; - - var emptyHtml = '<span class="cke_empty"> </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>'; - } - }); - - editor.on( 'selectionChange', function( ev ) - { - var env = CKEDITOR.env, - selection = ev.data.selection, - element = selection.getStartElement(), - html = [], - editor = ev.editor, - elementsList = editor._.elementsPath.list = [], - filters = editor._.elementsPath.filters; - - while ( element ) - { - var ignore = 0; - for ( var i = 0; i < filters.length; i++ ) - { - if ( filters[ i ]( element ) === false ) - { - ignore = 1; - break; - } - } - - if ( !ignore ) - { - var index = elementsList.push( element ) - 1; - var name; - if ( element.getAttribute( '_cke_real_element_type' ) ) - name = element.getAttribute( '_cke_real_element_type' ); - else - name = element.getName(); - - // Use this variable to add conditional stuff to the - // HTML (because we are doing it in reverse order... unshift). - var extra = ''; - - // Some browsers don't cancel key events in the keydown but in the - // keypress. - // TODO: Check if really needed for Gecko+Mac. - if ( env.opera || ( env.gecko && env.mac ) ) - extra += ' onkeypress="return false;"'; - - // With Firefox, we need to force the button to redraw, otherwise it - // will remain in the focus state. - if ( env.gecko ) - extra += ' onblur="this.style.cssText = this.style.cssText;"'; - - var label = editor.lang.elementsPath.eleTitle.replace( /%1/, name ); - html.unshift( - '<a' + - ' id="', idBase, index, '"' + - ' href="javascript:void(\'', name, '\')"' + - ' tabindex="-1"' + - ' title="', label, '"' + - ( ( CKEDITOR.env.gecko && CKEDITOR.env.version < 10900 ) ? - ' onfocus="event.preventBubble();"' : '' ) + - ' hidefocus="true" ' + - ' onkeydown="return CKEDITOR._.elementsPath.keydown(\'', editor.name, '\',', index, ', event);"' + - extra , - ' onclick="return CKEDITOR._.elementsPath.click(\'', editor.name, '\',', index, ');"', - ' 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(); - } - - getSpaceElement().setHtml( html.join('') + emptyHtml ); - }); - - editor.on( 'contentDomUnload', function() - { - getSpaceElement().setHtml( emptyHtml ); - }); - - editor.addCommand( 'elementsPathFocus', commands.toolbarFocus ); - } - }); -})(); - -/** - * Handles the click on an element in the element path. - * @private - */ -CKEDITOR._.elementsPath = -{ - click : function( instanceName, elementIndex ) - { - var editor = CKEDITOR.instances[ instanceName ]; - editor.focus(); - - var element = editor._.elementsPath.list[ elementIndex ]; - editor.getSelection().selectElement( element ); - - return false; - }, - - keydown : function( instanceName, elementIndex, ev ) - { - var instance = CKEDITOR.ui.button._.instances[ elementIndex ]; - var editor = CKEDITOR.instances[ instanceName ]; - var idBase = editor._.elementsPath.idBase; - - var 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 - this.click( instanceName, elementIndex ); - return false; - - //default : - // alert( ev.getKeystroke() ); - } - return true; - } -}; +/*
+Copyright (c) 2003-2012, 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"> </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
+ */
diff --git a/_source/plugins/enterkey/plugin.js b/_source/plugins/enterkey/plugin.js index bb57c61..dac6fb2 100644 --- a/_source/plugins/enterkey/plugin.js +++ b/_source/plugins/enterkey/plugin.js @@ -1,353 +1,433 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -(function() -{ - CKEDITOR.plugins.add( 'enterkey', - { - requires : [ 'keystrokes', 'indent' ], - - init : function( editor ) - { - var specialKeys = editor.specialKeys; - specialKeys[ 13 ] = enter; - specialKeys[ CKEDITOR.SHIFT + 13 ] = shiftEnter; - } - }); - - CKEDITOR.plugins.enterkey = - { - enterBlock : function( editor, mode, range, forceMode ) - { - // Get the range for the current selection. - range = range || getRange( editor ); - - var doc = range.document; - - // Exit the list when we're inside an empty list item block. (#5376) - if ( range.checkStartOfBlock() && range.checkEndOfBlock() ) - { - var path = new CKEDITOR.dom.elementPath( range.startContainer ), - block = path.block; - - if ( block && ( block.is( 'li' ) || block.getParent().is( 'li' ) ) ) - { - editor.execCommand( 'outdent' ); - return; - } - } - - // Determine the block element to be used. - var blockTag = ( mode == CKEDITOR.ENTER_DIV ? 'div' : 'p' ); - - // Split the range. - var splitInfo = range.splitBlock( blockTag ); - - if ( !splitInfo ) - return; - - // Get the current blocks. - var previousBlock = splitInfo.previousBlock, - nextBlock = splitInfo.nextBlock; - - var isStartOfBlock = splitInfo.wasStartOfBlock, - isEndOfBlock = splitInfo.wasEndOfBlock; - - var node; - - // If this is a block under a list item, split it as well. (#1647) - if ( nextBlock ) - { - node = nextBlock.getParent(); - if ( node.is( 'li' ) ) - { - nextBlock.breakParent( node ); - nextBlock.move( nextBlock.getNext(), true ); - } - } - else if ( previousBlock && ( node = previousBlock.getParent() ) && node.is( 'li' ) ) - { - previousBlock.breakParent( node ); - range.moveToElementEditStart( previousBlock.getNext() ); - previousBlock.move( previousBlock.getPrevious() ); - } - - // If we have both the previous and next blocks, it means that the - // boundaries were on separated blocks, or none of them where on the - // block limits (start/end). - if ( !isStartOfBlock && !isEndOfBlock ) - { - // If the next block is an <li> with another list tree as the first - // child, we'll need to append a filler (<br>/NBSP) or the list item - // wouldn't be editable. (#1420) - if ( nextBlock.is( 'li' ) - && ( node = nextBlock.getFirst( CKEDITOR.dom.walker.invisible( true ) ) ) - && node.is && node.is( 'ul', 'ol' ) ) - ( CKEDITOR.env.ie ? doc.createText( '\xa0' ) : doc.createElement( 'br' ) ).insertBefore( node ); - - // Move the selection to the end block. - if ( nextBlock ) - range.moveToElementEditStart( nextBlock ); - } - else - { - var newBlock; - - if ( previousBlock ) - { - // Do not enter this block if it's a header tag, or we are in - // a Shift+Enter (#77). Create a new block element instead - // (later in the code). - if ( previousBlock.is( 'li' ) || !headerTagRegex.test( previousBlock.getName() ) ) - { - // Otherwise, duplicate the previous block. - newBlock = previousBlock.clone(); - } - } - else if ( nextBlock ) - newBlock = nextBlock.clone(); - - if ( !newBlock ) - newBlock = doc.createElement( blockTag ); - // Force the enter block unless we're talking of a list item. - else if ( forceMode && !newBlock.is( 'li' ) ) - newBlock.renameNode( blockTag ); - - // Recreate the inline elements tree, which was available - // before hitting enter, so the same styles will be available in - // the new block. - var elementPath = splitInfo.elementPath; - if ( elementPath ) - { - for ( var i = 0, len = elementPath.elements.length ; i < len ; i++ ) - { - var element = elementPath.elements[ i ]; - - if ( element.equals( elementPath.block ) || element.equals( elementPath.blockLimit ) ) - break; - - if ( CKEDITOR.dtd.$removeEmpty[ element.getName() ] ) - { - element = element.clone(); - newBlock.moveChildren( element ); - newBlock.append( element ); - } - } - } - - if ( !CKEDITOR.env.ie ) - newBlock.appendBogus(); - - range.insertNode( newBlock ); - - // This is tricky, but to make the new block visible correctly - // we must select it. - // The previousBlock check has been included because it may be - // empty if we have fixed a block-less space (like ENTER into an - // empty table cell). - if ( CKEDITOR.env.ie && isStartOfBlock && ( !isEndOfBlock || !previousBlock.getChildCount() ) ) - { - // Move the selection to the new block. - range.moveToElementEditStart( isEndOfBlock ? previousBlock : newBlock ); - range.select(); - } - - // Move the selection to the new block. - range.moveToElementEditStart( isStartOfBlock && !isEndOfBlock ? nextBlock : newBlock ); - } - - if ( !CKEDITOR.env.ie ) - { - if ( nextBlock ) - { - // If we have split the block, adds a temporary span at the - // range position and scroll relatively to it. - var tmpNode = doc.createElement( 'span' ); - - // We need some content for Safari. - tmpNode.setHtml( ' ' ); - - 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 ); - - 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 ) ) - { - // Insert a <br> after the current paragraph. - doc.createElement( 'br' ).insertAfter( startBlock ); - - // A text node is required by Gecko only to make the cursor blink. - if ( CKEDITOR.env.gecko ) - doc.createText( '' ).insertAfter( startBlock ); - - // IE has different behaviors regarding position. - range.setStartAt( startBlock.getNext(), CKEDITOR.env.ie ? CKEDITOR.POSITION_BEFORE_START : CKEDITOR.POSITION_AFTER_START ); - } - else - { - var lineBreak; - - isPre = ( startBlockTag == 'pre' ); - - // Gecko prefers <br> as line-break inside <pre> (#4711). - if ( isPre && !CKEDITOR.env.gecko ) - lineBreak = doc.createText( CKEDITOR.env.ie ? '\r' : '\n' ); - else - lineBreak = doc.createElement( 'br' ); - - range.deleteContents(); - range.insertNode( lineBreak ); - - // A text node is required by Gecko only to make the cursor blink. - // We need some text inside of it, so the bogus <br> is properly - // created. - if ( !CKEDITOR.env.ie ) - doc.createText( '\ufeff' ).insertAfter( lineBreak ); - - // If we are at the end of a block, we must be sure the bogus node is available in that block. - if ( isEndOfBlock && !CKEDITOR.env.ie ) - lineBreak.getParent().appendBogus(); - - // Now we can remove the text node contents, so the caret doesn't - // stop on it. - if ( !CKEDITOR.env.ie ) - lineBreak.getNext().$.nodeValue = ''; - // IE has different behavior regarding position. - if ( CKEDITOR.env.ie ) - range.setStartAt( lineBreak, CKEDITOR.POSITION_AFTER_END ); - else - range.setStartAt( lineBreak.getNext(), CKEDITOR.POSITION_AFTER_START ); - - // Scroll into view, for non IE. - if ( !CKEDITOR.env.ie ) - { - var dummy = null; - - // BR is not positioned in Opera and Webkit. - if ( !CKEDITOR.env.gecko ) - { - dummy = doc.createElement( 'span' ); - // We need have some contents for Webkit to position it - // under parent node. ( #3681) - dummy.setHtml(' '); - } - else - dummy = doc.createElement( 'br' ); - - dummy.insertBefore( lineBreak.getNext() ); - dummy.scrollIntoView(); - dummy.remove(); - } - } - - // This collapse guarantees the cursor will be blinking. - range.collapse( true ); - - range.select( isPre ); - } - }; - - var plugin = CKEDITOR.plugins.enterkey, - enterBr = plugin.enterBr, - enterBlock = plugin.enterBlock, - headerTagRegex = /^h[1-6]$/; - - function shiftEnter( editor ) - { - // Only effective within document. - if ( editor.mode != 'wysiwyg' ) - return false; - - // On SHIFT+ENTER: - // 1. We want to enforce the mode to be respected, instead - // of cloning the current block. (#77) - // 2. Always perform a block break when inside <pre> (#5402). - if ( editor.getSelection().getStartElement().hasAscendant( 'pre', true ) ) - { - setTimeout( function() { enterBlock( editor, editor.config.enterMode, null, true ); }, 0 ); - return true; - } - else - return enter( editor, editor.config.shiftEnterMode, true ); - } - - function enter( editor, mode, forceMode ) - { - forceMode = editor.config.forceEnterMode || forceMode; - - // Only effective within document. - if ( editor.mode != 'wysiwyg' ) - return false; - - if ( !mode ) - mode = editor.config.enterMode; - - // Use setTimout so the keys get cancelled immediatelly. - setTimeout( function() - { - editor.fire( 'saveSnapshot' ); // Save undo step. - if ( mode == CKEDITOR.ENTER_BR || editor.getSelection().getStartElement().hasAscendant( 'pre', true ) ) - enterBr( editor, mode, null, forceMode ); - else - enterBlock( editor, mode, null, forceMode ); - - }, 0 ); - - return true; - } - - - function getRange( editor ) - { - // Get the selection ranges. - var ranges = editor.getSelection().getRanges(); - - // 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 ]; - } -})(); +/*
+Copyright (c) 2003-2012, 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( ' ' );
+
+ 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(' ');
+ }
+ 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 ];
+ }
+})();
diff --git a/_source/plugins/entities/plugin.js b/_source/plugins/entities/plugin.js index 6254dcd..5729831 100644 --- a/_source/plugins/entities/plugin.js +++ b/_source/plugins/entities/plugin.js @@ -1,17 +1,16 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
(function()
{
- var entities =
-
- // Base HTML entities.
- 'nbsp,gt,lt,quot,' +
+ // Base HTML entities.
+ var htmlbase = 'nbsp,gt,lt,amp';
+ var entities =
// Latin-1 Entities
- 'iexcl,cent,pound,curren,yen,brvbar,sect,uml,copy,ordf,laquo,' +
+ '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,' +
@@ -46,7 +45,11 @@ For licensing, see LICENSE.html or http://ckeditor.com/license 'omicron,pi,rho,sigmaf,sigma,tau,upsilon,phi,chi,psi,omega,thetasym,' +
'upsih,piv';
- function buildTable( entities )
+ /**
+ * 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 = [];
@@ -58,36 +61,45 @@ For licensing, see LICENSE.html or http://ckeditor.com/license nbsp : '\u00A0', // IE | FF
shy : '\u00AD', // IE
gt : '\u003E', // IE | FF | -- | Opera
- lt : '\u003C' // IE | FF | Safari | Opera
+ lt : '\u003C', // IE | FF | Safari | Opera
+ amp : '\u0026', // ALL
+ apos : '\u0027', // IE
+ quot : '\u0022' // IE
};
- entities = entities.replace( /\b(nbsp|shy|gt|lt|amp)(?:,|$)/g, function( match, entity )
+ entities = entities.replace( /\b(nbsp|shy|gt|lt|amp|apos|quot)(?:,|$)/g, function( match, entity )
{
- table[ specialTable[ entity ] ] = '&' + entity + ';';
- regex.push( specialTable[ entity ] );
+ var org = reverse ? '&' + entity + ';' : specialTable[ entity ],
+ result = reverse ? specialTable[ entity ] : '&' + entity + ';';
+
+ table[ org ] = result;
+ regex.push( org );
return '';
});
- // Transforms the entities string into an array.
- entities = entities.split( ',' );
+ 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;
+ // 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 );
+ // 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( '' );
+ table.regex = regex.join( reverse ? '|' : '' );
return table;
}
@@ -98,46 +110,66 @@ For licensing, see LICENSE.html or http://ckeditor.com/license {
var config = editor.config;
- if ( !config.entities )
- return;
-
var dataProcessor = editor.dataProcessor,
htmlFilter = dataProcessor && dataProcessor.htmlFilter;
if ( htmlFilter )
{
- var selectedEntities = entities;
+ // Mandatory HTML base entities.
+ var selectedEntities = [];
- if ( config.entities_latin )
- selectedEntities += ',' + latin;
+ if ( config.basicEntities !== false )
+ selectedEntities.push( htmlbase );
- if ( config.entities_greek )
- selectedEntities += ',' + greek;
+ if ( config.entities )
+ {
+ if ( selectedEntities.length )
+ selectedEntities.push( entities );
+
+ if ( config.entities_latin )
+ selectedEntities.push( latin );
- if ( config.entities_additional )
- selectedEntities += ',' + config.entities_additional;
+ if ( config.entities_greek )
+ selectedEntities.push( greek );
- var entitiesTable = buildTable( selectedEntities );
+ if ( config.entities_additional )
+ selectedEntities.push( config.entities_additional );
+ }
- // Create the Regex used to find entities in the text.
- var entitiesRegex = '[' + entitiesTable.regex + ']';
+ var entitiesTable = buildTable( selectedEntities.join( ',' ) );
+
+ // 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_processNumerical )
+ 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 entitiesTable[ character ] || ( '&#' + character.charCodeAt(0) + ';' );
+ return baseEntitiesTable[ character ];
}
htmlFilter.addRules(
{
text : function( text )
{
- return text.replace( entitiesRegex, getChar );
+ return text.replace( baseEntitiesRegex, getChar )
+ .replace( entitiesRegex, getEntity );
}
});
}
@@ -146,9 +178,26 @@ For licensing, see LICENSE.html or http://ckeditor.com/license })();
/**
+ * 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 true
+ * @default <code>true</code>
* @example
* config.entities = false;
*/
@@ -156,10 +205,11 @@ CKEDITOR.config.entities = true; /**
* Whether to convert some Latin characters (Latin alphabet No. 1, ISO 8859-1)
- * to HTML entities. The list of entities can be found at the
+ * 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 true
+ * @default <code>true</code>
* @example
* config.entities_latin = false;
*/
@@ -168,33 +218,37 @@ CKEDITOR.config.entities_latin = true; /**
* Whether to convert some symbols, mathematical symbols, and Greek letters to
* HTML entities. This may be more relevant for users typing text written in Greek.
- * The list of entities can be found at the
+ * 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 true
+ * @default <code>true</code>
* @example
* config.entities_greek = false;
*/
CKEDITOR.config.entities_greek = true;
/**
- * Whether to convert all remaining characters, not comprised in the ASCII
- * character table, to their relative numeric representation of HTML entity.
- * For example, the phrase "This is Chinese: 汉语." is outputted
+ * 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: 汉语." is output
* as "This is Chinese: &#27721;&#35821;."
- * @type Boolean
- * @default false
+ * @name CKEDITOR.config.entities_processNumerical
+ * @type Boolean|String
+ * @default <code>false</code>
* @example
* config.entities_processNumerical = true;
+ * config.entities_processNumerical = 'force'; //Converts from " " into " ";
*/
-CKEDITOR.config.entities_processNumerical = false;
/**
- * An additional list of entities to be used. It's a string containing each
- * entry separated by a comma. Entities names or number must be used, exclusing
- * the "&" preffix and the ";" termination.
- * @default '#39' // The single quote (') character.
+ * A comma separated list of additional entities to be used. Entity names
+ * or numbers must be used in a form that excludes the "&" 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 (Й).
*/
CKEDITOR.config.entities_additional = '#39';
diff --git a/_source/plugins/fakeobjects/plugin.js b/_source/plugins/fakeobjects/plugin.js index bb21062..f55922a 100644 --- a/_source/plugins/fakeobjects/plugin.js +++ b/_source/plugins/fakeobjects/plugin.js @@ -1,122 +1,175 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -(function() -{ - var htmlFilterRules = - { - elements : - { - $ : function( element ) - { - var attributes = element.attributes, - realHtml = attributes && attributes._cke_realelement, - realFragment = realHtml && new CKEDITOR.htmlParser.fragment.fromHtml( decodeURIComponent( realHtml ) ), - realElement = realFragment && realFragment.children[ 0 ]; - - // If we have width/height in the element, we must move it into - // the real element. - if ( realElement && element.attributes._cke_resizable ) - { - var style = element.attributes.style; - - if ( style ) - { - // Get the width from the style. - var match = /(?:^|\s)width\s*:\s*(\d+)/i.exec( style ), - width = match && match[1]; - - // Get the height from the style. - match = /(?:^|\s)height\s*:\s*(\d+)/i.exec( style ); - var height = match && match[1]; - - if ( width ) - realElement.attributes.width = width; - - if ( height ) - realElement.attributes.height = height; - } - } - - return realElement; - } - } - }; - - CKEDITOR.plugins.add( 'fakeobjects', - { - requires : [ 'htmlwriter' ], - - afterInit : function( editor ) - { - var dataProcessor = editor.dataProcessor, - htmlFilter = dataProcessor && dataProcessor.htmlFilter; - - if ( htmlFilter ) - htmlFilter.addRules( htmlFilterRules ); - } - }); -})(); - -CKEDITOR.editor.prototype.createFakeElement = function( realElement, className, realElementType, isResizable ) -{ - var lang = this.lang.fakeobjects; - - var attributes = - { - 'class' : className, - src : CKEDITOR.getUrl( 'images/spacer.gif' ), - _cke_realelement : encodeURIComponent( realElement.getOuterHtml() ), - _cke_real_node_type : realElement.type, - alt : lang[ realElementType ] || lang.unknown, - align : realElement.getAttribute( 'align' ) || '' - }; - - if ( realElementType ) - attributes._cke_real_element_type = realElementType; - - if ( isResizable ) - attributes._cke_resizable = isResizable; - - return this.document.createElement( 'img', { attributes : attributes } ); -}; - -CKEDITOR.editor.prototype.createFakeParserElement = function( realElement, className, realElementType, isResizable ) -{ - var lang = this.lang.fakeobjects, - html; - - var writer = new CKEDITOR.htmlParser.basicWriter(); - realElement.writeHtml( writer ); - html = writer.getHtml(); - - var attributes = - { - 'class' : className, - src : CKEDITOR.getUrl( 'images/spacer.gif' ), - _cke_realelement : encodeURIComponent( html ), - _cke_real_node_type : realElement.type, - alt : lang[ realElementType ] || lang.unknown, - align : realElement.attributes.align || '' - }; - - if ( realElementType ) - attributes._cke_real_element_type = realElementType; - - if ( isResizable ) - attributes._cke_resizable = isResizable; - - return new CKEDITOR.htmlParser.element( 'img', attributes ); -}; - -CKEDITOR.editor.prototype.restoreRealElement = function( fakeElement ) -{ - if ( fakeElement.getAttribute( '_cke_real_node_type' ) != CKEDITOR.NODE_ELEMENT ) - return null; - - return CKEDITOR.dom.element.createFromHtml( - decodeURIComponent( fakeElement.getAttribute( '_cke_realelement' ) ), - this.document ); -}; +/*
+Copyright (c) 2003-2012, 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;
+ };
+
+})();
diff --git a/_source/plugins/filebrowser/plugin.js b/_source/plugins/filebrowser/plugin.js index 1651a21..d354327 100644 --- a/_source/plugins/filebrowser/plugin.js +++ b/_source/plugins/filebrowser/plugin.js @@ -1,479 +1,533 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -/** - * @fileOverview The "filebrowser" plugin, it adds support for file uploads and - * browsing. - * - * When file is selected inside of the file browser or uploaded, its url is - * inserted automatically to a field, which is described in the 'filebrowser' - * attribute. To specify field that should be updated, pass the tab id and - * element id, separated with a colon. - * - * Example 1: (Browse) - * - * <pre> - * { - * type : 'button', - * id : 'browse', - * filebrowser : 'tabId:elementId', - * label : editor.lang.common.browseServer - * } - * </pre> - * - * If you set the 'filebrowser' attribute on any element other than - * 'fileButton', the 'Browse' action will be triggered. - * - * Example 2: (Quick Upload) - * - * <pre> - * { - * type : 'fileButton', - * id : 'uploadButton', - * filebrowser : 'tabId:elementId', - * label : editor.lang.common.uploadSubmit, - * 'for' : [ 'upload', 'upload' ] - * } - * </pre> - * - * If you set the 'filebrowser' attribute on a fileButton element, the - * 'QuickUpload' action will be executed. - * - * Filebrowser plugin also supports more advanced configuration (through - * javascript object). - * - * The following settings are supported: - * - * <pre> - * [action] - Browse or QuickUpload - * [target] - field to update, tabId:elementId - * [params] - additional arguments to be passed to the server connector (optional) - * [onSelect] - function to execute when file is selected/uploaded (optional) - * [url] - the URL to be called (optional) - * </pre> - * - * Example 3: (Quick Upload) - * - * <pre> - * { - * type : 'fileButton', - * label : editor.lang.common.uploadSubmit, - * id : 'buttonId', - * filebrowser : - * { - * action : 'QuickUpload', //required - * target : 'tab1:elementId', //required - * params : //optional - * { - * type : 'Files', - * currentFolder : '/folder/' - * }, - * onSelect : function( fileUrl, errorMessage ) //optional - * { - * // Do not call the built-in selectFuntion - * // return false; - * } - * }, - * 'for' : [ 'tab1', 'myFile' ] - * } - * </pre> - * - * Suppose we have a file element with id 'myFile', text field with id - * 'elementId' and a fileButton. If filebowser.url is not specified explicitly, - * form action will be set to 'filebrowser[DialogName]UploadUrl' or, if not - * specified, to 'filebrowserUploadUrl'. Additional parameters from 'params' - * object will be added to the query string. It is possible to create your own - * uploadHandler and cancel the built-in updateTargetElement command. - * - * Example 4: (Browse) - * - * <pre> - * { - * type : 'button', - * id : 'buttonId', - * label : editor.lang.common.browseServer, - * filebrowser : - * { - * action : 'Browse', - * url : '/ckfinder/ckfinder.html&type=Images', - * target : 'tab1:elementId' - * } - * } - * </pre> - * - * In this example, after pressing a button, file browser will be opened in a - * popup. If we don't specify filebrowser.url attribute, - * 'filebrowser[DialogName]BrowseUrl' or 'filebrowserBrowseUrl' will be used. - * After selecting a file in a file browser, an element with id 'elementId' will - * be updated. Just like in the third example, a custom 'onSelect' function may be - * defined. - */ -( function() -{ - /** - * Adds (additional) arguments to given url. - * - * @param {String} - * url The url. - * @param {Object} - * params Additional parameters. - */ - function addQueryString( url, params ) - { - var queryString = []; - - if ( !params ) - return url; - else - { - for ( var i in params ) - queryString.push( i + "=" + encodeURIComponent( params[ i ] ) ); - } - - return url + ( ( url.indexOf( "?" ) != -1 ) ? "&" : "?" ) + queryString.join( "&" ); - } - - /** - * Make a string's first character uppercase. - * - * @param {String} - * str String. - */ - function ucFirst( str ) - { - str += ''; - var f = str.charAt( 0 ).toUpperCase(); - return f + str.substr( 1 ); - } - - /** - * The onlick function assigned to the 'Browse Server' button. Opens the - * file browser and updates target field when file is selected. - * - * @param {CKEDITOR.event} - * evt The event object. - */ - function browseServer( evt ) - { - var dialog = this.getDialog(); - var editor = dialog.getParentEditor(); - - editor._.filebrowserSe = this; - - var width = editor.config[ 'filebrowser' + ucFirst( dialog.getName() ) + 'WindowWidth' ] - || editor.config.filebrowserWindowWidth || '80%'; - var height = editor.config[ 'filebrowser' + ucFirst( dialog.getName() ) + 'WindowHeight' ] - || editor.config.filebrowserWindowHeight || '70%'; - - var params = this.filebrowser.params || {}; - params.CKEditor = editor.name; - params.CKEditorFuncNum = editor._.filebrowserFn; - if ( !params.langCode ) - params.langCode = editor.langCode; - - var url = addQueryString( this.filebrowser.url, params ); - editor.popup( url, width, height ); - } - - /** - * The onlick function assigned to the 'Upload' button. Makes the final - * decision whether form is really submitted and updates target field when - * file is uploaded. - * - * @param {CKEDITOR.event} - * evt The event object. - */ - function uploadFile( evt ) - { - var dialog = this.getDialog(); - var editor = dialog.getParentEditor(); - - editor._.filebrowserSe = this; - - // If user didn't select the file, stop the upload. - if ( !dialog.getContentElement( this[ 'for' ][ 0 ], this[ 'for' ][ 1 ] ).getInputElement().$.value ) - return false; - - if ( !dialog.getContentElement( this[ 'for' ][ 0 ], this[ 'for' ][ 1 ] ).getAction() ) - return false; - - return true; - } - - /** - * Setups the file element. - * - * @param {CKEDITOR.ui.dialog.file} - * fileInput The file element used during file upload. - * @param {Object} - * filebrowser Object containing filebrowser settings assigned to - * the fileButton associated with this file element. - */ - function setupFileElement( editor, fileInput, filebrowser ) - { - var params = filebrowser.params || {}; - params.CKEditor = editor.name; - params.CKEditorFuncNum = editor._.filebrowserFn; - if ( !params.langCode ) - params.langCode = editor.langCode; - - fileInput.action = addQueryString( filebrowser.url, params ); - fileInput.filebrowser = filebrowser; - } - - /** - * Traverse through the content definition and attach filebrowser to - * elements with 'filebrowser' attribute. - * - * @param String - * dialogName Dialog name. - * @param {CKEDITOR.dialog.dialogDefinitionObject} - * definition Dialog definition. - * @param {Array} - * elements Array of {@link CKEDITOR.dialog.contentDefinition} - * objects. - */ - function attachFileBrowser( editor, dialogName, definition, elements ) - { - var element, fileInput; - - for ( var i in elements ) - { - element = elements[ i ]; - - if ( element.type == 'hbox' || element.type == 'vbox' ) - attachFileBrowser( editor, dialogName, definition, element.children ); - - if ( !element.filebrowser ) - continue; - - if ( typeof element.filebrowser == 'string' ) - { - var fb = - { - action : ( element.type == 'fileButton' ) ? 'QuickUpload' : 'Browse', - target : element.filebrowser - }; - element.filebrowser = fb; - } - - if ( element.filebrowser.action == 'Browse' ) - { - var url = element.filebrowser.url || editor.config[ 'filebrowser' + ucFirst( dialogName ) + 'BrowseUrl' ] - || 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 || editor.config[ 'filebrowser' + ucFirst( dialogName ) + 'UploadUrl' ] - || editor.config.filebrowserUploadUrl; - - if ( url ) - { - var onClick = element.onClick; - element.onClick = function( evt ) - { - // "element" here means the definition object, so we need to find the correct - // button to scope the event call - var sender = evt.sender; - if ( onClick && onClick.call( sender, evt ) === false ) - return false; - - return uploadFile.call( sender, evt ); - }; - - element.filebrowser.url = url; - element.hidden = false; - setupFileElement( editor, definition.getContents( element[ 'for' ][ 0 ] ).get( element[ 'for' ][ 1 ] ), element.filebrowser ); - } - } - } - } - - /** - * Updates the target element with the url of uploaded/selected file. - * - * @param {String} - * url The url of a file. - */ - function updateTargetElement( url, sourceElement ) - { - var dialog = sourceElement.getDialog(); - var targetElement = sourceElement.filebrowser.target || null; - url = url.replace( /#/g, '%23' ); - - // If there is a reference to targetElement, update it. - if ( targetElement ) - { - var target = targetElement.split( ':' ); - var element = dialog.getContentElement( target[ 0 ], target[ 1 ] ); - if ( element ) - { - element.setValue( url ); - dialog.selectPage( target[ 0 ] ); - } - } - } - - /** - * Returns true if filebrowser is configured in one of the elements. - * - * @param {CKEDITOR.dialog.dialogDefinitionObject} - * definition Dialog definition. - * @param String - * tabId The tab id where element(s) can be found. - * @param String - * elementId The element id (or ids, separated with a semicolon) to check. - */ - function isConfigured( definition, tabId, elementId ) - { - if ( elementId.indexOf( ";" ) !== -1 ) - { - var ids = elementId.split( ";" ); - for ( var i = 0 ; i < ids.length ; i++ ) - { - if ( isConfigured( definition, tabId, ids[i]) ) - return true; - } - return false; - } - - var elementFileBrowser = definition.getContents( tabId ).get( elementId ).filebrowser; - return ( elementFileBrowser && elementFileBrowser.url ); - } - - function setUrl( fileUrl, data ) - { - var dialog = this._.filebrowserSe.getDialog(), - targetInput = this._.filebrowserSe[ 'for' ], - onSelect = this._.filebrowserSe.filebrowser.onSelect; - - if ( targetInput ) - dialog.getContentElement( targetInput[ 0 ], targetInput[ 1 ] ).reset(); - - if ( typeof data == 'function' && data.call( this._.filebrowserSe ) === false ) - return; - - if ( onSelect && onSelect.call( this._.filebrowserSe, fileUrl, data ) === false ) - return; - - // The "data" argument may be used to pass the error message to the editor. - if ( typeof data == 'string' && data ) - alert( data ); - - if ( fileUrl ) - updateTargetElement( fileUrl, this._.filebrowserSe ); - } - - CKEDITOR.plugins.add( 'filebrowser', - { - init : function( editor, pluginPath ) - { - editor._.filebrowserFn = CKEDITOR.tools.addFunction( setUrl, editor ); - } - } ); - - CKEDITOR.on( 'dialogDefinition', function( evt ) - { - var definition = evt.data.definition, - element; - // Associate filebrowser to elements with 'filebrowser' attribute. - for ( var i in definition.contents ) - { - element = definition.contents[ i ] ; - attachFileBrowser( evt.editor, evt.data.name, definition, element.elements ); - if ( element.hidden && element.filebrowser ) - { - element.hidden = !isConfigured( definition, element[ 'id' ], element.filebrowser ); - } - } - } ); - -} )(); - -/** - * The location of an external file browser, that should be launched when "Browse Server" button is pressed. - * If configured, the "Browse Server" button will appear in Link, Image and Flash dialogs. - * @see The <a href="http://docs.cksource.com/CKEditor_3.x/Developers_Guide/File_Browser_(Uploader)">File Browser/Uploader</a> documentation. - * @name CKEDITOR.config.filebrowserBrowseUrl - * @since 3.0 - * @type String - * @default '' (empty string = disabled) - * @example - * config.filebrowserBrowseUrl = '/browser/browse.php'; - */ - -/** - * The location of a script that handles file uploads. - * If set, the "Upload" tab will appear in "Link", "Image" and "Flash" dialogs. - * @name CKEDITOR.config.filebrowserUploadUrl - * @see The <a href="http://docs.cksource.com/CKEditor_3.x/Developers_Guide/File_Browser_(Uploader)">File Browser/Uploader</a> documentation. - * @since 3.0 - * @type String - * @default '' (empty string = disabled) - * @example - * config.filebrowserUploadUrl = '/uploader/upload.php'; - */ - -/** - * The location of an external file browser, that should be launched when "Browse Server" button is pressed in the Image dialog. - * If not set, CKEditor will use {@link CKEDITOR.config.filebrowserBrowseUrl}. - * @name CKEDITOR.config.filebrowserImageBrowseUrl - * @since 3.0 - * @type String - * @default '' (empty string = disabled) - * @example - * config.filebrowserImageBrowseUrl = '/browser/browse.php?type=Images'; - */ - -/** - * The location of an external file browser, that should be launched when "Browse Server" button is pressed in the Flash dialog. - * If not set, CKEditor will use {@link CKEDITOR.config.filebrowserBrowseUrl}. - * @name CKEDITOR.config.filebrowserFlashBrowseUrl - * @since 3.0 - * @type String - * @default '' (empty string = disabled) - * @example - * config.filebrowserFlashBrowseUrl = '/browser/browse.php?type=Flash'; - */ - -/** - * The location of a script that handles file uploads in the Image dialog. - * If not set, CKEditor will use {@link CKEDITOR.config.filebrowserUploadUrl}. - * @name CKEDITOR.config.filebrowserImageUploadUrl - * @since 3.0 - * @type String - * @default '' (empty string = disabled) - * @example - * config.filebrowserImageUploadUrl = '/uploader/upload.php?type=Images'; - */ - -/** - * The location of a script that handles file uploads in the Flash dialog. - * If not set, CKEditor will use {@link CKEDITOR.config.filebrowserUploadUrl}. - * @name CKEDITOR.config.filebrowserFlashUploadUrl - * @since 3.0 - * @type String - * @default '' (empty string = disabled) - * @example - * config.filebrowserFlashUploadUrl = '/uploader/upload.php?type=Flash'; - */ - -/** - * The location of an external file browser, that should be launched when "Browse Server" button is pressed in the Link tab of Image dialog. - * If not set, CKEditor will use {@link CKEDITOR.config.filebrowserBrowseUrl}. - * @name CKEDITOR.config.filebrowserImageBrowseLinkUrl - * @since 3.2 - * @type String - * @default '' (empty string = disabled) - * @example - * config.filebrowserImageBrowseLinkUrl = '/browser/browse.php'; - */ +/*
+Copyright (c) 2003-2012, 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> – <code>Browse</code> or <code>QuickUpload</code>.</li>
+ * <li><code>target</code> – the field to update in the <code><em>tabId:elementId</em></code> format.</li>
+ * <li><code>params</code> – additional arguments to be passed to the server connector (optional).</li>
+ * <li><code>onSelect</code> – a function to execute when the file is selected/uploaded (optional).</li>
+ * <li><code>url</code> – 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&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' || element.type == 'fieldset' )
+ 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;
+
+ // 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%';
+ */
diff --git a/_source/plugins/find/dialogs/find.js b/_source/plugins/find/dialogs/find.js index fd999ee..a84315e 100644 --- a/_source/plugins/find/dialogs/find.js +++ b/_source/plugins/find/dialogs/find.js @@ -1,867 +1,915 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -(function() -{ - function nonEmptyText( node ) - { - return ( node.type == CKEDITOR.NODE_TEXT && node.getLength() > 0 ); - } - - /** - * Elements which break characters been considered as sequence. - */ - function nonCharactersBoundary ( node ) - { - return !( node.type == CKEDITOR.NODE_ELEMENT && node.isBlockBoundary( - CKEDITOR.tools.extend( {}, CKEDITOR.dtd.$empty, CKEDITOR.dtd.$nonEditable ) ) ); - } - - /** - * Get the cursor object which represent both current character and it's dom - * position thing. - */ - var cursorStep = function() - { - return { - textNode : this.textNode, - offset : this.offset, - character : this.textNode ? - this.textNode.getText().charAt( this.offset ) : null, - hitMatchBoundary : this._.matchBoundary - }; - }; - - var pages = [ 'find', 'replace' ], - fieldsMapping = [ - [ 'txtFindFind', 'txtFindReplace' ], - [ 'txtFindCaseChk', 'txtReplaceCaseChk' ], - [ 'txtFindWordChk', 'txtReplaceWordChk' ], - [ 'txtFindCyclic', 'txtReplaceCyclic' ] ]; - - /** - * Synchronize corresponding filed values between 'replace' and 'find' pages. - * @param {String} currentPageId The page id which receive values. - */ - function syncFieldsBetweenTabs( currentPageId ) - { - var sourceIndex, targetIndex, - sourceField, targetField; - - sourceIndex = currentPageId === 'find' ? 1 : 0; - targetIndex = 1 - sourceIndex; - var i, l = fieldsMapping.length; - for ( i = 0 ; i < l ; i++ ) - { - sourceField = this.getContentElement( pages[ sourceIndex ], - fieldsMapping[ i ][ sourceIndex ] ); - targetField = this.getContentElement( pages[ targetIndex ], - fieldsMapping[ i ][ targetIndex ] ); - - targetField.setValue( sourceField.getValue() ); - } - } - - var findDialog = function( editor, startupPage ) - { - // Style object for highlights: (#5018) - // 1. Defined as full match style to avoid compromising ordinary text color styles. - // 2. Must be apply onto inner-most text to avoid conflicting with ordinary text color styles visually. - var highlightStyle = new CKEDITOR.style( CKEDITOR.tools.extend( { fullMatch : true, childRule : function(){ return false; } }, - editor.config.find_highlight ) ); - - /** - * Iterator which walk through the specified range char by char. By - * default the walking will not stop at the character boundaries, until - * the end of the range is encountered. - * @param { CKEDITOR.dom.range } range - * @param {Boolean} matchWord Whether the walking will stop at character boundary. - */ - var characterWalker = function( range , matchWord ) - { - var walker = - new CKEDITOR.dom.walker( range ); - walker.guard = matchWord ? nonCharactersBoundary : null; - walker[ 'evaluator' ] = nonEmptyText; - walker.breakOnFalse = true; - - 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; - - // Marking as match character boundaries. - if ( !currentTextNode - && !nonCharactersBoundary( this._.walker.current ) ) - this._.matchBoundary = true; - - } - // 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 : false - }; - }; - - 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(); - highlightStyle.applyToRange( range ); - 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; - - highlightStyle.removeFromRange( this._.highlightRange ); - this.updateFromDomRange( this._.highlightRange ); - this._.highlightRange = null; - }, - - 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( true ); - 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 ) - { - // Successiveness of current replace/find. - var result = false; - - // 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 ) - { - // 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 = true; - } - else - result = this.find( pattern, matchCase, matchWord, matchCyclic, !isReplaceAll ); - - 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; - } - - return { - title : editor.lang.findAndReplace.title, - resizable : CKEDITOR.DIALOG_RESIZE_NONE, - minWidth : 350, - minHeight : 165, - buttons : [ CKEDITOR.dialog.cancelButton ], //Cancel button only. - contents : [ - { - id : 'find', - label : editor.lang.findAndReplace.find, - title : editor.lang.findAndReplace.find, - accessKey : '', - elements : [ - { - type : 'hbox', - widths : [ '230px', '90px' ], - children : - [ - { - type : 'text', - id : 'txtFindFind', - label : editor.lang.findAndReplace.findWhat, - isChanged : false, - labelLayout : 'horizontal', - accessKey : 'F' - }, - { - type : 'button', - align : 'left', - style : 'width:100%', - label : editor.lang.findAndReplace.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( editor.lang.findAndReplace - .notFoundMsg ); - } - } - ] - }, - { - type : 'vbox', - padding : 0, - children : - [ - { - type : 'checkbox', - id : 'txtFindCaseChk', - isChanged : false, - style : 'margin-top:28px', - label : editor.lang.findAndReplace.matchCase - }, - { - type : 'checkbox', - id : 'txtFindWordChk', - isChanged : false, - label : editor.lang.findAndReplace.matchWord - }, - { - type : 'checkbox', - id : 'txtFindCyclic', - isChanged : false, - 'default' : true, - label : editor.lang.findAndReplace.matchCyclic - } - ] - } - ] - }, - { - id : 'replace', - label : editor.lang.findAndReplace.replace, - accessKey : 'M', - elements : [ - { - type : 'hbox', - widths : [ '230px', '90px' ], - children : - [ - { - type : 'text', - id : 'txtFindReplace', - label : editor.lang.findAndReplace.findWhat, - isChanged : false, - labelLayout : 'horizontal', - accessKey : 'F' - }, - { - type : 'button', - align : 'left', - style : 'width:100%', - label : editor.lang.findAndReplace.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( editor.lang.findAndReplace - .notFoundMsg ); - } - } - ] - }, - { - type : 'hbox', - widths : [ '230px', '90px' ], - children : - [ - { - type : 'text', - id : 'txtReplace', - label : editor.lang.findAndReplace.replaceWith, - isChanged : false, - labelLayout : 'horizontal', - accessKey : 'R' - }, - { - type : 'button', - align : 'left', - style : 'width:100%', - label : editor.lang.findAndReplace.replaceAll, - isChanged : false, - onClick : function() - { - var dialog = this.getDialog(); - var replaceNums; - - finder.replaceCounter = 0; - - // Scope to full document. - finder.searchRange = getSearchRange( true ); - 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( editor.lang.findAndReplace.replaceSuccessMsg.replace( /%1/, finder.replaceCounter ) ); - editor.fire( 'saveSnapshot' ); - } - else - alert( editor.lang.findAndReplace.notFoundMsg ); - } - } - ] - }, - { - type : 'vbox', - padding : 0, - children : - [ - { - type : 'checkbox', - id : 'txtReplaceCaseChk', - isChanged : false, - label : editor.lang.findAndReplace - .matchCase - }, - { - type : 'checkbox', - id : 'txtReplaceWordChk', - isChanged : false, - label : editor.lang.findAndReplace - .matchWord - }, - { - type : 'checkbox', - id : 'txtReplaceCyclic', - isChanged : false, - 'default' : true, - label : editor.lang.findAndReplace - .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 = false; - this.on('hide', function() - { - isUserSelect = false; - } ); - this.on('show', function() - { - isUserSelect = true; - } ); - - 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; - } - - if ( isUserSelect ) - // synchronize fields on tab switch. - syncFieldsBetweenTabs.call( this, pageId ); - }; - } ); - - }, - onShow : function() - { - // Establish initial searching start position. - finder.searchRange = getSearchRange(); - - this.selectPage( startupPage ); - }, - onHide : function() - { - var range; - if ( finder.matchRange && finder.matchRange.isMatched() ) - { - finder.matchRange.removeHighlight(); - editor.focus(); - - range = finder.matchRange.toDomRange(); - if ( range ) - editor.getSelection().selectRanges( [ range ] ); - } - - // Clear current session before dialog close - delete finder.matchRange; - }, - onFocus : function() - { - if ( startupPage == 'replace' ) - return this.getContentElement( 'replace', 'txtFindReplace' ); - else - return this.getContentElement( 'find', 'txtFindFind' ); - } - }; - }; - - CKEDITOR.dialog.add( 'find', function( editor ) - { - return findDialog( editor, 'find' ); - }); - - CKEDITOR.dialog.add( 'replace', function( editor ) - { - return findDialog( editor, 'replace' ); - }); -})(); +/*
+Copyright (c) 2003-2012, 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' );
+ });
+})();
diff --git a/_source/plugins/find/plugin.js b/_source/plugins/find/plugin.js index 7b4ceca..2e9cda8 100644 --- a/_source/plugins/find/plugin.js +++ b/_source/plugins/find/plugin.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
@@ -15,6 +15,7 @@ CKEDITOR.plugins.add( 'find', });
var findCommand = editor.addCommand( 'find', new CKEDITOR.dialogCommand( 'find' ) );
findCommand.canUndo = false;
+ findCommand.readOnly = 1;
editor.ui.addButton( 'Replace',
{
diff --git a/_source/plugins/flash/dialogs/flash.js b/_source/plugins/flash/dialogs/flash.js index 2ad07c9..5ac131a 100644 --- a/_source/plugins/flash/dialogs/flash.js +++ b/_source/plugins/flash/dialogs/flash.js @@ -1,698 +1,674 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -(function() -{ - /* - * It is possible to set things in three different places. - * 1. As attributes in the object tag. - * 2. As param tags under the object tag. - * 3. As attributes in the embed tag. - * It is possible for a single attribute to be present in more than one place. - * So let's define a mapping between a sementic attribute and its syntactic - * equivalents. - * Then we'll set and retrieve attribute values according to the mapping, - * instead of having to check and set each syntactic attribute every time. - * - * Reference: http://kb.adobe.com/selfservice/viewContent.do?externalId=tn_12701 - */ - var ATTRTYPE_OBJECT = 1, - ATTRTYPE_PARAM = 2, - ATTRTYPE_EMBED = 4; - - var attributesMap = - { - id : [ { type : ATTRTYPE_OBJECT, name : 'id' } ], - classid : [ { type : ATTRTYPE_OBJECT, name : 'classid' } ], - codebase : [ { type : ATTRTYPE_OBJECT, name : 'codebase'} ], - pluginspage : [ { type : ATTRTYPE_EMBED, name : 'pluginspage' } ], - src : [ { type : ATTRTYPE_PARAM, name : 'movie' }, { type : ATTRTYPE_EMBED, name : 'src' } ], - name : [ { type : ATTRTYPE_EMBED, name : 'name' } ], - align : [ { type : ATTRTYPE_OBJECT, name : 'align' } ], - title : [ { type : ATTRTYPE_OBJECT, name : 'title' }, { type : ATTRTYPE_EMBED, name : 'title' } ], - 'class' : [ { type : ATTRTYPE_OBJECT, name : 'class' }, { type : ATTRTYPE_EMBED, name : 'class'} ], - width : [ { type : ATTRTYPE_OBJECT, name : 'width' }, { type : ATTRTYPE_EMBED, name : 'width' } ], - height : [ { type : ATTRTYPE_OBJECT, name : 'height' }, { type : ATTRTYPE_EMBED, name : 'height' } ], - hSpace : [ { type : ATTRTYPE_OBJECT, name : 'hSpace' }, { type : ATTRTYPE_EMBED, name : 'hSpace' } ], - vSpace : [ { type : ATTRTYPE_OBJECT, name : 'vSpace' }, { type : ATTRTYPE_EMBED, name : 'vSpace' } ], - style : [ { type : ATTRTYPE_OBJECT, name : 'style' }, { type : ATTRTYPE_EMBED, name : 'style' } ], - type : [ { type : ATTRTYPE_EMBED, name : 'type' } ] - }; - - var names = [ 'play', 'loop', 'menu', 'quality', 'scale', 'salign', 'wmode', 'bgcolor', 'base', 'flashvars', 'allowScriptAccess', - 'allowFullScreen' ]; - for ( var i = 0 ; i < names.length ; i++ ) - attributesMap[ names[i] ] = [ { type : ATTRTYPE_EMBED, name : names[i] }, { type : ATTRTYPE_PARAM, name : names[i] } ]; - names = [ 'allowFullScreen', 'play', 'loop', 'menu' ]; - for ( i = 0 ; i < names.length ; i++ ) - attributesMap[ names[i] ][0]['default'] = attributesMap[ names[i] ][1]['default'] = true; - - function loadValue( objectNode, embedNode, paramMap ) - { - var attributes = attributesMap[ this.id ]; - if ( !attributes ) - return; - - var isCheckbox = ( this instanceof CKEDITOR.ui.dialog.checkbox ); - for ( var i = 0 ; i < attributes.length ; i++ ) - { - var attrDef = attributes[ i ]; - switch ( attrDef.type ) - { - case ATTRTYPE_OBJECT: - if ( !objectNode ) - continue; - if ( objectNode.getAttribute( attrDef.name ) !== null ) - { - var value = objectNode.getAttribute( attrDef.name ); - if ( isCheckbox ) - this.setValue( value.toLowerCase() == 'true' ); - else - this.setValue( value ); - return; - } - else if ( isCheckbox ) - this.setValue( !!attrDef[ 'default' ] ); - break; - case ATTRTYPE_PARAM: - if ( !objectNode ) - continue; - if ( attrDef.name in paramMap ) - { - value = paramMap[ attrDef.name ]; - if ( isCheckbox ) - this.setValue( value.toLowerCase() == 'true' ); - else - this.setValue( value ); - return; - } - else if ( isCheckbox ) - this.setValue( !!attrDef[ 'default' ] ); - break; - case ATTRTYPE_EMBED: - if ( !embedNode ) - continue; - if ( embedNode.getAttribute( attrDef.name ) ) - { - value = embedNode.getAttribute( attrDef.name ); - if ( isCheckbox ) - this.setValue( value.toLowerCase() == 'true' ); - else - this.setValue( value ); - return; - } - else if ( isCheckbox ) - this.setValue( !!attrDef[ 'default' ] ); - } - } - } - - function commitValue( objectNode, embedNode, paramMap ) - { - var attributes = attributesMap[ this.id ]; - if ( !attributes ) - return; - - var isRemove = ( this.getValue() === '' ), - isCheckbox = ( this instanceof CKEDITOR.ui.dialog.checkbox ); - - for ( var i = 0 ; i < attributes.length ; i++ ) - { - var attrDef = attributes[i]; - switch ( attrDef.type ) - { - case ATTRTYPE_OBJECT: - if ( !objectNode ) - continue; - var value = this.getValue(); - if ( isRemove || isCheckbox && value === attrDef[ 'default' ] ) - objectNode.removeAttribute( attrDef.name ); - else - objectNode.setAttribute( attrDef.name, value ); - break; - case ATTRTYPE_PARAM: - if ( !objectNode ) - continue; - value = this.getValue(); - if ( isRemove || isCheckbox && value === attrDef[ 'default' ] ) - { - if ( attrDef.name in paramMap ) - paramMap[ attrDef.name ].remove(); - } - else - { - if ( attrDef.name in paramMap ) - paramMap[ attrDef.name ].setAttribute( 'value', value ); - else - { - var param = CKEDITOR.dom.element.createFromHtml( '<cke:param></cke:param>', objectNode.getDocument() ); - param.setAttributes( { name : attrDef.name, value : value } ); - if ( objectNode.getChildCount() < 1 ) - param.appendTo( objectNode ); - else - param.insertBefore( objectNode.getFirst() ); - } - } - break; - case ATTRTYPE_EMBED: - if ( !embedNode ) - continue; - value = this.getValue(); - if ( isRemove || isCheckbox && value === attrDef[ 'default' ]) - embedNode.removeAttribute( attrDef.name ); - else - embedNode.setAttribute( attrDef.name, value ); - } - } - } - - CKEDITOR.dialog.add( 'flash', function( editor ) - { - var makeObjectTag = !editor.config.flashEmbedTagOnly, - makeEmbedTag = editor.config.flashAddEmbedTag || editor.config.flashEmbedTagOnly; - - var previewPreloader, - previewAreaHtml = '<div>' + CKEDITOR.tools.htmlEncode( editor.lang.common.preview ) +'<br>' + - '<div id="FlashPreviewLoader' + CKEDITOR.tools.getNextNumber() + '" style="display:none"><div class="loading"> </div></div>' + - '<div id="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( 'embeded', editor.document ); - - // Try to detect any embed or object tag that has Flash parameters. - var fakeImage = this.getSelectedElement(); - if ( fakeImage && fakeImage.getAttribute( '_cke_real_element_type' ) && fakeImage.getAttribute( '_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.flash.width, - validate : CKEDITOR.dialog.validate.integer( editor.lang.flash.validateWidth ), - setup : function( objectNode, embedNode, paramMap, fakeImage ) - { - loadValue.apply( this, arguments ); - if ( fakeImage ) - { - var fakeImageWidth = parseInt( fakeImage.$.style.width, 10 ); - if ( !isNaN( fakeImageWidth ) ) - this.setValue( fakeImageWidth ); - } - }, - commit : function( objectNode, embedNode, paramMap, extraStyles ) - { - commitValue.apply( this, arguments ); - if ( this.getValue() ) - extraStyles.width = this.getValue() + 'px'; - } - }, - { - type : 'text', - id : 'height', - style : 'width:95px', - label : editor.lang.flash.height, - validate : CKEDITOR.dialog.validate.integer( editor.lang.flash.validateHeight ), - setup : function( objectNode, embedNode, paramMap, fakeImage ) - { - loadValue.apply( this, arguments ); - if ( fakeImage ) - { - var fakeImageHeight = parseInt( fakeImage.$.style.height, 10 ); - if ( !isNaN( fakeImageHeight ) ) - this.setValue( fakeImageHeight ); - } - }, - commit : function( objectNode, embedNode, paramMap, extraStyles ) - { - commitValue.apply( this, arguments ); - if ( this.getValue() ) - extraStyles.height = this.getValue() + 'px'; - } - }, - { - type : 'text', - id : 'hSpace', - style : 'width:95px', - label : editor.lang.flash.hSpace, - validate : CKEDITOR.dialog.validate.integer( editor.lang.flash.validateHSpace ), - setup : loadValue, - commit : commitValue - }, - { - type : 'text', - id : 'vSpace', - style : 'width:95px', - label : editor.lang.flash.vSpace, - validate : CKEDITOR.dialog.validate.integer( editor.lang.flash.validateVSpace ), - setup : loadValue, - commit : commitValue - } - ] - }, - - { - type : 'vbox', - children : - [ - { - type : 'html', - id : 'preview', - style : 'width:95%;', - html : previewAreaHtml - } - ] - } - ] - }, - { - id : 'Upload', - hidden : true, - filebrowser : 'uploadButton', - label : editor.lang.common.upload, - elements : - [ - { - type : 'file', - id : 'upload', - label : editor.lang.common.upload, - size : 38 - }, - { - type : 'fileButton', - id : 'uploadButton', - label : editor.lang.common.uploadSubmit, - filebrowser : 'info:src', - 'for' : [ 'Upload', 'upload' ] - } - ] - }, - { - id : 'properties', - label : editor.lang.flash.propertiesTab, - elements : - [ - { - type : 'hbox', - widths : [ '50%', '50%' ], - children : - [ - { - id : 'scale', - type : 'select', - label : editor.lang.flash.scale, - 'default' : '', - style : 'width : 100%;', - items : - [ - [ editor.lang.common.notSet , ''], - [ editor.lang.flash.scaleAll, 'showall' ], - [ editor.lang.flash.scaleNoBorder, 'noborder' ], - [ editor.lang.flash.scaleFit, 'exactfit' ] - ], - setup : loadValue, - commit : commitValue - }, - { - id : 'allowScriptAccess', - type : 'select', - label : editor.lang.flash.access, - 'default' : '', - style : 'width : 100%;', - items : - [ - [ editor.lang.common.notSet , ''], - [ editor.lang.flash.accessAlways, 'always' ], - [ editor.lang.flash.accessSameDomain, 'samedomain' ], - [ editor.lang.flash.accessNever, 'never' ] - ], - setup : loadValue, - commit : commitValue - } - ] - }, - { - type : 'hbox', - widths : [ '50%', '50%' ], - children : - [ - { - id : 'wmode', - type : 'select', - label : editor.lang.flash.windowMode, - 'default' : '', - style : 'width : 100%;', - items : - [ - [ editor.lang.common.notSet , '' ], - [ editor.lang.flash.windowModeWindow, 'window' ], - [ editor.lang.flash.windowModeOpaque, 'opaque' ], - [ editor.lang.flash.windowModeTransparent, 'transparent' ] - ], - setup : loadValue, - commit : commitValue - }, - { - id : 'quality', - type : 'select', - label : editor.lang.flash.quality, - 'default' : 'high', - style : 'width : 100%;', - items : - [ - [ editor.lang.common.notSet , '' ], - [ editor.lang.flash.qualityBest, 'best' ], - [ editor.lang.flash.qualityHigh, 'high' ], - [ editor.lang.flash.qualityAutoHigh, 'autohigh' ], - [ editor.lang.flash.qualityMedium, 'medium' ], - [ editor.lang.flash.qualityAutoLow, 'autolow' ], - [ editor.lang.flash.qualityLow, 'low' ] - ], - setup : loadValue, - commit : commitValue - } - ] - }, - { - type : 'hbox', - widths : [ '50%', '50%' ], - children : - [ - { - id : 'align', - type : 'select', - label : editor.lang.flash.align, - 'default' : '', - style : 'width : 100%;', - items : - [ - [ editor.lang.common.notSet , ''], - [ editor.lang.flash.alignLeft , 'left'], - [ editor.lang.flash.alignAbsBottom , 'absBottom'], - [ editor.lang.flash.alignAbsMiddle , 'absMiddle'], - [ editor.lang.flash.alignBaseline , 'baseline'], - [ editor.lang.flash.alignBottom , 'bottom'], - [ editor.lang.flash.alignMiddle , 'middle'], - [ editor.lang.flash.alignRight , 'right'], - [ editor.lang.flash.alignTextTop , 'textTop'], - [ editor.lang.flash.alignTop , 'top'] - ], - setup : loadValue, - commit : function( objectNode, embedNode, paramMap, extraStyles, extraAttributes ) - { - var value = this.getValue(); - commitValue.apply( this, arguments ); - value && ( extraAttributes.align = value ); - } - }, - { - type : 'html', - html : '<div></div>' - } - ] - }, - { - type : 'fieldset', - label : CKEDITOR.tools.htmlEncode( editor.lang.flash.flashvars ), - children : - [ - { - type : 'vbox', - padding : 0, - children : - [ - { - type : 'checkbox', - id : 'menu', - label : editor.lang.flash.chkMenu, - 'default' : true, - setup : loadValue, - commit : commitValue - }, - { - type : 'checkbox', - id : 'play', - label : editor.lang.flash.chkPlay, - 'default' : true, - setup : loadValue, - commit : commitValue - }, - { - type : 'checkbox', - id : 'loop', - label : editor.lang.flash.chkLoop, - 'default' : true, - setup : loadValue, - commit : commitValue - }, - { - type : 'checkbox', - id : 'allowFullScreen', - label : editor.lang.flash.chkFull, - 'default' : true, - setup : loadValue, - commit : commitValue - } - ] - } - ] - } - ] - }, - { - id : 'advanced', - label : editor.lang.common.advancedTab, - elements : - [ - { - type : 'hbox', - widths : [ '45%', '55%' ], - children : - [ - { - type : 'text', - id : 'id', - label : editor.lang.common.id, - setup : loadValue, - commit : commitValue - }, - { - type : 'text', - id : 'title', - label : editor.lang.common.advisoryTitle, - setup : loadValue, - commit : commitValue - } - ] - }, - { - type : 'hbox', - widths : [ '45%', '55%' ], - children : - [ - { - type : 'text', - id : 'bgcolor', - label : editor.lang.flash.bgcolor, - setup : loadValue, - commit : commitValue - }, - { - type : 'text', - id : 'class', - label : editor.lang.common.cssClass, - setup : loadValue, - commit : commitValue - } - ] - }, - { - type : 'text', - id : 'style', - label : editor.lang.common.cssStyle, - setup : loadValue, - commit : commitValue - } - ] - } - ] - }; - } ); -})(); +/*
+Copyright (c) 2003-2012, 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"> </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
+ }
+ ]
+ }
+ ]
+ };
+ } );
+})();
diff --git a/_source/plugins/flash/plugin.js b/_source/plugins/flash/plugin.js index f6d9c99..a018512 100644 --- a/_source/plugins/flash/plugin.js +++ b/_source/plugins/flash/plugin.js @@ -1,173 +1,154 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -(function() -{ - var flashFilenameRegex = /\.swf(?:$|\?)/i, - numberRegex = /^\d+(?:\.\d+)?$/; - - function cssifyLength( length ) - { - if ( numberRegex.test( length ) ) - return length + 'px'; - return length; - } - - function isFlashEmbed( element ) - { - var attributes = element.attributes; - - return ( attributes.type == 'application/x-shockwave-flash' || flashFilenameRegex.test( attributes.src || '' ) ); - } - - function createFakeElement( editor, realElement ) - { - var fakeElement = editor.createFakeParserElement( realElement, 'cke_flash', 'flash', true ), - fakeStyle = fakeElement.attributes.style || ''; - - var width = realElement.attributes.width, - height = realElement.attributes.height; - - if ( typeof width != 'undefined' ) - fakeStyle = fakeElement.attributes.style = fakeStyle + 'width:' + cssifyLength( width ) + ';'; - - if ( typeof height != 'undefined' ) - fakeStyle = fakeElement.attributes.style = fakeStyle + 'height:' + cssifyLength( height ) + ';'; - - return fakeElement; - } - - CKEDITOR.plugins.add( 'flash', - { - init : function( editor ) - { - editor.addCommand( 'flash', new CKEDITOR.dialogCommand( 'flash' ) ); - editor.ui.addButton( 'Flash', - { - label : editor.lang.common.flash, - command : 'flash' - }); - CKEDITOR.dialog.add( 'flash', this.path + 'dialogs/flash.js' ); - - editor.addCss( - 'img.cke_flash' + - '{' + - 'background-image: url(' + CKEDITOR.getUrl( this.path + 'images/placeholder.png' ) + ');' + - 'background-position: center center;' + - 'background-repeat: no-repeat;' + - 'border: 1px solid #a9a9a9;' + - 'width: 80px;' + - 'height: 80px;' + - '}' - ); - - // If the "menu" plugin is loaded, register the menu items. - if ( editor.addMenuItems ) - { - editor.addMenuItems( - { - flash : - { - label : editor.lang.flash.properties, - command : 'flash', - group : 'flash' - } - }); - } - - editor.on( 'doubleclick', function( evt ) - { - var element = evt.data.element; - - if ( element.is( 'img' ) && element.getAttribute( '_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.getAttribute( '_cke_real_element_type' ) == 'flash' ) - return { flash : CKEDITOR.TRISTATE_OFF }; - }); - } - }, - - afterInit : function( editor ) - { - var dataProcessor = editor.dataProcessor, - dataFilter = dataProcessor && dataProcessor.dataFilter; - - if ( dataFilter ) - { - dataFilter.addRules( - { - elements : - { - 'cke:object' : function( element ) - { - var attributes = element.attributes, - classId = attributes.classid && String( attributes.classid ).toLowerCase(); - - if ( !classId ) - { - // Look for the inner <embed> - for ( var i = 0 ; i < element.children.length ; i++ ) - { - if ( element.children[ i ].name == 'cke:embed' ) - { - if ( !isFlashEmbed( element.children[ i ] ) ) - return null; - - return createFakeElement( editor, element ); - } - } - return null; - } - - return createFakeElement( editor, element ); - }, - - 'cke:embed' : function( element ) - { - if ( !isFlashEmbed( element ) ) - return null; - - return createFakeElement( editor, element ); - } - } - }, - 5); - } - }, - - requires : [ 'fakeobjects' ] - }); -})(); - -CKEDITOR.tools.extend( CKEDITOR.config, -{ - /** - * Save as EMBED tag only. This tag is unrecommended. - * @type Boolean - * @default false - */ - flashEmbedTagOnly : false, - - /** - * Add EMBED tag as alternative: <object><embed></embed></object> - * @type Boolean - * @default false - */ - flashAddEmbedTag : true, - - /** - * Use embedTagOnly and addEmbedTag values on edit. - * @type Boolean - * @default false - */ - flashConvertOnEdit : false -} ); +/*
+Copyright (c) 2003-2012, 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: <object><embed></embed></object>
+ * @type Boolean
+ * @default false
+ */
+ flashAddEmbedTag : true,
+
+ /**
+ * Use embedTagOnly and addEmbedTag values on edit.
+ * @type Boolean
+ * @default false
+ */
+ flashConvertOnEdit : false
+} );
diff --git a/_source/plugins/floatpanel/plugin.js b/_source/plugins/floatpanel/plugin.js index 6d57442..7e9af39 100644 --- a/_source/plugins/floatpanel/plugin.js +++ b/_source/plugins/floatpanel/plugin.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
@@ -16,14 +16,8 @@ CKEDITOR.plugins.add( 'floatpanel', function getPanel( editor, doc, parentElement, definition, level )
{
// Generates the panel key: docId-eleId-skinName-langDir[-uiColor][-CSSs][-level]
- var key =
- doc.getUniqueId() +
- '-' + parentElement.getUniqueId() +
- '-' + editor.skinName +
- '-' + editor.lang.dir +
- ( ( editor.uiColor && ( '-' + editor.uiColor ) ) || '' ) +
- ( ( definition.css && ( '-' + definition.css ) ) || '' ) +
- ( ( level && ( '-' + level ) ) || '' );
+ var key = CKEDITOR.tools.genKey( doc.getUniqueId(), parentElement.getUniqueId(), editor.skinName, editor.lang.dir,
+ editor.uiColor || '', definition.css || '', level || '' );
var panel = panels[ key ];
@@ -46,17 +40,21 @@ CKEDITOR.plugins.add( 'floatpanel', {
$ : function( editor, parentElement, definition, level )
{
- definition.forceIFrame = true;
+ definition.forceIFrame = 1;
var doc = parentElement.getDocument(),
panel = getPanel( editor, doc, parentElement, definition, level || 0 ),
element = panel.element,
iframe = element.getFirst().getFirst();
+ // Disable native browser menu. (#4825)
+ element.disableContextMenu();
+
this.element = element;
this._ =
{
+ editor : editor,
// The panel that will be floating.
panel : panel,
parentElement : parentElement,
@@ -66,6 +64,8 @@ CKEDITOR.plugins.add( 'floatpanel', children : [],
dir : editor.lang.dir
};
+
+ editor.on( 'mode', function(){ this.hide(); }, this );
},
proto :
@@ -104,7 +104,11 @@ CKEDITOR.plugins.add( 'floatpanel', block = panel.showBlock( name );
this.allowBlur( false );
- isShowing = true;
+ 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,
@@ -130,13 +134,16 @@ CKEDITOR.plugins.add( 'floatpanel', element.setStyles(
{
top : top + 'px',
- left : '-3000px',
- opacity : '0', // FF3 is ignoring "visibility"
+ 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');
+ element.getFirst().removeStyle( 'width' );
// Configure the IFrame blur event. Do that only once.
if ( !this._.blurSet )
@@ -158,14 +165,17 @@ CKEDITOR.plugins.add( 'floatpanel', // the blur event may get fired even when focusing
// inside the window itself, so we must ensure the
// target is out of it.
- var target;
- if ( CKEDITOR.env.ie && !this.allowBlur()
- || ( target = ev.data.getTarget() )
- && target.getName && target.getName() != 'iframe' )
+ 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 );
@@ -191,9 +201,6 @@ CKEDITOR.plugins.add( 'floatpanel', CKEDITOR.tools.setTimeout( function()
{
- if ( rtl )
- left -= element.$.offsetWidth;
-
var panelLoad = CKEDITOR.tools.bind( function ()
{
var target = element.getFirst();
@@ -203,10 +210,10 @@ CKEDITOR.plugins.add( 'floatpanel', // 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)
+ if ( CKEDITOR.env.gecko || CKEDITOR.env.opera )
widthNode = widthNode.parentNode;
- if ( CKEDITOR.env.ie)
+ if ( CKEDITOR.env.ie )
widthNode = widthNode.document.body;
var width = widthNode.scrollWidth;
@@ -214,7 +221,7 @@ CKEDITOR.plugins.add( 'floatpanel', // 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 );
+ 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 ;
@@ -230,7 +237,7 @@ CKEDITOR.plugins.add( 'floatpanel', // 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 );
+ height += ( target.$.offsetHeight || 0 ) - ( target.$.clientHeight || 0 ) + 3;
target.setStyle( 'height', height + 'px' );
@@ -240,32 +247,109 @@ CKEDITOR.plugins.add( 'floatpanel', else
target.removeStyle( 'height' );
+ // Flip panel layout horizontally in RTL with known width.
+ if ( rtl )
+ left -= element.$.offsetWidth;
+
+ // Pop the style now for measurement.
+ element.setStyle( 'left', left + 'px' );
+
+ /* panel layout smartly fit the viewport size. */
var panelElement = panel.element,
panelWindow = panelElement.getWindow(),
- windowScroll = panelWindow.getScrollPosition(),
- viewportSize = panelWindow.getViewPaneSize(),
- panelSize =
+ rect = element.$.getBoundingClientRect(),
+ viewportSize = panelWindow.getViewPaneSize();
+
+ // Compensation for browsers that dont support "width" and "height".
+ var rectWidth = rect.width || rect.right - rect.left,
+ rectHeight = rect.height || rect.bottom - rect.top;
+
+ // Check if default horizontal layout is impossible.
+ var spaceAfter = rtl ? rect.right : viewportSize.width - rect.left,
+ spaceBefore = rtl ? viewportSize.width - rect.right : rect.left;
+
+ if ( rtl )
+ {
+ if ( spaceAfter < rectWidth )
{
- 'height' : panelElement.$.offsetHeight,
- 'width' : panelElement.$.offsetWidth
- };
+ // Flip to show on right.
+ if ( spaceBefore > rectWidth )
+ left += rectWidth;
+ // Align to window left.
+ else if ( viewportSize.width > rectWidth )
+ left = left - rect.left;
+ // Align to window right, never cutting the panel at right.
+ else
+ left = left - rect.right + viewportSize.width;
+ }
+ }
+ else if ( spaceAfter < rectWidth )
+ {
+ // Flip to show on left.
+ if ( spaceBefore > rectWidth )
+ left -= rectWidth;
+ // Align to window right.
+ else if ( viewportSize.width > rectWidth )
+ left = left - rect.right + viewportSize.width;
+ // Align to window left, never cutting the panel at left.
+ else
+ left = left - rect.left;
+ }
+
+
+ // Check if the default vertical layout is possible.
+ var spaceBelow = viewportSize.height - rect.top,
+ spaceAbove = rect.top;
+
+ if ( spaceBelow < rectHeight )
+ {
+ // Flip to show above.
+ if ( spaceAbove > rectHeight )
+ top -= rectHeight;
+ // Align to window bottom.
+ else if ( viewportSize.height > rectHeight )
+ top = top - rect.bottom + viewportSize.height;
+ // Align to top, never cutting the panel at top.
+ else
+ top = top - rect.top;
+ }
- // 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 ) );
+ // 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;
- // Vertical off screen is simpler.
- if ( top + panelSize.height > viewportSize.height + windowScroll.y )
- top -= panelSize.height;
+ // 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',
- opacity : '1'
+ left : left + 'px'
} );
-
+ element.setOpacity( 1 );
} , this );
panel.isLoaded ? panelLoad() : panel.onLoad = panelLoad;
@@ -277,22 +361,36 @@ CKEDITOR.plugins.add( 'floatpanel', // We need this get fired manually because of unfired focus() function.
this.allowBlur( true );
}, 0, this);
- }, 0, this);
+ }, CKEDITOR.env.air ? 200 : 0, this);
this.visible = 1;
if ( this.onShow )
this.onShow.call( this );
- isShowing = false;
+ isShowing = 0;
},
- hide : function()
+ 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();
+ }
}
},
@@ -348,6 +446,8 @@ CKEDITOR.plugins.add( 'floatpanel', if ( activeChild )
{
delete activeChild.onHide;
+ // Sub panels don't manage focus. (#7881)
+ delete activeChild._.returnFocus;
delete this._.activeChild;
activeChild.hide();
}
diff --git a/_source/plugins/font/plugin.js b/_source/plugins/font/plugin.js index f38002d..74d3c9d 100644 --- a/_source/plugins/font/plugin.js +++ b/_source/plugins/font/plugin.js @@ -1,234 +1,234 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -(function() -{ - function addCombo( editor, comboName, styleType, lang, entries, defaultLabel, styleDefinition ) - { - var config = editor.config; - - // Gets the list of fonts from the settings. - var names = entries.split( ';' ), - values = []; - - // Create style objects for all fonts. - var styles = {}; - for ( var i = 0 ; i < names.length ; i++ ) - { - var parts = names[ i ]; - - if ( parts ) - { - parts = parts.split( '/' ); - - var vars = {}, - name = names[ i ] = parts[ 0 ]; - - vars[ styleType ] = values[ i ] = parts[ 1 ] || name; - - styles[ name ] = new CKEDITOR.style( styleDefinition, vars ); - styles[ name ]._.definition.name = name; - } - else - names.splice( i--, 1 ); - } - - editor.ui.addRichCombo( comboName, - { - label : lang.label, - title : lang.panelTitle, - className : 'cke_' + ( styleType == 'size' ? 'fontSize' : 'font' ), - panel : - { - css : editor.skin.editor.css.concat( config.contentsCss ), - multiSelect : false, - attributes : { 'aria-label' : lang.panelTitle } - }, - - init : function() - { - this.startGroup( lang.panelTitle ); - - for ( var i = 0 ; i < names.length ; i++ ) - { - var name = names[ i ]; - - // Add the tag entry to the panel list. - this.add( name, styles[ name ].buildPreview(), name ); - } - }, - - onClick : function( value ) - { - editor.focus(); - editor.fire( 'saveSnapshot' ); - - var style = styles[ value ]; - - if ( this.getValue() == value ) - style.remove( editor.document ); - else - style.apply( editor.document ); - - editor.fire( 'saveSnapshot' ); - }, - - onRender : function() - { - editor.on( 'selectionChange', function( ev ) - { - var currentValue = this.getValue(); - - var elementPath = ev.data.path, - elements = elementPath.elements; - - // For each element into the elements path. - for ( var i = 0, element ; i < elements.length ; i++ ) - { - element = elements[i]; - - // Check if the element is removable by any of - // the styles. - for ( var value in styles ) - { - if ( styles[ value ].checkElementRemovable( element, true ) ) - { - if ( value != currentValue ) - this.setValue( value ); - return; - } - } - } - - // If no styles match, just empty it. - this.setValue( '', defaultLabel ); - }, - this); - } - }); - } - - CKEDITOR.plugins.add( 'font', - { - requires : [ 'richcombo', 'styles' ], - - init : function( editor ) - { - var config = editor.config; - - addCombo( editor, 'Font', 'family', editor.lang.font, config.font_names, config.font_defaultLabel, config.font_style ); - addCombo( editor, 'FontSize', 'size', editor.lang.fontSize, config.fontSize_sizes, config.fontSize_defaultLabel, config.fontSize_style ); - } - }); -})(); - -/** - * The list of fonts names to be displayed in the Font combo in the toolbar. - * Entries are separated by semi-colons (;), while it's possible to have more - * than one font for each entry, in the HTML way (separated by comma). - * - * A display name may be optionally defined by prefixing the entries with the - * name and the slash character. For example, "Arial/Arial, Helvetica, sans-serif" - * will be displayed as "Arial" in the list, but will be outputted as - * "Arial, Helvetica, sans-serif". - * @type String - * @example - * config.font_names = - * 'Arial/Arial, Helvetica, sans-serif;' + - * 'Times New Roman/Times New Roman, Times, serif;' + - * 'Verdana'; - * @example - * config.font_names = 'Arial;Times New Roman;Verdana'; - */ -CKEDITOR.config.font_names = - 'Arial/Arial, Helvetica, sans-serif;' + - 'Comic Sans MS/Comic Sans MS, cursive;' + - 'Courier New/Courier New, Courier, monospace;' + - 'Georgia/Georgia, serif;' + - 'Lucida Sans Unicode/Lucida Sans Unicode, Lucida Grande, sans-serif;' + - 'Tahoma/Tahoma, Geneva, sans-serif;' + - 'Times New Roman/Times New Roman, Times, serif;' + - 'Trebuchet MS/Trebuchet MS, Helvetica, sans-serif;' + - 'Verdana/Verdana, Geneva, sans-serif'; - -/** - * The text to be displayed in the Font combo is none of the available values - * matches the current cursor position or text selection. - * @type String - * @example - * // If the default site font is Arial, we may making it more explicit to the end user. - * config.font_defaultLabel = 'Arial'; - */ -CKEDITOR.config.font_defaultLabel = ''; - -/** - * The style definition to be used to apply the font in the text. - * @type Object - * @example - * // This is actually the default value for it. - * config.font_style = - * { - * element : 'span', - * styles : { 'font-family' : '#(family)' }, - * overrides : [ { element : 'font', attributes : { 'face' : null } } ] - * }; - */ -CKEDITOR.config.font_style = - { - element : 'span', - styles : { 'font-family' : '#(family)' }, - overrides : [ { element : 'font', attributes : { 'face' : null } } ] - }; - -/** - * The list of fonts size to be displayed in the Font Size combo in the - * toolbar. Entries are separated by semi-colons (;). - * - * Any kind of "CSS like" size can be used, like "12px", "2.3em", "130%", - * "larger" or "x-small". - * - * A display name may be optionally defined by prefixing the entries with the - * name and the slash character. For example, "Bigger Font/14px" will be - * displayed as "Bigger Font" in the list, but will be outputted as "14px". - * @type String - * @default '8/8px;9/9px;10/10px;11/11px;12/12px;14/14px;16/16px;18/18px;20/20px;22/22px;24/24px;26/26px;28/28px;36/36px;48/48px;72/72px' - * @example - * config.fontSize_sizes = '16/16px;24/24px;48/48px;'; - * @example - * config.fontSize_sizes = '12px;2.3em;130%;larger;x-small'; - * @example - * config.fontSize_sizes = '12 Pixels/12px;Big/2.3em;30 Percent More/130%;Bigger/larger;Very Small/x-small'; - */ -CKEDITOR.config.fontSize_sizes = - '8/8px;9/9px;10/10px;11/11px;12/12px;14/14px;16/16px;18/18px;20/20px;22/22px;24/24px;26/26px;28/28px;36/36px;48/48px;72/72px'; - -/** - * The text to be displayed in the Font Size combo is none of the available - * values matches the current cursor position or text selection. - * @type String - * @example - * // If the default site font size is 12px, we may making it more explicit to the end user. - * config.fontSize_defaultLabel = '12px'; - */ -CKEDITOR.config.fontSize_defaultLabel = ''; - -/** - * The style definition to be used to apply the font size in the text. - * @type Object - * @example - * // This is actually the default value for it. - * config.fontSize_style = - * { - * element : 'span', - * styles : { 'font-size' : '#(size)' }, - * overrides : [ { element : 'font', attributes : { 'size' : null } } ] - * }; - */ -CKEDITOR.config.fontSize_style = - { - element : 'span', - styles : { 'font-size' : '#(size)' }, - overrides : [ { element : 'font', attributes : { 'size' : null } } ] - }; +/*
+Copyright (c) 2003-2012, 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 } } ]
+ };
diff --git a/_source/plugins/format/plugin.js b/_source/plugins/format/plugin.js index f2d9df2..6488e75 100644 --- a/_source/plugins/format/plugin.js +++ b/_source/plugins/format/plugin.js @@ -1,194 +1,197 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -CKEDITOR.plugins.add( 'format', -{ - requires : [ 'richcombo', 'styles' ], - - init : function( editor ) - { - var config = editor.config, - lang = editor.lang.format; - - // Gets the list of tags from the settings. - var tags = config.format_tags.split( ';' ); - - // Create style objects for all defined styles. - var styles = {}; - for ( var i = 0 ; i < tags.length ; i++ ) - { - var tag = tags[ i ]; - styles[ tag ] = new CKEDITOR.style( config[ 'format_' + tag ] ); - styles[ tag ]._.enterMode = editor.config.enterMode; - } - - editor.ui.addRichCombo( 'Format', - { - label : lang.label, - title : lang.panelTitle, - className : 'cke_format', - panel : - { - css : editor.skin.editor.css.concat( config.contentsCss ), - multiSelect : false, - attributes : { 'aria-label' : lang.panelTitle } - }, - - init : function() - { - this.startGroup( lang.panelTitle ); - - for ( var tag in styles ) - { - var label = lang[ 'tag_' + tag ]; - - // Add the tag entry to the panel list. - this.add( tag, '<' + tag + '>' + label + '</' + tag + '>', label ); - } - }, - - onClick : function( value ) - { - editor.focus(); - editor.fire( 'saveSnapshot' ); - - styles[ value ].apply( editor.document ); - - // Save the undo snapshot after all changes are affected. (#4899) - setTimeout( function() - { - editor.fire( 'saveSnapshot' ); - }, 0 ); - }, - - onRender : function() - { - editor.on( 'selectionChange', function( ev ) - { - var currentTag = this.getValue(); - - var elementPath = ev.data.path; - - for ( var tag in styles ) - { - if ( styles[ tag ].checkActive( elementPath ) ) - { - if ( tag != currentTag ) - this.setValue( tag, editor.lang.format[ 'tag_' + tag ] ); - return; - } - } - - // If no styles match, just empty it. - this.setValue( '' ); - }, - this); - } - }); - } -}); - -/** - * A list of semi colon separated style names (by default tags) representing - * the style definition for each entry to be displayed in the Format combo in - * the toolbar. Each entry must have its relative definition configuration in a - * setting named "format_(tagName)". For example, the "p" entry has its - * definition taken from config.format_p. - * @type String - * @default 'p;h1;h2;h3;h4;h5;h6;pre;address;div' - * @example - * config.format_tags = 'p;h2;h3;pre' - */ -CKEDITOR.config.format_tags = 'p;h1;h2;h3;h4;h5;h6;pre;address;div'; - -/** - * The style definition to be used to apply the "Normal" format. - * @type Object - * @default { element : 'p' } - * @example - * config.format_p = { element : 'p', attributes : { class : 'normalPara' } }; - */ -CKEDITOR.config.format_p = { element : 'p' }; - -/** - * The style definition to be used to apply the "Normal (DIV)" format. - * @type Object - * @default { element : 'div' } - * @example - * config.format_div = { element : 'div', attributes : { class : 'normalDiv' } }; - */ -CKEDITOR.config.format_div = { element : 'div' }; - -/** - * The style definition to be used to apply the "Formatted" format. - * @type Object - * @default { element : 'pre' } - * @example - * config.format_pre = { element : 'pre', attributes : { class : 'code' } }; - */ -CKEDITOR.config.format_pre = { element : 'pre' }; - -/** - * The style definition to be used to apply the "Address" format. - * @type Object - * @default { element : 'address' } - * @example - * config.format_address = { element : 'address', attributes : { class : 'styledAddress' } }; - */ -CKEDITOR.config.format_address = { element : 'address' }; - -/** - * The style definition to be used to apply the "Heading 1" format. - * @type Object - * @default { element : 'h1' } - * @example - * config.format_h1 = { element : 'h1', attributes : { class : 'contentTitle1' } }; - */ -CKEDITOR.config.format_h1 = { element : 'h1' }; - -/** - * The style definition to be used to apply the "Heading 1" format. - * @type Object - * @default { element : 'h2' } - * @example - * config.format_h2 = { element : 'h2', attributes : { class : 'contentTitle2' } }; - */ -CKEDITOR.config.format_h2 = { element : 'h2' }; - -/** - * The style definition to be used to apply the "Heading 1" format. - * @type Object - * @default { element : 'h3' } - * @example - * config.format_h3 = { element : 'h3', attributes : { class : 'contentTitle3' } }; - */ -CKEDITOR.config.format_h3 = { element : 'h3' }; - -/** - * The style definition to be used to apply the "Heading 1" format. - * @type Object - * @default { element : 'h4' } - * @example - * config.format_h4 = { element : 'h4', attributes : { class : 'contentTitle4' } }; - */ -CKEDITOR.config.format_h4 = { element : 'h4' }; - -/** - * The style definition to be used to apply the "Heading 1" format. - * @type Object - * @default { element : 'h5' } - * @example - * config.format_h5 = { element : 'h5', attributes : { class : 'contentTitle5' } }; - */ -CKEDITOR.config.format_h5 = { element : 'h5' }; - -/** - * The style definition to be used to apply the "Heading 1" format. - * @type Object - * @default { element : 'h6' } - * @example - * config.format_h6 = { element : 'h6', attributes : { class : 'contentTitle6' } }; - */ -CKEDITOR.config.format_h6 = { element : 'h6' }; +/*
+Copyright (c) 2003-2012, 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' };
diff --git a/_source/plugins/forms/dialogs/button.js b/_source/plugins/forms/dialogs/button.js index 86cb4d7..abb6b8a 100644 --- a/_source/plugins/forms/dialogs/button.js +++ b/_source/plugins/forms/dialogs/button.js @@ -1,9 +1,26 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, 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,
@@ -12,10 +29,10 @@ CKEDITOR.dialog.add( 'button', function( editor ) {
delete this.button;
var element = this.getParentEditor().getSelection().getSelectedElement();
- if ( element && element.getName() == "input" )
+ if ( element && element.is( 'input' ) )
{
var type = element.getAttribute( 'type' );
- if ( type == "button" || type == "reset" || type == "submit" )
+ if ( type in { button:1, reset:1, submit:1 } )
{
this.button = element;
this.setupContent( element );
@@ -24,19 +41,25 @@ CKEDITOR.dialog.add( 'button', function( editor ) },
onOk : function()
{
- var editor,
+ 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
{
- editor = this.getParentEditor();
- element = editor.document.createElement( 'input' );
+ newElement.replace( element );
+ editor.getSelection().selectElement( newElement );
}
-
- if ( isInsertMode )
- editor.insertElement( element );
- this.commitContent( { element : element } );
},
contents : [
{
@@ -45,29 +68,18 @@ CKEDITOR.dialog.add( 'button', function( editor ) title : editor.lang.button.title,
elements : [
{
- id : '_cke_saved_name',
+ id : 'name',
type : 'text',
label : editor.lang.common.name,
'default' : '',
setup : function( element )
{
this.setValue(
- element.getAttribute( '_cke_saved_name' ) ||
+ element.data( 'cke-saved-name' ) ||
element.getAttribute( 'name' ) ||
'' );
},
- commit : function( data )
- {
- var element = data.element;
-
- if ( this.getValue() )
- element.setAttribute( '_cke_saved_name', this.getValue() );
- else
- {
- element.removeAttribute( '_cke_saved_name' );
- element.removeAttribute( 'name' );
- }
- }
+ commit : commitAttributes
},
{
id : 'value',
@@ -79,15 +91,7 @@ CKEDITOR.dialog.add( 'button', function( editor ) {
this.setValue( element.getAttribute( 'value' ) || '' );
},
- commit : function( data )
- {
- var element = data.element;
-
- if ( this.getValue() )
- element.setAttribute( 'value', this.getValue() );
- else
- element.removeAttribute( 'value' );
- }
+ commit : commitAttributes
},
{
id : 'type',
@@ -105,28 +109,7 @@ CKEDITOR.dialog.add( 'button', function( editor ) {
this.setValue( element.getAttribute( 'type' ) || '' );
},
- commit : function( data )
- {
- var element = data.element;
-
- if ( CKEDITOR.env.ie )
- {
- var elementType = element.getAttribute( 'type' );
- var currentType = this.getValue();
-
- if ( currentType != elementType )
- {
- var replace = CKEDITOR.dom.element.createFromHtml( '<input type="' + currentType +
- '"></input>', editor.document );
- element.copyAttributes( replace, { type : 1 } );
- replace.replace( element );
- editor.getSelection().selectElement( replace );
- data.element = replace;
- }
- }
- else
- element.setAttribute( 'type', this.getValue() );
- }
+ commit : commitAttributes
}
]
}
diff --git a/_source/plugins/forms/dialogs/checkbox.js b/_source/plugins/forms/dialogs/checkbox.js index 980ae08..5a44046 100644 --- a/_source/plugins/forms/dialogs/checkbox.js +++ b/_source/plugins/forms/dialogs/checkbox.js @@ -1,155 +1,153 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ -CKEDITOR.dialog.add( 'checkbox', function( editor ) -{ - return { - title : editor.lang.checkboxAndRadio.checkboxTitle, - minWidth : 350, - minHeight : 140, - onShow : function() - { - delete this.checkbox; - - var element = this.getParentEditor().getSelection().getSelectedElement(); - - if ( element && element.getAttribute( 'type' ) == "checkbox" ) - { - this.checkbox = element; - this.setupContent( element ); - } - }, - onOk : function() - { - var editor, - element = this.checkbox, - isInsertMode = !element; - - if ( isInsertMode ) - { - editor = this.getParentEditor(); - element = editor.document.createElement( 'input' ); - element.setAttribute( 'type', 'checkbox' ); - } - - if ( isInsertMode ) - 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.getAttribute( '_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.setAttribute( '_cke_saved_name', this.getValue() ); - else - { - element.removeAttribute( '_cke_saved_name' ); - 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' ); - var 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' ); - } - } - } - ] - } - ] - }; -}); +/*
+Copyright (c) 2003-2012, 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' );
+ }
+ }
+ }
+ ]
+ }
+ ]
+ };
+});
diff --git a/_source/plugins/forms/dialogs/form.js b/_source/plugins/forms/dialogs/form.js index 299e3c2..c637ab3 100644 --- a/_source/plugins/forms/dialogs/form.js +++ b/_source/plugins/forms/dialogs/form.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.dialog.add( 'form', function( editor )
@@ -39,7 +39,7 @@ CKEDITOR.dialog.add( 'form', function( editor ) {
editor = this.getParentEditor();
element = editor.document.createElement( 'form' );
- element.append( editor.document.createElement( 'br' ) );
+ !CKEDITOR.env.ie && element.append( editor.document.createElement( 'br' ) );
}
if ( isInsertMode )
@@ -84,17 +84,17 @@ CKEDITOR.dialog.add( 'form', function( editor ) accessKey : 'N',
setup : function( element )
{
- this.setValue( element.getAttribute( '_cke_saved_name' ) ||
+ this.setValue( element.data( 'cke-saved-name' ) ||
element.getAttribute( 'name' ) ||
'' );
},
commit : function( element )
{
if ( this.getValue() )
- element.setAttribute( '_cke_saved_name', this.getValue() );
+ element.data( 'cke-saved-name', this.getValue() );
else
{
- element.removeAttribute( '_cke_saved_name' );
+ element.data( 'cke-saved-name', false );
element.removeAttribute( 'name' );
}
}
@@ -104,7 +104,7 @@ CKEDITOR.dialog.add( 'form', function( editor ) type : 'text',
label : editor.lang.form.action,
'default' : '',
- accessKey : 'A'
+ accessKey : 'T'
},
{
type : 'hbox',
diff --git a/_source/plugins/forms/dialogs/hiddenfield.js b/_source/plugins/forms/dialogs/hiddenfield.js index 3e31706..2f629a9 100644 --- a/_source/plugins/forms/dialogs/hiddenfield.js +++ b/_source/plugins/forms/dialogs/hiddenfield.js @@ -1,98 +1,100 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ -CKEDITOR.dialog.add( 'hiddenfield', function( editor ) -{ - return { - title : editor.lang.hidden.title, - hiddenField : null, - minWidth : 350, - minHeight : 110, - onShow : function() - { - delete this.hiddenField; - - var editor = this.getParentEditor(), - selection = editor.getSelection(), - element = selection.getSelectedElement(); - - if ( element && element.getAttribute( '_cke_real_element_type' ) && element.getAttribute( '_cke_real_element_type' ) == 'hiddenfield' ) - { - this.hiddenField = element; - element = editor.restoreRealElement( this.hiddenField ); - this.setupContent( element ); - selection.selectElement( this.hiddenField ); - } - }, - onOk : function() - { - var name = this.getValueOf( 'info', '_cke_saved_name' ), - value = this.getValueOf( 'info', 'value' ), - editor = this.getParentEditor(), - element = CKEDITOR.env.ie ? editor.document.createElement( '<input name="' + CKEDITOR.tools.htmlEncode( name ) + '">' ) : editor.document.createElement( 'input' ); - - element.setAttribute( 'type', 'hidden' ); - this.commitContent( element ); - var fakeElement = editor.createFakeElement( element, 'cke_hidden', 'hiddenfield' ); - if ( !this.hiddenField ) - editor.insertElement( fakeElement ); - else - { - fakeElement.replace( this.hiddenField ); - editor.getSelection().selectElement( fakeElement ); - } - return true; - }, - contents : [ - { - id : 'info', - label : editor.lang.hidden.title, - title : editor.lang.hidden.title, - elements : [ - { - id : '_cke_saved_name', - type : 'text', - label : editor.lang.hidden.name, - 'default' : '', - accessKey : 'N', - setup : function( element ) - { - this.setValue( - element.getAttribute( '_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' ); - } - } - ] - } - ] - }; -}); +/*
+Copyright (c) 2003-2012, 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' );
+ }
+ }
+ ]
+ }
+ ]
+ };
+});
diff --git a/_source/plugins/forms/dialogs/radio.js b/_source/plugins/forms/dialogs/radio.js index a967388..9ea0673 100644 --- a/_source/plugins/forms/dialogs/radio.js +++ b/_source/plugins/forms/dialogs/radio.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.dialog.add( 'radio', function( editor )
@@ -13,7 +13,7 @@ CKEDITOR.dialog.add( 'radio', function( editor ) delete this.radioButton;
var element = this.getParentEditor().getSelection().getSelectedElement();
- if ( element && element.getName() == "input" && element.getAttribute( 'type' ) == "radio" )
+ if ( element && element.getName() == 'input' && element.getAttribute( 'type' ) == 'radio' )
{
this.radioButton = element;
this.setupContent( element );
@@ -51,7 +51,7 @@ CKEDITOR.dialog.add( 'radio', function( editor ) setup : function( element )
{
this.setValue(
- element.getAttribute( '_cke_saved_name' ) ||
+ element.data( 'cke-saved-name' ) ||
element.getAttribute( 'name' ) ||
'' );
},
@@ -60,10 +60,10 @@ CKEDITOR.dialog.add( 'radio', function( editor ) var element = data.element;
if ( this.getValue() )
- element.setAttribute( '_cke_saved_name', this.getValue() );
+ element.data( 'cke-saved-name', this.getValue() );
else
{
- element.removeAttribute( '_cke_saved_name' );
+ element.data( 'cke-saved-name', false );
element.removeAttribute( 'name' );
}
}
@@ -103,7 +103,7 @@ CKEDITOR.dialog.add( 'radio', function( editor ) {
var element = data.element;
- if ( !CKEDITOR.env.ie )
+ if ( !( CKEDITOR.env.ie || CKEDITOR.env.opera ) )
{
if ( this.getValue() )
element.setAttribute( 'checked', 'checked' );
diff --git a/_source/plugins/forms/dialogs/select.js b/_source/plugins/forms/dialogs/select.js index 228a05e..0117ec9 100644 --- a/_source/plugins/forms/dialogs/select.js +++ b/_source/plugins/forms/dialogs/select.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.dialog.add( 'select', function( editor )
@@ -161,12 +161,12 @@ CKEDITOR.dialog.add( 'select', function( editor ) if ( isInsertMode )
{
- editor.insertElement(element);
+ editor.insertElement( element );
if ( CKEDITOR.env.ie )
{
var sel = editor.getSelection(),
bms = sel.createBookmarks();
- setTimeout(function ()
+ setTimeout(function()
{
sel.selectBookmarks( bms );
}, 0 );
@@ -188,16 +188,15 @@ CKEDITOR.dialog.add( 'select', function( editor ) label : editor.lang.common.name,
'default' : '',
accessKey : 'N',
- align : 'center',
style : 'width:350px',
setup : function( name, element )
{
if ( name == 'clear' )
- this.setValue( '' );
+ this.setValue( this[ 'default' ] || '' );
else if ( name == 'select' )
{
this.setValue(
- element.getAttribute( '_cke_saved_name' ) ||
+ element.data( 'cke-saved-name' ) ||
element.getAttribute( 'name' ) ||
'' );
}
@@ -205,10 +204,10 @@ CKEDITOR.dialog.add( 'select', function( editor ) commit : function( element )
{
if ( this.getValue() )
- element.setAttribute( '_cke_saved_name', this.getValue() );
+ element.data( 'cke-saved-name', this.getValue() );
else
{
- element.removeAttribute( '_cke_saved_name' ) ;
+ element.data( 'cke-saved-name', false );
element.removeAttribute( 'name' );
}
}
@@ -237,13 +236,11 @@ CKEDITOR.dialog.add( 'select', function( editor ) {
type : 'hbox',
widths : [ '175px', '170px' ],
- align : 'center',
children :
[
{
id : 'txtSize',
type : 'text',
- align : 'center',
labelLayout : 'horizontal',
label : editor.lang.select.size,
'default' : '',
@@ -282,7 +279,6 @@ CKEDITOR.dialog.add( 'select', function( editor ) {
type : 'hbox',
widths : [ '115px', '115px' ,'100px' ],
- align : 'top',
children :
[
{
@@ -408,6 +404,7 @@ CKEDITOR.dialog.add( 'select', function( editor ) [
{
type : 'button',
+ id : 'btnAdd',
style : '',
label : editor.lang.select.btnAdd,
title : editor.lang.select.btnAdd,
@@ -431,6 +428,7 @@ CKEDITOR.dialog.add( 'select', function( editor ) },
{
type : 'button',
+ id : 'btnModify',
label : editor.lang.select.btnModify,
title : editor.lang.select.btnModify,
style : 'width:100%;',
@@ -453,6 +451,7 @@ CKEDITOR.dialog.add( 'select', function( editor ) },
{
type : 'button',
+ id : 'btnUp',
style : 'width:100%;',
label : editor.lang.select.btnUp,
title : editor.lang.select.btnUp,
@@ -469,6 +468,7 @@ CKEDITOR.dialog.add( 'select', function( editor ) },
{
type : 'button',
+ id : 'btnDown',
style : 'width:100%;',
label : editor.lang.select.btnDown,
title : editor.lang.select.btnDown,
@@ -494,6 +494,7 @@ CKEDITOR.dialog.add( 'select', function( editor ) [
{
type : 'button',
+ id : 'btnSetValue',
label : editor.lang.select.btnSetValue,
title : editor.lang.select.btnSetValue,
onClick : function()
@@ -507,6 +508,7 @@ CKEDITOR.dialog.add( 'select', function( editor ) },
{
type : 'button',
+ id : 'btnDelete',
label : editor.lang.select.btnDelete,
title : editor.lang.select.btnDelete,
onClick : function()
diff --git a/_source/plugins/forms/dialogs/textarea.js b/_source/plugins/forms/dialogs/textarea.js index a8ba271..cae945d 100644 --- a/_source/plugins/forms/dialogs/textarea.js +++ b/_source/plugins/forms/dialogs/textarea.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.dialog.add( 'textarea', function( editor )
@@ -7,7 +7,7 @@ CKEDITOR.dialog.add( 'textarea', function( editor ) return {
title : editor.lang.textarea.title,
minWidth : 350,
- minHeight : 150,
+ minHeight : 220,
onShow : function()
{
delete this.textarea;
@@ -50,63 +50,84 @@ CKEDITOR.dialog.add( 'textarea', function( editor ) setup : function( element )
{
this.setValue(
- element.getAttribute( '_cke_saved_name' ) ||
+ element.data( 'cke-saved-name' ) ||
element.getAttribute( 'name' ) ||
'' );
},
commit : function( element )
{
if ( this.getValue() )
- element.setAttribute( '_cke_saved_name', this.getValue() );
+ element.data( 'cke-saved-name', this.getValue() );
else
{
- element.removeAttribute( '_cke_saved_name' );
+ element.data( 'cke-saved-name', false );
element.removeAttribute( 'name' );
}
}
},
{
- id : 'cols',
- type : 'text',
- label : editor.lang.textarea.cols,
- 'default' : '',
- accessKey : 'C',
- style : 'width:50px',
- validate : CKEDITOR.dialog.validate.integer( editor.lang.common.validateNumberFailed ),
- setup : function( element )
- {
- var value = element.hasAttribute( 'cols' ) && element.getAttribute( 'cols' );
- this.setValue( value || '' );
- },
- commit : function( element )
- {
- if ( this.getValue() )
- element.setAttribute( 'cols', this.getValue() );
- else
- element.removeAttribute( 'cols' );
- }
+ 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 : 'rows',
- type : 'text',
- label : editor.lang.textarea.rows,
+ id : 'value',
+ type : 'textarea',
+ label : editor.lang.textfield.value,
'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 || '' );
+ this.setValue( element.$.defaultValue );
},
commit : function( element )
{
- if ( this.getValue() )
- element.setAttribute( 'rows', this.getValue() );
- else
- element.removeAttribute( 'rows' );
+ element.$.value = element.$.defaultValue = this.getValue() ;
}
}
+
]
}
]
diff --git a/_source/plugins/forms/dialogs/textfield.js b/_source/plugins/forms/dialogs/textfield.js index d54cb28..877edb8 100644 --- a/_source/plugins/forms/dialogs/textfield.js +++ b/_source/plugins/forms/dialogs/textfield.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.dialog.add( 'textfield', function( editor )
@@ -98,7 +98,7 @@ CKEDITOR.dialog.add( 'textfield', function( editor ) setup : function( element )
{
this.setValue(
- element.getAttribute( '_cke_saved_name' ) ||
+ element.data( 'cke-saved-name' ) ||
element.getAttribute( 'name' ) ||
'' );
},
@@ -107,10 +107,10 @@ CKEDITOR.dialog.add( 'textfield', function( editor ) var element = data.element;
if ( this.getValue() )
- element.setAttribute( '_cke_saved_name', this.getValue() );
+ element.data( 'cke-saved-name', this.getValue() );
else
{
- element.removeAttribute( '_cke_saved_name' );
+ element.data( 'cke-saved-name', false );
element.removeAttribute( 'name' );
}
}
@@ -147,7 +147,13 @@ CKEDITOR.dialog.add( 'textfield', function( editor ) 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',
diff --git a/_source/plugins/forms/plugin.js b/_source/plugins/forms/plugin.js index f82dbdf..8c247c4 100644 --- a/_source/plugins/forms/plugin.js +++ b/_source/plugins/forms/plugin.js @@ -1,281 +1,288 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -/** - * @file Forms Plugin - */ - -CKEDITOR.plugins.add( 'forms', -{ - init : function( editor ) - { - var lang = editor.lang; - - editor.addCss( - 'form' + - '{' + - 'border: 1px dotted #FF0000;' + - 'padding: 2px;' + - '}\n' ); - - editor.addCss( - 'img.cke_hidden' + - '{' + - 'background-image: url(' + CKEDITOR.getUrl( this.path + 'images/hiddenfield.gif' ) + ');' + - 'background-position: center center;' + - 'background-repeat: no-repeat;' + - 'border: 1px solid #a9a9a9;' + - 'width: 16px;' + - 'height: 16px;' + - '}' ); - - // 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 ) ) - return { form : CKEDITOR.TRISTATE_OFF }; - }); - - editor.contextMenu.addListener( function( element ) - { - if ( element ) - { - var name = element.getName(); - - if ( name == 'select' ) - return { select : CKEDITOR.TRISTATE_OFF }; - - if ( name == 'textarea' ) - return { textarea : CKEDITOR.TRISTATE_OFF }; - - if ( name == 'input' ) - { - var type = element.getAttribute( 'type' ); - - if ( type == 'text' || type == 'password' ) - return { textfield : CKEDITOR.TRISTATE_OFF }; - - if ( type == 'button' || type == 'submit' || type == 'reset' ) - return { button : CKEDITOR.TRISTATE_OFF }; - - if ( type == 'checkbox' ) - return { checkbox : CKEDITOR.TRISTATE_OFF }; - - if ( type == 'radio' ) - return { radio : CKEDITOR.TRISTATE_OFF }; - - if ( type == 'image' ) - return { imagebutton : CKEDITOR.TRISTATE_OFF }; - } - - if ( name == 'img' && element.getAttribute( '_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.getAttribute( '_cke_real_element_type' ) == 'hiddenfield' ) - evt.data.dialog = 'hiddenfield'; - else if ( element.is( 'input' ) ) - { - var type = element.getAttribute( 'type' ); - - switch ( type ) - { - case 'text' : case 'password': - evt.data.dialog = 'textfield'; - break; - case 'button' : case 'submit' : case 'reset' : - evt.data.dialog = 'button'; - break; - case 'checkbox' : - evt.data.dialog = 'checkbox'; - break; - case 'radio' : - evt.data.dialog = 'radio'; - break; - case 'image' : - evt.data.dialog = 'imagebutton'; - break; - } - } - }); - }, - - afterInit : function( editor ) - { - var dataProcessor = editor.dataProcessor, - htmlFilter = dataProcessor && dataProcessor.htmlFilter, - dataFilter = dataProcessor && dataProcessor.dataFilter; - - // Cleanup certain IE form elements default values. - if ( CKEDITOR.env.ie ) - { - htmlFilter && htmlFilter.addRules( - { - elements : - { - input : function( input ) - { - var attrs = input.attributes, - type = attrs.type; - if ( type == 'checkbox' || type == 'radio' ) - attrs.value == 'on' && delete attrs.value; - } - } - } ); - } - - if ( dataFilter ) - { - dataFilter.addRules( - { - elements : - { - input : function( element ) - { - if ( element.attributes.type == 'hidden' ) - return editor.createFakeParserElement( element, 'cke_hidden', 'hiddenfield' ); - } - } - } ); - } - }, - requires : [ 'image', 'fakeobjects' ] -} ); - -if ( CKEDITOR.env.ie ) -{ - CKEDITOR.dom.element.prototype.hasAttribute = function( name ) - { - var $attr = this.$.attributes.getNamedItem( name ); - - if ( this.getName() == 'input' ) - { - switch ( name ) - { - case 'class' : - return this.$.className.length > 0; - case 'checked' : - return !!this.$.checked; - case 'value' : - var type = this.getAttribute( 'type' ); - if ( type == 'checkbox' || type == 'radio' ) - return this.$.value != 'on'; - break; - default: - } - } - - return !!( $attr && $attr.specified ); - }; -} +/*
+Copyright (c) 2003-2012, 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 );
+ };
+ });
+}
diff --git a/_source/plugins/horizontalrule/plugin.js b/_source/plugins/horizontalrule/plugin.js index a5d1edd..7d8bde2 100644 --- a/_source/plugins/horizontalrule/plugin.js +++ b/_source/plugins/horizontalrule/plugin.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
@@ -14,7 +14,19 @@ For licensing, see LICENSE.html or http://ckeditor.com/license canUndo : false, // The undo snapshot will be handled by 'insertElement'.
exec : function( editor )
{
- editor.insertElement( editor.document.createElement( 'hr' ) );
+ 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();
}
};
diff --git a/_source/plugins/htmldataprocessor/plugin.js b/_source/plugins/htmldataprocessor/plugin.js index 503cae5..ecf87ed 100644 --- a/_source/plugins/htmldataprocessor/plugin.js +++ b/_source/plugins/htmldataprocessor/plugin.js @@ -1,485 +1,600 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -(function() -{ - // Regex to scan for at the end of blocks, which are actually placeholders. - // Safari transforms the to \xa0. (#4172) - var tailNbspRegex = /^[\t\r\n ]*(?: |\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 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 ) - { - var lastChild = lastNoneSpaceChild( block ); - - return !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 extendBlockForDisplay( block ) - { - trimFillers( block, true ); - - if ( blockNeedsExtension( block ) ) - { - if ( CKEDITOR.env.ie ) - block.add( new CKEDITOR.htmlParser.text( '\xa0' ) ); - else - block.add( new CKEDITOR.htmlParser.element( 'br', {} ) ); - } - } - - function extendBlockForOutput( block ) - { - trimFillers( block ); - - if ( blockNeedsExtension( block ) ) - block.add( new CKEDITOR.htmlParser.text( '\xa0' ) ); - } - - var dtd = CKEDITOR.dtd; - - // Find out the list of block-like tags that can contain <br>. - var blockLikeTags = CKEDITOR.tools.extend( {}, dtd.$block, dtd.$listItem, dtd.$tableContent ); - for ( var i in blockLikeTags ) - { - if ( ! ( 'br' in dtd[i] ) ) - delete blockLikeTags[i]; - } - // We just avoid filler in <pre> right now. - // TODO: Support filler for <pre>, line break is also occupy line height. - delete blockLikeTags.pre; - var defaultDataFilterRules = - { - elements : {}, - attributeNames : - [ - // Event attributes (onXYZ) must not be directly set. They can become - // active in the editing area (IE|WebKit). - [ ( /^on/ ), '_cke_pa_on' ] - ] - }; - - var defaultDataBlockFilterRules = { elements : {} }; - - for ( i in blockLikeTags ) - defaultDataBlockFilterRules.elements[ i ] = extendBlockForDisplay; - - var defaultHtmlFilterRules = - { - elementNames : - [ - // Remove the "cke:" namespace prefix. - [ ( /^cke:/ ), '' ], - - // Ignore <?xml:namespace> tags. - [ ( /^\?xml:namespace$/ ), '' ] - ], - - attributeNames : - [ - // Attributes saved for changes and protected attributes. - [ ( /^_cke_(saved|pa)_/ ), '' ], - - // All "_cke" attributes are to be ignored. - [ ( /^_cke.*/ ), '' ], - - [ 'hidefocus', '' ] - ], - - elements : - { - $ : function( element ) - { - var attribs = element.attributes; - - if ( attribs ) - { - // Elements marked as temporary are to be ignored. - if ( attribs.cke_temp ) - return false; - - // Remove duplicated attributes - #3789. - var attributeNames = [ 'name', 'href', 'src' ], - savedAttributeName; - for ( var i = 0 ; i < attributeNames.length ; i++ ) - { - savedAttributeName = '_cke_saved_' + attributeNames[ i ]; - savedAttributeName in attribs && ( delete attribs[ attributeNames[ i ] ] ); - } - } - - return element; - }, - - embed : function( element ) - { - var parent = element.parent; - - // If the <embed> is child of a <object>, copy the width - // and height attributes from it. - if ( parent && parent.name == 'object' ) - { - var parentWidth = parent.attributes.width, - parentHeight = parent.attributes.height; - parentWidth && ( element.attributes.width = parentWidth ); - parentHeight && ( element.attributes.height = parentHeight ); - } - }, - // Restore param elements into self-closing. - param : function( param ) - { - param.children = []; - param.isEmpty = true; - return param; - }, - - // Remove empty link but not empty anchor.(#3829) - a : function( element ) - { - if ( !( element.children.length || - element.attributes.name || - element.attributes._cke_saved_name ) ) - { - return false; - } - }, - - 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 ) - { - element.children[ 0 ].value = element.attributes[ '_cke_title' ]; - } - }, - - attributes : - { - 'class' : function( value, element ) - { - // Remove all class names starting with "cke_". - return CKEDITOR.tools.ltrim( value.replace( /(?:^|\s+)cke_[^\s]*/g, '' ) ) || false; - } - }, - - comment : function( contents ) - { - // If this is a comment for protected source. - if ( contents.substr( 0, protectedSourceMarker.length ) == protectedSourceMarker ) - { - // Remove the extra marker for real comments from it. - if ( contents.substr( protectedSourceMarker.length, 3 ) == '{C}' ) - contents = contents.substr( protectedSourceMarker.length + 3 ); - else - contents = contents.substr( protectedSourceMarker.length ); - - return new CKEDITOR.htmlParser.cdata( decodeURIComponent( contents ) ); - } - - return contents; - } - }; - - var defaultHtmlBlockFilterRules = { elements : {} }; - - for ( i in blockLikeTags ) - defaultHtmlBlockFilterRules.elements[ i ] = extendBlockForOutput; - - if ( CKEDITOR.env.ie ) - { - // IE outputs style attribute in capital letters. We should convert - // them back to lower case. - defaultHtmlFilterRules.attributes.style = function( value, element ) - { - return value.toLowerCase(); - }; - } - - function protectReadOnly( element ) - { - element.attributes.contenteditable = "false"; - } - function unprotectReadyOnly( element ) - { - delete element.attributes.contenteditable; - } - // Disable form elements editing mode provided by some browers. (#5746) - for ( i in { input : 1, textarea : 1 } ) - { - defaultDataFilterRules.elements[ i ] = protectReadOnly; - defaultHtmlFilterRules.elements[ i ] = unprotectReadyOnly; - } - - var protectAttributeRegex = /<(?:a|area|img|input)[\s\S]*?\s((?: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( protectAttributeRegex, '$& _cke_saved_$1' ); - } - - 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 protectRealComments( html ) - { - return html.replace( /<!--(?!{cke_protected})[\s\S]+?-->/g, function( match ) - { - return '<!--' + protectedSourceMarker + - '{C}' + - encodeURIComponent( match ).replace( /--/g, '%2D%2D' ) + - '-->'; - }); - } - - function unprotectRealComments( html ) - { - return html.replace( /<!--\{cke_protected\}\{C\}([\s\S]+?)-->/g, function( match, data ) - { - return decodeURIComponent( data ); - }); - } - - function protectSource( data, protectRegexes ) - { - var protectedHtml = [], - tempRegex = /<\!--\{cke_temp(comment)?\}(\d*?)-->/g; - - var regexes = - [ - // Script tags will also be forced to be protected, otherwise - // IE will execute them. - ( /<script[\s\S]*?<\/script>/gi ), - - // <noscript> tags (get lost in IE and messed up in FF). - /<noscript[\s\S]*?<\/noscript>/gi - ] - .concat( protectRegexes ); - - // First of any other protection, we must protect all comments - // to avoid loosing them (of course, IE related). - // Note that we use a different tag for comments, as we need to - // transform them when applying filters. - data = data.replace( (/<!--[\s\S]*?-->/g), function( match ) - { - return '<!--{cke_tempcomment}' + ( protectedHtml.push( match ) - 1 ) + '-->'; - }); - - for ( var i = 0 ; i < regexes.length ; i++ ) - { - data = data.replace( regexes[i], function( match ) - { - match = match.replace( tempRegex, // There could be protected source inside another one. (#3869). - function( $, isComment, id ) - { - return protectedHtml[ id ]; - } - ); - return '<!--{cke_temp}' + ( protectedHtml.push( match ) - 1 ) + '-->'; - }); - } - data = data.replace( tempRegex, function( $, isComment, id ) - { - return '<!--' + protectedSourceMarker + - ( isComment ? '{C}' : '' ) + - encodeURIComponent( protectedHtml[ id ] ).replace( /--/g, '%2D%2D' ) + - '-->'; - } - ); - return data; - } - - CKEDITOR.plugins.add( 'htmldataprocessor', - { - requires : [ 'htmlwriter' ], - - init : function( editor ) - { - var dataProcessor = editor.dataProcessor = new CKEDITOR.htmlDataProcessor( editor ); - - dataProcessor.writer.forceSimpleAmpersand = editor.config.forceSimpleAmpersand; - - dataProcessor.dataFilter.addRules( defaultDataFilterRules ); - dataProcessor.dataFilter.addRules( defaultDataBlockFilterRules ); - dataProcessor.htmlFilter.addRules( defaultHtmlFilterRules ); - dataProcessor.htmlFilter.addRules( defaultHtmlBlockFilterRules ); - } - }); - - CKEDITOR.htmlDataProcessor = function( editor ) - { - this.editor = editor; - - this.writer = new CKEDITOR.htmlWriter(); - this.dataFilter = new CKEDITOR.htmlParser.filter(); - this.htmlFilter = new CKEDITOR.htmlParser.filter(); - }; - - CKEDITOR.htmlDataProcessor.prototype = - { - toHtml : function( data, fixForBody ) - { - // The source data is already HTML, but we need to clean - // it up and apply the filter. - - data = protectSource( data, this.editor.config.protectedSource ); - - // Before anything, we must protect the URL attributes as the - // browser may changing them when setting the innerHTML later in - // the code. - data = protectAttributes( data ); - - // Protect elements than can't be set inside a DIV. E.g. IE removes - // style tags from innerHTML. (#3710) - data = protectElements( data ); - - // Certain elements has problem to go through DOM operation, protect - // them by prefixing 'cke' namespace. (#3591) - data = protectElementsNames( data ); - - // All none-IE browsers ignore self-closed custom elements, - // protecting them into open-close. (#3591) - data = protectSelfClosingElements( data ); - - // Call the browser to help us fixing a possibly invalid HTML - // structure. - var div = new CKEDITOR.dom.element( 'div' ); - // Add fake character to workaround IE comments bug. (#3801) - div.setHtml( 'a' + data ); - data = div.getHtml().substr( 1 ); - - // Unprotect "some" of the protected elements at this point. - data = unprotectElementNames( data ); - - data = unprotectElements( data ); - - // Restore the comments that have been protected, in this way they - // can be properly filtered. - data = unprotectRealComments( data ); - - // Now use our parser to make further fixes to the structure, as - // well as apply the filter. - var fragment = CKEDITOR.htmlParser.fragment.fromHtml( data, fixForBody ), - writer = new CKEDITOR.htmlParser.basicWriter(); - - fragment.writeHtml( writer, this.dataFilter ); - data = writer.getHtml( true ); - - // Protect the real comments again. - data = protectRealComments( data ); - - return data; - }, - - toDataFormat : function( html, fixForBody ) - { - var writer = this.writer, - fragment = CKEDITOR.htmlParser.fragment.fromHtml( html, fixForBody ); - - writer.reset(); - - fragment.writeHtml( writer, this.htmlFilter ); - - return writer.getHtml( true ); - } - }; -})(); - -/** - * Whether to force using "&" instead of "&amp;" 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>). - * @type Boolean - * @default false - * @example - * config.forceSimpleAmpersand = false; - */ -CKEDITOR.config.forceSimpleAmpersand = false; +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+(function()
+{
+ // Regex to scan for at the end of blocks, which are actually placeholders.
+ // Safari transforms the to \xa0. (#4172)
+ var tailNbspRegex = /^[\t\r\n ]*(?: |\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 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 ( ).
+ 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 + ' data-cke-' + CKEDITOR.rnd + '-' + 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 );
+
+ // Restore shortly protected attribute names.
+ data = data.replace( new RegExp( ' data-cke-' + CKEDITOR.rnd + '-', 'ig' ), ' ' );
+
+ // 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;" 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 - ) 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;
+ * }
+ */
diff --git a/_source/plugins/htmlwriter/plugin.js b/_source/plugins/htmlwriter/plugin.js index 0348855..32acfb0 100644 --- a/_source/plugins/htmlwriter/plugin.js +++ b/_source/plugins/htmlwriter/plugin.js @@ -1,314 +1,319 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -CKEDITOR.plugins.add( 'htmlwriter' ); - -/** - * Class used to write HTML data. - * @constructor - * @example - * var writer = new CKEDITOR.htmlWriter(); - * writer.openTag( 'p' ); - * writer.attribute( 'class', 'MyClass' ); - * writer.openTagClose( 'p' ); - * writer.text( 'Hello' ); - * writer.closeTag( 'p' ); - * alert( writer.getHtml() ); "<p class="MyClass">Hello</p>" - */ -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 " />" - * @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 = false; - - this.sortAttributes = true; - - this._.indent = false; - this._.indentation = ''; - 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 : true, - breakBeforeOpen : true, - breakAfterOpen : true, - breakBeforeClose : !dtd[ e ][ '#' ], - breakAfterClose : true - }); - } - - this.setRules( 'br', - { - breakAfterOpen : true - }); - - this.setRules( 'title', - { - indent : false, - breakAfterOpen : false - }); - - this.setRules( 'style', - { - indent : false, - breakBeforeClose : true - }); - - // Disable indentation on <pre>. - this.setRules( 'pre', - { - indent: false - }); - }, - - 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 "<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 ">". - * writer.openTagClose( 'p', false ); - * @example - * // Writes " />". - * 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(); - }, - - /** - * 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( /&/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 "</p>". - * 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, '>' ); - - 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(); - text = CKEDITOR.tools.ltrim( text ); - } - - this._.output.push( text ); - }, - - /** - * Writes a comment. - * @param {String} comment The comment text. - * @example - * // Writes "<!-- My comment -->". - * 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._.output.length > 0 ) - this._.output.push( this.lineBreakChars ); - this._.indent = true; - }, - - /** - * 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() - { - this._.output.push( this._.indentation ); - this._.indent = false; - }, - - /** - * 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; - } - } -}); +/*
+Copyright (c) 2003-2012, 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() ); "<p class="MyClass">Hello</p>"
+ */
+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 " />"
+ * @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 "<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 ">".
+ * writer.openTagClose( 'p', false );
+ * @example
+ * // Writes " />".
+ * 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( /&/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 "</p>".
+ * 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 "<!-- My comment -->".
+ * 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;
+ }
+ }
+});
diff --git a/_source/plugins/iframe/dialogs/iframe.js b/_source/plugins/iframe/dialogs/iframe.js new file mode 100644 index 0000000..4c97c66 --- /dev/null +++ b/_source/plugins/iframe/dialogs/iframe.js @@ -0,0 +1,229 @@ +/*
+Copyright (c) 2003-2012, 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 })
+ ]
+ };
+ });
+})();
diff --git a/_source/plugins/iframe/images/placeholder.png b/_source/plugins/iframe/images/placeholder.png Binary files differnew file mode 100644 index 0000000..b40c7b2 --- /dev/null +++ b/_source/plugins/iframe/images/placeholder.png diff --git a/_source/plugins/iframe/plugin.js b/_source/plugins/iframe/plugin.js new file mode 100644 index 0000000..23fb764 --- /dev/null +++ b/_source/plugins/iframe/plugin.js @@ -0,0 +1,87 @@ +/*
+Copyright (c) 2003-2012, 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 );
+ }
+ }
+ });
+ }
+ }
+ });
+})();
diff --git a/_source/plugins/iframedialog/plugin.js b/_source/plugins/iframedialog/plugin.js index dcde6fe..61ee605 100644 --- a/_source/plugins/iframedialog/plugin.js +++ b/_source/plugins/iframedialog/plugin.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
@@ -12,7 +12,19 @@ CKEDITOR.plugins.add( 'iframedialog', requires : [ 'dialog' ],
onLoad : function()
{
- CKEDITOR.dialog.addIframe = function( name, title, src, width, height, onContentLoad )
+ /**
+ * 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 =
{
@@ -24,12 +36,49 @@ CKEDITOR.plugins.add( 'iframedialog', 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 : width,
- minHeight : height,
+ minWidth : minWidth,
+ minHeight : minHeight,
contents :
[
{
@@ -41,7 +90,10 @@ CKEDITOR.plugins.add( 'iframedialog', ]
};
- return this.add( name, function(){ return definition; } );
+ for ( var i in userDefinition )
+ definition[i] = userDefinition[i];
+
+ this.add( name, function(){ return definition; } );
};
(function()
@@ -53,7 +105,7 @@ CKEDITOR.plugins.add( 'iframedialog', * @constructor
* @param {CKEDITOR.dialog} dialog
* Parent dialog object.
- * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition
+ * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition
* The element definition. Accepted fields:
* <ul>
* <li><strong>src</strong> (Required) The src field of the iframe. </li>
@@ -74,7 +126,7 @@ CKEDITOR.plugins.add( 'iframedialog', contentLoad = elementDefinition.onContentLoad && CKEDITOR.tools.bind( elementDefinition.onContentLoad, this ),
cssWidth = CKEDITOR.tools.cssLength( elementDefinition.width ),
cssHeight = CKEDITOR.tools.cssLength( elementDefinition.height );
- _.frameId = CKEDITOR.tools.getNextNumber() + '_iframe';
+ _.frameId = CKEDITOR.tools.getNextId() + '_iframe';
// IE BUG: Parent container does not resize to contain the iframe automatically.
dialog.on( 'load', function()
diff --git a/_source/plugins/image/dialogs/image.js b/_source/plugins/image/dialogs/image.js index decce28..992d700 100644 --- a/_source/plugins/image/dialogs/image.js +++ b/_source/plugins/image/dialogs/image.js @@ -1,1394 +1,1406 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -(function() -{ - var imageDialog = function( editor, dialogType ) - { - // Load image preview. - var IMAGE = 1, - LINK = 2, - PREVIEW = 4, - CLEANUP = 8, - regexGetSize = /^\s*(\d+)((px)|\%)?\s*$/i, - regexGetSizeOrEmpty = /(^\s*(\d+)((px)|\%)?\s*$)|^$/i, - pxLengthRegex = /^\d+px$/; - - var onSizeChange = function() - { - var value = this.getValue(), // This = input element. - dialog = this.getDialog(), - aMatch = value.match( regexGetSize ); // Check value - if ( aMatch ) - { - if ( aMatch[2] == '%' ) // % is allowed - > unlock ratio. - switchLockRatio( dialog, false ); // Unlock. - value = aMatch[1]; - } - - // Only if ratio is locked - if ( dialog.lockRatio ) - { - var oImageOriginal = dialog.originalElement; - if ( oImageOriginal.getCustomData( 'isReady' ) == 'true' ) - { - if ( this.id == 'txtHeight' ) - { - if ( value && value != '0' ) - value = Math.round( oImageOriginal.$.width * ( value / oImageOriginal.$.height ) ); - if ( !isNaN( value ) ) - dialog.setValueOf( 'info', 'txtWidth', value ); - } - else //this.id = txtWidth. - { - if ( value && value != '0' ) - value = Math.round( oImageOriginal.$.height * ( value / oImageOriginal.$.width ) ); - if ( !isNaN( value ) ) - dialog.setValueOf( 'info', 'txtHeight', value ); - } - } - } - updatePreview( dialog ); - }; - - var updatePreview = function( dialog ) - { - //Don't load before onShow. - if ( !dialog.originalElement || !dialog.preview ) - return 1; - - // Read attributes and update imagePreview; - dialog.commitContent( PREVIEW, dialog.preview ); - return 0; - }; - - // Custom commit dialog logic, where we're intended to give inline style - // field (txtdlgGenStyle) higher priority to avoid overwriting styles contribute - // by other fields. - function commitContent() - { - var args = arguments; - var inlineStyleField = this.getContentElement( 'advanced', 'txtdlgGenStyle' ); - inlineStyleField && inlineStyleField.commit.apply( inlineStyleField, args ); - - this.foreach( function( widget ) - { - if ( widget.commit && widget.id != 'txtdlgGenStyle' ) - widget.commit.apply( widget, args ); - }); - } - - // Avoid recursions. - var incommit; - - // Synchronous field values to other impacted fields is required, e.g. border - // size change should alter inline-style text as well. - function commitInternally( targetFields ) - { - if ( incommit ) - return; - - incommit = 1; - - var dialog = this.getDialog(), - element = dialog.imageElement; - if ( element ) - { - // Commit this field and broadcast to target fields. - this.commit( IMAGE, element ); - - targetFields = [].concat( targetFields ); - var length = targetFields.length, - field; - for ( var i = 0; i < length; i++ ) - { - field = dialog.getContentElement.apply( dialog, targetFields[ i ].split( ':' ) ); - // May cause recursion. - field && field.setup( IMAGE, element ); - } - } - - incommit = 0; - } - - var switchLockRatio = function( dialog, value ) - { - var oImageOriginal = dialog.originalElement; - - // Dialog may already closed. (#5505) - if( !oImageOriginal ) - return null; - - var ratioButton = CKEDITOR.document.getById( btnLockSizesId ); - - if ( oImageOriginal.getCustomData( 'isReady' ) == 'true' ) - { - if ( value == 'check' ) // Check image ratio and original image ratio. - { - var width = dialog.getValueOf( 'info', 'txtWidth' ), - height = dialog.getValueOf( 'info', 'txtHeight' ), - originalRatio = oImageOriginal.$.width * 1000 / oImageOriginal.$.height, - thisRatio = width * 1000 / height; - dialog.lockRatio = false; // Default: unlock ratio - - if ( !width && !height ) - dialog.lockRatio = true; - else if ( !isNaN( originalRatio ) && !isNaN( thisRatio ) ) - { - if ( Math.round( originalRatio ) == Math.round( thisRatio ) ) - dialog.lockRatio = true; - } - } - else if ( value != undefined ) - dialog.lockRatio = value; - else - dialog.lockRatio = !dialog.lockRatio; - } - else if ( value != 'check' ) // I can't lock ratio if ratio is unknown. - dialog.lockRatio = false; - - if ( dialog.lockRatio ) - ratioButton.removeClass( 'cke_btn_unlocked' ); - else - ratioButton.addClass( 'cke_btn_unlocked' ); - - var lang = dialog._.editor.lang.image, - label = lang[ dialog.lockRatio ? 'unlockRatio' : 'lockRatio' ]; - - ratioButton.setAttribute( 'title', label ); - ratioButton.getFirst().setText( label ); - - return dialog.lockRatio; - }; - - var resetSize = function( dialog ) - { - var oImageOriginal = dialog.originalElement; - if ( oImageOriginal.getCustomData( 'isReady' ) == 'true' ) - { - dialog.setValueOf( 'info', 'txtWidth', oImageOriginal.$.width ); - dialog.setValueOf( 'info', 'txtHeight', oImageOriginal.$.height ); - } - updatePreview( dialog ); - }; - - var setupDimension = function( type, element ) - { - if ( type != IMAGE ) - return; - - function checkDimension( size, defaultValue ) - { - var aMatch = size.match( regexGetSize ); - if ( aMatch ) - { - if ( aMatch[2] == '%' ) // % is allowed. - { - aMatch[1] += '%'; - switchLockRatio( dialog, false ); // Unlock ratio - } - return aMatch[1]; - } - return defaultValue; - } - - var dialog = this.getDialog(), - value = '', - dimension = (( this.id == 'txtWidth' )? 'width' : 'height' ), - size = element.getAttribute( dimension ); - - if ( size ) - value = checkDimension( size, value ); - value = checkDimension( element.getStyle( dimension ), value ); - - this.setValue( value ); - }; - - var previewPreloader; - - var onImgLoadEvent = function() - { - // Image is ready. - var original = this.originalElement; - original.setCustomData( 'isReady', 'true' ); - original.removeListener( 'load', onImgLoadEvent ); - original.removeListener( 'error', onImgLoadErrorEvent ); - original.removeListener( 'abort', onImgLoadErrorEvent ); - - // Hide loader - CKEDITOR.document.getById( imagePreviewLoaderId ).setStyle( 'display', 'none' ); - - // New image -> new domensions - if ( !this.dontResetSize ) - resetSize( this ); - - if ( this.firstLoad ) - CKEDITOR.tools.setTimeout( function(){ switchLockRatio( this, 'check' ); }, 0, this ); - - this.firstLoad = false; - this.dontResetSize = false; - }; - - var onImgLoadErrorEvent = function() - { - // Error. Image is not loaded. - var original = this.originalElement; - original.removeListener( 'load', onImgLoadEvent ); - original.removeListener( 'error', onImgLoadErrorEvent ); - original.removeListener( 'abort', onImgLoadErrorEvent ); - - // Set Error image. - var noimage = CKEDITOR.getUrl( editor.skinPath + 'images/noimage.png' ); - - if ( this.preview ) - this.preview.setAttribute( 'src', noimage ); - - // Hide loader - CKEDITOR.document.getById( imagePreviewLoaderId ).setStyle( 'display', 'none' ); - switchLockRatio( this, false ); // Unlock. - }; - - var numbering = function( id ){ return id + CKEDITOR.tools.getNextNumber(); }, - btnLockSizesId = numbering( 'btnLockSizes' ), - btnResetSizeId = numbering( 'btnResetSize' ), - imagePreviewLoaderId = numbering( 'ImagePreviewLoader' ), - imagePreviewBoxId = numbering( 'ImagePreviewBox' ), - previewLinkId = numbering( 'previewLink' ), - previewImageId = numbering( 'previewImage' ); - - return { - title : ( dialogType == 'image' ) ? editor.lang.image.title : editor.lang.image.titleButton, - minWidth : 420, - minHeight : 310, - onShow : function() - { - this.imageElement = false; - this.linkElement = false; - - // Default: create a new element. - this.imageEditMode = false; - this.linkEditMode = false; - - this.lockRatio = true; - this.dontResetSize = false; - this.firstLoad = true; - this.addLink = false; - - var editor = this.getParentEditor(), - sel = this.getParentEditor().getSelection(), - element = sel.getSelectedElement(), - link = element && element.getAscendant( 'a' ); - - //Hide loader. - CKEDITOR.document.getById( imagePreviewLoaderId ).setStyle( 'display', 'none' ); - // Create the preview before setup the dialog contents. - previewPreloader = new CKEDITOR.dom.element( 'img', editor.document ); - this.preview = CKEDITOR.document.getById( previewImageId ); - - // Copy of the image - this.originalElement = editor.document.createElement( 'img' ); - this.originalElement.setAttribute( 'alt', '' ); - this.originalElement.setCustomData( 'isReady', 'false' ); - - if ( link ) - { - this.linkElement = link; - this.linkEditMode = true; - - // Look for Image element. - var linkChildren = link.getChildren(); - if ( linkChildren.count() == 1 ) // 1 child. - { - var childTagName = linkChildren.getItem( 0 ).getName(); - if ( childTagName == 'img' || childTagName == 'input' ) - { - this.imageElement = linkChildren.getItem( 0 ); - if ( this.imageElement.getName() == 'img' ) - this.imageEditMode = 'img'; - else if ( this.imageElement.getName() == 'input' ) - this.imageEditMode = 'input'; - } - } - // Fill out all fields. - if ( dialogType == 'image' ) - this.setupContent( LINK, link ); - } - - if ( element && element.getName() == 'img' && !element.getAttribute( '_cke_realelement' ) - || element && element.getName() == 'input' && element.getAttribute( 'type' ) == 'image' ) - { - this.imageEditMode = element.getName(); - this.imageElement = element; - } - - if ( this.imageEditMode ) - { - // Use the original element as a buffer from since we don't want - // temporary changes to be committed, e.g. if the dialog is canceled. - this.cleanImageElement = this.imageElement; - this.imageElement = this.cleanImageElement.clone( true, true ); - - // Fill out all fields. - this.setupContent( IMAGE, this.imageElement ); - - // Refresh LockRatio button - switchLockRatio ( this, true ); - } - else - this.imageElement = editor.document.createElement( 'img' ); - - // Dont show preview if no URL given. - if ( !CKEDITOR.tools.trim( this.getValueOf( 'info', 'txtUrl' ) ) ) - { - this.preview.removeAttribute( 'src' ); - this.preview.setStyle( 'display', 'none' ); - } - }, - onOk : function() - { - // Edit existing Image. - if ( this.imageEditMode ) - { - var imgTagName = this.imageEditMode; - - // Image dialog and Input element. - if ( dialogType == 'image' && imgTagName == 'input' && confirm( editor.lang.image.button2Img ) ) - { - // Replace INPUT-> IMG - imgTagName = 'img'; - this.imageElement = editor.document.createElement( 'img' ); - this.imageElement.setAttribute( 'alt', '' ); - editor.insertElement( this.imageElement ); - } - // ImageButton dialog and Image element. - else if ( dialogType != 'image' && imgTagName == 'img' && confirm( editor.lang.image.img2Button )) - { - // Replace IMG -> INPUT - imgTagName = 'input'; - this.imageElement = editor.document.createElement( 'input' ); - this.imageElement.setAttributes( - { - type : 'image', - alt : '' - } - ); - editor.insertElement( this.imageElement ); - } - else - { - // Restore the original element before all commits. - this.imageElement = this.cleanImageElement; - delete this.cleanImageElement; - } - } - else // Create a new image. - { - // Image dialog -> create IMG element. - if ( dialogType == 'image' ) - this.imageElement = editor.document.createElement( 'img' ); - else - { - this.imageElement = editor.document.createElement( 'input' ); - this.imageElement.setAttribute ( 'type' ,'image' ); - } - this.imageElement.setAttribute( 'alt', '' ); - } - - // Create a new link. - if ( !this.linkEditMode ) - this.linkElement = editor.document.createElement( 'a' ); - - // Set attributes. - this.commitContent( IMAGE, this.imageElement ); - this.commitContent( LINK, this.linkElement ); - - // Remove empty style attribute. - if ( !this.imageElement.getAttribute( 'style' ) ) - this.imageElement.removeAttribute( 'style' ); - - // Insert a new Image. - if ( !this.imageEditMode ) - { - if ( this.addLink ) - { - //Insert a new Link. - if ( !this.linkEditMode ) - { - editor.insertElement(this.linkElement); - this.linkElement.append(this.imageElement, false); - } - else //Link already exists, image not. - editor.insertElement(this.imageElement ); - } - else - editor.insertElement( this.imageElement ); - } - else // Image already exists. - { - //Add a new link element. - if ( !this.linkEditMode && this.addLink ) - { - editor.insertElement( this.linkElement ); - this.imageElement.appendTo( this.linkElement ); - } - //Remove Link, Image exists. - else if ( this.linkEditMode && !this.addLink ) - { - editor.getSelection().selectElement( this.linkElement ); - editor.insertElement( this.imageElement ); - } - } - }, - onLoad : function() - { - if ( dialogType != 'image' ) - this.hidePage( 'Link' ); //Hide Link tab. - var doc = this._.element.getDocument(); - this.addFocusable( doc.getById( btnResetSizeId ), 5 ); - this.addFocusable( doc.getById( btnLockSizesId ), 5 ); - - this.commitContent = commitContent; - }, - onHide : function() - { - if ( this.preview ) - this.commitContent( CLEANUP, this.preview ); - - if ( this.originalElement ) - { - this.originalElement.removeListener( 'load', onImgLoadEvent ); - this.originalElement.removeListener( 'error', onImgLoadErrorEvent ); - this.originalElement.removeListener( 'abort', onImgLoadErrorEvent ); - this.originalElement.remove(); - this.originalElement = false; // Dialog is closed. - } - - delete this.imageElement; - }, - contents : [ - { - id : 'info', - label : editor.lang.image.infoTab, - accessKey : 'I', - elements : - [ - { - type : 'vbox', - padding : 0, - children : - [ - { - type : 'hbox', - widths : [ '280px', '110px' ], - align : 'right', - children : - [ - { - id : 'txtUrl', - type : 'text', - label : editor.lang.common.url, - required: true, - onChange : function() - { - var dialog = this.getDialog(), - newUrl = this.getValue(); - - //Update original image - if ( newUrl.length > 0 ) //Prevent from load before onShow - { - dialog = this.getDialog(); - var original = dialog.originalElement; - - dialog.preview.removeStyle( 'display' ); - - original.setCustomData( 'isReady', 'false' ); - // Show loader - var loader = CKEDITOR.document.getById( imagePreviewLoaderId ); - if ( loader ) - loader.setStyle( 'display', '' ); - - original.on( 'load', onImgLoadEvent, dialog ); - original.on( 'error', onImgLoadErrorEvent, dialog ); - original.on( 'abort', onImgLoadErrorEvent, dialog ); - original.setAttribute( 'src', newUrl ); - - // Query the preloader to figure out the url impacted by based href. - previewPreloader.setAttribute( 'src', newUrl ); - dialog.preview.setAttribute( 'src', previewPreloader.$.src ); - updatePreview( dialog ); - } - // Dont show preview if no URL given. - else if ( dialog.preview ) - { - dialog.preview.removeAttribute( 'src' ); - dialog.preview.setStyle( 'display', 'none' ); - } - }, - setup : function( type, element ) - { - if ( type == IMAGE ) - { - var url = element.getAttribute( '_cke_saved_src' ) || element.getAttribute( 'src' ); - var field = this; - - this.getDialog().dontResetSize = true; - - field.setValue( url ); // And call this.onChange() - // Manually set the initial value.(#4191) - field.setInitValue(); - field.focus(); - } - }, - commit : function( type, element ) - { - if ( type == IMAGE && ( this.getValue() || this.isChanged() ) ) - { - element.setAttribute( '_cke_saved_src', decodeURI( this.getValue() ) ); - element.setAttribute( 'src', decodeURI( this.getValue() ) ); - } - else if ( type == CLEANUP ) - { - element.setAttribute( 'src', '' ); // If removeAttribute doesn't work. - element.removeAttribute( 'src' ); - } - }, - validate : CKEDITOR.dialog.validate.notEmpty( editor.lang.image.urlMissing ) - }, - { - type : 'button', - id : 'browse', - // v-align with the 'txtUrl' field. - // TODO: We need something better than a fixed size here. - style : 'display:inline-block;margin-top:10px;', - align : 'center', - label : editor.lang.common.browseServer, - hidden : true, - filebrowser : 'info:txtUrl' - } - ] - } - ] - }, - { - id : 'txtAlt', - type : 'text', - label : editor.lang.image.alt, - accessKey : 'A', - 'default' : '', - onChange : function() - { - updatePreview( this.getDialog() ); - }, - setup : function( type, element ) - { - if ( type == IMAGE ) - this.setValue( element.getAttribute( 'alt' ) ); - }, - commit : function( type, element ) - { - if ( type == IMAGE ) - { - if ( this.getValue() || this.isChanged() ) - element.setAttribute( 'alt', this.getValue() ); - } - else if ( type == PREVIEW ) - { - element.setAttribute( 'alt', this.getValue() ); - } - else if ( type == CLEANUP ) - { - element.removeAttribute( 'alt' ); - } - } - }, - { - type : 'hbox', - widths : [ '140px', '240px' ], - children : - [ - { - type : 'vbox', - padding : 10, - children : - [ - { - type : 'hbox', - widths : [ '70%', '30%' ], - children : - [ - { - type : 'vbox', - padding : 1, - children : - [ - { - type : 'text', - width: '40px', - id : 'txtWidth', - labelLayout : 'horizontal', - label : editor.lang.image.width, - onKeyUp : onSizeChange, - onChange : function() - { - commitInternally.call( this, 'advanced:txtdlgGenStyle' ); - }, - validate : function() - { - var aMatch = this.getValue().match( regexGetSizeOrEmpty ); - if ( !aMatch ) - alert( editor.lang.image.validateWidth ); - return !!aMatch; - }, - setup : setupDimension, - commit : function( type, element, internalCommit ) - { - var value = this.getValue(); - if ( type == IMAGE ) - { - if ( value ) - element.setStyle( 'width', CKEDITOR.tools.cssLength( value ) ); - else if ( !value && this.isChanged( ) ) - element.removeStyle( 'width' ); - - !internalCommit && element.removeAttribute( 'width' ); - } - else if ( type == PREVIEW ) - { - var aMatch = value.match( regexGetSize ); - if ( !aMatch ) - { - var oImageOriginal = this.getDialog().originalElement; - if ( oImageOriginal.getCustomData( 'isReady' ) == 'true' ) - element.setStyle( 'width', oImageOriginal.$.width + 'px'); - } - else - element.setStyle( 'width', value + 'px'); - } - else if ( type == CLEANUP ) - { - element.removeAttribute( 'width' ); - element.removeStyle( 'width' ); - } - } - }, - { - type : 'text', - id : 'txtHeight', - width: '40px', - labelLayout : 'horizontal', - label : editor.lang.image.height, - onKeyUp : onSizeChange, - onChange : function() - { - commitInternally.call( this, 'advanced:txtdlgGenStyle' ); - }, - validate : function() - { - var aMatch = this.getValue().match( regexGetSizeOrEmpty ); - if ( !aMatch ) - alert( editor.lang.image.validateHeight ); - return !!aMatch; - }, - setup : setupDimension, - commit : function( type, element, internalCommit ) - { - var value = this.getValue(); - if ( type == IMAGE ) - { - if ( value ) - element.setStyle( 'height', CKEDITOR.tools.cssLength( value ) ); - else if ( !value && this.isChanged( ) ) - element.removeStyle( 'height' ); - - if ( !internalCommit && type == IMAGE ) - element.removeAttribute( 'height' ); - } - else if ( type == PREVIEW ) - { - var aMatch = value.match( regexGetSize ); - if ( !aMatch ) - { - var oImageOriginal = this.getDialog().originalElement; - if ( oImageOriginal.getCustomData( 'isReady' ) == 'true' ) - element.setStyle( 'height', oImageOriginal.$.height + 'px' ); - } - else - element.setStyle( 'height', value + 'px' ); - } - else if ( type == CLEANUP ) - { - element.removeAttribute( 'height' ); - element.removeStyle( 'height' ); - } - } - } - ] - }, - { - type : 'html', - style : 'margin-top:10px;width:40px;height:40px;', - onLoad : function() - { - // Activate Reset button - var resetButton = CKEDITOR.document.getById( btnResetSizeId ), - ratioButton = CKEDITOR.document.getById( btnLockSizesId ); - if ( resetButton ) - { - resetButton.on( 'click', function(evt) - { - resetSize( this ); - evt.data.preventDefault(); - }, this.getDialog() ); - resetButton.on( 'mouseover', function() - { - this.addClass( 'cke_btn_over' ); - }, resetButton ); - resetButton.on( 'mouseout', function() - { - this.removeClass( 'cke_btn_over' ); - }, resetButton ); - } - // Activate (Un)LockRatio button - if ( ratioButton ) - { - ratioButton.on( 'click', function(evt) - { - var locked = switchLockRatio( this ), - oImageOriginal = this.originalElement, - width = this.getValueOf( 'info', 'txtWidth' ); - - if ( oImageOriginal.getCustomData( 'isReady' ) == 'true' && width ) - { - var height = oImageOriginal.$.height / oImageOriginal.$.width * width; - if ( !isNaN( height ) ) - { - this.setValueOf( 'info', 'txtHeight', Math.round( height ) ); - updatePreview( this ); - } - } - evt.data.preventDefault(); - }, this.getDialog() ); - ratioButton.on( 'mouseover', function() - { - this.addClass( 'cke_btn_over' ); - }, ratioButton ); - ratioButton.on( 'mouseout', function() - { - this.removeClass( 'cke_btn_over' ); - }, ratioButton ); - } - }, - html : '<div>'+ - '<a href="javascript:void(0)" tabindex="-1" title="' + editor.lang.image.unlockRatio + - '" class="cke_btn_locked" id="' + btnLockSizesId + '" role="button"><span class="cke_label">' + editor.lang.image.unlockRatio + '</span></a>' + - '<a href="javascript:void(0)" tabindex="-1" title="' + editor.lang.image.resetSize + - '" class="cke_btn_reset" id="' + btnResetSizeId + '" role="button"><span class="cke_label">' + editor.lang.image.resetSize + '</span></a>'+ - '</div>' - } - ] - }, - { - type : 'vbox', - padding : 1, - children : - [ - { - type : 'text', - id : 'txtBorder', - width: '60px', - labelLayout : 'horizontal', - label : editor.lang.image.border, - 'default' : '', - onKeyUp : function() - { - updatePreview( this.getDialog() ); - }, - onChange : function() - { - commitInternally.call( this, 'advanced:txtdlgGenStyle' ); - }, - validate : CKEDITOR.dialog.validate.integer( editor.lang.image.validateBorder ), - setup : function( type, element ) - { - if ( type == IMAGE ) - { - var value, - borderStyle = element.getStyle( 'border-width' ); - borderStyle = borderStyle && borderStyle.match( /^(\d+px)(?: \1 \1 \1)?$/ ); - value = borderStyle && parseInt( borderStyle[ 1 ], 10 ); - isNaN ( parseInt( value, 10 ) ) && ( value = element.getAttribute( 'border' ) ); - this.setValue( value ); - } - }, - commit : function( type, element, internalCommit ) - { - var value = parseInt( this.getValue(), 10 ); - if ( type == IMAGE || type == PREVIEW ) - { - if ( !isNaN( value ) ) - { - element.setStyle( 'border-width', CKEDITOR.tools.cssLength( value ) ); - element.setStyle( 'border-style', 'solid' ); - } - else if ( !value && this.isChanged() ) - { - element.removeStyle( 'border-width' ); - element.removeStyle( 'border-style' ); - element.removeStyle( 'border-color' ); - } - - if ( !internalCommit && type == IMAGE ) - element.removeAttribute( 'border' ); - } - else if ( type == CLEANUP ) - { - element.removeAttribute( 'border' ); - element.removeStyle( 'border-width' ); - element.removeStyle( 'border-style' ); - element.removeStyle( 'border-color' ); - } - } - }, - { - type : 'text', - id : 'txtHSpace', - width: '60px', - labelLayout : 'horizontal', - label : editor.lang.image.hSpace, - 'default' : '', - onKeyUp : function() - { - updatePreview( this.getDialog() ); - }, - onChange : function() - { - commitInternally.call( this, 'advanced:txtdlgGenStyle' ); - }, - validate : CKEDITOR.dialog.validate.integer( editor.lang.image.validateHSpace ), - setup : function( type, element ) - { - if ( type == IMAGE ) - { - var value, - marginLeftPx, - marginRightPx, - marginLeftStyle = element.getStyle( 'margin-left' ), - marginRightStyle = element.getStyle( 'margin-right' ); - - marginLeftStyle = marginLeftStyle && marginLeftStyle.match( pxLengthRegex ); - marginRightStyle = marginRightStyle && marginRightStyle.match( pxLengthRegex ); - marginLeftPx = parseInt( marginLeftStyle, 10 ); - marginRightPx = parseInt( marginRightStyle, 10 ); - - value = ( marginLeftPx == marginRightPx ) && marginLeftPx; - isNaN( parseInt( value, 10 ) ) && ( value = element.getAttribute( 'hspace' ) ); - - this.setValue( value ); - } - }, - commit : function( type, element, internalCommit ) - { - var value = parseInt( this.getValue(), 10 ); - if ( type == IMAGE || type == PREVIEW ) - { - if ( !isNaN( value ) ) - { - element.setStyle( 'margin-left', CKEDITOR.tools.cssLength( value ) ); - element.setStyle( 'margin-right', CKEDITOR.tools.cssLength( value ) ); - } - else if ( !value && this.isChanged( ) ) - { - element.removeStyle( 'margin-left' ); - element.removeStyle( 'margin-right' ); - } - - if ( !internalCommit && type == IMAGE ) - element.removeAttribute( 'hspace' ); - } - else if ( type == CLEANUP ) - { - element.removeAttribute( 'hspace' ); - element.removeStyle( 'margin-left' ); - element.removeStyle( 'margin-right' ); - } - } - }, - { - type : 'text', - id : 'txtVSpace', - width : '60px', - labelLayout : 'horizontal', - label : editor.lang.image.vSpace, - 'default' : '', - onKeyUp : function() - { - updatePreview( this.getDialog() ); - }, - onChange : function() - { - commitInternally.call( this, 'advanced:txtdlgGenStyle' ); - }, - validate : CKEDITOR.dialog.validate.integer( editor.lang.image.validateVSpace ), - setup : function( type, element ) - { - if ( type == IMAGE ) - { - var value, - marginTopPx, - marginBottomPx, - marginTopStyle = element.getStyle( 'margin-top' ), - marginBottomStyle = element.getStyle( 'margin-bottom' ); - - marginTopStyle = marginTopStyle && marginTopStyle.match( pxLengthRegex ); - marginBottomStyle = marginBottomStyle && marginBottomStyle.match( pxLengthRegex ); - marginTopPx = parseInt( marginTopStyle, 10 ); - marginBottomPx = parseInt( marginBottomStyle, 10 ); - - value = ( marginTopPx == marginBottomPx ) && marginTopPx; - isNaN ( parseInt( value, 10 ) ) && ( value = element.getAttribute( 'vspace' ) ); - this.setValue( value ); - } - }, - commit : function( type, element, internalCommit ) - { - var value = parseInt( this.getValue(), 10 ); - if ( type == IMAGE || type == PREVIEW ) - { - if ( !isNaN( value ) ) - { - element.setStyle( 'margin-top', CKEDITOR.tools.cssLength( value ) ); - element.setStyle( 'margin-bottom', CKEDITOR.tools.cssLength( value ) ); - } - else if ( !value && this.isChanged( ) ) - { - element.removeStyle( 'margin-top' ); - element.removeStyle( 'margin-bottom' ); - } - - if ( !internalCommit && type == IMAGE ) - element.removeAttribute( 'vspace' ); - } - else if ( type == CLEANUP ) - { - element.removeAttribute( 'vspace' ); - element.removeStyle( 'margin-top' ); - element.removeStyle( 'margin-bottom' ); - } - } - }, - { - id : 'cmbAlign', - type : 'select', - labelLayout : 'horizontal', - widths : [ '35%','65%' ], - style : 'width:90px', - label : editor.lang.image.align, - 'default' : '', - items : - [ - [ editor.lang.common.notSet , ''], - [ editor.lang.image.alignLeft , 'left'], - [ editor.lang.image.alignRight , 'right'] - // Backward compatible with v2 on setup when specified as attribute value, - // while these values are no more available as select options. - // [ editor.lang.image.alignAbsBottom , 'absBottom'], - // [ editor.lang.image.alignAbsMiddle , 'absMiddle'], - // [ editor.lang.image.alignBaseline , 'baseline'], - // [ editor.lang.image.alignTextTop , 'text-top'], - // [ editor.lang.image.alignBottom , 'bottom'], - // [ editor.lang.image.alignMiddle , 'middle'], - // [ editor.lang.image.alignTop , 'top'] - ], - onChange : function() - { - updatePreview( this.getDialog() ); - commitInternally.call( this, 'advanced:txtdlgGenStyle' ); - }, - setup : function( type, element ) - { - if ( type == IMAGE ) - { - var value = element.getStyle( 'float' ); - switch( value ) - { - // Ignore those unrelated values. - case 'inherit': - case 'none': - value = ''; - } - - !value && ( value = ( element.getAttribute( 'align' ) || '' ).toLowerCase() ); - this.setValue( value ); - } - }, - commit : function( type, element, internalCommit ) - { - var value = this.getValue(); - if ( type == IMAGE || type == PREVIEW ) - { - if ( value ) - element.setStyle( 'float', value ); - else - element.removeStyle( 'float' ); - - if ( !internalCommit && type == IMAGE ) - { - value = ( element.getAttribute( 'align' ) || '' ).toLowerCase(); - switch( value ) - { - // we should remove it only if it matches "left" or "right", - // otherwise leave it intact. - case 'left': - case 'right': - element.removeAttribute( 'align' ); - } - } - } - else if ( type == CLEANUP ) - element.removeStyle( 'float' ); - - } - } - ] - } - ] - }, - { - type : 'vbox', - height : '250px', - children : - [ - { - type : 'html', - style : 'width:95%;', - html : '<div>' + CKEDITOR.tools.htmlEncode( editor.lang.common.preview ) +'<br>'+ - '<div id="' + imagePreviewLoaderId + '" class="ImagePreviewLoader" style="display:none"><div class="loading"> </div></div>'+ - '<div id="' + imagePreviewBoxId + '" class="ImagePreviewBox"><table><tr><td>'+ - '<a href="javascript:void(0)" target="_blank" onclick="return false;" id="' + previewLinkId + '">'+ - '<img id="' + previewImageId + '" alt="" /></a>' + - ( editor.config.image_previewText || - 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. '+ - 'Maecenas feugiat consequat diam. Maecenas metus. Vivamus diam purus, cursus a, commodo non, facilisis vitae, '+ - 'nulla. Aenean dictum lacinia tortor. Nunc iaculis, nibh non iaculis aliquam, orci felis euismod neque, sed ornare massa mauris sed velit. Nulla pretium mi et risus. Fusce mi pede, tempor id, cursus ac, ullamcorper nec, enim. Sed tortor. Curabitur molestie. Duis velit augue, condimentum at, ultrices a, luctus ut, orci. Donec pellentesque egestas eros. Integer cursus, augue in cursus faucibus, eros pede bibendum sem, in tempus tellus justo quis ligula. Etiam eget tortor. Vestibulum rutrum, est ut placerat elementum, lectus nisl aliquam velit, tempor aliquam eros nunc nonummy metus. In eros metus, gravida a, gravida sed, lobortis id, turpis. Ut ultrices, ipsum at venenatis fringilla, sem nulla lacinia tellus, eget aliquet turpis mauris non enim. Nam turpis. Suspendisse lacinia. Curabitur ac tortor ut ipsum egestas elementum. Nunc imperdiet gravida mauris.' ) + - '</td></tr></table></div></div>' - } - ] - } - ] - } - ] - }, - { - id : 'Link', - label : editor.lang.link.title, - padding : 0, - elements : - [ - { - id : 'txtUrl', - type : 'text', - label : editor.lang.common.url, - style : 'width: 100%', - 'default' : '', - setup : function( type, element ) - { - if ( type == LINK ) - { - var href = element.getAttribute( '_cke_saved_href' ); - if ( !href ) - href = element.getAttribute( 'href' ); - this.setValue( href ); - } - }, - commit : function( type, element ) - { - if ( type == LINK ) - { - if ( this.getValue() || this.isChanged() ) - { - element.setAttribute( '_cke_saved_href', decodeURI( this.getValue() ) ); - element.setAttribute( 'href', 'javascript:void(0)/*' + - CKEDITOR.tools.getNextNumber() + '*/' ); - - if ( this.getValue() || !editor.config.image_removeLinkByEmptyURL ) - this.getDialog().addLink = true; - } - } - } - }, - { - type : 'button', - id : 'browse', - filebrowser : - { - action : 'Browse', - target: 'Link:txtUrl', - url: editor.config.filebrowserImageBrowseLinkUrl || editor.config.filebrowserBrowseUrl - }, - style : 'float:right', - hidden : true, - label : editor.lang.common.browseServer - }, - { - id : 'cmbTarget', - type : 'select', - label : editor.lang.common.target, - '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'] - ], - setup : function( type, element ) - { - if ( type == LINK ) - this.setValue( element.getAttribute( 'target' ) ); - }, - commit : function( type, element ) - { - if ( type == LINK ) - { - if ( this.getValue() || this.isChanged() ) - element.setAttribute( 'target', this.getValue() ); - } - } - } - ] - }, - { - id : 'Upload', - hidden : true, - filebrowser : 'uploadButton', - label : editor.lang.image.upload, - elements : - [ - { - type : 'file', - id : 'upload', - label : editor.lang.image.btnUpload, - style: 'height:40px', - size : 38 - }, - { - type : 'fileButton', - id : 'uploadButton', - filebrowser : 'info:txtUrl', - label : editor.lang.image.btnUpload, - 'for' : [ 'Upload', 'upload' ] - } - ] - }, - { - id : 'advanced', - label : editor.lang.common.advancedTab, - elements : - [ - { - type : 'hbox', - widths : [ '50%', '25%', '25%' ], - children : - [ - { - type : 'text', - id : 'linkId', - label : editor.lang.common.id, - setup : function( type, element ) - { - if ( type == IMAGE ) - this.setValue( element.getAttribute( 'id' ) ); - }, - commit : function( type, element ) - { - if ( type == IMAGE ) - { - if ( this.getValue() || this.isChanged() ) - element.setAttribute( 'id', this.getValue() ); - } - } - }, - { - id : 'cmbLangDir', - type : 'select', - style : 'width : 100px;', - label : editor.lang.common.langDir, - 'default' : '', - items : - [ - [ editor.lang.common.notSet, '' ], - [ editor.lang.common.langDirLtr, 'ltr' ], - [ editor.lang.common.langDirRtl, 'rtl' ] - ], - setup : function( type, element ) - { - if ( type == IMAGE ) - this.setValue( element.getAttribute( 'dir' ) ); - }, - commit : function( type, element ) - { - if ( type == IMAGE ) - { - if ( this.getValue() || this.isChanged() ) - element.setAttribute( 'dir', this.getValue() ); - } - } - }, - { - type : 'text', - id : 'txtLangCode', - label : editor.lang.common.langCode, - 'default' : '', - setup : function( type, element ) - { - if ( type == IMAGE ) - this.setValue( element.getAttribute( 'lang' ) ); - }, - commit : function( type, element ) - { - if ( type == IMAGE ) - { - if ( this.getValue() || this.isChanged() ) - element.setAttribute( 'lang', this.getValue() ); - } - } - } - ] - }, - { - type : 'text', - id : 'txtGenLongDescr', - label : editor.lang.common.longDescr, - setup : function( type, element ) - { - if ( type == IMAGE ) - this.setValue( element.getAttribute( 'longDesc' ) ); - }, - commit : function( type, element ) - { - if ( type == IMAGE ) - { - if ( this.getValue() || this.isChanged() ) - element.setAttribute( 'longDesc', this.getValue() ); - } - } - }, - { - type : 'hbox', - widths : [ '50%', '50%' ], - children : - [ - { - type : 'text', - id : 'txtGenClass', - label : editor.lang.common.cssClass, - 'default' : '', - setup : function( type, element ) - { - if ( type == IMAGE ) - this.setValue( element.getAttribute( 'class' ) ); - }, - commit : function( type, element ) - { - if ( type == IMAGE ) - { - if ( this.getValue() || this.isChanged() ) - element.setAttribute( 'class', this.getValue() ); - } - } - }, - { - type : 'text', - id : 'txtGenTitle', - label : editor.lang.common.advisoryTitle, - 'default' : '', - onChange : function() - { - updatePreview( this.getDialog() ); - }, - setup : function( type, element ) - { - if ( type == IMAGE ) - this.setValue( element.getAttribute( 'title' ) ); - }, - commit : function( type, element ) - { - if ( type == IMAGE ) - { - if ( this.getValue() || this.isChanged() ) - element.setAttribute( 'title', this.getValue() ); - } - else if ( type == PREVIEW ) - { - element.setAttribute( 'title', this.getValue() ); - } - else if ( type == CLEANUP ) - { - element.removeAttribute( 'title' ); - } - } - } - ] - }, - { - type : 'text', - id : 'txtdlgGenStyle', - label : editor.lang.common.cssStyle, - 'default' : '', - setup : function( type, element ) - { - if ( type == IMAGE ) - { - var genStyle = element.getAttribute( 'style' ); - if ( !genStyle && element.$.style.cssText ) - genStyle = element.$.style.cssText; - this.setValue( genStyle ); - - var height = element.$.style.height, - width = element.$.style.width, - aMatchH = ( height ? height : '' ).match( regexGetSize ), - aMatchW = ( width ? width : '').match( regexGetSize ); - - this.attributesInStyle = - { - height : !!aMatchH, - width : !!aMatchW - }; - } - }, - onChange : function () - { - commitInternally.call( this, - [ 'info:cmbFloat', 'info:cmbAlign', - 'info:txtVSpace', 'info:txtHSpace', - 'info:txtBorder', - 'info:txtWidth', 'info:txtHeight' ] ); - updatePreview( this ); - }, - commit : function( type, element ) - { - if ( type == IMAGE && ( this.getValue() || this.isChanged() ) ) - { - element.setAttribute( 'style', this.getValue() ); - } - } - } - ] - } - ] - }; - }; - - CKEDITOR.dialog.add( 'image', function( editor ) - { - return imageDialog( editor, 'image' ); - }); - - CKEDITOR.dialog.add( 'imagebutton', function( editor ) - { - return imageDialog( editor, 'imagebutton' ); - }); -})(); +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+(function()
+{
+ var imageDialog = function( editor, dialogType )
+ {
+ // Load image preview.
+ var IMAGE = 1,
+ LINK = 2,
+ PREVIEW = 4,
+ CLEANUP = 8,
+ regexGetSize = /^\s*(\d+)((px)|\%)?\s*$/i,
+ regexGetSizeOrEmpty = /(^\s*(\d+)((px)|\%)?\s*$)|^$/i,
+ pxLengthRegex = /^\d+px$/;
+
+ var onSizeChange = function()
+ {
+ var value = this.getValue(), // This = input element.
+ dialog = this.getDialog(),
+ aMatch = value.match( regexGetSize ); // Check value
+ if ( aMatch )
+ {
+ if ( aMatch[2] == '%' ) // % is allowed - > unlock ratio.
+ switchLockRatio( dialog, false ); // Unlock.
+ value = aMatch[1];
+ }
+
+ // Only if ratio is locked
+ if ( dialog.lockRatio )
+ {
+ var oImageOriginal = dialog.originalElement;
+ if ( oImageOriginal.getCustomData( 'isReady' ) == 'true' )
+ {
+ if ( this.id == 'txtHeight' )
+ {
+ if ( value && value != '0' )
+ value = Math.round( oImageOriginal.$.width * ( value / oImageOriginal.$.height ) );
+ if ( !isNaN( value ) )
+ dialog.setValueOf( 'info', 'txtWidth', value );
+ }
+ else //this.id = txtWidth.
+ {
+ if ( value && value != '0' )
+ value = Math.round( oImageOriginal.$.height * ( value / oImageOriginal.$.width ) );
+ if ( !isNaN( value ) )
+ dialog.setValueOf( 'info', 'txtHeight', value );
+ }
+ }
+ }
+ updatePreview( dialog );
+ };
+
+ var updatePreview = function( dialog )
+ {
+ //Don't load before onShow.
+ if ( !dialog.originalElement || !dialog.preview )
+ return 1;
+
+ // Read attributes and update imagePreview;
+ dialog.commitContent( PREVIEW, dialog.preview );
+ return 0;
+ };
+
+ // Custom commit dialog logic, where we're intended to give inline style
+ // field (txtdlgGenStyle) higher priority to avoid overwriting styles contribute
+ // by other fields.
+ function commitContent()
+ {
+ var args = arguments;
+ var inlineStyleField = this.getContentElement( 'advanced', 'txtdlgGenStyle' );
+ inlineStyleField && inlineStyleField.commit.apply( inlineStyleField, args );
+
+ this.foreach( function( widget )
+ {
+ if ( widget.commit && widget.id != 'txtdlgGenStyle' )
+ widget.commit.apply( widget, args );
+ });
+ }
+
+ // Avoid recursions.
+ var incommit;
+
+ // Synchronous field values to other impacted fields is required, e.g. border
+ // size change should alter inline-style text as well.
+ function commitInternally( targetFields )
+ {
+ if ( incommit )
+ return;
+
+ incommit = 1;
+
+ var dialog = this.getDialog(),
+ element = dialog.imageElement;
+ if ( element )
+ {
+ // Commit this field and broadcast to target fields.
+ this.commit( IMAGE, element );
+
+ targetFields = [].concat( targetFields );
+ var length = targetFields.length,
+ field;
+ for ( var i = 0; i < length; i++ )
+ {
+ field = dialog.getContentElement.apply( dialog, targetFields[ i ].split( ':' ) );
+ // May cause recursion.
+ field && field.setup( IMAGE, element );
+ }
+ }
+
+ incommit = 0;
+ }
+
+ var switchLockRatio = function( dialog, value )
+ {
+ if ( !dialog.getContentElement( 'info', 'ratioLock' ) )
+ return null;
+
+ var oImageOriginal = dialog.originalElement;
+
+ // Dialog may already closed. (#5505)
+ if( !oImageOriginal )
+ return null;
+
+ // Check image ratio and original image ratio, but respecting user's preference.
+ if ( value == 'check' )
+ {
+ if ( !dialog.userlockRatio && oImageOriginal.getCustomData( 'isReady' ) == 'true' )
+ {
+ var width = dialog.getValueOf( 'info', 'txtWidth' ),
+ height = dialog.getValueOf( 'info', 'txtHeight' ),
+ originalRatio = oImageOriginal.$.width * 1000 / oImageOriginal.$.height,
+ thisRatio = width * 1000 / height;
+ dialog.lockRatio = false; // Default: unlock ratio
+
+ if ( !width && !height )
+ dialog.lockRatio = true;
+ else if ( !isNaN( originalRatio ) && !isNaN( thisRatio ) )
+ {
+ if ( Math.round( originalRatio ) == Math.round( thisRatio ) )
+ dialog.lockRatio = true;
+ }
+ }
+ }
+ else if ( value != undefined )
+ dialog.lockRatio = value;
+ else
+ {
+ dialog.userlockRatio = 1;
+ dialog.lockRatio = !dialog.lockRatio;
+ }
+
+ var ratioButton = CKEDITOR.document.getById( btnLockSizesId );
+ if ( dialog.lockRatio )
+ ratioButton.removeClass( 'cke_btn_unlocked' );
+ else
+ ratioButton.addClass( 'cke_btn_unlocked' );
+
+ ratioButton.setAttribute( 'aria-checked', dialog.lockRatio );
+
+ // Ratio button hc presentation - WHITE SQUARE / BLACK SQUARE
+ if ( CKEDITOR.env.hc )
+ {
+ var icon = ratioButton.getChild( 0 );
+ icon.setHtml( dialog.lockRatio ? CKEDITOR.env.ie ? '\u25A0': '\u25A3' : CKEDITOR.env.ie ? '\u25A1' : '\u25A2' );
+ }
+
+ return dialog.lockRatio;
+ };
+
+ var resetSize = function( dialog )
+ {
+ var oImageOriginal = dialog.originalElement;
+ if ( oImageOriginal.getCustomData( 'isReady' ) == 'true' )
+ {
+ var widthField = dialog.getContentElement( 'info', 'txtWidth' ),
+ heightField = dialog.getContentElement( 'info', 'txtHeight' );
+ widthField && widthField.setValue( oImageOriginal.$.width );
+ heightField && heightField.setValue( oImageOriginal.$.height );
+ }
+ updatePreview( dialog );
+ };
+
+ var setupDimension = function( type, element )
+ {
+ if ( type != IMAGE )
+ return;
+
+ function checkDimension( size, defaultValue )
+ {
+ var aMatch = size.match( regexGetSize );
+ if ( aMatch )
+ {
+ if ( aMatch[2] == '%' ) // % is allowed.
+ {
+ aMatch[1] += '%';
+ switchLockRatio( dialog, false ); // Unlock ratio
+ }
+ return aMatch[1];
+ }
+ return defaultValue;
+ }
+
+ var dialog = this.getDialog(),
+ value = '',
+ dimension = this.id == 'txtWidth' ? 'width' : 'height',
+ size = element.getAttribute( dimension );
+
+ if ( size )
+ value = checkDimension( size, value );
+ value = checkDimension( element.getStyle( dimension ), value );
+
+ this.setValue( value );
+ };
+
+ var previewPreloader;
+
+ var onImgLoadEvent = function()
+ {
+ // Image is ready.
+ var original = this.originalElement;
+ original.setCustomData( 'isReady', 'true' );
+ original.removeListener( 'load', onImgLoadEvent );
+ original.removeListener( 'error', onImgLoadErrorEvent );
+ original.removeListener( 'abort', onImgLoadErrorEvent );
+
+ // Hide loader
+ CKEDITOR.document.getById( imagePreviewLoaderId ).setStyle( 'display', 'none' );
+
+ // New image -> new domensions
+ if ( !this.dontResetSize )
+ resetSize( this );
+
+ if ( this.firstLoad )
+ CKEDITOR.tools.setTimeout( function(){ switchLockRatio( this, 'check' ); }, 0, this );
+
+ this.firstLoad = false;
+ this.dontResetSize = false;
+ };
+
+ var onImgLoadErrorEvent = function()
+ {
+ // Error. Image is not loaded.
+ var original = this.originalElement;
+ original.removeListener( 'load', onImgLoadEvent );
+ original.removeListener( 'error', onImgLoadErrorEvent );
+ original.removeListener( 'abort', onImgLoadErrorEvent );
+
+ // Set Error image.
+ var noimage = CKEDITOR.getUrl( editor.skinPath + 'images/noimage.png' );
+
+ if ( this.preview )
+ this.preview.setAttribute( 'src', noimage );
+
+ // Hide loader
+ CKEDITOR.document.getById( imagePreviewLoaderId ).setStyle( 'display', 'none' );
+ switchLockRatio( this, false ); // Unlock.
+ };
+
+ var numbering = function( id )
+ {
+ return CKEDITOR.tools.getNextId() + '_' + id;
+ },
+ btnLockSizesId = numbering( 'btnLockSizes' ),
+ btnResetSizeId = numbering( 'btnResetSize' ),
+ imagePreviewLoaderId = numbering( 'ImagePreviewLoader' ),
+ previewLinkId = numbering( 'previewLink' ),
+ previewImageId = numbering( 'previewImage' );
+
+ return {
+ title : editor.lang.image[ dialogType == 'image' ? 'title' : 'titleButton' ],
+ minWidth : 420,
+ minHeight : 360,
+ onShow : function()
+ {
+ this.imageElement = false;
+ this.linkElement = false;
+
+ // Default: create a new element.
+ this.imageEditMode = false;
+ this.linkEditMode = false;
+
+ this.lockRatio = true;
+ this.userlockRatio = 0;
+ this.dontResetSize = false;
+ this.firstLoad = true;
+ this.addLink = false;
+
+ var editor = this.getParentEditor(),
+ sel = editor.getSelection(),
+ element = sel && sel.getSelectedElement(),
+ link = element && element.getAscendant( 'a' );
+
+ //Hide loader.
+ CKEDITOR.document.getById( imagePreviewLoaderId ).setStyle( 'display', 'none' );
+ // Create the preview before setup the dialog contents.
+ previewPreloader = new CKEDITOR.dom.element( 'img', editor.document );
+ this.preview = CKEDITOR.document.getById( previewImageId );
+
+ // Copy of the image
+ this.originalElement = editor.document.createElement( 'img' );
+ this.originalElement.setAttribute( 'alt', '' );
+ this.originalElement.setCustomData( 'isReady', 'false' );
+
+ if ( link )
+ {
+ this.linkElement = link;
+ this.linkEditMode = true;
+
+ // Look for Image element.
+ var linkChildren = link.getChildren();
+ if ( linkChildren.count() == 1 ) // 1 child.
+ {
+ var childTagName = linkChildren.getItem( 0 ).getName();
+ if ( childTagName == 'img' || childTagName == 'input' )
+ {
+ this.imageElement = linkChildren.getItem( 0 );
+ if ( this.imageElement.getName() == 'img' )
+ this.imageEditMode = 'img';
+ else if ( this.imageElement.getName() == 'input' )
+ this.imageEditMode = 'input';
+ }
+ }
+ // Fill out all fields.
+ if ( dialogType == 'image' )
+ this.setupContent( LINK, link );
+ }
+
+ if ( element && element.getName() == 'img' && !element.data( 'cke-realelement' )
+ || element && element.getName() == 'input' && element.getAttribute( 'type' ) == 'image' )
+ {
+ this.imageEditMode = element.getName();
+ this.imageElement = element;
+ }
+
+ if ( this.imageEditMode )
+ {
+ // Use the original element as a buffer from since we don't want
+ // temporary changes to be committed, e.g. if the dialog is canceled.
+ this.cleanImageElement = this.imageElement;
+ this.imageElement = this.cleanImageElement.clone( true, true );
+
+ // Fill out all fields.
+ this.setupContent( IMAGE, this.imageElement );
+ }
+ else
+ this.imageElement = editor.document.createElement( 'img' );
+
+ // Refresh LockRatio button
+ switchLockRatio ( this, true );
+
+ // Dont show preview if no URL given.
+ if ( !CKEDITOR.tools.trim( this.getValueOf( 'info', 'txtUrl' ) ) )
+ {
+ this.preview.removeAttribute( 'src' );
+ this.preview.setStyle( 'display', 'none' );
+ }
+ },
+ onOk : function()
+ {
+ // Edit existing Image.
+ if ( this.imageEditMode )
+ {
+ var imgTagName = this.imageEditMode;
+
+ // Image dialog and Input element.
+ if ( dialogType == 'image' && imgTagName == 'input' && confirm( editor.lang.image.button2Img ) )
+ {
+ // Replace INPUT-> IMG
+ imgTagName = 'img';
+ this.imageElement = editor.document.createElement( 'img' );
+ this.imageElement.setAttribute( 'alt', '' );
+ editor.insertElement( this.imageElement );
+ }
+ // ImageButton dialog and Image element.
+ else if ( dialogType != 'image' && imgTagName == 'img' && confirm( editor.lang.image.img2Button ))
+ {
+ // Replace IMG -> INPUT
+ imgTagName = 'input';
+ this.imageElement = editor.document.createElement( 'input' );
+ this.imageElement.setAttributes(
+ {
+ type : 'image',
+ alt : ''
+ }
+ );
+ editor.insertElement( this.imageElement );
+ }
+ else
+ {
+ // Restore the original element before all commits.
+ this.imageElement = this.cleanImageElement;
+ delete this.cleanImageElement;
+ }
+ }
+ else // Create a new image.
+ {
+ // Image dialog -> create IMG element.
+ if ( dialogType == 'image' )
+ this.imageElement = editor.document.createElement( 'img' );
+ else
+ {
+ this.imageElement = editor.document.createElement( 'input' );
+ this.imageElement.setAttribute ( 'type' ,'image' );
+ }
+ this.imageElement.setAttribute( 'alt', '' );
+ }
+
+ // Create a new link.
+ if ( !this.linkEditMode )
+ this.linkElement = editor.document.createElement( 'a' );
+
+ // Set attributes.
+ this.commitContent( IMAGE, this.imageElement );
+ this.commitContent( LINK, this.linkElement );
+
+ // Remove empty style attribute.
+ if ( !this.imageElement.getAttribute( 'style' ) )
+ this.imageElement.removeAttribute( 'style' );
+
+ // Insert a new Image.
+ if ( !this.imageEditMode )
+ {
+ if ( this.addLink )
+ {
+ //Insert a new Link.
+ if ( !this.linkEditMode )
+ {
+ editor.insertElement( this.linkElement );
+ this.linkElement.append( this.imageElement, false );
+ }
+ else //Link already exists, image not.
+ editor.insertElement( this.imageElement );
+ }
+ else
+ editor.insertElement( this.imageElement );
+ }
+ else // Image already exists.
+ {
+ //Add a new link element.
+ if ( !this.linkEditMode && this.addLink )
+ {
+ editor.insertElement( this.linkElement );
+ this.imageElement.appendTo( this.linkElement );
+ }
+ //Remove Link, Image exists.
+ else if ( this.linkEditMode && !this.addLink )
+ {
+ editor.getSelection().selectElement( this.linkElement );
+ editor.insertElement( this.imageElement );
+ }
+ }
+ },
+ onLoad : function()
+ {
+ if ( dialogType != 'image' )
+ this.hidePage( 'Link' ); //Hide Link tab.
+ var doc = this._.element.getDocument();
+
+ if ( this.getContentElement( 'info', 'ratioLock' ) )
+ {
+ this.addFocusable( doc.getById( btnResetSizeId ), 5 );
+ this.addFocusable( doc.getById( btnLockSizesId ), 5 );
+ }
+
+ this.commitContent = commitContent;
+ },
+ onHide : function()
+ {
+ if ( this.preview )
+ this.commitContent( CLEANUP, this.preview );
+
+ if ( this.originalElement )
+ {
+ this.originalElement.removeListener( 'load', onImgLoadEvent );
+ this.originalElement.removeListener( 'error', onImgLoadErrorEvent );
+ this.originalElement.removeListener( 'abort', onImgLoadErrorEvent );
+ this.originalElement.remove();
+ this.originalElement = false; // Dialog is closed.
+ }
+
+ delete this.imageElement;
+ },
+ contents : [
+ {
+ id : 'info',
+ label : editor.lang.image.infoTab,
+ accessKey : 'I',
+ elements :
+ [
+ {
+ type : 'vbox',
+ padding : 0,
+ children :
+ [
+ {
+ type : 'hbox',
+ widths : [ '280px', '110px' ],
+ align : 'right',
+ children :
+ [
+ {
+ id : 'txtUrl',
+ type : 'text',
+ label : editor.lang.common.url,
+ required: true,
+ onChange : function()
+ {
+ var dialog = this.getDialog(),
+ newUrl = this.getValue();
+
+ //Update original image
+ if ( newUrl.length > 0 ) //Prevent from load before onShow
+ {
+ dialog = this.getDialog();
+ var original = dialog.originalElement;
+
+ dialog.preview.removeStyle( 'display' );
+
+ original.setCustomData( 'isReady', 'false' );
+ // Show loader
+ var loader = CKEDITOR.document.getById( imagePreviewLoaderId );
+ if ( loader )
+ loader.setStyle( 'display', '' );
+
+ original.on( 'load', onImgLoadEvent, dialog );
+ original.on( 'error', onImgLoadErrorEvent, dialog );
+ original.on( 'abort', onImgLoadErrorEvent, dialog );
+ original.setAttribute( 'src', newUrl );
+
+ // Query the preloader to figure out the url impacted by based href.
+ previewPreloader.setAttribute( 'src', newUrl );
+ dialog.preview.setAttribute( 'src', previewPreloader.$.src );
+ updatePreview( dialog );
+ }
+ // Dont show preview if no URL given.
+ else if ( dialog.preview )
+ {
+ dialog.preview.removeAttribute( 'src' );
+ dialog.preview.setStyle( 'display', 'none' );
+ }
+ },
+ setup : function( type, element )
+ {
+ if ( type == IMAGE )
+ {
+ var url = element.data( 'cke-saved-src' ) || element.getAttribute( 'src' );
+ var field = this;
+
+ this.getDialog().dontResetSize = true;
+
+ field.setValue( url ); // And call this.onChange()
+ // Manually set the initial value.(#4191)
+ field.setInitValue();
+ }
+ },
+ commit : function( type, element )
+ {
+ if ( type == IMAGE && ( this.getValue() || this.isChanged() ) )
+ {
+ element.data( 'cke-saved-src', this.getValue() );
+ element.setAttribute( 'src', this.getValue() );
+ }
+ else if ( type == CLEANUP )
+ {
+ element.setAttribute( 'src', '' ); // If removeAttribute doesn't work.
+ element.removeAttribute( 'src' );
+ }
+ },
+ validate : CKEDITOR.dialog.validate.notEmpty( editor.lang.image.urlMissing )
+ },
+ {
+ type : 'button',
+ id : 'browse',
+ // v-align with the 'txtUrl' field.
+ // TODO: We need something better than a fixed size here.
+ style : 'display:inline-block;margin-top:10px;',
+ align : 'center',
+ label : editor.lang.common.browseServer,
+ hidden : true,
+ filebrowser : 'info:txtUrl'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ id : 'txtAlt',
+ type : 'text',
+ label : editor.lang.image.alt,
+ accessKey : 'T',
+ 'default' : '',
+ onChange : function()
+ {
+ updatePreview( this.getDialog() );
+ },
+ setup : function( type, element )
+ {
+ if ( type == IMAGE )
+ this.setValue( element.getAttribute( 'alt' ) );
+ },
+ commit : function( type, element )
+ {
+ if ( type == IMAGE )
+ {
+ if ( this.getValue() || this.isChanged() )
+ element.setAttribute( 'alt', this.getValue() );
+ }
+ else if ( type == PREVIEW )
+ {
+ element.setAttribute( 'alt', this.getValue() );
+ }
+ else if ( type == CLEANUP )
+ {
+ element.removeAttribute( 'alt' );
+ }
+ }
+ },
+ {
+ type : 'hbox',
+ children :
+ [
+ {
+ id : 'basic',
+ type : 'vbox',
+ children :
+ [
+ {
+ type : 'hbox',
+ widths : [ '50%', '50%' ],
+ children :
+ [
+ {
+ type : 'vbox',
+ padding : 1,
+ children :
+ [
+ {
+ type : 'text',
+ width: '40px',
+ id : 'txtWidth',
+ label : editor.lang.common.width,
+ onKeyUp : onSizeChange,
+ onChange : function()
+ {
+ commitInternally.call( this, 'advanced:txtdlgGenStyle' );
+ },
+ validate : function()
+ {
+ var aMatch = this.getValue().match( regexGetSizeOrEmpty ),
+ isValid = !!( aMatch && parseInt( aMatch[1], 10 ) !== 0 );
+ if ( !isValid )
+ alert( editor.lang.common.invalidWidth );
+ return isValid;
+ },
+ setup : setupDimension,
+ commit : function( type, element, internalCommit )
+ {
+ var value = this.getValue();
+ if ( type == IMAGE )
+ {
+ if ( value )
+ element.setStyle( 'width', CKEDITOR.tools.cssLength( value ) );
+ else
+ element.removeStyle( 'width' );
+
+ !internalCommit && element.removeAttribute( 'width' );
+ }
+ else if ( type == PREVIEW )
+ {
+ var aMatch = value.match( regexGetSize );
+ if ( !aMatch )
+ {
+ var oImageOriginal = this.getDialog().originalElement;
+ if ( oImageOriginal.getCustomData( 'isReady' ) == 'true' )
+ element.setStyle( 'width', oImageOriginal.$.width + 'px');
+ }
+ else
+ element.setStyle( 'width', CKEDITOR.tools.cssLength( value ) );
+ }
+ else if ( type == CLEANUP )
+ {
+ element.removeAttribute( 'width' );
+ element.removeStyle( 'width' );
+ }
+ }
+ },
+ {
+ type : 'text',
+ id : 'txtHeight',
+ width: '40px',
+ label : editor.lang.common.height,
+ onKeyUp : onSizeChange,
+ onChange : function()
+ {
+ commitInternally.call( this, 'advanced:txtdlgGenStyle' );
+ },
+ validate : function()
+ {
+ var aMatch = this.getValue().match( regexGetSizeOrEmpty ),
+ isValid = !!( aMatch && parseInt( aMatch[1], 10 ) !== 0 );
+ if ( !isValid )
+ alert( editor.lang.common.invalidHeight );
+ return isValid;
+ },
+ setup : setupDimension,
+ commit : function( type, element, internalCommit )
+ {
+ var value = this.getValue();
+ if ( type == IMAGE )
+ {
+ if ( value )
+ element.setStyle( 'height', CKEDITOR.tools.cssLength( value ) );
+ else
+ element.removeStyle( 'height' );
+
+ !internalCommit && element.removeAttribute( 'height' );
+ }
+ else if ( type == PREVIEW )
+ {
+ var aMatch = value.match( regexGetSize );
+ if ( !aMatch )
+ {
+ var oImageOriginal = this.getDialog().originalElement;
+ if ( oImageOriginal.getCustomData( 'isReady' ) == 'true' )
+ element.setStyle( 'height', oImageOriginal.$.height + 'px' );
+ }
+ else
+ element.setStyle( 'height', CKEDITOR.tools.cssLength( value ) );
+ }
+ else if ( type == CLEANUP )
+ {
+ element.removeAttribute( 'height' );
+ element.removeStyle( 'height' );
+ }
+ }
+ }
+ ]
+ },
+ {
+ id : 'ratioLock',
+ type : 'html',
+ style : 'margin-top:30px;width:40px;height:40px;',
+ onLoad : function()
+ {
+ // Activate Reset button
+ var resetButton = CKEDITOR.document.getById( btnResetSizeId ),
+ ratioButton = CKEDITOR.document.getById( btnLockSizesId );
+ if ( resetButton )
+ {
+ resetButton.on( 'click', function( evt )
+ {
+ resetSize( this );
+ evt.data && evt.data.preventDefault();
+ }, this.getDialog() );
+ resetButton.on( 'mouseover', function()
+ {
+ this.addClass( 'cke_btn_over' );
+ }, resetButton );
+ resetButton.on( 'mouseout', function()
+ {
+ this.removeClass( 'cke_btn_over' );
+ }, resetButton );
+ }
+ // Activate (Un)LockRatio button
+ if ( ratioButton )
+ {
+ ratioButton.on( 'click', function(evt)
+ {
+ var locked = switchLockRatio( this ),
+ oImageOriginal = this.originalElement,
+ width = this.getValueOf( 'info', 'txtWidth' );
+
+ if ( oImageOriginal.getCustomData( 'isReady' ) == 'true' && width )
+ {
+ var height = oImageOriginal.$.height / oImageOriginal.$.width * width;
+ if ( !isNaN( height ) )
+ {
+ this.setValueOf( 'info', 'txtHeight', Math.round( height ) );
+ updatePreview( this );
+ }
+ }
+ evt.data && evt.data.preventDefault();
+ }, this.getDialog() );
+ ratioButton.on( 'mouseover', function()
+ {
+ this.addClass( 'cke_btn_over' );
+ }, ratioButton );
+ ratioButton.on( 'mouseout', function()
+ {
+ this.removeClass( 'cke_btn_over' );
+ }, ratioButton );
+ }
+ },
+ html : '<div>'+
+ '<a href="javascript:void(0)" tabindex="-1" title="' + editor.lang.image.lockRatio +
+ '" class="cke_btn_locked" id="' + btnLockSizesId + '" role="checkbox"><span class="cke_icon"></span><span class="cke_label">' + editor.lang.image.lockRatio + '</span></a>' +
+ '<a href="javascript:void(0)" tabindex="-1" title="' + editor.lang.image.resetSize +
+ '" class="cke_btn_reset" id="' + btnResetSizeId + '" role="button"><span class="cke_label">' + editor.lang.image.resetSize + '</span></a>'+
+ '</div>'
+ }
+ ]
+ },
+ {
+ type : 'vbox',
+ padding : 1,
+ children :
+ [
+ {
+ type : 'text',
+ id : 'txtBorder',
+ width: '60px',
+ label : editor.lang.image.border,
+ 'default' : '',
+ onKeyUp : function()
+ {
+ updatePreview( this.getDialog() );
+ },
+ onChange : function()
+ {
+ commitInternally.call( this, 'advanced:txtdlgGenStyle' );
+ },
+ validate : CKEDITOR.dialog.validate.integer( editor.lang.image.validateBorder ),
+ setup : function( type, element )
+ {
+ if ( type == IMAGE )
+ {
+ var value,
+ borderStyle = element.getStyle( 'border-width' );
+ borderStyle = borderStyle && borderStyle.match( /^(\d+px)(?: \1 \1 \1)?$/ );
+ value = borderStyle && parseInt( borderStyle[ 1 ], 10 );
+ isNaN ( parseInt( value, 10 ) ) && ( value = element.getAttribute( 'border' ) );
+ this.setValue( value );
+ }
+ },
+ commit : function( type, element, internalCommit )
+ {
+ var value = parseInt( this.getValue(), 10 );
+ if ( type == IMAGE || type == PREVIEW )
+ {
+ if ( !isNaN( value ) )
+ {
+ element.setStyle( 'border-width', CKEDITOR.tools.cssLength( value ) );
+ element.setStyle( 'border-style', 'solid' );
+ }
+ else if ( !value && this.isChanged() )
+ {
+ element.removeStyle( 'border-width' );
+ element.removeStyle( 'border-style' );
+ element.removeStyle( 'border-color' );
+ }
+
+ if ( !internalCommit && type == IMAGE )
+ element.removeAttribute( 'border' );
+ }
+ else if ( type == CLEANUP )
+ {
+ element.removeAttribute( 'border' );
+ element.removeStyle( 'border-width' );
+ element.removeStyle( 'border-style' );
+ element.removeStyle( 'border-color' );
+ }
+ }
+ },
+ {
+ type : 'text',
+ id : 'txtHSpace',
+ width: '60px',
+ label : editor.lang.image.hSpace,
+ 'default' : '',
+ onKeyUp : function()
+ {
+ updatePreview( this.getDialog() );
+ },
+ onChange : function()
+ {
+ commitInternally.call( this, 'advanced:txtdlgGenStyle' );
+ },
+ validate : CKEDITOR.dialog.validate.integer( editor.lang.image.validateHSpace ),
+ setup : function( type, element )
+ {
+ if ( type == IMAGE )
+ {
+ var value,
+ marginLeftPx,
+ marginRightPx,
+ marginLeftStyle = element.getStyle( 'margin-left' ),
+ marginRightStyle = element.getStyle( 'margin-right' );
+
+ marginLeftStyle = marginLeftStyle && marginLeftStyle.match( pxLengthRegex );
+ marginRightStyle = marginRightStyle && marginRightStyle.match( pxLengthRegex );
+ marginLeftPx = parseInt( marginLeftStyle, 10 );
+ marginRightPx = parseInt( marginRightStyle, 10 );
+
+ value = ( marginLeftPx == marginRightPx ) && marginLeftPx;
+ isNaN( parseInt( value, 10 ) ) && ( value = element.getAttribute( 'hspace' ) );
+
+ this.setValue( value );
+ }
+ },
+ commit : function( type, element, internalCommit )
+ {
+ var value = parseInt( this.getValue(), 10 );
+ if ( type == IMAGE || type == PREVIEW )
+ {
+ if ( !isNaN( value ) )
+ {
+ element.setStyle( 'margin-left', CKEDITOR.tools.cssLength( value ) );
+ element.setStyle( 'margin-right', CKEDITOR.tools.cssLength( value ) );
+ }
+ else if ( !value && this.isChanged( ) )
+ {
+ element.removeStyle( 'margin-left' );
+ element.removeStyle( 'margin-right' );
+ }
+
+ if ( !internalCommit && type == IMAGE )
+ element.removeAttribute( 'hspace' );
+ }
+ else if ( type == CLEANUP )
+ {
+ element.removeAttribute( 'hspace' );
+ element.removeStyle( 'margin-left' );
+ element.removeStyle( 'margin-right' );
+ }
+ }
+ },
+ {
+ type : 'text',
+ id : 'txtVSpace',
+ width : '60px',
+ label : editor.lang.image.vSpace,
+ 'default' : '',
+ onKeyUp : function()
+ {
+ updatePreview( this.getDialog() );
+ },
+ onChange : function()
+ {
+ commitInternally.call( this, 'advanced:txtdlgGenStyle' );
+ },
+ validate : CKEDITOR.dialog.validate.integer( editor.lang.image.validateVSpace ),
+ setup : function( type, element )
+ {
+ if ( type == IMAGE )
+ {
+ var value,
+ marginTopPx,
+ marginBottomPx,
+ marginTopStyle = element.getStyle( 'margin-top' ),
+ marginBottomStyle = element.getStyle( 'margin-bottom' );
+
+ marginTopStyle = marginTopStyle && marginTopStyle.match( pxLengthRegex );
+ marginBottomStyle = marginBottomStyle && marginBottomStyle.match( pxLengthRegex );
+ marginTopPx = parseInt( marginTopStyle, 10 );
+ marginBottomPx = parseInt( marginBottomStyle, 10 );
+
+ value = ( marginTopPx == marginBottomPx ) && marginTopPx;
+ isNaN ( parseInt( value, 10 ) ) && ( value = element.getAttribute( 'vspace' ) );
+ this.setValue( value );
+ }
+ },
+ commit : function( type, element, internalCommit )
+ {
+ var value = parseInt( this.getValue(), 10 );
+ if ( type == IMAGE || type == PREVIEW )
+ {
+ if ( !isNaN( value ) )
+ {
+ element.setStyle( 'margin-top', CKEDITOR.tools.cssLength( value ) );
+ element.setStyle( 'margin-bottom', CKEDITOR.tools.cssLength( value ) );
+ }
+ else if ( !value && this.isChanged( ) )
+ {
+ element.removeStyle( 'margin-top' );
+ element.removeStyle( 'margin-bottom' );
+ }
+
+ if ( !internalCommit && type == IMAGE )
+ element.removeAttribute( 'vspace' );
+ }
+ else if ( type == CLEANUP )
+ {
+ element.removeAttribute( 'vspace' );
+ element.removeStyle( 'margin-top' );
+ element.removeStyle( 'margin-bottom' );
+ }
+ }
+ },
+ {
+ id : 'cmbAlign',
+ type : 'select',
+ widths : [ '35%','65%' ],
+ style : 'width:90px',
+ label : editor.lang.common.align,
+ 'default' : '',
+ items :
+ [
+ [ editor.lang.common.notSet , ''],
+ [ editor.lang.common.alignLeft , 'left'],
+ [ editor.lang.common.alignRight , 'right']
+ // Backward compatible with v2 on setup when specified as attribute value,
+ // while these values are no more available as select options.
+ // [ editor.lang.image.alignAbsBottom , 'absBottom'],
+ // [ editor.lang.image.alignAbsMiddle , 'absMiddle'],
+ // [ editor.lang.image.alignBaseline , 'baseline'],
+ // [ editor.lang.image.alignTextTop , 'text-top'],
+ // [ editor.lang.image.alignBottom , 'bottom'],
+ // [ editor.lang.image.alignMiddle , 'middle'],
+ // [ editor.lang.image.alignTop , 'top']
+ ],
+ onChange : function()
+ {
+ updatePreview( this.getDialog() );
+ commitInternally.call( this, 'advanced:txtdlgGenStyle' );
+ },
+ setup : function( type, element )
+ {
+ if ( type == IMAGE )
+ {
+ var value = element.getStyle( 'float' );
+ switch( value )
+ {
+ // Ignore those unrelated values.
+ case 'inherit':
+ case 'none':
+ value = '';
+ }
+
+ !value && ( value = ( element.getAttribute( 'align' ) || '' ).toLowerCase() );
+ this.setValue( value );
+ }
+ },
+ commit : function( type, element, internalCommit )
+ {
+ var value = this.getValue();
+ if ( type == IMAGE || type == PREVIEW )
+ {
+ if ( value )
+ element.setStyle( 'float', value );
+ else
+ element.removeStyle( 'float' );
+
+ if ( !internalCommit && type == IMAGE )
+ {
+ value = ( element.getAttribute( 'align' ) || '' ).toLowerCase();
+ switch( value )
+ {
+ // we should remove it only if it matches "left" or "right",
+ // otherwise leave it intact.
+ case 'left':
+ case 'right':
+ element.removeAttribute( 'align' );
+ }
+ }
+ }
+ else if ( type == CLEANUP )
+ element.removeStyle( 'float' );
+
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ type : 'vbox',
+ height : '250px',
+ children :
+ [
+ {
+ type : 'html',
+ id : 'htmlPreview',
+ style : 'width:95%;',
+ html : '<div>' + CKEDITOR.tools.htmlEncode( editor.lang.common.preview ) +'<br>'+
+ '<div id="' + imagePreviewLoaderId + '" class="ImagePreviewLoader" style="display:none"><div class="loading"> </div></div>'+
+ '<div class="ImagePreviewBox"><table><tr><td>'+
+ '<a href="javascript:void(0)" target="_blank" onclick="return false;" id="' + previewLinkId + '">'+
+ '<img id="' + previewImageId + '" alt="" /></a>' +
+ ( editor.config.image_previewText ||
+ 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. '+
+ 'Maecenas feugiat consequat diam. Maecenas metus. Vivamus diam purus, cursus a, commodo non, facilisis vitae, '+
+ 'nulla. Aenean dictum lacinia tortor. Nunc iaculis, nibh non iaculis aliquam, orci felis euismod neque, sed ornare massa mauris sed velit. Nulla pretium mi et risus. Fusce mi pede, tempor id, cursus ac, ullamcorper nec, enim. Sed tortor. Curabitur molestie. Duis velit augue, condimentum at, ultrices a, luctus ut, orci. Donec pellentesque egestas eros. Integer cursus, augue in cursus faucibus, eros pede bibendum sem, in tempus tellus justo quis ligula. Etiam eget tortor. Vestibulum rutrum, est ut placerat elementum, lectus nisl aliquam velit, tempor aliquam eros nunc nonummy metus. In eros metus, gravida a, gravida sed, lobortis id, turpis. Ut ultrices, ipsum at venenatis fringilla, sem nulla lacinia tellus, eget aliquet turpis mauris non enim. Nam turpis. Suspendisse lacinia. Curabitur ac tortor ut ipsum egestas elementum. Nunc imperdiet gravida mauris.' ) +
+ '</td></tr></table></div></div>'
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ id : 'Link',
+ label : editor.lang.link.title,
+ padding : 0,
+ elements :
+ [
+ {
+ id : 'txtUrl',
+ type : 'text',
+ label : editor.lang.common.url,
+ style : 'width: 100%',
+ 'default' : '',
+ setup : function( type, element )
+ {
+ if ( type == LINK )
+ {
+ var href = element.data( 'cke-saved-href' );
+ if ( !href )
+ href = element.getAttribute( 'href' );
+ this.setValue( href );
+ }
+ },
+ commit : function( type, element )
+ {
+ if ( type == LINK )
+ {
+ if ( this.getValue() || this.isChanged() )
+ {
+ var url = decodeURI( this.getValue() );
+ element.data( 'cke-saved-href', url );
+ element.setAttribute( 'href', url );
+
+ if ( this.getValue() || !editor.config.image_removeLinkByEmptyURL )
+ this.getDialog().addLink = true;
+ }
+ }
+ }
+ },
+ {
+ type : 'button',
+ id : 'browse',
+ filebrowser :
+ {
+ action : 'Browse',
+ target: 'Link:txtUrl',
+ url: editor.config.filebrowserImageBrowseLinkUrl
+ },
+ style : 'float:right',
+ hidden : true,
+ label : editor.lang.common.browseServer
+ },
+ {
+ id : 'cmbTarget',
+ type : 'select',
+ label : editor.lang.common.target,
+ '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']
+ ],
+ setup : function( type, element )
+ {
+ if ( type == LINK )
+ this.setValue( element.getAttribute( 'target' ) || '' );
+ },
+ commit : function( type, element )
+ {
+ if ( type == LINK )
+ {
+ if ( this.getValue() || this.isChanged() )
+ element.setAttribute( 'target', this.getValue() );
+ }
+ }
+ }
+ ]
+ },
+ {
+ id : 'Upload',
+ hidden : true,
+ filebrowser : 'uploadButton',
+ label : editor.lang.image.upload,
+ elements :
+ [
+ {
+ type : 'file',
+ id : 'upload',
+ label : editor.lang.image.btnUpload,
+ style: 'height:40px',
+ size : 38
+ },
+ {
+ type : 'fileButton',
+ id : 'uploadButton',
+ filebrowser : 'info:txtUrl',
+ label : editor.lang.image.btnUpload,
+ 'for' : [ 'Upload', 'upload' ]
+ }
+ ]
+ },
+ {
+ id : 'advanced',
+ label : editor.lang.common.advancedTab,
+ elements :
+ [
+ {
+ type : 'hbox',
+ widths : [ '50%', '25%', '25%' ],
+ children :
+ [
+ {
+ type : 'text',
+ id : 'linkId',
+ label : editor.lang.common.id,
+ setup : function( type, element )
+ {
+ if ( type == IMAGE )
+ this.setValue( element.getAttribute( 'id' ) );
+ },
+ commit : function( type, element )
+ {
+ if ( type == IMAGE )
+ {
+ if ( this.getValue() || this.isChanged() )
+ element.setAttribute( 'id', this.getValue() );
+ }
+ }
+ },
+ {
+ id : 'cmbLangDir',
+ type : 'select',
+ style : 'width : 100px;',
+ label : editor.lang.common.langDir,
+ 'default' : '',
+ items :
+ [
+ [ editor.lang.common.notSet, '' ],
+ [ editor.lang.common.langDirLtr, 'ltr' ],
+ [ editor.lang.common.langDirRtl, 'rtl' ]
+ ],
+ setup : function( type, element )
+ {
+ if ( type == IMAGE )
+ this.setValue( element.getAttribute( 'dir' ) );
+ },
+ commit : function( type, element )
+ {
+ if ( type == IMAGE )
+ {
+ if ( this.getValue() || this.isChanged() )
+ element.setAttribute( 'dir', this.getValue() );
+ }
+ }
+ },
+ {
+ type : 'text',
+ id : 'txtLangCode',
+ label : editor.lang.common.langCode,
+ 'default' : '',
+ setup : function( type, element )
+ {
+ if ( type == IMAGE )
+ this.setValue( element.getAttribute( 'lang' ) );
+ },
+ commit : function( type, element )
+ {
+ if ( type == IMAGE )
+ {
+ if ( this.getValue() || this.isChanged() )
+ element.setAttribute( 'lang', this.getValue() );
+ }
+ }
+ }
+ ]
+ },
+ {
+ type : 'text',
+ id : 'txtGenLongDescr',
+ label : editor.lang.common.longDescr,
+ setup : function( type, element )
+ {
+ if ( type == IMAGE )
+ this.setValue( element.getAttribute( 'longDesc' ) );
+ },
+ commit : function( type, element )
+ {
+ if ( type == IMAGE )
+ {
+ if ( this.getValue() || this.isChanged() )
+ element.setAttribute( 'longDesc', this.getValue() );
+ }
+ }
+ },
+ {
+ type : 'hbox',
+ widths : [ '50%', '50%' ],
+ children :
+ [
+ {
+ type : 'text',
+ id : 'txtGenClass',
+ label : editor.lang.common.cssClass,
+ 'default' : '',
+ setup : function( type, element )
+ {
+ if ( type == IMAGE )
+ this.setValue( element.getAttribute( 'class' ) );
+ },
+ commit : function( type, element )
+ {
+ if ( type == IMAGE )
+ {
+ if ( this.getValue() || this.isChanged() )
+ element.setAttribute( 'class', this.getValue() );
+ }
+ }
+ },
+ {
+ type : 'text',
+ id : 'txtGenTitle',
+ label : editor.lang.common.advisoryTitle,
+ 'default' : '',
+ onChange : function()
+ {
+ updatePreview( this.getDialog() );
+ },
+ setup : function( type, element )
+ {
+ if ( type == IMAGE )
+ this.setValue( element.getAttribute( 'title' ) );
+ },
+ commit : function( type, element )
+ {
+ if ( type == IMAGE )
+ {
+ if ( this.getValue() || this.isChanged() )
+ element.setAttribute( 'title', this.getValue() );
+ }
+ else if ( type == PREVIEW )
+ {
+ element.setAttribute( 'title', this.getValue() );
+ }
+ else if ( type == CLEANUP )
+ {
+ element.removeAttribute( 'title' );
+ }
+ }
+ }
+ ]
+ },
+ {
+ type : 'text',
+ id : 'txtdlgGenStyle',
+ label : editor.lang.common.cssStyle,
+ validate : CKEDITOR.dialog.validate.inlineStyle( editor.lang.common.invalidInlineStyle ),
+ 'default' : '',
+ setup : function( type, element )
+ {
+ if ( type == IMAGE )
+ {
+ var genStyle = element.getAttribute( 'style' );
+ if ( !genStyle && element.$.style.cssText )
+ genStyle = element.$.style.cssText;
+ this.setValue( genStyle );
+
+ var height = element.$.style.height,
+ width = element.$.style.width,
+ aMatchH = ( height ? height : '' ).match( regexGetSize ),
+ aMatchW = ( width ? width : '').match( regexGetSize );
+
+ this.attributesInStyle =
+ {
+ height : !!aMatchH,
+ width : !!aMatchW
+ };
+ }
+ },
+ onChange : function ()
+ {
+ commitInternally.call( this,
+ [ 'info:cmbFloat', 'info:cmbAlign',
+ 'info:txtVSpace', 'info:txtHSpace',
+ 'info:txtBorder',
+ 'info:txtWidth', 'info:txtHeight' ] );
+ updatePreview( this );
+ },
+ commit : function( type, element )
+ {
+ if ( type == IMAGE && ( this.getValue() || this.isChanged() ) )
+ {
+ element.setAttribute( 'style', this.getValue() );
+ }
+ }
+ }
+ ]
+ }
+ ]
+ };
+ };
+
+ CKEDITOR.dialog.add( 'image', function( editor )
+ {
+ return imageDialog( editor, 'image' );
+ });
+
+ CKEDITOR.dialog.add( 'imagebutton', function( editor )
+ {
+ return imageDialog( editor, 'imagebutton' );
+ });
+})();
diff --git a/_source/plugins/image/plugin.js b/_source/plugins/image/plugin.js index 79e4f72..f3c6d6d 100644 --- a/_source/plugins/image/plugin.js +++ b/_source/plugins/image/plugin.js @@ -1,81 +1,164 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -/** - * @file Image plugin - */ - -CKEDITOR.plugins.add( 'image', -{ - init : function( editor ) - { - var pluginName = 'image'; - - // Register the dialog. - CKEDITOR.dialog.add( pluginName, this.path + 'dialogs/image.js' ); - - // Register the command. - editor.addCommand( pluginName, new CKEDITOR.dialogCommand( pluginName ) ); - - // Register the toolbar button. - editor.ui.addButton( 'Image', - { - label : editor.lang.common.image, - command : pluginName - }); - - editor.on( 'doubleclick', function( evt ) - { - var element = evt.data.element; - - if ( element.is( 'img' ) && !element.getAttribute( '_cke_realelement' ) ) - evt.data.dialog = 'image'; - }); - - // If the "menu" plugin is loaded, register the menu items. - if ( editor.addMenuItems ) - { - editor.addMenuItems( - { - image : - { - label : editor.lang.image.menu, - command : 'image', - group : 'image' - } - }); - } - - // If the "contextmenu" plugin is loaded, register the listeners. - if ( editor.contextMenu ) - { - editor.contextMenu.addListener( function( element, selection ) - { - if ( !element || !element.is( 'img' ) || element.getAttribute( '_cke_realelement' ) ) - 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 ); - */ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @file Image plugin
+ */
+
+(function()
+{
+
+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 ( getSelectedImage( editor, element ) )
+ return { image : CKEDITOR.TRISTATE_OFF };
+ });
+ }
+ },
+ afterInit : function( editor )
+ {
+ // Customize the behavior of the alignment commands. (#7430)
+ setupAlignCommand( 'left' );
+ setupAlignCommand( 'right' );
+ setupAlignCommand( 'center' );
+ setupAlignCommand( 'block' );
+
+ function setupAlignCommand( value )
+ {
+ var command = editor.getCommand( 'justify' + value );
+ if ( command )
+ {
+ if ( value == 'left' || value == 'right' )
+ {
+ command.on( 'exec', function( evt )
+ {
+ var img = getSelectedImage( editor ), align;
+ if ( img )
+ {
+ align = getImageAlignment( img );
+ if ( align == value )
+ {
+ img.removeStyle( 'float' );
+
+ // Remove "align" attribute when necessary.
+ if ( value == getImageAlignment( img ) )
+ img.removeAttribute( 'align' );
+ }
+ else
+ img.setStyle( 'float', value );
+
+ evt.cancel();
+ }
+ });
+ }
+
+ command.on( 'refresh', function( evt )
+ {
+ var img = getSelectedImage( editor ), align;
+ if ( img )
+ {
+ align = getImageAlignment( img );
+
+ this.setState(
+ ( align == value ) ? CKEDITOR.TRISTATE_ON :
+ ( value == 'right' || value == 'left' ) ? CKEDITOR.TRISTATE_OFF :
+ CKEDITOR.TRISTATE_DISABLED );
+
+ evt.cancel();
+ }
+ });
+ }
+ }
+ }
+});
+
+function getSelectedImage( editor, element )
+{
+ if ( !element )
+ {
+ var sel = editor.getSelection();
+ element = ( sel.getType() == CKEDITOR.SELECTION_ELEMENT ) && sel.getSelectedElement();
+ }
+
+ if ( element && element.is( 'img' ) && !element.data( 'cke-realelement' ) && !element.isReadOnly() )
+ return element;
+}
+
+function getImageAlignment( element )
+{
+ var align = element.getStyle( 'float' );
+
+ if ( align == 'inherit' || align == 'none' )
+ align = 0;
+
+ if ( !align )
+ align = element.getAttribute( 'align' );
+
+ return align;
+}
+
+})();
+
+/**
+ * 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 );
+ */
diff --git a/_source/plugins/indent/plugin.js b/_source/plugins/indent/plugin.js index 1cee4cb..1e3ec49 100644 --- a/_source/plugins/indent/plugin.js +++ b/_source/plugins/indent/plugin.js @@ -1,422 +1,480 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -/** - * @file Increse and decrease indent commands. - */ - -(function() -{ - var listNodeNames = { ol : 1, ul : 1 }; - - var isNotWhitespaces = CKEDITOR.dom.walker.whitespaces( true ), - isNotBookmark = CKEDITOR.dom.walker.bookmark( false, true ); - - function setState( editor, state ) - { - editor.getCommand( this.name ).setState( state ); - } - - function onSelectionChange( evt ) - { - var editor = evt.editor; - - var elementPath = evt.data.path, - list = elementPath && elementPath.contains( listNodeNames ); - - if ( list ) - return setState.call( this, editor, CKEDITOR.TRISTATE_OFF ); - - if ( !this.useIndentClasses && this.name == 'indent' ) - return setState.call( this, editor, CKEDITOR.TRISTATE_OFF ); - - var path = evt.data.path, - firstBlock = path.block || path.blockLimit; - if ( !firstBlock ) - return setState.call( this, editor, CKEDITOR.TRISTATE_DISABLED ); - - if ( this.useIndentClasses ) - { - var indentClass = firstBlock.$.className.match( this.classNameRegex ), - indentStep = 0; - if ( indentClass ) - { - indentClass = indentClass[1]; - indentStep = this.indentClassMap[ indentClass ]; - } - if ( ( this.name == 'outdent' && !indentStep ) || - ( this.name == 'indent' && indentStep == editor.config.indentClasses.length ) ) - return setState.call( this, editor, CKEDITOR.TRISTATE_DISABLED ); - return setState.call( this, editor, CKEDITOR.TRISTATE_OFF ); - } - else - { - var indent = parseInt( firstBlock.getStyle( this.indentCssProperty ), 10 ); - if ( isNaN( indent ) ) - indent = 0; - if ( indent <= 0 ) - return setState.call( this, editor, CKEDITOR.TRISTATE_DISABLED ); - return setState.call( this, editor, CKEDITOR.TRISTATE_OFF ); - } - } - - function indentList( editor, range, 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 = this.name == 'indent' ? 1 : -1, - startItem = itemsToMove[0], - lastItem = itemsToMove[ itemsToMove.length - 1 ], - database = {}; - - // 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, 0 ); - - // Avoid nested <li> after outdent even they're visually same, - // recording them for later refactoring.(#3982) - if ( this.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 ); - } - } - - // Clean up the markers. - CKEDITOR.dom.element.clearAllMarkers( database ); - } - - function indentBlock( editor, range ) - { - var iterator = range.createIterator(), - enterMode = editor.config.enterMode; - iterator.enforceRealBlocks = true; - iterator.enlargeBr = enterMode != CKEDITOR.ENTER_BR; - var block; - while ( ( block = iterator.getNextParagraph() ) ) - indentElement.call( this, editor, block ); - } - - function indentElement( editor, element ) - { - if ( this.useIndentClasses ) - { - // Transform current class name to indent step index. - var indentClass = element.$.className.match( this.classNameRegex ), - indentStep = 0; - if ( indentClass ) - { - indentClass = indentClass[1]; - indentStep = this.indentClassMap[ indentClass ]; - } - - // Operate on indent step index, transform indent step index back to class - // name. - if ( this.name == 'outdent' ) - indentStep--; - else - indentStep++; - - if ( indentStep < 0 ) - return false; - - indentStep = Math.min( indentStep, editor.config.indentClasses.length ); - indentStep = Math.max( indentStep, 0 ); - var className = CKEDITOR.tools.ltrim( element.$.className.replace( this.classNameRegex, '' ) ); - if ( indentStep < 1 ) - element.$.className = className; - else - element.addClass( editor.config.indentClasses[ indentStep - 1 ] ); - } - else - { - var currentOffset = parseInt( element.getStyle( this.indentCssProperty ), 10 ); - if ( isNaN( currentOffset ) ) - currentOffset = 0; - currentOffset += ( this.name == 'indent' ? 1 : -1 ) * editor.config.indentOffset; - - if ( currentOffset < 0 ) - return false; - - currentOffset = Math.max( currentOffset, 0 ); - currentOffset = Math.ceil( currentOffset / editor.config.indentOffset ) * editor.config.indentOffset; - element.setStyle( this.indentCssProperty, currentOffset ? currentOffset + editor.config.indentUnit : '' ); - if ( element.getAttribute( 'style' ) === '' ) - element.removeAttribute( 'style' ); - } - - return true; - } - - 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; - } - else - this.indentCssProperty = editor.config.contentsLangDirection == 'ltr' ? 'margin-left' : 'margin-right'; - this.startDisabled = name == 'outdent'; - } - - function isListItem( node ) - { - return node.type = CKEDITOR.NODE_ELEMENT && node.is( 'li' ); - } - - indentCommand.prototype = { - exec : function( editor ) - { - var selection = editor.getSelection(), - range = selection && selection.getRanges()[0]; - - var startContainer = range.startContainer, - endContainer = range.endContainer, - rangeRoot = range.getCommonAncestor(), - nearestListBlock = rangeRoot; - - while ( nearestListBlock && !( nearestListBlock.type == CKEDITOR.NODE_ELEMENT && - listNodeNames[ nearestListBlock.getName() ] ) ) - nearestListBlock = nearestListBlock.getParent(); - - // Avoid selection anchors under list root. - // <ul>[<li>...</li>]</ul> => <ul><li>[...]</li></ul> - if ( nearestListBlock && startContainer.type == CKEDITOR.NODE_ELEMENT - && startContainer.getName() in listNodeNames ) - { - var walker = new CKEDITOR.dom.walker( range ); - walker.evaluator = isListItem; - range.startContainer = walker.next(); - } - - if ( nearestListBlock && endContainer.type == CKEDITOR.NODE_ELEMENT - && endContainer.getName() in listNodeNames ) - { - walker = new CKEDITOR.dom.walker( range ); - walker.evaluator = isListItem; - range.endContainer = walker.previous(); - } - - var bookmarks = selection.createBookmarks( true ); - - if ( nearestListBlock ) - { - var firstListItem = nearestListBlock.getFirst( function( node ) - { - return node.type == CKEDITOR.NODE_ELEMENT && node.is( 'li' ); - }), - rangeStart = range.startContainer, - indentWholeList = firstListItem.equals( rangeStart ) || firstListItem.contains( rangeStart ); - - // Indent the entire list if cursor is inside the first list item. (#3893) - if ( !( indentWholeList && indentElement.call( this, editor, nearestListBlock ) ) ) - indentList.call( this, editor, range, nearestListBlock ); - } - else - indentBlock.call( this, editor, range ); - - editor.focus(); - editor.forceNextSelectionCheck(); - selection.selectBookmarks( bookmarks ); - } - }; - - CKEDITOR.plugins.add( 'indent', - { - init : function( editor ) - { - // Register commands. - var indent = new indentCommand( editor, 'indent' ), - outdent = new indentCommand( editor, 'outdent' ); - editor.addCommand( 'indent', indent ); - editor.addCommand( 'outdent', outdent ); - - // Register the toolbar buttons. - editor.ui.addButton( 'Indent', - { - label : editor.lang.indent, - command : 'indent' - }); - editor.ui.addButton( 'Outdent', - { - label : editor.lang.outdent, - command : 'outdent' - }); - - // Register the state changing handlers. - editor.on( 'selectionChange', CKEDITOR.tools.bind( onSelectionChange, indent ) ); - editor.on( 'selectionChange', CKEDITOR.tools.bind( onSelectionChange, outdent ) ); - - // [IE6/7] Raw lists are using margin instead of padding for visual indentation in wysiwyg mode. (#3893) - if ( CKEDITOR.env.ie6Compat || CKEDITOR.env.ie7Compat ) - { - editor.addCss( - "ul,ol" + - "{" + - " margin-left: 0px;" + - " padding-left: 40px;" + - "}" ); - } - }, - - requires : [ 'domiterator', 'list' ] - } ); -})(); - -CKEDITOR.tools.extend( CKEDITOR.config, - { - indentOffset : 40, - indentUnit : 'px', - indentClasses : null - }); - -/** - * Size of each indentation step - * @type Number - * @example - * config.indentOffset = 40; - */ - - /** - * Unit for the indentation style - * @type String - * @example - * config.indentUnit = 'px'; - */ - - /** - * List of classes to use for indenting the contents. - * @type Array - * @example - * // Don't use classes for indenting. (this is the default value) - * config.indentClasses = null; - * @example - * // Use the classes 'Indent1', 'Indent2', 'Indent3' - * config.indentClasses = ['Indent1', 'Indent2', 'Indent3']; - */ - -/** - * Size of each indentation step - * @type Number - * @default 40 - * @example - * config.indentOffset = 4; - */ - - /** - * Unit for the indentation style - * @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. - * @type Array - * default null - * @example - * // Use the classes 'Indent1', 'Indent2', 'Indent3' - * config.indentClasses = ['Indent1', 'Indent2', 'Indent3']; - */ +/*
+Copyright (c) 2003-2012, 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' );
+ }
+ }
+ });
+
+ editor.on( 'key', function( evt )
+ {
+ // Backspace at the beginning of list item should outdent it.
+ if ( editor.mode == 'wysiwyg' && evt.data.keyCode == 8 )
+ {
+ var sel = editor.getSelection(),
+ range = sel.getRanges()[ 0 ],
+ li;
+
+ if ( range.collapsed &&
+ ( li = range.startContainer.getAscendant( 'li', 1 ) ) &&
+ range.checkBoundaryOfElement( li, CKEDITOR.START ) )
+ {
+ editor.execCommand( 'outdent' );
+ evt.cancel();
+ }
+ }
+ });
+ },
+
+ 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'];
+ */
diff --git a/_source/plugins/justify/plugin.js b/_source/plugins/justify/plugin.js index a3ff0dd..1b3aaa5 100644 --- a/_source/plugins/justify/plugin.js +++ b/_source/plugins/justify/plugin.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
@@ -9,37 +9,47 @@ For licensing, see LICENSE.html or http://ckeditor.com/license (function()
{
- var alignRemoveRegex = /(-moz-|-webkit-|start|auto)/i;
-
- function getState( editor, path )
+ function getAlignment( element, useComputedState )
{
- var firstBlock = path.block || path.blockLimit;
+ 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' ) || '';
+ }
+
+ // Sometimes computed values doesn't tell.
+ align && ( align = align.replace( /(?:-(?:moz|webkit)-)?(?:start|auto)/i, '' ) );
- if ( !firstBlock || firstBlock.getName() == 'body' )
- return CKEDITOR.TRISTATE_OFF;
+ !align && useComputedState && ( align = element.getComputedStyle( 'direction' ) == 'rtl' ? 'right' : 'left' );
- var currentAlign = firstBlock.getComputedStyle( 'text-align' ).replace( alignRemoveRegex, '' );
- if ( ( !currentAlign && this.isDefaultAlign ) || currentAlign == this.value )
- return CKEDITOR.TRISTATE_ON;
- return CKEDITOR.TRISTATE_OFF;
+ return align;
}
function onSelectionChange( evt )
{
- var command = evt.editor.getCommand( this.name );
- command.state = getState.call( this, evt.editor, evt.data.path );
- command.fire( 'state' );
+ if ( evt.editor.readOnly )
+ return;
+
+ evt.editor.getCommand( this.name ).refresh( evt.data.path );
}
function justifyCommand( editor, name, value )
{
+ this.editor = editor;
this.name = name;
this.value = value;
- var contentDir = editor.config.contentsLangDirection;
- this.isDefaultAlign = ( value == 'left' && contentDir == 'ltr' ) ||
- ( value == 'right' && contentDir == 'rtl' );
-
var classes = editor.config.justifyClasses;
if ( classes )
{
@@ -63,6 +73,59 @@ For licensing, see LICENSE.html or http://ckeditor.com/license }
}
+ 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 )
{
@@ -73,40 +136,43 @@ For licensing, see LICENSE.html or http://ckeditor.com/license return;
var bookmarks = selection.createBookmarks(),
- ranges = selection.getRanges();
-
+ 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() ) )
+ 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 )
{
- // Remove any of the alignment classes from the className.
- var className = block.$.className =
- CKEDITOR.tools.ltrim( block.$.className.replace( this.cssClassRegex, '' ) );
-
// Append the desired class name.
- if ( this.state == CKEDITOR.TRISTATE_OFF && !this.isDefaultAlign )
+ if ( apply )
block.addClass( cssClassName );
else if ( !className )
block.removeAttribute( 'class' );
}
- else
- {
- if ( this.state == CKEDITOR.TRISTATE_OFF && !this.isDefaultAlign )
- block.setStyle( 'text-align', this.value );
- else
- block.removeStyle( 'text-align' );
- }
+ else if ( apply )
+ block.setStyle( 'text-align', this.value );
}
}
@@ -114,6 +180,16 @@ For licensing, see LICENSE.html or http://ckeditor.com/license editor.focus();
editor.forceNextSelectionCheck();
selection.selectBookmarks( bookmarks );
+ },
+
+ refresh : function( path )
+ {
+ var firstBlock = path.block || path.blockLimit;
+
+ this.setState( firstBlock.getName() != 'body' &&
+ getAlignment( firstBlock, this.editor.config.useComputedState ) == this.value ?
+ CKEDITOR.TRISTATE_ON :
+ CKEDITOR.TRISTATE_OFF );
}
};
@@ -156,13 +232,20 @@ For licensing, see LICENSE.html or http://ckeditor.com/license 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' ]
});
})();
-CKEDITOR.tools.extend( CKEDITOR.config,
- {
- justifyClasses : null
- } );
+ /**
+ * 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' ];
+ */
diff --git a/_source/plugins/keystrokes/plugin.js b/_source/plugins/keystrokes/plugin.js index 73c0085..d0dc82e 100644 --- a/_source/plugins/keystrokes/plugin.js +++ b/_source/plugins/keystrokes/plugin.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
@@ -28,14 +28,10 @@ CKEDITOR.plugins.add( '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;
- }
}
});
@@ -214,13 +210,13 @@ CKEDITOR.config.keystrokes = [ CKEDITOR.CTRL + 73 /*I*/, 'italic' ],
[ CKEDITOR.CTRL + 85 /*U*/, 'underline' ],
- [ CKEDITOR.ALT + 109 /*-*/, 'toolbarCollapse' ],
+ [ 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#key
+ * @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
diff --git a/_source/plugins/link/dialogs/anchor.js b/_source/plugins/link/dialogs/anchor.js index 1d28b27..1e5b0ae 100644 --- a/_source/plugins/link/dialogs/anchor.js +++ b/_source/plugins/link/dialogs/anchor.js @@ -1,73 +1,118 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.dialog.add( 'anchor', function( editor )
{
// Function called in onShow to load selected element.
- var loadElements = function( editor, selection, element )
+ var loadElements = function( element )
{
- this.editMode = true;
- this.editObj = element;
+ this._.selectedElement = element;
- var attributeValue = this.editObj.getAttribute( 'name' );
- if ( attributeValue )
- this.setValueOf( 'info','txtName', attributeValue );
- else
- this.setValueOf( 'info','txtName', "" );
+ 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()
{
- // Always create a new anchor, because of IE BUG.
- var name = this.getValueOf( 'info', 'txtName' ),
- element = CKEDITOR.env.ie ?
- editor.document.createElement( '<a name="' + CKEDITOR.tools.htmlEncode( name ) + '">' ) :
- editor.document.createElement( 'a' );
+ var name = this.getValueOf( 'info', 'txtName' );
+ var attributes =
+ {
+ name : name,
+ 'data-cke-saved-name' : name
+ };
- // Move contents and attributes of old anchor to new anchor.
- if ( this.editMode )
+ if ( this._.selectedElement )
{
- this.editObj.copyAttributes( element, { name : 1 } );
- this.editObj.moveChildren( element );
+ 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 );
}
-
- // Set name.
- element.removeAttribute( '_cke_saved_name' );
- element.setAttribute( 'name', name );
-
- // Insert a new anchor.
- var fakeElement = editor.createFakeElement( element, 'cke_anchor', 'anchor' );
- if ( !this.editMode )
- editor.insertElement( fakeElement );
else
{
- fakeElement.replace( this.fakeObj );
- editor.getSelection().selectElement( fakeElement );
+ 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 );
+ }
}
+ },
- return true;
+ onHide : function()
+ {
+ delete this._.selectedElement;
},
+
onShow : function()
{
- this.editObj = false;
- this.fakeObj = false;
- this.editMode = false;
+ var selection = editor.getSelection(),
+ fullySelected = selection.getSelectedElement(),
+ partialSelected;
- var selection = editor.getSelection();
- var element = selection.getSelectedElement();
- if ( element && element.getAttribute( '_cke_real_element_type' ) && element.getAttribute( '_cke_real_element_type' ) == 'anchor' )
+ // Detect the anchor under selection.
+ if ( fullySelected )
{
- this.fakeObj = element;
- element = editor.restoreRealElement( this.fakeObj );
- loadElements.apply( this, [ editor, selection, element ] );
- selection.selectElement( this.fakeObj );
+ 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 : [
diff --git a/_source/plugins/link/dialogs/link.js b/_source/plugins/link/dialogs/link.js index 696183e..238113a 100644 --- a/_source/plugins/link/dialogs/link.js +++ b/_source/plugins/link/dialogs/link.js @@ -1,1413 +1,1427 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -CKEDITOR.dialog.add( 'link', function( editor ) -{ - var plugin = CKEDITOR.plugins.link; - // Handles the event when the "Target" selection box is changed. - var targetChanged = function() - { - var dialog = this.getDialog(), - popupFeatures = dialog.getContentElement( 'target', 'popupFeatures' ), - targetName = dialog.getContentElement( 'target', 'linkTargetName' ), - value = this.getValue(); - - if ( !popupFeatures || !targetName ) - return; - - popupFeatures = popupFeatures.getElement(); - popupFeatures.hide(); - targetName.setValue( '' ); - - switch ( value ) - { - case 'frame' : - targetName.setLabel( editor.lang.link.targetFrameName ); - targetName.getElement().show(); - break; - case 'popup' : - popupFeatures.show(); - targetName.setLabel( editor.lang.link.targetPopupName ); - targetName.getElement().show(); - break; - default : - targetName.setValue( value ); - targetName.getElement().hide(); - break; - } - - }; - - // Handles the event when the "Type" selection box is changed. - var linkTypeChanged = function() - { - var dialog = this.getDialog(), - partIds = [ 'urlOptions', 'anchorOptions', 'emailOptions' ], - typeValue = this.getValue(), - uploadTab = dialog.definition.getContents( 'upload' ), - uploadInitiallyHidden = uploadTab && uploadTab.hidden; - - if ( typeValue == 'url' ) - { - if ( editor.config.linkShowTargetTab ) - dialog.showPage( 'target' ); - if ( !uploadInitiallyHidden ) - dialog.showPage( 'upload' ); - } - else - { - dialog.hidePage( 'target' ); - if ( !uploadInitiallyHidden ) - dialog.hidePage( 'upload' ); - } - - for ( var i = 0 ; i < partIds.length ; i++ ) - { - var element = dialog.getContentElement( 'info', partIds[i] ); - if ( !element ) - continue; - - element = element.getElement().getParent().getParent(); - if ( partIds[i] == typeValue + 'Options' ) - element.show(); - else - element.hide(); - } - }; - - // Loads the parameters in a selected link to the link dialog fields. - var javascriptProtocolRegex = /^javascript:/, - emailRegex = /^mailto:([^?]+)(?:\?(.+))?$/, - emailSubjectRegex = /subject=([^;?:@&=$,\/]*)/, - emailBodyRegex = /body=([^;?:@&=$,\/]*)/, - anchorRegex = /^#(.*)$/, - urlRegex = /^((?:http|https|ftp|news):\/\/)?(.*)$/, - selectableTargets = /^(_(?:self|top|parent|blank))$/, - encodedEmailLinkRegex = /^javascript:void\(location\.href='mailto:'\+String\.fromCharCode\(([^)]+)\)(?:\+'(.*)')?\)$/, - functionCallProtectedEmailLinkRegex = /^javascript:([^(]+)\(([^)]+)\)$/; - - var popupRegex = - /\s*window.open\(\s*this\.href\s*,\s*(?:'([^']*)'|null)\s*,\s*'([^']*)'\s*\)\s*;\s*return\s*false;*\s*/; - var popupFeaturesRegex = /(?:^|,)([^=]+)=(\d+|yes|no)/gi; - - var parseLink = function( editor, element ) - { - var href = ( element && ( element.getAttribute( '_cke_saved_href' ) || element.getAttribute( 'href' ) ) ) || '', - javascriptMatch, - emailMatch, - anchorMatch, - urlMatch, - retval = {}; - - if ( ( javascriptMatch = href.match( javascriptProtocolRegex ) ) ) - { - if ( emailProtection == 'encode' ) - { - href = href.replace( encodedEmailLinkRegex, - function ( match, protectedAddress, rest ) - { - return 'mailto:' + - String.fromCharCode.apply( String, protectedAddress.split( ',' ) ) + - ( rest && unescapeSingleQuote( rest ) ); - }); - } - // Protected email link as function call. - else if ( emailProtection ) - { - href.replace( functionCallProtectedEmailLinkRegex, function( match, funcName, funcArgs ) - { - if ( funcName == compiledProtectionFunction.name ) - { - retval.type = 'email'; - var email = retval.email = {}; - - var paramRegex = /[^,\s]+/g, - paramQuoteRegex = /(^')|('$)/g, - paramsMatch = funcArgs.match( paramRegex ), - paramsMatchLength = paramsMatch.length, - paramName, - paramVal; - - for ( var i = 0; i < paramsMatchLength; i++ ) - { - paramVal = decodeURIComponent( unescapeSingleQuote( paramsMatch[ i ].replace( paramQuoteRegex, '' ) ) ); - paramName = compiledProtectionFunction.params[ i ].toLowerCase(); - email[ paramName ] = paramVal; - } - email.address = [ email.name, email.domain ].join( '@' ); - } - } ); - } - } - - if ( !retval.type ) - { - if ( ( anchorMatch = href.match( anchorRegex ) ) ) - { - retval.type = 'anchor'; - retval.anchor = {}; - retval.anchor.name = retval.anchor.id = anchorMatch[1]; - } - // Protected email link as encoded string. - else if ( ( emailMatch = href.match( emailRegex ) ) ) - { - var subjectMatch = href.match( emailSubjectRegex ), - bodyMatch = href.match( emailBodyRegex ); - - retval.type = 'email'; - var email = ( retval.email = {} ); - email.address = emailMatch[ 1 ]; - subjectMatch && ( email.subject = decodeURIComponent( subjectMatch[ 1 ] ) ); - bodyMatch && ( email.body = decodeURIComponent( bodyMatch[ 1 ] ) ); - } - // urlRegex matches empty strings, so need to check for href as well. - else if ( href && ( urlMatch = href.match( urlRegex ) ) ) - { - retval.type = 'url'; - retval.url = {}; - retval.url.protocol = urlMatch[1]; - retval.url.url = urlMatch[2]; - } - else - retval.type = 'url'; - } - - // Load target and popup settings. - if ( element ) - { - var target = element.getAttribute( 'target' ); - retval.target = {}; - retval.adv = {}; - - // IE BUG: target attribute is an empty string instead of null in IE if it's not set. - if ( !target ) - { - var onclick = element.getAttribute( '_cke_pa_onclick' ) || element.getAttribute( 'onclick' ), - onclickMatch = onclick && onclick.match( popupRegex ); - if ( onclickMatch ) - { - retval.target.type = 'popup'; - retval.target.name = onclickMatch[1]; - - var featureMatch; - while ( ( featureMatch = popupFeaturesRegex.exec( onclickMatch[2] ) ) ) - { - if ( featureMatch[2] == 'yes' || featureMatch[2] == '1' ) - retval.target[ featureMatch[1] ] = true; - else if ( isFinite( featureMatch[2] ) ) - retval.target[ featureMatch[1] ] = featureMatch[2]; - } - } - } - else - { - var targetMatch = target.match( selectableTargets ); - if ( targetMatch ) - retval.target.type = retval.target.name = target; - else - { - retval.target.type = 'frame'; - retval.target.name = target; - } - } - - var me = this; - var advAttr = function( inputName, attrName ) - { - var value = element.getAttribute( attrName ); - if ( value !== null ) - retval.adv[ inputName ] = value || ''; - }; - advAttr( 'advId', 'id' ); - advAttr( 'advLangDir', 'dir' ); - advAttr( 'advAccessKey', 'accessKey' ); - advAttr( 'advName', 'name' ); - advAttr( 'advLangCode', 'lang' ); - advAttr( 'advTabIndex', 'tabindex' ); - advAttr( 'advTitle', 'title' ); - advAttr( 'advContentType', 'type' ); - advAttr( 'advCSSClasses', 'class' ); - advAttr( 'advCharset', 'charset' ); - advAttr( 'advStyles', 'style' ); - } - - // Find out whether we have any anchors in the editor. - // Get all IMG elements in CK document. - var elements = editor.document.getElementsByTag( 'img' ), - realAnchors = new CKEDITOR.dom.nodeList( editor.document.$.anchors ), - anchors = retval.anchors = []; - - for ( var i = 0; i < elements.count() ; i++ ) - { - var item = elements.getItem( i ); - if ( item.getAttribute( '_cke_realelement' ) && item.getAttribute( '_cke_real_element_type' ) == 'anchor' ) - { - anchors.push( editor.restoreRealElement( item ) ); - } - } - - for ( i = 0 ; i < realAnchors.count() ; i++ ) - anchors.push( realAnchors.getItem( i ) ); - - for ( i = 0 ; i < anchors.length ; i++ ) - { - item = anchors[ i ]; - anchors[ i ] = { name : item.getAttribute( 'name' ), id : item.getAttribute( 'id' ) }; - } - - // Record down the selected element in the dialog. - this._.selectedElement = element; - - return retval; - }; - - var setupParams = function( page, data ) - { - if ( data[page] ) - this.setValue( data[page][this.id] || '' ); - }; - - var setupPopupParams = function( data ) - { - return setupParams.call( this, 'target', data ); - }; - - var setupAdvParams = function( data ) - { - return setupParams.call( this, 'adv', data ); - }; - - var commitParams = function( page, data ) - { - if ( !data[page] ) - data[page] = {}; - - data[page][this.id] = this.getValue() || ''; - }; - - var commitPopupParams = function( data ) - { - return commitParams.call( this, 'target', data ); - }; - - var commitAdvParams = function( data ) - { - return commitParams.call( this, 'adv', data ); - }; - - function unescapeSingleQuote( str ) - { - return str.replace( /\\'/g, '\'' ); - } - - function escapeSingleQuote( str ) - { - return str.replace( /'/g, '\\$&' ); - } - - var emailProtection = editor.config.emailProtection || ''; - - // Compile the protection function pattern. - if ( emailProtection && emailProtection != 'encode' ) - { - var compiledProtectionFunction = {}; - - emailProtection.replace( /^([^(]+)\(([^)]+)\)$/, function( match, funcName, params ) - { - compiledProtectionFunction.name = funcName; - compiledProtectionFunction.params = []; - params.replace( /[^,\s]+/g, function( param ) - { - compiledProtectionFunction.params.push( param ); - } ); - } ); - } - - function protectEmailLinkAsFunction( email ) - { - var retval, - name = compiledProtectionFunction.name, - params = compiledProtectionFunction.params, - paramName, - paramValue; - - retval = [ name, '(' ]; - for ( var i = 0; i < params.length; i++ ) - { - paramName = params[ i ].toLowerCase(); - paramValue = email[ paramName ]; - - i > 0 && retval.push( ',' ); - retval.push( '\'', - paramValue ? - escapeSingleQuote( encodeURIComponent( email[ paramName ] ) ) - : '', - '\''); - } - retval.push( ')' ); - return retval.join( '' ); - } - - function protectEmailAddressAsEncodedString( address ) - { - var charCode, - length = address.length, - encodedChars = []; - for ( var i = 0; i < length; i++ ) - { - charCode = address.charCodeAt( i ); - encodedChars.push( charCode ); - } - return 'String.fromCharCode(' + encodedChars.join( ',' ) + ')'; - } - - return { - title : editor.lang.link.title, - minWidth : 350, - minHeight : 230, - contents : [ - { - id : 'info', - label : editor.lang.link.info, - title : editor.lang.link.info, - elements : - [ - { - id : 'linkType', - type : 'select', - label : editor.lang.link.type, - 'default' : 'url', - items : - [ - [ editor.lang.link.toUrl, 'url' ], - [ editor.lang.link.toAnchor, 'anchor' ], - [ editor.lang.link.toEmail, 'email' ] - ], - onChange : linkTypeChanged, - setup : function( data ) - { - if ( data.type ) - this.setValue( data.type ); - }, - commit : function( data ) - { - data.type = this.getValue(); - } - }, - { - type : 'vbox', - id : 'urlOptions', - children : - [ - { - type : 'hbox', - widths : [ '25%', '75%' ], - children : - [ - { - id : 'protocol', - type : 'select', - label : editor.lang.common.protocol, - 'default' : 'http://', - items : - [ - // Force 'ltr' for protocol names in BIDI. (#5433) - [ 'http://\u200E', 'http://' ], - [ 'https://\u200E', 'https://' ], - [ 'ftp://\u200E', 'ftp://' ], - [ 'news://\u200E', 'news://' ], - [ editor.lang.link.other , '' ] - ], - setup : function( data ) - { - if ( data.url ) - this.setValue( data.url.protocol || '' ); - }, - commit : function( data ) - { - if ( !data.url ) - data.url = {}; - - data.url.protocol = this.getValue(); - } - }, - { - type : 'text', - id : 'url', - label : editor.lang.common.url, - required: true, - onLoad : function () - { - this.allowOnChange = true; - }, - onKeyUp : function() - { - this.allowOnChange = false; - var protocolCmb = this.getDialog().getContentElement( 'info', 'protocol' ), - url = this.getValue(), - urlOnChangeProtocol = /^(http|https|ftp|news):\/\/(?=.)/gi, - urlOnChangeTestOther = /^((javascript:)|[#\/\.\?])/gi; - - var protocol = urlOnChangeProtocol.exec( url ); - if ( protocol ) - { - this.setValue( url.substr( protocol[ 0 ].length ) ); - protocolCmb.setValue( protocol[ 0 ].toLowerCase() ); - } - else if ( urlOnChangeTestOther.test( url ) ) - protocolCmb.setValue( '' ); - - this.allowOnChange = true; - }, - onChange : function() - { - if ( this.allowOnChange ) // Dont't call on dialog load. - this.onKeyUp(); - }, - validate : function() - { - var dialog = this.getDialog(); - - if ( dialog.getContentElement( 'info', 'linkType' ) && - dialog.getValueOf( 'info', 'linkType' ) != 'url' ) - return true; - - if ( this.getDialog().fakeObj ) // Edit Anchor. - return true; - - var func = CKEDITOR.dialog.validate.notEmpty( editor.lang.link.noUrl ); - return func.apply( this ); - }, - setup : function( data ) - { - this.allowOnChange = false; - if ( data.url ) - this.setValue( data.url.url ); - this.allowOnChange = true; - - }, - commit : function( data ) - { - // IE will not trigger the onChange event if the mouse has been used - // to carry all the operations #4724 - this.onChange(); - - if ( !data.url ) - data.url = {}; - - data.url.url = this.getValue(); - this.allowOnChange = false; - } - } - ], - setup : function( data ) - { - if ( !this.getDialog().getContentElement( 'info', 'linkType' ) ) - this.getElement().show(); - } - }, - { - type : 'button', - id : 'browse', - hidden : 'true', - filebrowser : 'info:url', - label : editor.lang.common.browseServer - } - ] - }, - { - type : 'vbox', - id : 'anchorOptions', - width : 260, - align : 'center', - padding : 0, - children : - [ - { - type : 'fieldset', - id : 'selectAnchorText', - label : editor.lang.link.selectAnchor, - setup : function( data ) - { - if ( data.anchors.length > 0 ) - this.getElement().show(); - else - this.getElement().hide(); - }, - children : - [ - { - type : 'hbox', - id : 'selectAnchor', - children : - [ - { - type : 'select', - id : 'anchorName', - 'default' : '', - label : editor.lang.link.anchorName, - style : 'width: 100%;', - items : - [ - [ '' ] - ], - setup : function( data ) - { - this.clear(); - this.add( '' ); - for ( var i = 0 ; i < data.anchors.length ; i++ ) - { - if ( data.anchors[i].name ) - this.add( data.anchors[i].name ); - } - - if ( data.anchor ) - this.setValue( data.anchor.name ); - - var linkType = this.getDialog().getContentElement( 'info', 'linkType' ); - if ( linkType && linkType.getValue() == 'email' ) - this.focus(); - }, - commit : function( data ) - { - if ( !data.anchor ) - data.anchor = {}; - - data.anchor.name = this.getValue(); - } - }, - { - type : 'select', - id : 'anchorId', - 'default' : '', - label : editor.lang.link.anchorId, - style : 'width: 100%;', - items : - [ - [ '' ] - ], - setup : function( data ) - { - this.clear(); - this.add( '' ); - for ( var i = 0 ; i < data.anchors.length ; i++ ) - { - if ( data.anchors[i].id ) - this.add( data.anchors[i].id ); - } - - if ( data.anchor ) - this.setValue( data.anchor.id ); - }, - commit : function( data ) - { - if ( !data.anchor ) - data.anchor = {}; - - data.anchor.id = this.getValue(); - } - } - ], - setup : function( data ) - { - if ( data.anchors.length > 0 ) - this.getElement().show(); - else - this.getElement().hide(); - } - } - ] - }, - { - type : 'html', - id : 'noAnchors', - style : 'text-align: center;', - html : '<div role="label" tabIndex="-1">' + CKEDITOR.tools.htmlEncode( editor.lang.link.noAnchors ) + '</div>', - // Focus the first element defined in above html. - focus : true, - setup : function( data ) - { - if ( data.anchors.length < 1 ) - this.getElement().show(); - else - this.getElement().hide(); - } - } - ], - setup : function( data ) - { - if ( !this.getDialog().getContentElement( 'info', 'linkType' ) ) - this.getElement().hide(); - } - }, - { - type : 'vbox', - id : 'emailOptions', - padding : 1, - children : - [ - { - type : 'text', - id : 'emailAddress', - label : editor.lang.link.emailAddress, - required : true, - validate : function() - { - var dialog = this.getDialog(); - - if ( !dialog.getContentElement( 'info', 'linkType' ) || - dialog.getValueOf( 'info', 'linkType' ) != 'email' ) - return true; - - var func = CKEDITOR.dialog.validate.notEmpty( editor.lang.link.noEmail ); - return func.apply( this ); - }, - setup : function( data ) - { - if ( data.email ) - this.setValue( data.email.address ); - - var linkType = this.getDialog().getContentElement( 'info', 'linkType' ); - if ( linkType && linkType.getValue() == 'email' ) - this.select(); - }, - commit : function( data ) - { - if ( !data.email ) - data.email = {}; - - data.email.address = this.getValue(); - } - }, - { - type : 'text', - id : 'emailSubject', - label : editor.lang.link.emailSubject, - setup : function( data ) - { - if ( data.email ) - this.setValue( data.email.subject ); - }, - commit : function( data ) - { - if ( !data.email ) - data.email = {}; - - data.email.subject = this.getValue(); - } - }, - { - type : 'textarea', - id : 'emailBody', - label : editor.lang.link.emailBody, - rows : 3, - 'default' : '', - setup : function( data ) - { - if ( data.email ) - this.setValue( data.email.body ); - }, - commit : function( data ) - { - if ( !data.email ) - data.email = {}; - - data.email.body = this.getValue(); - } - } - ], - setup : function( data ) - { - if ( !this.getDialog().getContentElement( 'info', 'linkType' ) ) - this.getElement().hide(); - } - } - ] - }, - { - id : 'target', - label : editor.lang.link.target, - title : editor.lang.link.target, - elements : - [ - { - type : 'hbox', - widths : [ '50%', '50%' ], - children : - [ - { - type : 'select', - id : 'linkTargetType', - label : editor.lang.common.target, - 'default' : 'notSet', - style : 'width : 100%;', - 'items' : - [ - [ editor.lang.common.notSet, 'notSet' ], - [ editor.lang.link.targetFrame, 'frame' ], - [ editor.lang.link.targetPopup, 'popup' ], - [ editor.lang.common.targetNew, '_blank' ], - [ editor.lang.common.targetTop, '_top' ], - [ editor.lang.common.targetSelf, '_self' ], - [ editor.lang.common.targetParent, '_parent' ] - ], - onChange : targetChanged, - setup : function( data ) - { - if ( data.target ) - this.setValue( data.target.type ); - }, - commit : function( data ) - { - if ( !data.target ) - data.target = {}; - - data.target.type = this.getValue(); - } - }, - { - type : 'text', - id : 'linkTargetName', - label : editor.lang.link.targetFrameName, - 'default' : '', - setup : function( data ) - { - if ( data.target ) - this.setValue( data.target.name ); - }, - commit : function( data ) - { - if ( !data.target ) - data.target = {}; - - data.target.name = this.getValue().replace(/\W/gi, ''); - } - } - ] - }, - { - type : 'vbox', - width : 260, - align : 'center', - padding : 2, - id : 'popupFeatures', - children : - [ - { - type : 'fieldset', - label : editor.lang.link.popupFeatures, - children : - [ - { - type : 'hbox', - children : - [ - { - type : 'checkbox', - id : 'resizable', - label : editor.lang.link.popupResizable, - setup : setupPopupParams, - commit : commitPopupParams - }, - { - type : 'checkbox', - id : 'status', - label : editor.lang.link.popupStatusBar, - setup : setupPopupParams, - commit : commitPopupParams - - } - ] - }, - { - type : 'hbox', - children : - [ - { - type : 'checkbox', - id : 'location', - label : editor.lang.link.popupLocationBar, - setup : setupPopupParams, - commit : commitPopupParams - - }, - { - type : 'checkbox', - id : 'toolbar', - label : editor.lang.link.popupToolbar, - setup : setupPopupParams, - commit : commitPopupParams - - } - ] - }, - { - type : 'hbox', - children : - [ - { - type : 'checkbox', - id : 'menubar', - label : editor.lang.link.popupMenuBar, - setup : setupPopupParams, - commit : commitPopupParams - - }, - { - type : 'checkbox', - id : 'fullscreen', - label : editor.lang.link.popupFullScreen, - setup : setupPopupParams, - commit : commitPopupParams - - } - ] - }, - { - type : 'hbox', - children : - [ - { - type : 'checkbox', - id : 'scrollbars', - label : editor.lang.link.popupScrollBars, - setup : setupPopupParams, - commit : commitPopupParams - - }, - { - type : 'checkbox', - id : 'dependent', - label : editor.lang.link.popupDependent, - setup : setupPopupParams, - commit : commitPopupParams - - } - ] - }, - { - type : 'hbox', - children : - [ - { - type : 'text', - widths : [ '30%', '70%' ], - labelLayout : 'horizontal', - label : editor.lang.link.popupWidth, - id : 'width', - setup : setupPopupParams, - commit : commitPopupParams - - }, - { - type : 'text', - labelLayout : 'horizontal', - widths : [ '55%', '45%' ], - label : editor.lang.link.popupLeft, - id : 'left', - setup : setupPopupParams, - commit : commitPopupParams - - } - ] - }, - { - type : 'hbox', - children : - [ - { - type : 'text', - labelLayout : 'horizontal', - widths : [ '30%', '70%' ], - label : editor.lang.link.popupHeight, - id : 'height', - setup : setupPopupParams, - commit : commitPopupParams - - }, - { - type : 'text', - labelLayout : 'horizontal', - label : editor.lang.link.popupTop, - widths : [ '55%', '45%' ], - id : 'top', - setup : setupPopupParams, - commit : commitPopupParams - - } - ] - } - ] - } - ] - } - ] - }, - { - id : 'upload', - label : editor.lang.link.upload, - title : editor.lang.link.upload, - hidden : true, - filebrowser : 'uploadButton', - elements : - [ - { - type : 'file', - id : 'upload', - label : editor.lang.common.upload, - style: 'height:40px', - size : 29 - }, - { - type : 'fileButton', - id : 'uploadButton', - label : editor.lang.common.uploadSubmit, - filebrowser : 'info:url', - 'for' : [ 'upload', 'upload' ] - } - ] - }, - { - id : 'advanced', - label : editor.lang.link.advanced, - title : editor.lang.link.advanced, - elements : - [ - { - type : 'vbox', - padding : 1, - children : - [ - { - type : 'hbox', - widths : [ '45%', '35%', '20%' ], - children : - [ - { - type : 'text', - id : 'advId', - label : editor.lang.link.id, - setup : setupAdvParams, - commit : commitAdvParams - }, - { - type : 'select', - id : 'advLangDir', - label : editor.lang.link.langDir, - 'default' : '', - style : 'width:110px', - items : - [ - [ editor.lang.common.notSet, '' ], - [ editor.lang.link.langDirLTR, 'ltr' ], - [ editor.lang.link.langDirRTL, 'rtl' ] - ], - setup : setupAdvParams, - commit : commitAdvParams - }, - { - type : 'text', - id : 'advAccessKey', - width : '80px', - label : editor.lang.link.acccessKey, - maxLength : 1, - setup : setupAdvParams, - commit : commitAdvParams - - } - ] - }, - { - type : 'hbox', - widths : [ '45%', '35%', '20%' ], - children : - [ - { - type : 'text', - label : editor.lang.link.name, - id : 'advName', - setup : setupAdvParams, - commit : commitAdvParams - - }, - { - type : 'text', - label : editor.lang.link.langCode, - id : 'advLangCode', - width : '110px', - 'default' : '', - setup : setupAdvParams, - commit : commitAdvParams - - }, - { - type : 'text', - label : editor.lang.link.tabIndex, - id : 'advTabIndex', - width : '80px', - maxLength : 5, - setup : setupAdvParams, - commit : commitAdvParams - - } - ] - } - ] - }, - { - type : 'vbox', - padding : 1, - children : - [ - { - type : 'hbox', - widths : [ '45%', '55%' ], - children : - [ - { - type : 'text', - label : editor.lang.link.advisoryTitle, - 'default' : '', - id : 'advTitle', - setup : setupAdvParams, - commit : commitAdvParams - - }, - { - type : 'text', - label : editor.lang.link.advisoryContentType, - 'default' : '', - id : 'advContentType', - setup : setupAdvParams, - commit : commitAdvParams - - } - ] - }, - { - type : 'hbox', - widths : [ '45%', '55%' ], - children : - [ - { - type : 'text', - label : editor.lang.link.cssClasses, - 'default' : '', - id : 'advCSSClasses', - setup : setupAdvParams, - commit : commitAdvParams - - }, - { - type : 'text', - label : editor.lang.link.charset, - 'default' : '', - id : 'advCharset', - setup : setupAdvParams, - commit : commitAdvParams - - } - ] - }, - { - type : 'hbox', - children : - [ - { - type : 'text', - label : editor.lang.link.styles, - 'default' : '', - id : 'advStyles', - setup : setupAdvParams, - commit : commitAdvParams - - } - ] - } - ] - } - ] - } - ], - onShow : function() - { - this.fakeObj = false; - - var editor = this.getParentEditor(), - selection = editor.getSelection(), - element = null; - - // Fill in all the relevant fields if there's already one link selected. - if ( ( element = plugin.getSelectedLink( editor ) ) && element.hasAttribute( 'href' ) ) - selection.selectElement( element ); - else if ( ( element = selection.getSelectedElement() ) && element.is( 'img' ) - && element.getAttribute( '_cke_real_element_type' ) - && element.getAttribute( '_cke_real_element_type' ) == 'anchor' ) - { - this.fakeObj = element; - element = editor.restoreRealElement( this.fakeObj ); - selection.selectElement( this.fakeObj ); - } - else - element = null; - - this.setupContent( parseLink.apply( this, [ editor, element ] ) ); - }, - onOk : function() - { - var attributes = { href : 'javascript:void(0)/*' + CKEDITOR.tools.getNextNumber() + '*/' }, - removeAttributes = [], - data = { href : attributes.href }, - me = this, - editor = this.getParentEditor(); - - this.commitContent( data ); - - // Compose the URL. - switch ( data.type || 'url' ) - { - case 'url': - var protocol = ( data.url && data.url.protocol != undefined ) ? data.url.protocol : 'http://', - url = ( data.url && data.url.url ) || ''; - attributes._cke_saved_href = ( url.indexOf( '/' ) === 0 ) ? url : protocol + url; - break; - case 'anchor': - var name = ( data.anchor && data.anchor.name ), - id = ( data.anchor && data.anchor.id ); - attributes._cke_saved_href = '#' + ( name || id || '' ); - break; - case 'email': - - var linkHref, - email = data.email, - address = email.address; - - switch( emailProtection ) - { - case '' : - case 'encode' : - { - var subject = encodeURIComponent( email.subject || '' ), - body = encodeURIComponent( email.body || '' ); - - // Build the e-mail parameters first. - var argList = []; - subject && argList.push( 'subject=' + subject ); - body && argList.push( 'body=' + body ); - argList = argList.length ? '?' + argList.join( '&' ) : ''; - - if ( emailProtection == 'encode' ) - { - linkHref = [ 'javascript:void(location.href=\'mailto:\'+', - protectEmailAddressAsEncodedString( address ) ]; - // parameters are optional. - argList && linkHref.push( '+\'', escapeSingleQuote( argList ), '\'' ); - - linkHref.push( ')' ); - } - else - linkHref = [ 'mailto:', address, argList ]; - - break; - } - default : - { - // Separating name and domain. - var nameAndDomain = address.split( '@', 2 ); - email.name = nameAndDomain[ 0 ]; - email.domain = nameAndDomain[ 1 ]; - - linkHref = [ 'javascript:', protectEmailLinkAsFunction( email ) ]; - } - } - - attributes._cke_saved_href = linkHref.join( '' ); - break; - } - - // Popups and target. - if ( data.target ) - { - if ( data.target.type == 'popup' ) - { - var onclickList = [ 'window.open(this.href, \'', - data.target.name || '', '\', \'' ]; - var featureList = [ 'resizable', 'status', 'location', 'toolbar', 'menubar', 'fullscreen', - 'scrollbars', 'dependent' ]; - var featureLength = featureList.length; - var addFeature = function( featureName ) - { - if ( data.target[ featureName ] ) - featureList.push( featureName + '=' + data.target[ featureName ] ); - }; - - for ( var i = 0 ; i < featureLength ; i++ ) - featureList[i] = featureList[i] + ( data.target[ featureList[i] ] ? '=yes' : '=no' ) ; - addFeature( 'width' ); - addFeature( 'left' ); - addFeature( 'height' ); - addFeature( 'top' ); - - onclickList.push( featureList.join( ',' ), '\'); return false;' ); - attributes[ '_cke_pa_onclick' ] = onclickList.join( '' ); - } - else - { - if ( data.target.type != 'notSet' && data.target.name ) - attributes.target = data.target.name; - else - removeAttributes.push( 'target' ); - - removeAttributes.push( '_cke_pa_onclick', 'onclick' ); - } - } - - // Advanced attributes. - if ( data.adv ) - { - var advAttr = function( inputName, attrName ) - { - var value = data.adv[ inputName ]; - if ( value ) - attributes[attrName] = value; - else - removeAttributes.push( attrName ); - }; - - if ( this._.selectedElement ) - advAttr( 'advId', 'id' ); - advAttr( 'advLangDir', 'dir' ); - advAttr( 'advAccessKey', 'accessKey' ); - advAttr( 'advName', 'name' ); - advAttr( 'advLangCode', 'lang' ); - advAttr( 'advTabIndex', 'tabindex' ); - advAttr( 'advTitle', 'title' ); - advAttr( 'advContentType', 'type' ); - advAttr( 'advCSSClasses', 'class' ); - advAttr( 'advCharset', 'charset' ); - advAttr( 'advStyles', 'style' ); - } - - if ( !this._.selectedElement ) - { - // Create element if current selection is collapsed. - var selection = editor.getSelection(), - ranges = selection.getRanges(); - if ( ranges.length == 1 && ranges[0].collapsed ) - { - var text = new CKEDITOR.dom.text( attributes._cke_saved_href, editor.document ); - ranges[0].insertNode( text ); - ranges[0].selectNodeContents( text ); - selection.selectRanges( ranges ); - } - - // Apply style. - var style = new CKEDITOR.style( { element : 'a', attributes : attributes } ); - style.type = CKEDITOR.STYLE_INLINE; // need to override... dunno why. - style.apply( editor.document ); - - // Id. Apply only to the first link. - if ( data.adv && data.adv.advId ) - { - var links = this.getParentEditor().document.$.getElementsByTagName( 'a' ); - for ( i = 0 ; i < links.length ; i++ ) - { - if ( links[i].href == attributes.href ) - { - links[i].id = data.adv.advId; - break; - } - } - } - } - else - { - // We're only editing an existing link, so just overwrite the attributes. - var element = this._.selectedElement, - href = element.getAttribute( '_cke_saved_href' ), - textView = element.getHtml(); - - // IE BUG: Setting the name attribute to an existing link doesn't work. - // Must re-create the link from weired syntax to workaround. - if ( CKEDITOR.env.ie && attributes.name != element.getAttribute( 'name' ) ) - { - var newElement = new CKEDITOR.dom.element( '<a name="' + CKEDITOR.tools.htmlEncode( attributes.name ) + '">', - editor.document ); - - selection = editor.getSelection(); - - element.moveChildren( newElement ); - element.copyAttributes( newElement, { name : 1 } ); - newElement.replace( element ); - element = newElement; - - selection.selectElement( element ); - } - - element.setAttributes( attributes ); - element.removeAttributes( removeAttributes ); - // Update text view when user changes protocol #4612. - if (href == textView) - element.setHtml( attributes._cke_saved_href ); - // Make the element display as an anchor if a name has been set. - if ( element.getAttribute( 'name' ) ) - element.addClass( 'cke_anchor' ); - else - element.removeClass( 'cke_anchor' ); - - if ( this.fakeObj ) - editor.createFakeElement( element, 'cke_anchor', 'anchor' ).replace( this.fakeObj ); - - delete this._.selectedElement; - } - }, - onLoad : function() - { - if ( !editor.config.linkShowAdvancedTab ) - this.hidePage( 'advanced' ); //Hide Advanded tab. - - if ( !editor.config.linkShowTargetTab ) - this.hidePage( 'target' ); //Hide Target tab. - - }, - // Inital focus on 'url' field if link is of type URL. - onFocus : function() - { - var linkType = this.getContentElement( 'info', 'linkType' ), - urlField; - if ( linkType && linkType.getValue( ) == 'url' ) - { - urlField = this.getContentElement( 'info', 'url' ); - urlField.select(); - } - } - }; -}); - -/** - * The e-mail address anti-spam protection option. The protection will be - * applied when creating or modifying e-mail links through the editor interface.<br> - * Two methods of protection can be choosed: - * <ol> <li>The e-mail parts (name, domain and any other query string) are - * assembled into a function call pattern. Such function must be - * provided by the developer in the pages that will use the contents. - * <li>Only the e-mail address is obfuscated into a special string that - * has no meaning for humans or spam bots, but which is properly - * rendered and accepted by the browser.</li></ol> - * Both approaches require JavaScript to be enabled. - * @name CKEDITOR.config.emailProtection - * @since 3.1 - * @type String - * @default '' (empty string = disabled) - * @example - * // href="mailto:tester@ckeditor.com?subject=subject&body=body" - * config.emailProtection = ''; - * @example - * // href="<a href=\"javascript:void(location.href=\'mailto:\'+String.fromCharCode(116,101,115,116,101,114,64,99,107,101,100,105,116,111,114,46,99,111,109)+\'?subject=subject&body=body\')\">e-mail</a>" - * config.emailProtection = 'encode'; - * @example - * // href="javascript:mt('tester','ckeditor.com','subject','body')" - * config.emailProtection = 'mt(NAME,DOMAIN,SUBJECT,BODY)'; - */ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.dialog.add( 'link', function( editor )
+{
+ var plugin = CKEDITOR.plugins.link;
+ // Handles the event when the "Target" selection box is changed.
+ var targetChanged = function()
+ {
+ var dialog = this.getDialog(),
+ popupFeatures = dialog.getContentElement( 'target', 'popupFeatures' ),
+ targetName = dialog.getContentElement( 'target', 'linkTargetName' ),
+ value = this.getValue();
+
+ if ( !popupFeatures || !targetName )
+ return;
+
+ popupFeatures = popupFeatures.getElement();
+ popupFeatures.hide();
+ targetName.setValue( '' );
+
+ switch ( value )
+ {
+ case 'frame' :
+ targetName.setLabel( editor.lang.link.targetFrameName );
+ targetName.getElement().show();
+ break;
+ case 'popup' :
+ popupFeatures.show();
+ targetName.setLabel( editor.lang.link.targetPopupName );
+ targetName.getElement().show();
+ break;
+ default :
+ targetName.setValue( value );
+ targetName.getElement().hide();
+ break;
+ }
+
+ };
+
+ // Handles the event when the "Type" selection box is changed.
+ var linkTypeChanged = function()
+ {
+ var dialog = this.getDialog(),
+ partIds = [ 'urlOptions', 'anchorOptions', 'emailOptions' ],
+ typeValue = this.getValue(),
+ uploadTab = dialog.definition.getContents( 'upload' ),
+ uploadInitiallyHidden = uploadTab && uploadTab.hidden;
+
+ if ( typeValue == 'url' )
+ {
+ if ( editor.config.linkShowTargetTab )
+ dialog.showPage( 'target' );
+ if ( !uploadInitiallyHidden )
+ dialog.showPage( 'upload' );
+ }
+ else
+ {
+ dialog.hidePage( 'target' );
+ if ( !uploadInitiallyHidden )
+ dialog.hidePage( 'upload' );
+ }
+
+ for ( var i = 0 ; i < partIds.length ; i++ )
+ {
+ var element = dialog.getContentElement( 'info', partIds[i] );
+ if ( !element )
+ continue;
+
+ element = element.getElement().getParent().getParent();
+ if ( partIds[i] == typeValue + 'Options' )
+ element.show();
+ else
+ element.hide();
+ }
+
+ dialog.layout();
+ };
+
+ // Loads the parameters in a selected link to the link dialog fields.
+ var javascriptProtocolRegex = /^javascript:/,
+ emailRegex = /^mailto:([^?]+)(?:\?(.+))?$/,
+ emailSubjectRegex = /subject=([^;?:@&=$,\/]*)/,
+ emailBodyRegex = /body=([^;?:@&=$,\/]*)/,
+ anchorRegex = /^#(.*)$/,
+ urlRegex = /^((?:http|https|ftp|news):\/\/)?(.*)$/,
+ selectableTargets = /^(_(?:self|top|parent|blank))$/,
+ encodedEmailLinkRegex = /^javascript:void\(location\.href='mailto:'\+String\.fromCharCode\(([^)]+)\)(?:\+'(.*)')?\)$/,
+ functionCallProtectedEmailLinkRegex = /^javascript:([^(]+)\(([^)]+)\)$/;
+
+ var popupRegex =
+ /\s*window.open\(\s*this\.href\s*,\s*(?:'([^']*)'|null)\s*,\s*'([^']*)'\s*\)\s*;\s*return\s*false;*\s*/;
+ var popupFeaturesRegex = /(?:^|,)([^=]+)=(\d+|yes|no)/gi;
+
+ var parseLink = function( editor, element )
+ {
+ var href = ( element && ( element.data( 'cke-saved-href' ) || element.getAttribute( 'href' ) ) ) || '',
+ javascriptMatch,
+ emailMatch,
+ anchorMatch,
+ urlMatch,
+ retval = {};
+
+ if ( ( javascriptMatch = href.match( javascriptProtocolRegex ) ) )
+ {
+ if ( emailProtection == 'encode' )
+ {
+ href = href.replace( encodedEmailLinkRegex,
+ function ( match, protectedAddress, rest )
+ {
+ return 'mailto:' +
+ String.fromCharCode.apply( String, protectedAddress.split( ',' ) ) +
+ ( rest && unescapeSingleQuote( rest ) );
+ });
+ }
+ // Protected email link as function call.
+ else if ( emailProtection )
+ {
+ href.replace( functionCallProtectedEmailLinkRegex, function( match, funcName, funcArgs )
+ {
+ if ( funcName == compiledProtectionFunction.name )
+ {
+ retval.type = 'email';
+ var email = retval.email = {};
+
+ var paramRegex = /[^,\s]+/g,
+ paramQuoteRegex = /(^')|('$)/g,
+ paramsMatch = funcArgs.match( paramRegex ),
+ paramsMatchLength = paramsMatch.length,
+ paramName,
+ paramVal;
+
+ for ( var i = 0; i < paramsMatchLength; i++ )
+ {
+ paramVal = decodeURIComponent( unescapeSingleQuote( paramsMatch[ i ].replace( paramQuoteRegex, '' ) ) );
+ paramName = compiledProtectionFunction.params[ i ].toLowerCase();
+ email[ paramName ] = paramVal;
+ }
+ email.address = [ email.name, email.domain ].join( '@' );
+ }
+ } );
+ }
+ }
+
+ if ( !retval.type )
+ {
+ if ( ( anchorMatch = href.match( anchorRegex ) ) )
+ {
+ retval.type = 'anchor';
+ retval.anchor = {};
+ retval.anchor.name = retval.anchor.id = anchorMatch[1];
+ }
+ // Protected email link as encoded string.
+ else if ( ( emailMatch = href.match( emailRegex ) ) )
+ {
+ var subjectMatch = href.match( emailSubjectRegex ),
+ bodyMatch = href.match( emailBodyRegex );
+
+ retval.type = 'email';
+ var email = ( retval.email = {} );
+ email.address = emailMatch[ 1 ];
+ subjectMatch && ( email.subject = decodeURIComponent( subjectMatch[ 1 ] ) );
+ bodyMatch && ( email.body = decodeURIComponent( bodyMatch[ 1 ] ) );
+ }
+ // urlRegex matches empty strings, so need to check for href as well.
+ else if ( href && ( urlMatch = href.match( urlRegex ) ) )
+ {
+ retval.type = 'url';
+ retval.url = {};
+ retval.url.protocol = urlMatch[1];
+ retval.url.url = urlMatch[2];
+ }
+ else
+ retval.type = 'url';
+ }
+
+ // Load target and popup settings.
+ if ( element )
+ {
+ var target = element.getAttribute( 'target' );
+ retval.target = {};
+ retval.adv = {};
+
+ // IE BUG: target attribute is an empty string instead of null in IE if it's not set.
+ if ( !target )
+ {
+ var onclick = element.data( 'cke-pa-onclick' ) || element.getAttribute( 'onclick' ),
+ onclickMatch = onclick && onclick.match( popupRegex );
+ if ( onclickMatch )
+ {
+ retval.target.type = 'popup';
+ retval.target.name = onclickMatch[1];
+
+ var featureMatch;
+ while ( ( featureMatch = popupFeaturesRegex.exec( onclickMatch[2] ) ) )
+ {
+ // Some values should remain numbers (#7300)
+ if ( ( featureMatch[2] == 'yes' || featureMatch[2] == '1' ) && !( featureMatch[1] in { height:1, width:1, top:1, left:1 } ) )
+ retval.target[ featureMatch[1] ] = true;
+ else if ( isFinite( featureMatch[2] ) )
+ retval.target[ featureMatch[1] ] = featureMatch[2];
+ }
+ }
+ }
+ else
+ {
+ var targetMatch = target.match( selectableTargets );
+ if ( targetMatch )
+ retval.target.type = retval.target.name = target;
+ else
+ {
+ retval.target.type = 'frame';
+ retval.target.name = target;
+ }
+ }
+
+ var me = this;
+ var advAttr = function( inputName, attrName )
+ {
+ var value = element.getAttribute( attrName );
+ if ( value !== null )
+ retval.adv[ inputName ] = value || '';
+ };
+ advAttr( 'advId', 'id' );
+ advAttr( 'advLangDir', 'dir' );
+ advAttr( 'advAccessKey', 'accessKey' );
+
+ retval.adv.advName =
+ element.data( 'cke-saved-name' )
+ || element.getAttribute( 'name' )
+ || '';
+ advAttr( 'advLangCode', 'lang' );
+ advAttr( 'advTabIndex', 'tabindex' );
+ advAttr( 'advTitle', 'title' );
+ advAttr( 'advContentType', 'type' );
+ CKEDITOR.plugins.link.synAnchorSelector ?
+ retval.adv.advCSSClasses = getLinkClass( element )
+ : advAttr( 'advCSSClasses', 'class' );
+ advAttr( 'advCharset', 'charset' );
+ advAttr( 'advStyles', 'style' );
+ advAttr( 'advRel', 'rel' );
+ }
+
+ // Find out whether we have any anchors in the editor.
+ var anchors = retval.anchors = [],
+ i, count, item;
+
+ // For some browsers we set contenteditable="false" on anchors, making document.anchors not to include them, so we must traverse the links manually (#7893).
+ if ( CKEDITOR.plugins.link.emptyAnchorFix )
+ {
+ var links = editor.document.getElementsByTag( 'a' );
+ for ( i = 0, count = links.count(); i < count; i++ )
+ {
+ item = links.getItem( i );
+ if ( item.data( 'cke-saved-name' ) || item.hasAttribute( 'name' ) )
+ anchors.push( { name : item.data( 'cke-saved-name' ) || item.getAttribute( 'name' ), id : item.getAttribute( 'id' ) } );
+ }
+ }
+ else
+ {
+ var anchorList = new CKEDITOR.dom.nodeList( editor.document.$.anchors );
+ for ( i = 0, count = anchorList.count(); i < count; i++ )
+ {
+ item = anchorList.getItem( i );
+ anchors[ i ] = { name : item.getAttribute( 'name' ), id : item.getAttribute( 'id' ) };
+ }
+ }
+
+ if ( CKEDITOR.plugins.link.fakeAnchor )
+ {
+ var imgs = editor.document.getElementsByTag( 'img' );
+ for ( i = 0, count = imgs.count(); i < count; i++ )
+ {
+ if ( ( item = CKEDITOR.plugins.link.tryRestoreFakeAnchor( editor, imgs.getItem( i ) ) ) )
+ anchors.push( { name : item.getAttribute( 'name' ), id : item.getAttribute( 'id' ) } );
+ }
+ }
+
+ // Record down the selected element in the dialog.
+ this._.selectedElement = element;
+ return retval;
+ };
+
+ var setupParams = function( page, data )
+ {
+ if ( data[page] )
+ this.setValue( data[page][this.id] || '' );
+ };
+
+ var setupPopupParams = function( data )
+ {
+ return setupParams.call( this, 'target', data );
+ };
+
+ var setupAdvParams = function( data )
+ {
+ return setupParams.call( this, 'adv', data );
+ };
+
+ var commitParams = function( page, data )
+ {
+ if ( !data[page] )
+ data[page] = {};
+
+ data[page][this.id] = this.getValue() || '';
+ };
+
+ var commitPopupParams = function( data )
+ {
+ return commitParams.call( this, 'target', data );
+ };
+
+ var commitAdvParams = function( data )
+ {
+ return commitParams.call( this, 'adv', data );
+ };
+
+ function unescapeSingleQuote( str )
+ {
+ return str.replace( /\\'/g, '\'' );
+ }
+
+ function escapeSingleQuote( str )
+ {
+ return str.replace( /'/g, '\\$&' );
+ }
+
+ var emailProtection = editor.config.emailProtection || '';
+
+ // Compile the protection function pattern.
+ if ( emailProtection && emailProtection != 'encode' )
+ {
+ var compiledProtectionFunction = {};
+
+ emailProtection.replace( /^([^(]+)\(([^)]+)\)$/, function( match, funcName, params )
+ {
+ compiledProtectionFunction.name = funcName;
+ compiledProtectionFunction.params = [];
+ params.replace( /[^,\s]+/g, function( param )
+ {
+ compiledProtectionFunction.params.push( param );
+ } );
+ } );
+ }
+
+ function protectEmailLinkAsFunction( email )
+ {
+ var retval,
+ name = compiledProtectionFunction.name,
+ params = compiledProtectionFunction.params,
+ paramName,
+ paramValue;
+
+ retval = [ name, '(' ];
+ for ( var i = 0; i < params.length; i++ )
+ {
+ paramName = params[ i ].toLowerCase();
+ paramValue = email[ paramName ];
+
+ i > 0 && retval.push( ',' );
+ retval.push( '\'',
+ paramValue ?
+ escapeSingleQuote( encodeURIComponent( email[ paramName ] ) )
+ : '',
+ '\'');
+ }
+ retval.push( ')' );
+ return retval.join( '' );
+ }
+
+ function protectEmailAddressAsEncodedString( address )
+ {
+ var charCode,
+ length = address.length,
+ encodedChars = [];
+ for ( var i = 0; i < length; i++ )
+ {
+ charCode = address.charCodeAt( i );
+ encodedChars.push( charCode );
+ }
+ return 'String.fromCharCode(' + encodedChars.join( ',' ) + ')';
+ }
+
+ function getLinkClass( ele )
+ {
+ var className = ele.getAttribute( 'class' );
+ return className ? className.replace( /\s*(?:cke_anchor_empty|cke_anchor)(?:\s*$)?/g, '' ) : '';
+ }
+
+ var commonLang = editor.lang.common,
+ linkLang = editor.lang.link;
+
+ return {
+ title : linkLang.title,
+ minWidth : 350,
+ minHeight : 230,
+ contents : [
+ {
+ id : 'info',
+ label : linkLang.info,
+ title : linkLang.info,
+ elements :
+ [
+ {
+ id : 'linkType',
+ type : 'select',
+ label : linkLang.type,
+ 'default' : 'url',
+ items :
+ [
+ [ linkLang.toUrl, 'url' ],
+ [ linkLang.toAnchor, 'anchor' ],
+ [ linkLang.toEmail, 'email' ]
+ ],
+ onChange : linkTypeChanged,
+ setup : function( data )
+ {
+ if ( data.type )
+ this.setValue( data.type );
+ },
+ commit : function( data )
+ {
+ data.type = this.getValue();
+ }
+ },
+ {
+ type : 'vbox',
+ id : 'urlOptions',
+ children :
+ [
+ {
+ type : 'hbox',
+ widths : [ '25%', '75%' ],
+ children :
+ [
+ {
+ id : 'protocol',
+ type : 'select',
+ label : commonLang.protocol,
+ 'default' : 'http://',
+ items :
+ [
+ // Force 'ltr' for protocol names in BIDI. (#5433)
+ [ 'http://\u200E', 'http://' ],
+ [ 'https://\u200E', 'https://' ],
+ [ 'ftp://\u200E', 'ftp://' ],
+ [ 'news://\u200E', 'news://' ],
+ [ linkLang.other , '' ]
+ ],
+ setup : function( data )
+ {
+ if ( data.url )
+ this.setValue( data.url.protocol || '' );
+ },
+ commit : function( data )
+ {
+ if ( !data.url )
+ data.url = {};
+
+ data.url.protocol = this.getValue();
+ }
+ },
+ {
+ type : 'text',
+ id : 'url',
+ label : commonLang.url,
+ required: true,
+ onLoad : function ()
+ {
+ this.allowOnChange = true;
+ },
+ onKeyUp : function()
+ {
+ this.allowOnChange = false;
+ var protocolCmb = this.getDialog().getContentElement( 'info', 'protocol' ),
+ url = this.getValue(),
+ urlOnChangeProtocol = /^(http|https|ftp|news):\/\/(?=.)/i,
+ urlOnChangeTestOther = /^((javascript:)|[#\/\.\?])/i;
+
+ var protocol = urlOnChangeProtocol.exec( url );
+ if ( protocol )
+ {
+ this.setValue( url.substr( protocol[ 0 ].length ) );
+ protocolCmb.setValue( protocol[ 0 ].toLowerCase() );
+ }
+ else if ( urlOnChangeTestOther.test( url ) )
+ protocolCmb.setValue( '' );
+
+ this.allowOnChange = true;
+ },
+ onChange : function()
+ {
+ if ( this.allowOnChange ) // Dont't call on dialog load.
+ this.onKeyUp();
+ },
+ validate : function()
+ {
+ var dialog = this.getDialog();
+
+ if ( dialog.getContentElement( 'info', 'linkType' ) &&
+ dialog.getValueOf( 'info', 'linkType' ) != 'url' )
+ return true;
+
+ if ( this.getDialog().fakeObj ) // Edit Anchor.
+ return true;
+
+ var func = CKEDITOR.dialog.validate.notEmpty( linkLang.noUrl );
+ return func.apply( this );
+ },
+ setup : function( data )
+ {
+ this.allowOnChange = false;
+ if ( data.url )
+ this.setValue( data.url.url );
+ this.allowOnChange = true;
+
+ },
+ commit : function( data )
+ {
+ // IE will not trigger the onChange event if the mouse has been used
+ // to carry all the operations #4724
+ this.onChange();
+
+ if ( !data.url )
+ data.url = {};
+
+ data.url.url = this.getValue();
+ this.allowOnChange = false;
+ }
+ }
+ ],
+ setup : function( data )
+ {
+ if ( !this.getDialog().getContentElement( 'info', 'linkType' ) )
+ this.getElement().show();
+ }
+ },
+ {
+ type : 'button',
+ id : 'browse',
+ hidden : 'true',
+ filebrowser : 'info:url',
+ label : commonLang.browseServer
+ }
+ ]
+ },
+ {
+ type : 'vbox',
+ id : 'anchorOptions',
+ width : 260,
+ align : 'center',
+ padding : 0,
+ children :
+ [
+ {
+ type : 'fieldset',
+ id : 'selectAnchorText',
+ label : linkLang.selectAnchor,
+ setup : function( data )
+ {
+ if ( data.anchors.length > 0 )
+ this.getElement().show();
+ else
+ this.getElement().hide();
+ },
+ children :
+ [
+ {
+ type : 'hbox',
+ id : 'selectAnchor',
+ children :
+ [
+ {
+ type : 'select',
+ id : 'anchorName',
+ 'default' : '',
+ label : linkLang.anchorName,
+ style : 'width: 100%;',
+ items :
+ [
+ [ '' ]
+ ],
+ setup : function( data )
+ {
+ this.clear();
+ this.add( '' );
+ for ( var i = 0 ; i < data.anchors.length ; i++ )
+ {
+ if ( data.anchors[i].name )
+ this.add( data.anchors[i].name );
+ }
+
+ if ( data.anchor )
+ this.setValue( data.anchor.name );
+
+ var linkType = this.getDialog().getContentElement( 'info', 'linkType' );
+ if ( linkType && linkType.getValue() == 'email' )
+ this.focus();
+ },
+ commit : function( data )
+ {
+ if ( !data.anchor )
+ data.anchor = {};
+
+ data.anchor.name = this.getValue();
+ }
+ },
+ {
+ type : 'select',
+ id : 'anchorId',
+ 'default' : '',
+ label : linkLang.anchorId,
+ style : 'width: 100%;',
+ items :
+ [
+ [ '' ]
+ ],
+ setup : function( data )
+ {
+ this.clear();
+ this.add( '' );
+ for ( var i = 0 ; i < data.anchors.length ; i++ )
+ {
+ if ( data.anchors[i].id )
+ this.add( data.anchors[i].id );
+ }
+
+ if ( data.anchor )
+ this.setValue( data.anchor.id );
+ },
+ commit : function( data )
+ {
+ if ( !data.anchor )
+ data.anchor = {};
+
+ data.anchor.id = this.getValue();
+ }
+ }
+ ],
+ setup : function( data )
+ {
+ if ( data.anchors.length > 0 )
+ this.getElement().show();
+ else
+ this.getElement().hide();
+ }
+ }
+ ]
+ },
+ {
+ type : 'html',
+ id : 'noAnchors',
+ style : 'text-align: center;',
+ html : '<div role="note" tabIndex="-1">' + CKEDITOR.tools.htmlEncode( linkLang.noAnchors ) + '</div>',
+ // Focus the first element defined in above html.
+ focus : true,
+ setup : function( data )
+ {
+ if ( data.anchors.length < 1 )
+ this.getElement().show();
+ else
+ this.getElement().hide();
+ }
+ }
+ ],
+ setup : function( data )
+ {
+ if ( !this.getDialog().getContentElement( 'info', 'linkType' ) )
+ this.getElement().hide();
+ }
+ },
+ {
+ type : 'vbox',
+ id : 'emailOptions',
+ padding : 1,
+ children :
+ [
+ {
+ type : 'text',
+ id : 'emailAddress',
+ label : linkLang.emailAddress,
+ required : true,
+ validate : function()
+ {
+ var dialog = this.getDialog();
+
+ if ( !dialog.getContentElement( 'info', 'linkType' ) ||
+ dialog.getValueOf( 'info', 'linkType' ) != 'email' )
+ return true;
+
+ var func = CKEDITOR.dialog.validate.notEmpty( linkLang.noEmail );
+ return func.apply( this );
+ },
+ setup : function( data )
+ {
+ if ( data.email )
+ this.setValue( data.email.address );
+
+ var linkType = this.getDialog().getContentElement( 'info', 'linkType' );
+ if ( linkType && linkType.getValue() == 'email' )
+ this.select();
+ },
+ commit : function( data )
+ {
+ if ( !data.email )
+ data.email = {};
+
+ data.email.address = this.getValue();
+ }
+ },
+ {
+ type : 'text',
+ id : 'emailSubject',
+ label : linkLang.emailSubject,
+ setup : function( data )
+ {
+ if ( data.email )
+ this.setValue( data.email.subject );
+ },
+ commit : function( data )
+ {
+ if ( !data.email )
+ data.email = {};
+
+ data.email.subject = this.getValue();
+ }
+ },
+ {
+ type : 'textarea',
+ id : 'emailBody',
+ label : linkLang.emailBody,
+ rows : 3,
+ 'default' : '',
+ setup : function( data )
+ {
+ if ( data.email )
+ this.setValue( data.email.body );
+ },
+ commit : function( data )
+ {
+ if ( !data.email )
+ data.email = {};
+
+ data.email.body = this.getValue();
+ }
+ }
+ ],
+ setup : function( data )
+ {
+ if ( !this.getDialog().getContentElement( 'info', 'linkType' ) )
+ this.getElement().hide();
+ }
+ }
+ ]
+ },
+ {
+ id : 'target',
+ label : linkLang.target,
+ title : linkLang.target,
+ elements :
+ [
+ {
+ type : 'hbox',
+ widths : [ '50%', '50%' ],
+ children :
+ [
+ {
+ type : 'select',
+ id : 'linkTargetType',
+ label : commonLang.target,
+ 'default' : 'notSet',
+ style : 'width : 100%;',
+ 'items' :
+ [
+ [ commonLang.notSet, 'notSet' ],
+ [ linkLang.targetFrame, 'frame' ],
+ [ linkLang.targetPopup, 'popup' ],
+ [ commonLang.targetNew, '_blank' ],
+ [ commonLang.targetTop, '_top' ],
+ [ commonLang.targetSelf, '_self' ],
+ [ commonLang.targetParent, '_parent' ]
+ ],
+ onChange : targetChanged,
+ setup : function( data )
+ {
+ if ( data.target )
+ this.setValue( data.target.type || 'notSet' );
+ targetChanged.call( this );
+ },
+ commit : function( data )
+ {
+ if ( !data.target )
+ data.target = {};
+
+ data.target.type = this.getValue();
+ }
+ },
+ {
+ type : 'text',
+ id : 'linkTargetName',
+ label : linkLang.targetFrameName,
+ 'default' : '',
+ setup : function( data )
+ {
+ if ( data.target )
+ this.setValue( data.target.name );
+ },
+ commit : function( data )
+ {
+ if ( !data.target )
+ data.target = {};
+
+ data.target.name = this.getValue().replace(/\W/gi, '');
+ }
+ }
+ ]
+ },
+ {
+ type : 'vbox',
+ width : '100%',
+ align : 'center',
+ padding : 2,
+ id : 'popupFeatures',
+ children :
+ [
+ {
+ type : 'fieldset',
+ label : linkLang.popupFeatures,
+ children :
+ [
+ {
+ type : 'hbox',
+ children :
+ [
+ {
+ type : 'checkbox',
+ id : 'resizable',
+ label : linkLang.popupResizable,
+ setup : setupPopupParams,
+ commit : commitPopupParams
+ },
+ {
+ type : 'checkbox',
+ id : 'status',
+ label : linkLang.popupStatusBar,
+ setup : setupPopupParams,
+ commit : commitPopupParams
+
+ }
+ ]
+ },
+ {
+ type : 'hbox',
+ children :
+ [
+ {
+ type : 'checkbox',
+ id : 'location',
+ label : linkLang.popupLocationBar,
+ setup : setupPopupParams,
+ commit : commitPopupParams
+
+ },
+ {
+ type : 'checkbox',
+ id : 'toolbar',
+ label : linkLang.popupToolbar,
+ setup : setupPopupParams,
+ commit : commitPopupParams
+
+ }
+ ]
+ },
+ {
+ type : 'hbox',
+ children :
+ [
+ {
+ type : 'checkbox',
+ id : 'menubar',
+ label : linkLang.popupMenuBar,
+ setup : setupPopupParams,
+ commit : commitPopupParams
+
+ },
+ {
+ type : 'checkbox',
+ id : 'fullscreen',
+ label : linkLang.popupFullScreen,
+ setup : setupPopupParams,
+ commit : commitPopupParams
+
+ }
+ ]
+ },
+ {
+ type : 'hbox',
+ children :
+ [
+ {
+ type : 'checkbox',
+ id : 'scrollbars',
+ label : linkLang.popupScrollBars,
+ setup : setupPopupParams,
+ commit : commitPopupParams
+
+ },
+ {
+ type : 'checkbox',
+ id : 'dependent',
+ label : linkLang.popupDependent,
+ setup : setupPopupParams,
+ commit : commitPopupParams
+
+ }
+ ]
+ },
+ {
+ type : 'hbox',
+ children :
+ [
+ {
+ type : 'text',
+ widths : [ '50%', '50%' ],
+ labelLayout : 'horizontal',
+ label : commonLang.width,
+ id : 'width',
+ setup : setupPopupParams,
+ commit : commitPopupParams
+
+ },
+ {
+ type : 'text',
+ labelLayout : 'horizontal',
+ widths : [ '50%', '50%' ],
+ label : linkLang.popupLeft,
+ id : 'left',
+ setup : setupPopupParams,
+ commit : commitPopupParams
+
+ }
+ ]
+ },
+ {
+ type : 'hbox',
+ children :
+ [
+ {
+ type : 'text',
+ labelLayout : 'horizontal',
+ widths : [ '50%', '50%' ],
+ label : commonLang.height,
+ id : 'height',
+ setup : setupPopupParams,
+ commit : commitPopupParams
+
+ },
+ {
+ type : 'text',
+ labelLayout : 'horizontal',
+ label : linkLang.popupTop,
+ widths : [ '50%', '50%' ],
+ id : 'top',
+ setup : setupPopupParams,
+ commit : commitPopupParams
+
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ id : 'upload',
+ label : linkLang.upload,
+ title : linkLang.upload,
+ hidden : true,
+ filebrowser : 'uploadButton',
+ elements :
+ [
+ {
+ type : 'file',
+ id : 'upload',
+ label : commonLang.upload,
+ style: 'height:40px',
+ size : 29
+ },
+ {
+ type : 'fileButton',
+ id : 'uploadButton',
+ label : commonLang.uploadSubmit,
+ filebrowser : 'info:url',
+ 'for' : [ 'upload', 'upload' ]
+ }
+ ]
+ },
+ {
+ id : 'advanced',
+ label : linkLang.advanced,
+ title : linkLang.advanced,
+ elements :
+ [
+ {
+ type : 'vbox',
+ padding : 1,
+ children :
+ [
+ {
+ type : 'hbox',
+ widths : [ '45%', '35%', '20%' ],
+ children :
+ [
+ {
+ type : 'text',
+ id : 'advId',
+ label : linkLang.id,
+ setup : setupAdvParams,
+ commit : commitAdvParams
+ },
+ {
+ type : 'select',
+ id : 'advLangDir',
+ label : linkLang.langDir,
+ 'default' : '',
+ style : 'width:110px',
+ items :
+ [
+ [ commonLang.notSet, '' ],
+ [ linkLang.langDirLTR, 'ltr' ],
+ [ linkLang.langDirRTL, 'rtl' ]
+ ],
+ setup : setupAdvParams,
+ commit : commitAdvParams
+ },
+ {
+ type : 'text',
+ id : 'advAccessKey',
+ width : '80px',
+ label : linkLang.acccessKey,
+ maxLength : 1,
+ setup : setupAdvParams,
+ commit : commitAdvParams
+
+ }
+ ]
+ },
+ {
+ type : 'hbox',
+ widths : [ '45%', '35%', '20%' ],
+ children :
+ [
+ {
+ type : 'text',
+ label : linkLang.name,
+ id : 'advName',
+ setup : setupAdvParams,
+ commit : commitAdvParams
+
+ },
+ {
+ type : 'text',
+ label : linkLang.langCode,
+ id : 'advLangCode',
+ width : '110px',
+ 'default' : '',
+ setup : setupAdvParams,
+ commit : commitAdvParams
+
+ },
+ {
+ type : 'text',
+ label : linkLang.tabIndex,
+ id : 'advTabIndex',
+ width : '80px',
+ maxLength : 5,
+ setup : setupAdvParams,
+ commit : commitAdvParams
+
+ }
+ ]
+ }
+ ]
+ },
+ {
+ type : 'vbox',
+ padding : 1,
+ children :
+ [
+ {
+ type : 'hbox',
+ widths : [ '45%', '55%' ],
+ children :
+ [
+ {
+ type : 'text',
+ label : linkLang.advisoryTitle,
+ 'default' : '',
+ id : 'advTitle',
+ setup : setupAdvParams,
+ commit : commitAdvParams
+
+ },
+ {
+ type : 'text',
+ label : linkLang.advisoryContentType,
+ 'default' : '',
+ id : 'advContentType',
+ setup : setupAdvParams,
+ commit : commitAdvParams
+
+ }
+ ]
+ },
+ {
+ type : 'hbox',
+ widths : [ '45%', '55%' ],
+ children :
+ [
+ {
+ type : 'text',
+ label : linkLang.cssClasses,
+ 'default' : '',
+ id : 'advCSSClasses',
+ setup : setupAdvParams,
+ commit : commitAdvParams
+
+ },
+ {
+ type : 'text',
+ label : linkLang.charset,
+ 'default' : '',
+ id : 'advCharset',
+ setup : setupAdvParams,
+ commit : commitAdvParams
+
+ }
+ ]
+ },
+ {
+ type : 'hbox',
+ widths : [ '45%', '55%' ],
+ children :
+ [
+ {
+ type : 'text',
+ label : linkLang.rel,
+ 'default' : '',
+ id : 'advRel',
+ setup : setupAdvParams,
+ commit : commitAdvParams
+ },
+ {
+ type : 'text',
+ label : linkLang.styles,
+ 'default' : '',
+ id : 'advStyles',
+ validate : CKEDITOR.dialog.validate.inlineStyle( editor.lang.common.invalidInlineStyle ),
+ setup : setupAdvParams,
+ commit : commitAdvParams
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ onShow : function()
+ {
+ var editor = this.getParentEditor(),
+ selection = editor.getSelection(),
+ element = null;
+
+ // Fill in all the relevant fields if there's already one link selected.
+ if ( ( element = plugin.getSelectedLink( editor ) ) && element.hasAttribute( 'href' ) )
+ selection.selectElement( element );
+ else
+ element = null;
+
+ this.setupContent( parseLink.apply( this, [ editor, element ] ) );
+ },
+ onOk : function()
+ {
+ var attributes = {},
+ removeAttributes = [],
+ data = {},
+ me = this,
+ editor = this.getParentEditor();
+
+ this.commitContent( data );
+
+ // Compose the URL.
+ switch ( data.type || 'url' )
+ {
+ case 'url':
+ var protocol = ( data.url && data.url.protocol != undefined ) ? data.url.protocol : 'http://',
+ url = ( data.url && CKEDITOR.tools.trim( data.url.url ) ) || '';
+ attributes[ 'data-cke-saved-href' ] = ( url.indexOf( '/' ) === 0 ) ? url : protocol + url;
+ break;
+ case 'anchor':
+ var name = ( data.anchor && data.anchor.name ),
+ id = ( data.anchor && data.anchor.id );
+ attributes[ 'data-cke-saved-href' ] = '#' + ( name || id || '' );
+ break;
+ case 'email':
+
+ var linkHref,
+ email = data.email,
+ address = email.address;
+
+ switch( emailProtection )
+ {
+ case '' :
+ case 'encode' :
+ {
+ var subject = encodeURIComponent( email.subject || '' ),
+ body = encodeURIComponent( email.body || '' );
+
+ // Build the e-mail parameters first.
+ var argList = [];
+ subject && argList.push( 'subject=' + subject );
+ body && argList.push( 'body=' + body );
+ argList = argList.length ? '?' + argList.join( '&' ) : '';
+
+ if ( emailProtection == 'encode' )
+ {
+ linkHref = [ 'javascript:void(location.href=\'mailto:\'+',
+ protectEmailAddressAsEncodedString( address ) ];
+ // parameters are optional.
+ argList && linkHref.push( '+\'', escapeSingleQuote( argList ), '\'' );
+
+ linkHref.push( ')' );
+ }
+ else
+ linkHref = [ 'mailto:', address, argList ];
+
+ break;
+ }
+ default :
+ {
+ // Separating name and domain.
+ var nameAndDomain = address.split( '@', 2 );
+ email.name = nameAndDomain[ 0 ];
+ email.domain = nameAndDomain[ 1 ];
+
+ linkHref = [ 'javascript:', protectEmailLinkAsFunction( email ) ];
+ }
+ }
+
+ attributes[ 'data-cke-saved-href' ] = linkHref.join( '' );
+ break;
+ }
+
+ // Popups and target.
+ if ( data.target )
+ {
+ if ( data.target.type == 'popup' )
+ {
+ var onclickList = [ 'window.open(this.href, \'',
+ data.target.name || '', '\', \'' ];
+ var featureList = [ 'resizable', 'status', 'location', 'toolbar', 'menubar', 'fullscreen',
+ 'scrollbars', 'dependent' ];
+ var featureLength = featureList.length;
+ var addFeature = function( featureName )
+ {
+ if ( data.target[ featureName ] )
+ featureList.push( featureName + '=' + data.target[ featureName ] );
+ };
+
+ for ( var i = 0 ; i < featureLength ; i++ )
+ featureList[i] = featureList[i] + ( data.target[ featureList[i] ] ? '=yes' : '=no' ) ;
+ addFeature( 'width' );
+ addFeature( 'left' );
+ addFeature( 'height' );
+ addFeature( 'top' );
+
+ onclickList.push( featureList.join( ',' ), '\'); return false;' );
+ attributes[ 'data-cke-pa-onclick' ] = onclickList.join( '' );
+
+ // Add the "target" attribute. (#5074)
+ removeAttributes.push( 'target' );
+ }
+ else
+ {
+ if ( data.target.type != 'notSet' && data.target.name )
+ attributes.target = data.target.name;
+ else
+ removeAttributes.push( 'target' );
+
+ removeAttributes.push( 'data-cke-pa-onclick', 'onclick' );
+ }
+ }
+
+ // Advanced attributes.
+ if ( data.adv )
+ {
+ var advAttr = function( inputName, attrName )
+ {
+ var value = data.adv[ inputName ];
+ if ( value )
+ attributes[attrName] = value;
+ else
+ removeAttributes.push( attrName );
+ };
+
+ advAttr( 'advId', 'id' );
+ advAttr( 'advLangDir', 'dir' );
+ advAttr( 'advAccessKey', 'accessKey' );
+
+ if ( data.adv[ 'advName' ] )
+ attributes[ 'name' ] = attributes[ 'data-cke-saved-name' ] = data.adv[ 'advName' ];
+ else
+ removeAttributes = removeAttributes.concat( [ 'data-cke-saved-name', 'name' ] );
+
+ advAttr( 'advLangCode', 'lang' );
+ advAttr( 'advTabIndex', 'tabindex' );
+ advAttr( 'advTitle', 'title' );
+ advAttr( 'advContentType', 'type' );
+ advAttr( 'advCSSClasses', 'class' );
+ advAttr( 'advCharset', 'charset' );
+ advAttr( 'advStyles', 'style' );
+ advAttr( 'advRel', 'rel' );
+ }
+
+
+ var selection = editor.getSelection();
+
+ // Browser need the "href" fro copy/paste link to work. (#6641)
+ attributes.href = attributes[ 'data-cke-saved-href' ];
+
+ if ( !this._.selectedElement )
+ {
+ // Create element if current selection is collapsed.
+ var ranges = selection.getRanges( true );
+ if ( ranges.length == 1 && ranges[0].collapsed )
+ {
+ // Short mailto link text view (#5736).
+ var text = new CKEDITOR.dom.text( data.type == 'email' ?
+ data.email.address : attributes[ 'data-cke-saved-href' ], editor.document );
+ ranges[0].insertNode( text );
+ ranges[0].selectNodeContents( text );
+ selection.selectRanges( ranges );
+ }
+
+ // Apply style.
+ var style = new CKEDITOR.style( { element : 'a', attributes : attributes } );
+ style.type = CKEDITOR.STYLE_INLINE; // need to override... dunno why.
+ style.apply( editor.document );
+ }
+ else
+ {
+ // We're only editing an existing link, so just overwrite the attributes.
+ var element = this._.selectedElement,
+ href = element.data( 'cke-saved-href' ),
+ textView = element.getHtml();
+
+ element.setAttributes( attributes );
+ element.removeAttributes( removeAttributes );
+
+ if ( data.adv && data.adv.advName && CKEDITOR.plugins.link.synAnchorSelector )
+ element.addClass( element.getChildCount() ? 'cke_anchor' : 'cke_anchor_empty' );
+
+ // Update text view when user changes protocol (#4612).
+ if ( href == textView || data.type == 'email' && textView.indexOf( '@' ) != -1 )
+ {
+ // Short mailto link text view (#5736).
+ element.setHtml( data.type == 'email' ?
+ data.email.address : attributes[ 'data-cke-saved-href' ] );
+ }
+
+ selection.selectElement( element );
+ delete this._.selectedElement;
+ }
+ },
+ onLoad : function()
+ {
+ if ( !editor.config.linkShowAdvancedTab )
+ this.hidePage( 'advanced' ); //Hide Advanded tab.
+
+ if ( !editor.config.linkShowTargetTab )
+ this.hidePage( 'target' ); //Hide Target tab.
+
+ },
+ // Inital focus on 'url' field if link is of type URL.
+ onFocus : function()
+ {
+ var linkType = this.getContentElement( 'info', 'linkType' ),
+ urlField;
+ if ( linkType && linkType.getValue() == 'url' )
+ {
+ urlField = this.getContentElement( 'info', 'url' );
+ urlField.select();
+ }
+ }
+ };
+});
+
+/**
+ * The e-mail address anti-spam protection option. The protection will be
+ * applied when creating or modifying e-mail links through the editor interface.<br>
+ * Two methods of protection can be choosed:
+ * <ol> <li>The e-mail parts (name, domain and any other query string) are
+ * assembled into a function call pattern. Such function must be
+ * provided by the developer in the pages that will use the contents.
+ * <li>Only the e-mail address is obfuscated into a special string that
+ * has no meaning for humans or spam bots, but which is properly
+ * rendered and accepted by the browser.</li></ol>
+ * Both approaches require JavaScript to be enabled.
+ * @name CKEDITOR.config.emailProtection
+ * @since 3.1
+ * @type String
+ * @default '' (empty string = disabled)
+ * @example
+ * // href="mailto:tester@ckeditor.com?subject=subject&body=body"
+ * config.emailProtection = '';
+ * @example
+ * // href="<a href=\"javascript:void(location.href=\'mailto:\'+String.fromCharCode(116,101,115,116,101,114,64,99,107,101,100,105,116,111,114,46,99,111,109)+\'?subject=subject&body=body\')\">e-mail</a>"
+ * config.emailProtection = 'encode';
+ * @example
+ * // href="javascript:mt('tester','ckeditor.com','subject','body')"
+ * config.emailProtection = 'mt(NAME,DOMAIN,SUBJECT,BODY)';
+ */
diff --git a/_source/plugins/link/plugin.js b/_source/plugins/link/plugin.js index ee2c004..d583369 100644 --- a/_source/plugins/link/plugin.js +++ b/_source/plugins/link/plugin.js @@ -1,229 +1,374 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -CKEDITOR.plugins.add( 'link', -{ - init : function( editor ) - { - // Add the link and unlink buttons. - editor.addCommand( 'link', new CKEDITOR.dialogCommand( 'link' ) ); - editor.addCommand( 'anchor', new CKEDITOR.dialogCommand( 'anchor' ) ); - editor.addCommand( 'unlink', new CKEDITOR.unlinkCommand() ); - editor.ui.addButton( 'Link', - { - label : editor.lang.link.toolbar, - command : 'link' - } ); - editor.ui.addButton( 'Unlink', - { - label : editor.lang.unlink, - command : 'unlink' - } ); - editor.ui.addButton( 'Anchor', - { - label : editor.lang.anchor.toolbar, - command : 'anchor' - } ); - CKEDITOR.dialog.add( 'link', this.path + 'dialogs/link.js' ); - CKEDITOR.dialog.add( 'anchor', this.path + 'dialogs/anchor.js' ); - - // Add the CSS styles for anchor placeholders. - editor.addCss( - 'img.cke_anchor' + - '{' + - 'background-image: url(' + CKEDITOR.getUrl( this.path + 'images/anchor.gif' ) + ');' + - 'background-position: center center;' + - 'background-repeat: no-repeat;' + - 'border: 1px solid #a9a9a9;' + - 'width: 18px;' + - 'height: 18px;' + - '}\n' + - 'a.cke_anchor' + - '{' + - 'background-image: url(' + CKEDITOR.getUrl( this.path + 'images/anchor.gif' ) + ');' + - 'background-position: 0 center;' + - 'background-repeat: no-repeat;' + - 'border: 1px solid #a9a9a9;' + - 'padding-left: 18px;' + - '}' - ); - - // Register selection change handler for the unlink button. - editor.on( 'selectionChange', function( evt ) - { - /* - * Despite our initial hope, document.queryCommandEnabled() does not work - * for this in Firefox. So we must detect the state by element paths. - */ - var command = editor.getCommand( 'unlink' ), - element = evt.data.path.lastElement.getAscendant( 'a', true ); - if ( element && element.getName() == 'a' && element.getAttribute( 'href' ) ) - command.setState( CKEDITOR.TRISTATE_OFF ); - else - command.setState( CKEDITOR.TRISTATE_DISABLED ); - } ); - - editor.on( 'doubleclick', function( evt ) - { - var element = CKEDITOR.plugins.link.getSelectedLink( editor ) || evt.data.element; - - if ( element.is( 'a' ) ) - evt.data.dialog = ( element.getAttribute( 'name' ) && !element.getAttribute( 'href' ) ) ? 'anchor' : 'link'; - else if ( element.is( 'img' ) && element.getAttribute( '_cke_real_element_type' ) == 'anchor' ) - evt.data.dialog = 'anchor'; - }); - - // If the "menu" plugin is loaded, register the menu items. - if ( editor.addMenuItems ) - { - editor.addMenuItems( - { - anchor : - { - label : editor.lang.anchor.menu, - command : 'anchor', - group : 'anchor' - }, - - link : - { - label : editor.lang.link.menu, - command : 'link', - group : 'link', - order : 1 - }, - - unlink : - { - label : editor.lang.unlink, - command : 'unlink', - group : 'link', - order : 5 - } - }); - } - - // If the "contextmenu" plugin is loaded, register the listeners. - if ( editor.contextMenu ) - { - editor.contextMenu.addListener( function( element, selection ) - { - if ( !element ) - return null; - - var isAnchor = ( element.is( 'img' ) && element.getAttribute( '_cke_real_element_type' ) == 'anchor' ); - - if ( !isAnchor ) - { - if ( !( element = CKEDITOR.plugins.link.getSelectedLink( editor ) ) ) - return null; - - isAnchor = ( element.getAttribute( 'name' ) && !element.getAttribute( 'href' ) ); - } - - return isAnchor ? - { anchor : CKEDITOR.TRISTATE_OFF } : - { link : CKEDITOR.TRISTATE_OFF, unlink : CKEDITOR.TRISTATE_OFF }; - }); - } - }, - - afterInit : function( editor ) - { - // Register a filter to displaying placeholders after mode change. - - var dataProcessor = editor.dataProcessor, - dataFilter = dataProcessor && dataProcessor.dataFilter; - - if ( dataFilter ) - { - dataFilter.addRules( - { - elements : - { - a : function( element ) - { - var attributes = element.attributes; - if ( attributes.name && !attributes.href ) - return editor.createFakeParserElement( element, 'cke_anchor', 'anchor' ); - } - } - }); - } - }, - - requires : [ 'fakeobjects' ] -} ); - -CKEDITOR.plugins.link = -{ - /** - * Get the surrounding link element of current selection. - * @param editor - * @example CKEDITOR.plugins.link.getSelectedLink( editor ); - * @since 3.2.1 - * The following selection will all return the link element. - * <pre> - * <a href="#">li^nk</a> - * <a href="#">[link]</a> - * text[<a href="#">link]</a> - * <a href="#">li[nk</a>] - * [<b><a href="#">li]nk</a></b>] - * [<a href="#"><b>li]nk</b></a> - * </pre> - */ - getSelectedLink : function( editor ) - { - var range; - try { range = editor.getSelection().getRanges()[ 0 ]; } - catch( e ) { return null; } - - range.shrink( CKEDITOR.SHRINK_TEXT ); - var root = range.getCommonAncestor(); - return root.getAscendant( 'a', true ); - } -}; - -CKEDITOR.unlinkCommand = function(){}; -CKEDITOR.unlinkCommand.prototype = -{ - /** @ignore */ - exec : function( editor ) - { - /* - * execCommand( 'unlink', ... ) in Firefox leaves behind <span> tags at where - * the <a> was, so again we have to remove the link ourselves. (See #430) - * - * TODO: Use the style system when it's complete. Let's use execCommand() - * as a stopgap solution for now. - */ - var selection = editor.getSelection(), - bookmarks = selection.createBookmarks(), - ranges = selection.getRanges(), - rangeRoot, - element; - - for ( var i = 0 ; i < ranges.length ; i++ ) - { - rangeRoot = ranges[i].getCommonAncestor( true ); - element = rangeRoot.getAscendant( 'a', true ); - if ( !element ) - continue; - ranges[i].selectNodeContents( element ); - } - - selection.selectRanges( ranges ); - editor.document.$.execCommand( 'unlink', false, null ); - selection.selectBookmarks( bookmarks ); - }, - - startDisabled : true -}; - -CKEDITOR.tools.extend( CKEDITOR.config, -{ - linkShowAdvancedTab : true, - linkShowTargetTab : true -} ); +/*
+Copyright (c) 2003-2012, 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
+} );
diff --git a/_source/plugins/list/plugin.js b/_source/plugins/list/plugin.js index 3de4edd..152be3c 100644 --- a/_source/plugins/list/plugin.js +++ b/_source/plugins/list/plugin.js @@ -1,656 +1,967 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -/** - * @file Insert and remove numbered and bulleted lists. - */ - -(function() -{ - var listNodeNames = { ol : 1, ul : 1 }, - emptyTextRegex = /^[\n\r\t ]*$/; - - CKEDITOR.plugins.list = { - /* - * Convert a DOM list tree into a data structure that is easier to - * manipulate. This operation should be non-intrusive in the sense that it - * does not change the DOM tree, with the exception that it may add some - * markers to the list item nodes when database is specified. - */ - listToArray : function( listNode, database, baseArray, baseIndentLevel, grandparentNode ) - { - if ( !listNodeNames[ listNode.getName() ] ) - return []; - - if ( !baseIndentLevel ) - baseIndentLevel = 0; - if ( !baseArray ) - baseArray = []; - - // Iterate over all list items to and look for inner lists. - for ( var i = 0, count = listNode.getChildCount() ; i < count ; i++ ) - { - var listItem = listNode.getChild( i ); - - // It may be a text node or some funny stuff. - if ( listItem.$.nodeName.toLowerCase() != 'li' ) - continue; - - var itemObj = { 'parent' : listNode, indent : baseIndentLevel, element : listItem, contents : [] }; - if ( !grandparentNode ) - { - itemObj.grandparent = listNode.getParent(); - if ( itemObj.grandparent && itemObj.grandparent.$.nodeName.toLowerCase() == 'li' ) - itemObj.grandparent = itemObj.grandparent.getParent(); - } - else - itemObj.grandparent = grandparentNode; - - if ( database ) - CKEDITOR.dom.element.setMarker( database, listItem, 'listarray_index', baseArray.length ); - baseArray.push( itemObj ); - - for ( var j = 0, itemChildCount = listItem.getChildCount(), child; j < itemChildCount ; j++ ) - { - child = listItem.getChild( j ); - if ( child.type == CKEDITOR.NODE_ELEMENT && listNodeNames[ child.getName() ] ) - // Note the recursion here, it pushes inner list items with - // +1 indentation in the correct order. - CKEDITOR.plugins.list.listToArray( child, database, baseArray, baseIndentLevel + 1, itemObj.grandparent ); - else - itemObj.contents.push( child ); - } - } - return baseArray; - }, - - // Convert our internal representation of a list back to a DOM forest. - arrayToList : function( listArray, database, baseIndex, paragraphMode ) - { - if ( !baseIndex ) - baseIndex = 0; - if ( !listArray || listArray.length < baseIndex + 1 ) - return null; - var doc = listArray[ baseIndex ].parent.getDocument(), - retval = new CKEDITOR.dom.documentFragment( doc ), - rootNode = null, - currentIndex = baseIndex, - indentLevel = Math.max( listArray[ baseIndex ].indent, 0 ), - currentListItem = null, - paragraphName = ( paragraphMode == CKEDITOR.ENTER_P ? 'p' : 'div' ); - while ( true ) - { - var item = listArray[ currentIndex ]; - if ( item.indent == indentLevel ) - { - if ( !rootNode || listArray[ currentIndex ].parent.getName() != rootNode.getName() ) - { - rootNode = listArray[ currentIndex ].parent.clone( false, true ); - retval.append( rootNode ); - } - currentListItem = rootNode.append( item.element.clone( false, true ) ); - for ( var i = 0 ; i < item.contents.length ; i++ ) - currentListItem.append( item.contents[i].clone( true, true ) ); - currentIndex++; - } - else if ( item.indent == Math.max( indentLevel, 0 ) + 1 ) - { - var listData = CKEDITOR.plugins.list.arrayToList( listArray, null, currentIndex, paragraphMode ); - currentListItem.append( listData.listNode ); - currentIndex = listData.nextIndex; - } - else if ( item.indent == -1 && !baseIndex && item.grandparent ) - { - currentListItem; - if ( listNodeNames[ item.grandparent.getName() ] ) - currentListItem = item.element.clone( false, true ); - else - { - // Create completely new blocks here, attributes are dropped. - if ( paragraphMode != CKEDITOR.ENTER_BR && item.grandparent.getName() != 'td' ) - currentListItem = doc.createElement( paragraphName ); - else - currentListItem = new CKEDITOR.dom.documentFragment( doc ); - } - - for ( i = 0 ; i < item.contents.length ; i++ ) - currentListItem.append( item.contents[i].clone( true, true ) ); - - if ( currentListItem.type == CKEDITOR.NODE_DOCUMENT_FRAGMENT - && currentIndex != listArray.length - 1 ) - { - if ( currentListItem.getLast() - && currentListItem.getLast().type == CKEDITOR.NODE_ELEMENT - && currentListItem.getLast().getAttribute( 'type' ) == '_moz' ) - currentListItem.getLast().remove(); - currentListItem.appendBogus(); - } - - if ( currentListItem.type == CKEDITOR.NODE_ELEMENT && - currentListItem.getName() == paragraphName && - currentListItem.$.firstChild ) - { - currentListItem.trim(); - var firstChild = currentListItem.getFirst(); - if ( firstChild.type == CKEDITOR.NODE_ELEMENT && firstChild.isBlockBoundary() ) - { - var tmp = new CKEDITOR.dom.documentFragment( doc ); - currentListItem.moveChildren( tmp ); - currentListItem = tmp; - } - } - - var currentListItemName = currentListItem.$.nodeName.toLowerCase(); - if ( !CKEDITOR.env.ie && ( currentListItemName == 'div' || currentListItemName == 'p' ) ) - currentListItem.appendBogus(); - retval.append( currentListItem ); - rootNode = null; - currentIndex++; - } - else - return null; - - if ( listArray.length <= currentIndex || Math.max( listArray[ currentIndex ].indent, 0 ) < indentLevel ) - break; - } - - // Clear marker attributes for the new list tree made of cloned nodes, if any. - if ( database ) - { - var currentNode = retval.getFirst(); - while ( currentNode ) - { - if ( currentNode.type == CKEDITOR.NODE_ELEMENT ) - CKEDITOR.dom.element.clearMarkers( database, currentNode ); - currentNode = currentNode.getNextSourceNode(); - } - } - - return { listNode : retval, nextIndex : currentIndex }; - } - }; - - function setState( editor, state ) - { - editor.getCommand( this.name ).setState( state ); - } - - function onSelectionChange( evt ) - { - var path = evt.data.path, - blockLimit = path.blockLimit, - elements = path.elements, - element; - - // Grouping should only happen under blockLimit.(#3940). - for ( var i = 0 ; i < elements.length && ( element = elements[ i ] ) - && !element.equals( blockLimit ); i++ ) - { - if ( listNodeNames[ elements[i].getName() ] ) - { - return setState.call( this, evt.editor, - this.type == elements[i].getName() ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF ); - } - } - - return setState.call( this, evt.editor, CKEDITOR.TRISTATE_OFF ); - } - - function changeListType( editor, groupObj, database, listsCreated ) - { - // This case is easy... - // 1. Convert the whole list into a one-dimensional array. - // 2. Change the list type by modifying the array. - // 3. Recreate the whole list by converting the array to a list. - // 4. Replace the original list with the recreated list. - var listArray = CKEDITOR.plugins.list.listToArray( groupObj.root, database ), - selectedListItems = []; - - for ( var i = 0 ; i < groupObj.contents.length ; i++ ) - { - var itemNode = groupObj.contents[i]; - itemNode = itemNode.getAscendant( 'li', true ); - if ( !itemNode || itemNode.getCustomData( 'list_item_processed' ) ) - continue; - selectedListItems.push( itemNode ); - CKEDITOR.dom.element.setMarker( database, itemNode, 'list_item_processed', true ); - } - - var fakeParent = groupObj.root.getDocument().createElement( this.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() ); - - // 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 ); - 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 ); - while ( listContents.length ) - { - var contentBlock = listContents.shift(), - listItem = doc.createElement( 'li' ); - - // Preserve heading structure when converting to list item. (#5271) - if ( headerTagRegex.test( contentBlock.getName() ) ) - contentBlock.appendTo( listItem ); - else - { - contentBlock.copyAttributes( listItem ); - contentBlock.moveChildren( listItem ); - contentBlock.remove(); - } - - listItem.appendTo( listNode ); - - // Append a bogus BR to force the LI to render at full height - if ( !CKEDITOR.env.ie ) - listItem.appendBogus(); - } - 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 ); - - // Compensate <br> before/after the list node if the surrounds are non-blocks.(#3836) - var docFragment = newList.listNode, boundaryNode, siblingNode; - function compensateBrs( isStart ) - { - if ( ( boundaryNode = docFragment[ isStart ? 'getFirst' : 'getLast' ]() ) - && !( boundaryNode.is && boundaryNode.isBlockBoundary() ) - && ( siblingNode = groupObj.root[ isStart ? 'getPrevious' : 'getNext' ] - ( CKEDITOR.dom.walker.whitespaces( true ) ) ) - && !( siblingNode.is && siblingNode.isBlockBoundary( { br : 1 } ) ) ) - editor.document.createElement( 'br' )[ isStart ? 'insertBefore' : 'insertAfter' ]( boundaryNode ); - } - compensateBrs( true ); - compensateBrs(); - - docFragment.replace( groupObj.root ); - } - - function listCommand( name, type ) - { - this.name = name; - this.type = type; - } - - listCommand.prototype = { - exec : function( editor ) - { - editor.focus(); - - var doc = editor.document, - selection = editor.getSelection(), - ranges = selection && selection.getRanges(); - - // There should be at least one selected range. - if ( !ranges || ranges.length < 1 ) - return; - - // Midas lists rule #1 says we can create a list even in an empty document. - // But DOM iterator wouldn't run if the document is really empty. - // So create a paragraph if the document is empty and we're going to create a list. - if ( this.state == CKEDITOR.TRISTATE_OFF ) - { - var body = doc.getBody(); - body.trim(); - if ( !body.getFirst() ) - { - var paragraph = doc.createElement( editor.config.enterMode == CKEDITOR.ENTER_P ? 'p' : - ( editor.config.enterMode == CKEDITOR.ENTER_DIV ? 'div' : 'br' ) ); - paragraph.appendTo( body ); - ranges = [ new CKEDITOR.dom.range( doc ) ]; - // IE exception on inserting anything when anchor inside <br>. - if ( paragraph.is( 'br' ) ) - { - ranges[ 0 ].setStartBefore( paragraph ); - ranges[ 0 ].setEndAfter( paragraph ); - } - else - ranges[ 0 ].selectNodeContents( paragraph ); - selection.selectRanges( ranges ); - } - // Maybe a single range there enclosing the whole list, - // turn on the list state manually(#4129). - else - { - var range = ranges.length == 1 && ranges[ 0 ], - enclosedNode = range && range.getEnclosedNode(); - if ( enclosedNode && enclosedNode.is - && this.type == enclosedNode.getName() ) - { - setState.call( this, editor, CKEDITOR.TRISTATE_ON ); - } - } - } - - var bookmarks = selection.createBookmarks( true ); - - // Group the blocks up because there are many cases where multiple lists have to be created, - // or multiple lists have to be cancelled. - var listGroups = [], - database = {}; - - while ( ranges.length > 0 ) - { - range = ranges.shift(); - - 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() ) ) - { - var path = new CKEDITOR.dom.elementPath( block ), - pathElements = path.elements, - pathElementsCount = pathElements.length, - listNode = null, - processedFlag = false, - 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' ); - - 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 = true; - break; - } - } - - if ( processedFlag ) - continue; - - // No list ancestor? Group by block limit. - var root = blockLimit; - if ( root.getCustomData( 'list_group_object' ) ) - root.getCustomData( 'list_group_object' ).contents.push( block ); - else - { - groupObj = { root : root, contents : [ block ] }; - CKEDITOR.dom.element.setMarker( database, root, 'list_group_object', groupObj ); - listGroups.push( groupObj ); - } - } - } - - // Now we have two kinds of list groups, groups rooted at a list, and groups rooted at a block limit element. - // We either have to build lists or remove lists, for removing a list does not makes sense when we are looking - // at the group that's not rooted at lists. So we have three cases to handle. - var listsCreated = []; - while ( listGroups.length > 0 ) - { - groupObj = listGroups.shift(); - if ( this.state == CKEDITOR.TRISTATE_OFF ) - { - if ( listNodeNames[ groupObj.root.getName() ] ) - changeListType.call( this, editor, groupObj, database, listsCreated ); - else - createList.call( this, editor, groupObj, listsCreated ); - } - else if ( this.state == CKEDITOR.TRISTATE_ON && listNodeNames[ groupObj.root.getName() ] ) - removeList.call( this, editor, groupObj, database ); - } - - // For all new lists created, merge adjacent, same type lists. - for ( i = 0 ; i < listsCreated.length ; i++ ) - { - listNode = listsCreated[i]; - var mergeSibling, listCommand = this; - ( mergeSibling = function( rtl ){ - - var sibling = listNode[ rtl ? - 'getPrevious' : 'getNext' ]( CKEDITOR.dom.walker.whitespaces( true ) ); - if ( sibling && sibling.getName && - sibling.getName() == listCommand.type ) - { - sibling.remove(); - // Move children order by merge direction.(#3820) - sibling.moveChildren( listNode, rtl ? true : false ); - } - } )(); - mergeSibling( true ); - } - - // 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 ]*(?: |\xa0)$/; - - function indexOfFirstChildElement( element, tagNameList ) - { - var child, - children = element.children, - length = children.length; - - for ( var i = 0 ; i < length ; i++ ) - { - child = children[ i ]; - if ( child.name && ( child.name in tagNameList ) ) - return i; - } - - return length; - } - - function getExtendNestedListFilter( isHtmlFilter ) - { - // An element filter function that corrects nested list start in an empty - // list item for better displaying/outputting. (#3165) - return function( listItem ) - { - var children = listItem.children, - firstNestedListIndex = indexOfFirstChildElement( listItem, dtd.$list ), - firstNestedList = children[ firstNestedListIndex ], - nodeBefore = firstNestedList && firstNestedList.previous, - tailNbspmatch; - - if ( nodeBefore - && ( nodeBefore.name && nodeBefore.name == 'br' - || nodeBefore.value && ( tailNbspmatch = nodeBefore.value.match( tailNbspRegex ) ) ) ) - { - var fillerNode = nodeBefore; - - // Always use 'nbsp' as filler node if we found a nested list appear - // in front of a list item. - if ( !( tailNbspmatch && tailNbspmatch.index ) && fillerNode == children[ 0 ] ) - children[ 0 ] = ( isHtmlFilter || CKEDITOR.env.ie ) ? - new CKEDITOR.htmlParser.text( '\xa0' ) : - new CKEDITOR.htmlParser.element( 'br', {} ); - - // Otherwise the filler is not needed anymore. - else if ( fillerNode.name == 'br' ) - children.splice( firstNestedListIndex - 1, 1 ); - else - fillerNode.value = fillerNode.value.replace( tailNbspRegex, '' ); - } - - }; - } - - var defaultListDataFilterRules = { elements : {} }; - for ( var i in dtd.$listItem ) - defaultListDataFilterRules.elements[ i ] = getExtendNestedListFilter(); - - var defaultListHtmlFilterRules = { elements : {} }; - for ( i in dtd.$listItem ) - defaultListHtmlFilterRules.elements[ i ] = getExtendNestedListFilter( true ); - - CKEDITOR.plugins.add( 'list', - { - init : function( editor ) - { - // Register commands. - var numberedListCommand = new listCommand( 'numberedlist', 'ol' ), - bulletedListCommand = new listCommand( 'bulletedlist', 'ul' ); - editor.addCommand( 'numberedlist', numberedListCommand ); - editor.addCommand( 'bulletedlist', bulletedListCommand ); - - // Register the toolbar button. - editor.ui.addButton( 'NumberedList', - { - label : editor.lang.numberedlist, - command : 'numberedlist' - } ); - editor.ui.addButton( 'BulletedList', - { - label : editor.lang.bulletedlist, - command : 'bulletedlist' - } ); - - // Register the state changing handlers. - editor.on( 'selectionChange', CKEDITOR.tools.bind( onSelectionChange, numberedListCommand ) ); - editor.on( 'selectionChange', CKEDITOR.tools.bind( onSelectionChange, bulletedListCommand ) ); - }, - - afterInit : function ( editor ) - { - var dataProcessor = editor.dataProcessor; - if ( dataProcessor ) - { - dataProcessor.dataFilter.addRules( defaultListDataFilterRules ); - dataProcessor.htmlFilter.addRules( defaultListHtmlFilterRules ); - } - }, - - requires : [ 'domiterator' ] - } ); -})(); +/*
+Copyright (c) 2003-2012, 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 ) );},
+ blockBogus = CKEDITOR.dom.walker.bogus();
+
+ 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 i,
+ 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,
+ block,
+ 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 ( 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
+ currentListItem = new CKEDITOR.dom.documentFragment( doc );
+
+ // Migrate all children to the new container,
+ // apply the proper text direction.
+ var dirLoose = item.grandparent.getDirection( 1 ) != orgDir,
+ needsBlock = currentListItem.type == CKEDITOR.NODE_DOCUMENT_FRAGMENT &&
+ paragraphMode != CKEDITOR.ENTER_BR,
+ li = item.element,
+ className = li.getAttribute( 'class' ),
+ style = li.getAttribute( 'style' );
+
+ var child, count = item.contents.length;
+ for ( i = 0 ; i < count; i++ )
+ {
+ child = item.contents[ i ];
+
+ if ( child.type == CKEDITOR.NODE_ELEMENT && child.isBlockBoundary() )
+ {
+ // Apply direction on content blocks.
+ if ( dirLoose && !child.getDirection() )
+ child.setAttribute( 'dir', orgDir );
+
+ // Merge into child styles.
+ style && child.setAttribute( 'style',
+ style.replace( /([^;])$/, '$1;') +
+ ( child.getAttribute( 'style' ) || '' ) );
+
+ className && child.addClass( className );
+ }
+ else if ( dirLoose || needsBlock || style || className )
+ {
+ // Establish new block to hold text direction and styles.
+ if ( !block )
+ {
+ block = doc.createElement( paragraphName );
+ dirLoose && block.setAttribute( 'dir', orgDir );
+ }
+
+ // Copy over styles to new block;
+ style && block.setAttribute( 'style', style );
+ className && block.setAttribute( 'class', className );
+
+ block.append( child.clone( 1, 1 ) );
+ }
+
+ currentListItem.append( block || child.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' ) );
+ }
+ }
+
+ var currentListItemName = currentListItem.$.nodeName.toLowerCase();
+ if ( !CKEDITOR.env.ie && ( currentListItemName == 'div' || currentListItemName == 'p' ) )
+ currentListItem.appendBogus();
+ retval.append( currentListItem );
+ rootNode = null;
+ currentIndex++;
+ }
+ else
+ return null;
+
+ block = 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;
+ }
+
+ var elementType = CKEDITOR.dom.walker.nodeType( CKEDITOR.NODE_ELEMENT );
+ // Merge list items with direction preserved. (#7448)
+ function mergeListItems( from, into, refNode, toHead )
+ {
+ var child, itemDir;
+ while ( ( child = from.getFirst( elementType ) ) )
+ {
+ if ( ( itemDir = child.getDirection( 1 ) ) !== into.getDirection( 1 ) )
+ child.setAttribute( 'dir', itemDir );
+
+ child.remove();
+
+ refNode ?
+ child[ toHead ? 'insertBefore' : 'insertAfter' ]( refNode ) :
+ into.append( child, toHead );
+ }
+ }
+
+ 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 into 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 )
+ {
+ // Move children order by merge direction.(#3820)
+ mergeListItems( listNode, sibling, null, !rtl );
+
+ listNode.remove();
+ listNode = sibling;
+ }
+ } )();
+ 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 ]*(?: |\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 );
+
+ // Check if node is block element that recieves text.
+ function isTextBlock( node )
+ {
+ return node.type == CKEDITOR.NODE_ELEMENT &&
+ ( node.getName() in CKEDITOR.dtd.$block ||
+ node.getName() in CKEDITOR.dtd.$listItem ) &&
+ CKEDITOR.dtd[ node.getName() ][ '#' ];
+ }
+
+ // Merge the visual line content at the cursor range into the block.
+ function joinNextLineToCursor( editor, cursor, nextCursor )
+ {
+ editor.fire( 'saveSnapshot' );
+
+ // Merge with previous block's content.
+ nextCursor.enlarge( CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS );
+ var frag = nextCursor.extractContents();
+
+ cursor.trim( false, true );
+
+ // Kill original bogus;
+ var currentPath = new CKEDITOR.dom.elementPath( cursor.startContainer );
+ var currentLi = currentPath.lastElement.getAscendant( 'li', 1 );
+
+ var bogus = currentPath.block.getBogus();
+ bogus && bogus.remove();
+
+ // Kill the tail br in extracted.
+ var last = frag.getLast();
+ if ( last && last.type == CKEDITOR.NODE_ELEMENT && last.is( 'br' ) )
+ last.remove();
+
+ // Insert fragment at the range position.
+ var nextNode = cursor.startContainer.getChild( cursor.startOffset );
+ if ( nextNode )
+ frag.insertBefore( nextNode );
+ else
+ cursor.startContainer.append( frag );
+
+ var nextPath = new CKEDITOR.dom.elementPath( nextCursor.startContainer );
+ var nextLi = nextCursor.startContainer.getAscendant( 'li', 1 );
+
+ // Move the sub list nested in the next list item.
+ if ( nextLi )
+ {
+ var sublist = getSubList( nextLi );
+ if ( sublist )
+ {
+ // If next line is in the sub list of the current list item.
+ if ( currentLi.contains( nextLi ) )
+ {
+ mergeListItems( sublist, nextLi.getParent(), nextLi );
+ sublist.remove();
+ }
+ // Migrate the sub list to current list item.
+ else
+ currentLi.append( sublist );
+ }
+ }
+
+
+ if ( nextCursor.checkStartOfBlock() &&
+ nextCursor.checkEndOfBlock() )
+ {
+ var nextBlock = nextPath.block,
+ parentBlock = nextBlock.getParent();
+
+ nextBlock.remove();
+
+ // Remove if the path block container is now empty, e.g. li.
+ if ( parentBlock &&
+ !parentBlock.getFirst( nonEmpty ) &&
+ !parentBlock.equals( nextPath.blockLimit ) )
+ {
+ parentBlock.remove();
+ }
+ }
+
+ // Make fresh selection.
+ cursor.select();
+
+ editor.fire( 'saveSnapshot' );
+ }
+
+ function getSubList( li )
+ {
+ var last = li.getLast( nonEmpty );
+ return last && last.type == CKEDITOR.NODE_ELEMENT && last.getName() in listNodeNames ? last : null;
+ }
+
+ 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 ) );
+
+ // [IE8] Fix "backspace" after list and "del" at the end of list item. (#8248)
+ if ( CKEDITOR.env.ie8Compat )
+ {
+ editor.on( 'key', function( evt )
+ {
+ var key = evt.data.keyCode;
+
+ // DEl/BACKSPACE
+ if ( editor.mode == 'wysiwyg' && key in { 8 : 1, 46 : 1 } )
+ {
+ var sel = editor.getSelection(),
+ range = sel.getRanges()[ 0 ];
+
+ if ( !range.collapsed )
+ return;
+
+ var isBackspace = key == 8;
+ var body = editor.document.getBody();
+ var walker = new CKEDITOR.dom.walker( range.clone() );
+ walker.evaluator = function( node ) { return nonEmpty( node ) && !blockBogus( node ); };
+
+ var cursor = range.clone();
+
+ if ( isBackspace )
+ {
+ walker.range.setStartAt( body, CKEDITOR.POSITION_AFTER_START );
+ walker.range.setEnd( range.startContainer, range.startOffset );
+
+ var previous = walker.previous();
+
+ // Check if cursor collapsed right behind of a list.
+ if ( previous &&
+ previous.type == CKEDITOR.NODE_ELEMENT &&
+ previous.getName() in listNodeNames )
+ {
+ walker.range.selectNodeContents( previous );
+ walker.reset();
+ walker.evaluator = isTextBlock;
+
+ // Place cursor at the end of previous block.
+ cursor.moveToElementEditEnd( walker.lastForward() );
+ joinNextLineToCursor( editor, cursor, range );
+ evt.cancel();
+ }
+ }
+ else
+ {
+ var li = range.startContainer.getAscendant( 'li', 1 );
+ if ( li )
+ {
+ walker.range.setEndAt( body, CKEDITOR.POSITION_BEFORE_END );
+
+ var last = li.getLast( nonEmpty );
+ var block = last && isTextBlock( last ) ? last : li;
+
+ // Indicate cursor at the visual end of an list item.
+ var isAtEnd = 0;
+
+ var next = walker.next();
+
+ // When list item contains a sub list.
+ if ( next && next.type == CKEDITOR.NODE_ELEMENT &&
+ next.getName() in listNodeNames
+ && next.equals( last ) )
+ {
+ isAtEnd = 1;
+
+ // Move to the first item in sub list.
+ next = walker.next();
+ }
+ // Right at the end of list item.
+ else if ( range.checkBoundaryOfElement( block, CKEDITOR.END ) )
+ isAtEnd = 1;
+
+
+ if ( isAtEnd && next )
+ {
+ // Put cursor range there.
+ var nextLine = range.clone();
+ nextLine.moveToElementEditStart( next );
+
+ joinNextLineToCursor( editor, cursor, nextLine );
+ evt.cancel();
+ }
+ }
+ }
+ }
+ } );
+ }
+ },
+
+ afterInit : function ( editor )
+ {
+ var dataProcessor = editor.dataProcessor;
+ if ( dataProcessor )
+ {
+ dataProcessor.dataFilter.addRules( defaultListDataFilterRules );
+ dataProcessor.htmlFilter.addRules( defaultListHtmlFilterRules );
+ }
+ },
+
+ requires : [ 'domiterator' ]
+ } );
+})();
diff --git a/_source/plugins/listblock/plugin.js b/_source/plugins/listblock/plugin.js index fc3909b..c505f0e 100644 --- a/_source/plugins/listblock/plugin.js +++ b/_source/plugins/listblock/plugin.js @@ -1,257 +1,260 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -CKEDITOR.plugins.add( 'listblock', -{ - requires : [ 'panel' ], - - onLoad : function() - { - CKEDITOR.ui.panel.prototype.addListBlock = function( name, definition ) - { - return this.addBlock( name, new CKEDITOR.ui.listBlock( this.getHolderElement(), definition ) ); - }; - - CKEDITOR.ui.listBlock = CKEDITOR.tools.createClass( - { - base : CKEDITOR.ui.panel.block, - - $ : function( blockHolder, blockDefinition ) - { - blockDefinition = blockDefinition || {}; - - var attribs = blockDefinition.attributes || ( blockDefinition.attributes = {} ); - ( this.multiSelect = !!blockDefinition.multiSelect ) && - ( attribs[ 'aria-multiselectable' ] = true ); - // Provide default role of 'listbox'. - !attribs.role && ( attribs.role = 'listbox' ); - - // Call the base contructor. - this.base.apply( this, arguments ); - - var keys = this.keys; - keys[ 40 ] = 'next'; // ARROW-DOWN - keys[ 9 ] = 'next'; // TAB - keys[ 38 ] = 'prev'; // ARROW-UP - keys[ CKEDITOR.SHIFT + 9 ] = 'prev'; // SHIFT + TAB - keys[ 32 ] = 'click'; // SPACE - - this._.pendingHtml = []; - this._.items = {}; - this._.groups = {}; - }, - - _ : - { - close : function() - { - if ( this._.started ) - { - this._.pendingHtml.push( '</ul>' ); - delete this._.started; - } - }, - - getClick : function() - { - if ( !this._.click ) - { - this._.click = CKEDITOR.tools.addFunction( function( value ) - { - var marked = true; - - if ( this.multiSelect ) - marked = this.toggle( value ); - else - this.mark( value ); - - if ( this.onClick ) - this.onClick( value, marked ); - }, - this ); - } - return this._.click; - } - }, - - proto : - { - add : function( value, html, title ) - { - var pendingHtml = this._.pendingHtml, - id = 'cke_' + CKEDITOR.tools.getNextNumber(); - - 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>' + - '<a id="', id, '_option" _cke_focus=1 hidefocus=true' + - ' title="', title || value, '"' + - ' href="javascript:void(\'', value, '\')"' + - ' onclick="CKEDITOR.tools.callFunction(', this._.getClick(), ',\'', value, '\'); return false;"', - ' role="option"' + - ' aria-posinset="' + ++this._.size + '">', - html || value, - '</a>' + - '</li>' ); - }, - - startGroup : function( title ) - { - this._.close(); - - var id = 'cke_' + CKEDITOR.tools.getNextNumber(); - - this._.groups[ title ] = id; - - this._.pendingHtml.push( '<h1 role="presentation" id=', id, ' class=cke_panel_grouptitle>', title, '</h1>' ); - }, - - commit : function() - { - this._.close(); - this.element.appendHtml( this._.pendingHtml.join( '' ) ); - - var items = this._.items, - doc = this.element.getDocument(); - for ( var value in items ) - doc.getById( items[ value ] + '_option' ).setAttribute( 'aria-setsize', this._.size ); - delete this._.size; - - this._.pendingHtml = []; - }, - - toggle : function( value ) - { - var isMarked = this.isMarked( value ); - - if ( isMarked ) - this.unmark( value ); - else - this.mark( value ); - - return !isMarked; - }, - - hideGroup : function( groupTitle ) - { - var group = this.element.getDocument().getById( this._.groups[ groupTitle ] ), - list = group && group.getNext(); - - if ( group ) - { - group.setStyle( 'display', 'none' ); - - if ( list && list.getName() == 'ul' ) - list.setStyle( 'display', 'none' ); - } - }, - - hideItem : function( value ) - { - this.element.getDocument().getById( this._.items[ value ] ).setStyle( 'display', 'none' ); - }, - - showAll : function() - { - var items = this._.items, - groups = this._.groups, - doc = this.element.getDocument(); - - for ( var value in items ) - { - doc.getById( items[ value ] ).setStyle( 'display', '' ); - } - - for ( var title in groups ) - { - var group = doc.getById( groups[ title ] ), - list = group.getNext(); - - group.setStyle( 'display', '' ); - - if ( list && list.getName() == 'ul' ) - list.setStyle( 'display', '' ); - } - }, - - mark : function( value ) - { - if ( !this.multiSelect ) - this.unmarkAll(); - - var itemId = this._.items[ value ], - item = this.element.getDocument().getById( itemId ); - item.addClass( 'cke_selected' ); - - this.element.getDocument().getById( itemId + '_option' ).setAttribute( 'aria-selected', true ); - this.element.setAttribute( 'aria-activedescendant', itemId + '_option' ); - - this.onMark && this.onMark( item ); - }, - - unmark : function( value ) - { - this.element.getDocument().getById( this._.items[ value ] ).removeClass( 'cke_selected' ); - this.onUnmark && this.onUnmark( this._.items[ value ] ); - }, - - unmarkAll : function() - { - var items = this._.items, - doc = this.element.getDocument(); - - for ( var value in items ) - { - doc.getById( items[ value ] ).removeClass( 'cke_selected' ); - } - - this.onUnmark && this.onUnmark(); - }, - - isMarked : function( value ) - { - return this.element.getDocument().getById( this._.items[ value ] ).hasClass( 'cke_selected' ); - }, - - focus : function( value ) - { - this._.focusIndex = -1; - - if ( value ) - { - var selected = this.element.getDocument().getById( this._.items[ value ] ).getFirst(); - - var links = this.element.getElementsByTag( 'a' ), - link, - i = -1; - - while ( ( link = links.getItem( ++i ) ) ) - { - if ( link.equals( selected ) ) - { - this._.focusIndex = i; - break; - } - } - - setTimeout( function() - { - selected.focus(); - }, - 0 ); - } - } - } - }); - } -}); +/*
+Copyright (c) 2003-2012, 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">',
+ 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( '' ) );
+ 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 );
+ }
+ }
+ }
+ });
+ }
+});
diff --git a/_source/plugins/liststyle/dialogs/liststyle.js b/_source/plugins/liststyle/dialogs/liststyle.js index 6430ac4..5b776ed 100644 --- a/_source/plugins/liststyle/dialogs/liststyle.js +++ b/_source/plugins/liststyle/dialogs/liststyle.js @@ -1,202 +1,225 @@ -/* - * Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. - * For licensing, see LICENSE.html or http://ckeditor.com/license - */ - -(function() -{ - function getListElement( editor, listTag ) - { - var range; - try { range = editor.getSelection().getRanges()[ 0 ]; } - catch( e ) { return null; } - - range.shrink( CKEDITOR.SHRINK_TEXT ); - return range.getCommonAncestor().getAscendant( listTag, true ); - } - - 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 ) - { - if ( startupPage == 'bulletedListStyle' ) - { - return { - title : editor.lang.list.bulletedTitle, - minWidth : 300, - minHeight : 50, - contents : - [ - { - id : 'info', - accessKey : 'I', - elements : - [ - { - type : 'select', - label : editor.lang.list.type, - id : 'type', - style : 'width: 150px; margin: auto;', - items : - [ - [ editor.lang.list.notset, '' ], - [ editor.lang.list.circle, 'circle' ], - [ editor.lang.list.disc, 'disc' ], - [ editor.lang.list.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 = - [ - [ editor.lang.list.notset, '' ], - [ editor.lang.list.lowerRoman, 'lower-roman' ], - [ editor.lang.list.upperRoman, 'upper-roman' ], - [ editor.lang.list.lowerAlpha, 'lower-alpha' ], - [ editor.lang.list.upperAlpha, 'upper-alpha' ], - [ editor.lang.list.decimal, 'decimal' ] - ]; - - if ( !CKEDITOR.env.ie || CKEDITOR.env.version > 7 ) - { - listStyleOptions.concat( [ - [ editor.lang.list.armenian, 'armenian' ], - [ editor.lang.list.decimalLeadingZero, 'decimal-leading-zero' ], - [ editor.lang.list.georgian, 'georgian' ], - [ editor.lang.list.lowerGreek, 'lower-greek' ] - ]); - } - - return { - title : editor.lang.list.numberedTitle, - minWidth : 300, - minHeight : 50, - contents : - [ - { - id : 'info', - accessKey : 'I', - elements : - [ - { - type : 'hbox', - widths : [ '25%', '75%' ], - children : - [ - { - label : editor.lang.list.start, - type : 'text', - id : 'start', - setup : function( element ) - { - var value = element.getAttribute( 'start' ) || 1; - value && this.setValue( value ); - }, - commit : function( element ) - { - element.setAttribute( 'start', this.getValue() ); - } - }, - { - type : 'select', - label : editor.lang.list.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' ); - }); -})(); +/*
+ * Copyright (c) 2003-2012, 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' );
+ });
+})();
diff --git a/_source/plugins/liststyle/plugin.js b/_source/plugins/liststyle/plugin.js index 133290f..e99b339 100644 --- a/_source/plugins/liststyle/plugin.js +++ b/_source/plugins/liststyle/plugin.js @@ -1,59 +1,66 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -(function() -{ - CKEDITOR.plugins.liststyle = - { - 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' ); - - //Register map group; - editor.addMenuGroup("list", 108); - // If the "menu" plugin is loaded, register the menu items. - if ( editor.addMenuItems ) - { - 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 ) - return null; - - if ( element.getAscendant( 'ol') ) - return { numberedlist: CKEDITOR.TRISTATE_OFF }; - - if ( element.getAscendant( 'ul' ) ) - return { bulletedlist: CKEDITOR.TRISTATE_OFF }; - }); - } - } - }; - - CKEDITOR.plugins.add( 'liststyle', CKEDITOR.plugins.liststyle ); -})(); +/*
+Copyright (c) 2003-2012, 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 );
+})();
diff --git a/_source/plugins/maximize/plugin.js b/_source/plugins/maximize/plugin.js index ea769f3..9e9676a 100644 --- a/_source/plugins/maximize/plugin.js +++ b/_source/plugins/maximize/plugin.js @@ -1,341 +1,353 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -(function() -{ - function protectFormStyles( formElement ) - { - if ( !formElement || formElement.type != CKEDITOR.NODE_ELEMENT || formElement.getName() != 'form' ) - return []; - - var hijackRecord = []; - var hijackNames = [ 'style', 'className' ]; - for ( var i = 0 ; i < hijackNames.length ; i++ ) - { - var name = hijackNames[i]; - var $node = formElement.$.elements.namedItem( name ); - if ( $node ) - { - var hijackNode = new CKEDITOR.dom.element( $node ); - hijackRecord.push( [ hijackNode, hijackNode.nextSibling ] ); - hijackNode.remove(); - } - } - - return hijackRecord; - } - - function restoreFormStyles( formElement, hijackRecord ) - { - if ( !formElement || formElement.type != CKEDITOR.NODE_ELEMENT || formElement.getName() != 'form' ) - return; - - if ( hijackRecord.length > 0 ) - { - for ( var i = hijackRecord.length - 1 ; i >= 0 ; i-- ) - { - var node = hijackRecord[i][0]; - var sibling = hijackRecord[i][1]; - if ( sibling ) - node.insertBefore( sibling ); - else - node.appendTo( formElement ); - } - } - } - - function saveStyles( element, isInsideEditor ) - { - var data = protectFormStyles( element ); - var retval = {}; - - var $element = element.$; - - if ( !isInsideEditor ) - { - retval[ 'class' ] = $element.className || ''; - $element.className = ''; - } - - retval.inline = $element.style.cssText || ''; - if ( !isInsideEditor ) // Reset any external styles that might interfere. (#2474) - $element.style.cssText = 'position: static; overflow: visible'; - - restoreFormStyles( data ); - return retval; - } - - function restoreStyles( element, savedStyles ) - { - var data = protectFormStyles( element ); - var $element = element.$; - if ( 'class' in savedStyles ) - $element.className = savedStyles[ 'class' ]; - if ( 'inline' in savedStyles ) - $element.style.cssText = savedStyles.inline; - restoreFormStyles( data ); - } - - function refreshCursor( editor ) - { - // Refresh all editor instances on the page (#5724). - var all = CKEDITOR.instances; - for ( var i in all ) - { - var one = all[ i ]; - if ( one.mode == 'wysiwyg' ) - { - var body = one.document.getBody(); - // Refresh 'contentEditable' otherwise - // DOM lifting breaks design mode. (#5560) - body.setAttribute( 'contentEditable', false ); - body.setAttribute( 'contentEditable', true ); - } - } - - if ( editor.focusManager.hasFocus ) - { - editor.toolbox.focus(); - editor.focus(); - } - } - - /** - * Adding an iframe shim to this element, OR removing the existing one if already applied. - * Note: This will only affect IE version below 7. - */ - function createIframeShim( element ) - { - if ( !CKEDITOR.env.ie || CKEDITOR.env.version > 6 ) - return null; - - var shim = CKEDITOR.dom.element.createFromHtml( '<iframe frameborder="0" tabindex="-1"' + - ' src="javascript:' + - 'void((function(){' + - 'document.open();' + - ( CKEDITOR.env.isCustomDomain() ? 'document.domain=\'' + this.getDocument().$.domain + '\';' : '' ) + - 'document.close();' + - '})())"' + - ' style="display:block;position:absolute;z-index:-1;' + - 'progid:DXImageTransform.Microsoft.Alpha(opacity=0);' + - '"></iframe>' ); - return element.append( shim, true ); - } - - CKEDITOR.plugins.add( 'maximize', - { - init : function( editor ) - { - var lang = editor.lang; - var mainDocument = CKEDITOR.document; - var mainWindow = mainDocument.getWindow(); - - // Saved selection and scroll position for the editing area. - var savedSelection; - var savedScroll; - - // Saved scroll position for the outer window. - var outerScroll; - - var shim; - - // Saved resize handler function. - function resizeHandler() - { - var viewPaneSize = mainWindow.getViewPaneSize(); - shim && shim.setStyles( { width : viewPaneSize.width + 'px', height : viewPaneSize.height + 'px' } ); - editor.resize( viewPaneSize.width, viewPaneSize.height, null, true ); - } - - // Retain state after mode switches. - var savedState = CKEDITOR.TRISTATE_OFF; - - editor.addCommand( 'maximize', - { - modes : { wysiwyg : 1, source : 1 }, - editorFocus : false, - exec : function() - { - var container = editor.container.getChild( 1 ); - var contents = editor.getThemeSpace( 'contents' ); - - // Save current selection and scroll position in editing area. - if ( editor.mode == 'wysiwyg' ) - { - var selection = editor.getSelection(); - savedSelection = selection && selection.getRanges(); - savedScroll = mainWindow.getScrollPosition(); - } - else - { - var $textarea = editor.textarea.$; - savedSelection = !CKEDITOR.env.ie && [ $textarea.selectionStart, $textarea.selectionEnd ]; - savedScroll = [ $textarea.scrollLeft, $textarea.scrollTop ]; - } - - if ( this.state == CKEDITOR.TRISTATE_OFF ) // Go fullscreen if the state is off. - { - // Add event handler for resizing. - mainWindow.on( 'resize', resizeHandler ); - - // Save the scroll bar position. - outerScroll = mainWindow.getScrollPosition(); - - // Save and reset the styles for the entire node tree. - var currentNode = editor.container; - while ( ( currentNode = currentNode.getParent() ) ) - { - currentNode.setCustomData( 'maximize_saved_styles', saveStyles( currentNode ) ); - currentNode.setStyle( 'z-index', editor.config.baseFloatZIndex - 1 ); - } - contents.setCustomData( 'maximize_saved_styles', saveStyles( contents, true ) ); - container.setCustomData( 'maximize_saved_styles', saveStyles( container, true ) ); - - // Hide scroll bars. - if ( CKEDITOR.env.ie ) - { - mainDocument.$.documentElement.style.overflow = - mainDocument.getBody().$.style.overflow = 'hidden'; - } - else - { - mainDocument.getBody().setStyles( - { - overflow : 'hidden', - width : '0px', - height : '0px' - } ); - } - - // 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. - var viewPaneSize = mainWindow.getViewPaneSize(); - container.setStyle( 'position', 'absolute' ); - container.$.offsetLeft; // SAFARI BUG: See #2066. - container.setStyles( - { - 'z-index' : editor.config.baseFloatZIndex - 1, - left : '0px', - top : '0px' - } ); - - shim = createIframeShim( container ); // IE6 select element penetration when maximized. (#4459) - 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 ); - - // Add cke_maximized class. - container.addClass( 'cke_maximized' ); - } - else if ( this.state == CKEDITOR.TRISTATE_ON ) // Restore from fullscreen if the state is on. - { - // Remove event handler for resizing. - mainWindow.removeListener( 'resize', resizeHandler ); - - // Restore CSS styles for the entire node tree. - var editorElements = [ contents, container ]; - for ( var i = 0 ; i < editorElements.length ; i++ ) - { - restoreStyles( editorElements[i], editorElements[i].getCustomData( 'maximize_saved_styles' ) ); - editorElements[i].removeCustomData( 'maximize_saved_styles' ); - } - - currentNode = editor.container; - while ( ( currentNode = currentNode.getParent() ) ) - { - restoreStyles( currentNode, currentNode.getCustomData( 'maximize_saved_styles' ) ); - currentNode.removeCustomData( 'maximize_saved_styles' ); - } - - // Restore the window scroll position. - CKEDITOR.env.ie ? - setTimeout( function() { mainWindow.$.scrollTo( outerScroll.x, outerScroll.y ); }, 0 ) : - mainWindow.$.scrollTo( outerScroll.x, outerScroll.y ); - - // Remove cke_maximized class. - container.removeClass( 'cke_maximized' ); - - if ( shim ) - { - shim.remove(); - shim = null; - } - - // Emit a resize event, because this time the size is modified in - // restoreStyles. - editor.fire( 'resize' ); - } - - this.toggleState(); - - // Toggle button label. - var button = this.uiItems[ 0 ]; - var label = ( this.state == CKEDITOR.TRISTATE_OFF ) - ? lang.maximize : lang.minimize; - var buttonNode = editor.element.getDocument().getById( button._.id ); - buttonNode.getChild( 1 ).setHtml( label ); - buttonNode.setAttribute( 'title', label ); - buttonNode.setAttribute( 'href', 'javascript:void("' + label + '");' ); - - // Restore selection and scroll position in editing area. - if ( editor.mode == 'wysiwyg' ) - { - if ( savedSelection ) - { - // Fixing positioning editor chrome in Firefox break design mode. (#5149) - CKEDITOR.env.gecko && refreshCursor( editor ); - - editor.getSelection().selectRanges(savedSelection); - var element = editor.getSelection().getStartElement(); - element && element.scrollIntoView( true ); - } - - else - mainWindow.$.scrollTo( savedScroll.x, savedScroll.y ); - } - else - { - if ( savedSelection ) - { - $textarea.selectionStart = savedSelection[0]; - $textarea.selectionEnd = savedSelection[1]; - } - $textarea.scrollLeft = savedScroll[0]; - $textarea.scrollTop = savedScroll[1]; - } - - savedSelection = savedScroll = null; - savedState = this.state; - }, - canUndo : false - } ); - - editor.ui.addButton( 'Maximize', - { - label : lang.maximize, - command : 'maximize' - } ); - - // Restore the command state after mode change. - editor.on( 'mode', function() - { - editor.getCommand( 'maximize' ).setState( savedState ); - }, null, null, 100 ); - } - } ); -})(); +/*
+Copyright (c) 2003-2012, 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 );
+ }
+ } );
+})();
diff --git a/_source/plugins/menu/plugin.js b/_source/plugins/menu/plugin.js index ac45279..265604c 100644 --- a/_source/plugins/menu/plugin.js +++ b/_source/plugins/menu/plugin.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
@@ -14,17 +14,36 @@ CKEDITOR.plugins.add( 'menu', 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 )
@@ -33,10 +52,27 @@ CKEDITOR.plugins.add( 'menu', }
};
+ /**
+ * 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' ]
@@ -49,10 +85,11 @@ CKEDITOR.plugins.add( 'menu', $ : function( editor, definition )
{
definition = this._.definition = definition || {};
- this.id = 'cke_' + CKEDITOR.tools.getNextNumber();
+ this.id = CKEDITOR.tools.getNextId();
this.editor = editor;
this.items = [];
+ this._.listeners = [];
this._.level = definition.level || 1;
@@ -71,6 +108,83 @@ CKEDITOR.plugins.add( 'menu', _ :
{
+ 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()
+ {
+ // Unlock the selection upon first panel closing.
+ if ( CKEDITOR.env.ie && !this.parent )
+ {
+ var selection = this.editor.getSelection();
+ selection && selection.unlock( true );
+ }
+
+ this.onHide && this.onHide();
+ },
+
showSubMenu : function( index )
{
var menu = this._.subMenu,
@@ -98,9 +212,7 @@ CKEDITOR.plugins.add( 'menu', 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 );
- // Sub menu use their own scope for binding onEscape.
- menu.onEscape = this.onEscape;
+ menu._.onClick = CKEDITOR.tools.bind( this._.onClick, this );
}
// Add all submenu items to the menu.
@@ -142,6 +254,17 @@ CKEDITOR.plugins.add( 'menu', 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,
@@ -157,14 +280,14 @@ CKEDITOR.plugins.add( 'menu', panel.onEscape = CKEDITOR.tools.bind( function( keystroke )
{
- if ( this.onEscape && this.onEscape( keystroke ) === false )
+ if ( this._.onEscape( keystroke ) === false )
return false;
},
this );
panel.onHide = CKEDITOR.tools.bind( function()
{
- this.onHide && this.onHide();
+ this._.onHide && this._.onHide();
},
this );
@@ -177,8 +300,9 @@ CKEDITOR.plugins.add( 'menu', keys[ 9 ] = 'next'; // TAB
keys[ 38 ] = 'prev'; // ARROW-UP
keys[ CKEDITOR.SHIFT + 9 ] = 'prev'; // SHIFT + TAB
- keys[ 32 ] = 'click'; // SPACE
- keys[ ( editor.lang.dir == 'rtl' ? 37 : 39 ) ] = 'click'; // ARROW-RIGHT/ARROW-LEFT(rtl)
+ 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 );
@@ -190,15 +314,15 @@ CKEDITOR.plugins.add( 'menu', this._.itemOverFn = CKEDITOR.tools.addFunction( function( index )
{
clearTimeout( this._.showSubTimeout );
- this._.showSubTimeout = CKEDITOR.tools.setTimeout( this._.showSubMenu, editor.config.menu_subMenuDelay, this, [ index ] );
+ this._.showSubTimeout = CKEDITOR.tools.setTimeout( this._.showSubMenu, editor.config.menu_subMenuDelay || 400, this, [ index ] );
},
- this);
+ this );
this._.itemOutFn = CKEDITOR.tools.addFunction( function( index )
{
clearTimeout( this._.showSubTimeout );
},
- this);
+ this );
this._.itemClickFn = CKEDITOR.tools.addFunction( function( index )
{
@@ -213,16 +337,19 @@ CKEDITOR.plugins.add( 'menu', if ( item.getItems )
this._.showSubMenu( index );
else
- this.onClick && this.onClick( item );
+ this._.onClick( item );
},
- this);
+ 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" role="presentation">' ];
+ var output = [ '<div class="cke_menu' + mixedContentClass + '" role="presentation">' ];
var length = items.length,
lastGroup = length && items[ 0 ].group;
@@ -244,6 +371,8 @@ CKEDITOR.plugins.add( 'menu', // 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 );
@@ -253,9 +382,15 @@ CKEDITOR.plugins.add( 'menu', editor.fire( 'menuShow', [ panel ] );
},
- hide : function()
+ addListener : function( listenerFn )
{
- this._.panel && this._.panel.hide();
+ this._.listeners.push( listenerFn );
+ },
+
+ hide : function( returnFocus )
+ {
+ this._.onHide && this._.onHide();
+ this._.panel && this._.panel.hide( returnFocus );
}
}
});
@@ -274,47 +409,45 @@ CKEDITOR.plugins.add( 'menu', 0;
});
}
-})();
-
-CKEDITOR.menuItem = CKEDITOR.tools.createClass(
-{
- $ : function( editor, name, definition )
+ CKEDITOR.menuItem = CKEDITOR.tools.createClass(
{
- CKEDITOR.tools.extend( this, definition,
- // Defaults
- {
- order : 0,
- className : 'cke_button_' + name
- });
+ $ : 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 ];
+ // Transform the group name into its order number.
+ this.group = editor._.menuGroups[ this.group ];
- this.editor = editor;
- this.name = name;
- },
+ this.editor = editor;
+ this.name = name;
+ },
- proto :
- {
- render : function( menu, index, output )
+ proto :
{
- var id = menu.id + String( index ),
- state = ( typeof this.state == 'undefined' ) ? CKEDITOR.TRISTATE_OFF : this.state;
+ 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 classes = ' cke_' + (
+ state == CKEDITOR.TRISTATE_ON ? 'on' :
+ state == CKEDITOR.TRISTATE_DISABLED ? 'disabled' :
+ 'off' );
- var htmlLabel = this.label;
+ var htmlLabel = this.label;
- if ( this.className )
- classes += ' ' + this.className;
+ if ( this.className )
+ classes += ' ' + this.className;
var hasSubMenu = this.getItems;
output.push(
- '<span class="cke_menuitem">' +
+ '<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, '"' +
@@ -326,29 +459,30 @@ CKEDITOR.menuItem = CKEDITOR.tools.createClass( ( 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;"' );
- }
+ // 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;"' );
- }
+ // 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(
+ var offset = ( this.iconOffset || 0 ) * -16;
+ output.push(
// ' onkeydown="return CKEDITOR.ui.button._.keydown(', index, ', event);"' +
' onmouseover="CKEDITOR.tools.callFunction(', menu._.itemOverFn, ',', index, ');"' +
- ' onmouseout="CKEDITOR.tools.callFunction(', menu._.itemOutFn, ',', index, ');"' +
- ' onclick="CKEDITOR.tools.callFunction(', menu._.itemClickFn, ',', index, '); return false;"' +
+ ' 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;"'
@@ -368,18 +502,21 @@ CKEDITOR.menuItem = CKEDITOR.tools.createClass( '</span>' );
}
- output.push(
- htmlLabel,
- '</span>' +
- '</a>' +
- '</span>' );
+ output.push(
+ htmlLabel,
+ '</span>' +
+ '</a>' +
+ '</span>' );
}
- }
-});
+ }
+ });
+
+})();
+
/**
- * The amount of time, in milliseconds, the editor waits before showing submenu
- * options when moving the mouse over options that contains submenus, like the
+ * 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
@@ -387,12 +524,11 @@ CKEDITOR.menuItem = CKEDITOR.tools.createClass( * // Remove the submenu delay.
* config.menu_subMenuDelay = 0;
*/
-CKEDITOR.config.menu_subMenuDelay = 400;
/**
* A comma separated list of items group names to be displayed in the context
- * menu. The items order will reflect the order in this list if no priority
- * has been definted in the groups.
+ * 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
diff --git a/_source/plugins/menubutton/plugin.js b/_source/plugins/menubutton/plugin.js index f453239..1bb6599 100644 --- a/_source/plugins/menubutton/plugin.js +++ b/_source/plugins/menubutton/plugin.js @@ -1,94 +1,98 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -CKEDITOR.plugins.add( 'menubutton', -{ - requires : [ 'button', 'contextmenu' ], - beforeInit : function( editor ) - { - editor.ui.addHandler( CKEDITOR.UI_MENUBUTTON, CKEDITOR.ui.menuButton.handler ); - } -}); - -/** - * Button UI element. - * @constant - * @example - */ -CKEDITOR.UI_MENUBUTTON = 5; - -(function() -{ - var clickFn = function( editor ) - { - var _ = this._; - - // Do nothing if this button is disabled. - if ( _.state === CKEDITOR.TRISTATE_DISABLED ) - return; - - _.previousState = _.state; - - // Check if we already have a menu for it, otherwise just create it. - var menu = _.menu; - if ( !menu ) - { - menu = _.menu = new CKEDITOR.plugins.contextMenu( editor ); - menu.definition.panel.attributes[ 'aria-label' ] = editor.lang.common.options; - - menu.onHide = CKEDITOR.tools.bind( function() - { - this.setState( _.previousState ); - }, - 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 ); - } - } - } - }); -})(); +/*
+Copyright (c) 2003-2012, 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 );
+ }
+ }
+ }
+ });
+})();
diff --git a/_source/plugins/newpage/plugin.js b/_source/plugins/newpage/plugin.js index 62c5405..6a9d7ca 100644 --- a/_source/plugins/newpage/plugin.js +++ b/_source/plugins/newpage/plugin.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
@@ -19,7 +19,7 @@ CKEDITOR.plugins.add( 'newpage', exec : function( editor )
{
var command = this;
- editor.setData( editor.config.newpage_html, function()
+ editor.setData( editor.config.newpage_html || '', function()
{
// Save the undo snapshot after all document changes are affected. (#4889)
setTimeout( function ()
@@ -29,6 +29,7 @@ CKEDITOR.plugins.add( 'newpage', name: command.name,
command: command
} );
+ editor.selectionChange();
}, 200 );
} );
@@ -46,9 +47,9 @@ CKEDITOR.plugins.add( '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 = '<p>Type your text here.</p>';
*/
-CKEDITOR.config.newpage_html = '';
diff --git a/_source/plugins/pagebreak/plugin.js b/_source/plugins/pagebreak/plugin.js index 3d840fc..3b54b68 100644 --- a/_source/plugins/pagebreak/plugin.js +++ b/_source/plugins/pagebreak/plugin.js @@ -1,107 +1,164 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -/** - * @file Horizontal Page Break - */ - -// Register a plugin named "pagebreak". -CKEDITOR.plugins.add( 'pagebreak', -{ - init : function( editor ) - { - // Register the command. - editor.addCommand( 'pagebreak', CKEDITOR.plugins.pagebreakCmd ); - - // Register the toolbar button. - editor.ui.addButton( 'PageBreak', - { - label : editor.lang.pagebreak, - command : 'pagebreak' - }); - - // Add the style that renders our placeholder. - editor.addCss( - 'img.cke_pagebreak' + - '{' + - 'background-image: url(' + CKEDITOR.getUrl( this.path + 'images/pagebreak.gif' ) + ');' + - 'background-position: center center;' + - 'background-repeat: no-repeat;' + - 'clear: both;' + - 'display: block;' + - 'float: none;' + - 'width:100%;_width:99.9%;' + - 'border-top: #999999 1px dotted;' + - 'border-bottom: #999999 1px dotted;' + - 'height: 5px;' + - 'page-break-after: always;' + - - '}' ); - }, - - afterInit : function( editor ) - { - // Register a filter to displaying placeholders after mode change. - - var dataProcessor = editor.dataProcessor, - dataFilter = dataProcessor && dataProcessor.dataFilter; - - if ( dataFilter ) - { - dataFilter.addRules( - { - elements : - { - div : function( element ) - { - var attributes = element.attributes, - style = attributes && attributes.style, - child = style && element.children.length == 1 && element.children[ 0 ], - childStyle = child && ( child.name == 'span' ) && child.attributes.style; - - if ( childStyle && ( /page-break-after\s*:\s*always/i ).test( style ) && ( /display\s*:\s*none/i ).test( childStyle ) ) - return editor.createFakeParserElement( element, 'cke_pagebreak', 'div' ); - } - } - }); - } - }, - - requires : [ 'fakeobjects' ] -}); - -CKEDITOR.plugins.pagebreakCmd = -{ - exec : function( editor ) - { - // Create the element that represents a print break. - var breakObject = CKEDITOR.dom.element.createFromHtml( '<div style="page-break-after: always;"><span style="display: none;"> </span></div>' ); - - // Creates the fake image used for this element. - breakObject = editor.createFakeElement( breakObject, 'cke_pagebreak', 'div' ); - - var ranges = editor.getSelection().getRanges(); - - editor.fire( 'saveSnapshot' ); - - for ( var range, i = 0 ; i < ranges.length ; i++ ) - { - range = ranges[ i ]; - - if ( i > 0 ) - breakObject = breakObject.clone( true ); - - range.splitBlock( 'p' ); - range.insertNode( breakObject ); - if ( i == ranges.length - 1 ) - { - range.moveToPosition( breakObject, CKEDITOR.POSITION_AFTER_END ); - range.select(); - } - } - - editor.fire( 'saveSnapshot' ); - } -}; +/*
+Copyright (c) 2003-2012, 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;"> </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' );
+ }
+};
diff --git a/_source/plugins/panel/plugin.js b/_source/plugins/panel/plugin.js index 2bc6ba9..6dc1616 100644 --- a/_source/plugins/panel/plugin.js +++ b/_source/plugins/panel/plugin.js @@ -1,397 +1,396 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -CKEDITOR.plugins.add( 'panel', -{ - beforeInit : function( editor ) - { - editor.ui.addHandler( CKEDITOR.UI_PANEL, CKEDITOR.ui.panel.handler ); - } -}); - -/** - * Panel UI element. - * @constant - * @example - */ -CKEDITOR.UI_PANEL = 2; - -CKEDITOR.ui.panel = function( document, definition ) -{ - // Copy all definition properties to this object. - if ( definition ) - CKEDITOR.tools.extend( this, definition ); - - // Set defaults. - CKEDITOR.tools.extend( this, - { - className : '', - css : [] - }); - - this.id = CKEDITOR.tools.getNextNumber(); - 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 = 'cke_' + 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( 'cke_' + this.id + '_frame' ), - parentDiv = iframe.getParent(), - dir = parentDiv.getAttribute( 'dir' ), - className = parentDiv.getParent().getAttribute( 'class' ), - langCode = parentDiv.getParent().getAttribute( 'lang' ), - doc = iframe.getFrameDocument(); - // Initialize the IFRAME document body. - doc.$.open(); - - // Support for custom document.domain in IE. - if ( CKEDITOR.env.isCustomDomain() ) - doc.$.domain = document.domain; - - var onLoad = CKEDITOR.tools.addFunction( CKEDITOR.tools.bind( function( ev ) - { - this.isLoaded = true; - if ( this.onLoad ) - this.onLoad(); - }, this ) ); - - doc.$.write( - '<!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.$.close(); - - var win = doc.getWindow(); - - // Register the CKEDITOR global. - win.$.CKEDITOR = CKEDITOR; - - doc.on( 'keydown', function( evt ) - { - var keystroke = evt.data.getKeystroke(), - dir = this.document.getById( 'cke_' + 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(); - } - else - holder = this.document.getById( 'cke_' + 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( 'cke_' + this.id + '_frame' ) - : this._.holder; - - // Disable context menu for block panel. - holder.getParent().getParent().disableContextMenu(); - - if ( current ) - { - // Clean up the current block's effects on holder. - holder.removeAttributes( current.attributes ); - current.hide(); - } - - this._.currentBlock = block; - - holder.setAttributes( block.attributes ); - CKEDITOR.fire( 'ariaWidget', holder ); - - // Reset the focus index, so it will always go into the first one. - block._.focusIndex = -1; - - this._.onKeyDown = block.onKeyDown && CKEDITOR.tools.bind( block.onKeyDown, block ); - - block.onMark = function( item ) - { - holder.setAttribute( 'aria-activedescendant', item.getId() + '_option' ); - }; - - block.onUnmark = function() - { - holder.removeAttribute( 'aria-activedescendant' ); - }; - - block.show(); - - return block; - }, - - destroy : function() - { - this.element && this.element.remove(); - } -}; - -CKEDITOR.ui.panel.block = CKEDITOR.tools.createClass( -{ - $ : function( blockHolder, blockDefinition ) - { - this.element = blockHolder.append( - blockHolder.getDocument().createElement( 'div', - { - attributes : - { - 'tabIndex' : -1, - 'class' : 'cke_panel_block', - 'role' : 'presentation' - }, - styles : - { - display : 'none' - } - }) ); - - // Copy all definition properties to this object. - if ( blockDefinition ) - CKEDITOR.tools.extend( this, blockDefinition ); - - if ( !this.attributes.title ) - this.attributes.title = this.attributes[ 'aria-label' ]; - - this.keys = {}; - - this._.focusIndex = -1; - - // Disable context menu for panels. - this.element.disableContextMenu(); - }, - - _ : { - - /** - * Mark the item specified by the index as current activated. - */ - markItem: function( index ) - { - if ( index == -1 ) - return; - var links = this.element.getElementsByTag( 'a' ); - var item = links.getItem( this._.focusIndex = index ); - - // Safari need focus on the iframe window first(#3389), but we need - // lock the blur to avoid hiding the panel. - if ( CKEDITOR.env.webkit ) - item.getDocument().getWindow().focus(); - item.focus(); - - this.onMark && this.onMark( item ); - } - }, - - proto : - { - show : function() - { - this.element.setStyle( 'display', '' ); - }, - - hide : function() - { - if ( !this.onHide || this.onHide.call( this ) !== true ) - this.element.setStyle( 'display', 'none' ); - }, - - onKeyDown : function( keystroke ) - { - var keyAction = this.keys[ keystroke ]; - switch ( keyAction ) - { - // Move forward. - case 'next' : - var index = this._.focusIndex, - links = this.element.getElementsByTag( 'a' ), - link; - - while ( ( link = links.getItem( ++index ) ) ) - { - // Move the focus only if the element is marked with - // the _cke_focus and it it's visible (check if it has - // width). - if ( link.getAttribute( '_cke_focus' ) && link.$.offsetWidth ) - { - this._.focusIndex = index; - link.focus(); - break; - } - } - return false; - - // Move backward. - case 'prev' : - index = this._.focusIndex; - links = this.element.getElementsByTag( 'a' ); - - while ( index > 0 && ( link = links.getItem( --index ) ) ) - { - // Move the focus only if the element is marked with - // the _cke_focus and it it's visible (check if it has - // width). - if ( link.getAttribute( '_cke_focus' ) && link.$.offsetWidth ) - { - this._.focusIndex = index; - link.focus(); - break; - } - } - return false; - - case 'click' : - index = this._.focusIndex; - link = index >= 0 && this.element.getElementsByTag( 'a' ).getItem( index ); - - if ( link ) - link.$.click ? link.$.click() : link.$.onclick(); - - return false; - } - - return true; - } - } -}); +/*
+Copyright (c) 2003-2012, 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>' +
+ CKEDITOR.tools.buildStyleHtml( this.css ) +
+ '</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>' +
+ '<\/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;
+
+ // ARIA role works better in IE on the body element, while on the iframe
+ // for FF. (#8864)
+ var holder = !this.forceIFrame || CKEDITOR.env.ie ?
+ this._.holder : this.document.getById( this.id + '_frame' );
+
+ 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
+ */
diff --git a/_source/plugins/panelbutton/plugin.js b/_source/plugins/panelbutton/plugin.js index 4e017b4..078b398 100644 --- a/_source/plugins/panelbutton/plugin.js +++ b/_source/plugins/panelbutton/plugin.js @@ -1,147 +1,144 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.add( 'panelbutton',
{
requires : [ 'button' ],
- beforeInit : function( editor )
+ onLoad : function()
{
- editor.ui.addHandler( CKEDITOR.UI_PANELBUTTON, CKEDITOR.ui.panelButton.handler );
- }
-});
-
-/**
- * Button UI element.
- * @constant
- * @example
- */
-CKEDITOR.UI_PANELBUTTON = 4;
+ function clickFn( editor )
+ {
+ var _ = this._;
-(function()
-{
- var clickFn = function( editor )
- {
- var _ = this._;
+ if ( _.state == CKEDITOR.TRISTATE_DISABLED )
+ return;
- if ( _.state == CKEDITOR.TRISTATE_DISABLED )
- return;
+ this.createPanel( editor );
- this.createPanel( editor );
+ if ( _.on )
+ {
+ _.panel.hide();
+ return;
+ }
- if ( _.on )
- {
- _.panel.hide();
- return;
+ _.panel.showBlock( this._.id, this.document.getById( this._.id ), 4 );
}
- _.panel.showBlock( this._.id, this.document.getById( this._.id ), 4 );
- };
-
-
- CKEDITOR.ui.panelButton = CKEDITOR.tools.createClass(
- {
- base : CKEDITOR.ui.button,
-
- $ : function( definition )
+ CKEDITOR.ui.panelButton = CKEDITOR.tools.createClass(
{
- // We don't want the panel definition in this object.
- var panelDefinition = definition.panel;
- delete definition.panel;
+ base : CKEDITOR.ui.button,
- this.base( definition );
+ $ : function( definition )
+ {
+ // We don't want the panel definition in this object.
+ var panelDefinition = definition.panel;
+ delete definition.panel;
- this.document = ( panelDefinition
- && panelDefinition.parent
- && panelDefinition.parent.getDocument() )
- || CKEDITOR.document;
+ this.base( definition );
- panelDefinition.block =
- {
- attributes : panelDefinition.attributes
- };
+ this.document = ( panelDefinition
+ && panelDefinition.parent
+ && panelDefinition.parent.getDocument() )
+ || CKEDITOR.document;
- this.hasArrow = true;
+ panelDefinition.block =
+ {
+ attributes : panelDefinition.attributes
+ };
- this.click = clickFn;
+ this.hasArrow = true;
- this._ =
- {
- panelDefinition : panelDefinition
- };
- },
+ this.click = clickFn;
- statics :
- {
- handler :
+ this._ =
+ {
+ panelDefinition : panelDefinition
+ };
+ },
+
+ statics :
{
- create : function( definition )
+ handler :
{
- return new CKEDITOR.ui.panelButton( definition );
+ create : function( definition )
+ {
+ return new CKEDITOR.ui.panelButton( definition );
+ }
}
- }
- },
+ },
- proto :
- {
- createPanel : function( editor )
+ proto :
{
- var _ = this._;
+ createPanel : function( editor )
+ {
+ var _ = this._;
- if ( _.panel )
- return;
+ 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;
+ 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' );
+ panel.onShow = function()
+ {
+ if ( me.className )
+ this.element.getFirst().addClass( me.className + '_panel' );
- _.oldState = me._.state;
- me.setState( CKEDITOR.TRISTATE_ON );
+ me.setState( CKEDITOR.TRISTATE_ON );
- _.on = 1;
+ _.on = 1;
- if ( me.onOpen )
- me.onOpen();
- };
+ if ( me.onOpen )
+ me.onOpen();
+ };
- panel.onHide = function()
- {
- if ( me.className )
- this.element.getFirst().removeClass( me.className + '_panel' );
+ panel.onHide = function( preventOnClose )
+ {
+ if ( me.className )
+ this.element.getFirst().removeClass( me.className + '_panel' );
- me.setState( _.oldState );
+ me.setState( me.modes && me.modes[ editor.mode ] ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED );
- _.on = 0;
+ _.on = 0;
- if ( me.onClose )
- me.onClose();
- };
+ if ( !preventOnClose && me.onClose )
+ me.onClose();
+ };
- panel.onEscape = function()
- {
- panel.hide();
- me.document.getById( _.id ).focus();
- };
+ panel.onEscape = function()
+ {
+ panel.hide();
+ me.document.getById( _.id ).focus();
+ };
- if ( this.onBlock )
- this.onBlock( panel, block );
+ if ( this.onBlock )
+ this.onBlock( panel, block );
- block.onHide = function()
+ block.onHide = function()
{
- _.on = 0;
- me.setState( CKEDITOR.TRISTATE_OFF );
+ _.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';
diff --git a/_source/plugins/pastefromword/filter/default.js b/_source/plugins/pastefromword/filter/default.js index b042a33..353e4bf 100644 --- a/_source/plugins/pastefromword/filter/default.js +++ b/_source/plugins/pastefromword/filter/default.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
@@ -60,15 +60,13 @@ For licensing, see LICENSE.html or http://ckeditor.com/license child = child.firstChild( evaluator );
if ( child )
return child;
- else
- continue;
}
}
return null;
};
- // Adding a (set) of styles to the element's attributes.
+ // Adding a (set) of styles to the element's 'style' attributes.
elementPrototype.addStyle = function( name, value, isPrepend )
{
var styleText, addingStyleText = '';
@@ -120,65 +118,107 @@ For licensing, see LICENSE.html or http://ckeditor.com/license return result;
};
- var cssLengthRelativeUnit = /^(\d[.\d]*)+(em|ex|px|gd|rem|vw|vh|vm|ch|mm|cm|in|pt|pc|deg|rad|ms|s|hz|khz){1}?/i;
- var emptyMarginRegex = /^(?:\b0[^\s]*\s*){1,4}$/;
+ // 1. move consistent list item styles up to list root.
+ // 2. clear out unnecessary list item numbering.
+ function postProcessList( list )
+ {
+ var children = list.children,
+ child,
+ attrs,
+ count = list.children.length,
+ match,
+ mergeStyle,
+ styleTypeRegexp = /list-style-type:(.*?)(?:;|$)/,
+ stylesFilter = CKEDITOR.plugins.pastefromword.filters.stylesFilter;
- var listBaseIndent = 0,
- previousListItemMargin;
+ attrs = list.attributes;
+ if ( styleTypeRegexp.exec( attrs.style ) )
+ return;
- CKEDITOR.plugins.pastefromword =
- {
- utils :
+ for ( var i = 0; i < count; i++ )
{
- // Create a <cke:listbullet> which indicate an list item type.
- createListBulletMarker : function ( bulletStyle, bulletText )
- {
- var marker = new CKEDITOR.htmlParser.element( 'cke:listbullet' ),
- listType;
+ child = children[ i ];
- // TODO: Support more list style type from MS-Word.
- if ( !bulletStyle )
- {
- bulletStyle = 'decimal';
- listType = 'ol';
- }
- else if ( bulletStyle[ 2 ] )
- {
- if ( !isNaN( bulletStyle[ 1 ] ) )
- bulletStyle = 'decimal';
- // No way to distinguish between Roman numerals and Alphas,
- // detect them as a whole.
- else if ( /^[a-z]+$/.test( bulletStyle[ 1 ] ) )
- bulletStyle = 'lower-alpha';
- else if ( /^[A-Z]+$/.test( bulletStyle[ 1 ] ) )
- bulletStyle = 'upper-alpha';
- // Simply use decimal for the rest forms of unrepresentable
- // numerals, e.g. Chinese...
- else
- bulletStyle = 'decimal';
+ if ( child.attributes.value && Number( child.attributes.value ) == i + 1 )
+ delete child.attributes.value;
- listType = 'ol';
- }
+ match = styleTypeRegexp.exec( child.attributes.style );
+
+ if ( match )
+ {
+ if ( match[ 1 ] == mergeStyle || !mergeStyle )
+ mergeStyle = match[ 1 ];
else
{
- if ( /[l\u00B7\u2002]/.test( bulletStyle[ 1 ] ) ) //l·•
- bulletStyle = 'disc';
- else if ( /[\u006F\u00D8]/.test( bulletStyle[ 1 ] ) ) //oØ
- bulletStyle = 'circle';
- else if ( /[\u006E\u25C6]/.test( bulletStyle[ 1 ] ) ) //n◆
- bulletStyle = 'square';
- else
- bulletStyle = 'disc';
-
- listType = 'ul';
+ mergeStyle = null;
+ break;
}
+ }
+ }
- // Represent list type as CSS style.
- marker.attributes =
- {
- 'cke:listtype' : listType,
- 'style' : 'list-style-type:' + bulletStyle + ';'
- };
+ if ( mergeStyle )
+ {
+ for ( i = 0; i < count; i++ )
+ {
+ attrs = children[ i ].attributes;
+ attrs.style && ( attrs.style = stylesFilter( [ [ 'list-style-type'] ] )( attrs.style ) || '' );
+ }
+
+ list.addStyle( 'list-style-type', mergeStyle );
+ }
+ }
+
+ var cssLengthRelativeUnit = /^([.\d]*)+(em|ex|px|gd|rem|vw|vh|vm|ch|mm|cm|in|pt|pc|deg|rad|ms|s|hz|khz){1}?/i;
+ var emptyMarginRegex = /^(?:\b0[^\s]*\s*){1,4}$/; // e.g. 0px 0pt 0px
+ var romanLiternalPattern = '^m{0,4}(cm|cd|d?c{0,3})(xc|xl|l?x{0,3})(ix|iv|v?i{0,3})$',
+ lowerRomanLiteralRegex = new RegExp( romanLiternalPattern ),
+ upperRomanLiteralRegex = new RegExp( romanLiternalPattern.toUpperCase() );
+
+ var orderedPatterns = { 'decimal' : /\d+/, 'lower-roman': lowerRomanLiteralRegex, 'upper-roman': upperRomanLiteralRegex, 'lower-alpha' : /^[a-z]+$/, 'upper-alpha': /^[A-Z]+$/ },
+ unorderedPatterns = { 'disc' : /[l\u00B7\u2002]/, 'circle' : /[\u006F\u00D8]/,'square' : /[\u006E\u25C6]/},
+ listMarkerPatterns = { 'ol' : orderedPatterns, 'ul' : unorderedPatterns },
+ romans = [ [1000, 'M'], [900, 'CM'], [500, 'D'], [400, 'CD'], [100, 'C'], [90, 'XC'], [50, 'L'], [40, 'XL'], [10, 'X'], [9, 'IX'], [5, 'V'], [4, 'IV'], [1, 'I'] ],
+ alpahbets = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+ // Convert roman numbering back to decimal.
+ function fromRoman( str )
+ {
+ str = str.toUpperCase();
+ var l = romans.length, retVal = 0;
+ for ( var i = 0; i < l; ++i )
+ {
+ for ( var j = romans[i], k = j[1].length; str.substr( 0, k ) == j[1]; str = str.substr( k ) )
+ retVal += j[ 0 ];
+ }
+ return retVal;
+ }
+
+ // Convert alphabet numbering back to decimal.
+ function fromAlphabet( str )
+ {
+ str = str.toUpperCase();
+ var l = alpahbets.length, retVal = 1;
+ for ( var x = 1; str.length > 0; x *= l )
+ {
+ retVal += alpahbets.indexOf( str.charAt( str.length - 1 ) ) * x;
+ str = str.substr( 0, str.length - 1 );
+ }
+ return retVal;
+ }
+
+ var listBaseIndent = 0,
+ previousListItemMargin = null,
+ previousListId;
+
+ var plugin = ( CKEDITOR.plugins.pastefromword =
+ {
+ utils :
+ {
+ // Create a <cke:listbullet> which indicate an list item type.
+ createListBulletMarker : function ( bullet, bulletText )
+ {
+ var marker = new CKEDITOR.htmlParser.element( 'cke:listbullet' );
+ marker.attributes = { 'cke:listsymbol' : bullet[ 0 ] };
marker.add( new CKEDITOR.htmlParser.text( bulletText ) );
return marker;
},
@@ -200,73 +240,75 @@ For licensing, see LICENSE.html or http://ckeditor.com/license resolveList : function( element )
{
// <cke:listbullet> indicate a list item.
- var children = element.children,
- attrs = element.attributes,
+ var attrs = element.attributes,
listMarker;
if ( ( listMarker = element.removeAnyChildWithName( 'cke:listbullet' ) )
- && listMarker.length
- && ( listMarker = listMarker[ 0 ] ) )
+ && listMarker.length
+ && ( listMarker = listMarker[ 0 ] ) )
{
element.name = 'cke:li';
if ( attrs.style )
{
- attrs.style = CKEDITOR.plugins.pastefromword.filters.stylesFilter(
+ attrs.style = plugin.filters.stylesFilter(
[
// Text-indent is not representing list item level any more.
[ 'text-indent' ],
[ 'line-height' ],
- // Resolve indent level from 'margin-left' value.
+ // First attempt is to resolve indent level from on a constant margin increment.
[ ( /^margin(:?-left)?$/ ), null, function( margin )
{
- // Be able to deal with component/short-hand form style.
+ // Deal with component/short-hand form.
var values = margin.split( ' ' );
- margin = values[ 3 ] || values[ 1 ] || values [ 0 ];
- margin = parseInt( margin, 10 );
+ margin = CKEDITOR.tools.convertToPx( values[ 3 ] || values[ 1 ] || values [ 0 ] );
- // Figure out the indent unit by looking at the first increament.
- if ( !listBaseIndent && previousListItemMargin && margin > previousListItemMargin )
+ // Figure out the indent unit by checking the first time of incrementation.
+ if ( !listBaseIndent && previousListItemMargin !== null && margin > previousListItemMargin )
listBaseIndent = margin - previousListItemMargin;
- attrs[ 'cke:margin' ] = previousListItemMargin = margin;
+ previousListItemMargin = margin;
+
+ attrs[ 'cke:indent' ] = listBaseIndent && ( Math.ceil( margin / listBaseIndent ) + 1 ) || 1;
+ } ],
+ // The best situation: "mso-list:l0 level1 lfo2" tells the belonged list root, list item indentation, etc.
+ [ ( /^mso-list$/ ), null, function( val )
+ {
+ val = val.split( ' ' );
+ var listId = Number( val[ 0 ].match( /\d+/ ) ),
+ indent = Number( val[ 1 ].match( /\d+/ ) );
+
+ if ( indent == 1 )
+ {
+ listId !== previousListId && ( attrs[ 'cke:reset' ] = 1 );
+ previousListId = listId;
+ }
+ attrs[ 'cke:indent' ] = indent;
} ]
- ] )( attrs.style, element ) || '' ;
+ ] )( attrs.style, element ) || '';
}
- // Inherit list-type-style from bullet.
- var listBulletAttrs = listMarker.attributes,
- listBulletStyle = listBulletAttrs.style;
+ // First level list item might be presented without a margin.
+
+
+ // In case all above doesn't apply.
+ if ( !attrs[ 'cke:indent' ] )
+ {
+ previousListItemMargin = 0;
+ attrs[ 'cke:indent' ] = 1;
+ }
- element.addStyle( listBulletStyle );
- CKEDITOR.tools.extend( attrs, listBulletAttrs );
+ // Inherit attributes from bullet.
+ CKEDITOR.tools.extend( attrs, listMarker.attributes );
return true;
}
+ // Current list disconnected.
+ else
+ previousListId = previousListItemMargin = listBaseIndent = null;
return false;
},
- // Convert various length units to 'px' in ignorance of DPI.
- convertToPx : ( function ()
- {
- var calculator = CKEDITOR.dom.element.createFromHtml(
- '<div style="position:absolute;left:-9999px;' +
- 'top:-9999px;margin:0px;padding:0px;border:0px;"' +
- '></div>', CKEDITOR.document );
- CKEDITOR.document.getBody().append( calculator );
-
- return function( cssLength )
- {
- if ( cssLengthRelativeUnit.test( cssLength ) )
- {
- calculator.setStyle( 'width', cssLength );
- return calculator.$.clientWidth + 'px';
- }
-
- return cssLength;
- };
- } )(),
-
// Providing a shorthand style then retrieve one or more style component values.
getStyleComponents : ( function()
{
@@ -296,20 +338,12 @@ For licensing, see LICENSE.html or http://ckeditor.com/license // E.g. <ul><li>level1<ol><li>level2</li></ol></li> =>
// <cke:li cke:listtype="ul" cke:indent="1">level1</cke:li>
// <cke:li cke:listtype="ol" cke:indent="2">level2</cke:li>
- flattenList : function( element )
+ flattenList : function( element, level )
{
- var attrs = element.attributes,
- parent = element.parent;
-
- var listStyleType,
- indentLevel = 1;
+ level = typeof level == 'number' ? level : 1;
- // Resolve how many level nested.
- while ( parent )
- {
- parent.attributes && parent.attributes[ 'cke:list'] && indentLevel++;
- parent = parent.parent;
- }
+ var attrs = element.attributes,
+ listStyleType;
// All list items are of the same type.
switch ( attrs.type )
@@ -317,6 +351,9 @@ For licensing, see LICENSE.html or http://ckeditor.com/license case 'a' :
listStyleType = 'lower-alpha';
break;
+ case '1' :
+ listStyleType = 'decimal';
+ break;
// TODO: Support more list style type from MS-Word.
}
@@ -326,30 +363,58 @@ For licensing, see LICENSE.html or http://ckeditor.com/license for ( var i = 0; i < children.length; i++ )
{
child = children[ i ];
- var attributes = child.attributes;
if ( child.name in CKEDITOR.dtd.$listItem )
{
- var listItemChildren = child.children,
+ var attributes = child.attributes,
+ listItemChildren = child.children,
count = listItemChildren.length,
last = listItemChildren[ count - 1 ];
// Move out nested list.
if ( last.name in CKEDITOR.dtd.$list )
{
- children.splice( i + 1, 0, last );
- last.parent = element;
+ element.add( last, i + 1 );
// Remove the parent list item if it's just a holder.
if ( !--listItemChildren.length )
- children.splice( i, 1 );
+ children.splice( i--, 1 );
}
child.name = 'cke:li';
- attributes[ 'cke:indent' ] = indentLevel;
- previousListItemMargin = 0;
+
+ // Inherit numbering from list root on the first list item.
+ attrs.start && !i && ( attributes.value = attrs.start );
+
+ plugin.filters.stylesFilter(
+ [
+ [ 'tab-stops', null, function( val )
+ {
+ var margin = val.split( ' ' )[ 1 ].match( cssLengthRelativeUnit );
+ margin && ( previousListItemMargin = CKEDITOR.tools.convertToPx( margin[ 0 ] ) );
+ } ],
+ ( level == 1 ? [ 'mso-list', null, function( val )
+ {
+ val = val.split( ' ' );
+ var listId = Number( val[ 0 ].match( /\d+/ ) );
+ listId !== previousListId && ( attributes[ 'cke:reset' ] = 1 );
+ previousListId = listId;
+ } ] : null )
+ ] )( attributes.style );
+
+ attributes[ 'cke:indent' ] = level;
attributes[ 'cke:listtype' ] = element.name;
- listStyleType && child.addStyle( 'list-style-type', listStyleType, true );
+ attributes[ 'cke:list-style-type' ] = listStyleType;
+ }
+ // Flatten sub list.
+ else if ( child.name in CKEDITOR.dtd.$list )
+ {
+ // Absorb sub list children.
+ arguments.callee.apply( this, [ child, level + 1 ] );
+ children = children.slice( 0, i ).concat( child.children ).concat( children.slice( i + 1 ) );
+ element.children = [];
+ for ( var j = 0, num = children.length; j < num ; j++ )
+ element.add( children[ j ] );
}
}
@@ -369,11 +434,19 @@ For licensing, see LICENSE.html or http://ckeditor.com/license var children = element.children, child,
listItem, // The current processing cke:li element.
listItemAttrs,
- listType, // Determine the root type of the list.
listItemIndent, // Indent level of current list item.
+ lastIndent,
lastListItem, // The previous one just been added to the list.
- list, parentList, // Current staging list and it's parent list if any.
- indent;
+ list, // Current staging list and it's parent list if any.
+ openedLists = [],
+ previousListStyleType,
+ previousListType;
+
+ // Properties of the list item are to be resolved from the list bullet.
+ var bullet,
+ listType,
+ listStyleType,
+ itemNumeric;
for ( var i = 0; i < children.length; i++ )
{
@@ -384,43 +457,128 @@ For licensing, see LICENSE.html or http://ckeditor.com/license child.name = 'li';
listItem = child;
listItemAttrs = listItem.attributes;
- listType = listItem.attributes[ 'cke:listtype' ];
+ bullet = listItemAttrs[ 'cke:listsymbol' ];
+ bullet = bullet && bullet.match( /^(?:[(]?)([^\s]+?)([.)]?)$/ );
+ listType = listStyleType = itemNumeric = null;
+
+ if ( listItemAttrs[ 'cke:ignored' ] )
+ {
+ children.splice( i--, 1 );
+ continue;
+ }
+
+
+ // This's from a new list root.
+ listItemAttrs[ 'cke:reset' ] && ( list = lastIndent = lastListItem = null );
// List item indent level might come from a real list indentation or
// been resolved from a pseudo list item's margin value, even get
// no indentation at all.
- listItemIndent = parseInt( listItemAttrs[ 'cke:indent' ], 10 )
- || listBaseIndent && ( Math.ceil( listItemAttrs[ 'cke:margin' ] / listBaseIndent ) )
- || 1;
+ listItemIndent = Number( listItemAttrs[ 'cke:indent' ] );
- // Ignore the 'list-style-type' attribute if it's matched with
- // the list root element's default style type.
- listItemAttrs.style && ( listItemAttrs.style =
- CKEDITOR.plugins.pastefromword.filters.stylesFilter(
- [
- [ 'list-style-type', listType == 'ol' ? 'decimal' : 'disc' ]
- ] )( listItemAttrs.style )
- || '' );
+ // We're moving out of the current list, cleaning up.
+ if ( listItemIndent != lastIndent )
+ previousListType = previousListStyleType = null;
+
+ // List type and item style are already resolved.
+ if ( !bullet )
+ {
+ listType = listItemAttrs[ 'cke:listtype' ] || 'ol';
+ listStyleType = listItemAttrs[ 'cke:list-style-type' ];
+ }
+ else
+ {
+ // Probably share the same list style type with previous list item,
+ // give it priority to avoid ambiguous between C(Alpha) and C.(Roman).
+ if ( previousListType && listMarkerPatterns[ previousListType ] [ previousListStyleType ].test( bullet[ 1 ] ) )
+ {
+ listType = previousListType;
+ listStyleType = previousListStyleType;
+ }
+ else
+ {
+ for ( var type in listMarkerPatterns )
+ {
+ for ( var style in listMarkerPatterns[ type ] )
+ {
+ if ( listMarkerPatterns[ type ][ style ].test( bullet[ 1 ] ) )
+ {
+ // Small numbering has higher priority, when dealing with ambiguous
+ // between C(Alpha) and C.(Roman).
+ if ( type == 'ol' && ( /alpha|roman/ ).test( style ) )
+ {
+ var num = /roman/.test( style ) ? fromRoman( bullet[ 1 ] ) : fromAlphabet( bullet[ 1 ] );
+ if ( !itemNumeric || num < itemNumeric )
+ {
+ itemNumeric = num;
+ listType = type;
+ listStyleType = style;
+ }
+ }
+ else
+ {
+ listType = type;
+ listStyleType = style;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Simply use decimal/disc for the rest forms of unrepresentable
+ // numerals, e.g. Chinese..., but as long as there a second part
+ // included, it has a bigger chance of being a order list ;)
+ !listType && ( listType = bullet[ 2 ] ? 'ol' : 'ul' );
+ }
+
+ previousListType = listType;
+ previousListStyleType = listStyleType || ( listType == 'ol' ? 'decimal' : 'disc' );
+ if ( listStyleType && listStyleType != ( listType == 'ol' ? 'decimal' : 'disc' ) )
+ listItem.addStyle( 'list-style-type', listStyleType );
+
+ // Figure out start numbering.
+ if ( listType == 'ol' && bullet )
+ {
+ switch ( listStyleType )
+ {
+ case 'decimal' :
+ itemNumeric = Number( bullet[ 1 ] );
+ break;
+ case 'lower-roman':
+ case 'upper-roman':
+ itemNumeric = fromRoman( bullet[ 1 ] );
+ break;
+ case 'lower-alpha':
+ case 'upper-alpha':
+ itemNumeric = fromAlphabet( bullet[ 1 ] );
+ break;
+ }
+
+ // Always create the numbering, swipe out unnecessary ones later.
+ listItem.attributes.value = itemNumeric;
+ }
+ // Start the list construction.
if ( !list )
{
- list = new CKEDITOR.htmlParser.element( listType );
+ openedLists.push( list = new CKEDITOR.htmlParser.element( listType ) );
list.add( listItem );
children[ i ] = list;
}
else
{
- if ( listItemIndent > indent )
+ if ( listItemIndent > lastIndent )
{
- list = new CKEDITOR.htmlParser.element( listType );
+ openedLists.push( list = new CKEDITOR.htmlParser.element( listType ) );
list.add( listItem );
lastListItem.add( list );
}
- else if ( listItemIndent < indent )
+ else if ( listItemIndent < lastIndent )
{
// There might be a negative gap between two list levels. (#4944)
- var diff = indent - listItemIndent,
- parent;
+ var diff = lastIndent - listItemIndent,
+ parent;
while ( diff-- && ( parent = list.parent ) )
list = parent.parent;
@@ -433,13 +591,16 @@ For licensing, see LICENSE.html or http://ckeditor.com/license }
lastListItem = listItem;
- indent = listItemIndent;
+ lastIndent = listItemIndent;
}
- else
- list = null;
+ else if ( list )
+ list = lastIndent = lastListItem = null;
}
- listBaseIndent = 0;
+ for ( i = 0; i < openedLists.length; i++ )
+ postProcessList( openedLists[ i ] );
+
+ list = lastIndent = lastListItem = previousListId = previousListItemMargin = listBaseIndent = null;
},
/**
@@ -465,7 +626,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license // html-encoded quote might be introduced by 'font-family'
// from MS-Word which confused the following regexp. e.g.
//'font-family: "Lucida, Console"'
- styleText
+ ( styleText || '' )
.replace( /"/g, '"' )
.replace( /\s*([^ :;]+)\s*:\s*([^;]+)\s*(?=;|$)/g,
function( match, name, value )
@@ -589,14 +750,17 @@ For licensing, see LICENSE.html or http://ckeditor.com/license stylesFilter = filters.stylesFilter,
elementMigrateFilter = filters.elementMigrateFilter,
styleMigrateFilter = CKEDITOR.tools.bind( this.filters.styleMigrateFilter, this.filters ),
- bogusAttrFilter = filters.bogusAttrFilter,
createListBulletMarker = this.utils.createListBulletMarker,
flattenList = filters.flattenList,
assembleList = filters.assembleList,
isListBulletIndicator = this.utils.isListBulletIndicator,
containsNothingButSpaces = this.utils.isContainingOnlySpaces,
resolveListItem = this.utils.resolveList,
- convertToPx = this.utils.convertToPx,
+ convertToPx = function( value )
+ {
+ value = CKEDITOR.tools.convertToPx( value );
+ return isNaN( value ) ? value : value + 'px';
+ },
getStyleComponents = this.utils.getStyleComponents,
listDtdParents = this.utils.listDtdParents,
removeFontStyles = config.pasteFromWordRemoveFontStyles !== false,
@@ -754,11 +918,21 @@ For licensing, see LICENSE.html or http://ckeditor.com/license 'p' : function( element )
{
- element.filterChildren();
+ // This's a fall-back approach to recognize list item in FF3.6,
+ // as it's not perfect as not all list style (e.g. "heading list") is shipped
+ // with this pattern. (#6662)
+ if ( /MsoListParagraph/.exec( element.attributes[ 'class' ] ) )
+ {
+ var bulletText = element.firstChild( function( node )
+ {
+ return node.type == CKEDITOR.NODE_TEXT && !containsNothingButSpaces( node.parent );
+ });
+ var bullet = bulletText && bulletText.parent,
+ bulletAttrs = bullet && bullet.attributes;
+ bulletAttrs && !bulletAttrs.style && ( bulletAttrs.style = 'mso-list: Ignore;' );
+ }
- var attrs = element.attributes,
- parent = element.parent,
- children = element.children;
+ element.filterChildren();
// Is the paragraph actually a list item?
if ( resolveListItem( element ) )
@@ -810,8 +984,8 @@ For licensing, see LICENSE.html or http://ckeditor.com/license 'font' : function( element )
{
- // IE/Safari: drop the font tag if it comes from list bullet text.
- if ( !CKEDITOR.env.gecko && isListBulletIndicator( element.parent ) )
+ // Drop the font tag if it comes from list bullet text.
+ if ( isListBulletIndicator( element.parent ) )
{
delete element.name;
return;
@@ -829,7 +1003,6 @@ For licensing, see LICENSE.html or http://ckeditor.com/license element.attributes );
styleText && parent.addStyle( styleText );
delete element.name;
- return;
}
// Convert the merged into a span with all attributes preserved.
else
@@ -863,8 +1036,8 @@ For licensing, see LICENSE.html or http://ckeditor.com/license 'span' : function( element )
{
- // IE/Safari: remove the span if it comes from list bullet text.
- if ( !CKEDITOR.env.gecko && isListBulletIndicator( element.parent ) )
+ // Remove the span if it comes from list bullet text.
+ if ( isListBulletIndicator( element.parent ) )
return false;
element.filterChildren();
@@ -874,9 +1047,9 @@ For licensing, see LICENSE.html or http://ckeditor.com/license return null;
}
- // For IE/Safari: List item bullet type is supposed to be indicated by
+ // List item bullet type is supposed to be indicated by
// the text of a span with style 'mso-list : Ignore' or an image.
- if ( !CKEDITOR.env.gecko && isListBulletIndicator( element ) )
+ if ( isListBulletIndicator( element ) )
{
var listSymbolNode = element.firstChild( function( node )
{
@@ -884,8 +1057,18 @@ For licensing, see LICENSE.html or http://ckeditor.com/license });
var listSymbol = listSymbolNode && ( listSymbolNode.value || 'l.' ),
- listType = listSymbol.match( /^([^\s]+?)([.)]?)$/ );
- return createListBulletMarker( listType, listSymbol );
+ listType = listSymbol && listSymbol.match( /^(?:[(]?)([^\s]+?)([.)]?)$/ );
+
+ if ( listType )
+ {
+ var marker = createListBulletMarker( listType, listSymbol );
+ // Some non-existed list items might be carried by an inconsequential list, indicate by "mso-hide:all/display:none",
+ // those are to be removed later, now mark it with "cke:ignored".
+ var ancestor = element.getAncestor( 'span' );
+ if ( ancestor && (/ mso-hide:\s*all|display:\s*none /).test( ancestor.attributes.style ) )
+ marker.attributes[ 'cke:ignored' ] = 1;
+ return marker;
+ }
}
// Update the src attribute of image element with href.
@@ -926,12 +1109,14 @@ For licensing, see LICENSE.html or http://ckeditor.com/license var attrs = element.attributes;
if ( attrs && !attrs.href && attrs.name )
delete element.name;
+ else if ( CKEDITOR.env.webkit && attrs.href && attrs.href.match( /file:\/\/\/[\S]+#/i ) )
+ attrs.href = attrs.href.replace( /file:\/\/\/[^#]+/i,'' );
},
'cke:listbullet' : function( element )
{
if ( element.getAncestor( /h\d/ ) && !config.pasteFromWordNumberedHeadingToList )
delete element.name;
- }
+ }
},
attributeNames :
@@ -953,6 +1138,9 @@ For licensing, see LICENSE.html or http://ckeditor.com/license // Provide a white-list of styles that we preserve, those should
// be the ones that could later be altered with editor tools.
[
+ // Leave list-style-type
+ [ ( /^list-style-type$/ ), null ],
+
// Preserve margin-left/right which used as default indent style in the editor.
[ ( /^margin$|^margin-(?!bottom|top)/ ), null, function( value, element, name )
{
@@ -1059,7 +1247,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license {
// Bullet symbol could be either text or an image.
var listSymbol = listInfo[ 1 ] || ( imageInfo && 'l.' ),
- listType = listSymbol && listSymbol.match( />([^\s]+?)([.)]?)</ );
+ listType = listSymbol && listSymbol.match( />(?:[(]?)([^\s]+?)([.)]?)</ );
return createListBulletMarker( listType, listSymbol );
}
@@ -1082,7 +1270,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license : falsyFilter
};
}
- };
+ });
// The paste processor here is just a reduced copy of html data processor.
var pasteProcessor = function()
diff --git a/_source/plugins/pastefromword/plugin.js b/_source/plugins/pastefromword/plugin.js index 8057745..5686c30 100644 --- a/_source/plugins/pastefromword/plugin.js +++ b/_source/plugins/pastefromword/plugin.js @@ -1,122 +1,141 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ -(function() -{ - CKEDITOR.plugins.add( 'pastefromword', - { - init : function( editor ) - { - - // Flag indicate this command is actually been asked instead of a generic - // pasting. - var forceFromWord = 0; - var resetFromWord = function() - { - setTimeout( function() { forceFromWord = 0; }, 0 ); - }; - - // Features bring by this command beside the normal process: - // 1. No more bothering of user about the clean-up. - // 2. Perform the clean-up even if content is not from MS-Word. - // (e.g. from a MS-Word similar application.) - editor.addCommand( 'pastefromword', - { - canUndo : false, - exec : function() - { - forceFromWord = 1; - if ( editor.execCommand( 'paste' ) === false ) - { - editor.on( 'dialogHide', function ( evt ) - { - evt.removeListener(); - resetFromWord(); - }); - } - else - resetFromWord(); - } - }); - - // Register the toolbar button. - editor.ui.addButton( 'PasteFromWord', - { - label : editor.lang.pastefromword.toolbar, - command : 'pastefromword' - }); - - editor.on( 'paste', function( evt ) - { - var data = evt.data, - mswordHtml; - - // MS-WORD format sniffing. - if ( ( mswordHtml = data[ 'html' ] ) - && ( forceFromWord || ( /(class=\"?Mso|style=\"[^\"]*\bmso\-|w:WordDocument)/ ).test( mswordHtml ) ) ) - { - var isLazyLoad = this.loadFilterRules( function() - { - // Event continuation with the original data. - if ( isLazyLoad ) - editor.fire( 'paste', data ); - else if ( !editor.config.pasteFromWordPromptCleanup - || ( forceFromWord || confirm( editor.lang.pastefromword.confirmCleanup ) ) ) - { - data[ 'html' ] = CKEDITOR.cleanWord( mswordHtml, editor ); - } - }); - - // The cleanup rules are to be loaded, we should just cancel - // this event. - isLazyLoad && evt.cancel(); - } - }, this ); - }, - - loadFilterRules : function( callback ) - { - - var isLoaded = CKEDITOR.cleanWord; - - if ( isLoaded ) - callback(); - else - { - var filterFilePath = CKEDITOR.getUrl( - CKEDITOR.config.pasteFromWordCleanupFile - || ( this.path + 'filter/default.js' ) ); - - // Load with busy indicator. - CKEDITOR.scriptLoader.load( filterFilePath, callback, null, false, true ); - } - - return !isLoaded; - } - }); -})(); - -/** - * Whether to prompt the user about the clean up of content being pasted from - * MS Word. - * @name CKEDITOR.config.pasteFromWordPromptCleanup - * @since 3.1 - * @type Boolean - * @default undefined - * @example - * config.pasteFromWordPromptCleanup = true; - */ - -/** - * The file that provides the MS Word cleanup function for pasting operations. - * Note: This is a global configuration shared by all editor instances present - * in the page. - * @name CKEDITOR.config.pasteFromWordCleanupFile - * @since 3.1 - * @type String - * @default 'default' - * @example - * // Load from 'pastefromword' plugin 'filter' sub folder (custom.js file). - * CKEDITOR.config.pasteFromWordCleanupFile = 'custom'; - */ +/*
+Copyright (c) 2003-2012, 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';
+ */
diff --git a/_source/plugins/pastetext/dialogs/pastetext.js b/_source/plugins/pastetext/dialogs/pastetext.js index 5ed557f..0256837 100644 --- a/_source/plugins/pastetext/dialogs/pastetext.js +++ b/_source/plugins/pastetext/dialogs/pastetext.js @@ -1,78 +1,67 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -(function() -{ - CKEDITOR.dialog.add( 'pastetext', function( editor ) - { - return { - title : editor.lang.pasteText.title, - - minWidth : CKEDITOR.env.ie && CKEDITOR.env.quirks ? 368 : 350, - minHeight : 240, - - onShow : function() - { - // Reset the textarea value. - this.getContentElement( 'general', 'content' ).getInputElement().setValue( '' ); - }, - - onOk : function() - { - // Get the textarea value. - var text = this.getContentElement( 'general', 'content' ).getInputElement().getValue(), - editor = this.getParentEditor(); - - setTimeout( function() - { - editor.fire( 'paste', { 'text' : text } ); - }, 0 ); - }, - - contents : - [ - { - label : editor.lang.common.generalTab, - id : 'general', - elements : - [ - { - type : 'html', - id : 'pasteMsg', - html : '<div style="white-space:normal;width:340px;">' + editor.lang.clipboard.pasteMsg + '</div>' - }, - { - type : 'html', - id : 'content', - style : 'width:340px;height:170px', - html : - '<textarea style="' + - 'width:346px;' + - 'height:170px;' + - 'resize: none;' + - 'direction:' + editor.config.contentsLangDirection + ';' + - 'border:1px solid black;' + - 'background-color:white">' + - '</textarea>', - - onLoad : function() - { - var label = this.getDialog().getContentElement( 'general', 'pasteMsg' ).getElement(), - input = this.getElement(); - - input.setAttribute( 'aria-labelledby', label.$.id ); - }, - - focus : function() - { - this.getElement().focus(); - } - } - ] - } - ] - }; - }); -})(); +/*
+Copyright (c) 2003-2012, 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 );
+ }
+ }
+ ]
+ }
+ ]
+ };
+ });
+})();
diff --git a/_source/plugins/pastetext/plugin.js b/_source/plugins/pastetext/plugin.js index 0d9cd9d..33eb8cd 100644 --- a/_source/plugins/pastetext/plugin.js +++ b/_source/plugins/pastetext/plugin.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
@@ -21,23 +21,6 @@ For licensing, see LICENSE.html or http://ckeditor.com/license if ( !clipboardText )
throw 0;
return clipboardText;
- },
- function()
- {
- window.netscape.security.PrivilegeManager.enablePrivilege( "UniversalXPConnect" );
-
- var clip = window.Components.classes[ "@mozilla.org/widget/clipboard;1" ]
- .getService( window.Components.interfaces.nsIClipboard );
- var trans = window.Components.classes[ "@mozilla.org/widget/transferable;1" ]
- .createInstance( window.Components.interfaces.nsITransferable );
- trans.addDataFlavor( "text/unicode" );
- clip.getData( trans, clip.kGlobalClipboard );
-
- var str = {}, strLength = {}, clipboardText;
- trans.getTransferData( "text/unicode", str, strLength );
- str = str.value.QueryInterface( window.Components.interfaces.nsISupportsString );
- clipboardText = str.data.substring( 0, strLength.value / 2 );
- return clipboardText;
}
// Any other approach that's working...
);
@@ -54,20 +37,6 @@ For licensing, see LICENSE.html or http://ckeditor.com/license }
};
- function doInsertText( doc, text )
- {
- // Native text insertion.
- if ( CKEDITOR.env.ie )
- {
- var selection = doc.selection;
- if ( selection.type == 'Control' )
- selection.clear();
- selection.createRange().pasteHTML( text );
- }
- else
- doc.execCommand( 'inserthtml', false, text );
- }
-
// Register the plugin.
CKEDITOR.plugins.add( 'pastetext',
{
@@ -89,64 +58,30 @@ For licensing, see LICENSE.html or http://ckeditor.com/license // Intercept the default pasting process.
editor.on( 'beforeCommandExec', function ( evt )
{
- if ( evt.data.name == 'paste' )
+ 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' ]
});
- function doEnter( editor, mode, times, forceMode )
- {
- while ( times-- )
- {
- CKEDITOR.plugins.enterkey[ mode == CKEDITOR.ENTER_BR ? 'enterBr' : 'enterBlock' ]
- ( editor, mode, null, forceMode );
- }
- }
-
- CKEDITOR.editor.prototype.insertText = function( text )
- {
- this.focus();
- this.fire( 'saveSnapshot' );
-
- var mode = this.getSelection().getStartElement().hasAscendant( 'pre', true ) ? CKEDITOR.ENTER_BR : this.config.enterMode,
- isEnterBrMode = mode == CKEDITOR.ENTER_BR,
- doc = this.document.$,
- self = this,
- line;
-
- text = CKEDITOR.tools.htmlEncode( text.replace( /\r\n|\r/g, '\n' ) );
-
- var startIndex = 0;
- text.replace( /\n+/g, function( match, lastIndex )
- {
- line = text.substring( startIndex, lastIndex );
- startIndex = lastIndex + match.length;
- line.length && doInsertText( doc, line );
-
- var lineBreakNums = match.length,
- // Duo consequence line-break as a enter block.
- enterBlockTimes = isEnterBrMode ? 0 : Math.floor( lineBreakNums / 2 ),
- // Per link-break as a enter br.
- enterBrTimes = isEnterBrMode ? lineBreakNums : lineBreakNums % 2;
-
- // Line-breaks are converted to editor enter key strokes.
- doEnter( self, mode, enterBlockTimes );
- doEnter( self, CKEDITOR.ENTER_BR, enterBrTimes, isEnterBrMode ? false : true );
- });
-
- // Insert the last text line of text.
- line = text.substring( startIndex, text.length );
- line.length && doInsertText( doc, line );
-
- this.fire( 'saveSnapshot' );
- };
})();
@@ -154,6 +89,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license * 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
diff --git a/_source/plugins/placeholder/dialogs/placeholder.js b/_source/plugins/placeholder/dialogs/placeholder.js new file mode 100644 index 0000000..94c8c9e --- /dev/null +++ b/_source/plugins/placeholder/dialogs/placeholder.js @@ -0,0 +1,71 @@ +/*
+ * Copyright (c) 2003-2012, 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 );
+ });
+} )();
diff --git a/_source/plugins/placeholder/lang/_translationstatus.txt b/_source/plugins/placeholder/lang/_translationstatus.txt new file mode 100644 index 0000000..a72d432 --- /dev/null +++ b/_source/plugins/placeholder/lang/_translationstatus.txt @@ -0,0 +1,26 @@ +Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+
+bg.js Found: 5 Missing: 0
+cs.js Found: 5 Missing: 0
+cy.js Found: 5 Missing: 0
+da.js Found: 5 Missing: 0
+de.js Found: 5 Missing: 0
+el.js Found: 5 Missing: 0
+eo.js Found: 5 Missing: 0
+et.js Found: 5 Missing: 0
+fa.js Found: 5 Missing: 0
+fi.js Found: 5 Missing: 0
+fr.js Found: 5 Missing: 0
+he.js Found: 5 Missing: 0
+hr.js Found: 5 Missing: 0
+it.js Found: 5 Missing: 0
+nb.js Found: 5 Missing: 0
+nl.js Found: 5 Missing: 0
+no.js Found: 5 Missing: 0
+pl.js Found: 5 Missing: 0
+tr.js Found: 5 Missing: 0
+ug.js Found: 5 Missing: 0
+uk.js Found: 5 Missing: 0
+vi.js Found: 3 Missing: 2
+zh-cn.js Found: 5 Missing: 0
diff --git a/_source/plugins/placeholder/lang/bg.js b/_source/plugins/placeholder/lang/bg.js new file mode 100644 index 0000000..96a088a --- /dev/null +++ b/_source/plugins/placeholder/lang/bg.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'placeholder', 'bg',
+{
+ placeholder :
+ {
+ title : 'Настройки на контейнера',
+ toolbar : 'Нов контейнер',
+ text : 'Текст за контейнера',
+ edit : 'Промяна на контейнер',
+ textMissing : 'Контейнера трябва да съдържа текст.'
+ }
+});
diff --git a/_source/plugins/placeholder/lang/cs.js b/_source/plugins/placeholder/lang/cs.js new file mode 100644 index 0000000..afa3537 --- /dev/null +++ b/_source/plugins/placeholder/lang/cs.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'placeholder', 'cs',
+{
+ placeholder :
+ {
+ title : 'Vlastnosti vyhrazeného prostoru',
+ toolbar : 'Vytvořit vyhrazený prostor',
+ text : 'Vyhrazený text',
+ edit : 'Upravit vyhrazený prostor',
+ textMissing : 'Vyhrazený prostor musí obsahovat text.'
+ }
+});
diff --git a/_source/plugins/placeholder/lang/cy.js b/_source/plugins/placeholder/lang/cy.js new file mode 100644 index 0000000..1ac1e03 --- /dev/null +++ b/_source/plugins/placeholder/lang/cy.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'placeholder', 'cy',
+{
+ placeholder :
+ {
+ title : 'Priodweddau\'r Daliwr Geiriau',
+ toolbar : 'Creu Daliwr Geiriau',
+ text : 'Testun y Daliwr Geiriau',
+ edit : 'Golygu\'r Dailwr Geiriau',
+ textMissing : 'Mae\'n rhaid i\'r daliwr geiriau gynnwys testun.'
+ }
+});
diff --git a/_source/plugins/placeholder/lang/da.js b/_source/plugins/placeholder/lang/da.js new file mode 100644 index 0000000..ad31c3f --- /dev/null +++ b/_source/plugins/placeholder/lang/da.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'placeholder', 'da',
+{
+ placeholder :
+ {
+ title : 'Egenskaber for pladsholder',
+ toolbar : 'Opret pladsholder',
+ text : 'Tekst til pladsholder',
+ edit : 'Rediger pladsholder',
+ textMissing : 'Pladsholder skal indeholde tekst'
+ }
+});
diff --git a/_source/plugins/placeholder/lang/de.js b/_source/plugins/placeholder/lang/de.js new file mode 100644 index 0000000..4d91ba4 --- /dev/null +++ b/_source/plugins/placeholder/lang/de.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'placeholder', 'de',
+{
+ placeholder :
+ {
+ title : 'Platzhalter Einstellungen',
+ toolbar : 'Platzhalter erstellen',
+ text : 'Platzhalter Text',
+ edit : 'Platzhalter bearbeiten',
+ textMissing : 'Der Platzhalter muss einen Text beinhalten.'
+ }
+});
diff --git a/_source/plugins/placeholder/lang/el.js b/_source/plugins/placeholder/lang/el.js new file mode 100644 index 0000000..315674b --- /dev/null +++ b/_source/plugins/placeholder/lang/el.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'placeholder', 'el',
+{
+ placeholder :
+ {
+ title : 'Ιδιότητες Υποκατάστατου Κειμένου',
+ toolbar : 'Δημιουργία Υποκατάσταστου Κειμένου',
+ text : 'Υποκαθιστόμενο Κείμενο',
+ edit : 'Επεξεργασία Υποκατάσταστου Κειμένου',
+ textMissing : 'Πρέπει να υπάρχει υποκαθιστόμενο κείμενο.'
+ }
+});
diff --git a/_source/plugins/placeholder/lang/en.js b/_source/plugins/placeholder/lang/en.js new file mode 100644 index 0000000..060850c --- /dev/null +++ b/_source/plugins/placeholder/lang/en.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, 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.'
+ }
+});
diff --git a/_source/plugins/placeholder/lang/eo.js b/_source/plugins/placeholder/lang/eo.js new file mode 100644 index 0000000..1cafa60 --- /dev/null +++ b/_source/plugins/placeholder/lang/eo.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'placeholder', 'eo',
+{
+ placeholder :
+ {
+ title : 'Atributoj de la rezervita spaco',
+ toolbar : 'Krei la rezervitan spacon',
+ text : 'Texto de la rezervita spaco',
+ edit : 'Modifi la rezervitan spacon',
+ textMissing : 'La rezervita spaco devas enteni tekston.'
+ }
+});
diff --git a/_source/plugins/placeholder/lang/et.js b/_source/plugins/placeholder/lang/et.js new file mode 100644 index 0000000..1b7dd98 --- /dev/null +++ b/_source/plugins/placeholder/lang/et.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'placeholder', 'et',
+{
+ placeholder :
+ {
+ title : 'Kohahoidja omadused',
+ toolbar : 'Kohahoidja loomine',
+ text : 'Kohahoidja tekst',
+ edit : 'Kohahoidja muutmine',
+ textMissing : 'Kohahoidja peab sisaldama teksti.'
+ }
+});
diff --git a/_source/plugins/placeholder/lang/fa.js b/_source/plugins/placeholder/lang/fa.js new file mode 100644 index 0000000..6154598 --- /dev/null +++ b/_source/plugins/placeholder/lang/fa.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'placeholder', 'fa',
+{
+ placeholder :
+ {
+ title : 'ویژگیهای محل نگهداری',
+ toolbar : 'ایجاد یک محل نگهداری',
+ text : 'متن محل نگهداری',
+ edit : 'ویرایش محل نگهداری',
+ textMissing : 'محل نگهداری باید محتوی متن باشد.'
+ }
+});
diff --git a/_source/plugins/placeholder/lang/fi.js b/_source/plugins/placeholder/lang/fi.js new file mode 100644 index 0000000..598cfca --- /dev/null +++ b/_source/plugins/placeholder/lang/fi.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'placeholder', 'fi',
+{
+ placeholder :
+ {
+ title : 'Paikkamerkin ominaisuudet',
+ toolbar : 'Luo paikkamerkki',
+ text : 'Paikkamerkin teksti',
+ edit : 'Muokkaa paikkamerkkiä',
+ textMissing : 'Paikkamerkin täytyy sisältää tekstiä'
+ }
+});
diff --git a/_source/plugins/placeholder/lang/fr.js b/_source/plugins/placeholder/lang/fr.js new file mode 100644 index 0000000..b887619 --- /dev/null +++ b/_source/plugins/placeholder/lang/fr.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'placeholder', 'fr',
+{
+ placeholder :
+ {
+ title : 'Propriétés de l\'Espace réservé',
+ toolbar : 'Créer l\'Espace réservé',
+ text : 'Texte de l\'Espace réservé',
+ edit : 'Modifier l\'Espace réservé',
+ textMissing : 'L\'Espace réservé doit contenir du texte.'
+ }
+});
diff --git a/_source/plugins/placeholder/lang/he.js b/_source/plugins/placeholder/lang/he.js new file mode 100644 index 0000000..5dfc09f --- /dev/null +++ b/_source/plugins/placeholder/lang/he.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, 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 : 'שומר המקום חייב להכיל טקסט.'
+ }
+});
diff --git a/_source/plugins/placeholder/lang/hr.js b/_source/plugins/placeholder/lang/hr.js new file mode 100644 index 0000000..7156ab3 --- /dev/null +++ b/_source/plugins/placeholder/lang/hr.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'placeholder', 'hr',
+{
+ placeholder :
+ {
+ title : 'Svojstva rezerviranog mjesta',
+ toolbar : 'Napravi rezervirano mjesto',
+ text : 'Tekst rezerviranog mjesta',
+ edit : 'Uredi rezervirano mjesto',
+ textMissing : 'Rezervirano mjesto mora sadržavati tekst.'
+ }
+});
diff --git a/_source/plugins/placeholder/lang/it.js b/_source/plugins/placeholder/lang/it.js new file mode 100644 index 0000000..920657a --- /dev/null +++ b/_source/plugins/placeholder/lang/it.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'placeholder', 'it',
+{
+ placeholder :
+ {
+ title : 'Proprietà segnaposto',
+ toolbar : 'Crea segnaposto',
+ text : 'Testo segnaposto',
+ edit : 'Modifica segnaposto',
+ textMissing : 'Il segnaposto deve contenere del testo.'
+ }
+});
diff --git a/_source/plugins/placeholder/lang/nb.js b/_source/plugins/placeholder/lang/nb.js new file mode 100644 index 0000000..d891784 --- /dev/null +++ b/_source/plugins/placeholder/lang/nb.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'placeholder', 'nb',
+{
+ placeholder :
+ {
+ title : 'Egenskaper for plassholder',
+ toolbar : 'Opprett plassholder',
+ text : 'Tekst for plassholder',
+ edit : 'Rediger plassholder',
+ textMissing : 'Plassholderen må inneholde tekst.'
+ }
+});
diff --git a/_source/plugins/placeholder/lang/nl.js b/_source/plugins/placeholder/lang/nl.js new file mode 100644 index 0000000..558504a --- /dev/null +++ b/_source/plugins/placeholder/lang/nl.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'placeholder', 'nl',
+{
+ placeholder :
+ {
+ title : 'Eigenschappen placeholder',
+ toolbar : 'Placeholder aanmaken',
+ text : 'Placeholder tekst',
+ edit : 'Placeholder wijzigen',
+ textMissing : 'De placeholder moet tekst bevatten.'
+ }
+});
diff --git a/_source/plugins/placeholder/lang/no.js b/_source/plugins/placeholder/lang/no.js new file mode 100644 index 0000000..f258ee3 --- /dev/null +++ b/_source/plugins/placeholder/lang/no.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'placeholder', 'no',
+{
+ placeholder :
+ {
+ title : 'Egenskaper for plassholder',
+ toolbar : 'Opprett plassholder',
+ text : 'Tekst for plassholder',
+ edit : 'Rediger plassholder',
+ textMissing : 'Plassholderen må inneholde tekst.'
+ }
+});
diff --git a/_source/plugins/placeholder/lang/pl.js b/_source/plugins/placeholder/lang/pl.js new file mode 100644 index 0000000..94c5390 --- /dev/null +++ b/_source/plugins/placeholder/lang/pl.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'placeholder', 'pl',
+{
+ placeholder :
+ {
+ title : 'Właściwości wypełniacza',
+ toolbar : 'Utwórz wypełniacz',
+ text : 'Tekst wypełnienia',
+ edit : 'Edytuj wypełnienie',
+ textMissing : 'Wypełnienie musi posiadać jakiś tekst.'
+ }
+});
diff --git a/_source/plugins/placeholder/lang/tr.js b/_source/plugins/placeholder/lang/tr.js new file mode 100644 index 0000000..117da13 --- /dev/null +++ b/_source/plugins/placeholder/lang/tr.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'placeholder', 'tr',
+{
+ placeholder :
+ {
+ title : 'Yer tutucu özellikleri',
+ toolbar : 'Yer tutucu oluşturun',
+ text : 'Yer tutucu metini',
+ edit : 'Yer tutucuyu düzenle',
+ textMissing : 'Yer tutucu metin içermelidir.'
+ }
+});
diff --git a/_source/plugins/placeholder/lang/ug.js b/_source/plugins/placeholder/lang/ug.js new file mode 100644 index 0000000..fb6e8a4 --- /dev/null +++ b/_source/plugins/placeholder/lang/ug.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'placeholder', 'ug',
+{
+ placeholder :
+ {
+ title : 'ئورۇن بەلگە خاسلىقى',
+ toolbar : 'ئورۇن بەلگە قۇر',
+ text : 'ئورۇن بەلگە تېكىستى',
+ edit : 'ئورۇن بەلگە تەھرىر',
+ textMissing : 'ئورۇن بەلگىسىدە چوقۇم تېكىست بولۇشى لازىم'
+ }
+});
diff --git a/_source/plugins/placeholder/lang/uk.js b/_source/plugins/placeholder/lang/uk.js new file mode 100644 index 0000000..c7620f7 --- /dev/null +++ b/_source/plugins/placeholder/lang/uk.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'placeholder', 'uk',
+{
+ placeholder :
+ {
+ title : 'Налаштування Заповнювача',
+ toolbar : 'Створити Заповнювач',
+ text : 'Текст Заповнювача',
+ edit : 'Редагувати Заповнювач',
+ textMissing : 'Заповнювач повинен містити текст.'
+ }
+});
diff --git a/_source/plugins/placeholder/lang/vi.js b/_source/plugins/placeholder/lang/vi.js new file mode 100644 index 0000000..bd3175f --- /dev/null +++ b/_source/plugins/placeholder/lang/vi.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'placeholder', 'vi',
+{
+ placeholder :
+ {
+ title : 'Thuộc tính đặt chỗ',
+ toolbar : 'Tạo đặt chỗ',
+ text : 'Văn bản đặt chỗ',
+ edit : 'Edit Placeholder', // MISSING
+ textMissing : 'The placeholder must contain text.' // MISSING
+ }
+});
diff --git a/_source/plugins/placeholder/lang/zh-cn.js b/_source/plugins/placeholder/lang/zh-cn.js new file mode 100644 index 0000000..adf7219 --- /dev/null +++ b/_source/plugins/placeholder/lang/zh-cn.js @@ -0,0 +1,16 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'placeholder', 'zh-cn',
+{
+ placeholder :
+ {
+ title : '占位符属性',
+ toolbar : '创建占位符',
+ text : '占位符文字',
+ edit : '编辑占位符',
+ textMissing : '占位符必需包含有文字'
+ }
+});
diff --git a/_source/plugins/placeholder/placeholder.gif b/_source/plugins/placeholder/placeholder.gif Binary files differnew file mode 100644 index 0000000..c07078c --- /dev/null +++ b/_source/plugins/placeholder/placeholder.gif diff --git a/_source/plugins/placeholder/plugin.js b/_source/plugins/placeholder/plugin.js new file mode 100644 index 0000000..1830741 --- /dev/null +++ b/_source/plugins/placeholder/plugin.js @@ -0,0 +1,171 @@ +/*
+Copyright (c) 2003-2012, 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 : [ 'bg', 'cs', 'cy', 'da', 'de', 'el', 'en', 'eo', 'et', 'fa', 'fi', 'fr', 'he', 'hr', 'it', 'nb', 'nl', 'no', 'pl', 'tr', 'ug', 'uk', 'vi', 'zh-cn' ],
+ 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;
+ }
+};
diff --git a/_source/plugins/popup/plugin.js b/_source/plugins/popup/plugin.js index 7fc400a..bd59fe2 100644 --- a/_source/plugins/popup/plugin.js +++ b/_source/plugins/popup/plugin.js @@ -1,9 +1,9 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
-CKEDITOR.plugins.add( 'popup');
+CKEDITOR.plugins.add( 'popup' );
CKEDITOR.tools.extend( CKEDITOR.editor.prototype,
{
@@ -13,8 +13,9 @@ CKEDITOR.tools.extend( CKEDITOR.editor.prototype, * @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 )
+ popup : function( url, width, height, options )
{
width = width || '80%';
height = height || '70%';
@@ -32,8 +33,9 @@ CKEDITOR.tools.extend( CKEDITOR.editor.prototype, height = 420;
var top = parseInt( ( window.screen.height - height ) / 2, 10 ),
- left = parseInt( ( window.screen.width - width ) / 2, 10 ),
- options = 'location=no,menubar=no,toolbar=no,dependent=yes,minimizable=no,modal=yes,alwaysRaised=yes,resizable=yes' +
+ 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 +
@@ -47,16 +49,21 @@ CKEDITOR.tools.extend( CKEDITOR.editor.prototype, try
{
- popupWindow.moveTo( left, top );
- popupWindow.resizeTo( width, height );
+ // Chrome 18 is problematic, but it's not really needed here (#8855).
+ var ua = navigator.userAgent.toLowerCase();
+ if ( ua.indexOf( ' chrome/18' ) == -1 )
+ {
+ popupWindow.moveTo( left, top );
+ popupWindow.resizeTo( width, height );
+ }
popupWindow.focus();
popupWindow.location.href = url;
}
- catch (e)
+ catch ( e )
{
popupWindow = window.open( url, null, options, true );
}
- return true ;
+ return true;
}
});
diff --git a/_source/plugins/preview/plugin.js b/_source/plugins/preview/plugin.js index dd53d43..fd6e633 100644 --- a/_source/plugins/preview/plugin.js +++ b/_source/plugins/preview/plugin.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
@@ -13,6 +13,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license {
modes : { wysiwyg:1, source:1 },
canUndo : false,
+ readOnly : 1,
exec : function( editor )
{
var sHTML,
@@ -24,7 +25,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license {
sHTML = editor.getData()
.replace( /<head>/, '$&' + baseTag )
- .replace( /[^>]*(?=<\/title>)/, editor.lang.preview );
+ .replace( /[^>]*(?=<\/title>)/, '$& — ' + editor.lang.preview );
}
else
{
@@ -83,9 +84,13 @@ For licensing, see LICENSE.html or http://ckeditor.com/license if ( !isCustomDomain )
{
- oWindow.document.open();
- oWindow.document.write( sHTML );
- oWindow.document.close();
+ 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 );
}
}
};
diff --git a/_source/plugins/print/plugin.js b/_source/plugins/print/plugin.js index 8145093..5f27770 100644 --- a/_source/plugins/print/plugin.js +++ b/_source/plugins/print/plugin.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
@@ -37,5 +37,6 @@ CKEDITOR.plugins.print = editor.document.$.execCommand( "Print" );
},
canUndo : false,
+ readOnly : 1,
modes : { wysiwyg : !( CKEDITOR.env.opera ) } // It is imposible to print the inner document in Opera.
};
diff --git a/_source/plugins/removeformat/plugin.js b/_source/plugins/removeformat/plugin.js index fe49f50..d031a65 100644 --- a/_source/plugins/removeformat/plugin.js +++ b/_source/plugins/removeformat/plugin.js @@ -1,172 +1,185 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -CKEDITOR.plugins.add( 'removeformat', -{ - requires : [ 'selection' ], - - init : function( editor ) - { - editor.addCommand( 'removeFormat', CKEDITOR.plugins.removeformat.commands.removeformat ); - editor.ui.addButton( 'RemoveFormat', - { - label : editor.lang.removeFormat, - command : 'removeFormat' - }); - - editor._.removeFormat = { filters: [] }; - } -}); - -CKEDITOR.plugins.removeformat = -{ - commands : - { - removeformat : - { - exec : function( editor ) - { - var tagsRegex = editor._.removeFormatRegex || - ( editor._.removeFormatRegex = new RegExp( '^(?:' + editor.config.removeFormatTags.replace( /,/g,'|' ) + ')$', 'i' ) ); - - var removeAttributes = editor._.removeAttributes || - ( editor._.removeAttributes = editor.config.removeFormatAttributes.split( ',' ) ); - - var filter = CKEDITOR.plugins.removeformat.filter; - var ranges = editor.getSelection().getRanges(); - - for ( var i = 0, range ; range = ranges[ i ] ; i++ ) - { - if ( range.collapsed ) - continue; - - range.enlarge( CKEDITOR.ENLARGE_ELEMENT ); - - // Bookmark the range so we can re-select it after processing. - var bookmark = range.createBookmark(); - - // The style will be applied within the bookmark boundaries. - var startNode = bookmark.startNode; - var endNode = bookmark.endNode; - - // We need to check the selection boundaries (bookmark spans) to break - // the code in a way that we can properly remove partially selected nodes. - // For example, removing a <b> style from - // <b>This is [some text</b> to show <b>the] problem</b> - // ... where [ and ] represent the selection, must result: - // <b>This is </b>[some text to show the]<b> problem</b> - // The strategy is simple, we just break the partial nodes before the - // removal logic, having something that could be represented this way: - // <b>This is </b>[<b>some text</b> to show <b>the</b>]<b> problem</b> - - var breakParent = function( node ) - { - // Let's start checking the start boundary. - var path = new CKEDITOR.dom.elementPath( node ); - var pathElements = path.elements; - - for ( var i = 1, pathElement ; pathElement = pathElements[ i ] ; i++ ) - { - if ( pathElement.equals( path.block ) || pathElement.equals( path.blockLimit ) ) - break; - - // If this element can be removed (even partially). - if ( tagsRegex.test( pathElement.getName() ) && filter( editor, pathElement ) ) - node.breakParent( pathElement ); - } - }; - - breakParent( startNode ); - breakParent( endNode ); - - // Navigate through all nodes between the bookmarks. - var currentNode = startNode.getNextSourceNode( true, CKEDITOR.NODE_ELEMENT ); - - while ( currentNode ) - { - // If we have reached the end of the selection, stop looping. - if ( currentNode.equals( endNode ) ) - break; - - // Cache the next node to be processed. Do it now, because - // currentNode may be removed. - var nextNode = currentNode.getNextSourceNode( false, CKEDITOR.NODE_ELEMENT ); - - // This node must not be a fake element. - if ( !( currentNode.getName() == 'img' - && currentNode.getAttribute( '_cke_realelement' ) ) - && filter( editor, currentNode ) ) - { - // Remove elements nodes that match with this style rules. - if ( tagsRegex.test( currentNode.getName() ) ) - currentNode.remove( true ); - else - currentNode.removeAttributes( removeAttributes ); - } - - 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'; +/*
+Copyright (c) 2003-2012, 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.
+ */
diff --git a/_source/plugins/resize/plugin.js b/_source/plugins/resize/plugin.js index 65ce9db..3fbec77 100644 --- a/_source/plugins/resize/plugin.js +++ b/_source/plugins/resize/plugin.js @@ -1,146 +1,169 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -CKEDITOR.plugins.add( 'resize', -{ - init : function( editor ) - { - var config = editor.config; - - if ( config.resize_enabled ) - { - var container = null, - origin, - startSize, - resizeHorizontal = ( config.resize_dir == 'both' || config.resize_dir == 'horizontal' ) && - ( config.resize_minWidth != config.resize_maxWidth ), - resizeVertical = ( config.resize_dir == 'both' || config.resize_dir == 'vertical' ) && - ( config.resize_minHeight != config.resize_maxHeight ); - - function dragHandler( evt ) - { - var dx = evt.data.$.screenX - origin.x, - dy = evt.data.$.screenY - origin.y, - width = startSize.width, - height = startSize.height, - internalWidth = width + dx * ( editor.lang.dir == 'rtl' ? -1 : 1 ), - internalHeight = height + dy; - - if ( resizeHorizontal ) - width = Math.max( config.resize_minWidth, Math.min( internalWidth, config.resize_maxWidth ) ); - - if ( resizeVertical ) - height = Math.max( config.resize_minHeight, Math.min( internalHeight, config.resize_maxHeight ) ); - - editor.resize( width, height ); - } - - function dragEndHandler ( evt ) - { - CKEDITOR.document.removeListener( 'mousemove', dragHandler ); - CKEDITOR.document.removeListener( 'mouseup', dragEndHandler ); - - if ( editor.document ) - { - editor.document.removeListener( 'mousemove', dragHandler ); - editor.document.removeListener( 'mouseup', dragEndHandler ); - } - } - - var mouseDownFn = CKEDITOR.tools.addFunction( function( $event ) - { - if ( !container ) - container = editor.getResizable(); - - startSize = { width : container.$.offsetWidth || 0, height : container.$.offsetHeight || 0 }; - origin = { x : $event.screenX, y : $event.screenY }; - - CKEDITOR.document.on( 'mousemove', dragHandler ); - CKEDITOR.document.on( 'mouseup', dragEndHandler ); - - if ( editor.document ) - { - editor.document.on( 'mousemove', dragHandler ); - editor.document.on( 'mouseup', dragEndHandler ); - } - }); - - editor.on( 'destroy', function() { CKEDITOR.tools.removeFunction( mouseDownFn ); } ); - - editor.on( 'themeSpace', function( event ) - { - if ( event.data.space == 'bottom' ) - { - var direction = ''; - if ( resizeHorizontal && !resizeVertical) - direction = ' cke_resizer_horizontal'; - if ( !resizeHorizontal && resizeVertical) - direction = ' cke_resizer_vertical'; - - event.data.html += '<div class="cke_resizer' + direction + '"' + - ' title="' + CKEDITOR.tools.htmlEncode( editor.lang.resize ) + '"' + - ' onmousedown="CKEDITOR.tools.callFunction(' + mouseDownFn + ', event)"' + - '></div>'; - } - }, editor, null, 100 ); - } - } -} ); - -/** - * The minimum editor width, in pixels, when resizing it with the resize handle. - * @type Number - * @default 750 - * @example - * config.resize_minWidth = 500; - */ -CKEDITOR.config.resize_minWidth = 750; - -/** - * The minimum editor height, in pixels, when resizing it with the resize handle. - * @type Number - * @default 250 - * @example - * config.resize_minHeight = 600; - */ -CKEDITOR.config.resize_minHeight = 250; - -/** - * The maximum editor width, in pixels, when resizing it with the resize handle. - * @type Number - * @default 3000 - * @example - * config.resize_maxWidth = 750; - */ -CKEDITOR.config.resize_maxWidth = 3000; - -/** - * The maximum editor height, in pixels, when resizing it with the resize handle. - * @type Number - * @default 3000 - * @example - * config.resize_maxHeight = 600; - */ -CKEDITOR.config.resize_maxHeight = 3000; - -/** - * Whether to enable the resizing feature. If disabled the resize handler will not be visible. - * @type Boolean - * @default true - * @example - * config.resize_enabled = false; - */ -CKEDITOR.config.resize_enabled = true; - -/** - * The directions to which the editor resizing is enabled. Possible values - * are "both", "vertical" and "horizontal". - * @type String - * @default 'both' - * @since 3.3 - * @example - * config.resize_dir = 'vertical'; - */ -CKEDITOR.config.resize_dir = 'both'; +/*
+Copyright (c) 2003-2012, 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 ) );
+
+ // DO NOT impose fixed size with single direction resize. (#6308)
+ editor.resize( resizeHorizontal ? width : null, 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';
+ */
diff --git a/_source/plugins/richcombo/plugin.js b/_source/plugins/richcombo/plugin.js index 39315a8..791dede 100644 --- a/_source/plugins/richcombo/plugin.js +++ b/_source/plugins/richcombo/plugin.js @@ -1,370 +1,381 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -CKEDITOR.plugins.add( 'richcombo', -{ - requires : [ 'floatpanel', 'listblock', 'button' ], - - beforeInit : function( editor ) - { - editor.ui.addHandler( CKEDITOR.UI_RICHCOMBO, CKEDITOR.ui.richCombo.handler ); - } -}); - -/** - * Button UI element. - * @constant - * @example - */ -CKEDITOR.UI_RICHCOMBO = 3; - -CKEDITOR.ui.richCombo = CKEDITOR.tools.createClass( -{ - $ : function( definition ) - { - // Copy all definition properties to this object. - CKEDITOR.tools.extend( this, definition, - // Set defaults. - { - title : definition.label, - modes : { wysiwyg : 1 } - }); - - // We don't want the panel definition in this object. - var panelDefinition = this.panel || {}; - delete this.panel; - - this.id = CKEDITOR.tools.getNextNumber(); - - this.document = ( panelDefinition - && panelDefinition.parent - && panelDefinition.parent.getDocument() ) - || CKEDITOR.document; - - panelDefinition.className = ( panelDefinition.className || '' ) + ' cke_rcombopanel'; - panelDefinition.block = - { - multiSelect : panelDefinition.multiSelect, - attributes : panelDefinition.attributes - }; - - this._ = - { - panelDefinition : panelDefinition, - items : {}, - state : CKEDITOR.TRISTATE_OFF - }; - }, - - statics : - { - handler : - { - create : function( definition ) - { - return new CKEDITOR.ui.richCombo( definition ); - } - } - }, - - proto : - { - renderHtml : function( editor ) - { - var output = []; - this.render( editor, output ); - return output.join( '' ); - }, - - /** - * Renders the combo. - * @param {CKEDITOR.editor} editor The editor instance which this button is - * to be used by. - * @param {Array} output The output array to which append the HTML relative - * to this button. - * @example - */ - render : function( editor, output ) - { - var env = CKEDITOR.env; - - var id = 'cke_' + this.id; - var clickFn = CKEDITOR.tools.addFunction( function( $element ) - { - var _ = this._; - - if ( _.state == CKEDITOR.TRISTATE_DISABLED ) - return; - - this.createPanel( editor ); - - if ( _.on ) - { - _.panel.hide(); - return; - } - - if ( !_.committed ) - { - _.list.commit(); - _.committed = 1; - } - - var value = this.getValue(); - if ( value ) - _.list.mark( value ); - else - _.list.unmarkAll(); - - _.panel.showBlock( this.id, new CKEDITOR.dom.element( $element ), 4 ); - }, - this ); - - var instance = { - id : id, - combo : this, - focus : function() - { - var element = CKEDITOR.document.getById( id ).getChild( 1 ); - element.focus(); - }, - clickFn : clickFn - }; - - editor.on( 'mode', function() - { - this.setState( this.modes[ editor.mode ] ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED ); - }, - this ); - - var keyDownFn = CKEDITOR.tools.addFunction( function( ev, element ) - { - ev = new CKEDITOR.dom.event( ev ); - - var keystroke = ev.getKeystroke(); - switch ( keystroke ) - { - case 13 : // ENTER - case 32 : // SPACE - case 40 : // ARROW-DOWN - // Show panel - CKEDITOR.tools.callFunction( clickFn, element ); - break; - default : - // Delegate the default behavior to toolbar button key handling. - instance.onkey( instance, keystroke ); - } - - // Avoid subsequent focus grab on editor document. - ev.preventDefault(); - }); - - // For clean up - instance.keyDownFn = keyDownFn; - - output.push( - '<span class="cke_rcombo">', - '<span id=', id ); - - if ( this.className ) - output.push( ' class="', this.className, ' cke_off"'); - - output.push( - '>', - '<span id="' + id+ '_label" class=cke_label>', this.label, '</span>', - '<a hidefocus=true title="', this.title, '" tabindex="-1"', - env.gecko && env.version >= 10900 && !env.hc ? '' : ' href="javascript:void(\'' + this.label + '\')"', - ' role="button" aria-labelledby="', id , '_label" aria-describedby="', id, '_text" aria-haspopup="true"' ); - - // Some browsers don't cancel key events in the keydown but in the - // keypress. - // TODO: Check if really needed for Gecko+Mac. - if ( CKEDITOR.env.opera || ( CKEDITOR.env.gecko && CKEDITOR.env.mac ) ) - { - output.push( - ' onkeypress="return false;"' ); - } - - // With Firefox, we need to force it to redraw, otherwise it - // will remain in the focus state. - if ( CKEDITOR.env.gecko ) - { - output.push( - ' onblur="this.style.cssText = this.style.cssText;"' ); - } - - output.push( - ' onkeydown="CKEDITOR.tools.callFunction( ', keyDownFn, ', event, this );"' + - ' onclick="CKEDITOR.tools.callFunction(', clickFn, ', this); return false;">' + - '<span>' + - '<span id="' + id + '_text" class="cke_text cke_inline_label">' + this.label + '</span>' + - '</span>' + - '<span class=cke_openbutton>' + ( CKEDITOR.env.hc ? '<span>▼</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() - { - if ( me.className ) - this.element.getFirst().removeClass( me.className + '_panel' ); - - me.setState( CKEDITOR.TRISTATE_OFF ); - - me._.on = 0; - - if ( me.onClose ) - me.onClose(); - }; - - panel.onEscape = function() - { - panel.hide(); - me.document.getById( 'cke_' + me.id ).getFirst().getNext().focus(); - }; - - list.onClick = function( value, marked ) - { - // Move the focus to the main windows, otherwise it will stay - // into the floating panel, even if invisible, and Safari and - // Opera will go a bit crazy. - me.document.getWindow().focus(); - - if ( me.onClick ) - me.onClick.call( me, value, marked ); - - if ( marked ) - me.setValue( value, me._.items[ value ] ); - else - me.setValue( '' ); - - panel.hide(); - }; - - this._.panel = panel; - this._.list = list; - - panel.getBlock( this.id ).onHide = function() - { - me._.on = 0; - me.setState( CKEDITOR.TRISTATE_OFF ); - }; - - if ( this.init ) - this.init(); - }, - - setValue : function( value, text ) - { - this._.value = value; - - var textElement = this.document.getById( 'cke_' + this.id + '_text' ); - - if ( !( value || text ) ) - { - text = this.label; - textElement.addClass( 'cke_inline_label' ); - } - else - textElement.removeClass( 'cke_inline_label' ); - - textElement.setHtml( typeof text != 'undefined' ? text : value ); - }, - - getValue : function() - { - return this._.value || ''; - }, - - unmarkAll : function() - { - this._.list.unmarkAll(); - }, - - mark : function( value ) - { - this._.list.mark( value ); - }, - - hideItem : function( value ) - { - this._.list.hideItem( value ); - }, - - hideGroup : function( groupTitle ) - { - this._.list.hideGroup( groupTitle ); - }, - - showAll : function() - { - this._.list.showAll(); - }, - - add : function( value, html, text ) - { - this._.items[ value ] = text || value; - this._.list.add( value, html, text ); - }, - - startGroup : function( title ) - { - this._.list.startGroup( title ); - }, - - commit : function() - { - this._.list.commit(); - }, - - 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 ); -}; +/*
+Copyright (c) 2003-2012, 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 ? '▼' : CKEDITOR.env.air ? ' ' : '' ) + '</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 );
+};
diff --git a/_source/plugins/save/plugin.js b/_source/plugins/save/plugin.js index 73fd23d..50bb168 100644 --- a/_source/plugins/save/plugin.js +++ b/_source/plugins/save/plugin.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
@@ -12,6 +12,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license var saveCmd =
{
modes : { wysiwyg:1, source:1 },
+ readOnly : 1,
exec : function( editor )
{
diff --git a/_source/plugins/scayt/dialogs/options.js b/_source/plugins/scayt/dialogs/options.js index 6033507..f60eee9 100644 --- a/_source/plugins/scayt/dialogs/options.js +++ b/_source/plugins/scayt/dialogs/options.js @@ -1,533 +1,537 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -CKEDITOR.dialog.add( 'scaytcheck', function( editor ) -{ - var firstLoad = true, - captions, - doc = CKEDITOR.document, - tags = [], - i, - contents = [], - userDicActive = false, - dic_buttons = [ - // [0] contains buttons for creating - "dic_create,dic_restore", - // [1] contains buton for manipulation - "dic_rename,dic_delete" - ], - optionsIds= [ 'mixedCase','mixedWithDigits','allCaps','ignoreDomainNames' ]; - - // common operations - - function getBOMAllOptions () { - return document.forms.optionsbar["options"]; - } - function getBOMAllLangs () { - return document.forms.languagesbar["scayt_lang"]; - } - - function setCheckedValue(radioObj, newValue) { - if (!radioObj) - return; - var radioLength = radioObj.length; - if (radioLength == undefined) { - radioObj.checked = (radioObj.value == newValue.toString()); - return; - } - for (var i = 0; i < radioLength; i++) { - radioObj[i].checked = false; - if (radioObj[i].value == newValue.toString()) { - radioObj[i].checked = true; - } - } - } - - var tags_contents = [ - { - id : 'options', - label : editor.lang.scayt.optionsTab, - elements : [ - { - type : 'html', - id : 'options', - html : '<form name="optionsbar"><div class="inner_options">' + - ' <div class="messagebox"></div>' + - ' <div style="display:none;">' + - ' <input type="checkbox" name="options" id="allCaps" />' + - ' <label for="allCaps" id="label_allCaps"></label>' + - ' </div>' + - ' <div style="display:none;">' + - ' <input name="options" type="checkbox" id="ignoreDomainNames" />' + - ' <label for="ignoreDomainNames" id="label_ignoreDomainNames"></label>' + - ' </div>' + - ' <div style="display:none;">' + - ' <input name="options" type="checkbox" id="mixedCase" />' + - ' <label for="mixedCase" id="label_mixedCase"></label>' + - ' </div>' + - ' <div style="display:none;">' + - ' <input name="options" type="checkbox" id="mixedWithDigits" />' + - ' <label for="mixedWithDigits" id="label_mixedWithDigits"></label>' + - ' </div>' + - '</div></form>' - } - ] - }, - { - id : 'langs', - label : editor.lang.scayt.languagesTab, - elements : [ - { - type : 'html', - id : 'langs', - html : '<form name="languagesbar"><div class="inner_langs">' + - ' <div class="messagebox"></div> ' + - ' <div style="float:left;width:45%;margin-left:5px;" id="scayt_lcol" ></div>' + - ' <div style="float:left;width:45%;margin-left:15px;" id="scayt_rcol"></div>' + - '</div></form>' - } - ] - }, - { - id : 'dictionaries', - label : editor.lang.scayt.dictionariesTab, - elements : [ - { - type : 'html', - style: '', - id : 'dictionaries', - html : '<form name="dictionarybar"><div class="inner_dictionary" style="text-align:left; white-space:normal; width:320px; overflow: hidden;">' + - ' <div style="margin:5px auto; width:80%;white-space:normal; overflow:hidden;" id="dic_message"> </div>' + - ' <div style="margin:5px auto; width:80%;white-space:normal;"> ' + - ' <span class="cke_dialog_ui_labeled_label" >Dictionary name</span><br>'+ - ' <span class="cke_dialog_ui_labeled_content" >'+ - ' <div class="cke_dialog_ui_input_text">'+ - ' <input id="dic_name" type="text" class="cke_dialog_ui_input_text"/>'+ - ' </div></span></div>'+ - ' <div style="margin:5px auto; width:80%;white-space:normal;">'+ - ' <a style="display:none;" class="cke_dialog_ui_button" href="javascript:void(0)" id="dic_create">'+ - ' </a>' + - ' <a style="display:none;" class="cke_dialog_ui_button" href="javascript:void(0)" id="dic_delete">'+ - ' </a>' + - ' <a style="display:none;" class="cke_dialog_ui_button" href="javascript:void(0)" id="dic_rename">'+ - ' </a>' + - ' <a style="display:none;" class="cke_dialog_ui_button" href="javascript:void(0)" id="dic_restore">'+ - ' </a>' + - ' </div>' + - ' <div style="margin:5px auto; width:95%;white-space:normal;" id="dic_info"></div>' + - '</div></form>' - } - ] - }, - { - id : 'about', - label : editor.lang.scayt.aboutTab, - elements : [ - { - type : 'html', - id : 'about', - style : 'margin: 5px 5px;', - html : '<div id="scayt_about"></div>' - } - ] - } - ]; - - var dialogDefiniton = { - title : editor.lang.scayt.title, - minWidth : 360, - minHeight : 220, - onShow : function() - { - var dialog = this; - dialog.data = editor.fire( 'scaytDialog', {} ); - dialog.options = dialog.data.scayt_control.option(); - dialog.sLang = dialog.data.scayt_control.sLang; - - if ( !dialog.data || !dialog.data.scayt || !dialog.data.scayt_control ) - { - alert( 'Error loading application service' ); - dialog.hide(); - return; - } - - var stop = 0; - if ( firstLoad ) - { - dialog.data.scayt.getCaption( editor.langCode || 'en', function( caps ) - { - if ( stop++ > 0 ) // Once only - return; - captions = caps; - init_with_captions.apply( dialog ); - reload.apply( dialog ); - firstLoad = false; - }); - } - else - reload.apply( dialog ); - - dialog.selectPage( dialog.data.tab ); - }, - onOk : function() - { - var scayt_control = this.data.scayt_control; - scayt_control.option( this.options ); - // Setup languge if it was changed. - var csLang = this.chosed_lang; - scayt_control.setLang( csLang ); - scayt_control.refresh(); - }, - onCancel: function() - { - var o = getBOMAllOptions(); - for (i in o) - o[i].checked = false; - - setCheckedValue(getBOMAllLangs(),""); - }, - contents : contents - }; - - var scayt_control = CKEDITOR.plugins.scayt.getScayt( editor ); - - tags = CKEDITOR.plugins.scayt.uiTabs; - - for ( i in tags ) - { - if ( tags[ i ] == 1 ) - contents[ contents.length ] = tags_contents[ i ]; - } - if ( tags[2] == 1 ) - userDicActive = true; - - - var init_with_captions = function() - { - var dialog = this, - lang_list = dialog.data.scayt.getLangList(), - buttons = [ 'dic_create','dic_delete','dic_rename','dic_restore' ], - labels = optionsIds, - i; - - // Add buttons titles - if ( userDicActive ) - { - for ( i = 0; i < buttons.length; i++ ) - { - var button = buttons[ i ]; - doc.getById( button ).setHtml( '<span class="cke_dialog_ui_button">' + captions[ 'button_' + button] +'</span>' ); - } - doc.getById( 'dic_info' ).setHtml( captions[ 'dic_info' ] ); - } - - - // Fill options and dictionary labels. - if ( tags[0] == 1 ) - { - for ( i in labels ) - { - var label = 'label_' + labels[ i ], - labelElement = doc.getById( label ); - - if ( 'undefined' != typeof labelElement - && 'undefined' != typeof captions[ label ] - && 'undefined' != typeof dialog.options[labels[ i ]] ) - { - labelElement.setHtml( captions[ label ] ); - var labelParent = labelElement.getParent(); - labelParent.$.style.display = "block"; - } - } - } - - var about = '<p><img src="' + window.scayt.getAboutInfo().logoURL + '" /></p>' + - '<p>' + captions[ 'version' ] + window.scayt.getAboutInfo().version.toString() + '</p>' + - '<p>' + captions[ 'about_throwt_copy' ] + '</p>'; - - doc.getById( 'scayt_about' ).setHtml( about ); - - // Create languages tab. - var createOption = function( option, list ) - { - var label = doc.createElement( 'label' ); - label.setAttribute( 'for', 'cke_option' + option ); - label.setHtml( list[ option ] ); - - if ( dialog.sLang == option ) // Current. - dialog.chosed_lang = option; - - var div = doc.createElement( 'div' ); - var radio = CKEDITOR.dom.element.createFromHtml( '<input id="cke_option' + - option + '" type="radio" ' + - ( dialog.sLang == option ? 'checked="checked"' : '' ) + - ' value="' + option + '" name="scayt_lang" />' ); - - radio.on( 'click', function() - { - this.$.checked = true; - dialog.chosed_lang = option; - }); - - div.append( radio ); - div.append( label ); - - return { - lang : list[ option ], - code : option, - radio : div - }; - }; - - var langList = []; - if (tags[1] ==1 ) - { - for ( i in lang_list.rtl ) - langList[ langList.length ] = createOption( i, lang_list.ltr ); - - for ( i in lang_list.ltr ) - langList[ langList.length ] = createOption( i, lang_list.ltr ); - - langList.sort( function( lang1, lang2 ) - { - return ( lang2.lang > lang1.lang ) ? -1 : 1 ; - }); - - var fieldL = doc.getById( 'scayt_lcol' ), - fieldR = doc.getById( 'scayt_rcol' ); - for ( i=0; i < langList.length; i++ ) - { - var field = ( i < langList.length / 2 ) ? fieldL : fieldR; - field.append( langList[ i ].radio ); - } - } - - // user dictionary handlers - var dic = {}; - dic.dic_create = function( el, dic_name , dic_buttons ) - { - // comma separated button's ids include repeats if exists - var all_buttons = dic_buttons[0] + ',' + dic_buttons[1]; - - var err_massage = captions["err_dic_create"]; - var suc_massage = captions["succ_dic_create"]; - - window.scayt.createUserDictionary(dic_name, - function(arg) - { - hide_dic_buttons ( all_buttons ); - display_dic_buttons ( dic_buttons[1] ); - suc_massage = suc_massage.replace("%s" , arg.dname ); - dic_success_message (suc_massage); - }, - function(arg) - { - err_massage = err_massage.replace("%s" ,arg.dname ); - dic_error_message ( err_massage + "( "+ (arg.message || "") +")"); - }); - - }; - - dic.dic_rename = function( el, dic_name ) - { - // - // try to rename dictionary - var err_massage = captions["err_dic_rename"] || ""; - var suc_massage = captions["succ_dic_rename"] || ""; - window.scayt.renameUserDictionary(dic_name, - function(arg) - { - suc_massage = suc_massage.replace("%s" , arg.dname ); - set_dic_name( dic_name ); - dic_success_message ( suc_massage ); - }, - function(arg) - { - err_massage = err_massage.replace("%s" , arg.dname ); - set_dic_name( dic_name ); - dic_error_message( err_massage + "( " + ( arg.message || "" ) + " )" ); - }); - }; - - dic.dic_delete = function ( el, dic_name , dic_buttons ) - { - var all_buttons = dic_buttons[0] + ',' + dic_buttons[1]; - var err_massage = captions["err_dic_delete"]; - var suc_massage = captions["succ_dic_delete"]; - - // try to delete dictionary - window.scayt.deleteUserDictionary( - function(arg) - { - suc_massage = suc_massage.replace("%s" , arg.dname ); - hide_dic_buttons ( all_buttons ); - display_dic_buttons ( dic_buttons[0] ); - set_dic_name( "" ); // empty input field - dic_success_message( suc_massage ); - }, - function(arg) - { - err_massage = err_massage.replace("%s" , arg.dname ); - dic_error_message(err_massage); - }); - }; - - dic.dic_restore = dialog.dic_restore || function ( el, dic_name , dic_buttons ) - { - // try to restore existing dictionary - var all_buttons = dic_buttons[0] + ',' + dic_buttons[1]; - var err_massage = captions["err_dic_restore"]; - var suc_massage = captions["succ_dic_restore"]; - - window.scayt.restoreUserDictionary(dic_name, - function(arg) - { - suc_massage = suc_massage.replace("%s" , arg.dname ); - hide_dic_buttons ( all_buttons ); - display_dic_buttons(dic_buttons[1]); - dic_success_message( suc_massage ); - }, - function(arg) - { - err_massage = err_massage.replace("%s" , arg.dname ); - dic_error_message( err_massage ); - }); - }; - - function onDicButtonClick( ev ) - { - var dic_name = doc.getById('dic_name').getValue(); - if ( !dic_name ) - { - dic_error_message(" Dictionary name should not be empty. "); - return false; - } - try{ - var el = id = ev.data.getTarget().getParent(); - var id = el.getId(); - dic[ id ].apply( null, [ el, dic_name, dic_buttons ] ); - }catch(err){ - dic_error_message(" Dictionary error. "); - } - - return true; - } - - // ** bind event listeners - var arr_buttons = ( dic_buttons[0] + ',' + dic_buttons[1] ).split( ',' ), - l; - - for ( i = 0, l = arr_buttons.length ; i < l ; i += 1 ) - { - var dic_button = doc.getById(arr_buttons[i]); - if ( dic_button ) - dic_button.on( 'click', onDicButtonClick, this ); - } - }; - - var reload = function() - { - var dialog = this; - // for enabled options tab - if (tags[0] == 1){ - var opto = getBOMAllOptions(); - - // Animate options. - for ( var k=0,l = opto.length; k<l;k++ ) - { - - var i = opto[k].id; - var checkbox = doc.getById( i ); - - if ( checkbox ) - { - opto[k].checked = false; - //alert (opto[k].removeAttribute) - if ( dialog.options[ i ] == 1 ) - { - opto[k].checked = true; - } - - - // Bind events. Do it only once. - if ( firstLoad ) - { - checkbox.on( 'click', function() - { - dialog.options[ this.getId() ] = this.$.checked ? 1 : 0 ; - }); - } - } - } - } - - //for enabled languages tab - if ( tags[1] == 1 ) - { - var domLang = doc.getById("cke_option"+dialog.sLang); - setCheckedValue(domLang.$,dialog.sLang); - } - - // * user dictionary - if ( userDicActive ) - { - window.scayt.getNameUserDictionary( - function( o ) - { - var dic_name = o.dname; - hide_dic_buttons( dic_buttons[0] + ',' + dic_buttons[1] ); - if ( dic_name ) - { - doc.getById( 'dic_name' ).setValue(dic_name); - display_dic_buttons( dic_buttons[1] ); - } - else - display_dic_buttons( dic_buttons[0] ); - - }, - function () - { - doc.getById( 'dic_name' ).setValue(""); - }); - dic_success_message(""); - } - - }; - - function dic_error_message ( m ) - { - doc.getById('dic_message').setHtml('<span style="color:red;">' + m + '</span>' ); - } - function dic_success_message ( m ) - { - doc.getById('dic_message').setHtml('<span style="color:blue;">' + m + '</span>') ; - } - function display_dic_buttons ( sIds ) - { - - sIds = String( sIds ); - var aIds = sIds.split(','); - for ( var i=0, l = aIds.length; i < l ; i+=1) - { - doc.getById( aIds[i] ).$.style.display = "inline"; - } - - } - function hide_dic_buttons ( sIds ) - { - sIds = String( sIds ); - var aIds = sIds.split(','); - for ( var i = 0, l = aIds.length; i < l ; i += 1 ) - { - doc.getById( aIds[i] ).$.style.display = "none"; - } - } - function set_dic_name ( dic_name ) - { - doc.getById('dic_name').$.value= dic_name; - } - - return dialogDefiniton; -}); +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.dialog.add( 'scaytcheck', function( editor )
+{
+ var firstLoad = true,
+ captions,
+ doc = CKEDITOR.document,
+ editorName = editor.name,
+ tags = CKEDITOR.plugins.scayt.getUiTabs( editor ),
+ i,
+ contents = [],
+ userDicActive = 0,
+ dic_buttons = [
+ // [0] contains buttons for creating
+ "dic_create_" + editorName + ",dic_restore_" + editorName,
+ // [1] contains buton for manipulation
+ "dic_rename_" + editorName + ",dic_delete_" + editorName
+ ],
+ optionsIds = [ 'mixedCase', 'mixedWithDigits', 'allCaps', 'ignoreDomainNames' ];
+
+ // common operations
+
+ function getBOMAllOptions()
+ {
+ if (typeof document.forms["optionsbar_" + editorName] != "undefined")
+ return document.forms["optionsbar_" + editorName]["options"];
+ return [];
+ }
+ function getBOMAllLangs()
+ {
+ if (typeof document.forms["languagesbar_" + editorName] != "undefined")
+ return document.forms["languagesbar_" + editorName]["scayt_lang"];
+ return [];
+ }
+
+ function setCheckedValue( radioObj, newValue )
+ {
+ if ( !radioObj )
+ return;
+ var radioLength = radioObj.length;
+ if ( radioLength == undefined )
+ {
+ radioObj.checked = radioObj.value == newValue.toString();
+ return;
+ }
+ for ( var i = 0; i < radioLength; i++ )
+ {
+ radioObj[i].checked = false;
+ if ( radioObj[i].value == newValue.toString() )
+ radioObj[i].checked = true;
+ }
+ }
+
+ var lang = editor.lang.scayt;
+ var tags_contents = [
+ {
+ id : 'options',
+ label : lang.optionsTab,
+ elements : [
+ {
+ type : 'html',
+ id : 'options',
+ html : '<form name="optionsbar_' + editorName + '"><div class="inner_options">' +
+ ' <div class="messagebox"></div>' +
+ ' <div style="display:none;">' +
+ ' <input type="checkbox" name="options" id="allCaps_' + editorName + '" />' +
+ ' <label for="allCaps" id="label_allCaps_' + editorName + '"></label>' +
+ ' </div>' +
+ ' <div style="display:none;">' +
+ ' <input name="options" type="checkbox" id="ignoreDomainNames_' + editorName + '" />' +
+ ' <label for="ignoreDomainNames" id="label_ignoreDomainNames_' + editorName + '"></label>' +
+ ' </div>' +
+ ' <div style="display:none;">' +
+ ' <input name="options" type="checkbox" id="mixedCase_' + editorName + '" />' +
+ ' <label for="mixedCase" id="label_mixedCase_' + editorName + '"></label>' +
+ ' </div>' +
+ ' <div style="display:none;">' +
+ ' <input name="options" type="checkbox" id="mixedWithDigits_' + editorName + '" />' +
+ ' <label for="mixedWithDigits" id="label_mixedWithDigits_' + editorName + '"></label>' +
+ ' </div>' +
+ '</div></form>'
+ }
+ ]
+ },
+ {
+ id : 'langs',
+ label : lang.languagesTab,
+ elements : [
+ {
+ type : 'html',
+ id : 'langs',
+ html : '<form name="languagesbar_' + editorName + '"><div class="inner_langs">' +
+ ' <div class="messagebox"></div> ' +
+ ' <div style="float:left;width:45%;margin-left:5px;" id="scayt_lcol_' + editorName + '" ></div>' +
+ ' <div style="float:left;width:45%;margin-left:15px;" id="scayt_rcol_' + editorName + '"></div>' +
+ '</div></form>'
+ }
+ ]
+ },
+ {
+ id : 'dictionaries',
+ label : lang.dictionariesTab,
+ elements : [
+ {
+ type : 'html',
+ style: '',
+ id : 'dictionaries',
+ html : '<form name="dictionarybar_' + editorName + '"><div class="inner_dictionary" style="text-align:left; white-space:normal; width:320px; overflow: hidden;">' +
+ ' <div style="margin:5px auto; width:80%;white-space:normal; overflow:hidden;" id="dic_message_' + editorName + '"> </div>' +
+ ' <div style="margin:5px auto; width:80%;white-space:normal;"> ' +
+ ' <span class="cke_dialog_ui_labeled_label" >Dictionary name</span><br>'+
+ ' <span class="cke_dialog_ui_labeled_content" >'+
+ ' <div class="cke_dialog_ui_input_text">'+
+ ' <input id="dic_name_' + editorName + '" type="text" class="cke_dialog_ui_input_text"/>'+
+ ' </div></span></div>'+
+ ' <div style="margin:5px auto; width:80%;white-space:normal;">'+
+ ' <a style="display:none;" class="cke_dialog_ui_button" href="javascript:void(0)" id="dic_create_' + editorName + '">'+
+ ' </a>' +
+ ' <a style="display:none;" class="cke_dialog_ui_button" href="javascript:void(0)" id="dic_delete_' + editorName + '">'+
+ ' </a>' +
+ ' <a style="display:none;" class="cke_dialog_ui_button" href="javascript:void(0)" id="dic_rename_' + editorName + '">'+
+ ' </a>' +
+ ' <a style="display:none;" class="cke_dialog_ui_button" href="javascript:void(0)" id="dic_restore_' + editorName + '">'+
+ ' </a>' +
+ ' </div>' +
+ ' <div style="margin:5px auto; width:95%;white-space:normal;" id="dic_info_' + editorName + '"></div>' +
+ '</div></form>'
+ }
+ ]
+ },
+ {
+ id : 'about',
+ label : lang.aboutTab,
+ elements : [
+ {
+ type : 'html',
+ id : 'about',
+ style : 'margin: 5px 5px;',
+ html : '<div id="scayt_about_' + editorName + '"></div>'
+ }
+ ]
+ }
+ ];
+
+ var dialogDefiniton = {
+ title : lang.title,
+ minWidth : 360,
+ minHeight : 220,
+ onShow : function()
+ {
+ var dialog = this;
+ dialog.data = editor.fire( 'scaytDialog', {} );
+ dialog.options = dialog.data.scayt_control.option();
+ dialog.chosed_lang = dialog.sLang = dialog.data.scayt_control.sLang;
+
+ if ( !dialog.data || !dialog.data.scayt || !dialog.data.scayt_control )
+ {
+ alert( 'Error loading application service' );
+ dialog.hide();
+ return;
+ }
+
+ var stop = 0;
+ if ( firstLoad )
+ {
+ dialog.data.scayt.getCaption( editor.langCode || 'en', function( caps )
+ {
+ if ( stop++ > 0 ) // Once only
+ return;
+ captions = caps;
+ init_with_captions.apply( dialog );
+ reload.apply( dialog );
+ firstLoad = false;
+ });
+ }
+ else
+ reload.apply( dialog );
+
+ dialog.selectPage( dialog.data.tab );
+ },
+ onOk : function()
+ {
+ var scayt_control = this.data.scayt_control;
+ scayt_control.option( this.options );
+ // Setup language if it was changed.
+ var csLang = this.chosed_lang;
+ scayt_control.setLang( csLang );
+ scayt_control.refresh();
+ },
+ onCancel: function()
+ {
+ var o = getBOMAllOptions();
+ for ( var i in o )
+ o[i].checked = false;
+
+ setCheckedValue( getBOMAllLangs(), "" );
+ },
+ contents : contents
+ };
+
+ var scayt_control = CKEDITOR.plugins.scayt.getScayt( editor );
+
+ for ( i = 0; i < tags.length; i++ )
+ {
+ if ( tags[ i ] == 1 )
+ contents[ contents.length ] = tags_contents[ i ];
+ }
+ if ( tags[2] == 1 )
+ userDicActive = 1;
+
+ var init_with_captions = function()
+ {
+ var dialog = this,
+ lang_list = dialog.data.scayt.getLangList(),
+ buttonCaptions = [ 'dic_create', 'dic_delete', 'dic_rename', 'dic_restore' ],
+ buttonIds = [],
+ langList = [],
+ labels = optionsIds,
+ i;
+
+ // Add buttons titles
+ if ( userDicActive )
+ {
+ for ( i = 0; i < buttonCaptions.length; i++ )
+ {
+ buttonIds[ i ] = buttonCaptions[ i ] + "_" + editorName;
+ doc.getById( buttonIds[ i ] ).setHtml( '<span class="cke_dialog_ui_button">' + captions[ 'button_' + buttonCaptions[ i ]] +'</span>' );
+ }
+ doc.getById( 'dic_info_' + editorName ).setHtml( captions[ 'dic_info' ] );
+ }
+
+ // Fill options and dictionary labels.
+ if ( tags[0] == 1 )
+ {
+ for ( i in labels )
+ {
+ var labelCaption = 'label_' + labels[ i ],
+ labelId = labelCaption + '_' + editorName,
+ labelElement = doc.getById( labelId );
+
+ if ( 'undefined' != typeof labelElement
+ && 'undefined' != typeof captions[ labelCaption ]
+ && 'undefined' != typeof dialog.options[labels[ i ]] )
+ {
+ labelElement.setHtml( captions[ labelCaption ] );
+ var labelParent = labelElement.getParent();
+ labelParent.$.style.display = "block";
+ }
+ }
+ }
+
+ var about = '<p><img src="' + window.scayt.getAboutInfo().logoURL + '" /></p>' +
+ '<p>' + captions[ 'version' ] + window.scayt.getAboutInfo().version.toString() + '</p>' +
+ '<p>' + captions[ 'about_throwt_copy' ] + '</p>';
+
+ doc.getById( 'scayt_about_' + editorName ).setHtml( about );
+
+ // Create languages tab.
+ var createOption = function( option, list )
+ {
+ var label = doc.createElement( 'label' );
+ label.setAttribute( 'for', 'cke_option' + option );
+ label.setHtml( list[ option ] );
+
+ if ( dialog.sLang == option ) // Current.
+ dialog.chosed_lang = option;
+
+ var div = doc.createElement( 'div' );
+ var radio = CKEDITOR.dom.element.createFromHtml( '<input id="cke_option' +
+ option + '" type="radio" ' +
+ ( dialog.sLang == option ? 'checked="checked"' : '' ) +
+ ' value="' + option + '" name="scayt_lang" />' );
+
+ radio.on( 'click', function()
+ {
+ this.$.checked = true;
+ dialog.chosed_lang = option;
+ });
+
+ div.append( radio );
+ div.append( label );
+
+ return {
+ lang : list[ option ],
+ code : option,
+ radio : div
+ };
+ };
+
+ if ( tags[1] ==1 )
+ {
+ for ( i in lang_list.rtl )
+ langList[ langList.length ] = createOption( i, lang_list.ltr );
+
+ for ( i in lang_list.ltr )
+ langList[ langList.length ] = createOption( i, lang_list.ltr );
+
+ langList.sort( function( lang1, lang2 )
+ {
+ return ( lang2.lang > lang1.lang ) ? -1 : 1 ;
+ });
+
+ var fieldL = doc.getById( 'scayt_lcol_' + editorName ),
+ fieldR = doc.getById( 'scayt_rcol_' + editorName );
+ for ( i=0; i < langList.length; i++ )
+ {
+ var field = ( i < langList.length / 2 ) ? fieldL : fieldR;
+ field.append( langList[ i ].radio );
+ }
+ }
+
+ // user dictionary handlers
+ var dic = {};
+ dic.dic_create = function( el, dic_name , dic_buttons )
+ {
+ // comma separated button's ids include repeats if exists
+ var all_buttons = dic_buttons[0] + ',' + dic_buttons[1];
+
+ var err_massage = captions["err_dic_create"];
+ var suc_massage = captions["succ_dic_create"];
+
+ window.scayt.createUserDictionary( dic_name,
+ function( arg )
+ {
+ hide_dic_buttons ( all_buttons );
+ display_dic_buttons ( dic_buttons[1] );
+ suc_massage = suc_massage.replace("%s" , arg.dname );
+ dic_success_message (suc_massage);
+ },
+ function( arg )
+ {
+ err_massage = err_massage.replace("%s" ,arg.dname );
+ dic_error_message ( err_massage + "( "+ (arg.message || "") +")");
+ });
+
+ };
+
+ dic.dic_rename = function( el, dic_name )
+ {
+ //
+ // try to rename dictionary
+ var err_massage = captions["err_dic_rename"] || "";
+ var suc_massage = captions["succ_dic_rename"] || "";
+ window.scayt.renameUserDictionary( dic_name,
+ function( arg )
+ {
+ suc_massage = suc_massage.replace("%s" , arg.dname );
+ set_dic_name( dic_name );
+ dic_success_message ( suc_massage );
+ },
+ function( arg )
+ {
+ err_massage = err_massage.replace("%s" , arg.dname );
+ set_dic_name( dic_name );
+ dic_error_message( err_massage + "( " + ( arg.message || "" ) + " )" );
+ });
+ };
+
+ dic.dic_delete = function( el, dic_name , dic_buttons )
+ {
+ var all_buttons = dic_buttons[0] + ',' + dic_buttons[1];
+ var err_massage = captions["err_dic_delete"];
+ var suc_massage = captions["succ_dic_delete"];
+
+ // try to delete dictionary
+ window.scayt.deleteUserDictionary(
+ function( arg )
+ {
+ suc_massage = suc_massage.replace("%s" , arg.dname );
+ hide_dic_buttons ( all_buttons );
+ display_dic_buttons ( dic_buttons[0] );
+ set_dic_name( "" ); // empty input field
+ dic_success_message( suc_massage );
+ },
+ function( arg )
+ {
+ err_massage = err_massage.replace("%s" , arg.dname );
+ dic_error_message(err_massage);
+ });
+ };
+
+ dic.dic_restore = dialog.dic_restore || function( el, dic_name , dic_buttons )
+ {
+ // try to restore existing dictionary
+ var all_buttons = dic_buttons[0] + ',' + dic_buttons[1];
+ var err_massage = captions["err_dic_restore"];
+ var suc_massage = captions["succ_dic_restore"];
+
+ window.scayt.restoreUserDictionary(dic_name,
+ function( arg )
+ {
+ suc_massage = suc_massage.replace("%s" , arg.dname );
+ hide_dic_buttons ( all_buttons );
+ display_dic_buttons(dic_buttons[1]);
+ dic_success_message( suc_massage );
+ },
+ function( arg )
+ {
+ err_massage = err_massage.replace("%s" , arg.dname );
+ dic_error_message( err_massage );
+ });
+ };
+
+ function onDicButtonClick( ev )
+ {
+ var dic_name = doc.getById('dic_name_' + editorName).getValue();
+ if ( !dic_name )
+ {
+ dic_error_message(" Dictionary name should not be empty. ");
+ return false;
+ }
+ try{
+ var el = ev.data.getTarget().getParent();
+ var id = /(dic_\w+)_[\w\d]+/.exec(el.getId())[1];
+ dic[ id ].apply( null, [ el, dic_name, dic_buttons ] );
+ }
+ catch(err)
+ {
+ dic_error_message(" Dictionary error. ");
+ }
+
+ return true;
+ }
+
+ // ** bind event listeners
+ var arr_buttons = ( dic_buttons[0] + ',' + dic_buttons[1] ).split( ',' ),
+ l;
+
+ for ( i = 0, l = arr_buttons.length ; i < l ; i += 1 )
+ {
+ var dic_button = doc.getById(arr_buttons[i]);
+ if ( dic_button )
+ dic_button.on( 'click', onDicButtonClick, this );
+ }
+ };
+
+ var reload = function()
+ {
+ var dialog = this;
+ // for enabled options tab
+ if ( tags[0] == 1 ){
+ var opto = getBOMAllOptions();
+
+ // Animate options.
+ for ( var k=0,l = opto.length; k<l;k++ )
+ {
+
+ var i = opto[k].id;
+ var checkbox = doc.getById( i );
+
+ if ( checkbox )
+ {
+ opto[k].checked = false;
+ //alert (opto[k].removeAttribute)
+ if ( dialog.options[ i.split("_")[0] ] == 1 )
+ {
+ opto[k].checked = true;
+ }
+
+
+ // Bind events. Do it only once.
+ if ( firstLoad )
+ {
+ checkbox.on( 'click', function()
+ {
+ dialog.options[ this.getId().split("_")[0] ] = this.$.checked ? 1 : 0 ;
+ });
+ }
+ }
+ }
+ }
+
+ //for enabled languages tab
+ if ( tags[1] == 1 )
+ {
+ var domLang = doc.getById("cke_option" + dialog.sLang);
+ setCheckedValue( domLang.$,dialog.sLang );
+ }
+
+ // * user dictionary
+ if ( userDicActive )
+ {
+ window.scayt.getNameUserDictionary(
+ function( o )
+ {
+ var dic_name = o.dname;
+ hide_dic_buttons( dic_buttons[0] + ',' + dic_buttons[1] );
+ if ( dic_name )
+ {
+ doc.getById( 'dic_name_' + editorName ).setValue(dic_name);
+ display_dic_buttons( dic_buttons[1] );
+ }
+ else
+ display_dic_buttons( dic_buttons[0] );
+
+ },
+ function()
+ {
+ doc.getById( 'dic_name_' + editorName ).setValue("");
+ });
+ dic_success_message("");
+ }
+
+ };
+
+ function dic_error_message( m )
+ {
+ doc.getById('dic_message_' + editorName).setHtml('<span style="color:red;">' + m + '</span>' );
+ }
+ function dic_success_message( m )
+ {
+ doc.getById('dic_message_' + editorName).setHtml('<span style="color:blue;">' + m + '</span>') ;
+ }
+ function display_dic_buttons( sIds )
+ {
+ sIds = String( sIds );
+ var aIds = sIds.split(',');
+ for ( var i=0, l = aIds.length; i < l ; i+=1)
+ doc.getById( aIds[i] ).$.style.display = "inline";
+ }
+ function hide_dic_buttons( sIds )
+ {
+ sIds = String( sIds );
+ var aIds = sIds.split(',');
+ for ( var i = 0, l = aIds.length; i < l ; i += 1 )
+ doc.getById( aIds[i] ).$.style.display = "none";
+ }
+ function set_dic_name( dic_name )
+ {
+ doc.getById('dic_name_' + editorName).$.value= dic_name;
+ }
+
+ return dialogDefiniton;
+});
diff --git a/_source/plugins/scayt/plugin.js b/_source/plugins/scayt/plugin.js index 9786b49..336fb3a 100644 --- a/_source/plugins/scayt/plugin.js +++ b/_source/plugins/scayt/plugin.js @@ -1,960 +1,973 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -/** - * @fileOverview Spell Check As You Type (SCAYT). - * Button name : Scayt. - */ - -(function() -{ - var commandName = 'scaytcheck', - openPage = ''; - - // Checks if a value exists in an array - function in_array(needle, haystack) - { - var found = false, key; - for (key in haystack) - { - if ((haystack[key] === needle) || ( haystack[key] == needle)) - { - found = true; - break; - } - } - return found; - } - - var onEngineLoad = function() - { - var editor = this; - - var createInstance = function() // Create new instance every time Document is created. - { - // Initialise Scayt instance. - var oParams = {}; - // Get the iframe. - oParams.srcNodeRef = editor.document.getWindow().$.frameElement; - // syntax : AppName.AppVersion@AppRevision - oParams.assocApp = 'CKEDITOR.' + CKEDITOR.version + '@' + CKEDITOR.revision; - oParams.customerid = editor.config.scayt_customerid || '1:WvF0D4-UtPqN1-43nkD4-NKvUm2-daQqk3-LmNiI-z7Ysb4-mwry24-T8YrS3-Q2tpq2'; - oParams.customDictionaryIds = editor.config.scayt_customDictionaryIds || ''; - oParams.userDictionaryName = editor.config.scayt_userDictionaryName || ''; - oParams.sLang = editor.config.scayt_sLang || 'en_US'; - - // Introduce SCAYT onLoad callback. (#5632) - oParams.onLoad = function() - { - // Draw down word marker to avoid being covered by background-color style.(#5466) - if ( !( CKEDITOR.env.ie && CKEDITOR.env.version < 8 ) ) - this.addStyle( this.selectorCss(), 'padding-bottom: 2px !important;' ); - - // Call scayt_control.focus when SCAYT loaded - // and only if editor has focus and scayt control creates at first time (#5720) - if ( editor.focusManager.hasFocus && !plugin.isControlRestored( editor ) ) - this.focus(); - - }; - - oParams.onBeforeChange = function() - { - if ( plugin.getScayt( editor ) && !editor.checkDirty() ) - setTimeout( function(){ editor.resetDirty(); } ); - }; - - var scayt_custom_params = window.scayt_custom_params; - if ( typeof scayt_custom_params == 'object') - { - for ( var k in scayt_custom_params ) - { - oParams[ k ] = scayt_custom_params[ k ]; - } - } - // needs for restoring a specific scayt control settings - if ( plugin.getControlId(editor) ) - oParams.id = plugin.getControlId(editor); - - var scayt_control = new window.scayt( oParams ); - - scayt_control.afterMarkupRemove.push( function( node ) - { - ( new CKEDITOR.dom.element( node, scayt_control.document ) ).mergeSiblings(); - } ); - - // Copy config. - var lastInstance = plugin.instances[ editor.name ]; - if ( lastInstance ) - { - scayt_control.sLang = lastInstance.sLang; - scayt_control.option( lastInstance.option() ); - scayt_control.paused = lastInstance.paused; - } - - plugin.instances[ editor.name ] = scayt_control; - - //window.scayt.uiTags - var menuGroup = 'scaytButton'; - var uiTabs = window.scayt.uiTags; - var fTabs = []; - - for (var i = 0,l=4; i<l; i++) - fTabs.push( uiTabs[i] && plugin.uiTabs[i] ); - - plugin.uiTabs = fTabs; - try { - scayt_control.setDisabled( plugin.isPaused( editor ) === false ); - } catch (e) {} - - editor.fire( 'showScaytState' ); - }; - - editor.on( 'contentDom', createInstance ); - editor.on( 'contentDomUnload', function() - { - // Remove scripts. - var scripts = CKEDITOR.document.getElementsByTag( 'script' ), - scaytIdRegex = /^dojoIoScript(\d+)$/i, - scaytSrcRegex = /^https?:\/\/svc\.spellchecker\.net\/spellcheck\/script\/ssrv\.cgi/i; - - for ( var i=0; i < scripts.count(); i++ ) - { - var script = scripts.getItem( i ), - id = script.getId(), - src = script.getAttribute( 'src' ); - - if ( id && src && id.match( scaytIdRegex ) && src.match( scaytSrcRegex )) - script.remove(); - } - }); - - editor.on( 'beforeCommandExec', function( ev ) // Disable SCAYT before Source command execution. - { - if ( (ev.data.name == 'source' || ev.data.name == 'newpage') && editor.mode == 'wysiwyg' ) - { - var scayt_instance = plugin.getScayt( editor ); - if ( scayt_instance ) - { - plugin.setPaused( editor, !scayt_instance.disabled ); - // store a control id for restore a specific scayt control settings - plugin.setControlId( editor, scayt_instance.id ); - scayt_instance.destroy( true ); - delete plugin.instances[ editor.name ]; - } - } - // Catch on source mode switch off (#5720) - else if ( ev.data.name == 'source' && editor.mode == 'source' ) - plugin.markControlRestore( editor ); - }); - - editor.on( 'afterCommandExec', function( ev ) - { - if ( !plugin.isScaytEnabled( editor ) ) - return; - - if ( editor.mode == 'wysiwyg' && ( ev.data.name == 'undo' || ev.data.name == 'redo' ) ) - window.setTimeout( function() { plugin.getScayt( editor ).refresh(); }, 10 ); - }); - - editor.on( 'destroy', function( ev ) - { - var editor = ev.editor, - scayt_instance = plugin.getScayt( editor ); - - // SCAYT instance might already get destroyed by mode switch (#5744). - if ( !scayt_instance ) - return; - - delete plugin.instances[ editor.name ]; - // store a control id for restore a specific scayt control settings - plugin.setControlId( editor, scayt_instance.id ); - scayt_instance.destroy( true ); - }); - - // Listen to data manipulation to reflect scayt markup. - editor.on( 'afterSetData', function() - { - if ( plugin.isScaytEnabled( editor ) ) { - window.setTimeout( function() - { - var instance = plugin.getScayt( editor ); - instance && instance.refresh(); - }, 10 ); - } - }); - - // Reload spell-checking for current word after insertion completed. - editor.on( 'insertElement', function() - { - var scayt_instance = plugin.getScayt( editor ); - if ( plugin.isScaytEnabled( editor ) ) - { - // Unlock the selection before reload, SCAYT will take - // care selection update. - if ( CKEDITOR.env.ie ) - editor.getSelection().unlock( true ); - - // Return focus to the editor and refresh SCAYT markup (#5573). - window.setTimeout( function() - { - scayt_instance.focus(); - scayt_instance.refresh(); - }, 10 ); - } - }, this, null, 50 ); - - editor.on( 'insertHtml', function() - { - var scayt_instance = plugin.getScayt( editor ); - if ( plugin.isScaytEnabled( editor ) ) - { - // Unlock the selection before reload, SCAYT will take - // care selection update. - if ( CKEDITOR.env.ie ) - editor.getSelection().unlock( true ); - - // Return focus to the editor (#5573) - // Refresh SCAYT markup - window.setTimeout( function() - { - scayt_instance.focus(); - scayt_instance.refresh(); - }, 10 ); - } - }, this, null, 50 ); - - editor.on( 'scaytDialog', function( ev ) // Communication with dialog. - { - ev.data.djConfig = window.djConfig; - ev.data.scayt_control = plugin.getScayt( editor ); - ev.data.tab = openPage; - ev.data.scayt = window.scayt; - }); - - var dataProcessor = editor.dataProcessor, - htmlFilter = dataProcessor && dataProcessor.htmlFilter; - - if ( htmlFilter ) - { - htmlFilter.addRules( - { - elements : - { - span : function( element ) - { - if ( element.attributes.scayt_word && element.attributes.scaytid ) - { - delete element.name; // Write children, but don't write this node. - return element; - } - } - } - } - ); - } - - // Override Image.equals method avoid CK snapshot module to add SCAYT markup to snapshots. (#5546) - var undoImagePrototype = CKEDITOR.plugins.undo.Image.prototype; - undoImagePrototype.equals = CKEDITOR.tools.override( undoImagePrototype.equals, function( org ) - { - return function( otherImage ) - { - var thisContents = this.contents, - otherContents = otherImage.contents; - var scayt_instance = plugin.getScayt( this.editor ); - // Making the comparison based on content without SCAYT word markers. - if ( scayt_instance && plugin.isScaytReady( this.editor ) ) - { - // scayt::reset might return value undefined. (#5742) - this.contents = scayt_instance.reset( thisContents ) || ''; - otherImage.contents = scayt_instance.reset( otherContents ) || ''; - } - - var retval = org.apply( this, arguments ); - - this.contents = thisContents; - otherImage.contents = otherContents; - return retval; - }; - }); - - if ( editor.document ) - createInstance(); - }; - -CKEDITOR.plugins.scayt = - { - engineLoaded : false, - instances : {}, - // Data storage for SCAYT control, based on editor instances - controlInfo : {}, - setControlInfo : function( editor, o ) - { - if ( editor && editor.name && typeof ( this.controlInfo[ editor.name ] ) != 'object' ) - this.controlInfo[ editor.name ] = {}; - - for ( var infoOpt in o ) - this.controlInfo[ editor.name ][ infoOpt ] = o[ infoOpt ]; - }, - isControlRestored : function ( editor ) - { - if ( editor && - editor.name && - this.controlInfo[ editor.name ] ) - { - return this.controlInfo[ editor.name ].restored ; - } - return false; - }, - markControlRestore : function ( editor ) - { - this.setControlInfo( editor,{ restored:true } ); - }, - setControlId: function (editor, id) - { - this.setControlInfo( editor,{ id:id } ); - }, - getControlId: function (editor) - { - if ( editor && - editor.name && - this.controlInfo[ editor.name ] && - this.controlInfo[ editor.name ].id ) - { - return this.controlInfo[ editor.name ].id; - } - return null; - }, - setPaused: function ( editor , bool ) - { - this.setControlInfo( editor,{ paused:bool } ); - }, - isPaused: function (editor) - { - if ( editor && - editor.name && - this.controlInfo[editor.name] ) - { - return this.controlInfo[editor.name].paused ; - } - return undefined; - }, - getScayt : function( editor ) - { - return this.instances[ editor.name ]; - }, - isScaytReady : function( editor ) - { - return this.engineLoaded === true && - 'undefined' !== typeof window.scayt && this.getScayt( editor ); - }, - isScaytEnabled : function( editor ) - { - var scayt_instance = this.getScayt( editor ); - return ( scayt_instance ) ? scayt_instance.disabled === false : false; - }, - loadEngine : function( editor ) - { - // SCAYT doesn't work with Firefox2, Opera. - if ( CKEDITOR.env.gecko && CKEDITOR.env.version < 10900 || CKEDITOR.env.opera ) - return editor.fire( 'showScaytState' ); - - if ( this.engineLoaded === true ) - return onEngineLoad.apply( editor ); // Add new instance. - else if ( this.engineLoaded == -1 ) // We are waiting. - return CKEDITOR.on( 'scaytReady', function(){ onEngineLoad.apply( editor ); } ); // Use function(){} to avoid rejection as duplicate. - - CKEDITOR.on( 'scaytReady', onEngineLoad, editor ); - CKEDITOR.on( 'scaytReady', function() - { - this.engineLoaded = true; - }, - this, - null, - 0 - ); // First to run. - - this.engineLoaded = -1; // Loading in progress. - - // compose scayt url - var protocol = document.location.protocol; - // Default to 'http' for unknown. - protocol = protocol.search( /https?:/) != -1? protocol : 'http:'; - var baseUrl = 'svc.spellchecker.net/spellcheck31/lf/scayt24/loader__base.js'; - - var scaytUrl = editor.config.scayt_srcUrl || ( protocol + '//' + baseUrl ); - var scaytConfigBaseUrl = plugin.parseUrl( scaytUrl ).path + '/'; - - if( window.scayt == undefined ) - { - CKEDITOR._djScaytConfig = - { - baseUrl: scaytConfigBaseUrl, - addOnLoad: - [ - function() - { - CKEDITOR.fireOnce( 'scaytReady' ); - } - ], - isDebug: false - }; - // Append javascript code. - CKEDITOR.document.getHead().append( - CKEDITOR.document.createElement( 'script', - { - attributes : - { - type : 'text/javascript', - async : 'true', - src : scaytUrl - } - }) - ); - } - else - CKEDITOR.fireOnce( 'scaytReady' ); - - return null; - }, - parseUrl : function ( data ) - { - var match; - if ( data.match && ( match = data.match(/(.*)[\/\\](.*?\.\w+)$/) ) ) - return { path: match[1], file: match[2] }; - else - return data; - } - }; - - var plugin = CKEDITOR.plugins.scayt; - - // Context menu constructing. - var addButtonCommand = function( editor, buttonName, buttonLabel, commandName, command, menugroup, menuOrder ) - { - editor.addCommand( commandName, command ); - - // If the "menu" plugin is loaded, register the menu item. - editor.addMenuItem( commandName, - { - label : buttonLabel, - command : commandName, - group : menugroup, - order : menuOrder - }); - }; - - var commandDefinition = - { - preserveState : true, - editorFocus : false, - - exec: function( editor ) - { - var autoStartup = editor.config.scayt_autoStartup; - autoStartup = ( autoStartup == undefined ) || autoStartup; - - if ( plugin.isScaytReady( editor ) ) - { - var isEnabled = plugin.isScaytEnabled( editor ); - - this.setState( isEnabled ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_ON ); - - var scayt_control = plugin.getScayt( editor ); - // the place where the status of editor focus should be restored - // after there will be ability to store its state before SCAYT button click - // if (storedFocusState is focused ) - // scayt_control.focus(); - // - // now focus is set certainly - scayt_control.focus( ); - scayt_control.setDisabled( isEnabled ); - } - else if ( !autoStartup && plugin.engineLoaded >= 0 ) // Load first time - { - this.setState( CKEDITOR.TRISTATE_DISABLED ); - plugin.loadEngine( editor ); - } - } - }; - - // Add scayt plugin. - CKEDITOR.plugins.add( 'scayt', - { - requires : [ 'menubutton' ], - - beforeInit : function( editor ) - { - var items_order = editor.config.scayt_contextMenuItemsOrder - || 'suggest|moresuggest|control', - items_order_str = ""; - - items_order = items_order.split( '|' ); - - if ( items_order && items_order.length ) - { - for ( var pos in items_order ) - items_order_str += 'scayt_' + items_order[ pos ] + ( items_order.length != parseInt( pos, 10 ) + 1 ? ',' : '' ); - } - - // Register scayt rbc menu group. - if ( editor.config.scayt_contextMenuOntop ) - // Put it on top of all context menu items - editor.config.menu_groups = items_order_str + ',' + editor.config.menu_groups; - else - // Put it down - editor.config.menu_groups = editor.config.menu_groups + ',' +items_order_str; - }, - - init : function( editor ) - { - var moreSuggestions = {}; - var mainSuggestions = {}; - - // Scayt command. - var command = editor.addCommand( commandName, commandDefinition ); - - // Add Options dialog. - CKEDITOR.dialog.add( commandName, CKEDITOR.getUrl( this.path + 'dialogs/options.js' ) ); - // read ui tags - var confuiTabs = editor.config.scayt_uiTabs || '1,1,1'; - var uiTabs =[]; - // string to array convert - confuiTabs = confuiTabs.split( ',' ); - // check array length ! always must be 3 filled with 1 or 0 - for (var i=0,l=3; i<l; i++) - { - var flag = parseInt(confuiTabs[i] || '1' ,10); - uiTabs.push( flag ); - } - - var menuGroup = 'scaytButton'; - editor.addMenuGroup( menuGroup ); - // combine menu items to render - var uiMuneItems = {}; - - // always added - uiMuneItems.scaytToggle = - { - label : editor.lang.scayt.enable, - command : commandName, - group : menuGroup - }; - - if (uiTabs[0] == 1) - uiMuneItems.scaytOptions = - { - label : editor.lang.scayt.options, - group : menuGroup, - onClick : function() - { - openPage = 'options'; - editor.openDialog( commandName ); - } - }; - - if (uiTabs[1] == 1) - uiMuneItems.scaytLangs = - { - label : editor.lang.scayt.langs, - group : menuGroup, - onClick : function() - { - openPage = 'langs'; - editor.openDialog( commandName ); - } - }; - if (uiTabs[2] == 1) - uiMuneItems.scaytDict = - { - label : editor.lang.scayt.dictionariesTab, - group : menuGroup, - onClick : function() - { - openPage = 'dictionaries'; - editor.openDialog( commandName ); - } - }; - // always added - uiMuneItems.scaytAbout = - { - label : editor.lang.scayt.about, - group : menuGroup, - onClick : function() - { - openPage = 'about'; - editor.openDialog( commandName ); - } - } - ; - - uiTabs[3] = 1; // about us tab is always on - plugin.uiTabs = uiTabs; - - editor.addMenuItems( uiMuneItems ); - - editor.ui.add( 'Scayt', CKEDITOR.UI_MENUBUTTON, - { - label : editor.lang.scayt.title, - title : CKEDITOR.env.opera ? editor.lang.scayt.opera_title : editor.lang.scayt.title, - className : 'cke_button_scayt', - onRender: function() - { - command.on( 'state', function() - { - this.setState( command.state ); - }, - this); - }, - onMenu : function() - { - var isEnabled = plugin.isScaytEnabled( editor ); - - editor.getMenuItem( 'scaytToggle' ).label = editor.lang.scayt[ isEnabled ? 'disable' : 'enable' ]; - - return { - scaytToggle : CKEDITOR.TRISTATE_OFF, - scaytOptions : isEnabled && plugin.uiTabs[0] ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED, - scaytLangs : isEnabled && plugin.uiTabs[1] ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED, - scaytDict : isEnabled && plugin.uiTabs[2] ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED, - scaytAbout : isEnabled && plugin.uiTabs[3] ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED - }; - } - }); - - // If the "contextmenu" plugin is loaded, register the listeners. - if ( editor.contextMenu && editor.addMenuItems ) - { - editor.contextMenu.addListener( function( ) - { - if ( !plugin.isScaytEnabled( editor ) ) - return null; - - var scayt_control = plugin.getScayt( editor ), - node = scayt_control.getScaytNode(); - - if ( !node ) - return null; - - var word = scayt_control.getWord( node ); - - if ( !word ) - return null; - - var sLang = scayt_control.getLang(), - _r = {}, - items_suggestion = window.scayt.getSuggestion( word, sLang ); - if ( !items_suggestion || !items_suggestion.length ) - return null; - // Remove unused commands and menuitems - for ( i in moreSuggestions ) - { - delete editor._.menuItems[ i ]; - delete editor._.commands[ i ]; - } - for ( i in mainSuggestions ) - { - delete editor._.menuItems[ i ]; - delete editor._.commands[ i ]; - } - moreSuggestions = {}; // Reset items. - mainSuggestions = {}; - - var moreSuggestionsUnable = editor.config.scayt_moreSuggestions || 'on'; - var moreSuggestionsUnableAdded = false; - - var maxSuggestions = editor.config.scayt_maxSuggestions; - ( typeof maxSuggestions != 'number' ) && ( maxSuggestions = 5 ); - !maxSuggestions && ( maxSuggestions = items_suggestion.length ); - - var contextCommands = editor.config.scayt_contextCommands || 'all'; - contextCommands = contextCommands.split( '|' ); - - for ( var i = 0, l = items_suggestion.length; i < l; i += 1 ) - { - var commandName = 'scayt_suggestion_' + items_suggestion[i].replace( ' ', '_' ); - var exec = ( function( el, s ) - { - return { - exec: function() - { - scayt_control.replace(el, s); - } - }; - })( node, items_suggestion[i] ); - - if ( i < maxSuggestions ) - { - addButtonCommand( editor, 'button_' + commandName, items_suggestion[i], - commandName, exec, 'scayt_suggest', i + 1 ); - _r[ commandName ] = CKEDITOR.TRISTATE_OFF; - mainSuggestions[ commandName ] = CKEDITOR.TRISTATE_OFF; - } - else if ( moreSuggestionsUnable == 'on' ) - { - addButtonCommand( editor, 'button_' + commandName, items_suggestion[i], - commandName, exec, 'scayt_moresuggest', i + 1 ); - moreSuggestions[ commandName ] = CKEDITOR.TRISTATE_OFF; - moreSuggestionsUnableAdded = true; - } - } - - if ( moreSuggestionsUnableAdded ) - { - // Register the More suggestions group; - editor.addMenuItem( 'scayt_moresuggest', - { - label : editor.lang.scayt.moreSuggestions, - group : 'scayt_moresuggest', - order : 10, - getItems : function() - { - return moreSuggestions; - } - }); - mainSuggestions[ 'scayt_moresuggest' ] = CKEDITOR.TRISTATE_OFF; - } - - if ( in_array( 'all', contextCommands ) || in_array( 'ignore', contextCommands) ) - { - var ignore_command = { - exec: function(){ - scayt_control.ignore( node ); - } - }; - addButtonCommand( editor, 'ignore', editor.lang.scayt.ignore, 'scayt_ignore', ignore_command, 'scayt_control', 1 ); - mainSuggestions[ 'scayt_ignore' ] = CKEDITOR.TRISTATE_OFF; - } - - if ( in_array( 'all', contextCommands ) || in_array( 'ignoreall', contextCommands ) ) - { - var ignore_all_command = { - exec: function(){ - scayt_control.ignoreAll( node ); - } - }; - addButtonCommand(editor, 'ignore_all', editor.lang.scayt.ignoreAll, 'scayt_ignore_all', ignore_all_command, 'scayt_control', 2); - mainSuggestions['scayt_ignore_all'] = CKEDITOR.TRISTATE_OFF; - } - - if ( in_array( 'all', contextCommands ) || in_array( 'add', contextCommands ) ) - { - var addword_command = { - exec: function(){ - window.scayt.addWordToUserDictionary( node ); - } - }; - addButtonCommand(editor, 'add_word', editor.lang.scayt.addWord, 'scayt_add_word', addword_command, 'scayt_control', 3); - mainSuggestions['scayt_add_word'] = CKEDITOR.TRISTATE_OFF; - } - - if ( scayt_control.fireOnContextMenu ) - scayt_control.fireOnContextMenu( editor ); - - return mainSuggestions; - }); - } - - var showInitialState = function() - { - editor.removeListener( 'showScaytState', showInitialState ); - - if ( !CKEDITOR.env.opera ) - command.setState( plugin.isScaytEnabled( editor ) ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF ); - else - command.setState( CKEDITOR.TRISTATE_DISABLED ); - }; - - editor.on( 'showScaytState', showInitialState ); - - if ( CKEDITOR.env.opera ) - { - editor.on( 'instanceReady', function() - { - showInitialState(); - }); - } - - // Start plugin - var autoStartup = editor.config.scayt_autoStartup; - if ( ( autoStartup == undefined ) || autoStartup ) - { - editor.on( 'instanceReady', function() - { - plugin.loadEngine( editor ); - }); - } - }, - - afterInit : function( editor ) - { - // Prevent word marker line from displaying in elements path and been removed when cleaning format. (#3570) (#4125) - var elementsPathFilters, - scaytFilter = function( element ) - { - if ( element.hasAttribute( 'scaytid' ) ) - return false; - }; - - if ( editor._.elementsPath && ( elementsPathFilters = editor._.elementsPath.filters ) ) - elementsPathFilters.push( scaytFilter ); - - editor.addRemoveFormatFilter && editor.addRemoveFormatFilter( scaytFilter ); - - } - }); -})(); - -/** - * If enabled (true), turns on SCAYT automatically after loading the editor. - * @name CKEDITOR.config.scayt_autoStartup - * @type Boolean - * @default true - * @example - * config.scayt_autoStartup = false; - */ - -/** - * Defines the number of SCAYT suggestions to show in the main context menu. - * The possible values are: - * <ul> - * <li>0 (zero): All suggestions are displayed in the main context menu.</li> - * <li>Positive number: The maximum number of suggestions to shown in context - * menu. Other entries will be shown in "More Suggestions" sub-menu.</li> - * <li>Negative number: No suggestions are shown in the main context menu. All - * entries will be listed in the "Suggestions" sub-menu.</li> - * </ul> - * @name CKEDITOR.config.scayt_maxSuggestions - * @type Number - * @default 5 - * @example - * // Display only three suggestions in the main context menu. - * config.scayt_maxSuggestions = 3; - * @example - * // Do not show the suggestions directly. - * config.scayt_maxSuggestions = -1; - */ - -/** - * Sets the customer ID for SCAYT. Required for migration from free version - * with banner to paid version. - * @name CKEDITOR.config.scayt_customerid - * @type String - * @default '' - * @example - * // Load SCAYT using my customer ID. - * config.scayt_customerid = 'your-encrypted-customer-id'; - */ - -/** - * Enables/disables the "More Suggestions" sub-menu in the context menu. - * The possible values are "on" or "off". - * @name CKEDITOR.config.scayt_moreSuggestions - * @type String - * @default 'on' - * @example - * // Disables the "More Suggestions" sub-menu. - * config.scayt_moreSuggestions = 'off'; - */ - -/** - * Customizes the display of SCAYT context menu commands ("Add Word", "Ignore" - * and "Ignore All"). It must be a string with one or more of the following - * words separated by a pipe ("|"): - * <ul> - * <li>"off": disables all options.</li> - * <li>"all": enables all options.</li> - * <li>"ignore": enables the "Ignore" option.</li> - * <li>"ignoreall": enables the "Ignore All" option.</li> - * <li>"add": enables the "Add Word" option.</li> - * </ul> - * @name CKEDITOR.config.scayt_contextCommands - * @type String - * @default 'all' - * @example - * // Show only "Add Word" and "Ignore All" in the context menu. - * config.scayt_contextCommands = 'add|ignoreall'; - */ - -/** - * Sets the default spellchecking language for SCAYT. - * @name CKEDITOR.config.scayt_sLang - * @type String - * @default 'en_US' - * @example - * // Sets SCAYT to German. - * config.scayt_sLang = 'de_DE'; - */ - -/** - * Sets the visibility of the SCAYT tabs in the settings dialog and toolbar - * button. The value must contain a "1" (enabled) or "0" (disabled) number for - * each of the following entries, in this precise order, separated by a - * comma (","): "Options", "Languages" and "Dictionary". - * @name CKEDITOR.config.scayt_uiTabs - * @type String - * @default '1,1,1' - * @example - * // Hide the "Languages" tab. - * config.scayt_uiTabs = '1,0,1'; - */ - - -/** - * Set the URL to SCAYT core. Required to switch to licensed version of SCAYT application. - * Further details at http://wiki.spellchecker.net/doku.php?id=3rd:wysiwyg:fckeditor:wscckf3l . - * @name CKEDITOR.config.scayt_srcUrl - * @type String - * @default '' - * @example - * config.scayt_srcUrl = "http://my-host/spellcheck/lf/scayt/scayt.js"; - */ - -/** - * Links SCAYT to custom dictionaries. It's a string containing dictionary ids - * separared by commas (","). Available only for licensed version. - * Further details at http://wiki.spellchecker.net/doku.php?id=custom_dictionary_support . - * @name CKEDITOR.config.scayt_customDictionaryIds - * @type String - * @default '' - * @example - * config.scayt_customDictionaryIds = '3021,3456,3478"'; - */ - -/** - * Makes it possible to activate a custom dictionary on SCAYT. The user - * dictionary name must be used. Available only for licensed version. - * @name CKEDITOR.config.scayt_userDictionaryName - * @type String - * @default '' - * @example - * config.scayt_userDictionaryName = 'MyDictionary'; - */ - -/** - * Makes it possible to place the SCAYT context menu items above others. - * @name CKEDITOR.config.scayt_contextMenuOntop - * @type Boolean - * @default false - * @example - * config.scayt_contextMenuOntop = true; - */ - -/** - * Define order of placing of SCAYT context menu items by groups. - * It must be a string with one or more of the following - * words separated by a pipe ("|"): - * <ul> - * <li>'suggest' - main suggestion word list,</li> - * <li>'moresuggest' - more suggestions word list,</li> - * <li>'control' - SCAYT commands, such as 'Ignore' and 'Add Word'</li> - * </ul> - * - * @name CKEDITOR.config.scayt_contextMenuItemsOrder - * @type String - * @default 'suggest|moresuggest|control' - * @example - * config.scayt_contextMenuItemsOrder = 'moresuggest|control|suggest'; - */ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Spell Check As You Type (SCAYT).
+ * Button name : Scayt.
+ */
+
+(function()
+{
+ var commandName = 'scaytcheck',
+ openPage = '';
+
+ // Checks if a value exists in an array
+ function in_array( needle, haystack )
+ {
+ var found = 0,
+ key;
+ for ( key in haystack )
+ {
+ if ( haystack[ key ] == needle )
+ {
+ found = 1;
+ break;
+ }
+ }
+ return found;
+ }
+
+ var onEngineLoad = function()
+ {
+ var editor = this;
+
+ var createInstance = function() // Create new instance every time Document is created.
+ {
+ var config = editor.config;
+ // Initialise Scayt instance.
+ var oParams = {};
+ // Get the iframe.
+ oParams.srcNodeRef = editor.document.getWindow().$.frameElement;
+ // syntax : AppName.AppVersion@AppRevision
+ oParams.assocApp = 'CKEDITOR.' + CKEDITOR.version + '@' + CKEDITOR.revision;
+ oParams.customerid = config.scayt_customerid || '1:WvF0D4-UtPqN1-43nkD4-NKvUm2-daQqk3-LmNiI-z7Ysb4-mwry24-T8YrS3-Q2tpq2';
+ oParams.customDictionaryIds = config.scayt_customDictionaryIds || '';
+ oParams.userDictionaryName = config.scayt_userDictionaryName || '';
+ oParams.sLang = config.scayt_sLang || 'en_US';
+
+ // Introduce SCAYT onLoad callback. (#5632)
+ oParams.onLoad = function()
+ {
+ // Draw down word marker to avoid being covered by background-color style.(#5466)
+ if ( !( CKEDITOR.env.ie && CKEDITOR.env.version < 8 ) )
+ this.addStyle( this.selectorCss(), 'padding-bottom: 2px !important;' );
+
+ // Call scayt_control.focus when SCAYT loaded
+ // and only if editor has focus and scayt control creates at first time (#5720)
+ if ( editor.focusManager.hasFocus && !plugin.isControlRestored( editor ) )
+ this.focus();
+
+ };
+
+ oParams.onBeforeChange = function()
+ {
+ if ( plugin.getScayt( editor ) && !editor.checkDirty() )
+ setTimeout( function(){ editor.resetDirty(); }, 0 );
+ };
+
+ var scayt_custom_params = window.scayt_custom_params;
+ if ( typeof scayt_custom_params == 'object' )
+ {
+ for ( var k in scayt_custom_params )
+ oParams[ k ] = scayt_custom_params[ k ];
+ }
+ // needs for restoring a specific scayt control settings
+ if ( plugin.getControlId( editor ) )
+ oParams.id = plugin.getControlId( editor );
+
+ var scayt_control = new window.scayt( oParams );
+
+ scayt_control.afterMarkupRemove.push( function( node )
+ {
+ ( new CKEDITOR.dom.element( node, scayt_control.document ) ).mergeSiblings();
+ } );
+
+ // Copy config.
+ var lastInstance = plugin.instances[ editor.name ];
+ if ( lastInstance )
+ {
+ scayt_control.sLang = lastInstance.sLang;
+ scayt_control.option( lastInstance.option() );
+ scayt_control.paused = lastInstance.paused;
+ }
+
+ plugin.instances[ editor.name ] = scayt_control;
+
+ try {
+ scayt_control.setDisabled( plugin.isPaused( editor ) === false );
+ } catch (e) {}
+
+ editor.fire( 'showScaytState' );
+ };
+
+ editor.on( 'contentDom', createInstance );
+ editor.on( 'contentDomUnload', function()
+ {
+ // Remove scripts.
+ var scripts = CKEDITOR.document.getElementsByTag( 'script' ),
+ scaytIdRegex = /^dojoIoScript(\d+)$/i,
+ scaytSrcRegex = /^https?:\/\/svc\.webspellchecker\.net\/spellcheck\/script\/ssrv\.cgi/i;
+
+ for ( var i=0; i < scripts.count(); i++ )
+ {
+ var script = scripts.getItem( i ),
+ id = script.getId(),
+ src = script.getAttribute( 'src' );
+
+ if ( id && src && id.match( scaytIdRegex ) && src.match( scaytSrcRegex ))
+ script.remove();
+ }
+ });
+
+ editor.on( 'beforeCommandExec', function( ev ) // Disable SCAYT before Source command execution.
+ {
+ if ( ( ev.data.name == 'source' || ev.data.name == 'newpage' ) && editor.mode == 'wysiwyg' )
+ {
+ var scayt_instance = plugin.getScayt( editor );
+ if ( scayt_instance )
+ {
+ plugin.setPaused( editor, !scayt_instance.disabled );
+ // store a control id for restore a specific scayt control settings
+ plugin.setControlId( editor, scayt_instance.id );
+ scayt_instance.destroy( true );
+ delete plugin.instances[ editor.name ];
+ }
+ }
+ // Catch on source mode switch off (#5720)
+ else if ( ev.data.name == 'source' && editor.mode == 'source' )
+ plugin.markControlRestore( editor );
+ });
+
+ editor.on( 'afterCommandExec', function( ev )
+ {
+ if ( !plugin.isScaytEnabled( editor ) )
+ return;
+
+ if ( editor.mode == 'wysiwyg' && ( ev.data.name == 'undo' || ev.data.name == 'redo' ) )
+ window.setTimeout( function() { plugin.getScayt( editor ).refresh(); }, 10 );
+ });
+
+ editor.on( 'destroy', function( ev )
+ {
+ var editor = ev.editor,
+ scayt_instance = plugin.getScayt( editor );
+
+ // SCAYT instance might already get destroyed by mode switch (#5744).
+ if ( !scayt_instance )
+ return;
+
+ delete plugin.instances[ editor.name ];
+ // store a control id for restore a specific scayt control settings
+ plugin.setControlId( editor, scayt_instance.id );
+ scayt_instance.destroy( true );
+ });
+
+ // Listen to data manipulation to reflect scayt markup.
+ editor.on( 'afterSetData', function()
+ {
+ if ( plugin.isScaytEnabled( editor ) ) {
+ window.setTimeout( function()
+ {
+ var instance = plugin.getScayt( editor );
+ instance && instance.refresh();
+ }, 10 );
+ }
+ });
+
+ // Reload spell-checking for current word after insertion completed.
+ editor.on( 'insertElement', function()
+ {
+ var scayt_instance = plugin.getScayt( editor );
+ if ( plugin.isScaytEnabled( editor ) )
+ {
+ // Unlock the selection before reload, SCAYT will take
+ // care selection update.
+ if ( CKEDITOR.env.ie )
+ editor.getSelection().unlock( true );
+
+ // Return focus to the editor and refresh SCAYT markup (#5573).
+ window.setTimeout( function()
+ {
+ scayt_instance.focus();
+ scayt_instance.refresh();
+ }, 10 );
+ }
+ }, this, null, 50 );
+
+ editor.on( 'insertHtml', function()
+ {
+ var scayt_instance = plugin.getScayt( editor );
+ if ( plugin.isScaytEnabled( editor ) )
+ {
+ // Unlock the selection before reload, SCAYT will take
+ // care selection update.
+ if ( CKEDITOR.env.ie )
+ editor.getSelection().unlock( true );
+
+ // Return focus to the editor (#5573)
+ // Refresh SCAYT markup
+ window.setTimeout( function()
+ {
+ scayt_instance.focus();
+ scayt_instance.refresh();
+ }, 10 );
+ }
+ }, this, null, 50 );
+
+ editor.on( 'scaytDialog', function( ev ) // Communication with dialog.
+ {
+ ev.data.djConfig = window.djConfig;
+ ev.data.scayt_control = plugin.getScayt( editor );
+ ev.data.tab = openPage;
+ ev.data.scayt = window.scayt;
+ });
+
+ var dataProcessor = editor.dataProcessor,
+ htmlFilter = dataProcessor && dataProcessor.htmlFilter;
+
+ if ( htmlFilter )
+ {
+ htmlFilter.addRules(
+ {
+ elements :
+ {
+ span : function( element )
+ {
+ if ( element.attributes[ 'data-scayt_word' ]
+ && element.attributes[ 'data-scaytid' ] )
+ {
+ delete element.name; // Write children, but don't write this node.
+ return element;
+ }
+ }
+ }
+ }
+ );
+ }
+
+ // Override Image.equals method avoid CK snapshot module to add SCAYT markup to snapshots. (#5546)
+ var undoImagePrototype = CKEDITOR.plugins.undo.Image.prototype;
+ undoImagePrototype.equals = CKEDITOR.tools.override( undoImagePrototype.equals, function( org )
+ {
+ return function( otherImage )
+ {
+ var thisContents = this.contents,
+ otherContents = otherImage.contents;
+ var scayt_instance = plugin.getScayt( this.editor );
+ // Making the comparison based on content without SCAYT word markers.
+ if ( scayt_instance && plugin.isScaytReady( this.editor ) )
+ {
+ // scayt::reset might return value undefined. (#5742)
+ this.contents = scayt_instance.reset( thisContents ) || '';
+ otherImage.contents = scayt_instance.reset( otherContents ) || '';
+ }
+
+ var retval = org.apply( this, arguments );
+
+ this.contents = thisContents;
+ otherImage.contents = otherContents;
+ return retval;
+ };
+ });
+
+ if ( editor.document )
+ createInstance();
+ };
+
+CKEDITOR.plugins.scayt =
+ {
+ engineLoaded : false,
+ instances : {},
+ // Data storage for SCAYT control, based on editor instances
+ controlInfo : {},
+ setControlInfo : function( editor, o )
+ {
+ if ( editor && editor.name && typeof ( this.controlInfo[ editor.name ] ) != 'object' )
+ this.controlInfo[ editor.name ] = {};
+
+ for ( var infoOpt in o )
+ this.controlInfo[ editor.name ][ infoOpt ] = o[ infoOpt ];
+ },
+ isControlRestored : function( editor )
+ {
+ if ( editor &&
+ editor.name &&
+ this.controlInfo[ editor.name ] )
+ {
+ return this.controlInfo[ editor.name ].restored ;
+ }
+ return false;
+ },
+ markControlRestore : function( editor )
+ {
+ this.setControlInfo( editor, { restored:true } );
+ },
+ setControlId: function( editor, id )
+ {
+ this.setControlInfo( editor, { id:id } );
+ },
+ getControlId: function( editor )
+ {
+ if ( editor &&
+ editor.name &&
+ this.controlInfo[ editor.name ] &&
+ this.controlInfo[ editor.name ].id )
+ {
+ return this.controlInfo[ editor.name ].id;
+ }
+ return null;
+ },
+ setPaused: function( editor , bool )
+ {
+ this.setControlInfo( editor, { paused:bool } );
+ },
+ isPaused: function( editor )
+ {
+ if ( editor &&
+ editor.name &&
+ this.controlInfo[editor.name] )
+ {
+ return this.controlInfo[editor.name].paused;
+ }
+ return undefined;
+ },
+ getScayt : function( editor )
+ {
+ return this.instances[ editor.name ];
+ },
+ isScaytReady : function( editor )
+ {
+ return this.engineLoaded === true &&
+ 'undefined' !== typeof window.scayt && this.getScayt( editor );
+ },
+ isScaytEnabled : function( editor )
+ {
+ var scayt_instance = this.getScayt( editor );
+ return ( scayt_instance ) ? scayt_instance.disabled === false : false;
+ },
+ getUiTabs : function( editor )
+ {
+ var uiTabs = [];
+
+ // read UI tabs value from config
+ var configUiTabs = editor.config.scayt_uiTabs || "1,1,1";
+
+ // convert string to array
+ configUiTabs = configUiTabs.split( ',' );
+
+ // "About us" should be always shown for standard config
+ configUiTabs[3] = "1";
+
+ for ( var i = 0; i < 4; i++ ) {
+ uiTabs[i] = (typeof window.scayt != "undefined" && typeof window.scayt.uiTags != "undefined")
+ ? (parseInt(configUiTabs[i],10) && window.scayt.uiTags[i])
+ : parseInt(configUiTabs[i],10);
+ }
+ return uiTabs;
+ },
+ loadEngine : function( editor )
+ {
+ // SCAYT doesn't work with Firefox2, Opera and AIR.
+ if ( CKEDITOR.env.gecko && CKEDITOR.env.version < 10900 || CKEDITOR.env.opera || CKEDITOR.env.air )
+ return editor.fire( 'showScaytState' );
+
+ if ( this.engineLoaded === true )
+ return onEngineLoad.apply( editor ); // Add new instance.
+ else if ( this.engineLoaded == -1 ) // We are waiting.
+ return CKEDITOR.on( 'scaytReady', function(){ onEngineLoad.apply( editor ); } ); // Use function(){} to avoid rejection as duplicate.
+
+ CKEDITOR.on( 'scaytReady', onEngineLoad, editor );
+ CKEDITOR.on( 'scaytReady', function()
+ {
+ this.engineLoaded = true;
+ },
+ this,
+ null,
+ 0
+ ); // First to run.
+
+ this.engineLoaded = -1; // Loading in progress.
+
+ // compose scayt url
+ var protocol = document.location.protocol;
+ // Default to 'http' for unknown.
+ protocol = protocol.search( /https?:/) != -1? protocol : 'http:';
+ var baseUrl = 'svc.webspellchecker.net/scayt26/loader__base.js';
+
+ var scaytUrl = editor.config.scayt_srcUrl || ( protocol + '//' + baseUrl );
+ var scaytConfigBaseUrl = plugin.parseUrl( scaytUrl ).path + '/';
+
+ if( window.scayt == undefined )
+ {
+ CKEDITOR._djScaytConfig =
+ {
+ baseUrl: scaytConfigBaseUrl,
+ addOnLoad:
+ [
+ function()
+ {
+ CKEDITOR.fireOnce( 'scaytReady' );
+ }
+ ],
+ isDebug: false
+ };
+ // Append javascript code.
+ CKEDITOR.document.getHead().append(
+ CKEDITOR.document.createElement( 'script',
+ {
+ attributes :
+ {
+ type : 'text/javascript',
+ async : 'true',
+ src : scaytUrl
+ }
+ })
+ );
+ }
+ else
+ CKEDITOR.fireOnce( 'scaytReady' );
+
+ return null;
+ },
+ parseUrl : function ( data )
+ {
+ var match;
+ if ( data.match && ( match = data.match(/(.*)[\/\\](.*?\.\w+)$/) ) )
+ return { path: match[1], file: match[2] };
+ else
+ return data;
+ }
+ };
+
+ var plugin = CKEDITOR.plugins.scayt;
+
+ // Context menu constructing.
+ var addButtonCommand = function( editor, buttonName, buttonLabel, commandName, command, menugroup, menuOrder )
+ {
+ editor.addCommand( commandName, command );
+
+ // If the "menu" plugin is loaded, register the menu item.
+ editor.addMenuItem( commandName,
+ {
+ label : buttonLabel,
+ command : commandName,
+ group : menugroup,
+ order : menuOrder
+ });
+ };
+
+ var commandDefinition =
+ {
+ preserveState : true,
+ editorFocus : false,
+ canUndo : false,
+
+ exec: function( editor )
+ {
+ if ( plugin.isScaytReady( editor ) )
+ {
+ var isEnabled = plugin.isScaytEnabled( editor );
+
+ this.setState( isEnabled ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_ON );
+
+ var scayt_control = plugin.getScayt( editor );
+ // the place where the status of editor focus should be restored
+ // after there will be ability to store its state before SCAYT button click
+ // if (storedFocusState is focused )
+ // scayt_control.focus();
+ //
+ // now focus is set certainly
+ scayt_control.focus();
+ scayt_control.setDisabled( isEnabled );
+ }
+ else if ( !editor.config.scayt_autoStartup && plugin.engineLoaded >= 0 ) // Load first time
+ {
+ this.setState( CKEDITOR.TRISTATE_DISABLED );
+ plugin.loadEngine( editor );
+ }
+ }
+ };
+
+ // Add scayt plugin.
+ CKEDITOR.plugins.add( 'scayt',
+ {
+ requires : [ 'menubutton' ],
+
+ beforeInit : function( editor )
+ {
+ var items_order = editor.config.scayt_contextMenuItemsOrder
+ || 'suggest|moresuggest|control',
+ items_order_str = "";
+
+ items_order = items_order.split( '|' );
+
+ if ( items_order && items_order.length )
+ {
+ for ( var pos = 0 ; pos < items_order.length ; pos++ )
+ items_order_str += 'scayt_' + items_order[ pos ] + ( items_order.length != parseInt( pos, 10 ) + 1 ? ',' : '' );
+ }
+
+ // Put it on top of all context menu items (#5717)
+ editor.config.menu_groups = items_order_str + ',' + editor.config.menu_groups;
+ },
+
+ init : function( editor )
+ {
+ // Delete span[data-scaytid] when text pasting in editor (#6921)
+ var dataFilter = editor.dataProcessor && editor.dataProcessor.dataFilter;
+ var dataFilterRules =
+ {
+ elements :
+ {
+ span : function( element )
+ {
+ var attrs = element.attributes;
+ if ( attrs && attrs[ 'data-scaytid' ] )
+ delete element.name;
+ }
+ }
+ };
+ dataFilter && dataFilter.addRules( dataFilterRules );
+
+ var moreSuggestions = {},
+ mainSuggestions = {};
+
+ // Scayt command.
+ var command = editor.addCommand( commandName, commandDefinition );
+
+ // Add Options dialog.
+ CKEDITOR.dialog.add( commandName, CKEDITOR.getUrl( this.path + 'dialogs/options.js' ) );
+
+ var uiTabs = plugin.getUiTabs( editor );
+
+ var menuGroup = 'scaytButton';
+ editor.addMenuGroup( menuGroup );
+ // combine menu items to render
+ var uiMenuItems = {};
+
+ var lang = editor.lang.scayt;
+
+ // always added
+ uiMenuItems.scaytToggle =
+ {
+ label : lang.enable,
+ command : commandName,
+ group : menuGroup
+ };
+
+ if ( uiTabs[0] == 1 )
+ uiMenuItems.scaytOptions =
+ {
+ label : lang.options,
+ group : menuGroup,
+ onClick : function()
+ {
+ openPage = 'options';
+ editor.openDialog( commandName );
+ }
+ };
+
+ if ( uiTabs[1] == 1 )
+ uiMenuItems.scaytLangs =
+ {
+ label : lang.langs,
+ group : menuGroup,
+ onClick : function()
+ {
+ openPage = 'langs';
+ editor.openDialog( commandName );
+ }
+ };
+ if ( uiTabs[2] == 1 )
+ uiMenuItems.scaytDict =
+ {
+ label : lang.dictionariesTab,
+ group : menuGroup,
+ onClick : function()
+ {
+ openPage = 'dictionaries';
+ editor.openDialog( commandName );
+ }
+ };
+ // always added
+ uiMenuItems.scaytAbout =
+ {
+ label : editor.lang.scayt.about,
+ group : menuGroup,
+ onClick : function()
+ {
+ openPage = 'about';
+ editor.openDialog( commandName );
+ }
+ };
+
+ editor.addMenuItems( uiMenuItems );
+
+ editor.ui.add( 'Scayt', CKEDITOR.UI_MENUBUTTON,
+ {
+ label : lang.title,
+ title : CKEDITOR.env.opera ? lang.opera_title : lang.title,
+ className : 'cke_button_scayt',
+ modes : { wysiwyg : 1 },
+ onRender: function()
+ {
+ command.on( 'state', function()
+ {
+ this.setState( command.state );
+ },
+ this);
+ },
+ onMenu : function()
+ {
+ var isEnabled = plugin.isScaytEnabled( editor );
+
+ editor.getMenuItem( 'scaytToggle' ).label = lang[ isEnabled ? 'disable' : 'enable' ];
+
+ var uiTabs = plugin.getUiTabs( editor );
+
+ return {
+ scaytToggle : CKEDITOR.TRISTATE_OFF,
+ scaytOptions : isEnabled && uiTabs[0] ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
+ scaytLangs : isEnabled && uiTabs[1] ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
+ scaytDict : isEnabled && uiTabs[2] ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
+ scaytAbout : isEnabled && uiTabs[3] ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED
+ };
+ }
+ });
+
+ // If the "contextmenu" plugin is loaded, register the listeners.
+ if ( editor.contextMenu && editor.addMenuItems )
+ {
+ editor.contextMenu.addListener( function( element, selection )
+ {
+ if ( !plugin.isScaytEnabled( editor )
+ || selection.getRanges()[ 0 ].checkReadOnly() )
+ return null;
+
+ var scayt_control = plugin.getScayt( editor ),
+ node = scayt_control.getScaytNode();
+
+ if ( !node )
+ return null;
+
+ var word = scayt_control.getWord( node );
+
+ if ( !word )
+ return null;
+
+ var sLang = scayt_control.getLang(),
+ _r = {},
+ items_suggestion = window.scayt.getSuggestion( word, sLang );
+ if ( !items_suggestion || !items_suggestion.length )
+ return null;
+ // Remove unused commands and menuitems
+ for ( var m in moreSuggestions )
+ {
+ delete editor._.menuItems[ m ];
+ delete editor._.commands[ m ];
+ }
+ for ( m in mainSuggestions )
+ {
+ delete editor._.menuItems[ m ];
+ delete editor._.commands[ m ];
+ }
+ moreSuggestions = {}; // Reset items.
+ mainSuggestions = {};
+
+ var moreSuggestionsUnable = editor.config.scayt_moreSuggestions || 'on';
+ var moreSuggestionsUnableAdded = false;
+
+ var maxSuggestions = editor.config.scayt_maxSuggestions;
+ ( typeof maxSuggestions != 'number' ) && ( maxSuggestions = 5 );
+ !maxSuggestions && ( maxSuggestions = items_suggestion.length );
+
+ var contextCommands = editor.config.scayt_contextCommands || 'all';
+ contextCommands = contextCommands.split( '|' );
+
+ for ( var i = 0, l = items_suggestion.length; i < l; i += 1 )
+ {
+ var commandName = 'scayt_suggestion_' + items_suggestion[i].replace( ' ', '_' );
+ var exec = ( function( el, s )
+ {
+ return {
+ exec: function()
+ {
+ scayt_control.replace( el, s );
+ }
+ };
+ })( node, items_suggestion[i] );
+
+ if ( i < maxSuggestions )
+ {
+ addButtonCommand( editor, 'button_' + commandName, items_suggestion[i],
+ commandName, exec, 'scayt_suggest', i + 1 );
+ _r[ commandName ] = CKEDITOR.TRISTATE_OFF;
+ mainSuggestions[ commandName ] = CKEDITOR.TRISTATE_OFF;
+ }
+ else if ( moreSuggestionsUnable == 'on' )
+ {
+ addButtonCommand( editor, 'button_' + commandName, items_suggestion[i],
+ commandName, exec, 'scayt_moresuggest', i + 1 );
+ moreSuggestions[ commandName ] = CKEDITOR.TRISTATE_OFF;
+ moreSuggestionsUnableAdded = true;
+ }
+ }
+
+ if ( moreSuggestionsUnableAdded )
+ {
+ // Register the More suggestions group;
+ editor.addMenuItem( 'scayt_moresuggest',
+ {
+ label : lang.moreSuggestions,
+ group : 'scayt_moresuggest',
+ order : 10,
+ getItems : function()
+ {
+ return moreSuggestions;
+ }
+ });
+ mainSuggestions[ 'scayt_moresuggest' ] = CKEDITOR.TRISTATE_OFF;
+ }
+
+ if ( in_array( 'all', contextCommands ) || in_array( 'ignore', contextCommands) )
+ {
+ var ignore_command = {
+ exec: function(){
+ scayt_control.ignore( node );
+ }
+ };
+ addButtonCommand( editor, 'ignore', lang.ignore, 'scayt_ignore', ignore_command, 'scayt_control', 1 );
+ mainSuggestions[ 'scayt_ignore' ] = CKEDITOR.TRISTATE_OFF;
+ }
+
+ if ( in_array( 'all', contextCommands ) || in_array( 'ignoreall', contextCommands ) )
+ {
+ var ignore_all_command = {
+ exec: function(){
+ scayt_control.ignoreAll( node );
+ }
+ };
+ addButtonCommand(editor, 'ignore_all', lang.ignoreAll, 'scayt_ignore_all', ignore_all_command, 'scayt_control', 2);
+ mainSuggestions['scayt_ignore_all'] = CKEDITOR.TRISTATE_OFF;
+ }
+
+ if ( in_array( 'all', contextCommands ) || in_array( 'add', contextCommands ) )
+ {
+ var addword_command = {
+ exec: function(){
+ window.scayt.addWordToUserDictionary( node );
+ }
+ };
+ addButtonCommand(editor, 'add_word', lang.addWord, 'scayt_add_word', addword_command, 'scayt_control', 3);
+ mainSuggestions['scayt_add_word'] = CKEDITOR.TRISTATE_OFF;
+ }
+
+ if ( scayt_control.fireOnContextMenu )
+ scayt_control.fireOnContextMenu( editor );
+
+ return mainSuggestions;
+ });
+ }
+
+ var showInitialState = function()
+ {
+ editor.removeListener( 'showScaytState', showInitialState );
+
+ if ( !CKEDITOR.env.opera && !CKEDITOR.env.air )
+ command.setState( plugin.isScaytEnabled( editor ) ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF );
+ else
+ command.setState( CKEDITOR.TRISTATE_DISABLED );
+ };
+
+ editor.on( 'showScaytState', showInitialState );
+
+ if ( CKEDITOR.env.opera || CKEDITOR.env.air )
+ {
+ editor.on( 'instanceReady', function()
+ {
+ showInitialState();
+ });
+ }
+
+ // Start plugin
+ if ( editor.config.scayt_autoStartup )
+ {
+ editor.on( 'instanceReady', function()
+ {
+ plugin.loadEngine( editor );
+ });
+ }
+ },
+
+ afterInit : function( editor )
+ {
+ // Prevent word marker line from displaying in elements path and been removed when cleaning format. (#3570) (#4125)
+ var elementsPathFilters,
+ scaytFilter = function( element )
+ {
+ if ( element.hasAttribute( 'data-scaytid' ) )
+ return false;
+ };
+
+ if ( editor._.elementsPath && ( elementsPathFilters = editor._.elementsPath.filters ) )
+ elementsPathFilters.push( scaytFilter );
+
+ editor.addRemoveFormatFilter && editor.addRemoveFormatFilter( scaytFilter );
+
+ }
+ });
+})();
+
+/**
+ * If enabled (set to <code>true</code>), turns on SCAYT automatically
+ * after loading the editor.
+ * @name CKEDITOR.config.scayt_autoStartup
+ * @type Boolean
+ * @default <code>false</code>
+ * @example
+ * config.scayt_autoStartup = true;
+ */
+
+/**
+ * Defines the number of SCAYT suggestions to show in the main context menu.
+ * Possible values are:
+ * <ul>
+ * <li><code>0</code> (zero) – All suggestions are displayed in the main context menu.</li>
+ * <li>Positive number – The maximum number of suggestions to show in the context
+ * menu. Other entries will be shown in the "More Suggestions" sub-menu.</li>
+ * <li>Negative number – No suggestions are shown in the main context menu. All
+ * entries will be listed in the the "Suggestions" sub-menu.</li>
+ * </ul>
+ * @name CKEDITOR.config.scayt_maxSuggestions
+ * @type Number
+ * @default <code>5</code>
+ * @example
+ * // Display only three suggestions in the main context menu.
+ * config.scayt_maxSuggestions = 3;
+ * @example
+ * // Do not show the suggestions directly.
+ * config.scayt_maxSuggestions = -1;
+ */
+
+/**
+ * Sets the customer ID for SCAYT. Required for migration from free,
+ * ad-supported version to paid, ad-free version.
+ * @name CKEDITOR.config.scayt_customerid
+ * @type String
+ * @default <code>''</code>
+ * @example
+ * // Load SCAYT using my customer ID.
+ * config.scayt_customerid = 'your-encrypted-customer-id';
+ */
+
+/**
+ * Enables/disables the "More Suggestions" sub-menu in the context menu.
+ * Possible values are <code>on</code> and <code>off</code>.
+ * @name CKEDITOR.config.scayt_moreSuggestions
+ * @type String
+ * @default <code>'on'</code>
+ * @example
+ * // Disables the "More Suggestions" sub-menu.
+ * config.scayt_moreSuggestions = 'off';
+ */
+
+/**
+ * Customizes the display of SCAYT context menu commands ("Add Word", "Ignore"
+ * and "Ignore All"). This must be a string with one or more of the following
+ * words separated by a pipe character ("|"):
+ * <ul>
+ * <li><code>off</code> – disables all options.</li>
+ * <li><code>all</code> – enables all options.</li>
+ * <li><code>ignore</code> – enables the "Ignore" option.</li>
+ * <li><code>ignoreall</code> – enables the "Ignore All" option.</li>
+ * <li><code>add</code> – enables the "Add Word" option.</li>
+ * </ul>
+ * @name CKEDITOR.config.scayt_contextCommands
+ * @type String
+ * @default <code>'all'</code>
+ * @example
+ * // Show only "Add Word" and "Ignore All" in the context menu.
+ * config.scayt_contextCommands = 'add|ignoreall';
+ */
+
+/**
+ * Sets the default spell checking language for SCAYT. Possible values are:
+ * <code>en_US</code>, <code>en_GB</code>, <code>pt_BR</code>, <code>da_DK</code>,
+ * <code>nl_NL</code>, <code>en_CA</code>, <code>fi_FI</code>, <code>fr_FR</code>,
+ * <code>fr_CA</code>, <code>de_DE</code>, <code>el_GR</code>, <code>it_IT</code>,
+ * <code>nb_NO</code>, <code>pt_PT</code>, <code>es_ES</code>, <code>sv_SE</code>.
+ * @name CKEDITOR.config.scayt_sLang
+ * @type String
+ * @default <code>'en_US'</code>
+ * @example
+ * // Sets SCAYT to German.
+ * config.scayt_sLang = 'de_DE';
+ */
+
+/**
+ * Sets the visibility of particular tabs in the SCAYT dialog window and toolbar
+ * button. This setting must contain a <code>1</code> (enabled) or <code>0</code>
+ * (disabled) value for each of the following entries, in this precise order,
+ * separated by a comma (","): "Options", "Languages", and "Dictionary".
+ * @name CKEDITOR.config.scayt_uiTabs
+ * @type String
+ * @default <code>'1,1,1'</code>
+ * @example
+ * // Hides the "Languages" tab.
+ * config.scayt_uiTabs = '1,0,1';
+ */
+
+
+/**
+ * Sets the URL to SCAYT core. Required to switch to the licensed version of SCAYT application.
+ * Further details available at
+ * <a href="http://wiki.webspellchecker.net/doku.php?id=migration:hosredfreetolicensedck">
+ * http://wiki.webspellchecker.net/doku.php?id=migration:hosredfreetolicensedck</a>.
+ * @name CKEDITOR.config.scayt_srcUrl
+ * @type String
+ * @default <code>''</code>
+ * @example
+ * config.scayt_srcUrl = "http://my-host/spellcheck/lf/scayt/scayt.js";
+ */
+
+/**
+ * Links SCAYT to custom dictionaries. This is a string containing dictionary IDs
+ * separared by commas (","). Available only for the licensed version.
+ * Further details at
+ * <a href="http://wiki.webspellchecker.net/doku.php?id=installationandconfiguration:customdictionaries:licensed">
+ * http://wiki.webspellchecker.net/doku.php?id=installationandconfiguration:customdictionaries:licensed</a>.
+ * @name CKEDITOR.config.scayt_customDictionaryIds
+ * @type String
+ * @default <code>''</code>
+ * @example
+ * config.scayt_customDictionaryIds = '3021,3456,3478"';
+ */
+
+/**
+ * Makes it possible to activate a custom dictionary in SCAYT. The user
+ * dictionary name must be used. Available only for the licensed version.
+ * @name CKEDITOR.config.scayt_userDictionaryName
+ * @type String
+ * @default <code>''</code>
+ * @example
+ * config.scayt_userDictionaryName = 'MyDictionary';
+ */
+
+/**
+ * Defines the order SCAYT context menu items by groups.
+ * This must be a string with one or more of the following
+ * words separated by a pipe character ("|"):
+ * <ul>
+ * <li><code>suggest</code> – main suggestion word list,</li>
+ * <li><code>moresuggest</code> – more suggestions word list,</li>
+ * <li><code>control</code> – SCAYT commands, such as "Ignore" and "Add Word".</li>
+ * </ul>
+ *
+ * @name CKEDITOR.config.scayt_contextMenuItemsOrder
+ * @type String
+ * @default <code>'suggest|moresuggest|control'</code>
+ * @example
+ * config.scayt_contextMenuItemsOrder = 'moresuggest|control|suggest';
+ */
diff --git a/_source/plugins/selection/plugin.js b/_source/plugins/selection/plugin.js index d7b4627..aff5ba2 100644 --- a/_source/plugins/selection/plugin.js +++ b/_source/plugins/selection/plugin.js @@ -1,1169 +1,1855 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -(function() -{ - // #### checkSelectionChange : START - - // The selection change check basically saves the element parent tree of - // the current node and check it on successive requests. If there is any - // change on the tree, then the selectionChange event gets fired. - function checkSelectionChange() - { - try - { - // In IE, the "selectionchange" event may still get thrown when - // releasing the WYSIWYG mode, so we need to check it first. - var sel = this.getSelection(); - if ( !sel ) - return; - - var firstElement = sel.getStartElement(); - var currentPath = new CKEDITOR.dom.elementPath( firstElement ); - - if ( !currentPath.compare( this._.selectionPreviousPath ) ) - { - this._.selectionPreviousPath = currentPath; - this.fire( 'selectionChange', { selection : sel, path : currentPath, element : firstElement } ); - } - } - catch (e) - {} - } - - var checkSelectionChangeTimer, - checkSelectionChangeTimeoutPending; - - function checkSelectionChangeTimeout() - { - // Firing the "OnSelectionChange" event on every key press started to - // be too slow. This function guarantees that there will be at least - // 200ms delay between selection checks. - - checkSelectionChangeTimeoutPending = true; - - if ( checkSelectionChangeTimer ) - return; - - checkSelectionChangeTimeoutExec.call( this ); - - checkSelectionChangeTimer = CKEDITOR.tools.setTimeout( checkSelectionChangeTimeoutExec, 200, this ); - } - - function checkSelectionChangeTimeoutExec() - { - checkSelectionChangeTimer = null; - - if ( checkSelectionChangeTimeoutPending ) - { - // Call this with a timeout so the browser properly moves the - // selection after the mouseup. It happened that the selection was - // being moved after the mouseup when clicking inside selected text - // with Firefox. - CKEDITOR.tools.setTimeout( checkSelectionChange, 0, this ); - - checkSelectionChangeTimeoutPending = false; - } - } - - // #### checkSelectionChange : END - - var selectAllCmd = - { - modes : { wysiwyg : 1, source : 1 }, - exec : function( editor ) - { - switch ( editor.mode ) - { - case 'wysiwyg' : - editor.document.$.execCommand( 'SelectAll', false, null ); - break; - case 'source' : - // Select the contents of the textarea - var textarea = editor.textarea.$ ; - if ( CKEDITOR.env.ie ) - { - textarea.createTextRange().execCommand( 'SelectAll' ) ; - } - else - { - textarea.selectionStart = 0 ; - textarea.selectionEnd = textarea.value.length ; - } - textarea.focus() ; - } - }, - canUndo : false - }; - - CKEDITOR.plugins.add( 'selection', - { - init : function( editor ) - { - editor.on( 'contentDom', function() - { - var doc = editor.document, - body = doc.getBody(); - - if ( CKEDITOR.env.ie ) - { - // Other browsers don't loose the selection if the - // editor document loose the focus. In IE, we don't - // have support for it, so we reproduce it here, other - // than firing the selection change event. - - var savedRange, - saveEnabled; - - // "onfocusin" is fired before "onfocus". It makes it - // possible to restore the selection before click - // events get executed. - body.on( 'focusin', function( evt ) - { - // If there are elements with layout they fire this event but - // it must be ignored to allow edit its contents #4682 - if ( evt.data.$.srcElement.nodeName != 'BODY' ) - return; - - // If we have saved a range, restore it at this - // point. - if ( savedRange ) - { - // Well not break because of this. - try - { - savedRange.select(); - } - catch (e) - {} - - savedRange = null; - } - }); - - body.on( 'focus', function() - { - // Enable selections to be saved. - saveEnabled = true; - - saveSelection(); - }); - - body.on( 'beforedeactivate', function( evt ) - { - // Ignore this event if it's caused by focus switch between - // internal editable control type elements, e.g. layouted paragraph. (#4682) - if ( evt.data.$.toElement ) - return; - - // Disable selections from being saved. - saveEnabled = false; - }); - - // 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( evt ) - { - editor.document.$.selection.empty(); - }); - } - - // IE fires the "selectionchange" event when clicking - // inside a selection. We don't want to capture that. - body.on( 'mousedown', disableSave ); - body.on( 'mouseup', - function() - { - saveEnabled = true; - setTimeout( function() - { - saveSelection( true ); - }, - 0 ); - }); - - body.on( 'keydown', disableSave ); - body.on( 'keyup', - function() - { - saveEnabled = true; - saveSelection(); - }); - - - // IE is the only to provide the "selectionchange" - // event. - doc.on( 'selectionchange', saveSelection ); - - function disableSave() - { - saveEnabled = false; - } - - function saveSelection( testIt ) - { - if ( saveEnabled ) - { - var doc = editor.document, - sel = editor.getSelection(), - nativeSel = sel && sel.getNative(); - - // There is a very specific case, when clicking - // inside a text selection. In that case, the - // selection collapses at the clicking point, - // but the selection object remains in an - // unknown state, making createRange return a - // range at the very start of the document. In - // such situation we have to test the range, to - // be sure it's valid. - if ( testIt && nativeSel && nativeSel.type == 'None' ) - { - // The "InsertImage" command can be used to - // test whether the selection is good or not. - // If not, it's enough to give some time to - // IE to put things in order for us. - if ( !doc.$.queryCommandEnabled( 'InsertImage' ) ) - { - CKEDITOR.tools.setTimeout( saveSelection, 50, this, true ); - return; - } - } - - // Avoid saving selection from within text input. (#5747) - var parentTag; - if ( nativeSel && nativeSel.type == 'Text' - && ( parentTag = nativeSel.createRange().parentElement().nodeName.toLowerCase() ) - && parentTag in { input: 1, textarea : 1 } ) - { - return; - } - - savedRange = nativeSel && sel.getRanges()[ 0 ]; - - checkSelectionChangeTimeout.call( editor ); - } - } - } - else - { - // In other browsers, we make the selection change - // check based on other events, like clicks or keys - // press. - - doc.on( 'mouseup', checkSelectionChangeTimeout, editor ); - doc.on( 'keyup', checkSelectionChangeTimeout, editor ); - } - }); - - editor.addCommand( 'selectAll', selectAllCmd ); - editor.ui.addButton( 'SelectAll', - { - label : editor.lang.selectAll, - command : 'selectAll' - }); - - editor.selectionChange = checkSelectionChangeTimeout; - } - }); - - /** - * Gets the current selection from the editing area when in WYSIWYG mode. - * @returns {CKEDITOR.dom.selection} A selection object or null if not on - * WYSIWYG mode or no selection is available. - * @example - * var selection = CKEDITOR.instances.editor1.<b>getSelection()</b>; - * alert( selection.getType() ); - */ - CKEDITOR.editor.prototype.getSelection = function() - { - return this.document && this.document.getSelection(); - }; - - CKEDITOR.editor.prototype.forceNextSelectionCheck = function() - { - delete this._.selectionPreviousPath; - }; - - /** - * Gets the current selection from the document. - * @returns {CKEDITOR.dom.selection} A selection object. - * @example - * var selection = CKEDITOR.instances.editor1.document.<b>getSelection()</b>; - * alert( selection.getType() ); - */ - CKEDITOR.dom.document.prototype.getSelection = function() - { - var sel = new CKEDITOR.dom.selection( this ); - return ( !sel || sel.isInvalid ) ? null : sel; - }; - - /** - * No selection. - * @constant - * @example - * if ( editor.getSelection().getType() == CKEDITOR.SELECTION_NONE ) - * alert( 'Nothing is selected' ); - */ - CKEDITOR.SELECTION_NONE = 1; - - /** - * Text or collapsed selection. - * @constant - * @example - * if ( editor.getSelection().getType() == CKEDITOR.SELECTION_TEXT ) - * alert( 'Text is selected' ); - */ - CKEDITOR.SELECTION_TEXT = 2; - - /** - * Element selection. - * @constant - * @example - * if ( editor.getSelection().getType() == CKEDITOR.SELECTION_ELEMENT ) - * alert( 'An element is selected' ); - */ - CKEDITOR.SELECTION_ELEMENT = 3; - - /** - * Manipulates the selection in a DOM document. - * @constructor - * @example - */ - CKEDITOR.dom.selection = function( document ) - { - var lockedSelection = document.getCustomData( 'cke_locked_selection' ); - - if ( lockedSelection ) - return lockedSelection; - - this.document = document; - this.isLocked = false; - this._ = - { - cache : {} - }; - - /** - * IE BUG: The selection's document may be a different document than the - * editor document. Return null if that's the case. - */ - if ( CKEDITOR.env.ie ) - { - var range = this.getNative().createRange(); - if ( !range - || ( range.item && range.item(0).ownerDocument != this.document.$ ) - || ( range.parentElement && range.parentElement().ownerDocument != this.document.$ ) ) - { - this.isInvalid = true; - } - } - - return this; - }; - - var styleObjectElements = - { - img:1,hr:1,li:1,table:1,tr:1,td:1,th:1,embed:1,object:1,ol:1,ul:1, - a:1, input:1, form:1, select:1, textarea:1, button:1, fieldset:1, th:1, thead:1, tfoot:1 - }; - - CKEDITOR.dom.selection.prototype = - { - /** - * Gets the native selection object from the browser. - * @function - * @returns {Object} The native selection object. - * @example - * var selection = editor.getSelection().<b>getNative()</b>; - */ - getNative : - CKEDITOR.env.ie ? - function() - { - return this._.cache.nativeSel || ( this._.cache.nativeSel = this.document.$.selection ); - } - : - function() - { - return this._.cache.nativeSel || ( this._.cache.nativeSel = this.document.getWindow().$.getSelection() ); - }, - - /** - * Gets the type of the current selection. The following values are - * available: - * <ul> - * <li>{@link CKEDITOR.SELECTION_NONE} (1): No selection.</li> - * <li>{@link CKEDITOR.SELECTION_TEXT} (2): Text is selected or - * collapsed selection.</li> - * <li>{@link CKEDITOR.SELECTION_ELEMENT} (3): A element - * selection.</li> - * </ul> - * @function - * @returns {Number} One of the following constant values: - * {@link CKEDITOR.SELECTION_NONE}, {@link CKEDITOR.SELECTION_TEXT} or - * {@link CKEDITOR.SELECTION_ELEMENT}. - * @example - * if ( editor.getSelection().<b>getType()</b> == CKEDITOR.SELECTION_TEXT ) - * alert( 'Text is selected' ); - */ - getType : - CKEDITOR.env.ie ? - function() - { - var cache = this._.cache; - if ( cache.type ) - return cache.type; - - var type = CKEDITOR.SELECTION_NONE; - - try - { - var sel = this.getNative(), - ieType = sel.type; - - if ( ieType == 'Text' ) - type = CKEDITOR.SELECTION_TEXT; - - if ( ieType == 'Control' ) - type = CKEDITOR.SELECTION_ELEMENT; - - // It is possible that we can still get a text range - // object even when type == 'None' is returned by IE. - // So we'd better check the object returned by - // createRange() rather than by looking at the type. - if ( sel.createRange().parentElement ) - type = CKEDITOR.SELECTION_TEXT; - } - catch(e) {} - - return ( cache.type = type ); - } - : - function() - { - var cache = this._.cache; - if ( cache.type ) - return cache.type; - - var type = CKEDITOR.SELECTION_TEXT; - - var sel = this.getNative(); - - if ( !sel ) - type = CKEDITOR.SELECTION_NONE; - else if ( sel.rangeCount == 1 ) - { - // Check if the actual selection is a control (IMG, - // TABLE, HR, etc...). - - var range = sel.getRangeAt(0), - startContainer = range.startContainer; - - if ( startContainer == range.endContainer - && startContainer.nodeType == 1 - && ( range.endOffset - range.startOffset ) == 1 - && styleObjectElements[ startContainer.childNodes[ range.startOffset ].nodeName.toLowerCase() ] ) - { - type = CKEDITOR.SELECTION_ELEMENT; - } - } - - return ( cache.type = type ); - }, - - getRanges : - CKEDITOR.env.ie ? - ( function() - { - // Finds the container and offset for a specific boundary - // of an IE range. - var getBoundaryInformation = function( range, start ) - { - // Creates a collapsed range at the requested boundary. - range = range.duplicate(); - range.collapse( start ); - - // Gets the element that encloses the range entirely. - var parent = range.parentElement(); - var siblings = parent.childNodes; - - var testRange; - - for ( var i = 0 ; i < siblings.length ; i++ ) - { - var child = siblings[ i ]; - if ( child.nodeType == 1 ) - { - testRange = range.duplicate(); - - testRange.moveToElementText( child ); - - var comparisonStart = testRange.compareEndPoints( 'StartToStart', range ), - comparisonEnd = testRange.compareEndPoints( 'EndToStart', range ); - - testRange.collapse(); - - if ( comparisonStart > 0 ) - break; - // When selection stay at the side of certain self-closing elements, e.g. BR, - // our comparison will never shows an equality. (#4824) - else if ( !comparisonStart - || comparisonEnd == 1 && comparisonStart == -1 ) - return { container : parent, offset : i }; - else if ( !comparisonEnd ) - return { container : parent, offset : i + 1 }; - - testRange = null; - } - } - - if ( !testRange ) - { - testRange = range.duplicate(); - testRange.moveToElementText( parent ); - testRange.collapse( false ); - } - - testRange.setEndPoint( 'StartToStart', range ); - // IE report line break as CRLF with range.text but - // only LF with textnode.nodeValue, normalize them to avoid - // breaking character counting logic below. (#3949) - var distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length; - - try - { - while ( distance > 0 ) - distance -= siblings[ --i ].nodeValue.length; - } - // Measurement in IE could be somtimes wrong because of <select> element. (#4611) - catch( e ) - { - distance = 0; - } - - - if ( distance === 0 ) - { - return { - container : parent, - offset : i - }; - } - else - { - return { - container : siblings[ i ], - offset : -distance - }; - } - }; - - return function() - { - var cache = this._.cache; - if ( cache.ranges ) - return cache.ranges; - - // IE doesn't have range support (in the W3C way), so we - // need to do some magic to transform selections into - // CKEDITOR.dom.range instances. - - var sel = this.getNative(), - nativeRange = sel && sel.createRange(), - type = this.getType(), - range; - - if ( !sel ) - return []; - - if ( type == CKEDITOR.SELECTION_TEXT ) - { - range = new CKEDITOR.dom.range( this.document ); - - var boundaryInfo = getBoundaryInformation( nativeRange, true ); - range.setStart( new CKEDITOR.dom.node( boundaryInfo.container ), boundaryInfo.offset ); - - boundaryInfo = getBoundaryInformation( nativeRange ); - range.setEnd( new CKEDITOR.dom.node( boundaryInfo.container ), boundaryInfo.offset ); - - return ( cache.ranges = [ range ] ); - } - else if ( type == CKEDITOR.SELECTION_ELEMENT ) - { - var retval = this._.cache.ranges = []; - - for ( var i = 0 ; i < nativeRange.length ; i++ ) - { - var element = nativeRange.item( i ), - parentElement = element.parentNode, - j = 0; - - range = new CKEDITOR.dom.range( this.document ); - - for (; j < parentElement.childNodes.length && parentElement.childNodes[j] != element ; j++ ) - { /*jsl:pass*/ } - - range.setStart( new CKEDITOR.dom.node( parentElement ), j ); - range.setEnd( new CKEDITOR.dom.node( parentElement ), j + 1 ); - retval.push( range ); - } - - return retval; - } - - return ( cache.ranges = [] ); - }; - })() - : - function() - { - var cache = this._.cache; - if ( cache.ranges ) - return cache.ranges; - - // On browsers implementing the W3C range, we simply - // tranform the native ranges in CKEDITOR.dom.range - // instances. - - var ranges = []; - var sel = this.getNative(); - - if ( !sel ) - return []; - - for ( var i = 0 ; i < sel.rangeCount ; i++ ) - { - var nativeRange = sel.getRangeAt( i ); - var range = new CKEDITOR.dom.range( this.document ); - - range.setStart( new CKEDITOR.dom.node( nativeRange.startContainer ), nativeRange.startOffset ); - range.setEnd( new CKEDITOR.dom.node( nativeRange.endContainer ), nativeRange.endOffset ); - ranges.push( range ); - } - - return ( cache.ranges = ranges ); - }, - - /** - * Gets the DOM element in which the selection starts. - * @returns {CKEDITOR.dom.element} The element at the beginning of the - * selection. - * @example - * var element = editor.getSelection().<b>getStartElement()</b>; - * alert( element.getName() ); - */ - getStartElement : function() - { - var cache = this._.cache; - if ( cache.startElement !== undefined ) - return cache.startElement; - - var node, - sel = this.getNative(); - - switch ( this.getType() ) - { - case CKEDITOR.SELECTION_ELEMENT : - return this.getSelectedElement(); - - case CKEDITOR.SELECTION_TEXT : - - var range = this.getRanges()[0]; - - if ( range ) - { - if ( !range.collapsed ) - { - range.optimize(); - - // Decrease the range content to exclude particial - // selected node on the start which doesn't have - // visual impact. ( #3231 ) - while ( true ) - { - var startContainer = range.startContainer, - startOffset = range.startOffset; - // Limit the fix only to non-block elements.(#3950) - if ( startOffset == ( startContainer.getChildCount ? - startContainer.getChildCount() : startContainer.getLength() ) - && !startContainer.isBlockBoundary() ) - range.setStartAfter( startContainer ); - else break; - } - - node = range.startContainer; - - if ( node.type != CKEDITOR.NODE_ELEMENT ) - return node.getParent(); - - node = node.getChild( range.startOffset ); - - if ( !node || node.type != CKEDITOR.NODE_ELEMENT ) - return range.startContainer; - - var child = node.getFirst(); - while ( child && child.type == CKEDITOR.NODE_ELEMENT ) - { - node = child; - child = child.getFirst(); - } - - return node; - } - } - - if ( CKEDITOR.env.ie ) - { - range = sel.createRange(); - range.collapse( true ); - - node = range.parentElement(); - } - else - { - node = sel.anchorNode; - - if ( node && node.nodeType != 1 ) - node = node.parentNode; - } - } - - return cache.startElement = ( node ? new CKEDITOR.dom.element( node ) : null ); - }, - - /** - * Gets the current selected element. - * @returns {CKEDITOR.dom.element} The selected element. Null if no - * selection is available or the selection type is not - * {@link CKEDITOR.SELECTION_ELEMENT}. - * @example - * var element = editor.getSelection().<b>getSelectedElement()</b>; - * alert( element.getName() ); - */ - getSelectedElement : function() - { - var cache = this._.cache; - if ( cache.selectedElement !== undefined ) - return cache.selectedElement; - - var self = this; - - var node = CKEDITOR.tools.tryThese( - // Is it native IE control type selection? - function() - { - return self.getNative().createRange().item( 0 ); - }, - // Figure it out by checking if there's a single enclosed - // node of the range. - function() - { - var range = self.getRanges()[ 0 ], - enclosed, - selected; - - // Check first any enclosed element, e.g. <ul>[<li><a href="#">item</a></li>]</ul> - for ( var i = 2; i && !( ( enclosed = range.getEnclosedNode() ) - && ( enclosed.type == CKEDITOR.NODE_ELEMENT ) - && styleObjectElements[ enclosed.getName() ] - && ( selected = enclosed ) ); i-- ) - { - // Then check any deep wrapped element, e.g. [<b><i><img /></i></b>] - range.shrink( CKEDITOR.SHRINK_ELEMENT ); - } - - return selected.$; - }); - - return cache.selectedElement = ( node ? new CKEDITOR.dom.element( node ) : null ); - }, - - lock : function() - { - // Call all cacheable function. - this.getRanges(); - this.getStartElement(); - this.getSelectedElement(); - - // The native selection is not available when locked. - this._.cache.nativeSel = {}; - - this.isLocked = true; - - // Save this selection inside the DOM document. - this.document.setCustomData( 'cke_locked_selection', this ); - }, - - unlock : function( restore ) - { - var doc = this.document, - lockedSelection = doc.getCustomData( 'cke_locked_selection' ); - - if ( lockedSelection ) - { - doc.setCustomData( 'cke_locked_selection', null ); - - if ( restore ) - { - var selectedElement = lockedSelection.getSelectedElement(), - ranges = !selectedElement && lockedSelection.getRanges(); - - this.isLocked = false; - this.reset(); - - doc.getBody().focus(); - - if ( selectedElement ) - this.selectElement( selectedElement ); - else - this.selectRanges( ranges ); - } - } - - if ( !lockedSelection || !restore ) - { - this.isLocked = false; - this.reset(); - } - }, - - reset : function() - { - this._.cache = {}; - }, - - selectElement : function( element ) - { - if ( this.isLocked ) - { - var range = new CKEDITOR.dom.range( this.document ); - range.setStartBefore( element ); - range.setEndAfter( element ); - - this._.cache.selectedElement = element; - this._.cache.startElement = element; - this._.cache.ranges = [ range ]; - this._.cache.type = CKEDITOR.SELECTION_ELEMENT; - - return; - } - - if ( CKEDITOR.env.ie ) - { - this.getNative().empty(); - - try - { - // Try to select the node as a control. - range = this.document.$.body.createControlRange(); - range.addElement( element.$ ); - range.select(); - } - catch(e) - { - // If failed, select it as a text range. - range = this.document.$.body.createTextRange(); - range.moveToElementText( element.$ ); - range.select(); - } - finally - { - this.document.fire( 'selectionchange' ); - } - - this.reset(); - } - else - { - // Create the range for the element. - range = this.document.$.createRange(); - range.selectNode( element.$ ); - - // Select the range. - var sel = this.getNative(); - sel.removeAllRanges(); - sel.addRange( range ); - - this.reset(); - } - }, - - selectRanges : function( ranges ) - { - if ( this.isLocked ) - { - this._.cache.selectedElement = null; - this._.cache.startElement = ranges[ 0 ].getTouchedStartNode(); - this._.cache.ranges = ranges; - this._.cache.type = CKEDITOR.SELECTION_TEXT; - - return; - } - - if ( CKEDITOR.env.ie ) - { - // IE doesn't accept multiple ranges selection, so we just - // select the first one. - if ( ranges[ 0 ] ) - ranges[ 0 ].select(); - - this.reset(); - } - else - { - var sel = this.getNative(); - sel.removeAllRanges(); - - for ( var i = 0 ; i < ranges.length ; i++ ) - { - var range = ranges[ i ]; - var nativeRange = this.document.$.createRange(); - var startContainer = range.startContainer; - - // In FF2, if we have a collapsed range, inside an empty - // element, we must add something to it otherwise the caret - // will not be visible. - if ( range.collapsed && - ( CKEDITOR.env.gecko && CKEDITOR.env.version < 10900 ) && - startContainer.type == CKEDITOR.NODE_ELEMENT && - !startContainer.getChildCount() ) - { - startContainer.appendText( '' ); - } - - nativeRange.setStart( startContainer.$, range.startOffset ); - nativeRange.setEnd( range.endContainer.$, range.endOffset ); - - // Select the range. - sel.addRange( nativeRange ); - } - - this.reset(); - } - }, - - createBookmarks : function( serializable ) - { - var retval = [], - ranges = this.getRanges(), - length = ranges.length, - bookmark; - for ( var i = 0; i < length ; i++ ) - { - retval.push( bookmark = ranges[ i ].createBookmark( serializable, true ) ); - - serializable = bookmark.serializable; - - var bookmarkStart = serializable ? this.document.getById( bookmark.startNode ) : bookmark.startNode, - bookmarkEnd = serializable ? this.document.getById( bookmark.endNode ) : bookmark.endNode; - - // Updating the offset values for rest of ranges which have been mangled(#3256). - for ( var j = i + 1 ; j < length ; j++ ) - { - var dirtyRange = ranges[ j ], - rangeStart = dirtyRange.startContainer, - rangeEnd = dirtyRange.endContainer; - - rangeStart.equals( bookmarkStart.getParent() ) && dirtyRange.startOffset++; - rangeStart.equals( bookmarkEnd.getParent() ) && dirtyRange.startOffset++; - rangeEnd.equals( bookmarkStart.getParent() ) && dirtyRange.endOffset++; - rangeEnd.equals( bookmarkEnd.getParent() ) && dirtyRange.endOffset++; - } - } - - return retval; - }, - - createBookmarks2 : function( normalized ) - { - var bookmarks = [], - ranges = this.getRanges(); - - for ( var i = 0 ; i < ranges.length ; i++ ) - bookmarks.push( ranges[i].createBookmark2( normalized ) ); - - return bookmarks; - }, - - selectBookmarks : function( bookmarks ) - { - var ranges = []; - for ( var i = 0 ; i < bookmarks.length ; i++ ) - { - var range = new CKEDITOR.dom.range( this.document ); - range.moveToBookmark( bookmarks[i] ); - ranges.push( range ); - } - this.selectRanges( ranges ); - return this; - }, - - getCommonAncestor : function() - { - var ranges = this.getRanges(), - startNode = ranges[ 0 ].startContainer, - endNode = ranges[ ranges.length - 1 ].endContainer; - return startNode.getCommonAncestor( endNode ); - }, - - // Moving scroll bar to the current selection's start position. - scrollIntoView : function() - { - // If we have split the block, adds a temporary span at the - // range position and scroll relatively to it. - var start = this.getStartElement(); - start.scrollIntoView(); - } - }; -})(); -( function() -{ -var notWhitespaces = CKEDITOR.dom.walker.whitespaces( true ); -var fillerTextRegex = /\ufeff|\u00a0/; -var nonCells = { table:1,tbody:1,tr:1 }; - -CKEDITOR.dom.range.prototype.select = - CKEDITOR.env.ie ? - // V2 - function( forceExpand ) - { - var collapsed = this.collapsed; - var isStartMarkerAlone; - var dummySpan; - - // IE doesn't support selecting the entire table row/cell, move the selection into cells, e.g. - // <table><tbody><tr>[<td>cell</b></td>... => <table><tbody><tr><td>[cell</td>... - if ( this.startContainer.type == CKEDITOR.NODE_ELEMENT && this.startContainer.getName() in nonCells - || this.endContainer.type == CKEDITOR.NODE_ELEMENT && this.endContainer.getName() in nonCells ) - { - this.shrink( CKEDITOR.NODE_ELEMENT, true ); - } - - var bookmark = this.createBookmark(); - - // Create marker tags for the start and end boundaries. - var startNode = bookmark.startNode; - - var endNode; - if ( !collapsed ) - endNode = bookmark.endNode; - - // Create the main range which will be used for the selection. - var ieRange = this.document.$.body.createTextRange(); - - // Position the range at the start boundary. - ieRange.moveToElementText( startNode.$ ); - ieRange.moveStart( 'character', 1 ); - - if ( endNode ) - { - // Create a tool range for the end. - var ieRangeEnd = this.document.$.body.createTextRange(); - - // Position the tool range at the end. - ieRangeEnd.moveToElementText( endNode.$ ); - - // Move the end boundary of the main range to match the tool range. - ieRange.setEndPoint( 'EndToEnd', ieRangeEnd ); - ieRange.moveEnd( 'character', -1 ); - } - else - { - // The isStartMarkerAlone logic comes from V2. It guarantees that the lines - // will expand and that the cursor will be blinking on the right place. - // Actually, we are using this flag just to avoid using this hack in all - // situations, but just on those needed. - var next = startNode.getNext( notWhitespaces ); - isStartMarkerAlone = ( !( next && next.getText && next.getText().match( fillerTextRegex ) ) // already a filler there? - && ( forceExpand || !startNode.hasPrevious() || ( startNode.getPrevious().is && startNode.getPrevious().is( 'br' ) ) ) ); - - // Append a temporary <span></span> before the selection. - // This is needed to avoid IE destroying selections inside empty - // inline elements, like <b></b> (#253). - // It is also needed when placing the selection right after an inline - // element to avoid the selection moving inside of it. - dummySpan = this.document.createElement( 'span' ); - dummySpan.setHtml( '' ); // Zero Width No-Break Space (U+FEFF). See #1359. - dummySpan.insertBefore( startNode ); - - if ( isStartMarkerAlone ) - { - // To expand empty blocks or line spaces after <br>, we need - // instead to have any char, which will be later deleted using the - // selection. - // \ufeff = Zero Width No-Break Space (U+FEFF). (#1359) - this.document.createText( '\ufeff' ).insertBefore( startNode ); - } - } - - // Remove the markers (reset the position, because of the changes in the DOM tree). - this.setStartBefore( startNode ); - startNode.remove(); - - if ( collapsed ) - { - if ( isStartMarkerAlone ) - { - // Move the selection start to include the temporary \ufeff. - ieRange.moveStart( 'character', -1 ); - - ieRange.select(); - - // Remove our temporary stuff. - this.document.$.selection.clear(); - } - else - ieRange.select(); - - this.moveToPosition( dummySpan, CKEDITOR.POSITION_BEFORE_START ); - dummySpan.remove(); - } - else - { - this.setEndBefore( endNode ); - endNode.remove(); - ieRange.select(); - } - - this.document.fire( 'selectionchange' ); - } - : - function() - { - var startContainer = this.startContainer; - - // If we have a collapsed range, inside an empty element, we must add - // something to it, otherwise the caret will not be visible. - if ( this.collapsed && startContainer.type == CKEDITOR.NODE_ELEMENT && !startContainer.getChildCount() ) - startContainer.append( new CKEDITOR.dom.text( '' ) ); - - var nativeRange = this.document.$.createRange(); - nativeRange.setStart( startContainer.$, this.startOffset ); - - try - { - nativeRange.setEnd( this.endContainer.$, this.endOffset ); - } - catch ( e ) - { - // There is a bug in Firefox implementation (it would be too easy - // otherwise). The new start can't be after the end (W3C says it can). - // So, let's create a new range and collapse it to the desired point. - if ( e.toString().indexOf( 'NS_ERROR_ILLEGAL_VALUE' ) >= 0 ) - { - this.collapse( true ); - nativeRange.setEnd( this.endContainer.$, this.endOffset ); - } - else - throw( e ); - } - - var selection = this.document.getSelection().getNative(); - selection.removeAllRanges(); - selection.addRange( nativeRange ); - }; -} )(); +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+(function()
+{
+ // #### checkSelectionChange : START
+
+ // The selection change check basically saves the element parent tree of
+ // the current node and check it on successive requests. If there is any
+ // change on the tree, then the selectionChange event gets fired.
+ function checkSelectionChange()
+ {
+ try
+ {
+ // In IE, the "selectionchange" event may still get thrown when
+ // releasing the WYSIWYG mode, so we need to check it first.
+ var sel = this.getSelection();
+ if ( !sel || !sel.document.getWindow().$ )
+ return;
+
+ var firstElement = sel.getStartElement();
+ var currentPath = new CKEDITOR.dom.elementPath( firstElement );
+
+ if ( !currentPath.compare( this._.selectionPreviousPath ) )
+ {
+ this._.selectionPreviousPath = currentPath;
+ this.fire( 'selectionChange', { selection : sel, path : currentPath, element : firstElement } );
+ }
+ }
+ catch (e)
+ {}
+ }
+
+ var checkSelectionChangeTimer,
+ checkSelectionChangeTimeoutPending;
+
+ function checkSelectionChangeTimeout()
+ {
+ // Firing the "OnSelectionChange" event on every key press started to
+ // be too slow. This function guarantees that there will be at least
+ // 200ms delay between selection checks.
+
+ checkSelectionChangeTimeoutPending = true;
+
+ if ( checkSelectionChangeTimer )
+ return;
+
+ checkSelectionChangeTimeoutExec.call( this );
+
+ checkSelectionChangeTimer = CKEDITOR.tools.setTimeout( checkSelectionChangeTimeoutExec, 200, this );
+ }
+
+ function checkSelectionChangeTimeoutExec()
+ {
+ checkSelectionChangeTimer = null;
+
+ if ( checkSelectionChangeTimeoutPending )
+ {
+ // Call this with a timeout so the browser properly moves the
+ // selection after the mouseup. It happened that the selection was
+ // being moved after the mouseup when clicking inside selected text
+ // with Firefox.
+ CKEDITOR.tools.setTimeout( checkSelectionChange, 0, this );
+
+ checkSelectionChangeTimeoutPending = false;
+ }
+ }
+
+ // #### checkSelectionChange : END
+
+ function rangeRequiresFix( range )
+ {
+ function isInlineCt( node )
+ {
+ return node && node.type == CKEDITOR.NODE_ELEMENT
+ && node.getName() in CKEDITOR.dtd.$removeEmpty;
+ }
+
+ function singletonBlock( node )
+ {
+ var body = range.document.getBody();
+ return !node.is( 'body' ) && body.getChildCount() == 1;
+ }
+
+ var start = range.startContainer,
+ offset = range.startOffset;
+
+ if ( start.type == CKEDITOR.NODE_TEXT )
+ return false;
+
+ // 1. Empty inline element. <span>^</span>
+ // 2. Adjoin to inline element. <p><strong>text</strong>^</p>
+ // 3. The only empty block in document. <body><p>^</p></body> (#7222)
+ return !CKEDITOR.tools.trim( start.getHtml() ) ? isInlineCt( start ) || singletonBlock( start )
+ : isInlineCt( start.getChild( offset - 1 ) ) || isInlineCt( start.getChild( offset ) );
+ }
+
+ var selectAllCmd =
+ {
+ modes : { wysiwyg : 1, source : 1 },
+ readOnly : CKEDITOR.env.ie || CKEDITOR.env.webkit,
+ exec : function( editor )
+ {
+ switch ( editor.mode )
+ {
+ case 'wysiwyg' :
+ editor.document.$.execCommand( 'SelectAll', false, null );
+ // Force triggering selectionChange (#7008)
+ editor.forceNextSelectionCheck();
+ editor.selectionChange();
+ break;
+ case 'source' :
+ // Select the contents of the textarea
+ var textarea = editor.textarea.$;
+ if ( CKEDITOR.env.ie )
+ textarea.createTextRange().execCommand( 'SelectAll' );
+ else
+ {
+ textarea.selectionStart = 0;
+ textarea.selectionEnd = textarea.value.length;
+ }
+ textarea.focus();
+ }
+ },
+ canUndo : false
+ };
+
+ function createFillingChar( doc )
+ {
+ removeFillingChar( doc );
+
+ var fillingChar = doc.createText( '\u200B' );
+ doc.setCustomData( 'cke-fillingChar', fillingChar );
+
+ return fillingChar;
+ }
+
+ function getFillingChar( doc )
+ {
+ return doc && doc.getCustomData( 'cke-fillingChar' );
+ }
+
+ // Checks if a filling char has been used, eventualy removing it (#1272).
+ function checkFillingChar( doc )
+ {
+ var fillingChar = doc && getFillingChar( doc );
+ if ( fillingChar )
+ {
+ // Use this flag to avoid removing the filling char right after
+ // creating it.
+ if ( fillingChar.getCustomData( 'ready' ) )
+ removeFillingChar( doc );
+ else
+ fillingChar.setCustomData( 'ready', 1 );
+ }
+ }
+
+ function removeFillingChar( doc )
+ {
+ var fillingChar = doc && doc.removeCustomData( 'cke-fillingChar' );
+ if ( fillingChar )
+ {
+ var bm,
+ sel = doc.getSelection().getNative(),
+ // Be error proof.
+ range = sel && sel.type != 'None' && sel.getRangeAt( 0 );
+
+ // Text selection position might get mangled by
+ // subsequent dom modification, save it now for restoring. (#8617)
+ if ( fillingChar.getLength() > 1
+ && range && range.intersectsNode( fillingChar.$ ) )
+ {
+ bm = [ sel.anchorOffset, sel.focusOffset ];
+
+ // Anticipate the offset change brought by the removed char.
+ var startAffected = sel.anchorNode == fillingChar.$ && sel.anchorOffset > 0,
+ endAffected = sel.focusNode == fillingChar.$ && sel.focusOffset > 0;
+ startAffected && bm[ 0 ]--;
+ endAffected && bm[ 1 ]--;
+
+ // Revert the bookmark order on reverse selection.
+ isReversedSelection( sel ) && bm.unshift( bm.pop() );
+ }
+
+ // We can't simply remove the filling node because the user
+ // will actually enlarge it when typing, so we just remove the
+ // invisible char from it.
+ fillingChar.setText( fillingChar.getText().replace( /\u200B/g, '' ) );
+
+ // Restore the bookmark.
+ if ( bm )
+ {
+ var rng = sel.getRangeAt( 0 );
+ rng.setStart( rng.startContainer, bm[ 0 ] );
+ rng.setEnd( rng.startContainer, bm[ 1 ] );
+ sel.removeAllRanges();
+ sel.addRange( rng );
+ }
+ }
+ }
+
+ function isReversedSelection( sel )
+ {
+ if ( !sel.isCollapsed )
+ {
+ var range = sel.getRangeAt( 0 );
+ // Potentially alter an reversed selection range.
+ range.setStart( sel.anchorNode, sel.anchorOffset );
+ range.setEnd( sel.focusNode, sel.focusOffset );
+ return range.collapsed;
+ }
+ }
+
+ CKEDITOR.plugins.add( 'selection',
+ {
+ init : function( editor )
+ {
+ // On WebKit only, we need a special "filling" char on some situations
+ // (#1272). Here we set the events that should invalidate that char.
+ if ( CKEDITOR.env.webkit )
+ {
+ editor.on( 'selectionChange', function() { checkFillingChar( editor.document ); } );
+ editor.on( 'beforeSetMode', function() { removeFillingChar( editor.document ); } );
+
+ var fillingCharBefore,
+ resetSelection;
+
+ function beforeData()
+ {
+ var doc = editor.document,
+ fillingChar = getFillingChar( doc );
+
+ if ( fillingChar )
+ {
+ // If cursor is right blinking by side of the filler node, save it for restoring,
+ // as the following text substitution will blind it. (#7437)
+ var sel = doc.$.defaultView.getSelection();
+ if ( sel.type == 'Caret' && sel.anchorNode == fillingChar.$ )
+ resetSelection = 1;
+
+ fillingCharBefore = fillingChar.getText();
+ fillingChar.setText( fillingCharBefore.replace( /\u200B/g, '' ) );
+ }
+ }
+ function afterData()
+ {
+ var doc = editor.document,
+ fillingChar = getFillingChar( doc );
+
+ if ( fillingChar )
+ {
+ fillingChar.setText( fillingCharBefore );
+
+ if ( resetSelection )
+ {
+ doc.$.defaultView.getSelection().setPosition( fillingChar.$,fillingChar.getLength() );
+ resetSelection = 0;
+ }
+ }
+ }
+ editor.on( 'beforeUndoImage', beforeData );
+ editor.on( 'afterUndoImage', afterData );
+ editor.on( 'beforeGetData', beforeData, null, null, 0 );
+ editor.on( 'getData', afterData );
+ }
+
+ editor.on( 'contentDom', function()
+ {
+ var doc = editor.document,
+ body = doc.getBody(),
+ html = doc.getDocumentElement();
+
+ if ( CKEDITOR.env.ie )
+ {
+ // Other browsers don't loose the selection if the
+ // editor document loose the focus. In IE, we don't
+ // have support for it, so we reproduce it here, other
+ // than firing the selection change event.
+
+ var savedRange,
+ saveEnabled,
+ restoreEnabled = 1;
+
+ // "onfocusin" is fired before "onfocus". It makes it
+ // possible to restore the selection before click
+ // events get executed.
+ body.on( 'focusin', function( evt )
+ {
+ // If there are elements with layout they fire this event but
+ // it must be ignored to allow edit its contents #4682
+ if ( evt.data.$.srcElement.nodeName != 'BODY' )
+ return;
+
+ // Give the priority to locked selection since it probably
+ // reflects the actual situation, besides locked selection
+ // could be interfered because of text nodes normalizing.
+ // (#6083, #6987)
+ var lockedSelection = doc.getCustomData( 'cke_locked_selection' );
+ if ( lockedSelection )
+ {
+ lockedSelection.unlock( 1 );
+ lockedSelection.lock();
+ }
+ // Then check ff we have saved a range, restore it at this
+ // point.
+ else if ( savedRange && restoreEnabled )
+ {
+ // Well not break because of this.
+ try { savedRange.select(); } catch (e) {}
+ savedRange = null;
+ }
+ });
+
+ body.on( 'focus', function()
+ {
+ // Enable selections to be saved.
+ saveEnabled = 1;
+
+ saveSelection();
+ });
+
+ body.on( 'beforedeactivate', function( evt )
+ {
+ // Ignore this event if it's caused by focus switch between
+ // internal editable control type elements, e.g. layouted paragraph. (#4682)
+ if ( evt.data.$.toElement )
+ return;
+
+ // Disable selections from being saved.
+ saveEnabled = 0;
+ restoreEnabled = 1;
+ });
+
+ // [IE] Iframe will still keep the selection when blurred, if
+ // focus is moved onto a non-editing host, e.g. link or button, but
+ // it becomes a problem for the object type selection, since the resizer
+ // handler attached on it will mark other part of the UI, especially
+ // for the dialog. (#8157)
+ // [IE<8] Even worse For old IEs, the cursor will not vanish even if
+ // the selection has been moved to another text input in some cases. (#4716)
+ //
+ // Now the range restore is disabled, so we simply force IE to clean
+ // up the selection before blur.
+ CKEDITOR.env.ie && editor.on( 'blur', function()
+ {
+ // Error proof when the editor is not visible. (#6375)
+ try{ doc.$.selection.empty(); } catch ( er){}
+ });
+
+ // Listening on document element ensures that
+ // scrollbar is included. (#5280)
+ html.on( 'mousedown', function()
+ {
+ // Lock restore selection now, as we have
+ // a followed 'click' event which introduce
+ // new selection. (#5735)
+ restoreEnabled = 0;
+ });
+
+ html.on( 'mouseup', function()
+ {
+ restoreEnabled = 1;
+ });
+
+ var scroll;
+ // IE fires the "selectionchange" event when clicking
+ // inside a selection. We don't want to capture that.
+ body.on( 'mousedown', function( evt )
+ {
+ // IE scrolls document to top on right mousedown
+ // when editor has no focus, remember this scroll
+ // position and revert it before context menu opens. (#5778)
+ if ( evt.data.$.button == 2 )
+ {
+ var sel = editor.document.$.selection;
+ if ( sel.type == 'None' )
+ scroll = editor.window.getScrollPosition();
+ }
+ disableSave();
+ });
+
+ body.on( 'mouseup',
+ function( evt )
+ {
+ // Restore recorded scroll position when needed on right mouseup.
+ if ( evt.data.$.button == 2 && scroll )
+ {
+ editor.document.$.documentElement.scrollLeft = scroll.x;
+ editor.document.$.documentElement.scrollTop = scroll.y;
+ }
+ scroll = null;
+
+ saveEnabled = 1;
+ setTimeout( function()
+ {
+ saveSelection( true );
+ },
+ 0 );
+ });
+
+ body.on( 'keydown', disableSave );
+ body.on( 'keyup',
+ function()
+ {
+ saveEnabled = 1;
+ saveSelection();
+ });
+
+ // When content doc is in standards mode, IE doesn't focus the editor when
+ // clicking at the region below body (on html element) content, we emulate
+ // the normal behavior on old IEs. (#1659, #7932)
+ if ( ( CKEDITOR.env.ie7Compat || CKEDITOR.env.ie6Compat )
+ && doc.$.compatMode != 'BackCompat' )
+ {
+ html.on( 'mousedown', function( evt )
+ {
+ evt = evt.data.$;
+
+ // Expand the text range along with mouse move.
+ function onHover( evt )
+ {
+ evt = evt.data.$;
+ if ( textRng )
+ {
+ // Read the current cursor.
+ var rngEnd = body.$.createTextRange();
+ rngEnd.moveToPoint( evt.x, evt.y );
+
+ // Handle drag directions.
+ textRng.setEndPoint(
+ textRng.compareEndPoints( 'StartToStart', rngEnd ) < 0 ?
+ 'EndToEnd' :
+ 'StartToStart',
+ rngEnd );
+
+ // Update selection with new range.
+ textRng.select();
+ }
+ }
+
+ // We're sure that the click happens at the region
+ // below body, but not on scrollbar.
+ if ( evt.y < html.$.clientHeight
+ && evt.y > body.$.offsetTop + body.$.clientHeight
+ && evt.x < html.$.clientWidth )
+ {
+ // Start to build the text range.
+ var textRng = body.$.createTextRange();
+ textRng.moveToPoint( evt.x, evt.y );
+ textRng.select();
+
+ html.on( 'mousemove', onHover );
+
+ html.on( 'mouseup', function( evt )
+ {
+ html.removeListener( 'mousemove', onHover );
+ evt.removeListener();
+ textRng.select();
+ textRng = null;
+ } );
+ }
+ });
+ }
+
+ // It's much simpler for IE8, we just need to reselect the reported range.
+ if ( CKEDITOR.env.ie8 )
+ {
+ html.on( 'mouseup', function( evt )
+ {
+ // The event is not fired when clicking on the scrollbars,
+ // so we can safely check the following to understand
+ // whether the empty space following <body> has been clicked.
+ if ( evt.data.getTarget().getName() == 'html' )
+ {
+ var sel = CKEDITOR.document.$.selection,
+ range = sel.createRange();
+ // The selection range is reported on host, but actually it should applies to the content doc.
+ if ( sel.type != 'None' && range.parentElement().ownerDocument == doc.$ )
+ range.select();
+ }
+ } );
+ }
+
+ // IE is the only to provide the "selectionchange"
+ // event.
+ doc.on( 'selectionchange', saveSelection );
+
+ function disableSave()
+ {
+ saveEnabled = 0;
+ }
+
+ function saveSelection( testIt )
+ {
+ if ( saveEnabled )
+ {
+ var doc = editor.document,
+ sel = editor.getSelection(),
+ nativeSel = sel && sel.getNative();
+
+ // There is a very specific case, when clicking
+ // inside a text selection. In that case, the
+ // selection collapses at the clicking point,
+ // but the selection object remains in an
+ // unknown state, making createRange return a
+ // range at the very start of the document. In
+ // such situation we have to test the range, to
+ // be sure it's valid.
+ if ( testIt && nativeSel && nativeSel.type == 'None' )
+ {
+ // The "InsertImage" command can be used to
+ // test whether the selection is good or not.
+ // If not, it's enough to give some time to
+ // IE to put things in order for us.
+ if ( !doc.$.queryCommandEnabled( 'InsertImage' ) )
+ {
+ CKEDITOR.tools.setTimeout( saveSelection, 50, this, true );
+ return;
+ }
+ }
+
+ // Avoid saving selection from within text input. (#5747)
+ var parentTag;
+ if ( nativeSel && nativeSel.type && nativeSel.type != 'Control'
+ && ( parentTag = nativeSel.createRange() )
+ && ( parentTag = parentTag.parentElement() )
+ && ( parentTag = parentTag.nodeName )
+ && parentTag.toLowerCase() in { input: 1, textarea : 1 } )
+ {
+ return;
+ }
+
+ savedRange = nativeSel && sel.getRanges()[ 0 ];
+
+ checkSelectionChangeTimeout.call( editor );
+ }
+ }
+ }
+ else
+ {
+ // In other browsers, we make the selection change
+ // check based on other events, like clicks or keys
+ // press.
+
+ doc.on( 'mouseup', checkSelectionChangeTimeout, editor );
+ doc.on( 'keyup', checkSelectionChangeTimeout, editor );
+ doc.on( 'selectionchange', checkSelectionChangeTimeout, editor );
+ }
+
+ if ( CKEDITOR.env.webkit )
+ {
+ doc.on( 'keydown', function( evt )
+ {
+ var key = evt.data.getKey();
+ // Remove the filling char before some keys get
+ // executed, so they'll not get blocked by it.
+ switch ( key )
+ {
+ case 13 : // ENTER
+ case 33 : // PAGEUP
+ case 34 : // PAGEDOWN
+ case 35 : // HOME
+ case 36 : // END
+ case 37 : // LEFT-ARROW
+ case 39 : // RIGHT-ARROW
+ case 8 : // BACKSPACE
+ case 45 : // INS
+ case 46 : // DEl
+ removeFillingChar( editor.document );
+ }
+
+ }, null, null, 10 );
+ }
+ });
+
+ // Clear the cached range path before unload. (#7174)
+ editor.on( 'contentDomUnload', editor.forceNextSelectionCheck, editor );
+
+ editor.addCommand( 'selectAll', selectAllCmd );
+ editor.ui.addButton( 'SelectAll',
+ {
+ label : editor.lang.selectAll,
+ command : 'selectAll'
+ });
+
+ editor.selectionChange = checkSelectionChangeTimeout;
+
+ // IE9 might cease to work if there's an object selection inside the iframe (#7639).
+ CKEDITOR.env.ie9Compat && editor.on( 'destroy', function()
+ {
+ var sel = editor.getSelection();
+ sel && sel.getNative().clear();
+ }, null, null, 9 );
+ }
+ });
+
+ /**
+ * Gets the current selection from the editing area when in WYSIWYG mode.
+ * @returns {CKEDITOR.dom.selection} A selection object or null if not in
+ * WYSIWYG mode or no selection is available.
+ * @example
+ * var selection = CKEDITOR.instances.editor1.<strong>getSelection()</strong>;
+ * alert( selection.getType() );
+ */
+ CKEDITOR.editor.prototype.getSelection = function()
+ {
+ return this.document && this.document.getSelection();
+ };
+
+ CKEDITOR.editor.prototype.forceNextSelectionCheck = function()
+ {
+ delete this._.selectionPreviousPath;
+ };
+
+ /**
+ * Gets the current selection from the document.
+ * @returns {CKEDITOR.dom.selection} A selection object.
+ * @example
+ * var selection = CKEDITOR.instances.editor1.document.<strong>getSelection()</strong>;
+ * alert( selection.getType() );
+ */
+ CKEDITOR.dom.document.prototype.getSelection = function()
+ {
+ var sel = new CKEDITOR.dom.selection( this );
+ return ( !sel || sel.isInvalid ) ? null : sel;
+ };
+
+ /**
+ * No selection.
+ * @constant
+ * @example
+ * if ( editor.getSelection().getType() == CKEDITOR.SELECTION_NONE )
+ * alert( 'Nothing is selected' );
+ */
+ CKEDITOR.SELECTION_NONE = 1;
+
+ /**
+ * A text or a collapsed selection.
+ * @constant
+ * @example
+ * if ( editor.getSelection().getType() == CKEDITOR.SELECTION_TEXT )
+ * alert( 'A text is selected' );
+ */
+ CKEDITOR.SELECTION_TEXT = 2;
+
+ /**
+ * Element selection.
+ * @constant
+ * @example
+ * if ( editor.getSelection().getType() == CKEDITOR.SELECTION_ELEMENT )
+ * alert( 'An element is selected' );
+ */
+ CKEDITOR.SELECTION_ELEMENT = 3;
+
+ /**
+ * Manipulates the selection in a DOM document.
+ * @constructor
+ * @param {CKEDITOR.dom.document} document The DOM document that contains the selection.
+ * @example
+ * var sel = new <strong>CKEDITOR.dom.selection( CKEDITOR.document )</strong>;
+ */
+ CKEDITOR.dom.selection = function( document )
+ {
+ var lockedSelection = document.getCustomData( 'cke_locked_selection' );
+
+ if ( lockedSelection )
+ return lockedSelection;
+
+ this.document = document;
+ this.isLocked = 0;
+ this._ =
+ {
+ cache : {}
+ };
+
+ /**
+ * IE BUG: The selection's document may be a different document than the
+ * editor document. Return null if that is the case.
+ */
+ if ( CKEDITOR.env.ie )
+ {
+ // Avoid breaking because of it. (#8836)
+ try
+ {
+ var range = this.getNative().createRange();
+ if ( !range ||
+ ( range.item && range.item( 0 ).ownerDocument != this.document.$ ) ||
+ ( range.parentElement && range.parentElement().ownerDocument != this.document.$ ) )
+ {
+ throw 0;
+ }
+ }
+ catch ( e )
+ {
+ this.isInvalid = true;
+ }
+ }
+
+ return this;
+ };
+
+ var styleObjectElements =
+ {
+ img:1,hr:1,li:1,table:1,tr:1,td:1,th:1,embed:1,object:1,ol:1,ul:1,
+ a:1,input:1,form:1,select:1,textarea:1,button:1,fieldset:1,thead:1,tfoot:1
+ };
+
+ CKEDITOR.dom.selection.prototype =
+ {
+ /**
+ * Gets the native selection object from the browser.
+ * @function
+ * @returns {Object} The native browser selection object.
+ * @example
+ * var selection = editor.getSelection().<strong>getNative()</strong>;
+ */
+ getNative :
+ CKEDITOR.env.ie ?
+ function()
+ {
+ return this._.cache.nativeSel || ( this._.cache.nativeSel = this.document.$.selection );
+ }
+ :
+ function()
+ {
+ return this._.cache.nativeSel || ( this._.cache.nativeSel = this.document.getWindow().$.getSelection() );
+ },
+
+ /**
+ * Gets the type of the current selection. The following values are
+ * available:
+ * <ul>
+ * <li><code>{@link CKEDITOR.SELECTION_NONE}</code> (1): No selection.</li>
+ * <li><code>{@link CKEDITOR.SELECTION_TEXT}</code> (2): A text or a collapsed
+ * selection is selected.</li>
+ * <li><code>{@link CKEDITOR.SELECTION_ELEMENT}</code> (3): An element is
+ * selected.</li>
+ * </ul>
+ * @function
+ * @returns {Number} One of the following constant values:
+ * <code>{@link CKEDITOR.SELECTION_NONE}</code>, <code>{@link CKEDITOR.SELECTION_TEXT}</code>, or
+ * <code>{@link CKEDITOR.SELECTION_ELEMENT}</code>.
+ * @example
+ * if ( editor.getSelection().<strong>getType()</strong> == CKEDITOR.SELECTION_TEXT )
+ * alert( 'A text is selected' );
+ */
+ getType :
+ CKEDITOR.env.ie ?
+ function()
+ {
+ var cache = this._.cache;
+ if ( cache.type )
+ return cache.type;
+
+ var type = CKEDITOR.SELECTION_NONE;
+
+ try
+ {
+ var sel = this.getNative(),
+ ieType = sel.type;
+
+ if ( ieType == 'Text' )
+ type = CKEDITOR.SELECTION_TEXT;
+
+ if ( ieType == 'Control' )
+ type = CKEDITOR.SELECTION_ELEMENT;
+
+ // It is possible that we can still get a text range
+ // object even when type == 'None' is returned by IE.
+ // So we'd better check the object returned by
+ // createRange() rather than by looking at the type.
+ if ( sel.createRange().parentElement )
+ type = CKEDITOR.SELECTION_TEXT;
+ }
+ catch(e) {}
+
+ return ( cache.type = type );
+ }
+ :
+ function()
+ {
+ var cache = this._.cache;
+ if ( cache.type )
+ return cache.type;
+
+ var type = CKEDITOR.SELECTION_TEXT;
+
+ var sel = this.getNative();
+
+ if ( !sel )
+ type = CKEDITOR.SELECTION_NONE;
+ else if ( sel.rangeCount == 1 )
+ {
+ // Check if the actual selection is a control (IMG,
+ // TABLE, HR, etc...).
+
+ var range = sel.getRangeAt(0),
+ startContainer = range.startContainer;
+
+ if ( startContainer == range.endContainer
+ && startContainer.nodeType == 1
+ && ( range.endOffset - range.startOffset ) == 1
+ && styleObjectElements[ startContainer.childNodes[ range.startOffset ].nodeName.toLowerCase() ] )
+ {
+ type = CKEDITOR.SELECTION_ELEMENT;
+ }
+ }
+
+ return ( cache.type = type );
+ },
+
+ /**
+ * Retrieves the <code>{@link CKEDITOR.dom.range}</code> instances that represent the current selection.
+ * Note: Some browsers return multiple ranges even for a continuous selection. Firefox, for example, returns
+ * one range for each table cell when one or more table rows are selected.
+ * @function
+ * @param {Boolean} [onlyEditables] If set to <code>true</code>, this function retrives editable ranges only.
+ * @return {Array} Range instances that represent the current selection.
+ * @example
+ * var ranges = selection.<strong>getRanges()</strong>;
+ * alert( ranges.length );
+ */
+ getRanges : (function()
+ {
+ var func = CKEDITOR.env.ie ?
+ ( function()
+ {
+ function getNodeIndex( node ) { return new CKEDITOR.dom.node( node ).getIndex(); }
+
+ // Finds the container and offset for a specific boundary
+ // of an IE range.
+ var getBoundaryInformation = function( range, start )
+ {
+ // Creates a collapsed range at the requested boundary.
+ range = range.duplicate();
+ range.collapse( start );
+
+ // Gets the element that encloses the range entirely.
+ var parent = range.parentElement(),
+ doc = parent.ownerDocument;
+
+ // Empty parent element, e.g. <i>^</i>
+ if ( !parent.hasChildNodes() )
+ return { container : parent, offset : 0 };
+
+ var siblings = parent.children,
+ child,
+ sibling,
+ testRange = range.duplicate(),
+ startIndex = 0,
+ endIndex = siblings.length - 1,
+ index = -1,
+ position,
+ distance,
+ container;
+
+ // Binary search over all element childs to test the range to see whether
+ // range is right on the boundary of one element.
+ while ( startIndex <= endIndex )
+ {
+ index = Math.floor( ( startIndex + endIndex ) / 2 );
+ child = siblings[ index ];
+ testRange.moveToElementText( child );
+ position = testRange.compareEndPoints( 'StartToStart', range );
+
+ if ( position > 0 )
+ endIndex = index - 1;
+ else if ( position < 0 )
+ startIndex = index + 1;
+ else
+ {
+ // IE9 report wrong measurement with compareEndPoints when range anchors between two BRs.
+ // e.g. <p>text<br />^<br /></p> (#7433)
+ if ( CKEDITOR.env.ie9Compat && child.tagName == 'BR' )
+ {
+ // "Fall back" to w3c selection.
+ var sel = doc.defaultView.getSelection();
+ return { container : sel[ start ? 'anchorNode' : 'focusNode' ],
+ offset : sel[ start ? 'anchorOffset' : 'focusOffset' ] };
+ }
+ else
+ return { container : parent, offset : getNodeIndex( child ) };
+ }
+ }
+
+ // All childs are text nodes,
+ // or to the right hand of test range are all text nodes. (#6992)
+ if ( index == -1 || index == siblings.length - 1 && position < 0 )
+ {
+ // Adapt test range to embrace the entire parent contents.
+ testRange.moveToElementText( parent );
+ testRange.setEndPoint( 'StartToStart', range );
+
+ // IE report line break as CRLF with range.text but
+ // only LF with textnode.nodeValue, normalize them to avoid
+ // breaking character counting logic below. (#3949)
+ distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length;
+
+ siblings = parent.childNodes;
+
+ // Actual range anchor right beside test range at the boundary of text node.
+ if ( !distance )
+ {
+ child = siblings[ siblings.length - 1 ];
+
+ if ( child.nodeType != CKEDITOR.NODE_TEXT )
+ return { container : parent, offset : siblings.length };
+ else
+ return { container : child, offset : child.nodeValue.length };
+ }
+
+ // Start the measuring until distance overflows, meanwhile count the text nodes.
+ var i = siblings.length;
+ while ( distance > 0 && i > 0 )
+ {
+ sibling = siblings[ --i ];
+ if ( sibling.nodeType == CKEDITOR.NODE_TEXT )
+ {
+ container = sibling;
+ distance -= sibling.nodeValue.length;
+ }
+ }
+
+ return { container : container, offset : -distance };
+ }
+ // Test range was one offset beyond OR behind the anchored text node.
+ else
+ {
+ // Adapt one side of test range to the actual range
+ // for measuring the offset between them.
+ testRange.collapse( position > 0 ? true : false );
+ testRange.setEndPoint( position > 0 ? 'StartToStart' : 'EndToStart', range );
+
+ // IE report line break as CRLF with range.text but
+ // only LF with textnode.nodeValue, normalize them to avoid
+ // breaking character counting logic below. (#3949)
+ distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length;
+
+ // Actual range anchor right beside test range at the inner boundary of text node.
+ if ( !distance )
+ return { container : parent, offset : getNodeIndex( child ) + ( position > 0 ? 0 : 1 ) };
+
+ // Start the measuring until distance overflows, meanwhile count the text nodes.
+ while ( distance > 0 )
+ {
+ try
+ {
+ sibling = child[ position > 0 ? 'previousSibling' : 'nextSibling' ];
+ if ( sibling.nodeType == CKEDITOR.NODE_TEXT )
+ {
+ distance -= sibling.nodeValue.length;
+ container = sibling;
+ }
+ child = sibling;
+ }
+ // Measurement in IE could be somtimes wrong because of <select> element. (#4611)
+ catch( e )
+ {
+ return { container : parent, offset : getNodeIndex( child ) };
+ }
+ }
+
+ return { container : container, offset : position > 0 ? -distance : container.nodeValue.length + distance };
+ }
+ };
+
+ return function()
+ {
+ // IE doesn't have range support (in the W3C way), so we
+ // need to do some magic to transform selections into
+ // CKEDITOR.dom.range instances.
+
+ var sel = this.getNative(),
+ nativeRange = sel && sel.createRange(),
+ type = this.getType(),
+ range;
+
+ if ( !sel )
+ return [];
+
+ if ( type == CKEDITOR.SELECTION_TEXT )
+ {
+ range = new CKEDITOR.dom.range( this.document );
+
+ var boundaryInfo = getBoundaryInformation( nativeRange, true );
+ range.setStart( new CKEDITOR.dom.node( boundaryInfo.container ), boundaryInfo.offset );
+
+ boundaryInfo = getBoundaryInformation( nativeRange );
+ range.setEnd( new CKEDITOR.dom.node( boundaryInfo.container ), boundaryInfo.offset );
+
+ // Correct an invalid IE range case on empty list item. (#5850)
+ if ( range.endContainer.getPosition( range.startContainer ) & CKEDITOR.POSITION_PRECEDING
+ && range.endOffset <= range.startContainer.getIndex() )
+ {
+ range.collapse();
+ }
+
+ return [ range ];
+ }
+ else if ( type == CKEDITOR.SELECTION_ELEMENT )
+ {
+ var retval = [];
+
+ for ( var i = 0 ; i < nativeRange.length ; i++ )
+ {
+ var element = nativeRange.item( i ),
+ parentElement = element.parentNode,
+ j = 0;
+
+ range = new CKEDITOR.dom.range( this.document );
+
+ for (; j < parentElement.childNodes.length && parentElement.childNodes[j] != element ; j++ )
+ { /*jsl:pass*/ }
+
+ range.setStart( new CKEDITOR.dom.node( parentElement ), j );
+ range.setEnd( new CKEDITOR.dom.node( parentElement ), j + 1 );
+ retval.push( range );
+ }
+
+ return retval;
+ }
+
+ return [];
+ };
+ })()
+ :
+ function()
+ {
+
+ // On browsers implementing the W3C range, we simply
+ // tranform the native ranges in CKEDITOR.dom.range
+ // instances.
+
+ var ranges = [],
+ range,
+ doc = this.document,
+ sel = this.getNative();
+
+ if ( !sel )
+ return ranges;
+
+ // On WebKit, it may happen that we'll have no selection
+ // available. We normalize it here by replicating the
+ // behavior of other browsers.
+ if ( !sel.rangeCount )
+ {
+ range = new CKEDITOR.dom.range( doc );
+ range.moveToElementEditStart( doc.getBody() );
+ ranges.push( range );
+ }
+
+ for ( var i = 0 ; i < sel.rangeCount ; i++ )
+ {
+ var nativeRange = sel.getRangeAt( i );
+
+ range = new CKEDITOR.dom.range( doc );
+
+ range.setStart( new CKEDITOR.dom.node( nativeRange.startContainer ), nativeRange.startOffset );
+ range.setEnd( new CKEDITOR.dom.node( nativeRange.endContainer ), nativeRange.endOffset );
+ ranges.push( range );
+ }
+ return ranges;
+ };
+
+ return function( onlyEditables )
+ {
+ var cache = this._.cache;
+ if ( cache.ranges && !onlyEditables )
+ return cache.ranges;
+ else if ( !cache.ranges )
+ cache.ranges = new CKEDITOR.dom.rangeList( func.call( this ) );
+
+ // Split range into multiple by read-only nodes.
+ if ( onlyEditables )
+ {
+ var ranges = cache.ranges;
+ for ( var i = 0; i < ranges.length; i++ )
+ {
+ var range = ranges[ i ];
+
+ // Drop range spans inside one ready-only node.
+ var parent = range.getCommonAncestor();
+ if ( parent.isReadOnly() )
+ ranges.splice( i, 1 );
+
+ if ( range.collapsed )
+ continue;
+
+ // Range may start inside a non-editable element,
+ // replace the range start after it.
+ if ( range.startContainer.isReadOnly() )
+ {
+ var current = range.startContainer;
+ while( current )
+ {
+ if ( current.is( 'body' ) || !current.isReadOnly() )
+ break;
+
+ if ( current.type == CKEDITOR.NODE_ELEMENT
+ && current.getAttribute( 'contentEditable' ) == 'false' )
+ range.setStartAfter( current );
+
+ current = current.getParent();
+ }
+ }
+
+ var startContainer = range.startContainer,
+ endContainer = range.endContainer,
+ startOffset = range.startOffset,
+ endOffset = range.endOffset,
+ walkerRange = range.clone();
+
+ // Enlarge range start/end with text node to avoid walker
+ // being DOM destructive, it doesn't interfere our checking
+ // of elements below as well.
+ if ( startContainer && startContainer.type == CKEDITOR.NODE_TEXT )
+ {
+ if ( startOffset >= startContainer.getLength() )
+ walkerRange.setStartAfter( startContainer );
+ else
+ walkerRange.setStartBefore( startContainer );
+ }
+
+ if ( endContainer && endContainer.type == CKEDITOR.NODE_TEXT )
+ {
+ if ( !endOffset )
+ walkerRange.setEndBefore( endContainer );
+ else
+ walkerRange.setEndAfter( endContainer );
+ }
+
+ // Looking for non-editable element inside the range.
+ var walker = new CKEDITOR.dom.walker( walkerRange );
+ walker.evaluator = function( node )
+ {
+ if ( node.type == CKEDITOR.NODE_ELEMENT
+ && node.isReadOnly() )
+ {
+ var newRange = range.clone();
+ range.setEndBefore( node );
+
+ // Drop collapsed range around read-only elements,
+ // it make sure the range list empty when selecting
+ // only non-editable elements.
+ if ( range.collapsed )
+ ranges.splice( i--, 1 );
+
+ // Avoid creating invalid range.
+ if ( !( node.getPosition( walkerRange.endContainer ) & CKEDITOR.POSITION_CONTAINS ) )
+ {
+ newRange.setStartAfter( node );
+ if ( !newRange.collapsed )
+ ranges.splice( i + 1, 0, newRange );
+ }
+
+ return true;
+ }
+
+ return false;
+ };
+
+ walker.next();
+ }
+ }
+
+ return cache.ranges;
+ };
+ })(),
+
+ /**
+ * Gets the DOM element in which the selection starts.
+ * @returns {CKEDITOR.dom.element} The element at the beginning of the
+ * selection.
+ * @example
+ * var element = editor.getSelection().<strong>getStartElement()</strong>;
+ * alert( element.getName() );
+ */
+ getStartElement : function()
+ {
+ var cache = this._.cache;
+ if ( cache.startElement !== undefined )
+ return cache.startElement;
+
+ var node,
+ sel = this.getNative();
+
+ switch ( this.getType() )
+ {
+ case CKEDITOR.SELECTION_ELEMENT :
+ return this.getSelectedElement();
+
+ case CKEDITOR.SELECTION_TEXT :
+
+ var range = this.getRanges()[0];
+
+ if ( range )
+ {
+ if ( !range.collapsed )
+ {
+ range.optimize();
+
+ // Decrease the range content to exclude particial
+ // selected node on the start which doesn't have
+ // visual impact. ( #3231 )
+ while ( 1 )
+ {
+ var startContainer = range.startContainer,
+ startOffset = range.startOffset;
+ // Limit the fix only to non-block elements.(#3950)
+ if ( startOffset == ( startContainer.getChildCount ?
+ startContainer.getChildCount() : startContainer.getLength() )
+ && !startContainer.isBlockBoundary() )
+ range.setStartAfter( startContainer );
+ else break;
+ }
+
+ node = range.startContainer;
+
+ if ( node.type != CKEDITOR.NODE_ELEMENT )
+ return node.getParent();
+
+ node = node.getChild( range.startOffset );
+
+ if ( !node || node.type != CKEDITOR.NODE_ELEMENT )
+ node = range.startContainer;
+ else
+ {
+ var child = node.getFirst();
+ while ( child && child.type == CKEDITOR.NODE_ELEMENT )
+ {
+ node = child;
+ child = child.getFirst();
+ }
+ }
+ }
+ else
+ {
+ node = range.startContainer;
+ if ( node.type != CKEDITOR.NODE_ELEMENT )
+ node = node.getParent();
+ }
+
+ node = node.$;
+ }
+ }
+
+ return cache.startElement = ( node ? new CKEDITOR.dom.element( node ) : null );
+ },
+
+ /**
+ * Gets the currently selected element.
+ * @returns {CKEDITOR.dom.element} The selected element. Null if no
+ * selection is available or the selection type is not
+ * <code>{@link CKEDITOR.SELECTION_ELEMENT}</code>.
+ * @example
+ * var element = editor.getSelection().<strong>getSelectedElement()</strong>;
+ * alert( element.getName() );
+ */
+ getSelectedElement : function()
+ {
+ var cache = this._.cache;
+ if ( cache.selectedElement !== undefined )
+ return cache.selectedElement;
+
+ var self = this;
+
+ var node = CKEDITOR.tools.tryThese(
+ // Is it native IE control type selection?
+ function()
+ {
+ return self.getNative().createRange().item( 0 );
+ },
+ // If a table or list is fully selected.
+ function()
+ {
+ var root,
+ retval,
+ range = self.getRanges()[ 0 ],
+ ancestor = range.getCommonAncestor( 1, 1 ),
+ tags = { table:1,ul:1,ol:1,dl:1 };
+
+ for ( var t in tags )
+ {
+ if ( ( root = ancestor.getAscendant( t, 1 ) ) )
+ break;
+ }
+
+ if ( root )
+ {
+ // Enlarging the start boundary.
+ var testRange = new CKEDITOR.dom.range( this.document );
+ testRange.setStartAt( root, CKEDITOR.POSITION_AFTER_START );
+ testRange.setEnd( range.startContainer, range.startOffset );
+
+ var enlargeables = CKEDITOR.tools.extend( tags, CKEDITOR.dtd.$listItem, CKEDITOR.dtd.$tableContent ),
+ walker = new CKEDITOR.dom.walker( testRange ),
+ // Check the range is at the inner boundary of the structural element.
+ guard = function( walker, isEnd )
+ {
+ return function( node, isWalkOut )
+ {
+ if ( node.type == CKEDITOR.NODE_TEXT && ( !CKEDITOR.tools.trim( node.getText() ) || node.getParent().data( 'cke-bookmark' ) ) )
+ return true;
+
+ var tag;
+ if ( node.type == CKEDITOR.NODE_ELEMENT )
+ {
+ tag = node.getName();
+
+ // Bypass bogus br at the end of block.
+ if ( tag == 'br' && isEnd && node.equals( node.getParent().getBogus() ) )
+ return true;
+
+ if ( isWalkOut && tag in enlargeables || tag in CKEDITOR.dtd.$removeEmpty )
+ return true;
+ }
+
+ walker.halted = 1;
+ return false;
+ };
+ };
+
+ walker.guard = guard( walker );
+
+ if ( walker.checkBackward() && !walker.halted )
+ {
+ walker = new CKEDITOR.dom.walker( testRange );
+ testRange.setStart( range.endContainer, range.endOffset );
+ testRange.setEndAt( root, CKEDITOR.POSITION_BEFORE_END );
+ walker.guard = guard( walker, 1 );
+ if ( walker.checkForward() && !walker.halted )
+ retval = root.$;
+ }
+ }
+
+ if ( !retval )
+ throw 0;
+
+ return retval;
+ },
+ // Figure it out by checking if there's a single enclosed
+ // node of the range.
+ function()
+ {
+ var range = self.getRanges()[ 0 ],
+ enclosed,
+ selected;
+
+ // Check first any enclosed element, e.g. <ul>[<li><a href="#">item</a></li>]</ul>
+ for ( var i = 2; i && !( ( enclosed = range.getEnclosedNode() )
+ && ( enclosed.type == CKEDITOR.NODE_ELEMENT )
+ && styleObjectElements[ enclosed.getName() ]
+ && ( selected = enclosed ) ); i-- )
+ {
+ // Then check any deep wrapped element, e.g. [<b><i><img /></i></b>]
+ range.shrink( CKEDITOR.SHRINK_ELEMENT );
+ }
+
+ return selected.$;
+ });
+
+ return cache.selectedElement = ( node ? new CKEDITOR.dom.element( node ) : null );
+ },
+
+ /**
+ * Retrieves the text contained within the range. An empty string is returned for non-text selection.
+ * @returns {String} A string of text within the current selection.
+ * @since 3.6.1
+ * @example
+ * var text = editor.getSelection().<strong>getSelectedText()</strong>;
+ * alert( text );
+ */
+ getSelectedText : function()
+ {
+ var cache = this._.cache;
+ if ( cache.selectedText !== undefined )
+ return cache.selectedText;
+
+ var text = '',
+ nativeSel = this.getNative();
+ if ( this.getType() == CKEDITOR.SELECTION_TEXT )
+ text = CKEDITOR.env.ie ? nativeSel.createRange().text : nativeSel.toString();
+
+ return ( cache.selectedText = text );
+ },
+
+ /**
+ * Locks the selection made in the editor in order to make it possible to
+ * manipulate it without browser interference. A locked selection is
+ * cached and remains unchanged until it is released with the <code>#unlock</code>
+ * method.
+ * @example
+ * editor.getSelection().<strong>lock()</strong>;
+ */
+ lock : function()
+ {
+ // Call all cacheable function.
+ this.getRanges();
+ this.getStartElement();
+ this.getSelectedElement();
+ this.getSelectedText();
+
+ // The native selection is not available when locked.
+ this._.cache.nativeSel = {};
+
+ this.isLocked = 1;
+
+ // Save this selection inside the DOM document.
+ this.document.setCustomData( 'cke_locked_selection', this );
+ },
+
+ /**
+ * Unlocks the selection made in the editor and locked with the <code>#lock</code> method.
+ * An unlocked selection is no longer cached and can be changed.
+ * @param {Boolean} [restore] If set to <code>true</code>, the selection is restored back to the selection saved earlier by using the <code>#lock</code> method.
+ * @example
+ * editor.getSelection().<strong>unlock()</strong>;
+ */
+ unlock : function( restore )
+ {
+ var doc = this.document,
+ lockedSelection = doc.getCustomData( 'cke_locked_selection' );
+
+ if ( lockedSelection )
+ {
+ doc.setCustomData( 'cke_locked_selection', null );
+
+ if ( restore )
+ {
+ var selectedElement = lockedSelection.getSelectedElement(),
+ ranges = !selectedElement && lockedSelection.getRanges();
+
+ this.isLocked = 0;
+ this.reset();
+
+ doc.getBody().focus();
+
+ if ( selectedElement )
+ this.selectElement( selectedElement );
+ else
+ this.selectRanges( ranges );
+ }
+ }
+
+ if ( !lockedSelection || !restore )
+ {
+ this.isLocked = 0;
+ this.reset();
+ }
+ },
+
+ /**
+ * Clears the selection cache.
+ * @example
+ * editor.getSelection().<strong>reset()</strong>;
+ */
+ reset : function()
+ {
+ this._.cache = {};
+ },
+
+ /**
+ * Makes the current selection of type <code>{@link CKEDITOR.SELECTION_ELEMENT}</code> by enclosing the specified element.
+ * @param {CKEDITOR.dom.element} element The element to enclose in the selection.
+ * @example
+ * var element = editor.document.getById( 'sampleElement' );
+ * editor.getSelection.<strong>selectElement( element )</strong>;
+ */
+ selectElement : function( element )
+ {
+ if ( this.isLocked )
+ {
+ var range = new CKEDITOR.dom.range( this.document );
+ range.setStartBefore( element );
+ range.setEndAfter( element );
+
+ this._.cache.selectedElement = element;
+ this._.cache.startElement = element;
+ this._.cache.ranges = new CKEDITOR.dom.rangeList( range );
+ this._.cache.type = CKEDITOR.SELECTION_ELEMENT;
+
+ return;
+ }
+
+ range = new CKEDITOR.dom.range( element.getDocument() );
+ range.setStartBefore( element );
+ range.setEndAfter( element );
+ range.select();
+
+ this.document.fire( 'selectionchange' );
+ this.reset();
+
+ },
+
+ /**
+ * Clears the original selection and adds the specified ranges
+ * to the document selection.
+ * @param {Array} ranges An array of <code>{@link CKEDITOR.dom.range}</code> instances representing ranges to be added to the document.
+ * @example
+ * var ranges = new CKEDITOR.dom.range( editor.document );
+ * editor.getSelection().<strong>selectRanges( [ ranges ] )</strong>;
+ */
+ selectRanges : function( ranges )
+ {
+ if ( this.isLocked )
+ {
+ this._.cache.selectedElement = null;
+ this._.cache.startElement = ranges[ 0 ] && ranges[ 0 ].getTouchedStartNode();
+ this._.cache.ranges = new CKEDITOR.dom.rangeList( ranges );
+ this._.cache.type = CKEDITOR.SELECTION_TEXT;
+
+ return;
+ }
+
+ if ( CKEDITOR.env.ie )
+ {
+ if ( ranges.length > 1 )
+ {
+ // IE doesn't accept multiple ranges selection, so we join all into one.
+ var last = ranges[ ranges.length -1 ] ;
+ ranges[ 0 ].setEnd( last.endContainer, last.endOffset );
+ ranges.length = 1;
+ }
+
+ if ( ranges[ 0 ] )
+ ranges[ 0 ].select();
+
+ this.reset();
+ }
+ else
+ {
+ var sel = this.getNative();
+
+ // getNative() returns null if iframe is "display:none" in FF. (#6577)
+ if ( !sel )
+ return;
+
+ if ( ranges.length )
+ {
+ sel.removeAllRanges();
+ // Remove any existing filling char first.
+ CKEDITOR.env.webkit && removeFillingChar( this.document );
+ }
+
+ for ( var i = 0 ; i < ranges.length ; i++ )
+ {
+ // Joining sequential ranges introduced by
+ // readonly elements protection.
+ if ( i < ranges.length -1 )
+ {
+ var left = ranges[ i ], right = ranges[ i +1 ],
+ between = left.clone();
+ between.setStart( left.endContainer, left.endOffset );
+ between.setEnd( right.startContainer, right.startOffset );
+
+ // Don't confused by Firefox adjancent multi-ranges
+ // introduced by table cells selection.
+ if ( !between.collapsed )
+ {
+ between.shrink( CKEDITOR.NODE_ELEMENT, true );
+ var ancestor = between.getCommonAncestor(),
+ enclosed = between.getEnclosedNode();
+
+ // The following cases has to be considered:
+ // 1. <span contenteditable="false">[placeholder]</span>
+ // 2. <input contenteditable="false" type="radio"/> (#6621)
+ if ( ancestor.isReadOnly() || enclosed && enclosed.isReadOnly() )
+ {
+ right.setStart( left.startContainer, left.startOffset );
+ ranges.splice( i--, 1 );
+ continue;
+ }
+ }
+ }
+
+ var range = ranges[ i ];
+ var nativeRange = this.document.$.createRange();
+ var startContainer = range.startContainer;
+
+ // In FF2, if we have a collapsed range, inside an empty
+ // element, we must add something to it otherwise the caret
+ // will not be visible.
+ // In Opera instead, the selection will be moved out of the
+ // element. (#4657)
+ if ( range.collapsed &&
+ ( CKEDITOR.env.opera || ( CKEDITOR.env.gecko && CKEDITOR.env.version < 10900 ) ) &&
+ startContainer.type == CKEDITOR.NODE_ELEMENT &&
+ !startContainer.getChildCount() )
+ {
+ startContainer.appendText( '' );
+ }
+
+ if ( range.collapsed
+ && CKEDITOR.env.webkit
+ && rangeRequiresFix( range ) )
+ {
+ // Append a zero-width space so WebKit will not try to
+ // move the selection by itself (#1272).
+ var fillingChar = createFillingChar( this.document );
+ range.insertNode( fillingChar ) ;
+
+ var next = fillingChar.getNext();
+
+ // If the filling char is followed by a <br>, whithout
+ // having something before it, it'll not blink.
+ // Let's remove it in this case.
+ if ( next && !fillingChar.getPrevious() && next.type == CKEDITOR.NODE_ELEMENT && next.getName() == 'br' )
+ {
+ removeFillingChar( this.document );
+ range.moveToPosition( next, CKEDITOR.POSITION_BEFORE_START );
+ }
+ else
+ range.moveToPosition( fillingChar, CKEDITOR.POSITION_AFTER_END );
+ }
+
+ nativeRange.setStart( range.startContainer.$, range.startOffset );
+
+ try
+ {
+ nativeRange.setEnd( range.endContainer.$, range.endOffset );
+ }
+ catch ( e )
+ {
+ // There is a bug in Firefox implementation (it would be too easy
+ // otherwise). The new start can't be after the end (W3C says it can).
+ // So, let's create a new range and collapse it to the desired point.
+ if ( e.toString().indexOf( 'NS_ERROR_ILLEGAL_VALUE' ) >= 0 )
+ {
+ range.collapse( 1 );
+ nativeRange.setEnd( range.endContainer.$, range.endOffset );
+ }
+ else
+ throw e;
+ }
+
+ // Select the range.
+ sel.addRange( nativeRange );
+ }
+
+ // Don't miss selection change event for non-IEs.
+ this.document.fire( 'selectionchange' );
+ this.reset();
+ }
+ },
+
+ /**
+ * Creates a bookmark for each range of this selection (from <code>#getRanges</code>)
+ * by calling the <code>{@link CKEDITOR.dom.range.prototype.createBookmark}</code> method,
+ * with extra care taken to avoid interference among those ranges. The arguments
+ * received are the same as with the underlying range method.
+ * @returns {Array} Array of bookmarks for each range.
+ * @example
+ * var bookmarks = editor.getSelection().<strong>createBookmarks()</strong>;
+ */
+ createBookmarks : function( serializable )
+ {
+ return this.getRanges().createBookmarks( serializable );
+ },
+
+ /**
+ * Creates a bookmark for each range of this selection (from <code>#getRanges</code>)
+ * by calling the <code>{@link CKEDITOR.dom.range.prototype.createBookmark2}</code> method,
+ * with extra care taken to avoid interference among those ranges. The arguments
+ * received are the same as with the underlying range method.
+ * @returns {Array} Array of bookmarks for each range.
+ * @example
+ * var bookmarks = editor.getSelection().<strong>createBookmarks2()</strong>;
+ */
+ createBookmarks2 : function( normalized )
+ {
+ return this.getRanges().createBookmarks2( normalized );
+ },
+
+ /**
+ * Selects the virtual ranges denoted by the bookmarks by calling <code>#selectRanges</code>.
+ * @param {Array} bookmarks The bookmarks representing ranges to be selected.
+ * @returns {CKEDITOR.dom.selection} This selection object, after the ranges were selected.
+ * @example
+ * var bookmarks = editor.getSelection().createBookmarks();
+ * editor.getSelection().<strong>selectBookmarks( bookmarks )</strong>;
+ */
+ selectBookmarks : function( bookmarks )
+ {
+ var ranges = [];
+ for ( var i = 0 ; i < bookmarks.length ; i++ )
+ {
+ var range = new CKEDITOR.dom.range( this.document );
+ range.moveToBookmark( bookmarks[i] );
+ ranges.push( range );
+ }
+ this.selectRanges( ranges );
+ return this;
+ },
+
+ /**
+ * Retrieves the common ancestor node of the first range and the last range.
+ * @returns {CKEDITOR.dom.element} The common ancestor of the selection.
+ * @example
+ * var ancestor = editor.getSelection().<strong>getCommonAncestor()</strong>;
+ */
+ getCommonAncestor : function()
+ {
+ var ranges = this.getRanges(),
+ startNode = ranges[ 0 ].startContainer,
+ endNode = ranges[ ranges.length - 1 ].endContainer;
+ return startNode.getCommonAncestor( endNode );
+ },
+
+ /**
+ * Moves the scrollbar to the starting position of the current selection.
+ * @example
+ * editor.getSelection().<strong>scrollIntoView()</strong>;
+ */
+ scrollIntoView : function()
+ {
+ // If we have split the block, adds a temporary span at the
+ // range position and scroll relatively to it.
+ var start = this.getStartElement();
+ start.scrollIntoView();
+ }
+ };
+})();
+
+( function()
+{
+ var notWhitespaces = CKEDITOR.dom.walker.whitespaces( true ),
+ fillerTextRegex = /\ufeff|\u00a0/,
+ nonCells = { table:1,tbody:1,tr:1 };
+
+ CKEDITOR.dom.range.prototype.select =
+ CKEDITOR.env.ie ?
+ // V2
+ function( forceExpand )
+ {
+ var collapsed = this.collapsed,
+ isStartMarkerAlone, dummySpan, ieRange;
+
+ // Try to make a object selection.
+ var selected = this.getEnclosedNode();
+ if ( selected )
+ {
+ try
+ {
+ ieRange = this.document.$.body.createControlRange();
+ ieRange.addElement( selected.$ );
+ ieRange.select();
+ return;
+ }
+ catch( er ) {}
+ }
+
+ // IE doesn't support selecting the entire table row/cell, move the selection into cells, e.g.
+ // <table><tbody><tr>[<td>cell</b></td>... => <table><tbody><tr><td>[cell</td>...
+ if ( this.startContainer.type == CKEDITOR.NODE_ELEMENT && this.startContainer.getName() in nonCells
+ || this.endContainer.type == CKEDITOR.NODE_ELEMENT && this.endContainer.getName() in nonCells )
+ {
+ this.shrink( CKEDITOR.NODE_ELEMENT, true );
+ }
+
+ var bookmark = this.createBookmark();
+
+ // Create marker tags for the start and end boundaries.
+ var startNode = bookmark.startNode;
+
+ var endNode;
+ if ( !collapsed )
+ endNode = bookmark.endNode;
+
+ // Create the main range which will be used for the selection.
+ ieRange = this.document.$.body.createTextRange();
+
+ // Position the range at the start boundary.
+ ieRange.moveToElementText( startNode.$ );
+ ieRange.moveStart( 'character', 1 );
+
+ if ( endNode )
+ {
+ // Create a tool range for the end.
+ var ieRangeEnd = this.document.$.body.createTextRange();
+
+ // Position the tool range at the end.
+ ieRangeEnd.moveToElementText( endNode.$ );
+
+ // Move the end boundary of the main range to match the tool range.
+ ieRange.setEndPoint( 'EndToEnd', ieRangeEnd );
+ ieRange.moveEnd( 'character', -1 );
+ }
+ else
+ {
+ // The isStartMarkerAlone logic comes from V2. It guarantees that the lines
+ // will expand and that the cursor will be blinking on the right place.
+ // Actually, we are using this flag just to avoid using this hack in all
+ // situations, but just on those needed.
+ var next = startNode.getNext( notWhitespaces );
+ isStartMarkerAlone = ( !( next && next.getText && next.getText().match( fillerTextRegex ) ) // already a filler there?
+ && ( forceExpand || !startNode.hasPrevious() || ( startNode.getPrevious().is && startNode.getPrevious().is( 'br' ) ) ) );
+
+ // Append a temporary <span></span> before the selection.
+ // This is needed to avoid IE destroying selections inside empty
+ // inline elements, like <b></b> (#253).
+ // It is also needed when placing the selection right after an inline
+ // element to avoid the selection moving inside of it.
+ dummySpan = this.document.createElement( 'span' );
+ dummySpan.setHtml( '' ); // Zero Width No-Break Space (U+FEFF). See #1359.
+ dummySpan.insertBefore( startNode );
+
+ if ( isStartMarkerAlone )
+ {
+ // To expand empty blocks or line spaces after <br>, we need
+ // instead to have any char, which will be later deleted using the
+ // selection.
+ // \ufeff = Zero Width No-Break Space (U+FEFF). (#1359)
+ this.document.createText( '\ufeff' ).insertBefore( startNode );
+ }
+ }
+
+ // Remove the markers (reset the position, because of the changes in the DOM tree).
+ this.setStartBefore( startNode );
+ startNode.remove();
+
+ if ( collapsed )
+ {
+ if ( isStartMarkerAlone )
+ {
+ // Move the selection start to include the temporary \ufeff.
+ ieRange.moveStart( 'character', -1 );
+
+ ieRange.select();
+
+ // Remove our temporary stuff.
+ this.document.$.selection.clear();
+ }
+ else
+ ieRange.select();
+
+ this.moveToPosition( dummySpan, CKEDITOR.POSITION_BEFORE_START );
+ dummySpan.remove();
+ }
+ else
+ {
+ this.setEndBefore( endNode );
+ endNode.remove();
+ ieRange.select();
+ }
+
+ this.document.fire( 'selectionchange' );
+ }
+ :
+ function()
+ {
+ this.document.getSelection().selectRanges( [ this ] );
+ };
+} )();
diff --git a/_source/plugins/showblocks/images/block_address.png b/_source/plugins/showblocks/images/block_address.png Binary files differindex 8bbae6e..1ee67aa 100644 --- a/_source/plugins/showblocks/images/block_address.png +++ b/_source/plugins/showblocks/images/block_address.png diff --git a/_source/plugins/showblocks/images/block_blockquote.png b/_source/plugins/showblocks/images/block_blockquote.png Binary files differindex cf065ba..cae3aec 100644 --- a/_source/plugins/showblocks/images/block_blockquote.png +++ b/_source/plugins/showblocks/images/block_blockquote.png diff --git a/_source/plugins/showblocks/images/block_div.png b/_source/plugins/showblocks/images/block_div.png Binary files differindex a2806b1..c71f397 100644 --- a/_source/plugins/showblocks/images/block_div.png +++ b/_source/plugins/showblocks/images/block_div.png diff --git a/_source/plugins/showblocks/images/block_h1.png b/_source/plugins/showblocks/images/block_h1.png Binary files differindex d43fbdb..3a64347 100644 --- a/_source/plugins/showblocks/images/block_h1.png +++ b/_source/plugins/showblocks/images/block_h1.png diff --git a/_source/plugins/showblocks/images/block_h2.png b/_source/plugins/showblocks/images/block_h2.png Binary files differindex 27b5475..8062ebe 100644 --- a/_source/plugins/showblocks/images/block_h2.png +++ b/_source/plugins/showblocks/images/block_h2.png diff --git a/_source/plugins/showblocks/images/block_h3.png b/_source/plugins/showblocks/images/block_h3.png Binary files differindex 3c3034f..5b6a403 100644 --- a/_source/plugins/showblocks/images/block_h3.png +++ b/_source/plugins/showblocks/images/block_h3.png diff --git a/_source/plugins/showblocks/images/block_h4.png b/_source/plugins/showblocks/images/block_h4.png Binary files differindex ab3f64d..6c7f795 100644 --- a/_source/plugins/showblocks/images/block_h4.png +++ b/_source/plugins/showblocks/images/block_h4.png diff --git a/_source/plugins/showblocks/images/block_h5.png b/_source/plugins/showblocks/images/block_h5.png Binary files differindex 93477d0..e153de0 100644 --- a/_source/plugins/showblocks/images/block_h5.png +++ b/_source/plugins/showblocks/images/block_h5.png diff --git a/_source/plugins/showblocks/images/block_h6.png b/_source/plugins/showblocks/images/block_h6.png Binary files differindex 11ea1c2..c8d993a 100644 --- a/_source/plugins/showblocks/images/block_h6.png +++ b/_source/plugins/showblocks/images/block_h6.png diff --git a/_source/plugins/showblocks/images/block_p.png b/_source/plugins/showblocks/images/block_p.png Binary files differindex d055c51..4e6035d 100644 --- a/_source/plugins/showblocks/images/block_p.png +++ b/_source/plugins/showblocks/images/block_p.png diff --git a/_source/plugins/showblocks/images/block_pre.png b/_source/plugins/showblocks/images/block_pre.png Binary files differindex be8ad26..d11a0ff 100644 --- a/_source/plugins/showblocks/images/block_pre.png +++ b/_source/plugins/showblocks/images/block_pre.png diff --git a/_source/plugins/showblocks/plugin.js b/_source/plugins/showblocks/plugin.js index 552cbe4..de95124 100644 --- a/_source/plugins/showblocks/plugin.js +++ b/_source/plugins/showblocks/plugin.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
@@ -24,9 +24,10 @@ For licensing, see LICENSE.html or http://ckeditor.com/license '.%2 h6'+
'{'+
'background-repeat: no-repeat;'+
+ 'background-position: top %3;'+
'border: 1px dotted gray;'+
'padding-top: 8px;'+
- 'padding-left: 8px;'+
+ 'padding-%3: 8px;'+
'}'+
'.%2 p'+
@@ -84,10 +85,11 @@ For licensing, see LICENSE.html or http://ckeditor.com/license '%1h6.png);'+
'}';
- var cssTemplateRegex = /%1/g, cssClassRegex = /%2/g;
+ var cssTemplateRegex = /%1/g, cssClassRegex = /%2/g, backgroundPositionRegex = /%3/g;
var commandDefinition =
{
+ readOnly : 1,
preserveState : true,
editorFocus : false,
@@ -99,8 +101,11 @@ For licensing, see LICENSE.html or http://ckeditor.com/license refresh : function( editor )
{
- var funcName = ( this.state == CKEDITOR.TRISTATE_ON ) ? 'addClass' : 'removeClass';
- editor.document.getBody()[ funcName ]( 'cke_show_blocks' );
+ if ( editor.document )
+ {
+ var funcName = ( this.state == CKEDITOR.TRISTATE_ON ) ? 'addClass' : 'removeClass';
+ editor.document.getBody()[ funcName ]( 'cke_show_blocks' );
+ }
}
};
@@ -118,7 +123,8 @@ For licensing, see LICENSE.html or http://ckeditor.com/license editor.addCss( cssTemplate
.replace( cssTemplateRegex, 'background-image: url(' + CKEDITOR.getUrl( this.path ) + 'images/block_' )
- .replace( cssClassRegex, 'cke_show_blocks ' ) );
+ .replace( cssClassRegex, 'cke_show_blocks ' )
+ .replace( backgroundPositionRegex, editor.lang.dir == 'rtl' ? 'right' : 'left' ) );
editor.ui.addButton( 'ShowBlocks',
{
@@ -145,10 +151,10 @@ For licensing, see LICENSE.html or http://ckeditor.com/license /**
* Whether to automaticaly enable the "show block" command when the editor
- * loads.
+ * loads. (StartupShowBlocks in FCKeditor)
+ * @name CKEDITOR.config.startupOutlineBlocks
* @type Boolean
* @default false
* @example
* config.startupOutlineBlocks = true;
*/
-CKEDITOR.config.startupOutlineBlocks = false;
diff --git a/_source/plugins/showborders/plugin.js b/_source/plugins/showborders/plugin.js index dc13d24..846a12b 100644 --- a/_source/plugins/showborders/plugin.js +++ b/_source/plugins/showborders/plugin.js @@ -1,167 +1,207 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -/** - * @fileOverview The "show border" plugin. The command display visible outline - * border line around all table elements if table doesn't have a none-zero 'border' attribute specified. - */ - -(function() -{ - var showBorderClassName = 'cke_show_border', - cssStyleText, - cssTemplate = - // TODO: For IE6, we don't have child selector support, - // where nested table cells could be incorrect. - ( CKEDITOR.env.ie6Compat ? - [ - '.%1 table.%2,', - '.%1 table.%2 td, .%1 table.%2 th,', - '{', - 'border : #d3d3d3 1px dotted', - '}' - ] : - [ - '.%1 table.%2,', - '.%1 table.%2 > tr > td, .%1 table.%2 > tr > th,', - '.%1 table.%2 > tbody > tr > td, .%1 table.%2 > tbody > tr > th,', - '.%1 table.%2 > thead > tr > td, .%1 table.%2 > thead > tr > th,', - '.%1 table.%2 > tfoot > tr > td, .%1 table.%2 > tfoot > tr > th', - '{', - 'border : #d3d3d3 1px dotted', - '}' - ] ).join( '' ); - - cssStyleText = cssTemplate.replace( /%2/g, showBorderClassName ).replace( /%1/g, 'cke_show_borders ' ); - - var commandDefinition = - { - preserveState : true, - editorFocus : false, - - exec : function ( editor ) - { - this.toggleState(); - this.refresh( editor ); - }, - - refresh : function( editor ) - { - var funcName = ( this.state == CKEDITOR.TRISTATE_ON ) ? 'addClass' : 'removeClass'; - editor.document.getBody()[ funcName ]( 'cke_show_borders' ); - } - }; - - CKEDITOR.plugins.add( 'showborders', - { - requires : [ 'wysiwygarea' ], - modes : { 'wysiwyg' : 1 }, - - init : function( editor ) - { - - var command = editor.addCommand( 'showborders', commandDefinition ); - command.canUndo = false; - - if ( editor.config.startupShowBorders !== false ) - command.setState( CKEDITOR.TRISTATE_ON ); - - editor.addCss( cssStyleText ); - - // Refresh the command on setData. - editor.on( 'mode', function() - { - if ( command.state != CKEDITOR.TRISTATE_DISABLED ) - command.refresh( editor ); - }, null, null, 100 ); - - // Refresh the command on wysiwyg frame reloads. - editor.on( 'contentDom', function() - { - if ( command.state != CKEDITOR.TRISTATE_DISABLED ) - command.refresh( editor ); - }); - }, - - afterInit : function( editor ) - { - var dataProcessor = editor.dataProcessor, - dataFilter = dataProcessor && dataProcessor.dataFilter, - htmlFilter = dataProcessor && dataProcessor.htmlFilter; - - if ( dataFilter ) - { - dataFilter.addRules( - { - elements : - { - 'table' : function( element ) - { - var attributes = element.attributes, - cssClass = attributes[ 'class' ], - border = parseInt( attributes.border, 10 ); - - if ( !border || border <= 0 ) - attributes[ 'class' ] = ( cssClass || '' ) + ' ' + showBorderClassName; - } - } - } ); - } - - if ( htmlFilter ) - { - htmlFilter.addRules( - { - elements : - { - 'table' : function( table ) - { - var attributes = table.attributes, - cssClass = attributes[ 'class' ]; - - cssClass && ( attributes[ 'class' ] = - cssClass.replace( showBorderClassName, '' ) - .replace( /\s{2}/, ' ' ) - .replace( /^\s+|\s+$/, '' ) ); - } - } - } ); - } - } - }); - - // Table dialog must be aware of it. - CKEDITOR.on( 'dialogDefinition', function( ev ) - { - var dialogName = ev.data.name; - - if ( dialogName == 'table' || dialogName == 'tableProperties' ) - { - var dialogDefinition = ev.data.definition, - infoTab = dialogDefinition.getContents( 'info' ), - borderField = infoTab.get( 'txtBorder' ), - originalCommit = borderField.commit; - - borderField.commit = CKEDITOR.tools.override( originalCommit, function( org ) - { - return function( data, selectedTable ) - { - org.apply( this, arguments ); - var value = parseInt( this.getValue(), 10 ); - selectedTable[ ( !value || value <= 0 ) ? 'addClass' : 'removeClass' ]( showBorderClassName ); - }; - } ); - } - }); - -} )(); - -/** - * Whether to automatically enable the "show borders" command when the editor loads. - * @type Boolean - * @default true - * @example - * config.startupShowBorders = false; - */ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview The "show border" plugin. The command display visible outline
+ * border line around all table elements if table doesn't have a none-zero 'border' attribute specified.
+ */
+
+(function()
+{
+ var showBorderClassName = 'cke_show_border',
+ cssStyleText,
+ cssTemplate =
+ // TODO: For IE6, we don't have child selector support,
+ // where nested table cells could be incorrect.
+ ( CKEDITOR.env.ie6Compat ?
+ [
+ '.%1 table.%2,',
+ '.%1 table.%2 td, .%1 table.%2 th',
+ '{',
+ 'border : #d3d3d3 1px dotted',
+ '}'
+ ] :
+ [
+ '.%1 table.%2,',
+ '.%1 table.%2 > tr > td, .%1 table.%2 > tr > th,',
+ '.%1 table.%2 > tbody > tr > td, .%1 table.%2 > tbody > tr > th,',
+ '.%1 table.%2 > thead > tr > td, .%1 table.%2 > thead > tr > th,',
+ '.%1 table.%2 > tfoot > tr > td, .%1 table.%2 > tfoot > tr > th',
+ '{',
+ 'border : #d3d3d3 1px dotted',
+ '}'
+ ] ).join( '' );
+
+ cssStyleText = cssTemplate.replace( /%2/g, showBorderClassName ).replace( /%1/g, 'cke_show_borders ' );
+
+ var commandDefinition =
+ {
+ preserveState : true,
+ editorFocus : false,
+ readOnly: 1,
+
+ exec : function ( editor )
+ {
+ this.toggleState();
+ this.refresh( editor );
+ },
+
+ refresh : function( editor )
+ {
+ if ( editor.document )
+ {
+ var funcName = ( this.state == CKEDITOR.TRISTATE_ON ) ? 'addClass' : 'removeClass';
+ editor.document.getBody()[ funcName ]( 'cke_show_borders' );
+ }
+ }
+ };
+
+ CKEDITOR.plugins.add( 'showborders',
+ {
+ requires : [ 'wysiwygarea' ],
+ modes : { 'wysiwyg' : 1 },
+
+ init : function( editor )
+ {
+
+ var command = editor.addCommand( 'showborders', commandDefinition );
+ command.canUndo = false;
+
+ if ( editor.config.startupShowBorders !== false )
+ command.setState( CKEDITOR.TRISTATE_ON );
+
+ editor.addCss( cssStyleText );
+
+ // Refresh the command on setData.
+ editor.on( 'mode', function()
+ {
+ if ( command.state != CKEDITOR.TRISTATE_DISABLED )
+ command.refresh( editor );
+ }, null, null, 100 );
+
+ // Refresh the command on wysiwyg frame reloads.
+ editor.on( 'contentDom', function()
+ {
+ if ( command.state != CKEDITOR.TRISTATE_DISABLED )
+ command.refresh( editor );
+ });
+
+ editor.on( 'removeFormatCleanup', function( evt )
+ {
+ var element = evt.data;
+ if ( editor.getCommand( 'showborders' ).state == CKEDITOR.TRISTATE_ON &&
+ element.is( 'table' ) && ( !element.hasAttribute( 'border' ) || parseInt( element.getAttribute( 'border' ), 10 ) <= 0 ) )
+ element.addClass( showBorderClassName );
+ });
+ },
+
+ afterInit : function( editor )
+ {
+ var dataProcessor = editor.dataProcessor,
+ dataFilter = dataProcessor && dataProcessor.dataFilter,
+ htmlFilter = dataProcessor && dataProcessor.htmlFilter;
+
+ if ( dataFilter )
+ {
+ dataFilter.addRules(
+ {
+ elements :
+ {
+ 'table' : function( element )
+ {
+ var attributes = element.attributes,
+ cssClass = attributes[ 'class' ],
+ border = parseInt( attributes.border, 10 );
+
+ if ( ( !border || border <= 0 ) && ( !cssClass || cssClass.indexOf( showBorderClassName ) == -1 ) )
+ attributes[ 'class' ] = ( cssClass || '' ) + ' ' + showBorderClassName;
+ }
+ }
+ } );
+ }
+
+ if ( htmlFilter )
+ {
+ htmlFilter.addRules(
+ {
+ elements :
+ {
+ 'table' : function( table )
+ {
+ var attributes = table.attributes,
+ cssClass = attributes[ 'class' ];
+
+ cssClass && ( attributes[ 'class' ] =
+ cssClass.replace( showBorderClassName, '' )
+ .replace( /\s{2}/, ' ' )
+ .replace( /^\s+|\s+$/, '' ) );
+ }
+ }
+ } );
+ }
+ }
+ });
+
+ // Table dialog must be aware of it.
+ CKEDITOR.on( 'dialogDefinition', function( ev )
+ {
+ var dialogName = ev.data.name;
+
+ if ( dialogName == 'table' || dialogName == 'tableProperties' )
+ {
+ var dialogDefinition = ev.data.definition,
+ infoTab = dialogDefinition.getContents( 'info' ),
+ borderField = infoTab.get( 'txtBorder' ),
+ originalCommit = borderField.commit;
+
+ borderField.commit = CKEDITOR.tools.override( originalCommit, function( org )
+ {
+ return function( data, selectedTable )
+ {
+ org.apply( this, arguments );
+ var value = parseInt( this.getValue(), 10 );
+ selectedTable[ ( !value || value <= 0 ) ? 'addClass' : 'removeClass' ]( showBorderClassName );
+ };
+ } );
+
+ var advTab = dialogDefinition.getContents( 'advanced' ),
+ classField = advTab && advTab.get( 'advCSSClasses' );
+
+ if ( classField )
+ {
+ classField.setup = CKEDITOR.tools.override( classField.setup, function( originalSetup )
+ {
+ return function()
+ {
+ originalSetup.apply( this, arguments );
+ this.setValue( this.getValue().replace( /cke_show_border/, '' ) );
+ };
+ });
+
+ classField.commit = CKEDITOR.tools.override( classField.commit, function( originalCommit )
+ {
+ return function( data, element )
+ {
+ originalCommit.apply( this, arguments );
+
+ if ( !parseInt( element.getAttribute( 'border' ), 10 ) )
+ element.addClass( 'cke_show_border' );
+ };
+ });
+ }
+ }
+ });
+
+} )();
+
+/**
+ * Whether to automatically enable the "show borders" command when the editor loads.
+ * (ShowBorders in FCKeditor)
+ * @name CKEDITOR.config.startupShowBorders
+ * @type Boolean
+ * @default true
+ * @example
+ * config.startupShowBorders = false;
+ */
diff --git a/_source/plugins/smiley/dialogs/smiley.js b/_source/plugins/smiley/dialogs/smiley.js index 4786655..eec2732 100644 --- a/_source/plugins/smiley/dialogs/smiley.js +++ b/_source/plugins/smiley/dialogs/smiley.js @@ -1,218 +1,224 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -CKEDITOR.dialog.add( 'smiley', function( editor ) -{ - var config = editor.config, - lang = editor.lang.smiley, - images = config.smiley_images, - columns = 8, - i; - - /** - * Simulate "this" of a dialog for non-dialog events. - * @type {CKEDITOR.dialog} - */ - var dialog; - var onClick = function( evt ) - { - var target = evt.data.getTarget(), - targetName = target.getName(); - - if ( targetName == 'td' ) - target = target.getChild( [ 0, 0 ] ); - else if ( targetName == 'a' ) - target = target.getChild( 0 ); - else if ( targetName != 'img' ) - return; - - var src = target.getAttribute( 'cke_src' ), - title = target.getAttribute( 'title' ); - - var img = editor.document.createElement( 'img', - { - attributes : - { - src : src, - _cke_saved_src : src, - title : title, - alt : title - } - }); - - editor.insertElement( img ); - - dialog.hide(); - evt.data.preventDefault(); - }; - - var onKeydown = CKEDITOR.tools.addFunction( function( ev, element ) - { - ev = new CKEDITOR.dom.event( ev ); - element = new CKEDITOR.dom.element( element ); - var relative, nodeToMove; - - var keystroke = ev.getKeystroke(); - var 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(); - } - 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.focus(); - } - ev.preventDefault(); - break; - // ENTER - // SPACE - case 32 : - onClick( { data: ev } ); - ev.preventDefault(); - break; - - // RIGHT-ARROW - case rtl ? 37 : 39 : - // TAB - case 9 : - // relative is TD - if ( ( relative = element.getParent().getNext() ) ) - { - nodeToMove = relative.getChild( 0 ); - nodeToMove.focus(); - ev.preventDefault(true); - } - // relative is TR - else if ( ( relative = element.getParent().getParent().getNext() ) ) - { - nodeToMove = relative.getChild( [0, 0] ); - if ( nodeToMove ) - nodeToMove.focus(); - ev.preventDefault(true); - } - break; - - // LEFT-ARROW - case rtl ? 39 : 37 : - // SHIFT + TAB - case CKEDITOR.SHIFT + 9 : - // relative is TD - if ( ( relative = element.getParent().getPrevious() ) ) - { - nodeToMove = relative.getChild( 0 ); - nodeToMove.focus(); - ev.preventDefault(true); - } - // relative is TR - else if ( ( relative = element.getParent().getParent().getPrevious() ) ) - { - nodeToMove = relative.getLast().getChild( 0 ); - nodeToMove.focus(); - ev.preventDefault(true); - } - break; - default : - // Do not stop not handled events. - return; - } - }); - - // Build the HTML for the smiley images table. - var labelId = 'smiley_emtions_label' + CKEDITOR.tools.getNextNumber(); - var html = - [ - '<div>' + - '<span id="' + labelId + '" class="cke_voice_label">' + lang.options +'</span>', - '<table role="listbox" aria-labelledby="' + labelId + '" style="width:100%;height:100%" cellspacing="2" cellpadding="2"', - CKEDITOR.env.ie && CKEDITOR.env.quirks ? ' style="position:absolute;"' : '', - '><tbody>' - ]; - - var size = images.length; - for ( i = 0 ; i < size ; i++ ) - { - if ( i % columns === 0 ) - html.push( '<tr>' ); - - var smileyLabelId = 'cke_smile_label_' + i + '_' + CKEDITOR.tools.getNextNumber(); - html.push( - '<td class="cke_dark_background cke_hand cke_centered" style="vertical-align: middle;">' + - '<a href="javascript:void(0)" role="option"', - ' aria-posinset="' + ( i +1 ) + '"', - ' aria-setsize="' + size + '"', - ' aria-labelledby="' + smileyLabelId + '"', - ' class="cke_smile" tabindex="-1" onkeydown="CKEDITOR.tools.callFunction( ', onKeydown, ', event, this );">', - '<img class="hand" title="', config.smiley_descriptions[i], '"' + - ' cke_src="', CKEDITOR.tools.htmlEncode( config.smiley_path + images[ i ] ), '" alt="', config.smiley_descriptions[i], '"', - ' src="', CKEDITOR.tools.htmlEncode( config.smiley_path + images[ i ] ), '"', - // IE BUG: Below is a workaround to an IE image loading bug to ensure the image sizes are correct. - ( CKEDITOR.env.ie ? ' onload="this.setAttribute(\'width\', 2); this.removeAttribute(\'width\');" ' : '' ), - '>' + - '<span id="' + smileyLabelId + '" class="cke_voice_label">' +config.smiley_descriptions[ i ] + '</span>' + - '</a>', - '</td>' ); - - if ( i % columns == columns - 1 ) - html.push( '</tr>' ); - } - - if ( i < columns - 1 ) - { - for ( ; i < columns - 1 ; i++ ) - html.push( '<td></td>' ); - html.push( '</tr>' ); - } - - html.push( '</tbody></table></div>' ); - - var smileySelector = - { - type : 'html', - html : html.join( '' ), - onLoad : function( event ) - { - dialog = event.sender; - }, - focus : function() - { - var firstSmile = this.getElement().getElementsByTag( 'a' ).getItem( 0 ); - firstSmile.focus(); - }, - onClick : onClick, - style : 'width: 100%; border-collapse: separate;' - }; - - return { - title : editor.lang.smiley.title, - minWidth : 270, - minHeight : 120, - contents : [ - { - id : 'tab1', - label : '', - title : '', - expand : true, - padding : 0, - elements : [ - smileySelector - ] - } - ], - buttons : [ CKEDITOR.dialog.cancelButton ] - }; -} ); +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.dialog.add( 'smiley', function( editor )
+{
+ var config = editor.config,
+ lang = editor.lang.smiley,
+ images = config.smiley_images,
+ columns = config.smiley_columns || 8,
+ i;
+
+ /**
+ * Simulate "this" of a dialog for non-dialog events.
+ * @type {CKEDITOR.dialog}
+ */
+ var dialog;
+ var onClick = function( evt )
+ {
+ var target = evt.data.getTarget(),
+ targetName = target.getName();
+
+ if ( targetName == 'a' )
+ target = target.getChild( 0 );
+ else if ( targetName != 'img' )
+ return;
+
+ var src = target.getAttribute( 'cke_src' ),
+ title = target.getAttribute( 'title' );
+
+ var img = editor.document.createElement( 'img',
+ {
+ attributes :
+ {
+ src : src,
+ 'data-cke-saved-src' : src,
+ title : title,
+ alt : title,
+ width : target.$.width,
+ height : target.$.height
+ }
+ });
+
+ editor.insertElement( img );
+
+ dialog.hide();
+ evt.data.preventDefault();
+ };
+
+ var onKeydown = CKEDITOR.tools.addFunction( function( ev, element )
+ {
+ ev = new CKEDITOR.dom.event( ev );
+ element = new CKEDITOR.dom.element( element );
+ 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();
+ }
+ 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.focus();
+ }
+ ev.preventDefault();
+ break;
+ // ENTER
+ // SPACE
+ case 32 :
+ onClick( { data: ev } );
+ ev.preventDefault();
+ break;
+
+ // RIGHT-ARROW
+ case rtl ? 37 : 39 :
+ // TAB
+ case 9 :
+ // relative is TD
+ if ( ( relative = element.getParent().getNext() ) )
+ {
+ nodeToMove = relative.getChild( 0 );
+ nodeToMove.focus();
+ ev.preventDefault(true);
+ }
+ // relative is TR
+ else if ( ( relative = element.getParent().getParent().getNext() ) )
+ {
+ nodeToMove = relative.getChild( [0, 0] );
+ if ( nodeToMove )
+ nodeToMove.focus();
+ ev.preventDefault(true);
+ }
+ break;
+
+ // LEFT-ARROW
+ case rtl ? 39 : 37 :
+ // SHIFT + TAB
+ case CKEDITOR.SHIFT + 9 :
+ // relative is TD
+ if ( ( relative = element.getParent().getPrevious() ) )
+ {
+ nodeToMove = relative.getChild( 0 );
+ nodeToMove.focus();
+ ev.preventDefault(true);
+ }
+ // relative is TR
+ else if ( ( relative = element.getParent().getParent().getPrevious() ) )
+ {
+ nodeToMove = relative.getLast().getChild( 0 );
+ nodeToMove.focus();
+ ev.preventDefault(true);
+ }
+ break;
+ default :
+ // Do not stop not handled events.
+ return;
+ }
+ });
+
+ // Build the HTML for the smiley images table.
+ var labelId = CKEDITOR.tools.getNextId() + '_smiley_emtions_label';
+ var html =
+ [
+ '<div>' +
+ '<span id="' + labelId + '" class="cke_voice_label">' + lang.options +'</span>',
+ '<table role="listbox" aria-labelledby="' + labelId + '" style="width:100%;height:100%" cellspacing="2" cellpadding="2"',
+ CKEDITOR.env.ie && CKEDITOR.env.quirks ? ' style="position:absolute;"' : '',
+ '><tbody>'
+ ];
+
+ var size = images.length;
+ for ( i = 0 ; i < size ; i++ )
+ {
+ if ( i % columns === 0 )
+ html.push( '<tr>' );
+
+ var smileyLabelId = 'cke_smile_label_' + i + '_' + CKEDITOR.tools.getNextNumber();
+ html.push(
+ '<td class="cke_dark_background cke_centered" style="vertical-align: middle;">' +
+ '<a href="javascript:void(0)" role="option"',
+ ' aria-posinset="' + ( i +1 ) + '"',
+ ' aria-setsize="' + size + '"',
+ ' aria-labelledby="' + smileyLabelId + '"',
+ ' class="cke_smile cke_hand" tabindex="-1" onkeydown="CKEDITOR.tools.callFunction( ', onKeydown, ', event, this );">',
+ '<img class="cke_hand" title="', config.smiley_descriptions[i], '"' +
+ ' cke_src="', CKEDITOR.tools.htmlEncode( config.smiley_path + images[ i ] ), '" alt="', config.smiley_descriptions[i], '"',
+ ' src="', CKEDITOR.tools.htmlEncode( config.smiley_path + images[ i ] ), '"',
+ // IE BUG: Below is a workaround to an IE image loading bug to ensure the image sizes are correct.
+ ( CKEDITOR.env.ie ? ' onload="this.setAttribute(\'width\', 2); this.removeAttribute(\'width\');" ' : '' ),
+ '>' +
+ '<span id="' + smileyLabelId + '" class="cke_voice_label">' +config.smiley_descriptions[ i ] + '</span>' +
+ '</a>',
+ '</td>' );
+
+ if ( i % columns == columns - 1 )
+ html.push( '</tr>' );
+ }
+
+ if ( i < columns - 1 )
+ {
+ for ( ; i < columns - 1 ; i++ )
+ html.push( '<td></td>' );
+ html.push( '</tr>' );
+ }
+
+ html.push( '</tbody></table></div>' );
+
+ var smileySelector =
+ {
+ type : 'html',
+ id : 'smileySelector',
+ html : html.join( '' ),
+ onLoad : function( event )
+ {
+ dialog = event.sender;
+ },
+ focus : function()
+ {
+ var self = this;
+ // IE need a while to move the focus (#6539).
+ setTimeout( function ()
+ {
+ var firstSmile = self.getElement().getElementsByTag( 'a' ).getItem( 0 );
+ firstSmile.focus();
+ }, 0 );
+ },
+ onClick : onClick,
+ style : 'width: 100%; border-collapse: separate;'
+ };
+
+ return {
+ title : editor.lang.smiley.title,
+ minWidth : 270,
+ minHeight : 120,
+ contents : [
+ {
+ id : 'tab1',
+ label : '',
+ title : '',
+ expand : true,
+ padding : 0,
+ elements : [
+ smileySelector
+ ]
+ }
+ ],
+ buttons : [ CKEDITOR.dialog.cancelButton ]
+ };
+} );
diff --git a/_source/plugins/smiley/plugin.js b/_source/plugins/smiley/plugin.js index da1ad54..9156be8 100644 --- a/_source/plugins/smiley/plugin.js +++ b/_source/plugins/smiley/plugin.js @@ -1,84 +1,94 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -CKEDITOR.plugins.add( 'smiley', -{ - requires : [ 'dialog' ], - - init : function( editor ) - { - editor.config.smiley_path = editor.config.smiley_path || ( this.path + 'images/' ); - editor.addCommand( 'smiley', new CKEDITOR.dialogCommand( 'smiley' ) ); - editor.ui.addButton( 'Smiley', - { - label : editor.lang.smiley.toolbar, - command : 'smiley' - }); - CKEDITOR.dialog.add( 'smiley', this.path + 'dialogs/smiley.js' ); - } -} ); - -/** - * The base path used to build the URL for the smiley images. It must end with - * a slash. - * @name CKEDITOR.config.smiley_path - * @type String - * @default {@link CKEDITOR.basePath} + 'plugins/smiley/images/' - * @example - * config.smiley_path = 'http://www.example.com/images/smileys/'; - * @example - * config.smiley_path = '/images/smileys/'; - */ - -/** - * The file names for the smileys to be displayed. These files must be - * contained inside the URL path defined with the - * {@link CKEDITOR.config.smiley_path} setting. - * @type Array - * @default (see example) - * @example - * // This is actually the default value. - * config.smiley_images = [ - * 'regular_smile.gif','sad_smile.gif','wink_smile.gif','teeth_smile.gif','confused_smile.gif','tounge_smile.gif', - * 'embaressed_smile.gif','omg_smile.gif','whatchutalkingabout_smile.gif','angry_smile.gif','angel_smile.gif','shades_smile.gif', - * 'devil_smile.gif','cry_smile.gif','lightbulb.gif','thumbs_down.gif','thumbs_up.gif','heart.gif', - * 'broken_heart.gif','kiss.gif','envelope.gif']; - */ -CKEDITOR.config.smiley_images = [ - 'regular_smile.gif','sad_smile.gif','wink_smile.gif','teeth_smile.gif','confused_smile.gif','tounge_smile.gif', - 'embaressed_smile.gif','omg_smile.gif','whatchutalkingabout_smile.gif','angry_smile.gif','angel_smile.gif','shades_smile.gif', - 'devil_smile.gif','cry_smile.gif','lightbulb.gif','thumbs_down.gif','thumbs_up.gif','heart.gif', - 'broken_heart.gif','kiss.gif','envelope.gif']; - -/** - * The description to be used for each of the smileys defined in the - * {@link CKEDITOR.config.smiley_images} setting. Each entry in this array list - * must match its relative pair in the {@link CKEDITOR.config.smiley_images} - * setting. - * @type Array - * @default The textual descriptions of smiley. - * @example - * // Default settings. - * config.smiley_descriptions = - * [ - * 'smiley', 'sad', 'wink', 'laugh', 'frown', 'cheeky', 'blush', 'surprise', - * 'indecision', 'angry', 'angel', 'cool', 'devil', 'crying', 'enlightened', 'no', - * 'yes', 'heart', 'broken heart', 'kiss', 'mail' - * ]; - * @example - * // Use textual emoticons as description. - * config.smiley_descriptions = - * [ - * ':)', ':(', ';)', ':D', ':/', ':P', ':*)', ':-o', - * ':|', '>:(', 'o:)', '8-)', '>:-)', ';(', '', '', '', - * '', '', ':-*', '' - * ]; - */ -CKEDITOR.config.smiley_descriptions = - [ - 'smiley', 'sad', 'wink', 'laugh', 'frown', 'cheeky', 'blush', 'surprise', - 'indecision', 'angry', 'angel', 'cool', 'devil', 'crying', 'enlightened', 'no', - 'yes', 'heart', 'broken heart', 'kiss', 'mail' - ]; +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.add( 'smiley',
+{
+ requires : [ 'dialog' ],
+
+ init : function( editor )
+ {
+ editor.config.smiley_path = editor.config.smiley_path || ( this.path + 'images/' );
+ editor.addCommand( 'smiley', new CKEDITOR.dialogCommand( 'smiley' ) );
+ editor.ui.addButton( 'Smiley',
+ {
+ label : editor.lang.smiley.toolbar,
+ command : 'smiley'
+ });
+ CKEDITOR.dialog.add( 'smiley', this.path + 'dialogs/smiley.js' );
+ }
+} );
+
+/**
+ * The base path used to build the URL for the smiley images. It must end with
+ * a slash.
+ * @name CKEDITOR.config.smiley_path
+ * @type String
+ * @default <code><em>CKEDITOR.basePath</em> + 'plugins/smiley/images/'</code>
+ * @example
+ * config.smiley_path = 'http://www.example.com/images/smileys/';
+ * @example
+ * config.smiley_path = '/images/smileys/';
+ */
+
+/**
+ * The file names for the smileys to be displayed. These files must be
+ * contained inside the URL path defined with the
+ * {@link CKEDITOR.config.smiley_path} setting.
+ * @type Array
+ * @default (see example)
+ * @example
+ * // This is actually the default value.
+ * config.smiley_images = [
+ * 'regular_smile.gif','sad_smile.gif','wink_smile.gif','teeth_smile.gif','confused_smile.gif','tounge_smile.gif',
+ * 'embaressed_smile.gif','omg_smile.gif','whatchutalkingabout_smile.gif','angry_smile.gif','angel_smile.gif','shades_smile.gif',
+ * 'devil_smile.gif','cry_smile.gif','lightbulb.gif','thumbs_down.gif','thumbs_up.gif','heart.gif',
+ * 'broken_heart.gif','kiss.gif','envelope.gif'];
+ */
+CKEDITOR.config.smiley_images = [
+ 'regular_smile.gif','sad_smile.gif','wink_smile.gif','teeth_smile.gif','confused_smile.gif','tounge_smile.gif',
+ 'embaressed_smile.gif','omg_smile.gif','whatchutalkingabout_smile.gif','angry_smile.gif','angel_smile.gif','shades_smile.gif',
+ 'devil_smile.gif','cry_smile.gif','lightbulb.gif','thumbs_down.gif','thumbs_up.gif','heart.gif',
+ 'broken_heart.gif','kiss.gif','envelope.gif'];
+
+/**
+ * The description to be used for each of the smileys defined in the
+ * {@link CKEDITOR.config.smiley_images} setting. Each entry in this array list
+ * must match its relative pair in the {@link CKEDITOR.config.smiley_images}
+ * setting.
+ * @type Array
+ * @default The textual descriptions of smiley.
+ * @example
+ * // Default settings.
+ * config.smiley_descriptions =
+ * [
+ * 'smiley', 'sad', 'wink', 'laugh', 'frown', 'cheeky', 'blush', 'surprise',
+ * 'indecision', 'angry', 'angel', 'cool', 'devil', 'crying', 'enlightened', 'no',
+ * 'yes', 'heart', 'broken heart', 'kiss', 'mail'
+ * ];
+ * @example
+ * // Use textual emoticons as description.
+ * config.smiley_descriptions =
+ * [
+ * ':)', ':(', ';)', ':D', ':/', ':P', ':*)', ':-o',
+ * ':|', '>:(', 'o:)', '8-)', '>:-)', ';(', '', '', '',
+ * '', '', ':-*', ''
+ * ];
+ */
+CKEDITOR.config.smiley_descriptions =
+ [
+ 'smiley', 'sad', 'wink', 'laugh', 'frown', 'cheeky', 'blush', 'surprise',
+ 'indecision', 'angry', 'angel', 'cool', 'devil', 'crying', 'enlightened', 'no',
+ 'yes', 'heart', 'broken heart', 'kiss', 'mail'
+ ];
+
+/**
+ * The number of columns to be generated by the smilies matrix.
+ * @name CKEDITOR.config.smiley_columns
+ * @type Number
+ * @default 8
+ * @since 3.3.2
+ * @example
+ * config.smiley_columns = 6;
+ */
diff --git a/_source/plugins/sourcearea/plugin.js b/_source/plugins/sourcearea/plugin.js index 05e1592..91430fe 100644 --- a/_source/plugins/sourcearea/plugin.js +++ b/_source/plugins/sourcearea/plugin.js @@ -1,207 +1,209 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -/** - * @fileOverview The "sourcearea" plugin. It registers the "source" editing - * mode, which displays the raw data being edited in the editor. - */ - -CKEDITOR.plugins.add( 'sourcearea', -{ - requires : [ 'editingblock' ], - - init : function( editor ) - { - var sourcearea = CKEDITOR.plugins.sourcearea, - win = CKEDITOR.document.getWindow(); - - editor.on( 'editingBlockReady', function() - { - var textarea, - onResize; - - editor.addMode( 'source', - { - load : function( holderElement, data ) - { - if ( CKEDITOR.env.ie && CKEDITOR.env.version < 8 ) - holderElement.setStyle( 'position', 'relative' ); - - // Create the source area <textarea>. - editor.textarea = textarea = new CKEDITOR.dom.element( 'textarea' ); - textarea.setAttributes( - { - dir : 'ltr', - tabIndex : CKEDITOR.env.webkit ? -1 : editor.tabIndex, - 'role' : 'textbox', - 'aria-label' : editor.lang.editorTitle.replace( '%1', editor.name ) - }); - textarea.addClass( 'cke_source' ); - textarea.addClass( 'cke_enable_context_menu' ); - - var styles = - { - // IE7 has overflow the <textarea> from wrapping table cell. - width : CKEDITOR.env.ie7Compat ? '99%' : '100%', - height : '100%', - resize : 'none', - outline : 'none', - 'text-align' : 'left' - }; - - // Having to make <textarea> fixed sized to conque the following bugs: - // 1. The textarea height/width='100%' doesn't constraint to the 'td' in IE6/7. - // 2. Unexpected vertical-scrolling behavior happens whenever focus is moving out of editor - // if text content within it has overflowed. (#4762) - if ( CKEDITOR.env.ie ) - { - onResize = function() - { - // Holder rectange size is stretched by textarea, - // so hide it just for a moment. - textarea.hide(); - textarea.setStyle( 'height', holderElement.$.clientHeight + 'px' ); - textarea.setStyle( 'width', holderElement.$.clientWidth + 'px' ); - // When we have proper holder size, show textarea again. - textarea.show(); - }; - - editor.on( 'resize', onResize ); - win.on( 'resize', onResize ); - setTimeout( onResize, 0 ); - } - else - { - // By some yet unknown reason, we must stop the - // mousedown propagation for the textarea, - // otherwise it's not possible to place the caret - // inside of it (non IE). - textarea.on( 'mousedown', function( evt ) - { - evt.data.stopPropagation(); - } ); - } - - // Reset the holder element and append the - // <textarea> to it. - holderElement.setHtml( '' ); - holderElement.append( textarea ); - textarea.setStyles( styles ); - - editor.fire( 'ariaWidget', textarea ); - - textarea.on( 'blur', function() - { - editor.focusManager.blur(); - }); - - textarea.on( 'focus', function() - { - editor.focusManager.focus(); - }); - - // The editor data "may be dirty" after this point. - editor.mayBeDirty = true; - - // Set the <textarea> value. - this.loadData( data ); - - var keystrokeHandler = editor.keystrokeHandler; - if ( keystrokeHandler ) - keystrokeHandler.attach( textarea ); - - setTimeout( function() - { - editor.mode = 'source'; - editor.fire( 'mode' ); - }, - ( CKEDITOR.env.gecko || CKEDITOR.env.webkit ) ? 100 : 0 ); - }, - - loadData : function( data ) - { - textarea.setValue( data ); - editor.fire( 'dataReady' ); - }, - - getData : function() - { - return textarea.getValue(); - }, - - getSnapshotData : function() - { - return textarea.getValue(); - }, - - unload : function( holderElement ) - { - textarea.clearCustomData(); - editor.textarea = textarea = null; - - if ( onResize ) - { - editor.removeListener( 'resize', onResize ); - win.removeListener( 'resize', onResize ); - } - - if ( CKEDITOR.env.ie && CKEDITOR.env.version < 8 ) - holderElement.removeStyle( 'position' ); - }, - - focus : function() - { - textarea.focus(); - } - }); - }); - - editor.addCommand( 'source', sourcearea.commands.source ); - - if ( editor.ui.addButton ) - { - editor.ui.addButton( 'Source', - { - label : editor.lang.source, - command : 'source' - }); - } - - editor.on( 'mode', function() - { - editor.getCommand( 'source' ).setState( - editor.mode == 'source' ? - CKEDITOR.TRISTATE_ON : - CKEDITOR.TRISTATE_OFF ); - }); - } -}); - -/** - * Holds the definition of commands an UI elements included with the sourcearea - * plugin. - * @example - */ -CKEDITOR.plugins.sourcearea = -{ - commands : - { - source : - { - modes : { wysiwyg:1, source:1 }, - editorFocus : false, - - exec : function( editor ) - { - if ( editor.mode == 'wysiwyg' ) - editor.fire( 'saveSnapshot' ); - editor.getCommand( 'source' ).setState( CKEDITOR.TRISTATE_DISABLED ); - editor.setMode( editor.mode == 'source' ? 'wysiwyg' : 'source' ); - }, - - canUndo : false - } - } -}; +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview The "sourcearea" plugin. It registers the "source" editing
+ * mode, which displays the raw data being edited in the editor.
+ */
+
+CKEDITOR.plugins.add( 'sourcearea',
+{
+ requires : [ 'editingblock' ],
+
+ init : function( editor )
+ {
+ var sourcearea = CKEDITOR.plugins.sourcearea,
+ win = CKEDITOR.document.getWindow();
+
+ editor.on( 'editingBlockReady', function()
+ {
+ var textarea,
+ onResize;
+
+ editor.addMode( 'source',
+ {
+ load : function( holderElement, data )
+ {
+ if ( CKEDITOR.env.ie && CKEDITOR.env.version < 8 )
+ holderElement.setStyle( 'position', 'relative' );
+
+ // Create the source area <textarea>.
+ editor.textarea = textarea = new CKEDITOR.dom.element( 'textarea' );
+ textarea.setAttributes(
+ {
+ dir : 'ltr',
+ tabIndex : CKEDITOR.env.webkit ? -1 : editor.tabIndex,
+ 'role' : 'textbox',
+ 'aria-label' : editor.lang.editorTitle.replace( '%1', editor.name )
+ });
+ textarea.addClass( 'cke_source' );
+ textarea.addClass( 'cke_enable_context_menu' );
+
+ editor.readOnly && textarea.setAttribute( 'readOnly', 'readonly' );
+
+ var styles =
+ {
+ // IE7 has overflow the <textarea> from wrapping table cell.
+ width : CKEDITOR.env.ie7Compat ? '99%' : '100%',
+ height : '100%',
+ resize : 'none',
+ outline : 'none',
+ 'text-align' : 'left'
+ };
+
+ // Having to make <textarea> fixed sized to conque the following bugs:
+ // 1. The textarea height/width='100%' doesn't constraint to the 'td' in IE6/7.
+ // 2. Unexpected vertical-scrolling behavior happens whenever focus is moving out of editor
+ // if text content within it has overflowed. (#4762)
+ if ( CKEDITOR.env.ie )
+ {
+ onResize = function()
+ {
+ // Holder rectange size is stretched by textarea,
+ // so hide it just for a moment.
+ textarea.hide();
+ textarea.setStyle( 'height', holderElement.$.clientHeight + 'px' );
+ textarea.setStyle( 'width', holderElement.$.clientWidth + 'px' );
+ // When we have proper holder size, show textarea again.
+ textarea.show();
+ };
+
+ editor.on( 'resize', onResize );
+ win.on( 'resize', onResize );
+ setTimeout( onResize, 0 );
+ }
+
+ // Reset the holder element and append the
+ // <textarea> to it.
+ holderElement.setHtml( '' );
+ holderElement.append( textarea );
+ textarea.setStyles( styles );
+
+ editor.fire( 'ariaWidget', textarea );
+
+ textarea.on( 'blur', function()
+ {
+ editor.focusManager.blur();
+ });
+
+ textarea.on( 'focus', function()
+ {
+ editor.focusManager.focus();
+ });
+
+ // The editor data "may be dirty" after this point.
+ editor.mayBeDirty = true;
+
+ // Set the <textarea> value.
+ this.loadData( data );
+
+ var keystrokeHandler = editor.keystrokeHandler;
+ if ( keystrokeHandler )
+ keystrokeHandler.attach( textarea );
+
+ setTimeout( function()
+ {
+ editor.mode = 'source';
+ editor.fire( 'mode', { previousMode : editor._.previousMode } );
+ },
+ ( CKEDITOR.env.gecko || CKEDITOR.env.webkit ) ? 100 : 0 );
+ },
+
+ loadData : function( data )
+ {
+ textarea.setValue( data );
+ editor.fire( 'dataReady' );
+ },
+
+ getData : function()
+ {
+ return textarea.getValue();
+ },
+
+ getSnapshotData : function()
+ {
+ return textarea.getValue();
+ },
+
+ unload : function( holderElement )
+ {
+ textarea.clearCustomData();
+ editor.textarea = textarea = null;
+
+ if ( onResize )
+ {
+ editor.removeListener( 'resize', onResize );
+ win.removeListener( 'resize', onResize );
+ }
+
+ if ( CKEDITOR.env.ie && CKEDITOR.env.version < 8 )
+ holderElement.removeStyle( 'position' );
+ },
+
+ focus : function()
+ {
+ textarea.focus();
+ }
+ });
+ });
+
+ editor.on( 'readOnly', function()
+ {
+ if ( editor.mode == 'source' )
+ {
+ if ( editor.readOnly )
+ editor.textarea.setAttribute( 'readOnly', 'readonly' );
+ else
+ editor.textarea.removeAttribute( 'readOnly' );
+ }
+ });
+
+ editor.addCommand( 'source', sourcearea.commands.source );
+
+ if ( editor.ui.addButton )
+ {
+ editor.ui.addButton( 'Source',
+ {
+ label : editor.lang.source,
+ command : 'source'
+ });
+ }
+
+ editor.on( 'mode', function()
+ {
+ editor.getCommand( 'source' ).setState(
+ editor.mode == 'source' ?
+ CKEDITOR.TRISTATE_ON :
+ CKEDITOR.TRISTATE_OFF );
+ });
+ }
+});
+
+/**
+ * Holds the definition of commands an UI elements included with the sourcearea
+ * plugin.
+ * @example
+ */
+CKEDITOR.plugins.sourcearea =
+{
+ commands :
+ {
+ source :
+ {
+ modes : { wysiwyg:1, source:1 },
+ editorFocus : false,
+ readOnly : 1,
+ exec : function( editor )
+ {
+ if ( editor.mode == 'wysiwyg' )
+ editor.fire( 'saveSnapshot' );
+ editor.getCommand( 'source' ).setState( CKEDITOR.TRISTATE_DISABLED );
+ editor.setMode( editor.mode == 'source' ? 'wysiwyg' : 'source' );
+ },
+
+ canUndo : false
+ }
+ }
+};
diff --git a/_source/plugins/specialchar/dialogs/specialchar.js b/_source/plugins/specialchar/dialogs/specialchar.js index f933ff4..ff2fded 100644 --- a/_source/plugins/specialchar/dialogs/specialchar.js +++ b/_source/plugins/specialchar/dialogs/specialchar.js @@ -1,406 +1,350 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -CKEDITOR.dialog.add( 'specialchar', function( editor ) -{ - /** - * Simulate "this" of a dialog for non-dialog events. - * @type {CKEDITOR.dialog} - */ - var dialog, - lang = editor.lang.specialChar; - - var insertSpecialChar = function ( specialChar ) - { - var selection = editor.getSelection(), - ranges = selection.getRanges(), - range, textNode; - - editor.fire( 'saveSnapshot' ); - - for ( var i = 0, len = ranges.length ; i < len ; i++ ) - { - range = ranges[ i ]; - range.deleteContents(); - - textNode = CKEDITOR.dom.element.createFromHtml( specialChar ); - range.insertNode( textNode ); - } - - range.moveToPosition( textNode, CKEDITOR.POSITION_AFTER_END ); - range.select(); - - editor.fire( 'saveSnapshot' ); - }; - - var onChoice = function( evt ) - { - var target, value; - if ( evt.data ) - target = evt.data.getTarget(); - else - target = new CKEDITOR.dom.element( evt ); - - if ( target.getName() == 'a' && ( value = target.getChild( 0 ).getHtml() ) ) - { - target.removeClass( "cke_light_background" ); - dialog.hide(); - - // Firefox has bug on insert chars into a element use its own API. (#5170) - if ( CKEDITOR.env.gecko ) - insertSpecialChar( value ); - else - editor.insertHtml( value ); - } - }; - - var onClick = CKEDITOR.tools.addFunction( onChoice ); - - var focusedNode; - - var onFocus = function( evt, target ) - { - var value; - target = target || evt.data.getTarget(); - - if ( target.getName() == 'span' ) - target = target.getParent(); - - if ( target.getName() == 'a' && ( value = target.getChild( 0 ).getHtml() ) ) - { - // Trigger blur manually if there is focused node. - if ( focusedNode ) - onBlur( null, focusedNode ); - - var htmlPreview = dialog.getContentElement( 'info', 'htmlPreview' ).getElement(); - - dialog.getContentElement( 'info', 'charPreview' ).getElement().setHtml( value ); - htmlPreview.setHtml( CKEDITOR.tools.htmlEncode( value ) ); - target.getParent().addClass( "cke_light_background" ); - - // Memorize focused node. - focusedNode = target; - } - }; - - var onBlur = function( evt, target ) - { - target = target || evt.data.getTarget(); - - if ( target.getName() == 'span' ) - target = target.getParent(); - - if ( target.getName() == 'a' ) - { - dialog.getContentElement( 'info', 'charPreview' ).getElement().setHtml( ' ' ); - dialog.getContentElement( 'info', 'htmlPreview' ).getElement().setHtml( ' ' ); - target.getParent().removeClass( "cke_light_background" ); - - focusedNode = undefined; - } - }; - - var onKeydown = CKEDITOR.tools.addFunction( function( ev ) - { - ev = new CKEDITOR.dom.event( ev ); - - // Get an Anchor element. - var element = ev.getTarget(); - var relative, nodeToMove; - var keystroke = ev.getKeystroke(); - var 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( null, element ); - onFocus( null, 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( null, element ); - onFocus( null, nodeToMove ); - } - } - ev.preventDefault(); - break; - // SPACE - // ENTER is already handled as onClick - case 32 : - onChoice( { data: ev } ); - ev.preventDefault(); - break; - - // RIGHT-ARROW - case rtl ? 37 : 39 : - // TAB - case 9 : - // relative is TD - if ( ( relative = element.getParent().getNext() ) ) - { - nodeToMove = relative.getChild( 0 ); - if ( nodeToMove.type == 1 ) - { - nodeToMove.focus(); - onBlur( null, element ); - onFocus( null, 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( null, element ); - onFocus( null, nodeToMove ); - ev.preventDefault( true ); - } - else - onBlur( null, element ); - } - break; - - // LEFT-ARROW - case rtl ? 39 : 37 : - // SHIFT + TAB - case CKEDITOR.SHIFT + 9 : - // relative is TD - if ( ( relative = element.getParent().getPrevious() ) ) - { - nodeToMove = relative.getChild( 0 ); - nodeToMove.focus(); - onBlur( null, element ); - onFocus( null, nodeToMove ); - ev.preventDefault( true ); - } - // relative is TR - else if ( ( relative = element.getParent().getParent().getPrevious() ) ) - { - nodeToMove = relative.getLast().getChild( 0 ); - nodeToMove.focus(); - onBlur( null, element ); - onFocus( null, nodeToMove ); - ev.preventDefault( true ); - } - else - onBlur( null, element ); - break; - default : - // Do not stop not handled events. - return; - } - }); - - return { - title : lang.title, - minWidth : 430, - minHeight : 280, - buttons : [ CKEDITOR.dialog.cancelButton ], - charColumns : 17, - chars : - [ - '!','"','#','$','%','&',"'",'(',')','*','+','-','.','/', - '0','1','2','3','4','5','6','7','8','9',':',';', - '<','=','>','?','@', - 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O', - 'P','Q','R','S','T','U','V','W','X','Y','Z', - '[',']','^','_','`', - 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p', - 'q','r','s','t','u','v','w','x','y','z', - '{','|','}','~','€(EURO SIGN)','‘(LEFT SINGLE QUOTATION MARK)','’(RIGHT SINGLE QUOTATION MARK)','“(LEFT DOUBLE QUOTATION MARK)', - '”(RIGHT DOUBLE QUOTATION MARK)','–(EN DASH)','—(EM DASH)','¡(INVERTED EXCLAMATION MARK)','¢(CENT SIGN)','£(POUND SIGN)', - '¤(CURRENCY SIGN)','¥(YEN SIGN)','¦(BROKEN BAR)','§(SECTION SIGN)','¨(DIAERESIS)','©(COPYRIGHT SIGN)','ª(FEMININE ORDINAL INDICATOR)', - '«(LEFT-POINTING DOUBLE ANGLE QUOTATION MARK)','¬(NOT SIGN)','®(REGISTERED SIGN)','¯(MACRON)','°(DEGREE SIGN)','±(PLUS-MINUS SIGN)','²(SUPERSCRIPT TWO)', - '³(SUPERSCRIPT THREE)','´(ACUTE ACCENT)','µ(MICRO SIGN)','¶(PILCROW SIGN)','·(MIDDLE DOT)','¸(CEDILLA)', - '¹(SUPERSCRIPT ONE)','º(MASCULINE ORDINAL INDICATOR)','»(RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK)','¼(VULGAR FRACTION ONE QUARTER)','½(VULGAR FRACTION ONE HALF)','¾(VULGAR FRACTION THREE QUARTERS)', - '¿(INVERTED QUESTION MARK)','À(LATIN CAPITAL LETTER A WITH GRAVE)','Á(LATIN CAPITAL LETTER A WITH ACUTE)','Â(LATIN CAPITAL LETTER A WITH CIRCUMFLEX)','Ã(LATIN CAPITAL LETTER A WITH TILDE)','Ä(LATIN CAPITAL LETTER A WITH DIAERESIS)', - 'Å(LATIN CAPITAL LETTER A WITH RING ABOVE)','Æ(LATIN CAPITAL LETTER AE)','Ç(LATIN CAPITAL LETTER C WITH CEDILLA)','È(LATIN CAPITAL LETTER E WITH GRAVE)','É(LATIN CAPITAL LETTER E WITH ACUTE)','Ê(LATIN CAPITAL LETTER E WITH CIRCUMFLEX)', - 'Ë(LATIN CAPITAL LETTER E WITH DIAERESIS)','Ì(LATIN CAPITAL LETTER I WITH GRAVE)','Í(LATIN CAPITAL LETTER I WITH ACUTE)','Î(LATIN CAPITAL LETTER I WITH CIRCUMFLEX)','Ï(LATIN CAPITAL LETTER I WITH DIAERESIS)','Ð(LATIN CAPITAL LETTER ETH)', - 'Ñ(LATIN CAPITAL LETTER N WITH TILDE)','Ò(LATIN CAPITAL LETTER O WITH GRAVE)','Ó(LATIN CAPITAL LETTER O WITH ACUTE)','Ô(LATIN CAPITAL LETTER O WITH CIRCUMFLEX)','Õ(LATIN CAPITAL LETTER O WITH TILDE)','Ö(LATIN CAPITAL LETTER O WITH DIAERESIS)', - '×(MULTIPLICATION SIGN)','Ø(LATIN CAPITAL LETTER O WITH STROKE)','Ù(LATIN CAPITAL LETTER U WITH GRAVE)','Ú(LATIN CAPITAL LETTER U WITH ACUTE)','Û(LATIN CAPITAL LETTER U WITH CIRCUMFLEX)','Ü(LATIN CAPITAL LETTER U WITH DIAERESIS)', - 'Ý(LATIN CAPITAL LETTER Y WITH ACUTE)','Þ(LATIN CAPITAL LETTER THORN)','ß(LATIN SMALL LETTER SHARP S)','à(LATIN SMALL LETTER A WITH GRAVE)','á(LATIN SMALL LETTER A WITH ACUTE)','â(LATIN SMALL LETTER A WITH CIRCUMFLEX)', - 'ã(LATIN SMALL LETTER A WITH TILDE)','ä(LATIN SMALL LETTER A WITH DIAERESIS)','å(LATIN SMALL LETTER A WITH RING ABOVE)','æ(LATIN SMALL LETTER AE)','ç(LATIN SMALL LETTER C WITH CEDILLA)','è(LATIN SMALL LETTER E WITH GRAVE)', - 'é(LATIN SMALL LETTER E WITH ACUTE)','ê(LATIN SMALL LETTER E WITH CIRCUMFLEX)','ë(LATIN SMALL LETTER E WITH DIAERESIS)','ì(LATIN SMALL LETTER I WITH GRAVE)','í(LATIN SMALL LETTER I WITH ACUTE)','î(LATIN SMALL LETTER I WITH CIRCUMFLEX)', - 'ï(LATIN SMALL LETTER I WITH DIAERESIS)','ð(LATIN SMALL LETTER ETH)','ñ(LATIN SMALL LETTER N WITH TILDE)','ò(LATIN SMALL LETTER O WITH GRAVE)','ó(LATIN SMALL LETTER O WITH ACUTE)','ô(LATIN SMALL LETTER O WITH CIRCUMFLEX)', - 'õ(LATIN SMALL LETTER O WITH TILDE)','ö(LATIN SMALL LETTER O WITH DIAERESIS)', - '÷(DIVISION SIGN)','ø(LATIN SMALL LETTER O WITH STROKE)', - 'ù(LATIN SMALL LETTER U WITH GRAVE)','ú(LATIN SMALL LETTER U WITH ACUTE)', - 'û(LATIN SMALL LETTER U WITH CIRCUMFLEX)','ü(LATIN SMALL LETTER U WITH DIAERESIS)', - 'ü(LATIN SMALL LETTER U WITH DIAERESIS)','ý(LATIN SMALL LETTER Y WITH ACUTE)','þ(LATIN SMALL LETTER THORN)','ÿ(LATIN SMALL LETTER Y WITH DIAERESIS)', - 'Œ(LATIN CAPITAL LIGATURE OE)', - 'œ(LATIN SMALL LIGATURE OE)','Ŵ(LATIN CAPITAL LETTER W WITH CIRCUMFLEX)', - 'Ŷ(LATIN CAPITAL LETTER Y WITH CIRCUMFLEX)','ŵ(LATIN SMALL LETTER W WITH CIRCUMFLEX)', - 'ŷ(LATIN SMALL LETTER Y WITH CIRCUMFLEX)','‚(SINGLE LOW-9 QUOTATION MARK)', - '‛(SINGLE HIGH-REVERSED-9 QUOTATION MARK)','„(DOUBLE LOW-9 QUOTATION MARK)','…(HORIZONTAL ELLIPSIS)', - '™(TRADE MARK SIGN)','►(BLACK RIGHT-POINTING POINTER)','•(BULLET)', - '→(RIGHTWARDS ARROW)','⇒(RIGHTWARDS DOUBLE ARROW)','⇔(LEFT RIGHT DOUBLE ARROW)','♦(BLACK DIAMOND SUIT)','≈(ALMOST EQUAL TO)' - ], - onLoad : function() - { - var columns = this.definition.charColumns, - chars = this.definition.chars; - - var charsTableLabel = 'specialchar_table_label' + CKEDITOR.tools.getNextNumber(); - var html = [ '<table role="listbox" aria-labelledby="' + charsTableLabel + '"' + - ' style="width: 320px; height: 100%; border-collapse: separate;"' + - ' align="center" cellspacing="2" cellpadding="2" border="0">' ]; - - var i = 0, - size = chars.length, - character, - charDesc; - - while ( i < size ) - { - html.push( '<tr>' ) ; - - for ( var j = 0 ; j < columns ; j++, i++ ) - { - if ( ( character = chars[ i ] ) ) - { - charDesc = ''; - character = character.replace( /\((.*?)\)/, function( match, desc ) - { - charDesc = desc; - return ''; - } ); - - // Use character in case description unavailable. - charDesc = charDesc || character; - - var charLabelId = 'cke_specialchar_label_' + i + '_' + CKEDITOR.tools.getNextNumber(); - - html.push( - '<td class="cke_dark_background" style="cursor: default" role="presentation">' + - '<a href="javascript: void(0);" role="option"' + - ' aria-posinset="' + ( i +1 ) + '"', - ' aria-setsize="' + size + '"', - ' aria-labelledby="' + charLabelId + '"', - ' style="cursor: inherit; display: block; height: 1.25em; margin-top: 0.25em; text-align: center;" title="', CKEDITOR.tools.htmlEncode( charDesc ), '"' + - ' onkeydown="CKEDITOR.tools.callFunction( ' + onKeydown + ', event, this )"' + - ' onclick="CKEDITOR.tools.callFunction(' + onClick + ', this); return false;"' + - ' tabindex="-1">' + - '<span style="margin: 0 auto;cursor: inherit">' + - character + - '</span>' + - '<span class="cke_voice_label" id="' + charLabelId + '">' + - charDesc + - '</span></a>'); - } - else - html.push( '<td class="cke_dark_background"> ' ); - - html.push( '</td>' ); - } - html.push( '</tr>' ); - } - - html.push( '</tbody></table>', '<span id="' + charsTableLabel + '" class="cke_voice_label">' + lang.options +'</span>' ); - - this.getContentElement( 'info', 'charContainer' ).getElement().setHtml( html.join( '' ) ); - }, - contents : [ - { - id : 'info', - label : editor.lang.common.generalTab, - title : editor.lang.common.generalTab, - padding : 0, - align : 'top', - elements : [ - { - type : 'hbox', - align : 'top', - widths : [ '320px', '90px' ], - children : - [ - { - type : 'html', - id : 'charContainer', - html : '', - onMouseover : onFocus, - onMouseout : onBlur, - focus : function() - { - var firstChar = this.getElement().getElementsByTag( 'a' ).getItem( 0 ); - setTimeout(function() - { - firstChar.focus(); - onFocus( null, firstChar ); - }); - }, - onShow : function() - { - var firstChar = this.getElement().getChild( [ 0, 0, 0, 0, 0 ] ); - setTimeout( function() - { - firstChar.focus(); - onFocus( null, firstChar ); - }); - }, - onLoad : function( event ) - { - dialog = event.sender; - } - }, - { - type : 'hbox', - align : 'top', - widths : [ '100%' ], - children : - [ - { - type : 'vbox', - align : 'top', - children : - [ - { - type : 'html', - html : '<div></div>' - }, - { - type : 'html', - id : 'charPreview', - className : 'cke_dark_background', - style : 'border:1px solid #eeeeee;font-size:28px;height:40px;width:70px;padding-top:9px;font-family:\'Microsoft Sans Serif\',Arial,Helvetica,Verdana;text-align:center;', - html : '<div> </div>' - }, - { - type : 'html', - id : 'htmlPreview', - className : 'cke_dark_background', - style : 'border:1px solid #eeeeee;font-size:14px;height:20px;width:70px;padding-top:2px;font-family:\'Microsoft Sans Serif\',Arial,Helvetica,Verdana;text-align:center;', - html : '<div> </div>' - } - ] - } - ] - } - ] - } - ] - } - ] - }; -} ); +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.dialog.add( 'specialchar', function( editor )
+{
+ /**
+ * Simulate "this" of a dialog for non-dialog events.
+ * @type {CKEDITOR.dialog}
+ */
+ var dialog,
+ lang = editor.lang.specialChar;
+
+ var onChoice = function( evt )
+ {
+ var target, value;
+ if ( evt.data )
+ target = evt.data.getTarget();
+ else
+ target = new CKEDITOR.dom.element( evt );
+
+ if ( target.getName() == 'a' && ( value = target.getChild( 0 ).getHtml() ) )
+ {
+ target.removeClass( "cke_light_background" );
+ dialog.hide();
+
+ // We must use "insertText" here to keep text styled.
+ var span = editor.document.createElement( 'span' );
+ span.setHtml( value );
+ editor.insertText( span.getText() );
+ }
+ };
+
+ var onClick = CKEDITOR.tools.addFunction( onChoice );
+
+ var focusedNode;
+
+ var onFocus = function( evt, target )
+ {
+ var value;
+ target = target || evt.data.getTarget();
+
+ if ( target.getName() == 'span' )
+ target = target.getParent();
+
+ if ( target.getName() == 'a' && ( value = target.getChild( 0 ).getHtml() ) )
+ {
+ // Trigger blur manually if there is focused node.
+ if ( focusedNode )
+ onBlur( null, focusedNode );
+
+ var htmlPreview = dialog.getContentElement( 'info', 'htmlPreview' ).getElement();
+
+ dialog.getContentElement( 'info', 'charPreview' ).getElement().setHtml( value );
+ htmlPreview.setHtml( CKEDITOR.tools.htmlEncode( value ) );
+ target.getParent().addClass( "cke_light_background" );
+
+ // Memorize focused node.
+ focusedNode = target;
+ }
+ };
+
+ var onBlur = function( evt, target )
+ {
+ target = target || evt.data.getTarget();
+
+ if ( target.getName() == 'span' )
+ target = target.getParent();
+
+ if ( target.getName() == 'a' )
+ {
+ dialog.getContentElement( 'info', 'charPreview' ).getElement().setHtml( ' ' );
+ dialog.getContentElement( 'info', 'htmlPreview' ).getElement().setHtml( ' ' );
+ target.getParent().removeClass( "cke_light_background" );
+
+ focusedNode = undefined;
+ }
+ };
+
+ var onKeydown = CKEDITOR.tools.addFunction( function( ev )
+ {
+ ev = new CKEDITOR.dom.event( ev );
+
+ // Get an Anchor element.
+ 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( null, element );
+ onFocus( null, 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( null, element );
+ onFocus( null, nodeToMove );
+ }
+ }
+ ev.preventDefault();
+ break;
+ // SPACE
+ // ENTER is already handled as onClick
+ case 32 :
+ onChoice( { data: ev } );
+ ev.preventDefault();
+ break;
+
+ // RIGHT-ARROW
+ case rtl ? 37 : 39 :
+ // TAB
+ case 9 :
+ // relative is TD
+ if ( ( relative = element.getParent().getNext() ) )
+ {
+ nodeToMove = relative.getChild( 0 );
+ if ( nodeToMove.type == 1 )
+ {
+ nodeToMove.focus();
+ onBlur( null, element );
+ onFocus( null, 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( null, element );
+ onFocus( null, nodeToMove );
+ ev.preventDefault( true );
+ }
+ else
+ onBlur( null, element );
+ }
+ break;
+
+ // LEFT-ARROW
+ case rtl ? 39 : 37 :
+ // SHIFT + TAB
+ case CKEDITOR.SHIFT + 9 :
+ // relative is TD
+ if ( ( relative = element.getParent().getPrevious() ) )
+ {
+ nodeToMove = relative.getChild( 0 );
+ nodeToMove.focus();
+ onBlur( null, element );
+ onFocus( null, nodeToMove );
+ ev.preventDefault( true );
+ }
+ // relative is TR
+ else if ( ( relative = element.getParent().getParent().getPrevious() ) )
+ {
+ nodeToMove = relative.getLast().getChild( 0 );
+ nodeToMove.focus();
+ onBlur( null, element );
+ onFocus( null, nodeToMove );
+ ev.preventDefault( true );
+ }
+ else
+ onBlur( null, element );
+ break;
+ default :
+ // Do not stop not handled events.
+ return;
+ }
+ });
+
+ return {
+ title : lang.title,
+ minWidth : 430,
+ minHeight : 280,
+ buttons : [ CKEDITOR.dialog.cancelButton ],
+ charColumns : 17,
+ onLoad : function()
+ {
+ var columns = this.definition.charColumns,
+ extraChars = editor.config.extraSpecialChars,
+ chars = editor.config.specialChars;
+
+ var charsTableLabel = CKEDITOR.tools.getNextId() + '_specialchar_table_label';
+ var html = [ '<table role="listbox" aria-labelledby="' + charsTableLabel + '"' +
+ ' style="width: 320px; height: 100%; border-collapse: separate;"' +
+ ' align="center" cellspacing="2" cellpadding="2" border="0">' ];
+
+ var i = 0,
+ size = chars.length,
+ character,
+ charDesc;
+
+ while ( i < size )
+ {
+ html.push( '<tr>' ) ;
+
+ for ( var j = 0 ; j < columns ; j++, i++ )
+ {
+ if ( ( character = chars[ i ] ) )
+ {
+ charDesc = '';
+
+ if ( character instanceof Array )
+ {
+ charDesc = character[ 1 ];
+ character = character[ 0 ];
+ }
+ else
+ {
+ var _tmpName = character.replace( '&', '' ).replace( ';', '' ).replace( '#', '' );
+
+ // Use character in case description unavailable.
+ charDesc = lang[ _tmpName ] || character;
+ }
+
+ var charLabelId = 'cke_specialchar_label_' + i + '_' + CKEDITOR.tools.getNextNumber();
+
+ html.push(
+ '<td class="cke_dark_background" style="cursor: default" role="presentation">' +
+ '<a href="javascript: void(0);" role="option"' +
+ ' aria-posinset="' + ( i +1 ) + '"',
+ ' aria-setsize="' + size + '"',
+ ' aria-labelledby="' + charLabelId + '"',
+ ' style="cursor: inherit; display: block; height: 1.25em; margin-top: 0.25em; text-align: center;" title="', CKEDITOR.tools.htmlEncode( charDesc ), '"' +
+ ' onkeydown="CKEDITOR.tools.callFunction( ' + onKeydown + ', event, this )"' +
+ ' onclick="CKEDITOR.tools.callFunction(' + onClick + ', this); return false;"' +
+ ' tabindex="-1">' +
+ '<span style="margin: 0 auto;cursor: inherit">' +
+ character +
+ '</span>' +
+ '<span class="cke_voice_label" id="' + charLabelId + '">' +
+ charDesc +
+ '</span></a>');
+ }
+ else
+ html.push( '<td class="cke_dark_background"> ' );
+
+ html.push( '</td>' );
+ }
+ html.push( '</tr>' );
+ }
+
+ html.push( '</tbody></table>', '<span id="' + charsTableLabel + '" class="cke_voice_label">' + lang.options +'</span>' );
+
+ this.getContentElement( 'info', 'charContainer' ).getElement().setHtml( html.join( '' ) );
+ },
+ contents : [
+ {
+ id : 'info',
+ label : editor.lang.common.generalTab,
+ title : editor.lang.common.generalTab,
+ padding : 0,
+ align : 'top',
+ elements : [
+ {
+ type : 'hbox',
+ align : 'top',
+ widths : [ '320px', '90px' ],
+ children :
+ [
+ {
+ type : 'html',
+ id : 'charContainer',
+ html : '',
+ onMouseover : onFocus,
+ onMouseout : onBlur,
+ focus : function()
+ {
+ var firstChar = this.getElement().getElementsByTag( 'a' ).getItem( 0 );
+ setTimeout( function()
+ {
+ firstChar.focus();
+ onFocus( null, firstChar );
+ }, 0 );
+ },
+ onShow : function()
+ {
+ var firstChar = this.getElement().getChild( [ 0, 0, 0, 0, 0 ] );
+ setTimeout( function()
+ {
+ firstChar.focus();
+ onFocus( null, firstChar );
+ }, 0 );
+ },
+ onLoad : function( event )
+ {
+ dialog = event.sender;
+ }
+ },
+ {
+ type : 'hbox',
+ align : 'top',
+ widths : [ '100%' ],
+ children :
+ [
+ {
+ type : 'vbox',
+ align : 'top',
+ children :
+ [
+ {
+ type : 'html',
+ html : '<div></div>'
+ },
+ {
+ type : 'html',
+ id : 'charPreview',
+ className : 'cke_dark_background',
+ style : 'border:1px solid #eeeeee;font-size:28px;height:40px;width:70px;padding-top:9px;font-family:\'Microsoft Sans Serif\',Arial,Helvetica,Verdana;text-align:center;',
+ html : '<div> </div>'
+ },
+ {
+ type : 'html',
+ id : 'htmlPreview',
+ className : 'cke_dark_background',
+ style : 'border:1px solid #eeeeee;font-size:14px;height:20px;width:70px;padding-top:2px;font-family:\'Microsoft Sans Serif\',Arial,Helvetica,Verdana;text-align:center;',
+ html : '<div> </div>'
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ };
+} );
diff --git a/_source/plugins/specialchar/lang/_translationstatus.txt b/_source/plugins/specialchar/lang/_translationstatus.txt new file mode 100644 index 0000000..569238a --- /dev/null +++ b/_source/plugins/specialchar/lang/_translationstatus.txt @@ -0,0 +1,19 @@ +Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+
+cs.js Found: 118 Missing: 0
+cy.js Found: 118 Missing: 0
+de.js Found: 118 Missing: 0
+eo.js Found: 118 Missing: 0
+et.js Found: 31 Missing: 87
+fa.js Found: 24 Missing: 94
+fi.js Found: 23 Missing: 95
+fr.js Found: 118 Missing: 0
+hr.js Found: 23 Missing: 95
+it.js Found: 118 Missing: 0
+nb.js Found: 118 Missing: 0
+nl.js Found: 118 Missing: 0
+no.js Found: 118 Missing: 0
+tr.js Found: 118 Missing: 0
+ug.js Found: 39 Missing: 79
+zh-cn.js Found: 118 Missing: 0
diff --git a/_source/plugins/specialchar/lang/cs.js b/_source/plugins/specialchar/lang/cs.js new file mode 100644 index 0000000..9e30089 --- /dev/null +++ b/_source/plugins/specialchar/lang/cs.js @@ -0,0 +1,126 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'specialchar', 'cs',
+{
+ euro: 'Znak eura',
+ lsquo: 'Počáteční uvozovka jednoduchá',
+ rsquo: 'Koncová uvozovka jednoduchá',
+ ldquo: 'Počáteční uvozovka dvojitá',
+ rdquo: 'Koncová uvozovka dvojitá',
+ ndash: 'En pomlčka',
+ mdash: 'Em pomlčka',
+ iexcl: 'Obrácený vykřičník',
+ cent: 'Znak centu',
+ pound: 'Znak libry',
+ curren: 'Znak měny',
+ yen: 'Znak jenu',
+ brvbar: 'Přerušená svislá čára',
+ sect: 'Znak oddílu',
+ uml: 'Přehláska',
+ copy: 'Znak copyrightu',
+ ordf: 'Ženský indikátor rodu',
+ laquo: 'Znak dvojitých lomených uvozovek vlevo',
+ not: 'Logistický zápor',
+ reg: 'Znak registrace',
+ macr: 'Pomlčka nad',
+ deg: 'Znak stupně',
+ sup2: 'Dvojka jako horní index',
+ sup3: 'Trojka jako horní index',
+ acute: 'Čárka nad vpravo',
+ micro: 'Znak mikro',
+ para: 'Znak odstavce',
+ middot: 'Tečka uprostřed',
+ cedil: 'Ocásek vlevo',
+ sup1: 'Jednička jako horní index',
+ ordm: 'Mužský indikátor rodu',
+ raquo: 'Znak dvojitých lomených uvozovek vpravo',
+ frac14: 'Obyčejný zlomek jedna čtvrtina',
+ frac12: 'Obyčejný zlomek jedna polovina',
+ frac34: 'Obyčejný zlomek tři čtvrtiny',
+ iquest: 'Znak obráceného otazníku',
+ Agrave: 'Velké písmeno latinky A s čárkou nad vlevo',
+ Aacute: 'Velké písmeno latinky A s čárkou nad vpravo',
+ Acirc: 'Velké písmeno latinky A s vokáněm',
+ Atilde: 'Velké písmeno latinky A s tildou',
+ Auml: 'Velké písmeno latinky A s dvěma tečkami',
+ Aring: 'Velké písmeno latinky A s kroužkem nad',
+ AElig: 'Velké písmeno latinky Ae',
+ Ccedil: 'Velké písmeno latinky C s ocáskem vlevo',
+ Egrave: 'Velké písmeno latinky E s čárkou nad vlevo',
+ Eacute: 'Velké písmeno latinky E s čárkou nad vpravo',
+ Ecirc: 'Velké písmeno latinky E s vokáněm',
+ Euml: 'Velké písmeno latinky E s dvěma tečkami',
+ Igrave: 'Velké písmeno latinky I s čárkou nad vlevo',
+ Iacute: 'Velké písmeno latinky I s čárkou nad vpravo',
+ Icirc: 'Velké písmeno latinky I s vokáněm',
+ Iuml: 'Velké písmeno latinky I s dvěma tečkami',
+ ETH: 'Velké písmeno latinky Eth',
+ Ntilde: 'Velké písmeno latinky N s tildou',
+ Ograve: 'Velké písmeno latinky O s čárkou nad vlevo',
+ Oacute: 'Velké písmeno latinky O s čárkou nad vpravo',
+ Ocirc: 'Velké písmeno latinky O s vokáněm',
+ Otilde: 'Velké písmeno latinky O s tildou',
+ Ouml: 'Velké písmeno latinky O s dvěma tečkami',
+ times: 'Znak násobení',
+ Oslash: 'Velké písmeno latinky O přeškrtnuté',
+ Ugrave: 'Velké písmeno latinky U s čárkou nad vlevo',
+ Uacute: 'Velké písmeno latinky U s čárkou nad vpravo',
+ Ucirc: 'Velké písmeno latinky U s vokáněm',
+ Uuml: 'Velké písmeno latinky U s dvěma tečkami',
+ Yacute: 'Velké písmeno latinky Y s čárkou nad vpravo',
+ THORN: 'Velké písmeno latinky Thorn',
+ szlig: 'Malé písmeno latinky ostré s',
+ agrave: 'Malé písmeno latinky a s čárkou nad vlevo',
+ aacute: 'Malé písmeno latinky a s čárkou nad vpravo',
+ acirc: 'Malé písmeno latinky a s vokáněm',
+ atilde: 'Malé písmeno latinky a s tildou',
+ auml: 'Malé písmeno latinky a s dvěma tečkami',
+ aring: 'Malé písmeno latinky a s kroužkem nad',
+ aelig: 'Malé písmeno latinky ae',
+ ccedil: 'Malé písmeno latinky c s ocáskem vlevo',
+ egrave: 'Malé písmeno latinky e s čárkou nad vlevo',
+ eacute: 'Malé písmeno latinky e s čárkou nad vpravo',
+ ecirc: 'Malé písmeno latinky e s vokáněm',
+ euml: 'Malé písmeno latinky e s dvěma tečkami',
+ igrave: 'Malé písmeno latinky i s čárkou nad vlevo',
+ iacute: 'Malé písmeno latinky i s čárkou nad vpravo',
+ icirc: 'Malé písmeno latinky i s vokáněm',
+ iuml: 'Malé písmeno latinky i s dvěma tečkami',
+ eth: 'Malé písmeno latinky eth',
+ ntilde: 'Malé písmeno latinky n s tildou',
+ ograve: 'Malé písmeno latinky o s čárkou nad vlevo',
+ oacute: 'Malé písmeno latinky o s čárkou nad vpravo',
+ ocirc: 'Malé písmeno latinky o s vokáněm',
+ otilde: 'Malé písmeno latinky o s tildou',
+ ouml: 'Malé písmeno latinky o s dvěma tečkami',
+ divide: 'Znak dělení',
+ oslash: 'Malé písmeno latinky o přeškrtnuté',
+ ugrave: 'Malé písmeno latinky u s čárkou nad vlevo',
+ uacute: 'Malé písmeno latinky u s čárkou nad vpravo',
+ ucirc: 'Malé písmeno latinky u s vokáněm',
+ uuml: 'Malé písmeno latinky u s dvěma tečkami',
+ yacute: 'Malé písmeno latinky y s čárkou nad vpravo',
+ thorn: 'Malé písmeno latinky thorn',
+ yuml: 'Malé písmeno latinky y s dvěma tečkami',
+ OElig: 'Velká ligatura latinky OE',
+ oelig: 'Malá ligatura latinky OE',
+ '372': 'Velké písmeno latinky W s vokáněm',
+ '374': 'Velké písmeno latinky Y s vokáněm',
+ '373': 'Malé písmeno latinky w s vokáněm',
+ '375': 'Malé písmeno latinky y s vokáněm',
+ sbquo: 'Dolní 9 uvozovka jednoduchá',
+ '8219': 'Horní obrácená 9 uvozovka jednoduchá',
+ bdquo: 'Dolní 9 uvozovka dvojitá',
+ hellip: 'Trojtečkový úvod',
+ trade: 'Obchodní značka',
+ '9658': 'Černý ukazatel směřující vpravo',
+ bull: 'Kolečko',
+ rarr: 'Šipka vpravo',
+ rArr: 'Dvojitá šipka vpravo',
+ hArr: 'Dvojitá šipka vlevo a vpravo',
+ diams: 'Černé piky',
+ asymp: 'Téměř se rovná'
+});
diff --git a/_source/plugins/specialchar/lang/cy.js b/_source/plugins/specialchar/lang/cy.js new file mode 100644 index 0000000..df549f8 --- /dev/null +++ b/_source/plugins/specialchar/lang/cy.js @@ -0,0 +1,126 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'specialchar', 'cy',
+{
+ euro: 'Arwydd yr Ewro',
+ lsquo: 'Dyfynnod chwith unigol',
+ rsquo: 'Dyfynnod dde unigol',
+ ldquo: 'Dyfynnod chwith dwbl',
+ rdquo: 'Dyfynnod dde dwbl',
+ ndash: 'Cysylltnod en',
+ mdash: 'Cysylltnod em',
+ iexcl: 'Ebychnod gwrthdro',
+ cent: 'Arwydd sent',
+ pound: 'Arwydd punt',
+ curren: 'Arwydd arian cyfred',
+ yen: 'Arwydd yen',
+ brvbar: 'Bar toriedig',
+ sect: 'Arwydd adran',
+ uml: 'Didolnod',
+ copy: 'Arwydd hawlfraint',
+ ordf: 'Dangosydd benywaidd',
+ laquo: 'Dyfynnod dwbl ar ongl i\'r chwith',
+ not: 'Arwydd Nid',
+ reg: 'Arwydd cofrestredig',
+ macr: 'Macron',
+ deg: 'Arwydd gradd',
+ sup2: 'Dau uwchsgript',
+ sup3: 'Tri uwchsgript',
+ acute: 'Acen ddyrchafedig',
+ micro: 'Arwydd micro',
+ para: 'Arwydd pilcrow',
+ middot: 'Dot canol',
+ cedil: 'Sedila',
+ sup1: 'Un uwchsgript',
+ ordm: 'Dangosydd gwrywaidd',
+ raquo: 'Dyfynnod dwbl ar ongl i\'r dde',
+ frac14: 'Ffracsiwn cyffredin un cwarter',
+ frac12: 'Ffracsiwn cyffredin un hanner',
+ frac34: 'Ffracsiwn cyffredin tri chwarter',
+ iquest: 'Marc cwestiwn gwrthdroëdig',
+ Agrave: 'Priflythyren A Lladinaidd gydag acen ddisgynedig',
+ Aacute: 'Priflythyren A Lladinaidd gydag acen ddyrchafedig',
+ Acirc: 'Priflythyren A Lladinaidd gydag acen grom',
+ Atilde: 'Priflythyren A Lladinaidd gyda thild',
+ Auml: 'Priflythyren A Lladinaidd gyda didolnod',
+ Aring: 'Priflythyren A Lladinaidd gyda chylch uwchben',
+ AElig: 'Priflythyren Æ Lladinaidd',
+ Ccedil: 'Priflythyren C Lladinaidd gyda sedila',
+ Egrave: 'Priflythyren E Lladinaidd gydag acen ddisgynedig',
+ Eacute: 'Priflythyren E Lladinaidd gydag acen ddyrchafedig',
+ Ecirc: 'Priflythyren E Lladinaidd gydag acen grom',
+ Euml: 'Priflythyren E Lladinaidd gyda didolnod',
+ Igrave: 'Priflythyren I Lladinaidd gydag acen ddisgynedig',
+ Iacute: 'Priflythyren I Lladinaidd gydag acen ddyrchafedig',
+ Icirc: 'Priflythyren I Lladinaidd gydag acen grom',
+ Iuml: 'Priflythyren I Lladinaidd gyda didolnod',
+ ETH: 'Priflythyren Eth',
+ Ntilde: 'Priflythyren N Lladinaidd gyda thild',
+ Ograve: 'Priflythyren O Lladinaidd gydag acen ddisgynedig',
+ Oacute: 'Priflythyren O Lladinaidd gydag acen ddyrchafedig',
+ Ocirc: 'Priflythyren O Lladinaidd gydag acen grom',
+ Otilde: 'Priflythyren O Lladinaidd gyda thild',
+ Ouml: 'Priflythyren O Lladinaidd gyda didolnod',
+ times: 'Arwydd lluosi',
+ Oslash: 'Priflythyren O Lladinaidd gyda strôc',
+ Ugrave: 'Priflythyren U Lladinaidd gydag acen ddisgynedig',
+ Uacute: 'Priflythyren U Lladinaidd gydag acen ddyrchafedig',
+ Ucirc: 'Priflythyren U Lladinaidd gydag acen grom',
+ Uuml: 'Priflythyren U Lladinaidd gyda didolnod',
+ Yacute: 'Priflythyren Y Lladinaidd gydag acen ddyrchafedig',
+ THORN: 'Priflythyren Thorn',
+ szlig: 'Llythyren s fach Lladinaidd siarp ',
+ agrave: 'Llythyren a fach Lladinaidd gydag acen ddisgynedig',
+ aacute: 'Llythyren a fach Lladinaidd gydag acen ddyrchafedig',
+ acirc: 'Llythyren a fach Lladinaidd gydag acen grom',
+ atilde: 'Llythyren a fach Lladinaidd gyda thild',
+ auml: 'Llythyren a fach Lladinaidd gyda didolnod',
+ aring: 'Llythyren a fach Lladinaidd gyda chylch uwchben',
+ aelig: 'Llythyren æ fach Lladinaidd',
+ ccedil: 'Llythyren c fach Lladinaidd gyda sedila',
+ egrave: 'Llythyren e fach Lladinaidd gydag acen ddisgynedig',
+ eacute: 'Llythyren e fach Lladinaidd gydag acen ddyrchafedig',
+ ecirc: 'Llythyren e fach Lladinaidd gydag acen grom',
+ euml: 'Llythyren e fach Lladinaidd gyda didolnod',
+ igrave: 'Llythyren i fach Lladinaidd gydag acen ddisgynedig',
+ iacute: 'Llythyren i fach Lladinaidd gydag acen ddyrchafedig',
+ icirc: 'Llythyren i fach Lladinaidd gydag acen grom',
+ iuml: 'Llythyren i fach Lladinaidd gyda didolnod',
+ eth: 'Llythyren eth fach',
+ ntilde: 'Llythyren n fach Lladinaidd gyda thild',
+ ograve: 'Llythyren o fach Lladinaidd gydag acen ddisgynedig',
+ oacute: 'Llythyren o fach Lladinaidd gydag acen ddyrchafedig',
+ ocirc: 'Llythyren o fach Lladinaidd gydag acen grom',
+ otilde: 'Llythyren o fach Lladinaidd gyda thild',
+ ouml: 'Llythyren o fach Lladinaidd gyda didolnod',
+ divide: 'Arwydd rhannu',
+ oslash: 'Llyth',
+ ugrave: 'Llythyren u fach Lladinaidd gydag acen ddisgynedig',
+ uacute: 'Llythyren u fach Lladinaidd gydag acen ddyrchafedig',
+ ucirc: 'Llythyren u fach Lladinaidd gydag acen grom',
+ uuml: 'Llythyren u fach Lladinaidd gyda didolnod',
+ yacute: 'Llythyren y fach Lladinaidd gydag acen ddisgynedig',
+ thorn: 'Llythyren o fach Lladinaidd gyda strôc',
+ yuml: 'Llythyren y fach Lladinaidd gyda didolnod',
+ OElig: 'Priflythyren cwlwm OE Lladinaidd ',
+ oelig: 'Priflythyren cwlwm oe Lladinaidd ',
+ '372': 'Priflythyren W gydag acen grom',
+ '374': 'Priflythyren Y gydag acen grom',
+ '373': 'Llythyren w fach gydag acen grom',
+ '375': 'Llythyren y fach gydag acen grom',
+ sbquo: 'Dyfynnod sengl 9-isel',
+ '8219': 'Dyfynnod sengl 9-uchel cildro',
+ bdquo: 'Dyfynnod dwbl 9-isel',
+ hellip: 'Coll geiriau llorweddol',
+ trade: 'Arwydd marc masnachol',
+ '9658': 'Pwyntydd du i\'r dde',
+ bull: 'Bwled',
+ rarr: 'Saeth i\'r dde',
+ rArr: 'Saeth ddwbl i\'r dde',
+ hArr: 'Saeth ddwbl i\'r chwith',
+ diams: 'Siwt diemwnt du',
+ asymp: 'Bron yn hafal iddo'
+});
diff --git a/_source/plugins/specialchar/lang/de.js b/_source/plugins/specialchar/lang/de.js new file mode 100644 index 0000000..01c88cf --- /dev/null +++ b/_source/plugins/specialchar/lang/de.js @@ -0,0 +1,126 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'specialchar', 'de',
+{
+ euro: 'Euro Zeichen',
+ lsquo: 'Hochkomma links',
+ rsquo: 'Hochkomma rechts',
+ ldquo: 'Anführungszeichen links',
+ rdquo: 'Anführungszeichen rechts',
+ ndash: 'kleiner Strich',
+ mdash: 'mittlerer Strich',
+ iexcl: 'invertiertes Ausrufezeichen',
+ cent: 'Cent',
+ pound: 'Pfund',
+ curren: 'Währung',
+ yen: 'Yen',
+ brvbar: 'gestrichelte Linie',
+ sect: '§ Zeichen',
+ uml: 'Diäresis',
+ copy: 'Copyright',
+ ordf: 'Feminine ordinal Anzeige',
+ laquo: 'Nach links zeigenden Doppel-Winkel Anführungszeichen',
+ not: 'Not-Zeichen',
+ reg: 'Registriert',
+ macr: 'Längezeichen',
+ deg: 'Grad',
+ sup2: 'Hoch 2',
+ sup3: 'Hoch 3',
+ acute: 'Akzentzeichen ',
+ micro: 'Micro',
+ para: 'Pilcrow-Zeichen',
+ middot: 'Mittelpunkt',
+ cedil: 'Cedilla',
+ sup1: 'Hoch 1',
+ ordm: 'Männliche Ordnungszahl Anzeige',
+ raquo: 'Nach rechts zeigenden Doppel-Winkel Anführungszeichen',
+ frac14: 'ein Viertel',
+ frac12: 'Hälfte',
+ frac34: 'Dreiviertel',
+ iquest: 'Umgekehrtes Fragezeichen',
+ Agrave: 'Lateinischer Buchstabe A mit AkzentGrave',
+ Aacute: 'Lateinischer Buchstabe A mit Akutakzent',
+ Acirc: 'Lateinischer Buchstabe A mit Zirkumflex',
+ Atilde: 'Lateinischer Buchstabe A mit Tilde',
+ Auml: 'Lateinischer Buchstabe A mit Trema',
+ Aring: 'Lateinischer Buchstabe A mit Ring oben',
+ AElig: 'Lateinischer Buchstabe Æ',
+ Ccedil: 'Lateinischer Buchstabe C mit Cedille',
+ Egrave: 'Lateinischer Buchstabe E mit AkzentGrave',
+ Eacute: 'Lateinischer Buchstabe E mit Akutakzent',
+ Ecirc: 'Lateinischer Buchstabe E mit Zirkumflex',
+ Euml: 'Lateinischer Buchstabe E Trema',
+ Igrave: 'Lateinischer Buchstabe I mit AkzentGrave',
+ Iacute: 'Lateinischer Buchstabe I mit Akutakzent',
+ Icirc: 'Lateinischer Buchstabe I mit Zirkumflex',
+ Iuml: 'Lateinischer Buchstabe I mit Trema',
+ ETH: 'Lateinischer Buchstabe Eth',
+ Ntilde: 'Lateinischer Buchstabe N mit Tilde',
+ Ograve: 'Lateinischer Buchstabe O mit AkzentGrave',
+ Oacute: 'Lateinischer Buchstabe O mit Akutakzent',
+ Ocirc: 'Lateinischer Buchstabe O mit Zirkumflex',
+ Otilde: 'Lateinischer Buchstabe O mit Tilde',
+ Ouml: 'Lateinischer Buchstabe O mit Trema',
+ times: 'Multiplikation',
+ Oslash: 'Lateinischer Buchstabe O durchgestrichen',
+ Ugrave: 'Lateinischer Buchstabe U mit Akzentgrave',
+ Uacute: 'Lateinischer Buchstabe U mit Akutakzent',
+ Ucirc: 'Lateinischer Buchstabe U mit Zirkumflex',
+ Uuml: 'Lateinischer Buchstabe a mit Trema',
+ Yacute: 'Lateinischer Buchstabe a mit Akzent',
+ THORN: 'Lateinischer Buchstabe mit Dorn',
+ szlig: 'Kleiner lateinischer Buchstabe scharfe s',
+ agrave: 'Kleiner lateinischer Buchstabe a mit Accent grave',
+ aacute: 'Kleiner lateinischer Buchstabe a mit Akut',
+ acirc: 'Lateinischer Buchstabe a mit Zirkumflex',
+ atilde: 'Lateinischer Buchstabe a mit Tilde',
+ auml: 'Kleiner lateinischer Buchstabe a mit Trema',
+ aring: 'Kleiner lateinischer Buchstabe a mit Ring oben',
+ aelig: 'Lateinischer Buchstabe æ',
+ ccedil: 'Kleiner lateinischer Buchstabe c mit Cedille',
+ egrave: 'Kleiner lateinischer Buchstabe e mit Accent grave',
+ eacute: 'Kleiner lateinischer Buchstabe e mit Akut',
+ ecirc: 'Kleiner lateinischer Buchstabe e mit Zirkumflex',
+ euml: 'Kleiner lateinischer Buchstabe e mit Trema',
+ igrave: 'Kleiner lateinischer Buchstabe i mit AkzentGrave',
+ iacute: 'Kleiner lateinischer Buchstabe i mit Akzent',
+ icirc: 'Kleiner lateinischer Buchstabe i mit Zirkumflex',
+ iuml: 'Kleiner lateinischer Buchstabe i mit Trema',
+ eth: 'Kleiner lateinischer Buchstabe eth',
+ ntilde: 'Kleiner lateinischer Buchstabe n mit Tilde',
+ ograve: 'Kleiner lateinischer Buchstabe o mit Accent grave',
+ oacute: 'Kleiner lateinischer Buchstabe o mit Akzent',
+ ocirc: 'Kleiner lateinischer Buchstabe o mit Zirkumflex',
+ otilde: 'Lateinischer Buchstabe i mit Tilde',
+ ouml: 'Kleiner lateinischer Buchstabe o mit Trema',
+ divide: 'Divisionszeichen',
+ oslash: 'Kleiner lateinischer Buchstabe o durchgestrichen',
+ ugrave: 'Kleiner lateinischer Buchstabe u mit Accent grave',
+ uacute: 'Kleiner lateinischer Buchstabe u mit Akut',
+ ucirc: 'Kleiner lateinischer Buchstabe u mit Zirkumflex',
+ uuml: 'Kleiner lateinischer Buchstabe u mit Trema',
+ yacute: 'Kleiner lateinischer Buchstabe y mit Akut',
+ thorn: 'Kleiner lateinischer Buchstabe Dorn',
+ yuml: 'Kleiner lateinischer Buchstabe y mit Trema',
+ OElig: 'Lateinischer Buchstabe Ligatur OE',
+ oelig: 'Kleiner lateinischer Buchstabe Ligatur OE',
+ '372': 'Lateinischer Buchstabe W mit Zirkumflex',
+ '374': 'Lateinischer Buchstabe Y mit Zirkumflex',
+ '373': 'Kleiner lateinischer Buchstabe w mit Zirkumflex',
+ '375': 'Kleiner lateinischer Buchstabe y mit Zirkumflex',
+ sbquo: 'Tiefergestelltes Komma',
+ '8219': 'Rumgedrehtes Komma',
+ bdquo: 'Doppeltes Anführungszeichen unten',
+ hellip: 'horizontale Auslassungspunkte',
+ trade: 'Handelszeichen',
+ '9658': 'Dreickspfeil rechts',
+ bull: 'Bullet',
+ rarr: 'Pfeil rechts',
+ rArr: 'Doppelpfeil rechts',
+ hArr: 'Doppelpfeil links',
+ diams: 'Karo',
+ asymp: 'Ungefähr'
+});
diff --git a/_source/plugins/specialchar/lang/en.js b/_source/plugins/specialchar/lang/en.js new file mode 100644 index 0000000..97e2ad5 --- /dev/null +++ b/_source/plugins/specialchar/lang/en.js @@ -0,0 +1,126 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'specialchar', 'en',
+{
+ euro: 'Euro sign',
+ lsquo: 'Left single quotation mark',
+ rsquo: 'Right single quotation mark',
+ ldquo: 'Left double quotation mark',
+ rdquo: 'Right double quotation mark',
+ ndash: 'En dash',
+ mdash: 'Em dash',
+ iexcl: 'Inverted exclamation mark',
+ cent: 'Cent sign',
+ pound: 'Pound sign',
+ curren: 'Currency sign',
+ yen: 'Yen sign',
+ brvbar: 'Broken bar',
+ sect: 'Section sign',
+ uml: 'Diaeresis',
+ copy: 'Copyright sign',
+ ordf: 'Feminine ordinal indicator',
+ laquo: 'Left-pointing double angle quotation mark',
+ not: 'Not sign',
+ reg: 'Registered sign',
+ macr: 'Macron',
+ deg: 'Degree sign',
+ sup2: 'Superscript two',
+ sup3: 'Superscript three',
+ acute: 'Acute accent',
+ micro: 'Micro sign',
+ para: 'Pilcrow sign',
+ middot: 'Middle dot',
+ cedil: 'Cedilla',
+ sup1: 'Superscript one',
+ ordm: 'Masculine ordinal indicator',
+ raquo: 'Right-pointing double angle quotation mark',
+ frac14: 'Vulgar fraction one quarter',
+ frac12: 'Vulgar fraction one half',
+ frac34: 'Vulgar fraction three quarters',
+ iquest: 'Inverted question mark',
+ Agrave: 'Latin capital letter A with grave accent',
+ Aacute: 'Latin capital letter A with acute accent',
+ Acirc: 'Latin capital letter A with circumflex',
+ Atilde: 'Latin capital letter A with tilde',
+ Auml: 'Latin capital letter A with diaeresis',
+ Aring: 'Latin capital letter A with ring above',
+ AElig: 'Latin Capital letter Æ',
+ Ccedil: 'Latin capital letter C with cedilla',
+ Egrave: 'Latin capital letter E with grave accent',
+ Eacute: 'Latin capital letter E with acute accent',
+ Ecirc: 'Latin capital letter E with circumflex',
+ Euml: 'Latin capital letter E with diaeresis',
+ Igrave: 'Latin capital letter I with grave accent',
+ Iacute: 'Latin capital letter I with acute accent',
+ Icirc: 'Latin capital letter I with circumflex',
+ Iuml: 'Latin capital letter I with diaeresis',
+ ETH: 'Latin capital letter Eth',
+ Ntilde: 'Latin capital letter N with tilde',
+ Ograve: 'Latin capital letter O with grave accent',
+ Oacute: 'Latin capital letter O with acute accent',
+ Ocirc: 'Latin capital letter O with circumflex',
+ Otilde: 'Latin capital letter O with tilde',
+ Ouml: 'Latin capital letter O with diaeresis',
+ times: 'Multiplication sign',
+ Oslash: 'Latin capital letter O with stroke',
+ Ugrave: 'Latin capital letter U with grave accent',
+ Uacute: 'Latin capital letter U with acute accent',
+ Ucirc: 'Latin capital letter U with circumflex',
+ Uuml: 'Latin capital letter U with diaeresis',
+ Yacute: 'Latin capital letter Y with acute accent',
+ THORN: 'Latin capital letter Thorn',
+ szlig: 'Latin small letter sharp s',
+ agrave: 'Latin small letter a with grave accent',
+ aacute: 'Latin small letter a with acute accent',
+ acirc: 'Latin small letter a with circumflex',
+ atilde: 'Latin small letter a with tilde',
+ auml: 'Latin small letter a with diaeresis',
+ aring: 'Latin small letter a with ring above',
+ aelig: 'Latin small letter æ',
+ ccedil: 'Latin small letter c with cedilla',
+ egrave: 'Latin small letter e with grave accent',
+ eacute: 'Latin small letter e with acute accent',
+ ecirc: 'Latin small letter e with circumflex',
+ euml: 'Latin small letter e with diaeresis',
+ igrave: 'Latin small letter i with grave accent',
+ iacute: 'Latin small letter i with acute accent',
+ icirc: 'Latin small letter i with circumflex',
+ iuml: 'Latin small letter i with diaeresis',
+ eth: 'Latin small letter eth',
+ ntilde: 'Latin small letter n with tilde',
+ ograve: 'Latin small letter o with grave accent',
+ oacute: 'Latin small letter o with acute accent',
+ ocirc: 'Latin small letter o with circumflex',
+ otilde: 'Latin small letter o with tilde',
+ ouml: 'Latin small letter o with diaeresis',
+ divide: 'Division sign',
+ oslash: 'Latin small letter o with stroke',
+ ugrave: 'Latin small letter u with grave accent',
+ uacute: 'Latin small letter u with acute accent',
+ ucirc: 'Latin small letter u with circumflex',
+ uuml: 'Latin small letter u with diaeresis',
+ yacute: 'Latin small letter y with acute accent',
+ thorn: 'Latin small letter thorn',
+ yuml: 'Latin small letter y with diaeresis',
+ OElig: 'Latin capital ligature OE',
+ oelig: 'Latin small ligature oe',
+ '372': 'Latin capital letter W with circumflex',
+ '374': 'Latin capital letter Y with circumflex',
+ '373': 'Latin small letter w with circumflex',
+ '375': 'Latin small letter y with circumflex',
+ sbquo: 'Single low-9 quotation mark',
+ '8219': 'Single high-reversed-9 quotation mark',
+ bdquo: 'Double low-9 quotation mark',
+ hellip: 'Horizontal ellipsis',
+ trade: 'Trade mark sign',
+ '9658': 'Black right-pointing pointer',
+ bull: 'Bullet',
+ rarr: 'Rightwards arrow',
+ rArr: 'Rightwards double arrow',
+ hArr: 'Left right double arrow',
+ diams: 'Black diamond suit',
+ asymp: 'Almost equal to'
+});
diff --git a/_source/plugins/specialchar/lang/eo.js b/_source/plugins/specialchar/lang/eo.js new file mode 100644 index 0000000..1ea2371 --- /dev/null +++ b/_source/plugins/specialchar/lang/eo.js @@ -0,0 +1,126 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'specialchar', 'eo',
+{
+ euro: 'Eŭrosigno',
+ lsquo: 'Supra 6-citilo',
+ rsquo: 'Supra 9-citilo',
+ ldquo: 'Supra 66-citilo',
+ rdquo: 'Supra 99-citilo',
+ ndash: 'Streketo',
+ mdash: 'Substreko',
+ iexcl: 'Renversita krisigno',
+ cent: 'Cendosigno',
+ pound: 'Pundosigno',
+ curren: 'Monersigno',
+ yen: 'Enosigno',
+ brvbar: 'Rompita vertikala streko',
+ sect: 'Kurba paragrafo',
+ uml: 'Tremao',
+ copy: 'Kopirajtosigno',
+ ordf: 'Adjektiva numerfinaĵo',
+ laquo: 'Duobla malplio-citilo',
+ not: 'Negohoko',
+ reg: 'Registrita marko',
+ macr: 'Superstreko',
+ deg: 'Gradosigno',
+ sup2: 'Supra indico 2',
+ sup3: 'Supra indico 3',
+ acute: 'Dekstra korno',
+ micro: 'Mikrosigno',
+ para: 'Rekta paragrafo',
+ middot: 'Meza punkto',
+ cedil: 'Zoeto',
+ sup1: 'Supra indico 1',
+ ordm: 'Substantiva numerfinaĵo',
+ raquo: 'Duobla plio-citilo',
+ frac14: 'Kvaronosigno',
+ frac12: 'Duonosigno',
+ frac34: 'Trikvaronosigno',
+ iquest: 'renversita demandosigno',
+ Agrave: 'Latina ĉeflitero A kun liva korno',
+ Aacute: 'Latina ĉeflitero A kun dekstra korno',
+ Acirc: 'Latina ĉeflitero A kun ĉapelo',
+ Atilde: 'Latina ĉeflitero A kun tildo',
+ Auml: 'Latina ĉeflitero A kun tremao',
+ Aring: 'Latina ĉeflitero A kun superringo',
+ AElig: 'Latina ĉeflitera ligaturo Æ',
+ Ccedil: 'Latina ĉeflitero C kun zoeto',
+ Egrave: 'Latina ĉeflitero E kun liva korno',
+ Eacute: 'Latina ĉeflitero E kun dekstra korno',
+ Ecirc: 'Latina ĉeflitero E kun ĉapelo',
+ Euml: 'Latina ĉeflitero E kun tremao',
+ Igrave: 'Latina ĉeflitero I kun liva korno',
+ Iacute: 'Latina ĉeflitero I kun dekstra korno',
+ Icirc: 'Latina ĉeflitero I kun ĉapelo',
+ Iuml: 'Latina ĉeflitero I kun tremao',
+ ETH: 'Latina ĉeflitero islanda edo',
+ Ntilde: 'Latina ĉeflitero N kun tildo',
+ Ograve: 'Latina ĉeflitero O kun liva korno',
+ Oacute: 'Latina ĉeflitero O kun dekstra korno',
+ Ocirc: 'Latina ĉeflitero O kun ĉapelo',
+ Otilde: 'Latina ĉeflitero O kun tildo',
+ Ouml: 'Latina ĉeflitero O kun tremao',
+ times: 'Multipliko',
+ Oslash: 'Latina ĉeflitero O trastrekita',
+ Ugrave: 'Latina ĉeflitero U kun liva korno',
+ Uacute: 'Latina ĉeflitero U kun dekstra korno',
+ Ucirc: 'Latina ĉeflitero U kun ĉapelo',
+ Uuml: 'Latina ĉeflitero U kun tremao',
+ Yacute: 'Latina ĉeflitero Y kun dekstra korno',
+ THORN: 'Latina ĉeflitero islanda dorno',
+ szlig: 'Latina etlitero germana sozo (akra s)',
+ agrave: 'Latina etlitero a kun liva korno',
+ aacute: 'Latina etlitero a kun dekstra korno',
+ acirc: 'Latina etlitero a kun ĉapelo',
+ atilde: 'Latina etlitero a kun tildo',
+ auml: 'Latina etlitero a kun tremao',
+ aring: 'Latina etlitero a kun superringo',
+ aelig: 'Latina etlitera ligaturo æ',
+ ccedil: 'Latina etlitero c kun zoeto',
+ egrave: 'Latina etlitero e kun liva korno',
+ eacute: 'Latina etlitero e kun dekstra korno',
+ ecirc: 'Latina etlitero e kun ĉapelo',
+ euml: 'Latina etlitero e kun tremao',
+ igrave: 'Latina etlitero i kun liva korno',
+ iacute: 'Latina etlitero i kun dekstra korno',
+ icirc: 'Latina etlitero i kun ĉapelo',
+ iuml: 'Latina etlitero i kun tremao',
+ eth: 'Latina etlitero islanda edo',
+ ntilde: 'Latina etlitero n kun tildo',
+ ograve: 'Latina etlitero o kun liva korno',
+ oacute: 'Latina etlitero o kun dekstra korno',
+ ocirc: 'Latina etlitero o kun ĉapelo',
+ otilde: 'Latina etlitero o kun tildo',
+ ouml: 'Latina etlitero o kun tremao',
+ divide: 'Dividosigno',
+ oslash: 'Latina etlitero o trastrekita',
+ ugrave: 'Latina etlitero u kun liva korno',
+ uacute: 'Latina etlitero u kun dekstra korno',
+ ucirc: 'Latina etlitero u kun ĉapelo',
+ uuml: 'Latina etlitero u kun tremao',
+ yacute: 'Latina etlitero y kun dekstra korno',
+ thorn: 'Latina etlitero islanda dorno',
+ yuml: 'Latina etlitero y kun tremao',
+ OElig: 'Latina ĉeflitera ligaturo Œ',
+ oelig: 'Latina etlitera ligaturo œ',
+ '372': 'Latina ĉeflitero W kun ĉapelo',
+ '374': 'Latina ĉeflitero Y kun ĉapelo',
+ '373': 'Latina etlitero w kun ĉapelo',
+ '375': 'Latina etlitero y kun ĉapelo',
+ sbquo: 'Suba 9-citilo',
+ '8219': 'Supra renversita 9-citilo',
+ bdquo: 'Suba 99-citilo',
+ hellip: 'Tripunkto',
+ trade: 'Varmarka signo',
+ '9658': 'Nigra sago dekstren',
+ bull: 'Bulmarko',
+ rarr: 'Sago dekstren',
+ rArr: 'Duobla sago dekstren',
+ hArr: 'Duobla sago maldekstren',
+ diams: 'Nigra kvadrato',
+ asymp: 'Preskaŭ egala'
+});
diff --git a/_source/plugins/specialchar/lang/et.js b/_source/plugins/specialchar/lang/et.js new file mode 100644 index 0000000..833852f --- /dev/null +++ b/_source/plugins/specialchar/lang/et.js @@ -0,0 +1,126 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'specialchar', 'et',
+{
+ euro: 'Euromärk',
+ lsquo: 'Alustav ühekordne jutumärk',
+ rsquo: 'Lõpetav ühekordne jutumärk',
+ ldquo: 'Alustav kahekordne jutumärk',
+ rdquo: 'Lõpetav kahekordne jutumärk',
+ ndash: 'Enn-kriips',
+ mdash: 'Emm-kriips',
+ iexcl: 'Pööratud hüüumärk',
+ cent: 'Sendimärk',
+ pound: 'Naela märk',
+ curren: 'Valuutamärk',
+ yen: 'Jeeni märk',
+ brvbar: 'Katkestatud kriips',
+ sect: 'Lõigu märk',
+ uml: 'Täpid',
+ copy: 'Autoriõiguse märk',
+ ordf: 'Feminine ordinal indicator', // MISSING
+ laquo: 'Left-pointing double angle quotation mark', // MISSING
+ not: 'Ei-märk',
+ reg: 'Registered sign', // MISSING
+ macr: 'Macron', // MISSING
+ deg: 'Kraadimärk',
+ sup2: 'Ülaindeks kaks',
+ sup3: 'Ülaindeks kolm',
+ acute: 'Acute accent', // MISSING
+ micro: 'Mikro-märk',
+ para: 'Pilcrow sign', // MISSING
+ middot: 'Keskpunkt',
+ cedil: 'Cedilla', // MISSING
+ sup1: 'Ülaindeks üks',
+ ordm: 'Masculine ordinal indicator', // MISSING
+ raquo: 'Right-pointing double angle quotation mark', // MISSING
+ frac14: 'Vulgar fraction one quarter', // MISSING
+ frac12: 'Vulgar fraction one half', // MISSING
+ frac34: 'Vulgar fraction three quarters', // MISSING
+ iquest: 'Inverted question mark', // MISSING
+ Agrave: 'Latin capital letter A with grave accent', // MISSING
+ Aacute: 'Latin capital letter A with acute accent', // MISSING
+ Acirc: 'Latin capital letter A with circumflex', // MISSING
+ Atilde: 'Ladina suur A tildega',
+ Auml: 'Latin capital letter A with diaeresis', // MISSING
+ Aring: 'Latin capital letter A with ring above', // MISSING
+ AElig: 'Latin Capital letter Æ', // MISSING
+ Ccedil: 'Latin capital letter C with cedilla', // MISSING
+ Egrave: 'Latin capital letter E with grave accent', // MISSING
+ Eacute: 'Latin capital letter E with acute accent', // MISSING
+ Ecirc: 'Latin capital letter E with circumflex', // MISSING
+ Euml: 'Latin capital letter E with diaeresis', // MISSING
+ Igrave: 'Latin capital letter I with grave accent', // MISSING
+ Iacute: 'Latin capital letter I with acute accent', // MISSING
+ Icirc: 'Latin capital letter I with circumflex', // MISSING
+ Iuml: 'Latin capital letter I with diaeresis', // MISSING
+ ETH: 'Latin capital letter Eth', // MISSING
+ Ntilde: 'Latin capital letter N with tilde', // MISSING
+ Ograve: 'Latin capital letter O with grave accent', // MISSING
+ Oacute: 'Latin capital letter O with acute accent', // MISSING
+ Ocirc: 'Latin capital letter O with circumflex', // MISSING
+ Otilde: 'Latin capital letter O with tilde', // MISSING
+ Ouml: 'Täppidega ladina suur O',
+ times: 'Multiplication sign', // MISSING
+ Oslash: 'Latin capital letter O with stroke', // MISSING
+ Ugrave: 'Latin capital letter U with grave accent', // MISSING
+ Uacute: 'Latin capital letter U with acute accent', // MISSING
+ Ucirc: 'Kandilise katusega suur ladina U',
+ Uuml: 'Täppidega ladina suur U',
+ Yacute: 'Latin capital letter Y with acute accent', // MISSING
+ THORN: 'Latin capital letter Thorn', // MISSING
+ szlig: 'Ladina väike terav s',
+ agrave: 'Latin small letter a with grave accent', // MISSING
+ aacute: 'Latin small letter a with acute accent', // MISSING
+ acirc: 'Kandilise katusega ladina väike a',
+ atilde: 'Tildega ladina väike a',
+ auml: 'Täppidega ladina väike a',
+ aring: 'Latin small letter a with ring above', // MISSING
+ aelig: 'Latin small letter æ', // MISSING
+ ccedil: 'Latin small letter c with cedilla', // MISSING
+ egrave: 'Latin small letter e with grave accent', // MISSING
+ eacute: 'Latin small letter e with acute accent', // MISSING
+ ecirc: 'Latin small letter e with circumflex', // MISSING
+ euml: 'Latin small letter e with diaeresis', // MISSING
+ igrave: 'Latin small letter i with grave accent', // MISSING
+ iacute: 'Latin small letter i with acute accent', // MISSING
+ icirc: 'Latin small letter i with circumflex', // MISSING
+ iuml: 'Latin small letter i with diaeresis', // MISSING
+ eth: 'Latin small letter eth', // MISSING
+ ntilde: 'Latin small letter n with tilde', // MISSING
+ ograve: 'Latin small letter o with grave accent', // MISSING
+ oacute: 'Latin small letter o with acute accent', // MISSING
+ ocirc: 'Latin small letter o with circumflex', // MISSING
+ otilde: 'Latin small letter o with tilde', // MISSING
+ ouml: 'Latin small letter o with diaeresis', // MISSING
+ divide: 'Division sign', // MISSING
+ oslash: 'Latin small letter o with stroke', // MISSING
+ ugrave: 'Latin small letter u with grave accent', // MISSING
+ uacute: 'Latin small letter u with acute accent', // MISSING
+ ucirc: 'Latin small letter u with circumflex', // MISSING
+ uuml: 'Latin small letter u with diaeresis', // MISSING
+ yacute: 'Latin small letter y with acute accent', // MISSING
+ thorn: 'Latin small letter thorn', // MISSING
+ yuml: 'Latin small letter y with diaeresis', // MISSING
+ OElig: 'Latin capital ligature OE', // MISSING
+ oelig: 'Latin small ligature oe', // MISSING
+ '372': 'Latin capital letter W with circumflex', // MISSING
+ '374': 'Latin capital letter Y with circumflex', // MISSING
+ '373': 'Latin small letter w with circumflex', // MISSING
+ '375': 'Latin small letter y with circumflex', // MISSING
+ sbquo: 'Single low-9 quotation mark', // MISSING
+ '8219': 'Single high-reversed-9 quotation mark', // MISSING
+ bdquo: 'Double low-9 quotation mark', // MISSING
+ hellip: 'Horizontal ellipsis', // MISSING
+ trade: 'Trade mark sign', // MISSING
+ '9658': 'Black right-pointing pointer', // MISSING
+ bull: 'Bullet', // MISSING
+ rarr: 'Rightwards arrow', // MISSING
+ rArr: 'Rightwards double arrow', // MISSING
+ hArr: 'Left right double arrow', // MISSING
+ diams: 'Black diamond suit', // MISSING
+ asymp: 'Almost equal to' // MISSING
+});
diff --git a/_source/plugins/specialchar/lang/fa.js b/_source/plugins/specialchar/lang/fa.js new file mode 100644 index 0000000..a97a23e --- /dev/null +++ b/_source/plugins/specialchar/lang/fa.js @@ -0,0 +1,126 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'specialchar', 'fa',
+{
+ euro: 'نشان یورو',
+ lsquo: 'علامت نقل قول تکی چپ',
+ rsquo: 'علامت نقل قول تکی راست',
+ ldquo: 'علامت دوتایی نقل قول چپ',
+ rdquo: 'علامت دوتایی نقل قول راست',
+ ndash: 'En dash', // MISSING
+ mdash: 'Em dash', // MISSING
+ iexcl: 'علامت گذاری به عنوان علامت تعجب وارونه',
+ cent: 'نشان سنت',
+ pound: 'نشان پوند',
+ curren: 'نشان ارز',
+ yen: 'نشان ین',
+ brvbar: 'نوار شکسته',
+ sect: 'نشان بخش',
+ uml: 'Diaeresis', // MISSING
+ copy: 'نشان کپی رایت',
+ ordf: 'Feminine ordinal indicator', // MISSING
+ laquo: 'Left-pointing double angle quotation mark', // MISSING
+ not: 'علامت ثبت نشده',
+ reg: 'علامت ثبت شده',
+ macr: 'Macron', // MISSING
+ deg: 'نشان درجه',
+ sup2: 'بالانویس دو',
+ sup3: 'بالانویس سه',
+ acute: 'لهجه غلیظ',
+ micro: 'نشان مایکرو',
+ para: 'Pilcrow sign', // MISSING
+ middot: 'نقطه میانی',
+ cedil: 'Cedilla', // MISSING
+ sup1: 'Superscript one', // MISSING
+ ordm: 'Masculine ordinal indicator', // MISSING
+ raquo: 'نشان زاویهدار دوتایی نقل قول راست چین',
+ frac14: 'Vulgar fraction one quarter', // MISSING
+ frac12: 'Vulgar fraction one half', // MISSING
+ frac34: 'Vulgar fraction three quarters', // MISSING
+ iquest: 'Inverted question mark', // MISSING
+ Agrave: 'Latin capital letter A with grave accent', // MISSING
+ Aacute: 'Latin capital letter A with acute accent', // MISSING
+ Acirc: 'Latin capital letter A with circumflex', // MISSING
+ Atilde: 'Latin capital letter A with tilde', // MISSING
+ Auml: 'Latin capital letter A with diaeresis', // MISSING
+ Aring: 'Latin capital letter A with ring above', // MISSING
+ AElig: 'Latin Capital letter Æ', // MISSING
+ Ccedil: 'Latin capital letter C with cedilla', // MISSING
+ Egrave: 'Latin capital letter E with grave accent', // MISSING
+ Eacute: 'Latin capital letter E with acute accent', // MISSING
+ Ecirc: 'Latin capital letter E with circumflex', // MISSING
+ Euml: 'Latin capital letter E with diaeresis', // MISSING
+ Igrave: 'Latin capital letter I with grave accent', // MISSING
+ Iacute: 'Latin capital letter I with acute accent', // MISSING
+ Icirc: 'Latin capital letter I with circumflex', // MISSING
+ Iuml: 'Latin capital letter I with diaeresis', // MISSING
+ ETH: 'Latin capital letter Eth', // MISSING
+ Ntilde: 'Latin capital letter N with tilde', // MISSING
+ Ograve: 'Latin capital letter O with grave accent', // MISSING
+ Oacute: 'Latin capital letter O with acute accent', // MISSING
+ Ocirc: 'Latin capital letter O with circumflex', // MISSING
+ Otilde: 'Latin capital letter O with tilde', // MISSING
+ Ouml: 'Latin capital letter O with diaeresis', // MISSING
+ times: 'Multiplication sign', // MISSING
+ Oslash: 'Latin capital letter O with stroke', // MISSING
+ Ugrave: 'Latin capital letter U with grave accent', // MISSING
+ Uacute: 'Latin capital letter U with acute accent', // MISSING
+ Ucirc: 'Latin capital letter U with circumflex', // MISSING
+ Uuml: 'Latin capital letter U with diaeresis', // MISSING
+ Yacute: 'Latin capital letter Y with acute accent', // MISSING
+ THORN: 'Latin capital letter Thorn', // MISSING
+ szlig: 'Latin small letter sharp s', // MISSING
+ agrave: 'Latin small letter a with grave accent', // MISSING
+ aacute: 'Latin small letter a with acute accent', // MISSING
+ acirc: 'Latin small letter a with circumflex', // MISSING
+ atilde: 'Latin small letter a with tilde', // MISSING
+ auml: 'Latin small letter a with diaeresis', // MISSING
+ aring: 'Latin small letter a with ring above', // MISSING
+ aelig: 'Latin small letter æ', // MISSING
+ ccedil: 'Latin small letter c with cedilla', // MISSING
+ egrave: 'Latin small letter e with grave accent', // MISSING
+ eacute: 'Latin small letter e with acute accent', // MISSING
+ ecirc: 'Latin small letter e with circumflex', // MISSING
+ euml: 'Latin small letter e with diaeresis', // MISSING
+ igrave: 'Latin small letter i with grave accent', // MISSING
+ iacute: 'Latin small letter i with acute accent', // MISSING
+ icirc: 'Latin small letter i with circumflex', // MISSING
+ iuml: 'Latin small letter i with diaeresis', // MISSING
+ eth: 'Latin small letter eth', // MISSING
+ ntilde: 'Latin small letter n with tilde', // MISSING
+ ograve: 'Latin small letter o with grave accent', // MISSING
+ oacute: 'Latin small letter o with acute accent', // MISSING
+ ocirc: 'Latin small letter o with circumflex', // MISSING
+ otilde: 'Latin small letter o with tilde', // MISSING
+ ouml: 'Latin small letter o with diaeresis', // MISSING
+ divide: 'Division sign', // MISSING
+ oslash: 'Latin small letter o with stroke', // MISSING
+ ugrave: 'Latin small letter u with grave accent', // MISSING
+ uacute: 'Latin small letter u with acute accent', // MISSING
+ ucirc: 'Latin small letter u with circumflex', // MISSING
+ uuml: 'Latin small letter u with diaeresis', // MISSING
+ yacute: 'Latin small letter y with acute accent', // MISSING
+ thorn: 'Latin small letter thorn', // MISSING
+ yuml: 'Latin small letter y with diaeresis', // MISSING
+ OElig: 'Latin capital ligature OE', // MISSING
+ oelig: 'Latin small ligature oe', // MISSING
+ '372': 'Latin capital letter W with circumflex', // MISSING
+ '374': 'Latin capital letter Y with circumflex', // MISSING
+ '373': 'Latin small letter w with circumflex', // MISSING
+ '375': 'Latin small letter y with circumflex', // MISSING
+ sbquo: 'Single low-9 quotation mark', // MISSING
+ '8219': 'Single high-reversed-9 quotation mark', // MISSING
+ bdquo: 'Double low-9 quotation mark', // MISSING
+ hellip: 'Horizontal ellipsis', // MISSING
+ trade: 'Trade mark sign', // MISSING
+ '9658': 'Black right-pointing pointer', // MISSING
+ bull: 'Bullet', // MISSING
+ rarr: 'Rightwards arrow', // MISSING
+ rArr: 'Rightwards double arrow', // MISSING
+ hArr: 'جهتنمای دوتایی چپ به راست',
+ diams: 'Black diamond suit', // MISSING
+ asymp: 'تقریبا برابر با'
+});
diff --git a/_source/plugins/specialchar/lang/fi.js b/_source/plugins/specialchar/lang/fi.js new file mode 100644 index 0000000..cbf23a7 --- /dev/null +++ b/_source/plugins/specialchar/lang/fi.js @@ -0,0 +1,126 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'specialchar', 'fi',
+{
+ euro: 'Euron merkki',
+ lsquo: 'Vasen yksittäinen lainausmerkki',
+ rsquo: 'Oikea yksittäinen lainausmerkki',
+ ldquo: 'Vasen kaksoislainausmerkki',
+ rdquo: 'Oikea kaksoislainausmerkki',
+ ndash: 'En dash', // MISSING
+ mdash: 'Em dash', // MISSING
+ iexcl: 'Inverted exclamation mark', // MISSING
+ cent: 'Sentin merkki',
+ pound: 'Punnan merkki',
+ curren: 'Valuuttamerkki',
+ yen: 'Yenin merkki',
+ brvbar: 'Broken bar', // MISSING
+ sect: 'Section sign', // MISSING
+ uml: 'Diaeresis', // MISSING
+ copy: 'Copyright sign', // MISSING
+ ordf: 'Feminine ordinal indicator', // MISSING
+ laquo: 'Left-pointing double angle quotation mark', // MISSING
+ not: 'Not sign', // MISSING
+ reg: 'Rekisteröity merkki',
+ macr: 'Macron', // MISSING
+ deg: 'Asteen merkki',
+ sup2: 'Yläindeksi kaksi',
+ sup3: 'Yläindeksi kolme',
+ acute: 'Acute accent', // MISSING
+ micro: 'Mikron merkki',
+ para: 'Pilcrow sign', // MISSING
+ middot: 'Middle dot', // MISSING
+ cedil: 'Cedilla', // MISSING
+ sup1: 'Yläindeksi yksi',
+ ordm: 'Masculine ordinal indicator', // MISSING
+ raquo: 'Right-pointing double angle quotation mark', // MISSING
+ frac14: 'Vulgar fraction one quarter', // MISSING
+ frac12: 'Vulgar fraction one half', // MISSING
+ frac34: 'Vulgar fraction three quarters', // MISSING
+ iquest: 'Ylösalaisin oleva kysymysmerkki',
+ Agrave: 'Latin capital letter A with grave accent', // MISSING
+ Aacute: 'Latin capital letter A with acute accent', // MISSING
+ Acirc: 'Latin capital letter A with circumflex', // MISSING
+ Atilde: 'Latin capital letter A with tilde', // MISSING
+ Auml: 'Latin capital letter A with diaeresis', // MISSING
+ Aring: 'Latin capital letter A with ring above', // MISSING
+ AElig: 'Latin Capital letter Æ', // MISSING
+ Ccedil: 'Latin capital letter C with cedilla', // MISSING
+ Egrave: 'Latin capital letter E with grave accent', // MISSING
+ Eacute: 'Latin capital letter E with acute accent', // MISSING
+ Ecirc: 'Latin capital letter E with circumflex', // MISSING
+ Euml: 'Latin capital letter E with diaeresis', // MISSING
+ Igrave: 'Latin capital letter I with grave accent', // MISSING
+ Iacute: 'Latin capital letter I with acute accent', // MISSING
+ Icirc: 'Latin capital letter I with circumflex', // MISSING
+ Iuml: 'Latin capital letter I with diaeresis', // MISSING
+ ETH: 'Latin capital letter Eth', // MISSING
+ Ntilde: 'Latin capital letter N with tilde', // MISSING
+ Ograve: 'Latin capital letter O with grave accent', // MISSING
+ Oacute: 'Latin capital letter O with acute accent', // MISSING
+ Ocirc: 'Latin capital letter O with circumflex', // MISSING
+ Otilde: 'Latin capital letter O with tilde', // MISSING
+ Ouml: 'Latin capital letter O with diaeresis', // MISSING
+ times: 'Kertomerkki',
+ Oslash: 'Latin capital letter O with stroke', // MISSING
+ Ugrave: 'Latin capital letter U with grave accent', // MISSING
+ Uacute: 'Latin capital letter U with acute accent', // MISSING
+ Ucirc: 'Latin capital letter U with circumflex', // MISSING
+ Uuml: 'Latin capital letter U with diaeresis', // MISSING
+ Yacute: 'Latin capital letter Y with acute accent', // MISSING
+ THORN: 'Latin capital letter Thorn', // MISSING
+ szlig: 'Latin small letter sharp s', // MISSING
+ agrave: 'Latin small letter a with grave accent', // MISSING
+ aacute: 'Latin small letter a with acute accent', // MISSING
+ acirc: 'Latin small letter a with circumflex', // MISSING
+ atilde: 'Latin small letter a with tilde', // MISSING
+ auml: 'Latin small letter a with diaeresis', // MISSING
+ aring: 'Latin small letter a with ring above', // MISSING
+ aelig: 'Latin small letter æ', // MISSING
+ ccedil: 'Latin small letter c with cedilla', // MISSING
+ egrave: 'Latin small letter e with grave accent', // MISSING
+ eacute: 'Latin small letter e with acute accent', // MISSING
+ ecirc: 'Latin small letter e with circumflex', // MISSING
+ euml: 'Latin small letter e with diaeresis', // MISSING
+ igrave: 'Latin small letter i with grave accent', // MISSING
+ iacute: 'Latin small letter i with acute accent', // MISSING
+ icirc: 'Latin small letter i with circumflex', // MISSING
+ iuml: 'Latin small letter i with diaeresis', // MISSING
+ eth: 'Latin small letter eth', // MISSING
+ ntilde: 'Latin small letter n with tilde', // MISSING
+ ograve: 'Latin small letter o with grave accent', // MISSING
+ oacute: 'Latin small letter o with acute accent', // MISSING
+ ocirc: 'Latin small letter o with circumflex', // MISSING
+ otilde: 'Latin small letter o with tilde', // MISSING
+ ouml: 'Latin small letter o with diaeresis', // MISSING
+ divide: 'Jakomerkki',
+ oslash: 'Latin small letter o with stroke', // MISSING
+ ugrave: 'Latin small letter u with grave accent', // MISSING
+ uacute: 'Latin small letter u with acute accent', // MISSING
+ ucirc: 'Latin small letter u with circumflex', // MISSING
+ uuml: 'Latin small letter u with diaeresis', // MISSING
+ yacute: 'Latin small letter y with acute accent', // MISSING
+ thorn: 'Latin small letter thorn', // MISSING
+ yuml: 'Latin small letter y with diaeresis', // MISSING
+ OElig: 'Latin capital ligature OE', // MISSING
+ oelig: 'Latin small ligature oe', // MISSING
+ '372': 'Latin capital letter W with circumflex', // MISSING
+ '374': 'Latin capital letter Y with circumflex', // MISSING
+ '373': 'Latin small letter w with circumflex', // MISSING
+ '375': 'Latin small letter y with circumflex', // MISSING
+ sbquo: 'Single low-9 quotation mark', // MISSING
+ '8219': 'Single high-reversed-9 quotation mark', // MISSING
+ bdquo: 'Double low-9 quotation mark', // MISSING
+ hellip: 'Horizontal ellipsis', // MISSING
+ trade: 'Tavaramerkki merkki',
+ '9658': 'Black right-pointing pointer', // MISSING
+ bull: 'Bullet', // MISSING
+ rarr: 'Nuoli oikealle',
+ rArr: 'Kaksoisnuoli oikealle',
+ hArr: 'Kaksoisnuoli oikealle ja vasemmalle',
+ diams: 'Black diamond suit', // MISSING
+ asymp: 'Noin'
+});
diff --git a/_source/plugins/specialchar/lang/fr.js b/_source/plugins/specialchar/lang/fr.js new file mode 100644 index 0000000..c22adf7 --- /dev/null +++ b/_source/plugins/specialchar/lang/fr.js @@ -0,0 +1,126 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'specialchar', 'fr',
+{
+ euro: 'Symbole Euro',
+ lsquo: 'Guillemet simple ouvrant',
+ rsquo: 'Guillemet simple fermant',
+ ldquo: 'Guillemet double ouvrant',
+ rdquo: 'Guillemet double fermant',
+ ndash: 'Tiret haut',
+ mdash: 'Tiret bas underscore',
+ iexcl: 'Point d\'exclamation inversé',
+ cent: 'Symbole Cent',
+ pound: 'Symbole Livre Sterling',
+ curren: 'Symbole monétaire',
+ yen: 'Symbole Yen',
+ brvbar: 'Barre verticale scindée',
+ sect: 'Section',
+ uml: 'Tréma',
+ copy: 'Symbole Copyright',
+ ordf: 'Indicateur ordinal féminin',
+ laquo: 'Guillemet français ouvrant',
+ not: 'Crochet de négation',
+ reg: 'Marque déposée',
+ macr: 'Macron',
+ deg: 'Degré',
+ sup2: 'Exposant 2',
+ sup3: '\\tExposant 3',
+ acute: 'Accent aigu',
+ micro: 'Omicron',
+ para: 'Paragraphe',
+ middot: 'Point médian',
+ cedil: 'Cédille',
+ sup1: '\\tExposant 1',
+ ordm: 'Indicateur ordinal masculin',
+ raquo: 'Guillemet français fermant',
+ frac14: 'Un quart',
+ frac12: 'Un demi',
+ frac34: 'Trois quarts',
+ iquest: 'Point d\'interrogation inversé',
+ Agrave: 'A majuscule accent grave',
+ Aacute: 'A majuscule accent aigu',
+ Acirc: 'A majuscule accent circonflexe',
+ Atilde: 'A majuscule avec caron',
+ Auml: 'A majuscule tréma',
+ Aring: 'A majuscule avec un rond au-dessus',
+ AElig: 'Æ majuscule ligaturés',
+ Ccedil: 'C majuscule cédille',
+ Egrave: 'E majuscule accent grave',
+ Eacute: 'E majuscule accent aigu',
+ Ecirc: 'E majuscule accent circonflexe',
+ Euml: 'E majuscule tréma',
+ Igrave: 'I majuscule accent grave',
+ Iacute: 'I majuscule accent aigu',
+ Icirc: 'I majuscule accent circonflexe',
+ Iuml: 'I majuscule tréma',
+ ETH: 'Lettre majuscule islandaise ED',
+ Ntilde: 'N majuscule avec caron',
+ Ograve: 'O majuscule accent grave',
+ Oacute: 'O majuscule accent aigu',
+ Ocirc: 'O majuscule accent circonflexe',
+ Otilde: 'O majuscule avec caron',
+ Ouml: 'O majuscule tréma',
+ times: 'Multiplication',
+ Oslash: 'O majuscule barré',
+ Ugrave: 'U majuscule accent grave',
+ Uacute: 'U majuscule accent aigu',
+ Ucirc: 'U majuscule accent circonflexe',
+ Uuml: 'U majuscule tréma',
+ Yacute: 'Y majuscule accent aigu',
+ THORN: 'Lettre islandaise Thorn majuscule',
+ szlig: 'Lettre minuscule allemande s dur',
+ agrave: 'a minuscule accent grave',
+ aacute: 'a minuscule accent aigu',
+ acirc: 'a minuscule accent circonflexe',
+ atilde: 'a minuscule avec caron',
+ auml: 'a minuscule tréma',
+ aring: 'a minuscule avec un rond au-dessus',
+ aelig: 'æ minuscule ligaturés',
+ ccedil: 'c minuscule cédille',
+ egrave: 'e minuscule accent grave',
+ eacute: 'e minuscule accent aigu',
+ ecirc: 'e minuscule accent circonflexe',
+ euml: 'e minuscule tréma',
+ igrave: 'i minuscule accent grave',
+ iacute: 'i minuscule accent aigu',
+ icirc: 'i minuscule accent circonflexe',
+ iuml: 'i minuscule tréma',
+ eth: 'Lettre minuscule islandaise ED',
+ ntilde: 'n minuscule avec caron',
+ ograve: 'o minuscule accent grave',
+ oacute: 'o minuscule accent aigu',
+ ocirc: 'o minuscule accent circonflexe',
+ otilde: 'o minuscule avec caron',
+ ouml: 'o minuscule tréma',
+ divide: 'Division',
+ oslash: 'o minuscule barré',
+ ugrave: 'u minuscule accent grave',
+ uacute: 'u minuscule accent aigu',
+ ucirc: 'u minuscule accent circonflexe',
+ uuml: 'u minuscule tréma',
+ yacute: 'y minuscule accent aigu',
+ thorn: 'Lettre islandaise thorn minuscule',
+ yuml: 'y minuscule tréma',
+ OElig: 'ligature majuscule latine Œ',
+ oelig: 'ligature minuscule latine œ',
+ '372': 'W majuscule accent circonflexe',
+ '374': 'Y majuscule accent circonflexe',
+ '373': 'w minuscule accent circonflexe',
+ '375': 'y minuscule accent circonflexe',
+ sbquo: 'Guillemet simple fermant (anglais)',
+ '8219': 'Guillemet-virgule supérieur culbuté',
+ bdquo: 'Guillemet-virgule double inférieur',
+ hellip: 'Points de suspension',
+ trade: 'Marque commerciale (trade mark)',
+ '9658': 'Flèche noire pointant vers la droite',
+ bull: 'Gros point médian',
+ rarr: 'Flèche vers la droite',
+ rArr: 'Double flèche vers la droite',
+ hArr: 'Double flèche vers la gauche',
+ diams: 'Carreau noir',
+ asymp: 'Presque égal'
+});
diff --git a/_source/plugins/specialchar/lang/hr.js b/_source/plugins/specialchar/lang/hr.js new file mode 100644 index 0000000..5457139 --- /dev/null +++ b/_source/plugins/specialchar/lang/hr.js @@ -0,0 +1,126 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'specialchar', 'hr',
+{
+ euro: 'Euro znak',
+ lsquo: 'Lijevi jednostruki navodnik',
+ rsquo: 'Desni jednostruki navodnik',
+ ldquo: 'Lijevi dvostruki navodnik',
+ rdquo: 'Desni dvostruki navodnik',
+ ndash: 'En crtica',
+ mdash: 'Em crtica',
+ iexcl: 'Naopaki uskličnik',
+ cent: 'Cent znak',
+ pound: 'Funta znak',
+ curren: 'Znak valute',
+ yen: 'Yen znak',
+ brvbar: 'Potrgana prečka',
+ sect: 'Znak odjeljka',
+ uml: 'Diaeresis', // MISSING
+ copy: 'Copyright znak',
+ ordf: 'Feminine ordinal indicator', // MISSING
+ laquo: 'Lijevi dvostruki uglati navodnik',
+ not: 'Not znak',
+ reg: 'Registered znak',
+ macr: 'Macron', // MISSING
+ deg: 'Stupanj znak',
+ sup2: 'Superscript two', // MISSING
+ sup3: 'Superscript three', // MISSING
+ acute: 'Acute accent', // MISSING
+ micro: 'Micro sign', // MISSING
+ para: 'Pilcrow sign', // MISSING
+ middot: 'Srednja točka',
+ cedil: 'Cedilla', // MISSING
+ sup1: 'Superscript one', // MISSING
+ ordm: 'Masculine ordinal indicator', // MISSING
+ raquo: 'Desni dvostruku uglati navodnik',
+ frac14: 'Vulgar fraction one quarter', // MISSING
+ frac12: 'Vulgar fraction one half', // MISSING
+ frac34: 'Vulgar fraction three quarters', // MISSING
+ iquest: 'Naopaki upitnik',
+ Agrave: 'Veliko latinsko slovo A s akcentom',
+ Aacute: 'Latin capital letter A with acute accent', // MISSING
+ Acirc: 'Latin capital letter A with circumflex', // MISSING
+ Atilde: 'Latin capital letter A with tilde', // MISSING
+ Auml: 'Latin capital letter A with diaeresis', // MISSING
+ Aring: 'Latin capital letter A with ring above', // MISSING
+ AElig: 'Latin Capital letter Æ', // MISSING
+ Ccedil: 'Latin capital letter C with cedilla', // MISSING
+ Egrave: 'Latin capital letter E with grave accent', // MISSING
+ Eacute: 'Latin capital letter E with acute accent', // MISSING
+ Ecirc: 'Latin capital letter E with circumflex', // MISSING
+ Euml: 'Latin capital letter E with diaeresis', // MISSING
+ Igrave: 'Latin capital letter I with grave accent', // MISSING
+ Iacute: 'Latin capital letter I with acute accent', // MISSING
+ Icirc: 'Latin capital letter I with circumflex', // MISSING
+ Iuml: 'Latin capital letter I with diaeresis', // MISSING
+ ETH: 'Latin capital letter Eth', // MISSING
+ Ntilde: 'Latin capital letter N with tilde', // MISSING
+ Ograve: 'Latin capital letter O with grave accent', // MISSING
+ Oacute: 'Latin capital letter O with acute accent', // MISSING
+ Ocirc: 'Latin capital letter O with circumflex', // MISSING
+ Otilde: 'Latin capital letter O with tilde', // MISSING
+ Ouml: 'Latin capital letter O with diaeresis', // MISSING
+ times: 'Multiplication sign', // MISSING
+ Oslash: 'Latin capital letter O with stroke', // MISSING
+ Ugrave: 'Latin capital letter U with grave accent', // MISSING
+ Uacute: 'Latin capital letter U with acute accent', // MISSING
+ Ucirc: 'Latin capital letter U with circumflex', // MISSING
+ Uuml: 'Latin capital letter U with diaeresis', // MISSING
+ Yacute: 'Latin capital letter Y with acute accent', // MISSING
+ THORN: 'Latin capital letter Thorn', // MISSING
+ szlig: 'Latin small letter sharp s', // MISSING
+ agrave: 'Latin small letter a with grave accent', // MISSING
+ aacute: 'Latin small letter a with acute accent', // MISSING
+ acirc: 'Latin small letter a with circumflex', // MISSING
+ atilde: 'Latin small letter a with tilde', // MISSING
+ auml: 'Latin small letter a with diaeresis', // MISSING
+ aring: 'Latin small letter a with ring above', // MISSING
+ aelig: 'Latin small letter æ', // MISSING
+ ccedil: 'Latin small letter c with cedilla', // MISSING
+ egrave: 'Latin small letter e with grave accent', // MISSING
+ eacute: 'Latin small letter e with acute accent', // MISSING
+ ecirc: 'Latin small letter e with circumflex', // MISSING
+ euml: 'Latin small letter e with diaeresis', // MISSING
+ igrave: 'Latin small letter i with grave accent', // MISSING
+ iacute: 'Latin small letter i with acute accent', // MISSING
+ icirc: 'Latin small letter i with circumflex', // MISSING
+ iuml: 'Latin small letter i with diaeresis', // MISSING
+ eth: 'Latin small letter eth', // MISSING
+ ntilde: 'Latin small letter n with tilde', // MISSING
+ ograve: 'Latin small letter o with grave accent', // MISSING
+ oacute: 'Latin small letter o with acute accent', // MISSING
+ ocirc: 'Latin small letter o with circumflex', // MISSING
+ otilde: 'Latin small letter o with tilde', // MISSING
+ ouml: 'Latin small letter o with diaeresis', // MISSING
+ divide: 'Division sign', // MISSING
+ oslash: 'Latin small letter o with stroke', // MISSING
+ ugrave: 'Latin small letter u with grave accent', // MISSING
+ uacute: 'Latin small letter u with acute accent', // MISSING
+ ucirc: 'Latin small letter u with circumflex', // MISSING
+ uuml: 'Latin small letter u with diaeresis', // MISSING
+ yacute: 'Latin small letter y with acute accent', // MISSING
+ thorn: 'Latin small letter thorn', // MISSING
+ yuml: 'Latin small letter y with diaeresis', // MISSING
+ OElig: 'Latin capital ligature OE', // MISSING
+ oelig: 'Latin small ligature oe', // MISSING
+ '372': 'Latin capital letter W with circumflex', // MISSING
+ '374': 'Latin capital letter Y with circumflex', // MISSING
+ '373': 'Latin small letter w with circumflex', // MISSING
+ '375': 'Latin small letter y with circumflex', // MISSING
+ sbquo: 'Single low-9 quotation mark', // MISSING
+ '8219': 'Single high-reversed-9 quotation mark', // MISSING
+ bdquo: 'Double low-9 quotation mark', // MISSING
+ hellip: 'Horizontal ellipsis', // MISSING
+ trade: 'Trade mark sign', // MISSING
+ '9658': 'Black right-pointing pointer', // MISSING
+ bull: 'Bullet', // MISSING
+ rarr: 'Rightwards arrow', // MISSING
+ rArr: 'Rightwards double arrow', // MISSING
+ hArr: 'Left right double arrow', // MISSING
+ diams: 'Black diamond suit', // MISSING
+ asymp: 'Almost equal to' // MISSING
+});
diff --git a/_source/plugins/specialchar/lang/it.js b/_source/plugins/specialchar/lang/it.js new file mode 100644 index 0000000..3ac66e2 --- /dev/null +++ b/_source/plugins/specialchar/lang/it.js @@ -0,0 +1,126 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'specialchar', 'it',
+{
+ euro: 'Simbolo Euro',
+ lsquo: 'Virgoletta singola sinistra',
+ rsquo: 'Virgoletta singola destra',
+ ldquo: 'Virgolette aperte',
+ rdquo: 'Virgolette chiuse',
+ ndash: 'Trattino',
+ mdash: 'Trattino lungo',
+ iexcl: 'Punto esclavamativo invertito',
+ cent: 'Simbolo Cent',
+ pound: 'Simbolo Sterlina',
+ curren: 'Simbolo Moneta',
+ yen: 'Simbolo Yen',
+ brvbar: 'Barra interrotta',
+ sect: 'Simbolo di sezione',
+ uml: 'Dieresi',
+ copy: 'Simbolo Copyright',
+ ordf: 'Indicatore ordinale femminile',
+ laquo: 'Virgolette basse aperte',
+ not: 'Nessun segno',
+ reg: 'Simbolo Registrato',
+ macr: 'Macron',
+ deg: 'Simbolo Grado',
+ sup2: 'Apice Due',
+ sup3: 'Apice Tre',
+ acute: 'Accento acuto',
+ micro: 'Simbolo Micro',
+ para: 'Simbolo Paragrafo',
+ middot: 'Punto centrale',
+ cedil: 'Cediglia',
+ sup1: 'Apice Uno',
+ ordm: 'Indicatore ordinale maschile',
+ raquo: 'Virgolette basse chiuse',
+ frac14: 'Frazione volgare un quarto',
+ frac12: 'Frazione volgare un mezzo',
+ frac34: 'Frazione volgare tre quarti',
+ iquest: 'Punto interrogativo invertito',
+ Agrave: 'Lettera maiuscola latina A con accento grave',
+ Aacute: 'Lettera maiuscola latina A con accento acuto',
+ Acirc: 'Lettera maiuscola latina A con accento circonflesso',
+ Atilde: 'Lettera maiuscola latina A con tilde',
+ Auml: 'Lettera maiuscola latina A con dieresi',
+ Aring: 'Lettera maiuscola latina A con anello sopra',
+ AElig: 'Lettera maiuscola latina AE',
+ Ccedil: 'Lettera maiuscola latina C con cediglia',
+ Egrave: 'Lettera maiuscola latina E con accento grave',
+ Eacute: 'Lettera maiuscola latina E con accento acuto',
+ Ecirc: 'Lettera maiuscola latina E con accento circonflesso',
+ Euml: 'Lettera maiuscola latina E con dieresi',
+ Igrave: 'Lettera maiuscola latina I con accento grave',
+ Iacute: 'Lettera maiuscola latina I con accento acuto',
+ Icirc: 'Lettera maiuscola latina I con accento circonflesso',
+ Iuml: 'Lettera maiuscola latina I con dieresi',
+ ETH: 'Lettera maiuscola latina Eth',
+ Ntilde: 'Lettera maiuscola latina N con tilde',
+ Ograve: 'Lettera maiuscola latina O con accento grave',
+ Oacute: 'Lettera maiuscola latina O con accento acuto',
+ Ocirc: 'Lettera maiuscola latina O con accento circonflesso',
+ Otilde: 'Lettera maiuscola latina O con tilde',
+ Ouml: 'Lettera maiuscola latina O con dieresi',
+ times: 'Simbolo di moltiplicazione',
+ Oslash: 'Lettera maiuscola latina O barrata',
+ Ugrave: 'Lettera maiuscola latina U con accento grave',
+ Uacute: 'Lettera maiuscola latina U con accento acuto',
+ Ucirc: 'Lettera maiuscola latina U con accento circonflesso',
+ Uuml: 'Lettera maiuscola latina U con accento circonflesso',
+ Yacute: 'Lettera maiuscola latina Y con accento acuto',
+ THORN: 'Lettera maiuscola latina Thorn',
+ szlig: 'Lettera latina minuscola doppia S',
+ agrave: 'Lettera minuscola latina a con accento grave',
+ aacute: 'Lettera minuscola latina a con accento acuto',
+ acirc: 'Lettera minuscola latina a con accento circonflesso',
+ atilde: 'Lettera minuscola latina a con tilde',
+ auml: 'Lettera minuscola latina a con dieresi',
+ aring: 'Lettera minuscola latina a con anello superiore',
+ aelig: 'Lettera minuscola latina ae',
+ ccedil: 'Lettera minuscola latina c con cediglia',
+ egrave: 'Lettera minuscola latina e con accento grave',
+ eacute: 'Lettera minuscola latina e con accento acuto',
+ ecirc: 'Lettera minuscola latina e con accento circonflesso',
+ euml: 'Lettera minuscola latina e con dieresi',
+ igrave: 'Lettera minuscola latina i con accento grave',
+ iacute: 'Lettera minuscola latina i con accento acuto',
+ icirc: 'Lettera minuscola latina i con accento circonflesso',
+ iuml: 'Lettera minuscola latina i con dieresi',
+ eth: 'Lettera minuscola latina eth',
+ ntilde: 'Lettera minuscola latina n con tilde',
+ ograve: 'Lettera minuscola latina o con accento grave',
+ oacute: 'Lettera minuscola latina o con accento acuto',
+ ocirc: 'Lettera minuscola latina o con accento circonflesso',
+ otilde: 'Lettera minuscola latina o con tilde',
+ ouml: 'Lettera minuscola latina o con dieresi',
+ divide: 'Simbolo di divisione',
+ oslash: 'Lettera minuscola latina o barrata',
+ ugrave: 'Lettera minuscola latina u con accento grave',
+ uacute: 'Lettera minuscola latina u con accento acuto',
+ ucirc: 'Lettera minuscola latina u con accento circonflesso',
+ uuml: 'Lettera minuscola latina u con dieresi',
+ yacute: 'Lettera minuscola latina y con accento acuto',
+ thorn: 'Lettera minuscola latina thorn',
+ yuml: 'Lettera minuscola latina y con dieresi',
+ OElig: 'Legatura maiuscola latina OE',
+ oelig: 'Legatura minuscola latina oe',
+ '372': 'Lettera maiuscola latina W con accento circonflesso',
+ '374': 'Lettera maiuscola latina Y con accento circonflesso',
+ '373': 'Lettera minuscola latina w con accento circonflesso',
+ '375': 'Lettera minuscola latina y con accento circonflesso',
+ sbquo: 'Singola virgoletta bassa low-9',
+ '8219': 'Singola virgoletta bassa low-9 inversa',
+ bdquo: 'Doppia virgoletta bassa low-9',
+ hellip: 'Ellissi orizzontale',
+ trade: 'Simbolo TM',
+ '9658': 'Puntatore nero rivolto verso destra',
+ bull: 'Punto',
+ rarr: 'Freccia verso destra',
+ rArr: 'Doppia freccia verso destra',
+ hArr: 'Doppia freccia sinistra destra',
+ diams: 'Simbolo nero diamante',
+ asymp: 'Quasi uguale a'
+});
diff --git a/_source/plugins/specialchar/lang/nb.js b/_source/plugins/specialchar/lang/nb.js new file mode 100644 index 0000000..6690663 --- /dev/null +++ b/_source/plugins/specialchar/lang/nb.js @@ -0,0 +1,126 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'specialchar', 'nb',
+{
+ euro: 'Eurosymbol',
+ lsquo: 'Venstre enkelt anførselstegn',
+ rsquo: 'Høyre enkelt anførselstegn',
+ ldquo: 'Venstre dobbelt anførselstegn',
+ rdquo: 'Høyre anførsesltegn',
+ ndash: 'Kort tankestrek',
+ mdash: 'Lang tankestrek',
+ iexcl: 'Omvendt utropstegn',
+ cent: 'Centsymbol',
+ pound: 'Pundsymbol',
+ curren: 'Valutategn',
+ yen: 'Yensymbol',
+ brvbar: 'Brutt loddrett strek',
+ sect: 'Paragraftegn',
+ uml: 'Tøddel',
+ copy: 'Copyrighttegn',
+ ordf: 'Feminin ordensindikator',
+ laquo: 'Venstre anførselstegn',
+ not: 'Negasjonstegn',
+ reg: 'Registrert varemerke-tegn',
+ macr: 'Makron',
+ deg: 'Gradsymbol',
+ sup2: 'Hevet totall',
+ sup3: 'Hevet tretall',
+ acute: 'Akutt aksent',
+ micro: 'Mikrosymbol',
+ para: 'Avsnittstegn',
+ middot: 'Midtstilt prikk',
+ cedil: 'Cedille',
+ sup1: 'Hevet ettall',
+ ordm: 'Maskulin ordensindikator',
+ raquo: 'Høyre anførselstegn',
+ frac14: 'Fjerdedelsbrøk',
+ frac12: 'Halvbrøk',
+ frac34: 'Tre fjerdedelers brøk',
+ iquest: 'Omvendt spørsmålstegn',
+ Agrave: 'Stor A med grav aksent',
+ Aacute: 'Stor A med akutt aksent',
+ Acirc: 'Stor A med cirkumfleks',
+ Atilde: 'Stor A med tilde',
+ Auml: 'Stor A med tøddel',
+ Aring: 'Stor Å',
+ AElig: 'Stor Æ',
+ Ccedil: 'Stor C med cedille',
+ Egrave: 'Stor E med grav aksent',
+ Eacute: 'Stor E med akutt aksent',
+ Ecirc: 'Stor E med cirkumfleks',
+ Euml: 'Stor E med tøddel',
+ Igrave: 'Stor I med grav aksent',
+ Iacute: 'Stor I med akutt aksent',
+ Icirc: 'Stor I med cirkumfleks',
+ Iuml: 'Stor I med tøddel',
+ ETH: 'Stor Edd/stungen D',
+ Ntilde: 'Stor N med tilde',
+ Ograve: 'Stor O med grav aksent',
+ Oacute: 'Stor O med akutt aksent',
+ Ocirc: 'Stor O med cirkumfleks',
+ Otilde: 'Stor O med tilde',
+ Ouml: 'Stor O med tøddel',
+ times: 'Multiplikasjonstegn',
+ Oslash: 'Stor Ø',
+ Ugrave: 'Stor U med grav aksent',
+ Uacute: 'Stor U med akutt aksent',
+ Ucirc: 'Stor U med cirkumfleks',
+ Uuml: 'Stor U med tøddel',
+ Yacute: 'Stor Y med akutt aksent',
+ THORN: 'Stor Thorn',
+ szlig: 'Liten dobbelt-s/Eszett',
+ agrave: 'Liten a med grav aksent',
+ aacute: 'Liten a med akutt aksent',
+ acirc: 'Liten a med cirkumfleks',
+ atilde: 'Liten a med tilde',
+ auml: 'Liten a med tøddel',
+ aring: 'Liten å',
+ aelig: 'Liten æ',
+ ccedil: 'Liten c med cedille',
+ egrave: 'Liten e med grav aksent',
+ eacute: 'Liten e med akutt aksent',
+ ecirc: 'Liten e med cirkumfleks',
+ euml: 'Liten e med tøddel',
+ igrave: 'Liten i med grav aksent',
+ iacute: 'Liten i med akutt aksent',
+ icirc: 'Liten i med cirkumfleks',
+ iuml: 'Liten i med tøddel',
+ eth: 'Liten edd/stungen d',
+ ntilde: 'Liten n med tilde',
+ ograve: 'Liten o med grav aksent',
+ oacute: 'Liten o med akutt aksent',
+ ocirc: 'Liten o med cirkumfleks',
+ otilde: 'Liten o med tilde',
+ ouml: 'Liten o med tøddel',
+ divide: 'Divisjonstegn',
+ oslash: 'Liten ø',
+ ugrave: 'Liten u med grav aksent',
+ uacute: 'Liten u med akutt aksent',
+ ucirc: 'Liten u med cirkumfleks',
+ uuml: 'Liten u med tøddel',
+ yacute: 'Liten y med akutt aksent',
+ thorn: 'Liten thorn',
+ yuml: 'Liten y med tøddel',
+ OElig: 'Stor ligatur av O og E',
+ oelig: 'Liten ligatur av o og e',
+ '372': 'Stor W med cirkumfleks',
+ '374': 'Stor Y med cirkumfleks',
+ '373': 'Liten w med cirkumfleks',
+ '375': 'Liten y med cirkumfleks',
+ sbquo: 'Enkelt lavt 9-anførselstegn',
+ '8219': 'Enkelt høyt reversert 9-anførselstegn',
+ bdquo: 'Dobbelt lavt 9-anførselstegn',
+ hellip: 'Ellipse',
+ trade: 'Varemerkesymbol',
+ '9658': 'Svart høyrevendt peker',
+ bull: 'Tykk interpunkt',
+ rarr: 'Høyrevendt pil',
+ rArr: 'Dobbel høyrevendt pil',
+ hArr: 'Dobbel venstrevendt pil',
+ diams: 'Svart ruter',
+ asymp: 'Omtrent likhetstegn'
+});
diff --git a/_source/plugins/specialchar/lang/nl.js b/_source/plugins/specialchar/lang/nl.js new file mode 100644 index 0000000..b986328 --- /dev/null +++ b/_source/plugins/specialchar/lang/nl.js @@ -0,0 +1,126 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'specialchar', 'nl',
+{
+ euro: 'Euro-teken',
+ lsquo: 'Linker enkel aanhalingsteken',
+ rsquo: 'Rechter enkel aanhalingsteken',
+ ldquo: 'Linker dubbel aanhalingsteken',
+ rdquo: 'Rechter dubbel aanhalingsteken',
+ ndash: 'En dash',
+ mdash: 'Em dash',
+ iexcl: 'Omgekeerd uitroepteken',
+ cent: 'Cent-teken',
+ pound: 'Pond-teken',
+ curren: 'Valuta-teken',
+ yen: 'Yen-teken',
+ brvbar: 'Gebroken streep',
+ sect: 'Paragraaf-teken',
+ uml: 'Trema',
+ copy: 'Copyright-teken',
+ ordf: 'Vrouwelijk ordinaal',
+ laquo: 'Linker guillemet',
+ not: 'Ongelijk-teken',
+ reg: 'Geregistreerd handelsmerk-teken',
+ macr: 'Macron',
+ deg: 'Graden-teken',
+ sup2: 'Superscript twee',
+ sup3: 'Superscript drie',
+ acute: 'Accent aigu',
+ micro: 'Mico-teken',
+ para: 'Alinea-teken',
+ middot: 'Halfhoge punt',
+ cedil: 'Cedille',
+ sup1: 'Superscript een',
+ ordm: 'Mannelijk ordinaal',
+ raquo: 'Rechter guillemet',
+ frac14: 'Breuk kwart',
+ frac12: 'Breuk half',
+ frac34: 'Breuk driekwart',
+ iquest: 'Omgekeerd vraagteken',
+ Agrave: 'Latijnse hoofdletter A met een accent grave',
+ Aacute: 'Latijnse hoofdletter A met een accent aigu',
+ Acirc: 'Latijnse hoofdletter A met een circonflexe',
+ Atilde: 'Latijnse hoofdletter A met een tilde',
+ Auml: 'Latijnse hoofdletter A met een trema',
+ Aring: 'Latijnse hoofdletter A met een corona',
+ AElig: 'Latijnse hoofdletter Æ',
+ Ccedil: 'Latijnse hoofdletter C met een cedille',
+ Egrave: 'Latijnse hoofdletter E met een accent grave',
+ Eacute: 'Latijnse hoofdletter E met een accent aigu',
+ Ecirc: 'Latijnse hoofdletter E met een circonflexe',
+ Euml: 'Latijnse hoofdletter E met een trema',
+ Igrave: 'Latijnse hoofdletter I met een accent grave',
+ Iacute: 'Latijnse hoofdletter I met een accent aigu',
+ Icirc: 'Latijnse hoofdletter I met een circonflexe',
+ Iuml: 'Latijnse hoofdletter I met een trema',
+ ETH: 'Latijnse hoofdletter Eth',
+ Ntilde: 'Latijnse hoofdletter N met een tilde',
+ Ograve: 'Latijnse hoofdletter O met een accent grave',
+ Oacute: 'Latijnse hoofdletter O met een accent aigu',
+ Ocirc: 'Latijnse hoofdletter O met een circonflexe',
+ Otilde: 'Latijnse hoofdletter O met een tilde',
+ Ouml: 'Latijnse hoofdletter O met een trema',
+ times: 'Maal-teken',
+ Oslash: 'Latijnse hoofdletter O met een schuine streep',
+ Ugrave: 'Latijnse hoofdletter U met een accent grave',
+ Uacute: 'Latijnse hoofdletter U met een accent aigu',
+ Ucirc: 'Latijnse hoofdletter U met een circonflexe',
+ Uuml: 'Latijnse hoofdletter U met een trema',
+ Yacute: 'Latijnse hoofdletter Y met een accent aigu',
+ THORN: 'Latijnse hoofdletter Thorn',
+ szlig: 'Latijnse kleine ringel-s',
+ agrave: 'Latijnse kleine letter a met een accent grave',
+ aacute: 'Latijnse kleine letter a met een accent aigu',
+ acirc: 'Latijnse kleine letter a met een circonflexe',
+ atilde: 'Latijnse kleine letter a met een tilde',
+ auml: 'Latijnse kleine letter a met een trema',
+ aring: 'Latijnse kleine letter a met een corona',
+ aelig: 'Latijnse kleine letter æ',
+ ccedil: 'Latijnse kleine letter c met een cedille',
+ egrave: 'Latijnse kleine letter e met een accent grave',
+ eacute: 'Latijnse kleine letter e met een accent aigu',
+ ecirc: 'Latijnse kleine letter e met een circonflexe',
+ euml: 'Latijnse kleine letter e met een trema',
+ igrave: 'Latijnse kleine letter i met een accent grave',
+ iacute: 'Latijnse kleine letter i met een accent aigu',
+ icirc: 'Latijnse kleine letter i met een circonflexe',
+ iuml: 'Latijnse kleine letter i met een trema',
+ eth: 'Latijnse kleine letter eth',
+ ntilde: 'Latijnse kleine letter n met een tilde',
+ ograve: 'Latijnse kleine letter o met een accent grave',
+ oacute: 'Latijnse kleine letter o met een accent aigu',
+ ocirc: 'Latijnse kleine letter o met een circonflexe',
+ otilde: 'Latijnse kleine letter o met een tilde',
+ ouml: 'Latijnse kleine letter o met een trema',
+ divide: 'Deel-teken',
+ oslash: 'Latijnse kleine letter o met een schuine streep',
+ ugrave: 'Latijnse kleine letter u met een accent grave',
+ uacute: 'Latijnse kleine letter u met een accent aigu',
+ ucirc: 'Latijnse kleine letter u met een circonflexe',
+ uuml: 'Latijnse kleine letter u met een trema',
+ yacute: 'Latijnse kleine letter y met een accent aigu',
+ thorn: 'Latijnse kleine letter thorn',
+ yuml: 'Latijnse kleine letter y met een trema',
+ OElig: 'Latijnse hoofdletter Œ',
+ oelig: 'Latijnse kleine letter œ',
+ '372': 'Latijnse hoofdletter W met een circonflexe',
+ '374': 'Latijnse hoofdletter Y met een circonflexe',
+ '373': 'Latijnse kleine letter w met een circonflexe',
+ '375': 'Latijnse kleine letter y met een circonflexe',
+ sbquo: 'Lage enkele aanhalingsteken',
+ '8219': 'Hoge omgekeerde enkele aanhalingsteken',
+ bdquo: 'Lage dubbele aanhalingsteken',
+ hellip: 'Beletselteken',
+ trade: 'Trademark-teken',
+ '9658': 'Zwarte driehoek naar rechts',
+ bull: 'Bullet',
+ rarr: 'Pijl naar rechts',
+ rArr: 'Dubbele pijl naar rechts',
+ hArr: 'Dubbele pijl naar links',
+ diams: 'Zwart ruitje',
+ asymp: 'Benaderingsteken'
+});
diff --git a/_source/plugins/specialchar/lang/no.js b/_source/plugins/specialchar/lang/no.js new file mode 100644 index 0000000..f732e6d --- /dev/null +++ b/_source/plugins/specialchar/lang/no.js @@ -0,0 +1,126 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'specialchar', 'no',
+{
+ euro: 'Eurosymbol',
+ lsquo: 'Venstre enkelt anførselstegn',
+ rsquo: 'Høyre enkelt anførselstegn',
+ ldquo: 'Venstre dobbelt anførselstegn',
+ rdquo: 'Høyre anførsesltegn',
+ ndash: 'Kort tankestrek',
+ mdash: 'Lang tankestrek',
+ iexcl: 'Omvendt utropstegn',
+ cent: 'Centsymbol',
+ pound: 'Pundsymbol',
+ curren: 'Valutategn',
+ yen: 'Yensymbol',
+ brvbar: 'Brutt loddrett strek',
+ sect: 'Paragraftegn',
+ uml: 'Tøddel',
+ copy: 'Copyrighttegn',
+ ordf: 'Feminin ordensindikator',
+ laquo: 'Venstre anførselstegn',
+ not: 'Negasjonstegn',
+ reg: 'Registrert varemerke-tegn',
+ macr: 'Makron',
+ deg: 'Gradsymbol',
+ sup2: 'Hevet totall',
+ sup3: 'Hevet tretall',
+ acute: 'Akutt aksent',
+ micro: 'Mikrosymbol',
+ para: 'Avsnittstegn',
+ middot: 'Midtstilt prikk',
+ cedil: 'Cedille',
+ sup1: 'Hevet ettall',
+ ordm: 'Maskulin ordensindikator',
+ raquo: 'Høyre anførselstegn',
+ frac14: 'Fjerdedelsbrøk',
+ frac12: 'Halvbrøk',
+ frac34: 'Tre fjerdedelers brøk',
+ iquest: 'Omvendt spørsmålstegn',
+ Agrave: 'Stor A med grav aksent',
+ Aacute: 'Stor A med akutt aksent',
+ Acirc: 'Stor A med cirkumfleks',
+ Atilde: 'Stor A med tilde',
+ Auml: 'Stor A med tøddel',
+ Aring: 'Stor Å',
+ AElig: 'Stor Æ',
+ Ccedil: 'Stor C med cedille',
+ Egrave: 'Stor E med grav aksent',
+ Eacute: 'Stor E med akutt aksent',
+ Ecirc: 'Stor E med cirkumfleks',
+ Euml: 'Stor E med tøddel',
+ Igrave: 'Stor I med grav aksent',
+ Iacute: 'Stor I med akutt aksent',
+ Icirc: 'Stor I med cirkumfleks',
+ Iuml: 'Stor I med tøddel',
+ ETH: 'Stor Edd/stungen D',
+ Ntilde: 'Stor N med tilde',
+ Ograve: 'Stor O med grav aksent',
+ Oacute: 'Stor O med akutt aksent',
+ Ocirc: 'Stor O med cirkumfleks',
+ Otilde: 'Stor O med tilde',
+ Ouml: 'Stor O med tøddel',
+ times: 'Multiplikasjonstegn',
+ Oslash: 'Stor Ø',
+ Ugrave: 'Stor U med grav aksent',
+ Uacute: 'Stor U med akutt aksent',
+ Ucirc: 'Stor U med cirkumfleks',
+ Uuml: 'Stor U med tøddel',
+ Yacute: 'Stor Y med akutt aksent',
+ THORN: 'Stor Thorn',
+ szlig: 'Liten dobbelt-s/Eszett',
+ agrave: 'Liten a med grav aksent',
+ aacute: 'Liten a med akutt aksent',
+ acirc: 'Liten a med cirkumfleks',
+ atilde: 'Liten a med tilde',
+ auml: 'Liten a med tøddel',
+ aring: 'Liten å',
+ aelig: 'Liten æ',
+ ccedil: 'Liten c med cedille',
+ egrave: 'Liten e med grav aksent',
+ eacute: 'Liten e med akutt aksent',
+ ecirc: 'Liten e med cirkumfleks',
+ euml: 'Liten e med tøddel',
+ igrave: 'Liten i med grav aksent',
+ iacute: 'Liten i med akutt aksent',
+ icirc: 'Liten i med cirkumfleks',
+ iuml: 'Liten i med tøddel',
+ eth: 'Liten edd/stungen d',
+ ntilde: 'Liten n med tilde',
+ ograve: 'Liten o med grav aksent',
+ oacute: 'Liten o med akutt aksent',
+ ocirc: 'Liten o med cirkumfleks',
+ otilde: 'Liten o med tilde',
+ ouml: 'Liten o med tøddel',
+ divide: 'Divisjonstegn',
+ oslash: 'Liten ø',
+ ugrave: 'Liten u med grav aksent',
+ uacute: 'Liten u med akutt aksent',
+ ucirc: 'Liten u med cirkumfleks',
+ uuml: 'Liten u med tøddel',
+ yacute: 'Liten y med akutt aksent',
+ thorn: 'Liten thorn',
+ yuml: 'Liten y med tøddel',
+ OElig: 'Stor ligatur av O og E',
+ oelig: 'Liten ligatur av o og e',
+ '372': 'Stor W med cirkumfleks',
+ '374': 'Stor Y med cirkumfleks',
+ '373': 'Liten w med cirkumfleks',
+ '375': 'Liten y med cirkumfleks',
+ sbquo: 'Enkelt lavt 9-anførselstegn',
+ '8219': 'Enkelt høyt reversert 9-anførselstegn',
+ bdquo: 'Dobbelt lavt 9-anførselstegn',
+ hellip: 'Ellipse',
+ trade: 'Varemerkesymbol',
+ '9658': 'Svart høyrevendt peker',
+ bull: 'Tykk interpunkt',
+ rarr: 'Høyrevendt pil',
+ rArr: 'Dobbel høyrevendt pil',
+ hArr: 'Dobbel venstrevendt pil',
+ diams: 'Svart ruter',
+ asymp: 'Omtrent likhetstegn'
+});
diff --git a/_source/plugins/specialchar/lang/tr.js b/_source/plugins/specialchar/lang/tr.js new file mode 100644 index 0000000..b5615ec --- /dev/null +++ b/_source/plugins/specialchar/lang/tr.js @@ -0,0 +1,126 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'specialchar', 'tr',
+{
+ euro: 'Euro işareti',
+ lsquo: 'Sol tek tırnak işareti',
+ rsquo: 'Sağ tek tırnak işareti',
+ ldquo: 'Sol çift tırnak işareti',
+ rdquo: 'Sağ çift tırnak işareti',
+ ndash: 'En tire',
+ mdash: 'Em tire',
+ iexcl: 'Ters ünlem işareti',
+ cent: 'Cent işareti',
+ pound: 'Pound işareti',
+ curren: 'Para birimi işareti',
+ yen: 'Yen işareti',
+ brvbar: 'Kırık bar',
+ sect: 'Bölüm işareti',
+ uml: 'İki sesli harfin ayrılması',
+ copy: 'Telif hakkı işareti',
+ ordf: 'Dişil sıralı gösterge',
+ laquo: 'Sol-işaret çift açı tırnak işareti',
+ not: 'Not işareti',
+ reg: 'Kayıtlı işareti',
+ macr: 'Makron',
+ deg: 'Derece işareti',
+ sup2: 'İkili üstsimge',
+ sup3: 'Üçlü üstsimge',
+ acute: 'Aksan işareti',
+ micro: 'Mikro işareti',
+ para: 'Pilcrow işareti',
+ middot: 'Orta nokta',
+ cedil: 'Kedilla',
+ sup1: 'Üstsimge',
+ ordm: 'Eril sıralı gösterge',
+ raquo: 'Sağ işaret çift açı tırnak işareti',
+ frac14: 'Bayağı kesrin dörtte biri',
+ frac12: 'Bayağı kesrin bir yarım',
+ frac34: 'Bayağı kesrin dörtte üç',
+ iquest: 'Ters soru işareti',
+ Agrave: 'Aksanlı latin harfi',
+ Aacute: 'Aşırı aksanıyla Latin harfi',
+ Acirc: 'Çarpık Latin harfi',
+ Atilde: 'Tilde latin harfi',
+ Auml: 'Sesli harf ayrılımlıı latin harfi',
+ Aring: 'Halkalı latin büyük A harfi',
+ AElig: 'Latin büyük Æ harfi',
+ Ccedil: 'Latin büyük C harfi ile kedilla',
+ Egrave: 'Aksanlı latin büyük E harfi',
+ Eacute: 'Aşırı vurgulu latin büyük E harfi',
+ Ecirc: 'Çarpık latin büyük E harfi',
+ Euml: 'Sesli harf ayrılımlıı latin büyük E harfi',
+ Igrave: 'Aksanlı latin büyük I harfi',
+ Iacute: 'Aşırı aksanlı latin büyük I harfi',
+ Icirc: 'Çarpık latin büyük I harfi',
+ Iuml: 'Sesli harf ayrılımlıı latin büyük I harfi',
+ ETH: 'Latin büyük Eth harfi',
+ Ntilde: 'Tildeli latin büyük N harfi',
+ Ograve: 'Aksanlı latin büyük O harfi',
+ Oacute: 'Aşırı aksanlı latin büyük O harfi',
+ Ocirc: 'Çarpık latin büyük O harfi',
+ Otilde: 'Tildeli latin büyük O harfi',
+ Ouml: 'Sesli harf ayrılımlı latin büyük O harfi',
+ times: 'Çarpma işareti',
+ Oslash: 'Vurgulu latin büyük O harfi',
+ Ugrave: 'Aksanlı latin büyük U harfi',
+ Uacute: 'Aşırı aksanlı latin büyük U harfi',
+ Ucirc: 'Çarpık latin büyük U harfi',
+ Uuml: 'Sesli harf ayrılımlı latin büyük U harfi',
+ Yacute: 'Aşırı aksanlı latin büyük Y harfi',
+ THORN: 'Latin büyük Thorn harfi',
+ szlig: 'Latin küçük keskin s harfi',
+ agrave: 'Aksanlı latin küçük a harfi',
+ aacute: 'Aşırı aksanlı latin küçük a harfi',
+ acirc: 'Çarpık latin küçük a harfi',
+ atilde: 'Tildeli latin küçük a harfi',
+ auml: 'Sesli harf ayrılımlı latin küçük a harfi',
+ aring: 'Halkalı latin küçük a harfi',
+ aelig: 'Latin büyük æ harfi',
+ ccedil: 'Kedillalı latin küçük c harfi',
+ egrave: 'Aksanlı latin küçük e harfi',
+ eacute: 'Aşırı aksanlı latin küçük e harfi',
+ ecirc: 'Çarpık latin küçük e harfi',
+ euml: 'Sesli harf ayrılımlı latin küçük e harfi',
+ igrave: 'Aksanlı latin küçük i harfi',
+ iacute: 'Aşırı aksanlı latin küçük i harfi',
+ icirc: 'Çarpık latin küçük i harfi',
+ iuml: 'Sesli harf ayrılımlı latin küçük i harfi',
+ eth: 'Latin küçük eth harfi',
+ ntilde: 'Tildeli latin küçük n harfi',
+ ograve: 'Aksanlı latin küçük o harfi',
+ oacute: 'Aşırı aksanlı latin küçük o harfi',
+ ocirc: 'Çarpık latin küçük o harfi',
+ otilde: 'Tildeli latin küçük o harfi',
+ ouml: 'Sesli harf ayrılımlı latin küçük o harfi',
+ divide: 'Bölme işareti',
+ oslash: 'Vurgulu latin küçük o harfi',
+ ugrave: 'Aksanlı latin küçük u harfi',
+ uacute: 'Aşırı aksanlı latin küçük u harfi',
+ ucirc: 'Çarpık latin küçük u harfi',
+ uuml: 'Sesli harf ayrılımlı latin küçük u harfi',
+ yacute: 'Aşırı aksanlı latin küçük y harfi',
+ thorn: 'Latin küçük thorn harfi',
+ yuml: 'Sesli harf ayrılımlı latin küçük y harfi',
+ OElig: 'Latin büyük bağlı OE harfi',
+ oelig: 'Latin küçük bağlı oe harfi',
+ '372': 'Çarpık latin büyük W harfi',
+ '374': 'Çarpık latin büyük Y harfi',
+ '373': 'Çarpık latin küçük w harfi',
+ '375': 'Çarpık latin küçük y harfi',
+ sbquo: 'Tek düşük-9 tırnak işareti',
+ '8219': 'Tek yüksek-ters-9 tırnak işareti',
+ bdquo: 'Çift düşük-9 tırnak işareti',
+ hellip: 'Yatay elips',
+ trade: 'Marka tescili işareti',
+ '9658': 'Siyah sağ işaret işaretçisi',
+ bull: 'Koyu nokta',
+ rarr: 'Sağa doğru ok',
+ rArr: 'Sağa doğru çift ok',
+ hArr: 'Sol, sağ çift ok',
+ diams: 'Siyah elmas takımı',
+ asymp: 'Hemen hemen eşit'
+});
diff --git a/_source/plugins/specialchar/lang/ug.js b/_source/plugins/specialchar/lang/ug.js new file mode 100644 index 0000000..4d53f54 --- /dev/null +++ b/_source/plugins/specialchar/lang/ug.js @@ -0,0 +1,126 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'specialchar', 'ug',
+{
+ euro: 'ياۋرو بەلگىسى',
+ lsquo: 'يالاڭ پەش سول',
+ rsquo: 'يالاڭ پەش ئوڭ',
+ ldquo: 'قوش پەش سول',
+ rdquo: 'قوش پەش ئوڭ',
+ ndash: 'سىزىقچە',
+ mdash: 'سىزىق',
+ iexcl: 'ئۈندەش',
+ cent: 'تىيىن بەلگىسى',
+ pound: 'فوند ستېرلىڭ',
+ curren: 'پۇل بەلگىسى',
+ yen: 'ياپونىيە يىنى',
+ brvbar: 'ئۈزۈك بالداق',
+ sect: 'پاراگراف بەلگىسى',
+ uml: 'تاۋۇش ئايرىش بەلگىسى',
+ copy: 'نەشر ھوقۇقى بەلگىسى',
+ ordf: 'Feminine ordinal indicator', // MISSING
+ laquo: 'قوش تىرناق سول',
+ not: 'غەيرى بەلگە',
+ reg: 'خەتلەتكەن تاۋار ماركىسى',
+ macr: 'سوزۇش بەلگىسى',
+ deg: 'گىرادۇس بەلگىسى',
+ sup2: 'يۇقىرى ئىندېكىس 2',
+ sup3: 'يۇقىرى ئىندېكىس 3',
+ acute: 'ئۇرغۇ بەلگىسى',
+ micro: 'Micro sign', // MISSING
+ para: 'ئابزاس بەلگىسى',
+ middot: 'ئوتتۇرا چېكىت',
+ cedil: 'ئاستىغا قوشۇلىدىغان بەلگە',
+ sup1: 'يۇقىرى ئىندېكىس 1',
+ ordm: 'Masculine ordinal indicator', // MISSING
+ raquo: 'قوش تىرناق ئوڭ',
+ frac14: 'ئاددىي كەسىر تۆتتىن بىر',
+ frac12: 'ئاددىي كەسىر ئىككىدىن بىر',
+ frac34: 'ئاددىي كەسىر ئۈچتىن تۆرت',
+ iquest: 'Inverted question mark', // MISSING
+ Agrave: 'Latin capital letter A with grave accent', // MISSING
+ Aacute: 'Latin capital letter A with acute accent', // MISSING
+ Acirc: 'Latin capital letter A with circumflex', // MISSING
+ Atilde: 'Latin capital letter A with tilde', // MISSING
+ Auml: 'Latin capital letter A with diaeresis', // MISSING
+ Aring: 'Latin capital letter A with ring above', // MISSING
+ AElig: 'Latin Capital letter Æ', // MISSING
+ Ccedil: 'Latin capital letter C with cedilla', // MISSING
+ Egrave: 'Latin capital letter E with grave accent', // MISSING
+ Eacute: 'Latin capital letter E with acute accent', // MISSING
+ Ecirc: 'Latin capital letter E with circumflex', // MISSING
+ Euml: 'Latin capital letter E with diaeresis', // MISSING
+ Igrave: 'Latin capital letter I with grave accent', // MISSING
+ Iacute: 'Latin capital letter I with acute accent', // MISSING
+ Icirc: 'Latin capital letter I with circumflex', // MISSING
+ Iuml: 'Latin capital letter I with diaeresis', // MISSING
+ ETH: 'Latin capital letter Eth', // MISSING
+ Ntilde: 'Latin capital letter N with tilde', // MISSING
+ Ograve: 'قوش پەش ئوڭ',
+ Oacute: 'Latin capital letter O with acute accent', // MISSING
+ Ocirc: 'Latin capital letter O with circumflex', // MISSING
+ Otilde: 'Latin capital letter O with tilde', // MISSING
+ Ouml: 'Latin capital letter O with diaeresis', // MISSING
+ times: 'Multiplication sign', // MISSING
+ Oslash: 'Latin capital letter O with stroke', // MISSING
+ Ugrave: 'Latin capital letter U with grave accent', // MISSING
+ Uacute: 'Latin capital letter U with acute accent', // MISSING
+ Ucirc: 'Latin capital letter U with circumflex', // MISSING
+ Uuml: 'Latin capital letter U with diaeresis', // MISSING
+ Yacute: 'Latin capital letter Y with acute accent', // MISSING
+ THORN: 'Latin capital letter Thorn', // MISSING
+ szlig: 'Latin small letter sharp s', // MISSING
+ agrave: 'Latin small letter a with grave accent', // MISSING
+ aacute: 'Latin small letter a with acute accent', // MISSING
+ acirc: 'Latin small letter a with circumflex', // MISSING
+ atilde: 'Latin small letter a with tilde', // MISSING
+ auml: 'Latin small letter a with diaeresis', // MISSING
+ aring: 'Latin small letter a with ring above', // MISSING
+ aelig: 'Latin small letter æ', // MISSING
+ ccedil: 'Latin small letter c with cedilla', // MISSING
+ egrave: 'Latin small letter e with grave accent', // MISSING
+ eacute: 'Latin small letter e with acute accent', // MISSING
+ ecirc: 'Latin small letter e with circumflex', // MISSING
+ euml: 'Latin small letter e with diaeresis', // MISSING
+ igrave: 'Latin small letter i with grave accent', // MISSING
+ iacute: 'Latin small letter i with acute accent', // MISSING
+ icirc: 'Latin small letter i with circumflex', // MISSING
+ iuml: 'Latin small letter i with diaeresis', // MISSING
+ eth: 'Latin small letter eth', // MISSING
+ ntilde: 'تىك موللاق سوئال بەلگىسى',
+ ograve: 'Latin small letter o with grave accent', // MISSING
+ oacute: 'Latin small letter o with acute accent', // MISSING
+ ocirc: 'Latin small letter o with circumflex', // MISSING
+ otilde: 'Latin small letter o with tilde', // MISSING
+ ouml: 'Latin small letter o with diaeresis', // MISSING
+ divide: 'Division sign', // MISSING
+ oslash: 'Latin small letter o with stroke', // MISSING
+ ugrave: 'Latin small letter u with grave accent', // MISSING
+ uacute: 'Latin small letter u with acute accent', // MISSING
+ ucirc: 'Latin small letter u with circumflex', // MISSING
+ uuml: 'Latin small letter u with diaeresis', // MISSING
+ yacute: 'Latin small letter y with acute accent', // MISSING
+ thorn: 'Latin small letter thorn', // MISSING
+ yuml: 'Latin small letter y with diaeresis', // MISSING
+ OElig: 'Latin capital ligature OE', // MISSING
+ oelig: 'Latin small ligature oe', // MISSING
+ '372': 'Latin capital letter W with circumflex', // MISSING
+ '374': 'Latin capital letter Y with circumflex', // MISSING
+ '373': 'Latin small letter w with circumflex', // MISSING
+ '375': 'Latin small letter y with circumflex', // MISSING
+ sbquo: 'Single low-9 quotation mark', // MISSING
+ '8219': 'Single high-reversed-9 quotation mark', // MISSING
+ bdquo: 'Double low-9 quotation mark', // MISSING
+ hellip: 'Horizontal ellipsis', // MISSING
+ trade: 'Trade mark sign', // MISSING
+ '9658': 'Black right-pointing pointer', // MISSING
+ bull: 'Bullet', // MISSING
+ rarr: 'ئوڭ يا ئوق',
+ rArr: 'ئوڭ قوش سىزىق يا ئوق',
+ hArr: 'ئوڭ سول قوش سىزىق يا ئوق',
+ diams: 'ئۇيۇل غىچ',
+ asymp: 'تەخمىنەن تەڭ'
+});
diff --git a/_source/plugins/specialchar/lang/zh-cn.js b/_source/plugins/specialchar/lang/zh-cn.js new file mode 100644 index 0000000..293d461 --- /dev/null +++ b/_source/plugins/specialchar/lang/zh-cn.js @@ -0,0 +1,126 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'specialchar', 'zh-cn',
+{
+ euro: '欧元符号',
+ lsquo: '左单引号',
+ rsquo: '右单引号',
+ ldquo: '左双引号',
+ rdquo: '右双引号',
+ ndash: '短划线',
+ mdash: '破折号',
+ iexcl: '竖翻叹号',
+ cent: '分币标记',
+ pound: '英镑标记',
+ curren: '货币标记',
+ yen: '日元标记',
+ brvbar: '间断条',
+ sect: '节标记',
+ uml: '分音符',
+ copy: '版权所有标记',
+ ordf: '阴性顺序指示符',
+ laquo: '左指双尖引号',
+ not: '非标记',
+ reg: '注册标记',
+ macr: '长音符',
+ deg: '度标记',
+ sup2: '上标二',
+ sup3: '上标三',
+ acute: '锐音符',
+ micro: '微符',
+ para: '段落标记',
+ middot: '中间点',
+ cedil: '下加符',
+ sup1: '上标一',
+ ordm: '阳性顺序指示符',
+ raquo: '右指双尖引号',
+ frac14: '普通分数四分之一',
+ frac12: '普通分数二分之一',
+ frac34: '普通分数四分之三',
+ iquest: '竖翻问号',
+ Agrave: '带抑音符的拉丁文大写字母 A',
+ Aacute: '带锐音符的拉丁文大写字母 A',
+ Acirc: '带扬抑符的拉丁文大写字母 A',
+ Atilde: '带颚化符的拉丁文大写字母 A',
+ Auml: '带分音符的拉丁文大写字母 A',
+ Aring: '带上圆圈的拉丁文大写字母 A',
+ AElig: '拉丁文大写字母 Ae',
+ Ccedil: '带下加符的拉丁文大写字母 C',
+ Egrave: '带抑音符的拉丁文大写字母 E',
+ Eacute: '带锐音符的拉丁文大写字母 E',
+ Ecirc: '带扬抑符的拉丁文大写字母 E',
+ Euml: '带分音符的拉丁文大写字母 E',
+ Igrave: '带抑音符的拉丁文大写字母 I',
+ Iacute: '带锐音符的拉丁文大写字母 I',
+ Icirc: '带扬抑符的拉丁文大写字母 I',
+ Iuml: '带分音符的拉丁文大写字母 I',
+ ETH: '拉丁文大写字母 Eth',
+ Ntilde: '带颚化符的拉丁文大写字母 N',
+ Ograve: '带抑音符的拉丁文大写字母 O',
+ Oacute: '带锐音符的拉丁文大写字母 O',
+ Ocirc: '带扬抑符的拉丁文大写字母 O',
+ Otilde: '带颚化符的拉丁文大写字母 O',
+ Ouml: '带分音符的拉丁文大写字母 O',
+ times: '乘号',
+ Oslash: '带粗线的拉丁文大写字母 O',
+ Ugrave: '带抑音符的拉丁文大写字母 U',
+ Uacute: '带锐音符的拉丁文大写字母 U',
+ Ucirc: '带扬抑符的拉丁文大写字母 U',
+ Uuml: '带分音符的拉丁文大写字母 U',
+ Yacute: '带抑音符的拉丁文大写字母 Y',
+ THORN: '拉丁文大写字母 Thorn',
+ szlig: '拉丁文小写字母清音 S',
+ agrave: '带抑音符的拉丁文小写字母 A',
+ aacute: '带锐音符的拉丁文小写字母 A',
+ acirc: '带扬抑符的拉丁文小写字母 A',
+ atilde: '带颚化符的拉丁文小写字母 A',
+ auml: '带分音符的拉丁文小写字母 A',
+ aring: '带上圆圈的拉丁文小写字母 A',
+ aelig: '拉丁文小写字母 Ae',
+ ccedil: '带下加符的拉丁文小写字母 C',
+ egrave: '带抑音符的拉丁文小写字母 E',
+ eacute: '带锐音符的拉丁文小写字母 E',
+ ecirc: '带扬抑符的拉丁文小写字母 E',
+ euml: '带分音符的拉丁文小写字母 E',
+ igrave: '带抑音符的拉丁文小写字母 I',
+ iacute: '带锐音符的拉丁文小写字母 I',
+ icirc: '带扬抑符的拉丁文小写字母 I',
+ iuml: '带分音符的拉丁文小写字母 I',
+ eth: '拉丁文小写字母 Eth',
+ ntilde: '带颚化符的拉丁文小写字母 N',
+ ograve: '带抑音符的拉丁文小写字母 O',
+ oacute: '带锐音符的拉丁文小写字母 O',
+ ocirc: '带扬抑符的拉丁文小写字母 O',
+ otilde: '带颚化符的拉丁文小写字母 O',
+ ouml: '带分音符的拉丁文小写字母 O',
+ divide: '除号',
+ oslash: '带粗线的拉丁文小写字母 O',
+ ugrave: '带抑音符的拉丁文小写字母 U',
+ uacute: '带锐音符的拉丁文小写字母 U',
+ ucirc: '带扬抑符的拉丁文小写字母 U',
+ uuml: '带分音符的拉丁文小写字母 U',
+ yacute: '带抑音符的拉丁文小写字母 Y',
+ thorn: '拉丁文小写字母 Thorn',
+ yuml: '带分音符的拉丁文小写字母 Y',
+ OElig: '拉丁文大写连字 Oe',
+ oelig: '拉丁文小写连字 Oe',
+ '372': '带扬抑符的拉丁文大写字母 W',
+ '374': '带扬抑符的拉丁文大写字母 Y',
+ '373': '带扬抑符的拉丁文小写字母 W',
+ '375': '带扬抑符的拉丁文小写字母 Y',
+ sbquo: '单下 9 形引号',
+ '8219': '单高横翻 9 形引号',
+ bdquo: '双下 9 形引号',
+ hellip: '水平省略号',
+ trade: '商标标志',
+ '9658': '实心右指指针',
+ bull: '加重号',
+ rarr: '向右箭头',
+ rArr: '向右双线箭头',
+ hArr: '左右双线箭头',
+ diams: '实心方块纸牌',
+ asymp: '约等于'
+});
diff --git a/_source/plugins/specialchar/plugin.js b/_source/plugins/specialchar/plugin.js index e6b02a7..6c6fc97 100644 --- a/_source/plugins/specialchar/plugin.js +++ b/_source/plugins/specialchar/plugin.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
@@ -9,15 +9,35 @@ For licensing, see LICENSE.html or http://ckeditor.com/license CKEDITOR.plugins.add( 'specialchar',
{
+ // List of available localizations.
+ availableLangs : { cs:1, cy:1, de:1, en:1, eo:1, et:1, fa:1, fi:1, fr:1, hr:1, it:1, nb:1, nl:1, no:1, tr:1, ug:1, 'zh-cn':1 },
+
init : function( editor )
{
- var pluginName = 'specialchar';
+ var pluginName = 'specialchar',
+ plugin = this;
// Register the dialog.
CKEDITOR.dialog.add( pluginName, this.path + 'dialogs/specialchar.js' );
- // Register the command.
- editor.addCommand( pluginName, new CKEDITOR.dialogCommand( pluginName ) );
+ editor.addCommand( pluginName,
+ {
+ 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.specialChar, plugin.langEntries[ langCode ] );
+ editor.openDialog( pluginName );
+ });
+ },
+ modes : { wysiwyg:1 },
+ canUndo : false
+ });
// Register the toolbar button.
editor.ui.addButton( 'SpecialChar',
@@ -27,3 +47,24 @@ CKEDITOR.plugins.add( 'specialchar', });
}
} );
+
+/**
+ * The list of special characters visible in the Special Character dialog window.
+ * @type Array
+ * @example
+ * config.specialChars = [ '"', '’', [ '&custom;', 'Custom label' ] ];
+ * config.specialChars = config.specialChars.concat( [ '"', [ '’', 'Custom label' ] ] );
+ */
+CKEDITOR.config.specialChars =
+ [
+ '!','"','#','$','%','&',"'",'(',')','*','+','-','.','/',
+ '0','1','2','3','4','5','6','7','8','9',':',';',
+ '<','=','>','?','@',
+ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',
+ 'P','Q','R','S','T','U','V','W','X','Y','Z',
+ '[',']','^','_','`',
+ 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p',
+ 'q','r','s','t','u','v','w','x','y','z',
+ '{','|','}','~',
+ "€", "‘", "’", "“", "”", "–", "—", "¡", "¢", "£", "¤", "¥", "¦", "§", "¨", "©", "ª", "«", "¬", "®", "¯", "°", "²", "³", "´", "µ", "¶", "·", "¸", "¹", "º", "»", "¼", "½", "¾", "¿", "À", "Á", "Â", "Ã", "Ä", "Å", "Æ", "Ç", "È", "É", "Ê", "Ë", "Ì", "Í", "Î", "Ï", "Ð", "Ñ", "Ò", "Ó", "Ô", "Õ", "Ö", "×", "Ø", "Ù", "Ú", "Û", "Ü", "Ý", "Þ", "ß", "à", "á", "â", "ã", "ä", "å", "æ", "ç", "è", "é", "ê", "ë", "ì", "í", "î", "ï", "ð", "ñ", "ò", "ó", "ô", "õ", "ö", "÷", "ø", "ù", "ú", "û", "ü", "ý", "þ", "ÿ", "Œ", "œ", "Ŵ", "Ŷ", "ŵ", "ŷ", "‚", "‛", "„", "…", "™", "►", "•", "→", "⇒", "⇔", "♦", "≈"
+ ];
diff --git a/_source/plugins/styles/plugin.js b/_source/plugins/styles/plugin.js index 55e4ed3..2c16b7e 100644 --- a/_source/plugins/styles/plugin.js +++ b/_source/plugins/styles/plugin.js @@ -1,1381 +1,1706 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -CKEDITOR.plugins.add( 'styles', -{ - requires : [ 'selection' ] -}); - -/** - * Registers a function to be called whenever a style changes its state in the - * editing area. The current state is passed to the function. The possible - * states are {@link CKEDITOR.TRISTATE_ON} and {@link CKEDITOR.TRISTATE_OFF}. - * @param {CKEDITOR.style} The style to be watched. - * @param {Function} The function to be called when the style state changes. - * @example - * // Create a style object for the <b> element. - * var style = new CKEDITOR.style( { element : 'b' } ); - * var editor = CKEDITOR.instances.editor1; - * editor.attachStyleStateChange( style, function( state ) - * { - * if ( state == CKEDITOR.TRISTATE_ON ) - * alert( 'The current state for the B element is ON' ); - * else - * alert( 'The current state for the B element is OFF' ); - * }); - */ -CKEDITOR.editor.prototype.attachStyleStateChange = function( style, callback ) -{ - // Try to get the list of attached callbacks. - var styleStateChangeCallbacks = this._.styleStateChangeCallbacks; - - // If it doesn't exist, it means this is the first call. So, let's create - // all the structure to manage the style checks and the callback calls. - if ( !styleStateChangeCallbacks ) - { - // Create the callbacks array. - styleStateChangeCallbacks = this._.styleStateChangeCallbacks = []; - - // Attach to the selectionChange event, so we can check the styles at - // that point. - this.on( 'selectionChange', function( ev ) - { - // Loop throw all registered callbacks. - for ( var i = 0 ; i < styleStateChangeCallbacks.length ; i++ ) - { - var callback = styleStateChangeCallbacks[ i ]; - - // Check the current state for the style defined for that - // callback. - var currentState = callback.style.checkActive( ev.data.path ) ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF; - - // If the state changed since the last check. - if ( callback.state !== currentState ) - { - // Call the callback function, passing the current - // state to it. - callback.fn.call( this, currentState ); - - // Save the current state, so it can be compared next - // time. - callback.state !== currentState; - } - } - }); - } - - // Save the callback info, so it can be checked on the next occurence of - // selectionChange. - styleStateChangeCallbacks.push( { style : style, fn : callback } ); -}; - -CKEDITOR.STYLE_BLOCK = 1; -CKEDITOR.STYLE_INLINE = 2; -CKEDITOR.STYLE_OBJECT = 3; - -(function() -{ - var blockElements = { address:1,div:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,p:1,pre:1 }; - var objectElements = { a:1,embed:1,hr:1,img:1,li:1,object:1,ol:1,table:1,td:1,tr:1,th:1,ul:1,dl:1,dt:1,dd:1,form:1}; - - var semicolonFixRegex = /\s*(?:;\s*|$)/; - - CKEDITOR.style = function( styleDefinition, variablesValues ) - { - if ( variablesValues ) - { - styleDefinition = CKEDITOR.tools.clone( styleDefinition ); - - replaceVariables( styleDefinition.attributes, variablesValues ); - replaceVariables( styleDefinition.styles, variablesValues ); - } - - var element = this.element = ( styleDefinition.element || '*' ).toLowerCase(); - - this.type = - ( element == '#' || blockElements[ element ] ) ? - CKEDITOR.STYLE_BLOCK - : objectElements[ element ] ? - CKEDITOR.STYLE_OBJECT - : - CKEDITOR.STYLE_INLINE; - - this._ = - { - definition : styleDefinition - }; - }; - - CKEDITOR.style.prototype = - { - apply : function( document ) - { - applyStyle.call( this, document, false ); - }, - - remove : function( document ) - { - applyStyle.call( this, document, true ); - }, - - applyToRange : function( range ) - { - return ( this.applyToRange = - this.type == CKEDITOR.STYLE_INLINE ? - applyInlineStyle - : this.type == CKEDITOR.STYLE_BLOCK ? - applyBlockStyle - : this.type == CKEDITOR.STYLE_OBJECT ? - applyObjectStyle - : null ).call( this, range ); - }, - - removeFromRange : function( range ) - { - return ( this.removeFromRange = - this.type == CKEDITOR.STYLE_INLINE ? - removeInlineStyle - : null ).call( this, range ); - }, - - applyToObject : function( element ) - { - setupElement( element, this ); - }, - - /** - * Get the style state inside an element path. Returns "true" if the - * element is active in the path. - */ - checkActive : function( elementPath ) - { - switch ( this.type ) - { - case CKEDITOR.STYLE_BLOCK : - return this.checkElementRemovable( elementPath.block || elementPath.blockLimit, true ); - - case CKEDITOR.STYLE_OBJECT : - case CKEDITOR.STYLE_INLINE : - - var elements = elementPath.elements; - - for ( var i = 0, element ; i < elements.length ; i++ ) - { - element = elements[ i ]; - - if ( this.type == CKEDITOR.STYLE_INLINE - && ( element == elementPath.block || element == elementPath.blockLimit ) ) - continue; - - if( this.type == CKEDITOR.STYLE_OBJECT - && !( element.getName() in objectElements ) ) - continue; - - if ( this.checkElementRemovable( element, true ) ) - return true; - } - } - return false; - }, - - checkApplicable : function( elementPath ) - { - switch ( this.type ) - { - case CKEDITOR.STYLE_INLINE : - case CKEDITOR.STYLE_BLOCK : - break; - - case CKEDITOR.STYLE_OBJECT : - return elementPath.lastElement.getAscendant( this.element, true ); - } - - return true; - }, - - // Checks if an element, or any of its attributes, is removable by the - // current style definition. - checkElementRemovable : function( element, fullMatch ) - { - if ( !element ) - return false; - - var def = this._.definition, - attribs; - - // If the element name is the same as the style name. - if ( element.getName() == this.element ) - { - // If no attributes are defined in the element. - if ( !fullMatch && !element.hasAttributes() ) - return true; - - attribs = getAttributesForComparison( def ); - - if ( attribs._length ) - { - for ( var attName in attribs ) - { - if ( attName == '_length' ) - continue; - - var elementAttr = element.getAttribute( attName ) || ''; - if ( attName == 'style' ? - compareCssText( attribs[ attName ], normalizeCssText( elementAttr, false ) ) - : attribs[ attName ] == elementAttr ) - { - if ( !fullMatch ) - return true; - } - else if ( fullMatch ) - return false; - } - if ( fullMatch ) - return true; - } - else - return true; - } - - // Check if the element can be somehow overriden. - var override = getOverrides( this )[ element.getName() ] ; - if ( override ) - { - // If no attributes have been defined, remove the element. - if ( !( attribs = override.attributes ) ) - return true; - - for ( var i = 0 ; i < attribs.length ; i++ ) - { - attName = attribs[i][0]; - var actualAttrValue = element.getAttribute( attName ); - if ( actualAttrValue ) - { - var attValue = attribs[i][1]; - - // Remove the attribute if: - // - The override definition value is null; - // - The override definition value is a string that - // matches the attribute value exactly. - // - The override definition value is a regex that - // has matches in the attribute value. - if ( attValue === null || - ( typeof attValue == 'string' && actualAttrValue == attValue ) || - attValue.test( actualAttrValue ) ) - return true; - } - } - } - return false; - }, - - // Builds the preview HTML based on the styles definition. - buildPreview : function() - { - var styleDefinition = this._.definition, - html = [], - elementName = styleDefinition.element; - - // Avoid <bdo> in the preview. - if ( elementName == 'bdo' ) - elementName = 'span'; - - html = [ '<', elementName ]; - - // Assign all defined attributes. - var attribs = styleDefinition.attributes; - if ( attribs ) - { - for ( var att in attribs ) - { - html.push( ' ', att, '="', attribs[ att ], '"' ); - } - } - - // Assign the style attribute. - var cssStyle = CKEDITOR.style.getStyleText( styleDefinition ); - if ( cssStyle ) - html.push( ' style="', cssStyle, '"' ); - - html.push( '>', styleDefinition.name, '</', elementName, '>' ); - - return html.join( '' ); - } - }; - - // Build the cssText based on the styles definition. - CKEDITOR.style.getStyleText = function( styleDefinition ) - { - // If we have already computed it, just return it. - var stylesDef = styleDefinition._ST; - if ( stylesDef ) - return stylesDef; - - stylesDef = styleDefinition.styles; - - // Builds the StyleText. - var stylesText = ( styleDefinition.attributes && styleDefinition.attributes[ 'style' ] ) || '', - specialStylesText = ''; - - if ( stylesText.length ) - stylesText = stylesText.replace( semicolonFixRegex, ';' ); - - for ( var style in stylesDef ) - { - var styleVal = stylesDef[ style ], - text = ( style + ':' + styleVal ).replace( semicolonFixRegex, ';' ); - - // Some browsers don't support 'inherit' property value, leave them intact. (#5242) - if ( styleVal == 'inherit' ) - specialStylesText += text; - else - stylesText += text; - } - - // Browsers make some changes to the style when applying them. So, here - // we normalize it to the browser format. - if ( stylesText.length ) - stylesText = normalizeCssText( stylesText ); - - stylesText += specialStylesText; - - // Return it, saving it to the next request. - return ( styleDefinition._ST = stylesText ); - }; - - function applyInlineStyle( range ) - { - var document = range.document; - - if ( range.collapsed ) - { - // Create the element to be inserted in the DOM. - var collapsedElement = getElement( this, document ); - - // Insert the empty element into the DOM at the range position. - range.insertNode( collapsedElement ); - - // Place the selection right inside the empty element. - range.moveToPosition( collapsedElement, CKEDITOR.POSITION_BEFORE_END ); - - return; - } - - var elementName = this.element; - var def = this._.definition; - var isUnknownElement; - - // Get the DTD definition for the element. Defaults to "span". - var dtd = CKEDITOR.dtd[ elementName ] || ( isUnknownElement = true, CKEDITOR.dtd.span ); - - // Bookmark the range so we can re-select it after processing. - var bookmark = range.createBookmark(); - - // Expand the range. - range.enlarge( CKEDITOR.ENLARGE_ELEMENT ); - range.trim(); - - // Get the first node to be processed and the last, which concludes the - // processing. - var boundaryNodes = range.createBookmark(), - firstNode = boundaryNodes.startNode, - lastNode = boundaryNodes.endNode; - - var currentNode = firstNode; - - var styleRange; - - while ( currentNode ) - { - var applyStyle = false; - - if ( currentNode.equals( lastNode ) ) - { - currentNode = null; - applyStyle = true; - } - else - { - var nodeType = currentNode.type; - var nodeName = nodeType == CKEDITOR.NODE_ELEMENT ? currentNode.getName() : null; - - if ( nodeName && currentNode.getAttribute( '_fck_bookmark' ) ) - { - currentNode = currentNode.getNextSourceNode( true ); - continue; - } - - // Check if the current node can be a child of the style element. - if ( !nodeName || ( dtd[ nodeName ] - && ( currentNode.getPosition( lastNode ) | CKEDITOR.POSITION_PRECEDING | CKEDITOR.POSITION_IDENTICAL | CKEDITOR.POSITION_IS_CONTAINED ) == ( CKEDITOR.POSITION_PRECEDING + CKEDITOR.POSITION_IDENTICAL + CKEDITOR.POSITION_IS_CONTAINED ) - && ( !def.childRule || def.childRule( currentNode ) ) ) ) - { - var currentParent = currentNode.getParent(); - - // Check if the style element can be a child of the current - // node parent or if the element is not defined in the DTD. - if ( currentParent - && ( ( currentParent.getDtd() || CKEDITOR.dtd.span )[ elementName ] || isUnknownElement ) - && ( !def.parentRule || def.parentRule( currentParent ) ) ) - { - // This node will be part of our range, so if it has not - // been started, place its start right before the node. - // In the case of an element node, it will be included - // only if it is entirely inside the range. - if ( !styleRange && ( !nodeName || !CKEDITOR.dtd.$removeEmpty[ nodeName ] || ( currentNode.getPosition( lastNode ) | CKEDITOR.POSITION_PRECEDING | CKEDITOR.POSITION_IDENTICAL | CKEDITOR.POSITION_IS_CONTAINED ) == ( CKEDITOR.POSITION_PRECEDING + CKEDITOR.POSITION_IDENTICAL + CKEDITOR.POSITION_IS_CONTAINED ) ) ) - { - styleRange = new CKEDITOR.dom.range( document ); - styleRange.setStartBefore( currentNode ); - } - - // Non element nodes, or empty elements can be added - // completely to the range. - if ( nodeType == CKEDITOR.NODE_TEXT || ( nodeType == CKEDITOR.NODE_ELEMENT && !currentNode.getChildCount() ) ) - { - var includedNode = currentNode; - var parentNode; - - // This node is about to be included completelly, but, - // if this is the last node in its parent, we must also - // check if the parent itself can be added completelly - // to the range. - while ( !includedNode.$.nextSibling - && ( parentNode = includedNode.getParent(), dtd[ parentNode.getName() ] ) - && ( parentNode.getPosition( firstNode ) | CKEDITOR.POSITION_FOLLOWING | CKEDITOR.POSITION_IDENTICAL | CKEDITOR.POSITION_IS_CONTAINED ) == ( CKEDITOR.POSITION_FOLLOWING + CKEDITOR.POSITION_IDENTICAL + CKEDITOR.POSITION_IS_CONTAINED ) - && ( !def.childRule || def.childRule( parentNode ) ) ) - { - includedNode = parentNode; - } - - styleRange.setEndAfter( includedNode ); - - // If the included node still is the last node in its - // parent, it means that the parent can't be included - // in this style DTD, so apply the style immediately. - if ( !includedNode.$.nextSibling ) - applyStyle = true; - - } - } - else - applyStyle = true; - } - else - applyStyle = true; - - // Get the next node to be processed. - currentNode = currentNode.getNextSourceNode(); - } - - // Apply the style if we have something to which apply it. - if ( applyStyle && styleRange && !styleRange.collapsed ) - { - // Build the style element, based on the style object definition. - var styleNode = getElement( this, document ); - - // Get the element that holds the entire range. - var parent = styleRange.getCommonAncestor(); - - // Loop through the parents, removing the redundant attributes - // from the element to be applied. - while ( styleNode && parent ) - { - if ( parent.getName() == elementName ) - { - for ( var attName in def.attributes ) - { - if ( styleNode.getAttribute( attName ) == parent.getAttribute( attName ) ) - styleNode.removeAttribute( attName ); - } - - for ( var styleName in def.styles ) - { - if ( styleNode.getStyle( styleName ) == parent.getStyle( styleName ) ) - styleNode.removeStyle( styleName ); - } - - if ( !styleNode.hasAttributes() ) - { - styleNode = null; - break; - } - } - - parent = parent.getParent(); - } - - if ( styleNode ) - { - // Move the contents of the range to the style element. - styleRange.extractContents().appendTo( styleNode ); - - // Here we do some cleanup, removing all duplicated - // elements from the style element. - removeFromInsideElement( this, styleNode ); - - // Insert it into the range position (it is collapsed after - // extractContents. - styleRange.insertNode( styleNode ); - - // Let's merge our new style with its neighbors, if possible. - styleNode.mergeSiblings(); - - // As the style system breaks text nodes constantly, let's normalize - // things for performance. - // With IE, some paragraphs get broken when calling normalize() - // repeatedly. Also, for IE, we must normalize body, not documentElement. - // IE is also known for having a "crash effect" with normalize(). - // We should try to normalize with IE too in some way, somewhere. - if ( !CKEDITOR.env.ie ) - styleNode.$.normalize(); - } - - // Style applied, let's release the range, so it gets - // re-initialization in the next loop. - styleRange = null; - } - } - - firstNode.remove(); - lastNode.remove(); - range.moveToBookmark( bookmark ); - // Minimize the result range to exclude empty text nodes. (#5374) - range.shrink( CKEDITOR.SHRINK_TEXT ); - } - - function removeInlineStyle( range ) - { - /* - * Make sure our range has included all "collpased" parent inline nodes so - * that our operation logic can be simpler. - */ - range.enlarge( CKEDITOR.ENLARGE_ELEMENT ); - - var bookmark = range.createBookmark(), - startNode = bookmark.startNode; - - if ( range.collapsed ) - { - - var startPath = new CKEDITOR.dom.elementPath( startNode.getParent() ), - // The topmost element in elementspatch which we should jump out of. - boundaryElement; - - - for ( var i = 0, element ; i < startPath.elements.length - && ( element = startPath.elements[i] ) ; i++ ) - { - /* - * 1. If it's collaped inside text nodes, try to remove the style from the whole element. - * - * 2. Otherwise if it's collapsed on element boundaries, moving the selection - * outside the styles instead of removing the whole tag, - * also make sure other inner styles were well preserverd.(#3309) - */ - if ( element == startPath.block || element == startPath.blockLimit ) - break; - - if ( this.checkElementRemovable( element ) ) - { - var endOfElement = range.checkBoundaryOfElement( element, CKEDITOR.END ), - startOfElement = !endOfElement && range.checkBoundaryOfElement( element, CKEDITOR.START ); - if ( startOfElement || endOfElement ) - { - boundaryElement = element; - boundaryElement.match = startOfElement ? 'start' : 'end'; - } - else - { - /* - * Before removing the style node, there may be a sibling to the style node - * that's exactly the same to the one to be removed. To the user, it makes - * no difference that they're separate entities in the DOM tree. So, merge - * them before removal. - */ - element.mergeSiblings(); - removeFromElement( this, element ); - - } - } - } - - // Re-create the style tree after/before the boundary element, - // the replication start from bookmark start node to define the - // new range. - if ( boundaryElement ) - { - var clonedElement = startNode; - for ( i = 0 ;; i++ ) - { - var newElement = startPath.elements[ i ]; - if ( newElement.equals( boundaryElement ) ) - break; - // Avoid copying any matched element. - else if ( newElement.match ) - continue; - else - newElement = newElement.clone(); - newElement.append( clonedElement ); - clonedElement = newElement; - } - clonedElement[ boundaryElement.match == 'start' ? - 'insertBefore' : 'insertAfter' ]( boundaryElement ); - } - } - else - { - /* - * Now our range isn't collapsed. Lets walk from the start node to the end - * node via DFS and remove the styles one-by-one. - */ - var endNode = bookmark.endNode, - me = this; - - /* - * Find out the style ancestor that needs to be broken down at startNode - * and endNode. - */ - function breakNodes() - { - var startPath = new CKEDITOR.dom.elementPath( startNode.getParent() ), - endPath = new CKEDITOR.dom.elementPath( endNode.getParent() ), - breakStart = null, - breakEnd = null; - for ( var i = 0 ; i < startPath.elements.length ; i++ ) - { - var element = startPath.elements[ i ]; - - if ( element == startPath.block || element == startPath.blockLimit ) - break; - - if ( me.checkElementRemovable( element ) ) - breakStart = element; - } - for ( i = 0 ; i < endPath.elements.length ; i++ ) - { - element = endPath.elements[ i ]; - - if ( element == endPath.block || element == endPath.blockLimit ) - break; - - if ( me.checkElementRemovable( element ) ) - breakEnd = element; - } - - if ( breakEnd ) - endNode.breakParent( breakEnd ); - if ( breakStart ) - startNode.breakParent( breakStart ); - } - breakNodes(); - - // Now, do the DFS walk. - var currentNode = startNode.getNext(); - while ( !currentNode.equals( endNode ) ) - { - /* - * Need to get the next node first because removeFromElement() can remove - * the current node from DOM tree. - */ - var nextNode = currentNode.getNextSourceNode(); - if ( currentNode.type == CKEDITOR.NODE_ELEMENT && this.checkElementRemovable( currentNode ) ) - { - // Remove style from element or overriding element. - if ( currentNode.getName() == this.element ) - removeFromElement( this, currentNode ); - else - removeOverrides( currentNode, getOverrides( this )[ currentNode.getName() ] ); - - /* - * removeFromElement() may have merged the next node with something before - * the startNode via mergeSiblings(). In that case, the nextNode would - * contain startNode and we'll have to call breakNodes() again and also - * reassign the nextNode to something after startNode. - */ - if ( nextNode.type == CKEDITOR.NODE_ELEMENT && nextNode.contains( startNode ) ) - { - breakNodes(); - nextNode = startNode.getNext(); - } - } - currentNode = nextNode; - } - } - - range.moveToBookmark( bookmark ); -} - - function applyObjectStyle( range ) - { - var root = range.getCommonAncestor( true, true ), - element = root.getAscendant( this.element, true ); - element && setupElement( element, this ); - } - - function applyBlockStyle( range ) - { - // Serializible bookmarks is needed here since - // elements may be merged. - var bookmark = range.createBookmark( true ); - - var iterator = range.createIterator(); - iterator.enforceRealBlocks = true; - - // make recognize <br /> tag as a separator in ENTER_BR mode (#5121) - if ( this._.enterMode ) - iterator.enlargeBr = ( this._.enterMode != CKEDITOR.ENTER_BR ); - - var block; - var doc = range.document; - var previousPreBlock; - - while ( ( block = iterator.getNextParagraph() ) ) // Only one = - { - var newBlock = getElement( this, doc ); - replaceBlock( block, newBlock ); - } - - range.moveToBookmark( bookmark ); - } - - // Replace the original block with new one, with special treatment - // for <pre> blocks to make sure content format is well preserved, and merging/splitting adjacent - // when necessary.(#3188) - function replaceBlock( block, newBlock ) - { - var newBlockIsPre = newBlock.is( 'pre' ); - var blockIsPre = block.is( 'pre' ); - - var isToPre = newBlockIsPre && !blockIsPre; - var isFromPre = !newBlockIsPre && blockIsPre; - - if ( isToPre ) - newBlock = toPre( block, newBlock ); - else if ( isFromPre ) - // Split big <pre> into pieces before start to convert. - newBlock = fromPres( splitIntoPres( block ), newBlock ); - else - block.moveChildren( newBlock ); - - newBlock.replace( block ); - - if ( newBlockIsPre ) - { - // Merge previous <pre> blocks. - mergePre( newBlock ); - } - } - - /** - * Merge a <pre> block with a previous sibling if available. - */ - function mergePre( preBlock ) - { - var previousBlock; - if ( !( ( previousBlock = preBlock.getPreviousSourceNode( true, CKEDITOR.NODE_ELEMENT ) ) - && previousBlock.is - && previousBlock.is( 'pre') ) ) - return; - - // Merge the previous <pre> block contents into the current <pre> - // block. - // - // Another thing to be careful here is that currentBlock might contain - // a '\n' at the beginning, and previousBlock might contain a '\n' - // towards the end. These new lines are not normally displayed but they - // become visible after merging. - var mergedHtml = replace( previousBlock.getHtml(), /\n$/, '' ) + '\n\n' + - replace( preBlock.getHtml(), /^\n/, '' ) ; - - // Krugle: IE normalizes innerHTML from <pre>, breaking whitespaces. - if ( CKEDITOR.env.ie ) - preBlock.$.outerHTML = '<pre>' + mergedHtml + '</pre>'; - else - preBlock.setHtml( mergedHtml ); - - previousBlock.remove(); - } - - /** - * Split into multiple <pre> blocks separated by double line-break. - * @param preBlock - */ - function splitIntoPres( preBlock ) - { - // Exclude the ones at header OR at tail, - // and ignore bookmark content between them. - var duoBrRegex = /(\S\s*)\n(?:\s|(<span[^>]+_fck_bookmark.*?\/span>))*\n(?!$)/gi, - blockName = preBlock.getName(), - splitedHtml = replace( preBlock.getOuterHtml(), - duoBrRegex, - function( match, charBefore, bookmark ) - { - return charBefore + '</pre>' + bookmark + '<pre>'; - } ); - - var pres = []; - splitedHtml.replace( /<pre\b.*?>([\s\S]*?)<\/pre>/gi, function( match, preContent ){ - pres.push( preContent ); - } ); - return pres; - } - - // Wrapper function of String::replace without considering of head/tail bookmarks nodes. - function replace( str, regexp, replacement ) - { - var headBookmark = '', - tailBookmark = ''; - - str = str.replace( /(^<span[^>]+_fck_bookmark.*?\/span>)|(<span[^>]+_fck_bookmark.*?\/span>$)/gi, - function( str, m1, m2 ){ - m1 && ( headBookmark = m1 ); - m2 && ( tailBookmark = m2 ); - return ''; - } ); - return headBookmark + str.replace( regexp, replacement ) + tailBookmark; - } - /** - * Converting a list of <pre> into blocks with format well preserved. - */ - function fromPres( preHtmls, newBlock ) - { - var docFrag = new CKEDITOR.dom.documentFragment( newBlock.getDocument() ); - for ( var i = 0 ; i < preHtmls.length ; i++ ) - { - var blockHtml = preHtmls[ i ]; - - // 1. Trim the first and last line-breaks immediately after and before <pre>, - // they're not visible. - blockHtml = blockHtml.replace( /(\r\n|\r)/g, '\n' ) ; - blockHtml = replace( blockHtml, /^[ \t]*\n/, '' ) ; - blockHtml = replace( blockHtml, /\n$/, '' ) ; - // 2. Convert spaces or tabs at the beginning or at the end to - blockHtml = replace( blockHtml, /^[ \t]+|[ \t]+$/g, function( match, offset, s ) - { - if ( match.length == 1 ) // one space, preserve it - return ' ' ; - else if ( !offset ) // beginning of block - return CKEDITOR.tools.repeat( ' ', match.length - 1 ) + ' '; - else // end of block - return ' ' + CKEDITOR.tools.repeat( ' ', match.length - 1 ); - } ) ; - - // 3. Convert \n to <BR>. - // 4. Convert contiguous (i.e. non-singular) spaces or tabs to - blockHtml = blockHtml.replace( /\n/g, '<br>' ) ; - blockHtml = blockHtml.replace( /[ \t]{2,}/g, - function ( match ) - { - return CKEDITOR.tools.repeat( ' ', match.length - 1 ) + ' ' ; - } ) ; - - var newBlockClone = newBlock.clone(); - newBlockClone.setHtml( blockHtml ); - docFrag.append( newBlockClone ); - } - return docFrag; - } - - /** - * Converting from a non-PRE block to a PRE block in formatting operations. - */ - function toPre( block, newBlock ) - { - // First trim the block content. - var preHtml = block.getHtml(); - - // 1. Trim head/tail spaces, they're not visible. - preHtml = replace( preHtml, /(?:^[ \t\n\r]+)|(?:[ \t\n\r]+$)/g, '' ); - // 2. Delete ANSI whitespaces immediately before and after <BR> because - // they are not visible. - preHtml = preHtml.replace( /[ \t\r\n]*(<br[^>]*>)[ \t\r\n]*/gi, '$1' ); - // 3. Compress other ANSI whitespaces since they're only visible as one - // single space previously. - // 4. Convert to spaces since is no longer needed in <PRE>. - preHtml = preHtml.replace( /([ \t\n\r]+| )/g, ' ' ); - // 5. Convert any <BR /> to \n. This must not be done earlier because - // the \n would then get compressed. - preHtml = preHtml.replace( /<br\b[^>]*>/gi, '\n' ); - - // Krugle: IE normalizes innerHTML to <pre>, breaking whitespaces. - if ( CKEDITOR.env.ie ) - { - var temp = block.getDocument().createElement( 'div' ); - temp.append( newBlock ); - newBlock.$.outerHTML = '<pre>' + preHtml + '</pre>'; - newBlock = temp.getFirst().remove(); - } - else - newBlock.setHtml( preHtml ); - - return newBlock; - } - - // Removes a style from an element itself, don't care about its subtree. - function removeFromElement( style, element ) - { - var def = style._.definition, - attributes = CKEDITOR.tools.extend( {}, def.attributes, getOverrides( style )[ element.getName() ] ), - styles = def.styles, - // If the style is only about the element itself, we have to remove the element. - removeEmpty = CKEDITOR.tools.isEmpty( attributes ) && CKEDITOR.tools.isEmpty( styles ); - - // Remove definition attributes/style from the elemnt. - for ( var attName in attributes ) - { - // The 'class' element value must match (#1318). - if ( ( attName == 'class' || style._.definition.fullMatch ) - && element.getAttribute( attName ) != normalizeProperty( attName, attributes[ attName ] ) ) - continue; - removeEmpty = element.hasAttribute( attName ); - element.removeAttribute( attName ); - } - - for ( var styleName in styles ) - { - // Full match style insist on having fully equivalence. (#5018) - if ( style._.definition.fullMatch - && element.getStyle( styleName ) != normalizeProperty( styleName, styles[ styleName ], true ) ) - continue; - - removeEmpty = removeEmpty || !!element.getStyle( styleName ); - element.removeStyle( styleName ); - } - - removeEmpty && removeNoAttribsElement( element ); - } - - // Removes a style from inside an element. - function removeFromInsideElement( style, element ) - { - var def = style._.definition, - attribs = def.attributes, - styles = def.styles, - overrides = getOverrides( style ); - - var innerElements = element.getElementsByTag( style.element ); - - for ( var i = innerElements.count(); --i >= 0 ; ) - removeFromElement( style, innerElements.getItem( i ) ); - - // Now remove any other element with different name that is - // defined to be overriden. - for ( var overrideElement in overrides ) - { - if ( overrideElement != style.element ) - { - innerElements = element.getElementsByTag( overrideElement ) ; - for ( i = innerElements.count() - 1 ; i >= 0 ; i-- ) - { - var innerElement = innerElements.getItem( i ); - removeOverrides( innerElement, overrides[ overrideElement ] ) ; - } - } - } - - } - - /** - * Remove overriding styles/attributes from the specific element. - * Note: Remove the element if no attributes remain. - * @param {Object} element - * @param {Object} overrides - */ - function removeOverrides( element, overrides ) - { - var attributes = overrides && overrides.attributes ; - - if ( attributes ) - { - for ( var i = 0 ; i < attributes.length ; i++ ) - { - var attName = attributes[i][0], actualAttrValue ; - - if ( ( actualAttrValue = element.getAttribute( attName ) ) ) - { - var attValue = attributes[i][1] ; - - // Remove the attribute if: - // - The override definition value is null ; - // - The override definition valie is a string that - // matches the attribute value exactly. - // - The override definition value is a regex that - // has matches in the attribute value. - if ( attValue === null || - ( attValue.test && attValue.test( actualAttrValue ) ) || - ( typeof attValue == 'string' && actualAttrValue == attValue ) ) - element.removeAttribute( attName ) ; - } - } - } - - removeNoAttribsElement( element ); - } - - // If the element has no more attributes, remove it. - function removeNoAttribsElement( element ) - { - // If no more attributes remained in the element, remove it, - // leaving its children. - if ( !element.hasAttributes() ) - { - // Removing elements may open points where merging is possible, - // so let's cache the first and last nodes for later checking. - var firstChild = element.getFirst(); - var lastChild = element.getLast(); - - element.remove( true ); - - if ( firstChild ) - { - // Check the cached nodes for merging. - firstChild.type == CKEDITOR.NODE_ELEMENT && firstChild.mergeSiblings(); - - if ( lastChild && !firstChild.equals( lastChild ) - && lastChild.type == CKEDITOR.NODE_ELEMENT ) - lastChild.mergeSiblings(); - } - } - } - - function getElement( style, targetDocument ) - { - var el; - - var def = style._.definition; - - var elementName = style.element; - - // The "*" element name will always be a span for this function. - if ( elementName == '*' ) - elementName = 'span'; - - // Create the element. - el = new CKEDITOR.dom.element( elementName, targetDocument ); - - return setupElement( el, style ); - } - - function setupElement( el, style ) - { - var def = style._.definition; - var attributes = def.attributes; - var styles = CKEDITOR.style.getStyleText( def ); - - // Assign all defined attributes. - if ( attributes ) - { - for ( var att in attributes ) - { - el.setAttribute( att, attributes[ att ] ); - } - } - - // Assign all defined styles. - if ( styles ) - el.setAttribute( 'style', styles ); - - return el; - } - - var varRegex = /#\((.+?)\)/g; - function replaceVariables( list, variablesValues ) - { - for ( var item in list ) - { - list[ item ] = list[ item ].replace( varRegex, function( match, varName ) - { - return variablesValues[ varName ]; - }); - } - } - - - // Returns an object that can be used for style matching comparison. - // Attributes names and values are all lowercased, and the styles get - // merged with the style attribute. - function getAttributesForComparison( styleDefinition ) - { - // If we have already computed it, just return it. - var attribs = styleDefinition._AC; - if ( attribs ) - return attribs; - - attribs = {}; - - var length = 0; - - // Loop through all defined attributes. - var styleAttribs = styleDefinition.attributes; - if ( styleAttribs ) - { - for ( var styleAtt in styleAttribs ) - { - length++; - attribs[ styleAtt ] = styleAttribs[ styleAtt ]; - } - } - - // Includes the style definitions. - var styleText = CKEDITOR.style.getStyleText( styleDefinition ); - if ( styleText ) - { - if ( !attribs[ 'style' ] ) - length++; - attribs[ 'style' ] = styleText; - } - - // Appends the "length" information to the object. - attribs._length = length; - - // Return it, saving it to the next request. - return ( styleDefinition._AC = attribs ); - } - - /** - * Get the the collection used to compare the elements and attributes, - * defined in this style overrides, with other element. All information in - * it is lowercased. - * @param {CKEDITOR.style} style - */ - function getOverrides( style ) - { - if ( style._.overrides ) - return style._.overrides; - - var overrides = ( style._.overrides = {} ), - definition = style._.definition.overrides; - - if ( definition ) - { - // The override description can be a string, object or array. - // Internally, well handle arrays only, so transform it if needed. - if ( !CKEDITOR.tools.isArray( definition ) ) - definition = [ definition ]; - - // Loop through all override definitions. - for ( var i = 0 ; i < definition.length ; i++ ) - { - var override = definition[i]; - var elementName; - var overrideEl; - var attrs; - - // If can be a string with the element name. - if ( typeof override == 'string' ) - elementName = override.toLowerCase(); - // Or an object. - else - { - elementName = override.element ? override.element.toLowerCase() : style.element; - attrs = override.attributes; - } - - // We can have more than one override definition for the same - // element name, so we attempt to simply append information to - // it if it already exists. - overrideEl = overrides[ elementName ] || ( overrides[ elementName ] = {} ); - - if ( attrs ) - { - // The returning attributes list is an array, because we - // could have different override definitions for the same - // attribute name. - var overrideAttrs = ( overrideEl.attributes = overrideEl.attributes || new Array() ); - for ( var attName in attrs ) - { - // Each item in the attributes array is also an array, - // where [0] is the attribute name and [1] is the - // override value. - overrideAttrs.push( [ attName.toLowerCase(), attrs[ attName ] ] ); - } - } - } - } - - return overrides; - } - - function normalizeProperty( name, value, isStyle ) - { - var temp = new CKEDITOR.dom.element( 'span' ); - temp [ isStyle ? 'setStyle' : 'setAttribute' ]( name, value ); - return temp[ isStyle ? 'getStyle' : 'getAttribute' ]( name ); - } - - function normalizeCssText( unparsedCssText, nativeNormalize ) - { - var styleText; - if ( nativeNormalize !== false ) - { - // Injects the style in a temporary span object, so the browser parses it, - // retrieving its final format. - var temp = new CKEDITOR.dom.element( 'span' ); - temp.setAttribute( 'style', unparsedCssText ); - styleText = temp.getAttribute( 'style' ) || ''; - } - else - styleText = unparsedCssText; - - // Shrinking white-spaces around colon and semi-colon (#4147). - // Compensate tail semi-colon. - return styleText.replace( /\s*([;:])\s*/, '$1' ) - .replace( /([^\s;])$/, '$1;') - .replace( /,\s+/g, ',' ) // Trimming spaces after comma (e.g. font-family name)(#4107). - .toLowerCase(); - } - - // Turn inline style text properties into one hash. - function parseStyleText( styleText ) - { - var retval = {}; - styleText - .replace( /"/g, '"' ) - .replace( /\s*([^ :;]+)\s*:\s*([^;]+)\s*(?=;|$)/g, function( match, name, value ) - { - retval[ name ] = value; - } ); - return retval; - } - - function compareCssText( source, target ) - { - typeof source == 'string' && ( source = parseStyleText( source ) ); - typeof target == 'string' && ( target = parseStyleText( target ) ); - for( var name in source ) - { - // Value 'inherit' is treated as a wildcard, - // which will match any value. - if ( !( name in target && - ( target[ name ] == source[ name ] - || source[ name ] == 'inherit' - || target[ name ] == 'inherit' ) ) ) - { - return false; - } - } - return true; - } - - function applyStyle( document, remove ) - { - // Get all ranges from the selection. - var selection = document.getSelection(); - var ranges = selection.getRanges(); - var func = remove ? this.removeFromRange : this.applyToRange; - - // Apply the style to the ranges. - for ( var i = 0 ; i < ranges.length ; i++ ) - func.call( this, ranges[ i ] ); - - // Select the ranges again. - selection.selectRanges( ranges ); - } -})(); - -CKEDITOR.styleCommand = function( style ) -{ - this.style = style; -}; - -CKEDITOR.styleCommand.prototype.exec = function( editor ) -{ - editor.focus(); - - var doc = editor.document; - - if ( doc ) - { - if ( this.state == CKEDITOR.TRISTATE_OFF ) - this.style.apply( doc ); - else if ( this.state == CKEDITOR.TRISTATE_ON ) - this.style.remove( doc ); - } - - return !!doc; -}; - -CKEDITOR.stylesSet = new CKEDITOR.resourceManager( '', 'stylesSet' ); - -// Backward compatibility (#5025). -CKEDITOR.addStylesSet = CKEDITOR.tools.bind( CKEDITOR.stylesSet.add, CKEDITOR.stylesSet ); -CKEDITOR.loadStylesSet = function( name, url, callback ) - { - CKEDITOR.stylesSet.addExternal( name, url, '' ); - CKEDITOR.stylesSet.load( name, callback ); - }; - - -/** - * Gets the current styleSet for this instance - * @param {Function} The function to be called with the styles data. - * @example - * editor.getStylesSet( function( stylesDefinitions ) {} ); - */ -CKEDITOR.editor.prototype.getStylesSet = function( callback ) -{ - if ( !this._.stylesDefinitions ) - { - var editor = this, - // Respect the backwards compatible definition entry - configStyleSet = editor.config.stylesCombo_stylesSet || editor.config.stylesSet || 'default'; - - // #5352 Allow to define the styles directly in the config object - if ( configStyleSet instanceof Array ) - { - editor._.stylesDefinitions = configStyleSet; - callback( configStyleSet ); - return; - } - - var partsStylesSet = configStyleSet.split( ':' ), - styleSetName = partsStylesSet[ 0 ], - externalPath = partsStylesSet[ 1 ], - pluginPath = CKEDITOR.plugins.registered.styles.path; - - CKEDITOR.stylesSet.addExternal( styleSetName, - externalPath ? - partsStylesSet.slice( 1 ).join( ':' ) : - pluginPath + 'styles/' + styleSetName + '.js', '' ); - - CKEDITOR.stylesSet.load( styleSetName, function( stylesSet ) - { - editor._.stylesDefinitions = stylesSet[ styleSetName ]; - callback( editor._.stylesDefinitions ); - } ) ; - } - else - callback( this._.stylesDefinitions ); -}; - -/** - * The "styles definition set" to use in the editor. They will be used in the - * styles combo and the Style selector of the div container. <br> - * The styles may be defined in the page containing the editor, or can be - * loaded on demand from an external file. In the second case, if this setting - * contains only a name, the styles definition file will be loaded from the - * "styles" folder inside the styles plugin folder. - * Otherwise, this setting has the "name:url" syntax, making it - * possible to set the URL from which loading the styles file.<br> - * Previously this setting was available as config.stylesCombo_stylesSet<br> - * @name CKEDITOR.config.stylesSet - * @type String|Array - * @default 'default' - * @since 3.3 - * @example - * // Load from the styles' styles folder (mystyles.js file). - * config.stylesSet = 'mystyles'; - * @example - * // Load from a relative URL. - * config.stylesSet = 'mystyles:/editorstyles/styles.js'; - * @example - * // Load from a full URL. - * config.stylesSet = 'mystyles:http://www.example.com/editorstyles/styles.js'; - * @example - * // Load from a list of definitions. - * config.stylesSet = [ - * { name : 'Strong Emphasis', element : 'strong' }, - * { name : 'Emphasis', element : 'em' }, ... ]; - */ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.add( 'styles',
+{
+ requires : [ 'selection' ],
+ init : function( editor )
+ {
+ // This doesn't look like correct, but it's the safest way to proper
+ // pass the disableReadonlyStyling configuration to the style system
+ // without having to change any method signature in the API. (#6103)
+ editor.on( 'contentDom', function()
+ {
+ editor.document.setCustomData( 'cke_includeReadonly', !editor.config.disableReadonlyStyling );
+ });
+ }
+});
+
+/**
+ * Registers a function to be called whenever the selection position changes in the
+ * editing area. The current state is passed to the function. The possible
+ * states are {@link CKEDITOR.TRISTATE_ON} and {@link CKEDITOR.TRISTATE_OFF}.
+ * @param {CKEDITOR.style} style The style to be watched.
+ * @param {Function} callback The function to be called.
+ * @example
+ * // Create a style object for the <b> element.
+ * var style = new CKEDITOR.style( { element : 'b' } );
+ * var editor = CKEDITOR.instances.editor1;
+ * editor.attachStyleStateChange( style, function( state )
+ * {
+ * if ( state == CKEDITOR.TRISTATE_ON )
+ * alert( 'The current state for the B element is ON' );
+ * else
+ * alert( 'The current state for the B element is OFF' );
+ * });
+ */
+CKEDITOR.editor.prototype.attachStyleStateChange = function( style, callback )
+{
+ // Try to get the list of attached callbacks.
+ var styleStateChangeCallbacks = this._.styleStateChangeCallbacks;
+
+ // If it doesn't exist, it means this is the first call. So, let's create
+ // all the structure to manage the style checks and the callback calls.
+ if ( !styleStateChangeCallbacks )
+ {
+ // Create the callbacks array.
+ styleStateChangeCallbacks = this._.styleStateChangeCallbacks = [];
+
+ // Attach to the selectionChange event, so we can check the styles at
+ // that point.
+ this.on( 'selectionChange', function( ev )
+ {
+ // Loop throw all registered callbacks.
+ for ( var i = 0 ; i < styleStateChangeCallbacks.length ; i++ )
+ {
+ var callback = styleStateChangeCallbacks[ i ];
+
+ // Check the current state for the style defined for that
+ // callback.
+ var currentState = callback.style.checkActive( ev.data.path ) ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF;
+
+ // Call the callback function, passing the current
+ // state to it.
+ callback.fn.call( this, currentState );
+ }
+ });
+ }
+
+ // Save the callback info, so it can be checked on the next occurrence of
+ // selectionChange.
+ styleStateChangeCallbacks.push( { style : style, fn : callback } );
+};
+
+CKEDITOR.STYLE_BLOCK = 1;
+CKEDITOR.STYLE_INLINE = 2;
+CKEDITOR.STYLE_OBJECT = 3;
+
+(function()
+{
+ var blockElements = { address:1,div:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,p:1,pre:1,section:1,header:1,footer:1,nav:1,article:1,aside:1,figure:1,dialog:1,hgroup:1,time:1,meter:1,menu:1,command:1,keygen:1,output:1,progress:1,details:1,datagrid:1,datalist:1 },
+ objectElements = { a:1,embed:1,hr:1,img:1,li:1,object:1,ol:1,table:1,td:1,tr:1,th:1,ul:1,dl:1,dt:1,dd:1,form:1,audio:1,video:1 };
+
+ var semicolonFixRegex = /\s*(?:;\s*|$)/,
+ varRegex = /#\((.+?)\)/g;
+
+ var notBookmark = CKEDITOR.dom.walker.bookmark( 0, 1 ),
+ nonWhitespaces = CKEDITOR.dom.walker.whitespaces( 1 );
+
+ CKEDITOR.style = function( styleDefinition, variablesValues )
+ {
+ if ( variablesValues )
+ {
+ styleDefinition = CKEDITOR.tools.clone( styleDefinition );
+
+ replaceVariables( styleDefinition.attributes, variablesValues );
+ replaceVariables( styleDefinition.styles, variablesValues );
+ }
+
+ var element = this.element = styleDefinition.element ?
+ ( typeof styleDefinition.element == 'string' ? styleDefinition.element.toLowerCase() : styleDefinition.element )
+ : '*';
+
+ this.type =
+ blockElements[ element ] ?
+ CKEDITOR.STYLE_BLOCK
+ : objectElements[ element ] ?
+ CKEDITOR.STYLE_OBJECT
+ :
+ CKEDITOR.STYLE_INLINE;
+
+ // If the 'element' property is an object with a set of possible element, it will be applied like an object style: only to existing elements
+ if ( typeof this.element == 'object' )
+ this.type = CKEDITOR.STYLE_OBJECT;
+
+ this._ =
+ {
+ definition : styleDefinition
+ };
+ };
+
+ CKEDITOR.style.prototype =
+ {
+ apply : function( document )
+ {
+ applyStyle.call( this, document, false );
+ },
+
+ remove : function( document )
+ {
+ applyStyle.call( this, document, true );
+ },
+
+ applyToRange : function( range )
+ {
+ return ( this.applyToRange =
+ this.type == CKEDITOR.STYLE_INLINE ?
+ applyInlineStyle
+ : this.type == CKEDITOR.STYLE_BLOCK ?
+ applyBlockStyle
+ : this.type == CKEDITOR.STYLE_OBJECT ?
+ applyObjectStyle
+ : null ).call( this, range );
+ },
+
+ removeFromRange : function( range )
+ {
+ return ( this.removeFromRange =
+ this.type == CKEDITOR.STYLE_INLINE ?
+ removeInlineStyle
+ : this.type == CKEDITOR.STYLE_BLOCK ?
+ removeBlockStyle
+ : this.type == CKEDITOR.STYLE_OBJECT ?
+ removeObjectStyle
+ : null ).call( this, range );
+ },
+
+ applyToObject : function( element )
+ {
+ setupElement( element, this );
+ },
+
+ /**
+ * Get the style state inside an element path. Returns "true" if the
+ * element is active in the path.
+ */
+ checkActive : function( elementPath )
+ {
+ switch ( this.type )
+ {
+ case CKEDITOR.STYLE_BLOCK :
+ return this.checkElementRemovable( elementPath.block || elementPath.blockLimit, true );
+
+ case CKEDITOR.STYLE_OBJECT :
+ case CKEDITOR.STYLE_INLINE :
+
+ var elements = elementPath.elements;
+
+ for ( var i = 0, element ; i < elements.length ; i++ )
+ {
+ element = elements[ i ];
+
+ if ( this.type == CKEDITOR.STYLE_INLINE
+ && ( element == elementPath.block || element == elementPath.blockLimit ) )
+ continue;
+
+ if( this.type == CKEDITOR.STYLE_OBJECT )
+ {
+ var name = element.getName();
+ if ( !( typeof this.element == 'string' ? name == this.element : name in this.element ) )
+ continue;
+ }
+
+ if ( this.checkElementRemovable( element, true ) )
+ return true;
+ }
+ }
+ return false;
+ },
+
+ /**
+ * Whether this style can be applied at the element path.
+ * @param elementPath
+ */
+ checkApplicable : function( elementPath )
+ {
+ switch ( this.type )
+ {
+ case CKEDITOR.STYLE_INLINE :
+ case CKEDITOR.STYLE_BLOCK :
+ break;
+
+ case CKEDITOR.STYLE_OBJECT :
+ return elementPath.lastElement.getAscendant( this.element, true );
+ }
+
+ return true;
+ },
+
+ // Checks if an element, or any of its attributes, is removable by the
+ // current style definition.
+ checkElementRemovable : function( element, fullMatch )
+ {
+ var def = this._.definition;
+
+ if ( !element || !def.ignoreReadonly && element.isReadOnly() )
+ return false;
+
+ var attribs,
+ name = element.getName();
+
+ // If the element name is the same as the style name.
+ if ( typeof this.element == 'string' ? name == this.element : name in this.element )
+ {
+ // If no attributes are defined in the element.
+ if ( !fullMatch && !element.hasAttributes() )
+ return true;
+
+ attribs = getAttributesForComparison( def );
+
+ if ( attribs._length )
+ {
+ for ( var attName in attribs )
+ {
+ if ( attName == '_length' )
+ continue;
+
+ var elementAttr = element.getAttribute( attName ) || '';
+
+ // Special treatment for 'style' attribute is required.
+ if ( attName == 'style' ?
+ compareCssText( attribs[ attName ], normalizeCssText( elementAttr, false ) )
+ : attribs[ attName ] == elementAttr )
+ {
+ if ( !fullMatch )
+ return true;
+ }
+ else if ( fullMatch )
+ return false;
+ }
+ if ( fullMatch )
+ return true;
+ }
+ else
+ return true;
+ }
+
+ // Check if the element can be somehow overriden.
+ var override = getOverrides( this )[ element.getName() ] ;
+ if ( override )
+ {
+ // If no attributes have been defined, remove the element.
+ if ( !( attribs = override.attributes ) )
+ return true;
+
+ for ( var i = 0 ; i < attribs.length ; i++ )
+ {
+ attName = attribs[i][0];
+ var actualAttrValue = element.getAttribute( attName );
+ if ( actualAttrValue )
+ {
+ var attValue = attribs[i][1];
+
+ // Remove the attribute if:
+ // - The override definition value is null;
+ // - The override definition value is a string that
+ // matches the attribute value exactly.
+ // - The override definition value is a regex that
+ // has matches in the attribute value.
+ if ( attValue === null ||
+ ( typeof attValue == 'string' && actualAttrValue == attValue ) ||
+ attValue.test( actualAttrValue ) )
+ return true;
+ }
+ }
+ }
+ return false;
+ },
+
+ // Builds the preview HTML based on the styles definition.
+ buildPreview : function( label )
+ {
+ var styleDefinition = this._.definition,
+ html = [],
+ elementName = styleDefinition.element;
+
+ // Avoid <bdo> in the preview.
+ if ( elementName == 'bdo' )
+ elementName = 'span';
+
+ html = [ '<', elementName ];
+
+ // Assign all defined attributes.
+ var attribs = styleDefinition.attributes;
+ if ( attribs )
+ {
+ for ( var att in attribs )
+ {
+ html.push( ' ', att, '="', attribs[ att ], '"' );
+ }
+ }
+
+ // Assign the style attribute.
+ var cssStyle = CKEDITOR.style.getStyleText( styleDefinition );
+ if ( cssStyle )
+ html.push( ' style="', cssStyle, '"' );
+
+ html.push( '>', ( label || styleDefinition.name ), '</', elementName, '>' );
+
+ return html.join( '' );
+ }
+ };
+
+ // Build the cssText based on the styles definition.
+ CKEDITOR.style.getStyleText = function( styleDefinition )
+ {
+ // If we have already computed it, just return it.
+ var stylesDef = styleDefinition._ST;
+ if ( stylesDef )
+ return stylesDef;
+
+ stylesDef = styleDefinition.styles;
+
+ // Builds the StyleText.
+ var stylesText = ( styleDefinition.attributes && styleDefinition.attributes[ 'style' ] ) || '',
+ specialStylesText = '';
+
+ if ( stylesText.length )
+ stylesText = stylesText.replace( semicolonFixRegex, ';' );
+
+ for ( var style in stylesDef )
+ {
+ var styleVal = stylesDef[ style ],
+ text = ( style + ':' + styleVal ).replace( semicolonFixRegex, ';' );
+
+ // Some browsers don't support 'inherit' property value, leave them intact. (#5242)
+ if ( styleVal == 'inherit' )
+ specialStylesText += text;
+ else
+ stylesText += text;
+ }
+
+ // Browsers make some changes to the style when applying them. So, here
+ // we normalize it to the browser format.
+ if ( stylesText.length )
+ stylesText = normalizeCssText( stylesText );
+
+ stylesText += specialStylesText;
+
+ // Return it, saving it to the next request.
+ return ( styleDefinition._ST = stylesText );
+ };
+
+ // Gets the parent element which blocks the styling for an element. This
+ // can be done through read-only elements (contenteditable=false) or
+ // elements with the "data-nostyle" attribute.
+ function getUnstylableParent( element )
+ {
+ var unstylable,
+ editable;
+
+ while ( ( element = element.getParent() ) )
+ {
+ if ( element.getName() == 'body' )
+ break;
+
+ if ( element.getAttribute( 'data-nostyle' ) )
+ unstylable = element;
+ else if ( !editable )
+ {
+ var contentEditable = element.getAttribute( 'contentEditable' );
+
+ if ( contentEditable == 'false' )
+ unstylable = element;
+ else if ( contentEditable == 'true' )
+ editable = 1;
+ }
+ }
+
+ return unstylable;
+ }
+
+ function applyInlineStyle( range )
+ {
+ var document = range.document;
+
+ if ( range.collapsed )
+ {
+ // Create the element to be inserted in the DOM.
+ var collapsedElement = getElement( this, document );
+
+ // Insert the empty element into the DOM at the range position.
+ range.insertNode( collapsedElement );
+
+ // Place the selection right inside the empty element.
+ range.moveToPosition( collapsedElement, CKEDITOR.POSITION_BEFORE_END );
+
+ return;
+ }
+
+ var elementName = this.element;
+ var def = this._.definition;
+ var isUnknownElement;
+
+ // Indicates that fully selected read-only elements are to be included in the styling range.
+ var ignoreReadonly = def.ignoreReadonly,
+ includeReadonly = ignoreReadonly || def.includeReadonly;
+
+ // If the read-only inclusion is not available in the definition, try
+ // to get it from the document data.
+ if ( includeReadonly == undefined )
+ includeReadonly = document.getCustomData( 'cke_includeReadonly' );
+
+ // Get the DTD definition for the element. Defaults to "span".
+ var dtd = CKEDITOR.dtd[ elementName ] || ( isUnknownElement = true, CKEDITOR.dtd.span );
+
+ // Expand the range.
+ range.enlarge( CKEDITOR.ENLARGE_ELEMENT, 1 );
+ range.trim();
+
+ // Get the first node to be processed and the last, which concludes the
+ // processing.
+ var boundaryNodes = range.createBookmark(),
+ firstNode = boundaryNodes.startNode,
+ lastNode = boundaryNodes.endNode;
+
+ var currentNode = firstNode;
+
+ var styleRange;
+
+ if ( !ignoreReadonly )
+ {
+ // Check if the boundaries are inside non stylable elements.
+ var firstUnstylable = getUnstylableParent( firstNode ),
+ lastUnstylable = getUnstylableParent( lastNode );
+
+ // If the first element can't be styled, we'll start processing right
+ // after its unstylable root.
+ if ( firstUnstylable )
+ currentNode = firstUnstylable.getNextSourceNode( true );
+
+ // If the last element can't be styled, we'll stop processing on its
+ // unstylable root.
+ if ( lastUnstylable )
+ lastNode = lastUnstylable;
+ }
+
+ // Do nothing if the current node now follows the last node to be processed.
+ if ( currentNode.getPosition( lastNode ) == CKEDITOR.POSITION_FOLLOWING )
+ currentNode = 0;
+
+ while ( currentNode )
+ {
+ var applyStyle = false;
+
+ if ( currentNode.equals( lastNode ) )
+ {
+ currentNode = null;
+ applyStyle = true;
+ }
+ else
+ {
+ var nodeType = currentNode.type;
+ var nodeName = nodeType == CKEDITOR.NODE_ELEMENT ? currentNode.getName() : null;
+ var nodeIsReadonly = nodeName && ( currentNode.getAttribute( 'contentEditable' ) == 'false' );
+ var nodeIsNoStyle = nodeName && currentNode.getAttribute( 'data-nostyle' );
+
+ if ( nodeName && currentNode.data( 'cke-bookmark' ) )
+ {
+ currentNode = currentNode.getNextSourceNode( true );
+ continue;
+ }
+
+ // Check if the current node can be a child of the style element.
+ if ( !nodeName || ( dtd[ nodeName ]
+ && !nodeIsNoStyle
+ && ( !nodeIsReadonly || includeReadonly )
+ && ( currentNode.getPosition( lastNode ) | CKEDITOR.POSITION_PRECEDING | CKEDITOR.POSITION_IDENTICAL | CKEDITOR.POSITION_IS_CONTAINED ) == ( CKEDITOR.POSITION_PRECEDING + CKEDITOR.POSITION_IDENTICAL + CKEDITOR.POSITION_IS_CONTAINED )
+ && ( !def.childRule || def.childRule( currentNode ) ) ) )
+ {
+ var currentParent = currentNode.getParent();
+
+ // Check if the style element can be a child of the current
+ // node parent or if the element is not defined in the DTD.
+ if ( currentParent
+ && ( ( currentParent.getDtd() || CKEDITOR.dtd.span )[ elementName ] || isUnknownElement )
+ && ( !def.parentRule || def.parentRule( currentParent ) ) )
+ {
+ // This node will be part of our range, so if it has not
+ // been started, place its start right before the node.
+ // In the case of an element node, it will be included
+ // only if it is entirely inside the range.
+ if ( !styleRange && ( !nodeName || !CKEDITOR.dtd.$removeEmpty[ nodeName ] || ( currentNode.getPosition( lastNode ) | CKEDITOR.POSITION_PRECEDING | CKEDITOR.POSITION_IDENTICAL | CKEDITOR.POSITION_IS_CONTAINED ) == ( CKEDITOR.POSITION_PRECEDING + CKEDITOR.POSITION_IDENTICAL + CKEDITOR.POSITION_IS_CONTAINED ) ) )
+ {
+ styleRange = new CKEDITOR.dom.range( document );
+ styleRange.setStartBefore( currentNode );
+ }
+
+ // Non element nodes, readonly elements, or empty
+ // elements can be added completely to the range.
+ if ( nodeType == CKEDITOR.NODE_TEXT || nodeIsReadonly || ( nodeType == CKEDITOR.NODE_ELEMENT && !currentNode.getChildCount() ) )
+ {
+ var includedNode = currentNode;
+ var parentNode;
+
+ // This node is about to be included completelly, but,
+ // if this is the last node in its parent, we must also
+ // check if the parent itself can be added completelly
+ // to the range, otherwise apply the style immediately.
+ while ( ( applyStyle = !includedNode.getNext( notBookmark ) )
+ && ( parentNode = includedNode.getParent(), dtd[ parentNode.getName() ] )
+ && ( parentNode.getPosition( firstNode ) | CKEDITOR.POSITION_FOLLOWING | CKEDITOR.POSITION_IDENTICAL | CKEDITOR.POSITION_IS_CONTAINED ) == ( CKEDITOR.POSITION_FOLLOWING + CKEDITOR.POSITION_IDENTICAL + CKEDITOR.POSITION_IS_CONTAINED )
+ && ( !def.childRule || def.childRule( parentNode ) ) )
+ {
+ includedNode = parentNode;
+ }
+
+ styleRange.setEndAfter( includedNode );
+
+ }
+ }
+ else
+ applyStyle = true;
+ }
+ else
+ applyStyle = true;
+
+ // Get the next node to be processed.
+ currentNode = currentNode.getNextSourceNode( nodeIsNoStyle || nodeIsReadonly );
+ }
+
+ // Apply the style if we have something to which apply it.
+ if ( applyStyle && styleRange && !styleRange.collapsed )
+ {
+ // Build the style element, based on the style object definition.
+ var styleNode = getElement( this, document ),
+ styleHasAttrs = styleNode.hasAttributes();
+
+ // Get the element that holds the entire range.
+ var parent = styleRange.getCommonAncestor();
+
+ var removeList = {
+ styles : {},
+ attrs : {},
+ // Styles cannot be removed.
+ blockedStyles : {},
+ // Attrs cannot be removed.
+ blockedAttrs : {}
+ };
+
+ var attName, styleName, value;
+
+ // Loop through the parents, removing the redundant attributes
+ // from the element to be applied.
+ while ( styleNode && parent )
+ {
+ if ( parent.getName() == elementName )
+ {
+ for ( attName in def.attributes )
+ {
+ if ( removeList.blockedAttrs[ attName ] || !( value = parent.getAttribute( styleName ) ) )
+ continue;
+
+ if ( styleNode.getAttribute( attName ) == value )
+ removeList.attrs[ attName ] = 1;
+ else
+ removeList.blockedAttrs[ attName ] = 1;
+ }
+
+ for ( styleName in def.styles )
+ {
+ if ( removeList.blockedStyles[ styleName ] || !( value = parent.getStyle( styleName ) ) )
+ continue;
+
+ if ( styleNode.getStyle( styleName ) == value )
+ removeList.styles[ styleName ] = 1;
+ else
+ removeList.blockedStyles[ styleName ] = 1;
+ }
+ }
+
+ parent = parent.getParent();
+ }
+
+ for ( attName in removeList.attrs )
+ styleNode.removeAttribute( attName );
+
+ for ( styleName in removeList.styles )
+ styleNode.removeStyle( styleName );
+
+ if ( styleHasAttrs && !styleNode.hasAttributes() )
+ styleNode = null;
+
+ if ( styleNode )
+ {
+ // Move the contents of the range to the style element.
+ styleRange.extractContents().appendTo( styleNode );
+
+ // Here we do some cleanup, removing all duplicated
+ // elements from the style element.
+ removeFromInsideElement( this, styleNode );
+
+ // Insert it into the range position (it is collapsed after
+ // extractContents.
+ styleRange.insertNode( styleNode );
+
+ // Let's merge our new style with its neighbors, if possible.
+ styleNode.mergeSiblings();
+
+ // As the style system breaks text nodes constantly, let's normalize
+ // things for performance.
+ // With IE, some paragraphs get broken when calling normalize()
+ // repeatedly. Also, for IE, we must normalize body, not documentElement.
+ // IE is also known for having a "crash effect" with normalize().
+ // We should try to normalize with IE too in some way, somewhere.
+ if ( !CKEDITOR.env.ie )
+ styleNode.$.normalize();
+ }
+ // Style already inherit from parents, left just to clear up any internal overrides. (#5931)
+ else
+ {
+ styleNode = new CKEDITOR.dom.element( 'span' );
+ styleRange.extractContents().appendTo( styleNode );
+ styleRange.insertNode( styleNode );
+ removeFromInsideElement( this, styleNode );
+ styleNode.remove( true );
+ }
+
+ // Style applied, let's release the range, so it gets
+ // re-initialization in the next loop.
+ styleRange = null;
+ }
+ }
+
+ // Remove the bookmark nodes.
+ range.moveToBookmark( boundaryNodes );
+
+ // Minimize the result range to exclude empty text nodes. (#5374)
+ range.shrink( CKEDITOR.SHRINK_TEXT );
+ }
+
+ function removeInlineStyle( range )
+ {
+ /*
+ * Make sure our range has included all "collpased" parent inline nodes so
+ * that our operation logic can be simpler.
+ */
+ range.enlarge( CKEDITOR.ENLARGE_ELEMENT, 1 );
+
+ var bookmark = range.createBookmark(),
+ startNode = bookmark.startNode;
+
+ if ( range.collapsed )
+ {
+
+ var startPath = new CKEDITOR.dom.elementPath( startNode.getParent() ),
+ // The topmost element in elementspatch which we should jump out of.
+ boundaryElement;
+
+
+ for ( var i = 0, element ; i < startPath.elements.length
+ && ( element = startPath.elements[i] ) ; i++ )
+ {
+ /*
+ * 1. If it's collaped inside text nodes, try to remove the style from the whole element.
+ *
+ * 2. Otherwise if it's collapsed on element boundaries, moving the selection
+ * outside the styles instead of removing the whole tag,
+ * also make sure other inner styles were well preserverd.(#3309)
+ */
+ if ( element == startPath.block || element == startPath.blockLimit )
+ break;
+
+ if ( this.checkElementRemovable( element ) )
+ {
+ var isStart;
+
+ if ( range.collapsed && (
+ range.checkBoundaryOfElement( element, CKEDITOR.END ) ||
+ ( isStart = range.checkBoundaryOfElement( element, CKEDITOR.START ) ) ) )
+ {
+ boundaryElement = element;
+ boundaryElement.match = isStart ? 'start' : 'end';
+ }
+ else
+ {
+ /*
+ * Before removing the style node, there may be a sibling to the style node
+ * that's exactly the same to the one to be removed. To the user, it makes
+ * no difference that they're separate entities in the DOM tree. So, merge
+ * them before removal.
+ */
+ element.mergeSiblings();
+ if ( element.getName() == this.element )
+ removeFromElement( this, element );
+ else
+ removeOverrides( element, getOverrides( this )[ element.getName() ] );
+ }
+ }
+ }
+
+ // Re-create the style tree after/before the boundary element,
+ // the replication start from bookmark start node to define the
+ // new range.
+ if ( boundaryElement )
+ {
+ var clonedElement = startNode;
+ for ( i = 0 ;; i++ )
+ {
+ var newElement = startPath.elements[ i ];
+ if ( newElement.equals( boundaryElement ) )
+ break;
+ // Avoid copying any matched element.
+ else if ( newElement.match )
+ continue;
+ else
+ newElement = newElement.clone();
+ newElement.append( clonedElement );
+ clonedElement = newElement;
+ }
+ clonedElement[ boundaryElement.match == 'start' ?
+ 'insertBefore' : 'insertAfter' ]( boundaryElement );
+ }
+ }
+ else
+ {
+ /*
+ * Now our range isn't collapsed. Lets walk from the start node to the end
+ * node via DFS and remove the styles one-by-one.
+ */
+ var endNode = bookmark.endNode,
+ me = this;
+
+ /*
+ * Find out the style ancestor that needs to be broken down at startNode
+ * and endNode.
+ */
+ function breakNodes()
+ {
+ var startPath = new CKEDITOR.dom.elementPath( startNode.getParent() ),
+ endPath = new CKEDITOR.dom.elementPath( endNode.getParent() ),
+ breakStart = null,
+ breakEnd = null;
+ for ( var i = 0 ; i < startPath.elements.length ; i++ )
+ {
+ var element = startPath.elements[ i ];
+
+ if ( element == startPath.block || element == startPath.blockLimit )
+ break;
+
+ if ( me.checkElementRemovable( element ) )
+ breakStart = element;
+ }
+ for ( i = 0 ; i < endPath.elements.length ; i++ )
+ {
+ element = endPath.elements[ i ];
+
+ if ( element == endPath.block || element == endPath.blockLimit )
+ break;
+
+ if ( me.checkElementRemovable( element ) )
+ breakEnd = element;
+ }
+
+ if ( breakEnd )
+ endNode.breakParent( breakEnd );
+ if ( breakStart )
+ startNode.breakParent( breakStart );
+ }
+ breakNodes();
+
+ // Now, do the DFS walk.
+ var currentNode = startNode;
+ while ( !currentNode.equals( endNode ) )
+ {
+ /*
+ * Need to get the next node first because removeFromElement() can remove
+ * the current node from DOM tree.
+ */
+ var nextNode = currentNode.getNextSourceNode();
+ if ( currentNode.type == CKEDITOR.NODE_ELEMENT && this.checkElementRemovable( currentNode ) )
+ {
+ // Remove style from element or overriding element.
+ if ( currentNode.getName() == this.element )
+ removeFromElement( this, currentNode );
+ else
+ removeOverrides( currentNode, getOverrides( this )[ currentNode.getName() ] );
+
+ /*
+ * removeFromElement() may have merged the next node with something before
+ * the startNode via mergeSiblings(). In that case, the nextNode would
+ * contain startNode and we'll have to call breakNodes() again and also
+ * reassign the nextNode to something after startNode.
+ */
+ if ( nextNode.type == CKEDITOR.NODE_ELEMENT && nextNode.contains( startNode ) )
+ {
+ breakNodes();
+ nextNode = startNode.getNext();
+ }
+ }
+ currentNode = nextNode;
+ }
+ }
+
+ range.moveToBookmark( bookmark );
+ }
+
+ function applyObjectStyle( range )
+ {
+ var root = range.getCommonAncestor( true, true ),
+ element = root.getAscendant( this.element, true );
+ element && !element.isReadOnly() && setupElement( element, this );
+ }
+
+ function removeObjectStyle( range )
+ {
+ var root = range.getCommonAncestor( true, true ),
+ element = root.getAscendant( this.element, true );
+
+ if ( !element )
+ return;
+
+ var style = this,
+ def = style._.definition,
+ attributes = def.attributes;
+
+ // Remove all defined attributes.
+ if ( attributes )
+ {
+ for ( var att in attributes )
+ {
+ element.removeAttribute( att, attributes[ att ] );
+ }
+ }
+
+ // Assign all defined styles.
+ if ( def.styles )
+ {
+ for ( var i in def.styles )
+ {
+ if ( !def.styles.hasOwnProperty( i ) )
+ continue;
+
+ element.removeStyle( i );
+ }
+ }
+ }
+
+ function applyBlockStyle( range )
+ {
+ // Serializible bookmarks is needed here since
+ // elements may be merged.
+ var bookmark = range.createBookmark( true );
+
+ var iterator = range.createIterator();
+ iterator.enforceRealBlocks = true;
+
+ // make recognize <br /> tag as a separator in ENTER_BR mode (#5121)
+ if ( this._.enterMode )
+ iterator.enlargeBr = ( this._.enterMode != CKEDITOR.ENTER_BR );
+
+ var block;
+ var doc = range.document;
+ var previousPreBlock;
+
+ while ( ( block = iterator.getNextParagraph() ) ) // Only one =
+ {
+ if ( !block.isReadOnly() )
+ {
+ var newBlock = getElement( this, doc, block );
+ replaceBlock( block, newBlock );
+ }
+ }
+
+ range.moveToBookmark( bookmark );
+ }
+
+ function removeBlockStyle( range )
+ {
+ // Serializible bookmarks is needed here since
+ // elements may be merged.
+ var bookmark = range.createBookmark( 1 );
+
+ var iterator = range.createIterator();
+ iterator.enforceRealBlocks = true;
+ iterator.enlargeBr = this._.enterMode != CKEDITOR.ENTER_BR;
+
+ var block;
+ while ( ( block = iterator.getNextParagraph() ) )
+ {
+ if ( this.checkElementRemovable( block ) )
+ {
+ // <pre> get special treatment.
+ if ( block.is( 'pre' ) )
+ {
+ var newBlock = this._.enterMode == CKEDITOR.ENTER_BR ?
+ null : range.document.createElement(
+ this._.enterMode == CKEDITOR.ENTER_P ? 'p' : 'div' );
+
+ newBlock && block.copyAttributes( newBlock );
+ replaceBlock( block, newBlock );
+ }
+ else
+ removeFromElement( this, block, 1 );
+ }
+ }
+
+ range.moveToBookmark( bookmark );
+ }
+
+ // Replace the original block with new one, with special treatment
+ // for <pre> blocks to make sure content format is well preserved, and merging/splitting adjacent
+ // when necessary.(#3188)
+ function replaceBlock( block, newBlock )
+ {
+ // Block is to be removed, create a temp element to
+ // save contents.
+ var removeBlock = !newBlock;
+ if ( removeBlock )
+ {
+ newBlock = block.getDocument().createElement( 'div' );
+ block.copyAttributes( newBlock );
+ }
+
+ var newBlockIsPre = newBlock && newBlock.is( 'pre' );
+ var blockIsPre = block.is( 'pre' );
+
+ var isToPre = newBlockIsPre && !blockIsPre;
+ var isFromPre = !newBlockIsPre && blockIsPre;
+
+ if ( isToPre )
+ newBlock = toPre( block, newBlock );
+ else if ( isFromPre )
+ // Split big <pre> into pieces before start to convert.
+ newBlock = fromPres( removeBlock ?
+ [ block.getHtml() ] : splitIntoPres( block ), newBlock );
+ else
+ block.moveChildren( newBlock );
+
+ newBlock.replace( block );
+
+ if ( newBlockIsPre )
+ {
+ // Merge previous <pre> blocks.
+ mergePre( newBlock );
+ }
+ else if ( removeBlock )
+ removeNoAttribsElement( newBlock );
+ }
+
+ /**
+ * Merge a <pre> block with a previous sibling if available.
+ */
+ function mergePre( preBlock )
+ {
+ var previousBlock;
+ if ( !( ( previousBlock = preBlock.getPrevious( nonWhitespaces ) )
+ && previousBlock.is
+ && previousBlock.is( 'pre') ) )
+ return;
+
+ // Merge the previous <pre> block contents into the current <pre>
+ // block.
+ //
+ // Another thing to be careful here is that currentBlock might contain
+ // a '\n' at the beginning, and previousBlock might contain a '\n'
+ // towards the end. These new lines are not normally displayed but they
+ // become visible after merging.
+ var mergedHtml = replace( previousBlock.getHtml(), /\n$/, '' ) + '\n\n' +
+ replace( preBlock.getHtml(), /^\n/, '' ) ;
+
+ // Krugle: IE normalizes innerHTML from <pre>, breaking whitespaces.
+ if ( CKEDITOR.env.ie )
+ preBlock.$.outerHTML = '<pre>' + mergedHtml + '</pre>';
+ else
+ preBlock.setHtml( mergedHtml );
+
+ previousBlock.remove();
+ }
+
+ /**
+ * Split into multiple <pre> blocks separated by double line-break.
+ * @param preBlock
+ */
+ function splitIntoPres( preBlock )
+ {
+ // Exclude the ones at header OR at tail,
+ // and ignore bookmark content between them.
+ var duoBrRegex = /(\S\s*)\n(?:\s|(<span[^>]+data-cke-bookmark.*?\/span>))*\n(?!$)/gi,
+ blockName = preBlock.getName(),
+ splitedHtml = replace( preBlock.getOuterHtml(),
+ duoBrRegex,
+ function( match, charBefore, bookmark )
+ {
+ return charBefore + '</pre>' + bookmark + '<pre>';
+ } );
+
+ var pres = [];
+ splitedHtml.replace( /<pre\b.*?>([\s\S]*?)<\/pre>/gi, function( match, preContent ){
+ pres.push( preContent );
+ } );
+ return pres;
+ }
+
+ // Wrapper function of String::replace without considering of head/tail bookmarks nodes.
+ function replace( str, regexp, replacement )
+ {
+ var headBookmark = '',
+ tailBookmark = '';
+
+ str = str.replace( /(^<span[^>]+data-cke-bookmark.*?\/span>)|(<span[^>]+data-cke-bookmark.*?\/span>$)/gi,
+ function( str, m1, m2 ){
+ m1 && ( headBookmark = m1 );
+ m2 && ( tailBookmark = m2 );
+ return '';
+ } );
+ return headBookmark + str.replace( regexp, replacement ) + tailBookmark;
+ }
+
+ /**
+ * Converting a list of <pre> into blocks with format well preserved.
+ */
+ function fromPres( preHtmls, newBlock )
+ {
+ var docFrag;
+ if ( preHtmls.length > 1 )
+ docFrag = new CKEDITOR.dom.documentFragment( newBlock.getDocument() );
+
+ for ( var i = 0 ; i < preHtmls.length ; i++ )
+ {
+ var blockHtml = preHtmls[ i ];
+
+ // 1. Trim the first and last line-breaks immediately after and before <pre>,
+ // they're not visible.
+ blockHtml = blockHtml.replace( /(\r\n|\r)/g, '\n' ) ;
+ blockHtml = replace( blockHtml, /^[ \t]*\n/, '' ) ;
+ blockHtml = replace( blockHtml, /\n$/, '' ) ;
+ // 2. Convert spaces or tabs at the beginning or at the end to
+ blockHtml = replace( blockHtml, /^[ \t]+|[ \t]+$/g, function( match, offset, s )
+ {
+ if ( match.length == 1 ) // one space, preserve it
+ return ' ' ;
+ else if ( !offset ) // beginning of block
+ return CKEDITOR.tools.repeat( ' ', match.length - 1 ) + ' ';
+ else // end of block
+ return ' ' + CKEDITOR.tools.repeat( ' ', match.length - 1 );
+ } ) ;
+
+ // 3. Convert \n to <BR>.
+ // 4. Convert contiguous (i.e. non-singular) spaces or tabs to
+ blockHtml = blockHtml.replace( /\n/g, '<br>' ) ;
+ blockHtml = blockHtml.replace( /[ \t]{2,}/g,
+ function ( match )
+ {
+ return CKEDITOR.tools.repeat( ' ', match.length - 1 ) + ' ' ;
+ } ) ;
+
+ if ( docFrag )
+ {
+ var newBlockClone = newBlock.clone();
+ newBlockClone.setHtml( blockHtml );
+ docFrag.append( newBlockClone );
+ }
+ else
+ newBlock.setHtml( blockHtml );
+ }
+
+ return docFrag || newBlock;
+ }
+
+ /**
+ * Converting from a non-PRE block to a PRE block in formatting operations.
+ */
+ function toPre( block, newBlock )
+ {
+ var bogus = block.getBogus();
+ bogus && bogus.remove();
+
+ // First trim the block content.
+ var preHtml = block.getHtml();
+
+ // 1. Trim head/tail spaces, they're not visible.
+ preHtml = replace( preHtml, /(?:^[ \t\n\r]+)|(?:[ \t\n\r]+$)/g, '' );
+ // 2. Delete ANSI whitespaces immediately before and after <BR> because
+ // they are not visible.
+ preHtml = preHtml.replace( /[ \t\r\n]*(<br[^>]*>)[ \t\r\n]*/gi, '$1' );
+ // 3. Compress other ANSI whitespaces since they're only visible as one
+ // single space previously.
+ // 4. Convert to spaces since is no longer needed in <PRE>.
+ preHtml = preHtml.replace( /([ \t\n\r]+| )/g, ' ' );
+ // 5. Convert any <BR /> to \n. This must not be done earlier because
+ // the \n would then get compressed.
+ preHtml = preHtml.replace( /<br\b[^>]*>/gi, '\n' );
+
+ // Krugle: IE normalizes innerHTML to <pre>, breaking whitespaces.
+ if ( CKEDITOR.env.ie )
+ {
+ var temp = block.getDocument().createElement( 'div' );
+ temp.append( newBlock );
+ newBlock.$.outerHTML = '<pre>' + preHtml + '</pre>';
+ newBlock.copyAttributes( temp.getFirst() );
+ newBlock = temp.getFirst().remove();
+ }
+ else
+ newBlock.setHtml( preHtml );
+
+ return newBlock;
+ }
+
+ // Removes a style from an element itself, don't care about its subtree.
+ function removeFromElement( style, element )
+ {
+ var def = style._.definition,
+ attributes = def.attributes,
+ styles = def.styles,
+ overrides = getOverrides( style )[ element.getName() ],
+ // If the style is only about the element itself, we have to remove the element.
+ removeEmpty = CKEDITOR.tools.isEmpty( attributes ) && CKEDITOR.tools.isEmpty( styles );
+
+ // Remove definition attributes/style from the elemnt.
+ for ( var attName in attributes )
+ {
+ // The 'class' element value must match (#1318).
+ if ( ( attName == 'class' || style._.definition.fullMatch )
+ && element.getAttribute( attName ) != normalizeProperty( attName, attributes[ attName ] ) )
+ continue;
+ removeEmpty = element.hasAttribute( attName );
+ element.removeAttribute( attName );
+ }
+
+ for ( var styleName in styles )
+ {
+ // Full match style insist on having fully equivalence. (#5018)
+ if ( style._.definition.fullMatch
+ && element.getStyle( styleName ) != normalizeProperty( styleName, styles[ styleName ], true ) )
+ continue;
+
+ removeEmpty = removeEmpty || !!element.getStyle( styleName );
+ element.removeStyle( styleName );
+ }
+
+ // Remove overrides, but don't remove the element if it's a block element
+ removeOverrides( element, overrides, blockElements[ element.getName() ] ) ;
+
+ if ( removeEmpty )
+ {
+ !CKEDITOR.dtd.$block[ element.getName() ] || style._.enterMode == CKEDITOR.ENTER_BR && !element.hasAttributes() ?
+ removeNoAttribsElement( element ) :
+ element.renameNode( style._.enterMode == CKEDITOR.ENTER_P ? 'p' : 'div' );
+ }
+ }
+
+ // Removes a style from inside an element.
+ function removeFromInsideElement( style, element )
+ {
+ var def = style._.definition,
+ attribs = def.attributes,
+ styles = def.styles,
+ overrides = getOverrides( style ),
+ innerElements = element.getElementsByTag( style.element );
+
+ for ( var i = innerElements.count(); --i >= 0 ; )
+ removeFromElement( style, innerElements.getItem( i ) );
+
+ // Now remove any other element with different name that is
+ // defined to be overriden.
+ for ( var overrideElement in overrides )
+ {
+ if ( overrideElement != style.element )
+ {
+ innerElements = element.getElementsByTag( overrideElement ) ;
+ for ( i = innerElements.count() - 1 ; i >= 0 ; i-- )
+ {
+ var innerElement = innerElements.getItem( i );
+ removeOverrides( innerElement, overrides[ overrideElement ] ) ;
+ }
+ }
+ }
+ }
+
+ /**
+ * Remove overriding styles/attributes from the specific element.
+ * Note: Remove the element if no attributes remain.
+ * @param {Object} element
+ * @param {Object} overrides
+ * @param {Boolean} Don't remove the element
+ */
+ function removeOverrides( element, overrides, dontRemove )
+ {
+ var attributes = overrides && overrides.attributes ;
+
+ if ( attributes )
+ {
+ for ( var i = 0 ; i < attributes.length ; i++ )
+ {
+ var attName = attributes[i][0], actualAttrValue ;
+
+ if ( ( actualAttrValue = element.getAttribute( attName ) ) )
+ {
+ var attValue = attributes[i][1] ;
+
+ // Remove the attribute if:
+ // - The override definition value is null ;
+ // - The override definition valie is a string that
+ // matches the attribute value exactly.
+ // - The override definition value is a regex that
+ // has matches in the attribute value.
+ if ( attValue === null ||
+ ( attValue.test && attValue.test( actualAttrValue ) ) ||
+ ( typeof attValue == 'string' && actualAttrValue == attValue ) )
+ element.removeAttribute( attName ) ;
+ }
+ }
+ }
+
+ if ( !dontRemove )
+ removeNoAttribsElement( element );
+ }
+
+ // If the element has no more attributes, remove it.
+ function removeNoAttribsElement( element )
+ {
+ // If no more attributes remained in the element, remove it,
+ // leaving its children.
+ if ( !element.hasAttributes() )
+ {
+ if ( CKEDITOR.dtd.$block[ element.getName() ] )
+ {
+ var previous = element.getPrevious( nonWhitespaces ),
+ next = element.getNext( nonWhitespaces );
+
+ if ( previous && ( previous.type == CKEDITOR.NODE_TEXT || !previous.isBlockBoundary( { br : 1 } ) ) )
+ element.append( 'br', 1 );
+ if ( next && ( next.type == CKEDITOR.NODE_TEXT || !next.isBlockBoundary( { br : 1 } ) ) )
+ element.append( 'br' );
+
+ element.remove( true );
+ }
+ else
+ {
+ // Removing elements may open points where merging is possible,
+ // so let's cache the first and last nodes for later checking.
+ var firstChild = element.getFirst();
+ var lastChild = element.getLast();
+
+ element.remove( true );
+
+ if ( firstChild )
+ {
+ // Check the cached nodes for merging.
+ firstChild.type == CKEDITOR.NODE_ELEMENT && firstChild.mergeSiblings();
+
+ if ( lastChild && !firstChild.equals( lastChild )
+ && lastChild.type == CKEDITOR.NODE_ELEMENT )
+ lastChild.mergeSiblings();
+ }
+
+ }
+ }
+ }
+
+ function getElement( style, targetDocument, element )
+ {
+ var el,
+ def = style._.definition,
+ elementName = style.element;
+
+ // The "*" element name will always be a span for this function.
+ if ( elementName == '*' )
+ elementName = 'span';
+
+ // Create the element.
+ el = new CKEDITOR.dom.element( elementName, targetDocument );
+
+ // #6226: attributes should be copied before the new ones are applied
+ if ( element )
+ element.copyAttributes( el );
+
+ el = setupElement( el, style );
+
+ // Avoid ID duplication.
+ if ( targetDocument.getCustomData( 'doc_processing_style' ) && el.hasAttribute( 'id' ) )
+ el.removeAttribute( 'id' );
+ else
+ targetDocument.setCustomData( 'doc_processing_style', 1 );
+
+ return el;
+ }
+
+ function setupElement( el, style )
+ {
+ var def = style._.definition,
+ attributes = def.attributes,
+ styles = CKEDITOR.style.getStyleText( def );
+
+ // Assign all defined attributes.
+ if ( attributes )
+ {
+ for ( var att in attributes )
+ {
+ el.setAttribute( att, attributes[ att ] );
+ }
+ }
+
+ // Assign all defined styles.
+ if( styles )
+ el.setAttribute( 'style', styles );
+
+ return el;
+ }
+
+ function replaceVariables( list, variablesValues )
+ {
+ for ( var item in list )
+ {
+ list[ item ] = list[ item ].replace( varRegex, function( match, varName )
+ {
+ return variablesValues[ varName ];
+ });
+ }
+ }
+
+ // Returns an object that can be used for style matching comparison.
+ // Attributes names and values are all lowercased, and the styles get
+ // merged with the style attribute.
+ function getAttributesForComparison( styleDefinition )
+ {
+ // If we have already computed it, just return it.
+ var attribs = styleDefinition._AC;
+ if ( attribs )
+ return attribs;
+
+ attribs = {};
+
+ var length = 0;
+
+ // Loop through all defined attributes.
+ var styleAttribs = styleDefinition.attributes;
+ if ( styleAttribs )
+ {
+ for ( var styleAtt in styleAttribs )
+ {
+ length++;
+ attribs[ styleAtt ] = styleAttribs[ styleAtt ];
+ }
+ }
+
+ // Includes the style definitions.
+ var styleText = CKEDITOR.style.getStyleText( styleDefinition );
+ if ( styleText )
+ {
+ if ( !attribs[ 'style' ] )
+ length++;
+ attribs[ 'style' ] = styleText;
+ }
+
+ // Appends the "length" information to the object.
+ attribs._length = length;
+
+ // Return it, saving it to the next request.
+ return ( styleDefinition._AC = attribs );
+ }
+
+ /**
+ * Get the the collection used to compare the elements and attributes,
+ * defined in this style overrides, with other element. All information in
+ * it is lowercased.
+ * @param {CKEDITOR.style} style
+ */
+ function getOverrides( style )
+ {
+ if ( style._.overrides )
+ return style._.overrides;
+
+ var overrides = ( style._.overrides = {} ),
+ definition = style._.definition.overrides;
+
+ if ( definition )
+ {
+ // The override description can be a string, object or array.
+ // Internally, well handle arrays only, so transform it if needed.
+ if ( !CKEDITOR.tools.isArray( definition ) )
+ definition = [ definition ];
+
+ // Loop through all override definitions.
+ for ( var i = 0 ; i < definition.length ; i++ )
+ {
+ var override = definition[i];
+ var elementName;
+ var overrideEl;
+ var attrs;
+
+ // If can be a string with the element name.
+ if ( typeof override == 'string' )
+ elementName = override.toLowerCase();
+ // Or an object.
+ else
+ {
+ elementName = override.element ? override.element.toLowerCase() : style.element;
+ attrs = override.attributes;
+ }
+
+ // We can have more than one override definition for the same
+ // element name, so we attempt to simply append information to
+ // it if it already exists.
+ overrideEl = overrides[ elementName ] || ( overrides[ elementName ] = {} );
+
+ if ( attrs )
+ {
+ // The returning attributes list is an array, because we
+ // could have different override definitions for the same
+ // attribute name.
+ var overrideAttrs = ( overrideEl.attributes = overrideEl.attributes || new Array() );
+ for ( var attName in attrs )
+ {
+ // Each item in the attributes array is also an array,
+ // where [0] is the attribute name and [1] is the
+ // override value.
+ overrideAttrs.push( [ attName.toLowerCase(), attrs[ attName ] ] );
+ }
+ }
+ }
+ }
+
+ return overrides;
+ }
+
+ // Make the comparison of attribute value easier by standardizing it.
+ function normalizeProperty( name, value, isStyle )
+ {
+ var temp = new CKEDITOR.dom.element( 'span' );
+ temp [ isStyle ? 'setStyle' : 'setAttribute' ]( name, value );
+ return temp[ isStyle ? 'getStyle' : 'getAttribute' ]( name );
+ }
+
+ // Make the comparison of style text easier by standardizing it.
+ function normalizeCssText( unparsedCssText, nativeNormalize )
+ {
+ var styleText;
+ if ( nativeNormalize !== false )
+ {
+ // Injects the style in a temporary span object, so the browser parses it,
+ // retrieving its final format.
+ var temp = new CKEDITOR.dom.element( 'span' );
+ temp.setAttribute( 'style', unparsedCssText );
+ styleText = temp.getAttribute( 'style' ) || '';
+ }
+ else
+ styleText = unparsedCssText;
+
+ // Normalize font-family property, ignore quotes and being case insensitive. (#7322)
+ // http://www.w3.org/TR/css3-fonts/#font-family-the-font-family-property
+ styleText = styleText.replace( /(font-family:)(.*?)(?=;|$)/, function ( match, prop, val )
+ {
+ var names = val.split( ',' );
+ for ( var i = 0; i < names.length; i++ )
+ names[ i ] = CKEDITOR.tools.trim( names[ i ].replace( /["']/g, '' ) );
+ return prop + names.join( ',' );
+ });
+
+ // Shrinking white-spaces around colon and semi-colon (#4147).
+ // Compensate tail semi-colon.
+ return styleText.replace( /\s*([;:])\s*/, '$1' )
+ .replace( /([^\s;])$/, '$1;')
+ // Trimming spaces after comma(#4107),
+ // remove quotations(#6403),
+ // mostly for differences on "font-family".
+ .replace( /,\s+/g, ',' )
+ .replace( /\"/g,'' )
+ .toLowerCase();
+ }
+
+ // Turn inline style text properties into one hash.
+ function parseStyleText( styleText )
+ {
+ var retval = {};
+ styleText
+ .replace( /"/g, '"' )
+ .replace( /\s*([^ :;]+)\s*:\s*([^;]+)\s*(?=;|$)/g, function( match, name, value )
+ {
+ retval[ name ] = value;
+ } );
+ return retval;
+ }
+
+ /**
+ * Compare two bunch of styles, with the speciality that value 'inherit'
+ * is treated as a wildcard which will match any value.
+ * @param {Object|String} source
+ * @param {Object|String} target
+ */
+ function compareCssText( source, target )
+ {
+ typeof source == 'string' && ( source = parseStyleText( source ) );
+ typeof target == 'string' && ( target = parseStyleText( target ) );
+ for( var name in source )
+ {
+ if ( !( name in target &&
+ ( target[ name ] == source[ name ]
+ || source[ name ] == 'inherit'
+ || target[ name ] == 'inherit' ) ) )
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ function applyStyle( document, remove )
+ {
+ var selection = document.getSelection(),
+ // Bookmark the range so we can re-select it after processing.
+ bookmarks = selection.createBookmarks( 1 ),
+ ranges = selection.getRanges(),
+ func = remove ? this.removeFromRange : this.applyToRange,
+ range;
+
+ var iterator = ranges.createIterator();
+ while ( ( range = iterator.getNextRange() ) )
+ func.call( this, range );
+
+ if ( bookmarks.length == 1 && bookmarks[ 0 ].collapsed )
+ {
+ selection.selectRanges( ranges );
+ document.getById( bookmarks[ 0 ].startNode ).remove();
+ }
+ else
+ selection.selectBookmarks( bookmarks );
+
+ document.removeCustomData( 'doc_processing_style' );
+ }
+})();
+
+CKEDITOR.styleCommand = function( style )
+{
+ this.style = style;
+};
+
+CKEDITOR.styleCommand.prototype.exec = function( editor )
+{
+ editor.focus();
+
+ var doc = editor.document;
+
+ if ( doc )
+ {
+ if ( this.state == CKEDITOR.TRISTATE_OFF )
+ this.style.apply( doc );
+ else if ( this.state == CKEDITOR.TRISTATE_ON )
+ this.style.remove( doc );
+ }
+
+ return !!doc;
+};
+
+/**
+ * Manages styles registration and loading. See also {@link CKEDITOR.config.stylesSet}.
+ * @namespace
+ * @augments CKEDITOR.resourceManager
+ * @constructor
+ * @since 3.2
+ * @example
+ * // The set of styles for the <b>Styles</b> combo
+ * CKEDITOR.stylesSet.add( 'default',
+ * [
+ * // Block Styles
+ * { name : 'Blue Title' , element : 'h3', styles : { 'color' : 'Blue' } },
+ * { name : 'Red Title' , element : 'h3', styles : { 'color' : 'Red' } },
+ *
+ * // Inline Styles
+ * { name : 'Marker: Yellow' , element : 'span', styles : { 'background-color' : 'Yellow' } },
+ * { name : 'Marker: Green' , element : 'span', styles : { 'background-color' : 'Lime' } },
+ *
+ * // Object Styles
+ * {
+ * name : 'Image on Left',
+ * element : 'img',
+ * attributes :
+ * {
+ * 'style' : 'padding: 5px; margin-right: 5px',
+ * 'border' : '2',
+ * 'align' : 'left'
+ * }
+ * }
+ * ]);
+ */
+CKEDITOR.stylesSet = new CKEDITOR.resourceManager( '', 'stylesSet' );
+
+// Backward compatibility (#5025).
+CKEDITOR.addStylesSet = CKEDITOR.tools.bind( CKEDITOR.stylesSet.add, CKEDITOR.stylesSet );
+CKEDITOR.loadStylesSet = function( name, url, callback )
+ {
+ CKEDITOR.stylesSet.addExternal( name, url, '' );
+ CKEDITOR.stylesSet.load( name, callback );
+ };
+
+
+/**
+ * Gets the current styleSet for this instance
+ * @param {Function} callback The function to be called with the styles data.
+ * @example
+ * editor.getStylesSet( function( stylesDefinitions ) {} );
+ */
+CKEDITOR.editor.prototype.getStylesSet = function( callback )
+{
+ if ( !this._.stylesDefinitions )
+ {
+ var editor = this,
+ // Respect the backwards compatible definition entry
+ configStyleSet = editor.config.stylesCombo_stylesSet || editor.config.stylesSet || 'default';
+
+ // #5352 Allow to define the styles directly in the config object
+ if ( configStyleSet instanceof Array )
+ {
+ editor._.stylesDefinitions = configStyleSet;
+ callback( configStyleSet );
+ return;
+ }
+
+ var partsStylesSet = configStyleSet.split( ':' ),
+ styleSetName = partsStylesSet[ 0 ],
+ externalPath = partsStylesSet[ 1 ],
+ pluginPath = CKEDITOR.plugins.registered.styles.path;
+
+ CKEDITOR.stylesSet.addExternal( styleSetName,
+ externalPath ?
+ partsStylesSet.slice( 1 ).join( ':' ) :
+ pluginPath + 'styles/' + styleSetName + '.js', '' );
+
+ CKEDITOR.stylesSet.load( styleSetName, function( stylesSet )
+ {
+ editor._.stylesDefinitions = stylesSet[ styleSetName ];
+ callback( editor._.stylesDefinitions );
+ } ) ;
+ }
+ else
+ callback( this._.stylesDefinitions );
+};
+
+/**
+ * Indicates that fully selected read-only elements will be included when
+ * applying the style (for inline styles only).
+ * @name CKEDITOR.style.includeReadonly
+ * @type Boolean
+ * @default false
+ * @since 3.5
+ */
+
+ /**
+ * Disables inline styling on read-only elements.
+ * @name CKEDITOR.config.disableReadonlyStyling
+ * @type Boolean
+ * @default false
+ * @since 3.5
+ */
+
+/**
+ * The "styles definition set" to use in the editor. They will be used in the
+ * styles combo and the Style selector of the div container. <br>
+ * The styles may be defined in the page containing the editor, or can be
+ * loaded on demand from an external file. In the second case, if this setting
+ * contains only a name, the styles definition file will be loaded from the
+ * "styles" folder inside the styles plugin folder.
+ * Otherwise, this setting has the "name:url" syntax, making it
+ * possible to set the URL from which loading the styles file.<br>
+ * Previously this setting was available as config.stylesCombo_stylesSet<br>
+ * @name CKEDITOR.config.stylesSet
+ * @type String|Array
+ * @default 'default'
+ * @since 3.3
+ * @example
+ * // Load from the styles' styles folder (mystyles.js file).
+ * config.stylesSet = 'mystyles';
+ * @example
+ * // Load from a relative URL.
+ * config.stylesSet = 'mystyles:/editorstyles/styles.js';
+ * @example
+ * // Load from a full URL.
+ * config.stylesSet = 'mystyles:http://www.example.com/editorstyles/styles.js';
+ * @example
+ * // Load from a list of definitions.
+ * config.stylesSet = [
+ * { name : 'Strong Emphasis', element : 'strong' },
+ * { name : 'Emphasis', element : 'em' }, ... ];
+ */
diff --git a/_source/plugins/styles/styles/default.js b/_source/plugins/styles/styles/default.js index 1e8ca6d..4859ea5 100644 --- a/_source/plugins/styles/styles/default.js +++ b/_source/plugins/styles/styles/default.js @@ -1,88 +1,88 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -CKEDITOR.stylesSet.add( 'default', -[ - /* Block Styles */ - - // These styles are already available in the "Format" combo, so they are - // not needed here by default. You may enable them to avoid placing the - // "Format" combo in the toolbar, maintaining the same features. - /* - { name : 'Paragraph' , element : 'p' }, - { name : 'Heading 1' , element : 'h1' }, - { name : 'Heading 2' , element : 'h2' }, - { name : 'Heading 3' , element : 'h3' }, - { name : 'Heading 4' , element : 'h4' }, - { name : 'Heading 5' , element : 'h5' }, - { name : 'Heading 6' , element : 'h6' }, - { name : 'Preformatted Text', element : 'pre' }, - { name : 'Address' , element : 'address' }, - */ - - { name : 'Blue Title' , element : 'h3', styles : { 'color' : 'Blue' } }, - { name : 'Red Title' , element : 'h3', styles : { 'color' : 'Red' } }, - - /* Inline Styles */ - - // These are core styles available as toolbar buttons. You may opt enabling - // some of them in the Styles combo, removing them from the toolbar. - /* - { name : 'Strong' , element : 'strong', overrides : 'b' }, - { name : 'Emphasis' , element : 'em' , overrides : 'i' }, - { name : 'Underline' , element : 'u' }, - { name : 'Strikethrough' , element : 'strike' }, - { name : 'Subscript' , element : 'sub' }, - { name : 'Superscript' , element : 'sup' }, - */ - - { name : 'Marker: Yellow' , element : 'span', styles : { 'background-color' : 'Yellow' } }, - { name : 'Marker: Green' , element : 'span', styles : { 'background-color' : 'Lime' } }, - - { name : 'Big' , element : 'big' }, - { name : 'Small' , element : 'small' }, - { name : 'Typewriter' , element : 'tt' }, - - { name : 'Computer Code' , element : 'code' }, - { name : 'Keyboard Phrase' , element : 'kbd' }, - { name : 'Sample Text' , element : 'samp' }, - { name : 'Variable' , element : 'var' }, - - { name : 'Deleted Text' , element : 'del' }, - { name : 'Inserted Text' , element : 'ins' }, - - { name : 'Cited Work' , element : 'cite' }, - { name : 'Inline Quotation' , element : 'q' }, - - { name : 'Language: RTL' , element : 'span', attributes : { 'dir' : 'rtl' } }, - { name : 'Language: LTR' , element : 'span', attributes : { 'dir' : 'ltr' } }, - - /* Object Styles */ - - { - name : 'Image on Left', - element : 'img', - attributes : - { - 'style' : 'padding: 5px; margin-right: 5px', - 'border' : '2', - 'align' : 'left' - } - }, - - { - name : 'Image on Right', - element : 'img', - attributes : - { - 'style' : 'padding: 5px; margin-left: 5px', - 'border' : '2', - 'align' : 'right' - } - }, - - { name : 'Borderless Table', element : 'table', styles: { 'border-style': 'hidden', 'background-color' : '#E6E6FA' } }, - { name : 'Square Bulleted List', element : 'ul', styles : { 'list-style-type' : 'square' } } -]); +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.stylesSet.add( 'default',
+[
+ /* Block Styles */
+
+ // These styles are already available in the "Format" combo, so they are
+ // not needed here by default. You may enable them to avoid placing the
+ // "Format" combo in the toolbar, maintaining the same features.
+ /*
+ { name : 'Paragraph' , element : 'p' },
+ { name : 'Heading 1' , element : 'h1' },
+ { name : 'Heading 2' , element : 'h2' },
+ { name : 'Heading 3' , element : 'h3' },
+ { name : 'Heading 4' , element : 'h4' },
+ { name : 'Heading 5' , element : 'h5' },
+ { name : 'Heading 6' , element : 'h6' },
+ { name : 'Preformatted Text', element : 'pre' },
+ { name : 'Address' , element : 'address' },
+ */
+
+ { name : 'Blue Title' , element : 'h3', styles : { 'color' : 'Blue' } },
+ { name : 'Red Title' , element : 'h3', styles : { 'color' : 'Red' } },
+
+ /* Inline Styles */
+
+ // These are core styles available as toolbar buttons. You may opt enabling
+ // some of them in the Styles combo, removing them from the toolbar.
+ /*
+ { name : 'Strong' , element : 'strong', overrides : 'b' },
+ { name : 'Emphasis' , element : 'em' , overrides : 'i' },
+ { name : 'Underline' , element : 'u' },
+ { name : 'Strikethrough' , element : 'strike' },
+ { name : 'Subscript' , element : 'sub' },
+ { name : 'Superscript' , element : 'sup' },
+ */
+
+ { name : 'Marker: Yellow' , element : 'span', styles : { 'background-color' : 'Yellow' } },
+ { name : 'Marker: Green' , element : 'span', styles : { 'background-color' : 'Lime' } },
+
+ { name : 'Big' , element : 'big' },
+ { name : 'Small' , element : 'small' },
+ { name : 'Typewriter' , element : 'tt' },
+
+ { name : 'Computer Code' , element : 'code' },
+ { name : 'Keyboard Phrase' , element : 'kbd' },
+ { name : 'Sample Text' , element : 'samp' },
+ { name : 'Variable' , element : 'var' },
+
+ { name : 'Deleted Text' , element : 'del' },
+ { name : 'Inserted Text' , element : 'ins' },
+
+ { name : 'Cited Work' , element : 'cite' },
+ { name : 'Inline Quotation' , element : 'q' },
+
+ { name : 'Language: RTL' , element : 'span', attributes : { 'dir' : 'rtl' } },
+ { name : 'Language: LTR' , element : 'span', attributes : { 'dir' : 'ltr' } },
+
+ /* Object Styles */
+
+ {
+ name : 'Image on Left',
+ element : 'img',
+ attributes :
+ {
+ 'style' : 'padding: 5px; margin-right: 5px',
+ 'border' : '2',
+ 'align' : 'left'
+ }
+ },
+
+ {
+ name : 'Image on Right',
+ element : 'img',
+ attributes :
+ {
+ 'style' : 'padding: 5px; margin-left: 5px',
+ 'border' : '2',
+ 'align' : 'right'
+ }
+ },
+
+ { name : 'Borderless Table', element : 'table', styles: { 'border-style': 'hidden', 'background-color' : '#E6E6FA' } },
+ { name : 'Square Bulleted List', element : 'ul', styles : { 'list-style-type' : 'square' } }
+]);
diff --git a/_source/plugins/stylescombo/plugin.js b/_source/plugins/stylescombo/plugin.js index b4d9f49..f690602 100644 --- a/_source/plugins/stylescombo/plugin.js +++ b/_source/plugins/stylescombo/plugin.js @@ -1,205 +1,218 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -(function() -{ - CKEDITOR.plugins.add( 'stylescombo', - { - requires : [ 'richcombo', 'styles' ], - - init : function( editor ) - { - var config = editor.config, - lang = editor.lang.stylesCombo, - styles = {}, - stylesList = []; - - function loadStylesSet( callback ) - { - editor.getStylesSet( function( stylesDefinitions ) - { - if ( !stylesList.length ) - { - var style, - styleName; - - // Put all styles into an Array. - for ( var i = 0 ; i < stylesDefinitions.length ; i++ ) - { - var styleDefinition = stylesDefinitions[ i ]; - - styleName = styleDefinition.name; - - style = styles[ styleName ] = new CKEDITOR.style( styleDefinition ); - style._name = styleName; - style._.enterMode = config.enterMode; - - stylesList.push( style ); - } - - // Sorts the Array, so the styles get grouped by type. - stylesList.sort( sortStyles ); - } - - callback && callback(); - }); - } - - editor.ui.addRichCombo( 'Styles', - { - label : lang.label, - title : lang.panelTitle, - className : 'cke_styles', - - panel : - { - css : editor.skin.editor.css.concat( config.contentsCss ), - multiSelect : true, - attributes : { 'aria-label' : lang.panelTitle } - }, - - init : function() - { - var combo = this; - - loadStylesSet( function() - { - var style, styleName; - - // Loop over the Array, adding all items to the - // combo. - var lastType; - for ( var i = 0 ; i < stylesList.length ; i++ ) - { - style = stylesList[ i ]; - styleName = style._name; - - var type = style.type; - - if ( type != lastType ) - { - combo.startGroup( lang[ 'panelTitle' + String( type ) ] ); - lastType = type; - } - - combo.add( - styleName, - style.type == CKEDITOR.STYLE_OBJECT ? styleName : style.buildPreview(), - styleName ); - } - - combo.commit(); - - combo.onOpen(); - }); - }, - - onClick : function( value ) - { - editor.focus(); - editor.fire( 'saveSnapshot' ); - - var style = styles[ value ], - selection = editor.getSelection(); - - var elementPath = new CKEDITOR.dom.elementPath( selection.getStartElement() ); - - if ( style.type == CKEDITOR.STYLE_INLINE && style.checkActive( elementPath ) ) - 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( '' ); - }, - this); - }, - - onOpen : function() - { - if ( CKEDITOR.env.ie || CKEDITOR.env.webkit ) - editor.focus(); - - var selection = editor.getSelection(); - - var element = selection.getSelectedElement(), - elementPath = new CKEDITOR.dom.elementPath( element || selection.getStartElement() ); - - var counter = [ 0, 0, 0, 0 ]; - this.showAll(); - this.unmarkAll(); - for ( var name in styles ) - { - var style = styles[ name ], - type = style.type; - - if ( style.checkActive( elementPath ) ) - this.mark( name ); - else if ( type == CKEDITOR.STYLE_OBJECT && !style.checkApplicable( elementPath ) ) - { - this.hideItem( name ); - counter[ type ]--; - } - - counter[ type ]++; - } - - if ( !counter[ CKEDITOR.STYLE_BLOCK ] ) - this.hideGroup( lang[ 'panelTitle' + String( CKEDITOR.STYLE_BLOCK ) ] ); - - if ( !counter[ CKEDITOR.STYLE_INLINE ] ) - this.hideGroup( lang[ 'panelTitle' + String( CKEDITOR.STYLE_INLINE ) ] ); - - if ( !counter[ CKEDITOR.STYLE_OBJECT ] ) - this.hideGroup( lang[ 'panelTitle' + String( CKEDITOR.STYLE_OBJECT ) ] ); - } - }); - - editor.on( 'instanceReady', function() { loadStylesSet(); } ); - } - }); - - function sortStyles( styleA, styleB ) - { - var typeA = styleA.type, - typeB = styleB.type; - - return typeA == typeB ? 0 : - typeA == CKEDITOR.STYLE_OBJECT ? -1 : - typeB == CKEDITOR.STYLE_OBJECT ? 1 : - typeB == CKEDITOR.STYLE_BLOCK ? 1 : - -1; - } -})(); +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+(function()
+{
+ CKEDITOR.plugins.add( 'stylescombo',
+ {
+ requires : [ 'richcombo', 'styles' ],
+
+ init : function( editor )
+ {
+ var config = editor.config,
+ lang = editor.lang.stylesCombo,
+ styles = {},
+ stylesList = [],
+ combo;
+
+ function loadStylesSet( callback )
+ {
+ editor.getStylesSet( function( stylesDefinitions )
+ {
+ if ( !stylesList.length )
+ {
+ var style,
+ styleName;
+
+ // Put all styles into an Array.
+ for ( var i = 0, count = stylesDefinitions.length ; i < count ; i++ )
+ {
+ var styleDefinition = stylesDefinitions[ i ];
+
+ styleName = styleDefinition.name;
+
+ style = styles[ styleName ] = new CKEDITOR.style( styleDefinition );
+ style._name = styleName;
+ style._.enterMode = config.enterMode;
+
+ stylesList.push( style );
+ }
+
+ // Sorts the Array, so the styles get grouped by type.
+ stylesList.sort( sortStyles );
+ }
+
+ callback && callback();
+ });
+ }
+
+ editor.ui.addRichCombo( 'Styles',
+ {
+ label : lang.label,
+ title : lang.panelTitle,
+ className : 'cke_styles',
+
+ panel :
+ {
+ css : editor.skin.editor.css.concat( config.contentsCss ),
+ multiSelect : true,
+ attributes : { 'aria-label' : lang.panelTitle }
+ },
+
+ init : function()
+ {
+ combo = this;
+
+ loadStylesSet( function()
+ {
+ var style,
+ styleName,
+ lastType,
+ type,
+ i,
+ count;
+
+ // Loop over the Array, adding all items to the
+ // combo.
+ for ( i = 0, count = stylesList.length ; i < count ; i++ )
+ {
+ style = stylesList[ i ];
+ styleName = style._name;
+ type = style.type;
+
+ if ( type != lastType )
+ {
+ combo.startGroup( lang[ 'panelTitle' + String( type ) ] );
+ lastType = type;
+ }
+
+ combo.add(
+ styleName,
+ style.type == CKEDITOR.STYLE_OBJECT ? styleName : style.buildPreview(),
+ styleName );
+ }
+
+ combo.commit();
+
+ });
+ },
+
+ onClick : function( value )
+ {
+ editor.focus();
+ editor.fire( 'saveSnapshot' );
+
+ var style = styles[ value ],
+ selection = editor.getSelection(),
+ elementPath = new CKEDITOR.dom.elementPath( selection.getStartElement() );
+
+ style[ style.checkActive( elementPath ) ? 'remove' : 'apply' ]( editor.document );
+
+ editor.fire( 'saveSnapshot' );
+ },
+
+ onRender : function()
+ {
+ editor.on( 'selectionChange', function( ev )
+ {
+ var currentValue = this.getValue(),
+ elementPath = ev.data.path,
+ elements = elementPath.elements;
+
+ // For each element into the elements path.
+ for ( var i = 0, count = elements.length, element ; i < count ; 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( '' );
+ },
+ this);
+ },
+
+ onOpen : function()
+ {
+ if ( CKEDITOR.env.ie || CKEDITOR.env.webkit )
+ editor.focus();
+
+ var selection = editor.getSelection(),
+ element = selection.getSelectedElement(),
+ elementPath = new CKEDITOR.dom.elementPath( element || selection.getStartElement() ),
+ counter = [ 0, 0, 0, 0 ];
+
+ this.showAll();
+ this.unmarkAll();
+ for ( var name in styles )
+ {
+ var style = styles[ name ],
+ type = style.type;
+
+ if ( style.checkActive( elementPath ) )
+ this.mark( name );
+ else if ( type == CKEDITOR.STYLE_OBJECT && !style.checkApplicable( elementPath ) )
+ {
+ this.hideItem( name );
+ counter[ type ]--;
+ }
+
+ counter[ type ]++;
+ }
+
+ if ( !counter[ CKEDITOR.STYLE_BLOCK ] )
+ this.hideGroup( lang[ 'panelTitle' + String( CKEDITOR.STYLE_BLOCK ) ] );
+
+ if ( !counter[ CKEDITOR.STYLE_INLINE ] )
+ this.hideGroup( lang[ 'panelTitle' + String( CKEDITOR.STYLE_INLINE ) ] );
+
+ if ( !counter[ CKEDITOR.STYLE_OBJECT ] )
+ this.hideGroup( lang[ 'panelTitle' + String( CKEDITOR.STYLE_OBJECT ) ] );
+ },
+
+ // Force a reload of the data
+ reset: function()
+ {
+ if ( combo )
+ {
+ delete combo._.panel;
+ delete combo._.list;
+ combo._.committed = 0;
+ combo._.items = {};
+ combo._.state = CKEDITOR.TRISTATE_OFF;
+ }
+ styles = {};
+ stylesList = [];
+ loadStylesSet();
+ }
+ });
+
+ editor.on( 'instanceReady', function() { loadStylesSet(); } );
+ }
+ });
+
+ function sortStyles( styleA, styleB )
+ {
+ var typeA = styleA.type,
+ typeB = styleB.type;
+
+ return typeA == typeB ? 0 :
+ typeA == CKEDITOR.STYLE_OBJECT ? -1 :
+ typeB == CKEDITOR.STYLE_OBJECT ? 1 :
+ typeB == CKEDITOR.STYLE_BLOCK ? 1 :
+ -1;
+ }
+})();
diff --git a/_source/plugins/stylescombo/styles/default.js b/_source/plugins/stylescombo/styles/default.js deleted file mode 100644 index fe92de3..0000000 --- a/_source/plugins/stylescombo/styles/default.js +++ /dev/null @@ -1,85 +0,0 @@ -/*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
-For licensing, see LICENSE.html or http://ckeditor.com/license
-*/
-
-CKEDITOR.stylesSet.add( 'default',
-[
- /* Block Styles */
-
- // These styles are already available in the "Format" combo, so they are
- // not needed here by default. You may enable them to avoid placing the
- // "Format" combo in the toolbar, maintaining the same features.
- /*
- { name : 'Paragraph' , element : 'p' },
- { name : 'Heading 1' , element : 'h1' },
- { name : 'Heading 2' , element : 'h2' },
- { name : 'Heading 3' , element : 'h3' },
- { name : 'Heading 4' , element : 'h4' },
- { name : 'Heading 5' , element : 'h5' },
- { name : 'Heading 6' , element : 'h6' },
- { name : 'Preformatted Text', element : 'pre' },
- { name : 'Address' , element : 'address' },
- */
-
- { name : 'Blue Title' , element : 'h3', styles : { 'color' : 'Blue' } },
- { name : 'Red Title' , element : 'h3', styles : { 'color' : 'Red' } },
-
- /* Inline Styles */
-
- // These are core styles available as toolbar buttons. You may opt enabling
- // some of them in the Styles combo, removing them from the toolbar.
- /*
- { name : 'Strong' , element : 'strong', overrides : 'b' },
- { name : 'Emphasis' , element : 'em' , overrides : 'i' },
- { name : 'Underline' , element : 'u' },
- { name : 'Strikethrough' , element : 'strike' },
- { name : 'Subscript' , element : 'sub' },
- { name : 'Superscript' , element : 'sup' },
- */
-
- { name : 'Marker: Yellow' , element : 'span', styles : { 'background-color' : 'Yellow' } },
- { name : 'Marker: Green' , element : 'span', styles : { 'background-color' : 'Lime' } },
-
- { name : 'Big' , element : 'big' },
- { name : 'Small' , element : 'small' },
- { name : 'Typewriter' , element : 'tt' },
-
- { name : 'Computer Code' , element : 'code' },
- { name : 'Keyboard Phrase' , element : 'kbd' },
- { name : 'Sample Text' , element : 'samp' },
- { name : 'Variable' , element : 'var' },
-
- { name : 'Deleted Text' , element : 'del' },
- { name : 'Inserted Text' , element : 'ins' },
-
- { name : 'Cited Work' , element : 'cite' },
- { name : 'Inline Quotation' , element : 'q' },
-
- { name : 'Language: RTL' , element : 'span', attributes : { 'dir' : 'rtl' } },
- { name : 'Language: LTR' , element : 'span', attributes : { 'dir' : 'ltr' } },
-
- /* Object Styles */
-
- {
- name : 'Image on Left',
- element : 'img',
- attributes :
- {
- 'style' : 'padding: 5px; margin-right: 5px',
- 'border' : '2',
- 'align' : 'left'
- }
- },
-
- {
- name : 'Image on Right',
- element : 'img',
- attributes :
- {
- 'style' : 'padding: 5px; margin-left: 5px',
- 'border' : '2',
- 'align' : 'right'
- }
- }
-]);
diff --git a/_source/plugins/stylesheetparser/plugin.js b/_source/plugins/stylesheetparser/plugin.js new file mode 100644 index 0000000..8f0adfe --- /dev/null +++ b/_source/plugins/stylesheetparser/plugin.js @@ -0,0 +1,148 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @stylesheetParser plugin.
+ */
+
+(function()
+{
+ // We want to extract only the elements with classes defined in the stylesheets:
+ function parseClasses( aRules, skipSelectors, validSelectors )
+ {
+ // aRules are just the different rules in the style sheets
+ // We want to merge them and them split them by commas, so we end up with only
+ // the selectors
+ var s = aRules.join(' ');
+ // Remove selectors splitting the elements, leave only the class selector (.)
+ s = s.replace( /(,|>|\+|~)/g, ' ' );
+ // Remove attribute selectors: table[border="0"]
+ s = s.replace( /\[[^\]]*/g, '' );
+ // Remove Ids: div#main
+ s = s.replace( /#[^\s]*/g, '' );
+ // Remove pseudo-selectors and pseudo-elements: :hover :nth-child(2n+1) ::before
+ s = s.replace( /\:{1,2}[^\s]*/g, '' );
+
+ s = s.replace( /\s+/g, ' ' );
+
+ var aSelectors = s.split( ' ' ),
+ aClasses = [];
+
+ for ( var i = 0; i < aSelectors.length ; i++ )
+ {
+ var selector = aSelectors[ i ];
+
+ if ( validSelectors.test( selector ) && !skipSelectors.test( selector ) )
+ {
+ // If we still don't know about this one, add it
+ if ( CKEDITOR.tools.indexOf( aClasses, selector ) == -1 )
+ aClasses.push( selector );
+ }
+ }
+
+ return aClasses;
+ }
+
+ function LoadStylesCSS( theDoc, skipSelectors, validSelectors )
+ {
+ var styles = [],
+ // It will hold all the rules of the applied stylesheets (except those internal to CKEditor)
+ aRules = [],
+ i;
+
+ for ( i = 0; i < theDoc.styleSheets.length; i++ )
+ {
+ var sheet = theDoc.styleSheets[ i ],
+ node = sheet.ownerNode || sheet.owningElement;
+
+ // Skip the internal stylesheets
+ if ( node.getAttribute( 'data-cke-temp' ) )
+ continue;
+
+ // Exclude stylesheets injected by extensions
+ if ( sheet.href && sheet.href.substr(0, 9) == 'chrome://' )
+ continue;
+
+ var sheetRules = sheet.cssRules || sheet.rules;
+ for ( var j = 0; j < sheetRules.length; j++ )
+ aRules.push( sheetRules[ j ].selectorText );
+ }
+
+ var aClasses = parseClasses( aRules, skipSelectors, validSelectors );
+
+ // Add each style to our "Styles" collection.
+ for ( i = 0; i < aClasses.length; i++ )
+ {
+ var oElement = aClasses[ i ].split( '.' ),
+ element = oElement[ 0 ].toLowerCase(),
+ sClassName = oElement[ 1 ];
+
+ styles.push( {
+ name : element + '.' + sClassName,
+ element : element,
+ attributes : {'class' : sClassName}
+ });
+ }
+
+ return styles;
+ }
+
+ // Register a plugin named "stylesheetparser".
+ CKEDITOR.plugins.add( 'stylesheetparser',
+ {
+ requires: [ 'styles' ],
+ onLoad : function()
+ {
+ var obj = CKEDITOR.editor.prototype;
+ obj.getStylesSet = CKEDITOR.tools.override( obj.getStylesSet, function( org )
+ {
+ return function( callback )
+ {
+ var self = this;
+ org.call( this, function( definitions )
+ {
+ // Rules that must be skipped
+ var skipSelectors = self.config.stylesheetParser_skipSelectors || ( /(^body\.|^\.)/i ),
+ // Rules that are valid
+ validSelectors = self.config.stylesheetParser_validSelectors || ( /\w+\.\w+/ );
+
+ callback( ( self._.stylesDefinitions = definitions.concat( LoadStylesCSS( self.document.$, skipSelectors, validSelectors ) ) ) );
+ });
+ };
+ });
+
+ }
+ });
+})();
+
+
+/**
+ * A regular expression that defines whether a CSS rule will be
+ * skipped by the Stylesheet Parser plugin. A CSS rule matching
+ * the regular expression will be ignored and will not be available
+ * in the Styles drop-down list.
+ * @name CKEDITOR.config.stylesheetParser_skipSelectors
+ * @type RegExp
+ * @default /(^body\.|^\.)/i
+ * @since 3.6
+ * @see CKEDITOR.config.stylesheetParser_validSelectors
+ * @example
+ * // Ignore rules for body and caption elements, classes starting with "high", and any class defined for no specific element.
+ * config.stylesheetParser_skipSelectors = /(^body\.|^caption\.|\.high|^\.)/i;
+ */
+
+ /**
+ * A regular expression that defines which CSS rules will be used
+ * by the Stylesheet Parser plugin. A CSS rule matching the regular
+ * expression will be available in the Styles drop-down list.
+ * @name CKEDITOR.config.stylesheetParser_validSelectors
+ * @type RegExp
+ * @default /\w+\.\w+/
+ * @since 3.6
+ * @see CKEDITOR.config.stylesheetParser_skipSelectors
+ * @example
+ * // Only add rules for p and span elements.
+ * config.stylesheetParser_validSelectors = /\^(p|span)\.\w+/;
+ */
diff --git a/_source/plugins/tab/plugin.js b/_source/plugins/tab/plugin.js index 5c3f734..b8070f6 100644 --- a/_source/plugins/tab/plugin.js +++ b/_source/plugins/tab/plugin.js @@ -1,261 +1,367 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -(function() -{ - var meta = - { - editorFocus : false, - modes : { wysiwyg:1, source:1 } - }; - - var blurCommand = - { - exec : function( editor ) - { - editor.container.focusNext( true, editor.tabIndex ); - } - }; - - var blurBackCommand = - { - exec : function( editor ) - { - editor.container.focusPrevious( true, editor.tabIndex ); - } - }; - - CKEDITOR.plugins.add( 'tab', - { - requires : [ 'keystrokes' ], - - init : function( editor ) - { - var tabSpaces = editor.config.tabSpaces || 0, - tabText = ''; - - while ( tabSpaces-- ) - tabText += '\xa0'; - - if ( tabText ) - { - editor.on( 'key', function( ev ) - { - if ( ev.data.keyCode == 9 ) // TAB - { - editor.insertHtml( tabText ); - ev.cancel(); - } - }); - } - - if ( CKEDITOR.env.webkit || CKEDITOR.env.gecko ) - { - editor.on( 'key', function( ev ) - { - var keyCode = ev.data.keyCode; - - if ( keyCode == 9 && !tabText ) // TAB - { - ev.cancel(); - editor.execCommand( 'blur' ); - } - - if ( keyCode == ( CKEDITOR.SHIFT + 9 ) ) // SHIFT+TAB - { - editor.execCommand( 'blurBack' ); - ev.cancel(); - } - }); - } - - editor.addCommand( 'blur', CKEDITOR.tools.extend( blurCommand, meta ) ); - editor.addCommand( 'blurBack', CKEDITOR.tools.extend( blurBackCommand, meta ) ); - } - }); -})(); - -/** - * Moves the UI focus to the element following this element in the tabindex - * order. - * @example - * var element = CKEDITOR.document.getById( 'example' ); - * element.focusNext(); - */ -CKEDITOR.dom.element.prototype.focusNext = function( ignoreChildren, indexToUse ) -{ - var $ = this.$, - curTabIndex = ( indexToUse === undefined ? this.getTabIndex() : indexToUse ), - passedCurrent, enteredCurrent, - elected, electedTabIndex, - element, elementTabIndex; - - if ( curTabIndex <= 0 ) - { - // If this element has tabindex <= 0 then we must simply look for any - // element following it containing tabindex=0. - - element = this.getNextSourceNode( ignoreChildren, CKEDITOR.NODE_ELEMENT ); - - while ( element ) - { - if ( element.isVisible() && element.getTabIndex() === 0 ) - { - elected = element; - break; - } - - element = element.getNextSourceNode( false, CKEDITOR.NODE_ELEMENT ); - } - } - else - { - // If this element has tabindex > 0 then we must look for: - // 1. An element following this element with the same tabindex. - // 2. The first element in source other with the lowest tabindex - // that is higher than this element tabindex. - // 3. The first element with tabindex=0. - - element = this.getDocument().getBody().getFirst(); - - while ( ( element = element.getNextSourceNode( false, CKEDITOR.NODE_ELEMENT ) ) ) - { - if ( !passedCurrent ) - { - if ( !enteredCurrent && element.equals( this ) ) - { - enteredCurrent = true; - - // Ignore this element, if required. - if ( ignoreChildren ) - { - if ( !( element = element.getNextSourceNode( true, CKEDITOR.NODE_ELEMENT ) ) ) - break; - passedCurrent = 1; - } - } - else if ( enteredCurrent && !this.contains( element ) ) - passedCurrent = 1; - } - - if ( !element.isVisible() || ( elementTabIndex = element.getTabIndex() ) < 0 ) - continue; - - if ( passedCurrent && elementTabIndex == curTabIndex ) - { - elected = element; - break; - } - - if ( elementTabIndex > curTabIndex && ( !elected || !electedTabIndex || elementTabIndex < electedTabIndex ) ) - { - elected = element; - electedTabIndex = elementTabIndex; - } - else if ( !elected && elementTabIndex === 0 ) - { - elected = element; - electedTabIndex = elementTabIndex; - } - } - } - - if ( elected ) - elected.focus(); -}; - -/** - * Moves the UI focus to the element before this element in the tabindex order. - * @example - * var element = CKEDITOR.document.getById( 'example' ); - * element.focusPrevious(); - */ -CKEDITOR.dom.element.prototype.focusPrevious = function( ignoreChildren, indexToUse ) -{ - var $ = this.$, - curTabIndex = ( indexToUse === undefined ? this.getTabIndex() : indexToUse ), - passedCurrent, enteredCurrent, - elected, - electedTabIndex = 0, - elementTabIndex; - - var element = this.getDocument().getBody().getLast(); - - while ( ( element = element.getPreviousSourceNode( false, CKEDITOR.NODE_ELEMENT ) ) ) - { - if ( !passedCurrent ) - { - if ( !enteredCurrent && element.equals( this ) ) - { - enteredCurrent = true; - - // Ignore this element, if required. - if ( ignoreChildren ) - { - if ( !( element = element.getPreviousSourceNode( true, CKEDITOR.NODE_ELEMENT ) ) ) - break; - passedCurrent = 1; - } - } - else if ( enteredCurrent && !this.contains( element ) ) - passedCurrent = 1; - } - - if ( !element.isVisible() || ( elementTabIndex = element.getTabIndex() ) < 0 ) - continue; - - if ( curTabIndex <= 0 ) - { - // If this element has tabindex <= 0 then we must look for: - // 1. An element before this one containing tabindex=0. - // 2. The last element with the highest tabindex. - - if ( passedCurrent && elementTabIndex === 0 ) - { - elected = element; - break; - } - - if ( elementTabIndex > electedTabIndex ) - { - elected = element; - electedTabIndex = elementTabIndex; - } - } - else - { - // If this element has tabindex > 0 we must look for: - // 1. An element preceeding this one, with the same tabindex. - // 2. The last element in source other with the highest tabindex - // that is lower than this element tabindex. - - if ( passedCurrent && elementTabIndex == curTabIndex ) - { - elected = element; - break; - } - - if ( elementTabIndex < curTabIndex && ( !elected || elementTabIndex > electedTabIndex ) ) - { - elected = element; - electedTabIndex = elementTabIndex; - } - } - } - - if ( elected ) - elected.focus(); -}; - -/** - * Intructs the editor to add a number of spaces (&nbsp;) to the text when - * hitting the TAB key. If set to zero, the TAB key will be used to move the - * cursor focus to the next element in the page, out of the editor focus. - * @name CKEDITOR.config.tabSpaces - * @type Number - * @default 0 - * @example - * config.tabSpaces = 4; - */ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+(function()
+{
+ var meta =
+ {
+ editorFocus : false,
+ modes : { wysiwyg:1, source:1 }
+ };
+
+ var blurCommand =
+ {
+ exec : function( editor )
+ {
+ editor.container.focusNext( true, editor.tabIndex );
+ }
+ };
+
+ var blurBackCommand =
+ {
+ exec : function( editor )
+ {
+ editor.container.focusPrevious( true, editor.tabIndex );
+ }
+ };
+
+ function selectNextCellCommand( backward )
+ {
+ return {
+ editorFocus : false,
+ canUndo : false,
+ modes : { wysiwyg : 1 },
+ exec : function( editor )
+ {
+ if ( editor.focusManager.hasFocus )
+ {
+ var sel = editor.getSelection(),
+ ancestor = sel.getCommonAncestor(),
+ cell;
+
+ if ( ( cell = ( ancestor.getAscendant( 'td', true ) || ancestor.getAscendant( 'th', true ) ) ) )
+ {
+ var resultRange = new CKEDITOR.dom.range( editor.document ),
+ next = CKEDITOR.tools.tryThese( function()
+ {
+ var row = cell.getParent(),
+ next = row.$.cells[ cell.$.cellIndex + ( backward ? - 1 : 1 ) ];
+
+ // Invalid any empty value.
+ next.parentNode.parentNode;
+ return next;
+ },
+ function()
+ {
+ var row = cell.getParent(),
+ table = row.getAscendant( 'table' ),
+ nextRow = table.$.rows[ row.$.rowIndex + ( backward ? - 1 : 1 ) ];
+
+ return nextRow.cells[ backward? nextRow.cells.length -1 : 0 ];
+ });
+
+ // Clone one more row at the end of table and select the first newly established cell.
+ if ( ! ( next || backward ) )
+ {
+ var table = cell.getAscendant( 'table' ).$,
+ cells = cell.getParent().$.cells;
+
+ var newRow = new CKEDITOR.dom.element( table.insertRow( -1 ), editor.document );
+
+ for ( var i = 0, count = cells.length ; i < count; i++ )
+ {
+ var newCell = newRow.append( new CKEDITOR.dom.element(
+ cells[ i ], editor.document ).clone( false, false ) );
+ !CKEDITOR.env.ie && newCell.appendBogus();
+ }
+
+ resultRange.moveToElementEditStart( newRow );
+ }
+ else if ( next )
+ {
+ next = new CKEDITOR.dom.element( next );
+ resultRange.moveToElementEditStart( next );
+ // Avoid selecting empty block makes the cursor blind.
+ if ( !( resultRange.checkStartOfBlock() && resultRange.checkEndOfBlock() ) )
+ resultRange.selectNodeContents( next );
+ }
+ else
+ return true;
+
+ resultRange.select( true );
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+ }
+
+ CKEDITOR.plugins.add( 'tab',
+ {
+ requires : [ 'keystrokes' ],
+
+ init : function( editor )
+ {
+ var tabTools = editor.config.enableTabKeyTools !== false,
+ tabSpaces = editor.config.tabSpaces || 0,
+ tabText = '';
+
+ while ( tabSpaces-- )
+ tabText += '\xa0';
+
+ if ( tabText )
+ {
+ editor.on( 'key', function( ev )
+ {
+ if ( ev.data.keyCode == 9 ) // TAB
+ {
+ editor.insertHtml( tabText );
+ ev.cancel();
+ }
+ });
+ }
+
+ if ( tabTools )
+ {
+ editor.on( 'key', function( ev )
+ {
+ if ( ev.data.keyCode == 9 && editor.execCommand( 'selectNextCell' ) || // TAB
+ ev.data.keyCode == ( CKEDITOR.SHIFT + 9 ) && editor.execCommand( 'selectPreviousCell' ) ) // SHIFT+TAB
+ ev.cancel();
+ });
+ }
+
+ if ( CKEDITOR.env.webkit || CKEDITOR.env.gecko )
+ {
+ editor.on( 'key', function( ev )
+ {
+ var keyCode = ev.data.keyCode;
+
+ if ( keyCode == 9 && !tabText ) // TAB
+ {
+ ev.cancel();
+ editor.execCommand( 'blur' );
+ }
+
+ if ( keyCode == ( CKEDITOR.SHIFT + 9 ) ) // SHIFT+TAB
+ {
+ editor.execCommand( 'blurBack' );
+ ev.cancel();
+ }
+ });
+ }
+
+ editor.addCommand( 'blur', CKEDITOR.tools.extend( blurCommand, meta ) );
+ editor.addCommand( 'blurBack', CKEDITOR.tools.extend( blurBackCommand, meta ) );
+ editor.addCommand( 'selectNextCell', selectNextCellCommand() );
+ editor.addCommand( 'selectPreviousCell', selectNextCellCommand( true ) );
+ }
+ });
+})();
+
+/**
+ * Moves the UI focus to the element following this element in the tabindex
+ * order.
+ * @example
+ * var element = CKEDITOR.document.getById( 'example' );
+ * element.focusNext();
+ */
+CKEDITOR.dom.element.prototype.focusNext = function( ignoreChildren, indexToUse )
+{
+ var $ = this.$,
+ curTabIndex = ( indexToUse === undefined ? this.getTabIndex() : indexToUse ),
+ passedCurrent, enteredCurrent,
+ elected, electedTabIndex,
+ element, elementTabIndex;
+
+ if ( curTabIndex <= 0 )
+ {
+ // If this element has tabindex <= 0 then we must simply look for any
+ // element following it containing tabindex=0.
+
+ element = this.getNextSourceNode( ignoreChildren, CKEDITOR.NODE_ELEMENT );
+
+ while ( element )
+ {
+ if ( element.isVisible() && element.getTabIndex() === 0 )
+ {
+ elected = element;
+ break;
+ }
+
+ element = element.getNextSourceNode( false, CKEDITOR.NODE_ELEMENT );
+ }
+ }
+ else
+ {
+ // If this element has tabindex > 0 then we must look for:
+ // 1. An element following this element with the same tabindex.
+ // 2. The first element in source other with the lowest tabindex
+ // that is higher than this element tabindex.
+ // 3. The first element with tabindex=0.
+
+ element = this.getDocument().getBody().getFirst();
+
+ while ( ( element = element.getNextSourceNode( false, CKEDITOR.NODE_ELEMENT ) ) )
+ {
+ if ( !passedCurrent )
+ {
+ if ( !enteredCurrent && element.equals( this ) )
+ {
+ enteredCurrent = true;
+
+ // Ignore this element, if required.
+ if ( ignoreChildren )
+ {
+ if ( !( element = element.getNextSourceNode( true, CKEDITOR.NODE_ELEMENT ) ) )
+ break;
+ passedCurrent = 1;
+ }
+ }
+ else if ( enteredCurrent && !this.contains( element ) )
+ passedCurrent = 1;
+ }
+
+ if ( !element.isVisible() || ( elementTabIndex = element.getTabIndex() ) < 0 )
+ continue;
+
+ if ( passedCurrent && elementTabIndex == curTabIndex )
+ {
+ elected = element;
+ break;
+ }
+
+ if ( elementTabIndex > curTabIndex && ( !elected || !electedTabIndex || elementTabIndex < electedTabIndex ) )
+ {
+ elected = element;
+ electedTabIndex = elementTabIndex;
+ }
+ else if ( !elected && elementTabIndex === 0 )
+ {
+ elected = element;
+ electedTabIndex = elementTabIndex;
+ }
+ }
+ }
+
+ if ( elected )
+ elected.focus();
+};
+
+/**
+ * Moves the UI focus to the element before this element in the tabindex order.
+ * @example
+ * var element = CKEDITOR.document.getById( 'example' );
+ * element.focusPrevious();
+ */
+CKEDITOR.dom.element.prototype.focusPrevious = function( ignoreChildren, indexToUse )
+{
+ var $ = this.$,
+ curTabIndex = ( indexToUse === undefined ? this.getTabIndex() : indexToUse ),
+ passedCurrent, enteredCurrent,
+ elected,
+ electedTabIndex = 0,
+ elementTabIndex;
+
+ var element = this.getDocument().getBody().getLast();
+
+ while ( ( element = element.getPreviousSourceNode( false, CKEDITOR.NODE_ELEMENT ) ) )
+ {
+ if ( !passedCurrent )
+ {
+ if ( !enteredCurrent && element.equals( this ) )
+ {
+ enteredCurrent = true;
+
+ // Ignore this element, if required.
+ if ( ignoreChildren )
+ {
+ if ( !( element = element.getPreviousSourceNode( true, CKEDITOR.NODE_ELEMENT ) ) )
+ break;
+ passedCurrent = 1;
+ }
+ }
+ else if ( enteredCurrent && !this.contains( element ) )
+ passedCurrent = 1;
+ }
+
+ if ( !element.isVisible() || ( elementTabIndex = element.getTabIndex() ) < 0 )
+ continue;
+
+ if ( curTabIndex <= 0 )
+ {
+ // If this element has tabindex <= 0 then we must look for:
+ // 1. An element before this one containing tabindex=0.
+ // 2. The last element with the highest tabindex.
+
+ if ( passedCurrent && elementTabIndex === 0 )
+ {
+ elected = element;
+ break;
+ }
+
+ if ( elementTabIndex > electedTabIndex )
+ {
+ elected = element;
+ electedTabIndex = elementTabIndex;
+ }
+ }
+ else
+ {
+ // If this element has tabindex > 0 we must look for:
+ // 1. An element preceeding this one, with the same tabindex.
+ // 2. The last element in source other with the highest tabindex
+ // that is lower than this element tabindex.
+
+ if ( passedCurrent && elementTabIndex == curTabIndex )
+ {
+ elected = element;
+ break;
+ }
+
+ if ( elementTabIndex < curTabIndex && ( !elected || elementTabIndex > electedTabIndex ) )
+ {
+ elected = element;
+ electedTabIndex = elementTabIndex;
+ }
+ }
+ }
+
+ if ( elected )
+ elected.focus();
+};
+
+/**
+ * Intructs the editor to add a number of spaces (&nbsp;) to the text when
+ * hitting the TAB key. If set to zero, the TAB key will be used to move the
+ * cursor focus to the next element in the page, out of the editor focus.
+ * @name CKEDITOR.config.tabSpaces
+ * @type Number
+ * @default 0
+ * @example
+ * config.tabSpaces = 4;
+ */
+
+/**
+ * Allow context-sensitive tab key behaviors, including the following scenarios:
+ * <h5>When selection is anchored inside <b>table cells</b>:</h5>
+ * <ul>
+ * <li>If TAB is pressed, select the contents of the "next" cell. If in the last cell in the table, add a new row to it and focus its first cell.</li>
+ * <li>If SHIFT+TAB is pressed, select the contents of the "previous" cell. Do nothing when it's in the first cell.</li>
+ * </ul>
+ * @name CKEDITOR.config.enableTabKeyTools
+ * @type Boolean
+ * @default true
+ * @example
+ * config.enableTabKeyTools = false;
+ */
+
+// If the TAB key is not supposed to be enabled for navigation, the following
+// settings could be used alternatively:
+// config.keystrokes.push(
+// [ CKEDITOR.ALT + 38 /*Arrow Up*/, 'selectPreviousCell' ],
+// [ CKEDITOR.ALT + 40 /*Arrow Down*/, 'selectNextCell' ]
+// );
diff --git a/_source/plugins/table/dialogs/table.js b/_source/plugins/table/dialogs/table.js index 31b375a..d4bfb89 100644 --- a/_source/plugins/table/dialogs/table.js +++ b/_source/plugins/table/dialogs/table.js @@ -1,596 +1,618 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -(function() -{ - var widthPattern = /^(\d+(?:\.\d+)?)(px|%)$/, - heightPattern = /^(\d+(?:\.\d+)?)px$/; - - var commitValue = function( data ) - { - var id = this.id; - if ( !data.info ) - data.info = {}; - data.info[id] = this.getValue(); - }; - - function tableDialog( editor, command ) - { - var makeElement = function( name ){ return new CKEDITOR.dom.element( name, editor.document ); }; - - return { - title : editor.lang.table.title, - minWidth : 310, - minHeight : CKEDITOR.env.ie ? 310 : 280, - onShow : function() - { - // Detect if there's a selected table. - var selection = editor.getSelection(), - ranges = selection.getRanges(), - selectedTable = null; - - var rowsInput = this.getContentElement( 'info', 'txtRows' ), - colsInput = this.getContentElement( 'info', 'txtCols' ), - widthInput = this.getContentElement( 'info', 'txtWidth' ); - if ( command == 'tableProperties' ) - { - if ( ( selectedTable = editor.getSelection().getSelectedElement() ) ) - { - if ( selectedTable.getName() != 'table' ) - selectedTable = null; - } - else if ( ranges.length > 0 ) - { - // Webkit could report the following range on cell selection (#4948): - // <table><tr><td>[ </td></tr></table>] - if ( CKEDITOR.env.webkit ) - ranges[ 0 ].shrink( CKEDITOR.NODE_ELEMENT ); - - var rangeRoot = ranges[0].getCommonAncestor( true ); - selectedTable = rangeRoot.getAscendant( 'table', true ); - } - - // Save a reference to the selected table, and push a new set of default values. - this._.selectedElement = selectedTable; - } - - // Enable, disable and select the row, cols, width fields. - if ( selectedTable ) - { - this.setupContent( selectedTable ); - rowsInput && rowsInput.disable(); - colsInput && colsInput.disable(); - widthInput && widthInput.select(); - } - else - { - rowsInput && rowsInput.enable(); - colsInput && colsInput.enable(); - rowsInput && rowsInput.select(); - } - }, - onOk : function() - { - if ( this._.selectedElement ) - { - var selection = editor.getSelection(), - bms = editor.getSelection().createBookmarks(); - } - - var table = this._.selectedElement || makeElement( 'table' ), - me = this, - data = {}; - - this.commitContent( data, table ); - - if ( data.info ) - { - var info = data.info; - - // Generate the rows and cols. - if ( !this._.selectedElement ) - { - var tbody = table.append( makeElement( 'tbody' ) ), - rows = parseInt( info.txtRows, 10 ) || 0, - cols = parseInt( info.txtCols, 10 ) || 0; - - for ( var i = 0 ; i < rows ; i++ ) - { - var row = tbody.append( makeElement( 'tr' ) ); - for ( var j = 0 ; j < cols ; j++ ) - { - var cell = row.append( makeElement( 'td' ) ); - if ( !CKEDITOR.env.ie ) - cell.append( makeElement( 'br' ) ); - } - } - } - - // Modify the table headers. Depends on having rows and cols generated - // correctly so it can't be done in commit functions. - - // Should we make a <thead>? - var headers = info.selHeaders; - if ( !table.$.tHead && ( headers == 'row' || headers == 'both' ) ) - { - var thead = new CKEDITOR.dom.element( table.$.createTHead() ); - tbody = table.getElementsByTag( 'tbody' ).getItem( 0 ); - var theRow = tbody.getElementsByTag( 'tr' ).getItem( 0 ); - - // Change TD to TH: - for ( i = 0 ; i < theRow.getChildCount() ; i++ ) - { - var th = theRow.getChild( i ); - if ( th.type == CKEDITOR.NODE_ELEMENT ) - { - th.renameNode( 'th' ); - th.setAttribute( 'scope', 'col' ); - } - } - thead.append( theRow.remove() ); - } - - if ( table.$.tHead !== null && !( headers == 'row' || headers == 'both' ) ) - { - // Move the row out of the THead and put it in the TBody: - thead = new CKEDITOR.dom.element( table.$.tHead ); - tbody = table.getElementsByTag( 'tbody' ).getItem( 0 ); - - var previousFirstRow = tbody.getFirst(); - while ( thead.getChildCount() > 0 ) - { - theRow = thead.getFirst(); - for ( i = 0; i < theRow.getChildCount() ; i++ ) - { - var newCell = theRow.getChild( i ); - if ( newCell.type == CKEDITOR.NODE_ELEMENT ) - { - newCell.renameNode( 'td' ); - newCell.removeAttribute( 'scope' ); - } - } - theRow.insertBefore( previousFirstRow ); - } - thead.remove(); - } - - // Should we make all first cells in a row TH? - if ( !this.hasColumnHeaders && ( headers == 'col' || headers == 'both' ) ) - { - for ( row = 0 ; row < table.$.rows.length ; row++ ) - { - newCell = new CKEDITOR.dom.element( table.$.rows[ row ].cells[ 0 ] ); - newCell.renameNode( 'th' ); - newCell.setAttribute( 'scope', 'row' ); - } - } - - // Should we make all first TH-cells in a row make TD? If 'yes' we do it the other way round :-) - if ( ( this.hasColumnHeaders ) && !( headers == 'col' || headers == 'both' ) ) - { - for ( i = 0 ; i < table.$.rows.length ; i++ ) - { - row = new CKEDITOR.dom.element( table.$.rows[i] ); - if ( row.getParent().getName() == 'tbody' ) - { - newCell = new CKEDITOR.dom.element( row.$.cells[0] ); - newCell.renameNode( 'td' ); - newCell.removeAttribute( 'scope' ); - } - } - } - - // Set the width and height. - var styles = []; - if ( info.txtHeight ) - table.setStyle( 'height', CKEDITOR.tools.cssLength( info.txtHeight ) ); - else - table.removeStyle( 'height' ); - - if ( info.txtWidth ) - { - var type = info.cmbWidthType || 'pixels'; - table.setStyle( 'width', info.txtWidth + ( type == 'pixels' ? 'px' : '%' ) ); - } - else - table.removeStyle( 'width' ); - - if ( !table.getAttribute( 'style' ) ) - table.removeAttribute( 'style' ); - } - - // Insert the table element if we're creating one. - if ( !this._.selectedElement ) - editor.insertElement( table ); - // Properly restore the selection inside table. (#4822) - else - selection.selectBookmarks( bms ); - - return true; - }, - contents : [ - { - id : 'info', - label : editor.lang.table.title, - elements : - [ - { - type : 'hbox', - widths : [ null, null ], - styles : [ 'vertical-align:top' ], - children : - [ - { - type : 'vbox', - padding : 0, - children : - [ - { - type : 'text', - id : 'txtRows', - 'default' : 3, - label : editor.lang.table.rows, - style : 'width:5em', - validate : function() - { - var pass = true, - value = this.getValue(); - pass = pass && CKEDITOR.dialog.validate.integer()( value ) - && value > 0; - if ( !pass ) - { - alert( editor.lang.table.invalidRows ); - this.select(); - } - return pass; - }, - setup : function( selectedElement ) - { - this.setValue( selectedElement.$.rows.length ); - }, - commit : commitValue - }, - { - type : 'text', - id : 'txtCols', - 'default' : 2, - label : editor.lang.table.columns, - style : 'width:5em', - validate : function() - { - var pass = true, - value = this.getValue(); - pass = pass && CKEDITOR.dialog.validate.integer()( value ) - && value > 0; - if ( !pass ) - { - alert( editor.lang.table.invalidCols ); - this.select(); - } - return pass; - }, - setup : function( selectedTable ) - { - this.setValue( selectedTable.$.rows[0].cells.length); - }, - commit : commitValue - }, - { - type : 'html', - html : ' ' - }, - { - type : 'select', - id : 'selHeaders', - 'default' : '', - label : editor.lang.table.headers, - items : - [ - [ editor.lang.table.headersNone, '' ], - [ editor.lang.table.headersRow, 'row' ], - [ editor.lang.table.headersColumn, 'col' ], - [ editor.lang.table.headersBoth, 'both' ] - ], - setup : function( selectedTable ) - { - // Fill in the headers field. - var dialog = this.getDialog(); - dialog.hasColumnHeaders = true; - - // Check if all the first cells in every row are TH - for ( var row = 0 ; row < selectedTable.$.rows.length ; row++ ) - { - // If just one cell isn't a TH then it isn't a header column - if ( selectedTable.$.rows[row].cells[0].nodeName.toLowerCase() != 'th' ) - { - dialog.hasColumnHeaders = false; - break; - } - } - - // Check if the table contains <thead>. - if ( ( selectedTable.$.tHead !== null) ) - this.setValue( dialog.hasColumnHeaders ? 'both' : 'row' ); - else - this.setValue( dialog.hasColumnHeaders ? 'col' : '' ); - }, - commit : commitValue - }, - { - type : 'text', - id : 'txtBorder', - 'default' : 1, - label : editor.lang.table.border, - style : 'width:3em', - validate : CKEDITOR.dialog.validate['number']( editor.lang.table.invalidBorder ), - setup : function( selectedTable ) - { - this.setValue( selectedTable.getAttribute( 'border' ) || '' ); - }, - commit : function( data, selectedTable ) - { - if ( this.getValue() ) - selectedTable.setAttribute( 'border', this.getValue() ); - else - selectedTable.removeAttribute( 'border' ); - } - }, - { - id : 'cmbAlign', - type : 'select', - 'default' : '', - label : editor.lang.table.align, - items : - [ - [ editor.lang.common.notSet , ''], - [ editor.lang.table.alignLeft , 'left'], - [ editor.lang.table.alignCenter , 'center'], - [ editor.lang.table.alignRight , 'right'] - ], - setup : function( selectedTable ) - { - this.setValue( selectedTable.getAttribute( 'align' ) || '' ); - }, - commit : function( data, selectedTable ) - { - if ( this.getValue() ) - selectedTable.setAttribute( 'align', this.getValue() ); - else - selectedTable.removeAttribute( 'align' ); - } - } - ] - }, - { - type : 'vbox', - padding : 0, - children : - [ - { - type : 'hbox', - widths : [ '5em' ], - children : - [ - { - type : 'text', - id : 'txtWidth', - style : 'width:5em', - label : editor.lang.table.width, - 'default' : 200, - validate : CKEDITOR.dialog.validate['number']( editor.lang.table.invalidWidth ), - - // Extra labelling of width unit type. - onLoad : function() - { - var widthType = this.getDialog().getContentElement( 'info', 'cmbWidthType' ), - labelElement = widthType.getElement(), - inputElement = this.getInputElement(), - ariaLabelledByAttr = inputElement.getAttribute( 'aria-labelledby' ); - - inputElement.setAttribute( 'aria-labelledby', [ ariaLabelledByAttr, labelElement.$.id ].join( ' ' ) ); - }, - - setup : function( selectedTable ) - { - var widthMatch = widthPattern.exec( selectedTable.$.style.width ); - if ( widthMatch ) - this.setValue( widthMatch[1] ); - else - this.setValue( '' ); - }, - commit : commitValue - }, - { - id : 'cmbWidthType', - type : 'select', - label : editor.lang.table.widthUnit, - labelStyle: 'visibility:hidden', - 'default' : 'pixels', - items : - [ - [ editor.lang.table.widthPx , 'pixels'], - [ editor.lang.table.widthPc , 'percents'] - ], - setup : function( selectedTable ) - { - var widthMatch = widthPattern.exec( selectedTable.$.style.width ); - if ( widthMatch ) - this.setValue( widthMatch[2] == 'px' ? 'pixels' : 'percents' ); - }, - commit : commitValue - } - ] - }, - { - type : 'hbox', - widths : [ '5em' ], - children : - [ - { - type : 'text', - id : 'txtHeight', - style : 'width:5em', - label : editor.lang.table.height, - 'default' : '', - validate : CKEDITOR.dialog.validate['number']( editor.lang.table.invalidHeight ), - - // Extra labelling of height unit type. - onLoad : function() - { - var heightType = this.getDialog().getContentElement( 'info', 'htmlHeightType' ), - labelElement = heightType.getElement(), - inputElement = this.getInputElement(), - ariaLabelledByAttr = inputElement.getAttribute( 'aria-labelledby' ); - - inputElement.setAttribute( 'aria-labelledby', [ ariaLabelledByAttr, labelElement.$.id ].join( ' ' ) ); - }, - - setup : function( selectedTable ) - { - var heightMatch = heightPattern.exec( selectedTable.$.style.height ); - if ( heightMatch ) - this.setValue( heightMatch[1] ); - }, - commit : commitValue - }, - { - id : 'htmlHeightType', - type : 'html', - html : '<div><br />' + editor.lang.table.widthPx + '</div>' - } - ] - }, - { - type : 'html', - html : ' ' - }, - { - type : 'text', - id : 'txtCellSpace', - style : 'width:3em', - label : editor.lang.table.cellSpace, - 'default' : 1, - validate : CKEDITOR.dialog.validate['number']( editor.lang.table.invalidCellSpacing ), - setup : function( selectedTable ) - { - this.setValue( selectedTable.getAttribute( 'cellSpacing' ) || '' ); - }, - commit : function( data, selectedTable ) - { - if ( this.getValue() ) - selectedTable.setAttribute( 'cellSpacing', this.getValue() ); - else - selectedTable.removeAttribute( 'cellSpacing' ); - } - }, - { - type : 'text', - id : 'txtCellPad', - style : 'width:3em', - label : editor.lang.table.cellPad, - 'default' : 1, - validate : CKEDITOR.dialog.validate['number']( editor.lang.table.invalidCellPadding ), - setup : function( selectedTable ) - { - this.setValue( selectedTable.getAttribute( 'cellPadding' ) || '' ); - }, - commit : function( data, selectedTable ) - { - if ( this.getValue() ) - selectedTable.setAttribute( 'cellPadding', this.getValue() ); - else - selectedTable.removeAttribute( 'cellPadding' ); - } - } - ] - } - ] - }, - { - type : 'html', - align : 'right', - html : '' - }, - { - type : 'vbox', - padding : 0, - children : - [ - { - type : 'text', - id : 'txtCaption', - label : editor.lang.table.caption, - setup : function( selectedTable ) - { - var nodeList = selectedTable.getElementsByTag( 'caption' ); - if ( nodeList.count() > 0 ) - { - var caption = nodeList.getItem( 0 ); - caption = ( caption.getChild( 0 ) && caption.getChild( 0 ).getText() ) || ''; - caption = CKEDITOR.tools.trim( caption ); - this.setValue( caption ); - } - }, - commit : function( data, table ) - { - var caption = this.getValue(), - captionElement = table.getElementsByTag( 'caption' ); - if ( caption ) - { - if ( captionElement.count() > 0 ) - { - captionElement = captionElement.getItem( 0 ); - captionElement.setHtml( '' ); - } - else - { - captionElement = new CKEDITOR.dom.element( 'caption', editor.document ); - if ( table.getChildCount() ) - captionElement.insertBefore( table.getFirst() ); - else - captionElement.appendTo( table ); - } - captionElement.append( new CKEDITOR.dom.text( caption, editor.document ) ); - } - else if ( captionElement.count() > 0 ) - { - for ( var i = captionElement.count() - 1 ; i >= 0 ; i-- ) - captionElement.getItem( i ).remove(); - } - } - }, - { - type : 'text', - id : 'txtSummary', - label : editor.lang.table.summary, - setup : function( selectedTable ) - { - this.setValue( selectedTable.getAttribute( 'summary' ) || '' ); - }, - commit : function( data, selectedTable ) - { - if ( this.getValue() ) - selectedTable.setAttribute( 'summary', this.getValue() ); - else - selectedTable.removeAttribute( 'summary' ); - } - } - ] - } - ] - } - ] - }; - } - - CKEDITOR.dialog.add( 'table', function( editor ) - { - return tableDialog( editor, 'table' ); - } ); - CKEDITOR.dialog.add( 'tableProperties', function( editor ) - { - return tableDialog( editor, 'tableProperties' ); - } ); -})(); +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+(function()
+{
+ var defaultToPixel = CKEDITOR.tools.cssLength;
+
+ var commitValue = function( data )
+ {
+ var id = this.id;
+ if ( !data.info )
+ data.info = {};
+ data.info[id] = this.getValue();
+ };
+
+ function tableColumns( table )
+ {
+ var cols = 0, maxCols = 0;
+ for ( var i = 0, row, rows = table.$.rows.length; i < rows; i++ )
+ {
+ row = table.$.rows[ i ], cols = 0;
+ for ( var j = 0, cell, cells = row.cells.length; j < cells; j++ )
+ {
+ cell = row.cells[ j ];
+ cols += cell.colSpan;
+ }
+
+ cols > maxCols && ( maxCols = cols );
+ }
+
+ return maxCols;
+ }
+
+
+ // Whole-positive-integer validator.
+ function validatorNum( msg )
+ {
+ return function()
+ {
+ var value = this.getValue(),
+ pass = !!( CKEDITOR.dialog.validate.integer()( value ) && value > 0 );
+
+ if ( !pass )
+ {
+ alert( msg );
+ this.select();
+ }
+
+ return pass;
+ };
+ }
+
+ function tableDialog( editor, command )
+ {
+ var makeElement = function( name )
+ {
+ return new CKEDITOR.dom.element( name, editor.document );
+ };
+
+ var dialogadvtab = editor.plugins.dialogadvtab;
+
+ return {
+ title : editor.lang.table.title,
+ minWidth : 310,
+ minHeight : CKEDITOR.env.ie ? 310 : 280,
+
+ onLoad : function()
+ {
+ var dialog = this;
+
+ var styles = dialog.getContentElement( 'advanced', 'advStyles' );
+
+ if ( styles )
+ {
+ styles.on( 'change', function( evt )
+ {
+ // Synchronize width value.
+ var width = this.getStyle( 'width', '' ),
+ txtWidth = dialog.getContentElement( 'info', 'txtWidth' );
+
+ txtWidth && txtWidth.setValue( width, true );
+
+ // Synchronize height value.
+ var height = this.getStyle( 'height', '' ),
+ txtHeight = dialog.getContentElement( 'info', 'txtHeight' );
+
+ txtHeight && txtHeight.setValue( height, true );
+ });
+ }
+ },
+
+ onShow : function()
+ {
+ // Detect if there's a selected table.
+ var selection = editor.getSelection(),
+ ranges = selection.getRanges(),
+ selectedTable = null;
+
+ var rowsInput = this.getContentElement( 'info', 'txtRows' ),
+ colsInput = this.getContentElement( 'info', 'txtCols' ),
+ widthInput = this.getContentElement( 'info', 'txtWidth' ),
+ heightInput = this.getContentElement( 'info', 'txtHeight' );
+
+ if ( command == 'tableProperties' )
+ {
+ if ( ( selectedTable = selection.getSelectedElement() ) )
+ selectedTable = selectedTable.getAscendant( 'table', true );
+ else if ( ranges.length > 0 )
+ {
+ // Webkit could report the following range on cell selection (#4948):
+ // <table><tr><td>[ </td></tr></table>]
+ if ( CKEDITOR.env.webkit )
+ ranges[ 0 ].shrink( CKEDITOR.NODE_ELEMENT );
+
+ var rangeRoot = ranges[0].getCommonAncestor( true );
+ selectedTable = rangeRoot.getAscendant( 'table', true );
+ }
+
+ // Save a reference to the selected table, and push a new set of default values.
+ this._.selectedElement = selectedTable;
+ }
+
+ // Enable or disable the row, cols, width fields.
+ if ( selectedTable )
+ {
+ this.setupContent( selectedTable );
+ rowsInput && rowsInput.disable();
+ colsInput && colsInput.disable();
+ }
+ else
+ {
+ rowsInput && rowsInput.enable();
+ colsInput && colsInput.enable();
+ }
+
+ // Call the onChange method for the widht and height fields so
+ // they get reflected into the Advanced tab.
+ widthInput && widthInput.onChange();
+ heightInput && heightInput.onChange();
+ },
+ onOk : function()
+ {
+ var selection = editor.getSelection(),
+ bms = this._.selectedElement && selection.createBookmarks();
+
+ var table = this._.selectedElement || makeElement( 'table' ),
+ me = this,
+ data = {};
+
+ this.commitContent( data, table );
+
+ if ( data.info )
+ {
+ var info = data.info;
+
+ // Generate the rows and cols.
+ if ( !this._.selectedElement )
+ {
+ var tbody = table.append( makeElement( 'tbody' ) ),
+ rows = parseInt( info.txtRows, 10 ) || 0,
+ cols = parseInt( info.txtCols, 10 ) || 0;
+
+ for ( var i = 0 ; i < rows ; i++ )
+ {
+ var row = tbody.append( makeElement( 'tr' ) );
+ for ( var j = 0 ; j < cols ; j++ )
+ {
+ var cell = row.append( makeElement( 'td' ) );
+ if ( !CKEDITOR.env.ie )
+ cell.append( makeElement( 'br' ) );
+ }
+ }
+ }
+
+ // Modify the table headers. Depends on having rows and cols generated
+ // correctly so it can't be done in commit functions.
+
+ // Should we make a <thead>?
+ var headers = info.selHeaders;
+ if ( !table.$.tHead && ( headers == 'row' || headers == 'both' ) )
+ {
+ var thead = new CKEDITOR.dom.element( table.$.createTHead() );
+ tbody = table.getElementsByTag( 'tbody' ).getItem( 0 );
+ var theRow = tbody.getElementsByTag( 'tr' ).getItem( 0 );
+
+ // Change TD to TH:
+ for ( i = 0 ; i < theRow.getChildCount() ; i++ )
+ {
+ var th = theRow.getChild( i );
+ // Skip bookmark nodes. (#6155)
+ if ( th.type == CKEDITOR.NODE_ELEMENT && !th.data( 'cke-bookmark' ) )
+ {
+ th.renameNode( 'th' );
+ th.setAttribute( 'scope', 'col' );
+ }
+ }
+ thead.append( theRow.remove() );
+ }
+
+ if ( table.$.tHead !== null && !( headers == 'row' || headers == 'both' ) )
+ {
+ // Move the row out of the THead and put it in the TBody:
+ thead = new CKEDITOR.dom.element( table.$.tHead );
+ tbody = table.getElementsByTag( 'tbody' ).getItem( 0 );
+
+ var previousFirstRow = tbody.getFirst();
+ while ( thead.getChildCount() > 0 )
+ {
+ theRow = thead.getFirst();
+ for ( i = 0; i < theRow.getChildCount() ; i++ )
+ {
+ var newCell = theRow.getChild( i );
+ if ( newCell.type == CKEDITOR.NODE_ELEMENT )
+ {
+ newCell.renameNode( 'td' );
+ newCell.removeAttribute( 'scope' );
+ }
+ }
+ theRow.insertBefore( previousFirstRow );
+ }
+ thead.remove();
+ }
+
+ // Should we make all first cells in a row TH?
+ if ( !this.hasColumnHeaders && ( headers == 'col' || headers == 'both' ) )
+ {
+ for ( row = 0 ; row < table.$.rows.length ; row++ )
+ {
+ newCell = new CKEDITOR.dom.element( table.$.rows[ row ].cells[ 0 ] );
+ newCell.renameNode( 'th' );
+ newCell.setAttribute( 'scope', 'row' );
+ }
+ }
+
+ // Should we make all first TH-cells in a row make TD? If 'yes' we do it the other way round :-)
+ if ( ( this.hasColumnHeaders ) && !( headers == 'col' || headers == 'both' ) )
+ {
+ for ( i = 0 ; i < table.$.rows.length ; i++ )
+ {
+ row = new CKEDITOR.dom.element( table.$.rows[i] );
+ if ( row.getParent().getName() == 'tbody' )
+ {
+ newCell = new CKEDITOR.dom.element( row.$.cells[0] );
+ newCell.renameNode( 'td' );
+ newCell.removeAttribute( 'scope' );
+ }
+ }
+ }
+
+ // Set the width and height.
+ info.txtHeight ? table.setStyle( 'height', info.txtHeight ) : table.removeStyle( 'height' );
+ info.txtWidth ? table.setStyle( 'width', info.txtWidth ) : table.removeStyle( 'width' );
+
+ if ( !table.getAttribute( 'style' ) )
+ table.removeAttribute( 'style' );
+ }
+
+ // Insert the table element if we're creating one.
+ if ( !this._.selectedElement )
+ {
+ editor.insertElement( table );
+ // Override the default cursor position after insertElement to place
+ // cursor inside the first cell (#7959), IE needs a while.
+ setTimeout( function()
+ {
+ var firstCell = new CKEDITOR.dom.element( table.$.rows[ 0 ].cells[ 0 ] );
+ var range = new CKEDITOR.dom.range( editor.document );
+ range.moveToPosition( firstCell, CKEDITOR.POSITION_AFTER_START );
+ range.select( 1 );
+ }, 0 );
+ }
+ // Properly restore the selection, (#4822) but don't break
+ // because of this, e.g. updated table caption.
+ else
+ try { selection.selectBookmarks( bms ); } catch( er ){}
+ },
+ contents : [
+ {
+ id : 'info',
+ label : editor.lang.table.title,
+ elements :
+ [
+ {
+ type : 'hbox',
+ widths : [ null, null ],
+ styles : [ 'vertical-align:top' ],
+ children :
+ [
+ {
+ type : 'vbox',
+ padding : 0,
+ children :
+ [
+ {
+ type : 'text',
+ id : 'txtRows',
+ 'default' : 3,
+ label : editor.lang.table.rows,
+ required : true,
+ controlStyle : 'width:5em',
+ validate : validatorNum( editor.lang.table.invalidRows ),
+ setup : function( selectedElement )
+ {
+ this.setValue( selectedElement.$.rows.length );
+ },
+ commit : commitValue
+ },
+ {
+ type : 'text',
+ id : 'txtCols',
+ 'default' : 2,
+ label : editor.lang.table.columns,
+ required : true,
+ controlStyle : 'width:5em',
+ validate : validatorNum( editor.lang.table.invalidCols ),
+ setup : function( selectedTable )
+ {
+ this.setValue( tableColumns( selectedTable ) );
+ },
+ commit : commitValue
+ },
+ {
+ type : 'html',
+ html : ' '
+ },
+ {
+ type : 'select',
+ id : 'selHeaders',
+ 'default' : '',
+ label : editor.lang.table.headers,
+ items :
+ [
+ [ editor.lang.table.headersNone, '' ],
+ [ editor.lang.table.headersRow, 'row' ],
+ [ editor.lang.table.headersColumn, 'col' ],
+ [ editor.lang.table.headersBoth, 'both' ]
+ ],
+ setup : function( selectedTable )
+ {
+ // Fill in the headers field.
+ var dialog = this.getDialog();
+ dialog.hasColumnHeaders = true;
+
+ // Check if all the first cells in every row are TH
+ for ( var row = 0 ; row < selectedTable.$.rows.length ; row++ )
+ {
+ // If just one cell isn't a TH then it isn't a header column
+ var headCell = selectedTable.$.rows[row].cells[0];
+ if ( headCell && headCell.nodeName.toLowerCase() != 'th' )
+ {
+ dialog.hasColumnHeaders = false;
+ break;
+ }
+ }
+
+ // Check if the table contains <thead>.
+ if ( ( selectedTable.$.tHead !== null) )
+ this.setValue( dialog.hasColumnHeaders ? 'both' : 'row' );
+ else
+ this.setValue( dialog.hasColumnHeaders ? 'col' : '' );
+ },
+ commit : commitValue
+ },
+ {
+ type : 'text',
+ id : 'txtBorder',
+ 'default' : 1,
+ label : editor.lang.table.border,
+ controlStyle : 'width:3em',
+ validate : CKEDITOR.dialog.validate['number']( editor.lang.table.invalidBorder ),
+ setup : function( selectedTable )
+ {
+ this.setValue( selectedTable.getAttribute( 'border' ) || '' );
+ },
+ commit : function( data, selectedTable )
+ {
+ if ( this.getValue() )
+ selectedTable.setAttribute( 'border', this.getValue() );
+ else
+ selectedTable.removeAttribute( 'border' );
+ }
+ },
+ {
+ id : 'cmbAlign',
+ type : 'select',
+ 'default' : '',
+ label : editor.lang.common.align,
+ items :
+ [
+ [ editor.lang.common.notSet , ''],
+ [ editor.lang.common.alignLeft , 'left'],
+ [ editor.lang.common.alignCenter , 'center'],
+ [ editor.lang.common.alignRight , 'right']
+ ],
+ setup : function( selectedTable )
+ {
+ this.setValue( selectedTable.getAttribute( 'align' ) || '' );
+ },
+ commit : function( data, selectedTable )
+ {
+ if ( this.getValue() )
+ selectedTable.setAttribute( 'align', this.getValue() );
+ else
+ selectedTable.removeAttribute( 'align' );
+ }
+ }
+ ]
+ },
+ {
+ type : 'vbox',
+ padding : 0,
+ children :
+ [
+ {
+ type : 'hbox',
+ widths : [ '5em' ],
+ children :
+ [
+ {
+ type : 'text',
+ id : 'txtWidth',
+ controlStyle : 'width:5em',
+ label : editor.lang.common.width,
+ title : editor.lang.common.cssLengthTooltip,
+ 'default' : 500,
+ getValue : defaultToPixel,
+ validate : CKEDITOR.dialog.validate.cssLength( editor.lang.common.invalidCssLength.replace( '%1', editor.lang.common.width ) ),
+ onChange : function()
+ {
+ var styles = this.getDialog().getContentElement( 'advanced', 'advStyles' );
+ styles && styles.updateStyle( 'width', this.getValue() );
+ },
+ setup : function( selectedTable )
+ {
+ var val = selectedTable.getStyle( 'width' );
+ val && this.setValue( val );
+ },
+ commit : commitValue
+ }
+ ]
+ },
+ {
+ type : 'hbox',
+ widths : [ '5em' ],
+ children :
+ [
+ {
+ type : 'text',
+ id : 'txtHeight',
+ controlStyle : 'width:5em',
+ label : editor.lang.common.height,
+ title : editor.lang.common.cssLengthTooltip,
+ 'default' : '',
+ getValue : defaultToPixel,
+ validate : CKEDITOR.dialog.validate.cssLength( editor.lang.common.invalidCssLength.replace( '%1', editor.lang.common.height ) ),
+ onChange : function()
+ {
+ var styles = this.getDialog().getContentElement( 'advanced', 'advStyles' );
+ styles && styles.updateStyle( 'height', this.getValue() );
+ },
+
+ setup : function( selectedTable )
+ {
+ var val = selectedTable.getStyle( 'height' );
+ val && this.setValue( val );
+ },
+ commit : commitValue
+ }
+ ]
+ },
+ {
+ type : 'html',
+ html : ' '
+ },
+ {
+ type : 'text',
+ id : 'txtCellSpace',
+ controlStyle : 'width:3em',
+ label : editor.lang.table.cellSpace,
+ 'default' : 1,
+ validate : CKEDITOR.dialog.validate.number( editor.lang.table.invalidCellSpacing ),
+ setup : function( selectedTable )
+ {
+ this.setValue( selectedTable.getAttribute( 'cellSpacing' ) || '' );
+ },
+ commit : function( data, selectedTable )
+ {
+ if ( this.getValue() )
+ selectedTable.setAttribute( 'cellSpacing', this.getValue() );
+ else
+ selectedTable.removeAttribute( 'cellSpacing' );
+ }
+ },
+ {
+ type : 'text',
+ id : 'txtCellPad',
+ controlStyle : 'width:3em',
+ label : editor.lang.table.cellPad,
+ 'default' : 1,
+ validate : CKEDITOR.dialog.validate.number( editor.lang.table.invalidCellPadding ),
+ setup : function( selectedTable )
+ {
+ this.setValue( selectedTable.getAttribute( 'cellPadding' ) || '' );
+ },
+ commit : function( data, selectedTable )
+ {
+ if ( this.getValue() )
+ selectedTable.setAttribute( 'cellPadding', this.getValue() );
+ else
+ selectedTable.removeAttribute( 'cellPadding' );
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ type : 'html',
+ align : 'right',
+ html : ''
+ },
+ {
+ type : 'vbox',
+ padding : 0,
+ children :
+ [
+ {
+ type : 'text',
+ id : 'txtCaption',
+ label : editor.lang.table.caption,
+ setup : function( selectedTable )
+ {
+ this.enable();
+
+ var nodeList = selectedTable.getElementsByTag( 'caption' );
+ if ( nodeList.count() > 0 )
+ {
+ var caption = nodeList.getItem( 0 );
+ var firstElementChild = caption.getFirst( CKEDITOR.dom.walker.nodeType( CKEDITOR.NODE_ELEMENT ) );
+
+ if ( firstElementChild && !firstElementChild.equals( caption.getBogus() ) )
+ {
+ this.disable();
+ this.setValue( caption.getText() );
+ return;
+ }
+
+ caption = CKEDITOR.tools.trim( caption.getText() );
+ this.setValue( caption );
+ }
+ },
+ commit : function( data, table )
+ {
+ if ( !this.isEnabled() )
+ return;
+
+ var caption = this.getValue(),
+ captionElement = table.getElementsByTag( 'caption' );
+ if ( caption )
+ {
+ if ( captionElement.count() > 0 )
+ {
+ captionElement = captionElement.getItem( 0 );
+ captionElement.setHtml( '' );
+ }
+ else
+ {
+ captionElement = new CKEDITOR.dom.element( 'caption', editor.document );
+ if ( table.getChildCount() )
+ captionElement.insertBefore( table.getFirst() );
+ else
+ captionElement.appendTo( table );
+ }
+ captionElement.append( new CKEDITOR.dom.text( caption, editor.document ) );
+ }
+ else if ( captionElement.count() > 0 )
+ {
+ for ( var i = captionElement.count() - 1 ; i >= 0 ; i-- )
+ captionElement.getItem( i ).remove();
+ }
+ }
+ },
+ {
+ type : 'text',
+ id : 'txtSummary',
+ label : editor.lang.table.summary,
+ setup : function( selectedTable )
+ {
+ this.setValue( selectedTable.getAttribute( 'summary' ) || '' );
+ },
+ commit : function( data, selectedTable )
+ {
+ if ( this.getValue() )
+ selectedTable.setAttribute( 'summary', this.getValue() );
+ else
+ selectedTable.removeAttribute( 'summary' );
+ }
+ }
+ ]
+ }
+ ]
+ },
+ dialogadvtab && dialogadvtab.createAdvancedTab( editor )
+ ]
+ };
+ }
+
+ CKEDITOR.dialog.add( 'table', function( editor )
+ {
+ return tableDialog( editor, 'table' );
+ } );
+ CKEDITOR.dialog.add( 'tableProperties', function( editor )
+ {
+ return tableDialog( editor, 'tableProperties' );
+ } );
+})();
diff --git a/_source/plugins/table/plugin.js b/_source/plugins/table/plugin.js index cb212e1..def36a7 100644 --- a/_source/plugins/table/plugin.js +++ b/_source/plugins/table/plugin.js @@ -1,78 +1,78 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -CKEDITOR.plugins.add( 'table', -{ - init : function( editor ) - { - var table = CKEDITOR.plugins.table, - lang = editor.lang.table; - - editor.addCommand( 'table', new CKEDITOR.dialogCommand( 'table' ) ); - editor.addCommand( 'tableProperties', new CKEDITOR.dialogCommand( 'tableProperties' ) ); - - editor.ui.addButton( 'Table', - { - label : lang.toolbar, - command : 'table' - }); - - CKEDITOR.dialog.add( 'table', this.path + 'dialogs/table.js' ); - CKEDITOR.dialog.add( 'tableProperties', this.path + 'dialogs/table.js' ); - - // If the "menu" plugin is loaded, register the menu items. - if ( editor.addMenuItems ) - { - editor.addMenuItems( - { - table : - { - label : lang.menu, - command : 'tableProperties', - group : 'table', - order : 5 - }, - - tabledelete : - { - label : lang.deleteTable, - command : 'tableDelete', - group : 'table', - order : 1 - } - } ); - } - - editor.on( 'doubleclick', function( evt ) - { - var element = evt.data.element; - - if ( element.is( 'table' ) ) - evt.data.dialog = 'tableProperties'; - }); - - // If the "contextmenu" plugin is loaded, register the listeners. - if ( editor.contextMenu ) - { - editor.contextMenu.addListener( function( element, selection ) - { - if ( !element ) - return null; - - var isTable = element.is( 'table' ) || element.hasAscendant( 'table' ); - - if ( isTable ) - { - return { - tabledelete : CKEDITOR.TRISTATE_OFF, - table : CKEDITOR.TRISTATE_OFF - }; - } - - return null; - } ); - } - } -} ); +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.add( 'table',
+{
+ init : function( editor )
+ {
+ var table = CKEDITOR.plugins.table,
+ lang = editor.lang.table;
+
+ editor.addCommand( 'table', new CKEDITOR.dialogCommand( 'table' ) );
+ editor.addCommand( 'tableProperties', new CKEDITOR.dialogCommand( 'tableProperties' ) );
+
+ editor.ui.addButton( 'Table',
+ {
+ label : lang.toolbar,
+ command : 'table'
+ });
+
+ CKEDITOR.dialog.add( 'table', this.path + 'dialogs/table.js' );
+ CKEDITOR.dialog.add( 'tableProperties', this.path + 'dialogs/table.js' );
+
+ // If the "menu" plugin is loaded, register the menu items.
+ if ( editor.addMenuItems )
+ {
+ editor.addMenuItems(
+ {
+ table :
+ {
+ label : lang.menu,
+ command : 'tableProperties',
+ group : 'table',
+ order : 5
+ },
+
+ tabledelete :
+ {
+ label : lang.deleteTable,
+ command : 'tableDelete',
+ group : 'table',
+ order : 1
+ }
+ } );
+ }
+
+ editor.on( 'doubleclick', function( evt )
+ {
+ var element = evt.data.element;
+
+ if ( element.is( 'table' ) )
+ evt.data.dialog = 'tableProperties';
+ });
+
+ // 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 isTable = element.hasAscendant( 'table', 1 );
+
+ if ( isTable )
+ {
+ return {
+ tabledelete : CKEDITOR.TRISTATE_OFF,
+ table : CKEDITOR.TRISTATE_OFF
+ };
+ }
+
+ return null;
+ } );
+ }
+ }
+} );
diff --git a/_source/plugins/tableresize/plugin.js b/_source/plugins/tableresize/plugin.js new file mode 100644 index 0000000..a28ab12 --- /dev/null +++ b/_source/plugins/tableresize/plugin.js @@ -0,0 +1,443 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+(function()
+{
+ var pxUnit = CKEDITOR.tools.cssLength,
+ needsIEHacks = CKEDITOR.env.ie && ( CKEDITOR.env.ie7Compat || CKEDITOR.env.quirks || CKEDITOR.env.version < 7 );
+
+ function getWidth( el )
+ {
+ return CKEDITOR.env.ie ? el.$.clientWidth : parseInt( el.getComputedStyle( 'width' ), 10 );
+ }
+
+ function getBorderWidth( element, side )
+ {
+ var computed = element.getComputedStyle( 'border-' + side + '-width' ),
+ borderMap =
+ {
+ thin: '0px',
+ medium: '1px',
+ thick: '2px'
+ };
+
+ if ( computed.indexOf( 'px' ) < 0 )
+ {
+ // look up keywords
+ if ( computed in borderMap && element.getComputedStyle( 'border-style' ) != 'none' )
+ computed = borderMap[ computed ];
+ else
+ computed = 0;
+ }
+
+ return parseInt( computed, 10 );
+ }
+
+ // Gets the table row that contains the most columns.
+ function getMasterPillarRow( table )
+ {
+ var $rows = table.$.rows,
+ maxCells = 0, cellsCount,
+ $elected, $tr;
+
+ for ( var i = 0, len = $rows.length ; i < len; i++ )
+ {
+ $tr = $rows[ i ];
+ cellsCount = $tr.cells.length;
+
+ if ( cellsCount > maxCells )
+ {
+ maxCells = cellsCount;
+ $elected = $tr;
+ }
+ }
+
+ return $elected;
+ }
+
+ function buildTableColumnPillars( table )
+ {
+ var pillars = [],
+ pillarIndex = -1,
+ rtl = ( table.getComputedStyle( 'direction' ) == 'rtl' );
+
+ // Get the raw row element that cointains the most columns.
+ var $tr = getMasterPillarRow( table );
+
+ // Get the tbody element and position, which will be used to set the
+ // top and bottom boundaries.
+ var tbody = new CKEDITOR.dom.element( table.$.tBodies[ 0 ] ),
+ tbodyPosition = tbody.getDocumentPosition();
+
+ // Loop thorugh all cells, building pillars after each one of them.
+ for ( var i = 0, len = $tr.cells.length ; i < len ; i++ )
+ {
+ // Both the current cell and the successive one will be used in the
+ // pillar size calculation.
+ var td = new CKEDITOR.dom.element( $tr.cells[ i ] ),
+ nextTd = $tr.cells[ i + 1 ] && new CKEDITOR.dom.element( $tr.cells[ i + 1 ] );
+
+ pillarIndex += td.$.colSpan || 1;
+
+ // Calculate the pillar boundary positions.
+ var pillarLeft, pillarRight, pillarWidth;
+
+ var x = td.getDocumentPosition().x;
+
+ // Calculate positions based on the current cell.
+ rtl ?
+ pillarRight = x + getBorderWidth( td, 'left' ) :
+ pillarLeft = x + td.$.offsetWidth - getBorderWidth( td, 'right' );
+
+ // Calculate positions based on the next cell, if available.
+ if ( nextTd )
+ {
+ x = nextTd.getDocumentPosition().x;
+
+ rtl ?
+ pillarLeft = x + nextTd.$.offsetWidth - getBorderWidth( nextTd, 'right' ) :
+ pillarRight = x + getBorderWidth( nextTd, 'left' );
+ }
+ // Otherwise calculate positions based on the table (for last cell).
+ else
+ {
+ x = table.getDocumentPosition().x;
+
+ rtl ?
+ pillarLeft = x :
+ pillarRight = x + table.$.offsetWidth;
+ }
+
+ pillarWidth = Math.max( pillarRight - pillarLeft, 3 );
+
+ // The pillar should reflects exactly the shape of the hovered
+ // column border line.
+ pillars.push( {
+ table : table,
+ index : pillarIndex,
+ x : pillarLeft,
+ y : tbodyPosition.y,
+ width : pillarWidth,
+ height : tbody.$.offsetHeight,
+ rtl : rtl } );
+ }
+
+ return pillars;
+ }
+
+ function getPillarAtPosition( pillars, positionX )
+ {
+ for ( var i = 0, len = pillars.length ; i < len ; i++ )
+ {
+ var pillar = pillars[ i ];
+
+ if ( positionX >= pillar.x && positionX <= ( pillar.x + pillar.width ) )
+ return pillar;
+ }
+
+ return null;
+ }
+
+ function cancel( evt )
+ {
+ ( evt.data || evt ).preventDefault();
+ }
+
+ function columnResizer( editor )
+ {
+ var pillar,
+ document,
+ resizer,
+ isResizing,
+ startOffset,
+ currentShift;
+
+ var leftSideCells, rightSideCells, leftShiftBoundary, rightShiftBoundary;
+
+ function detach()
+ {
+ pillar = null;
+ currentShift = 0;
+ isResizing = 0;
+
+ document.removeListener( 'mouseup', onMouseUp );
+ resizer.removeListener( 'mousedown', onMouseDown );
+ resizer.removeListener( 'mousemove', onMouseMove );
+
+ document.getBody().setStyle( 'cursor', 'auto' );
+
+ // Hide the resizer (remove it on IE7 - #5890).
+ needsIEHacks ? resizer.remove() : resizer.hide();
+ }
+
+ function resizeStart()
+ {
+ // Before starting to resize, figure out which cells to change
+ // and the boundaries of this resizing shift.
+
+ var columnIndex = pillar.index,
+ map = CKEDITOR.tools.buildTableMap( pillar.table ),
+ leftColumnCells = [],
+ rightColumnCells = [],
+ leftMinSize = Number.MAX_VALUE,
+ rightMinSize = leftMinSize,
+ rtl = pillar.rtl;
+
+ for ( var i = 0, len = map.length ; i < len ; i++ )
+ {
+ var row = map[ i ],
+ leftCell = row[ columnIndex + ( rtl ? 1 : 0 ) ],
+ rightCell = row[ columnIndex + ( rtl ? 0 : 1 ) ];
+
+ leftCell = leftCell && new CKEDITOR.dom.element( leftCell );
+ rightCell = rightCell && new CKEDITOR.dom.element( rightCell );
+
+ if ( !leftCell || !rightCell || !leftCell.equals( rightCell ) )
+ {
+ leftCell && ( leftMinSize = Math.min( leftMinSize, getWidth( leftCell ) ) );
+ rightCell && ( rightMinSize = Math.min( rightMinSize, getWidth( rightCell ) ) );
+
+ leftColumnCells.push( leftCell );
+ rightColumnCells.push( rightCell );
+ }
+ }
+
+ // Cache the list of cells to be resized.
+ leftSideCells = leftColumnCells;
+ rightSideCells = rightColumnCells;
+
+ // Cache the resize limit boundaries.
+ leftShiftBoundary = pillar.x - leftMinSize;
+ rightShiftBoundary = pillar.x + rightMinSize;
+
+ resizer.setOpacity( 0.5 );
+ startOffset = parseInt( resizer.getStyle( 'left' ), 10 );
+ currentShift = 0;
+ isResizing = 1;
+
+ resizer.on( 'mousemove', onMouseMove );
+
+ // Prevent the native drag behavior otherwise 'mousemove' won't fire.
+ document.on( 'dragstart', cancel );
+ }
+
+ function resizeEnd()
+ {
+ isResizing = 0;
+
+ resizer.setOpacity( 0 );
+
+ currentShift && resizeColumn();
+
+ var table = pillar.table;
+ setTimeout( function () { table.removeCustomData( '_cke_table_pillars' ); }, 0 );
+
+ document.removeListener( 'dragstart', cancel );
+ }
+
+ function resizeColumn()
+ {
+ var rtl = pillar.rtl,
+ cellsCount = rtl ? rightSideCells.length : leftSideCells.length;
+
+ // Perform the actual resize to table cells, only for those by side of the pillar.
+ for ( var i = 0 ; i < cellsCount ; i++ )
+ {
+ var leftCell = leftSideCells[ i ],
+ rightCell = rightSideCells[ i ],
+ table = pillar.table;
+
+ // Defer the resizing to avoid any interference among cells.
+ CKEDITOR.tools.setTimeout(
+ function( leftCell, leftOldWidth, rightCell, rightOldWidth, tableWidth, sizeShift )
+ {
+ leftCell && leftCell.setStyle( 'width', pxUnit( Math.max( leftOldWidth + sizeShift, 0 ) ) );
+ rightCell && rightCell.setStyle( 'width', pxUnit( Math.max( rightOldWidth - sizeShift, 0 ) ) );
+
+ // If we're in the last cell, we need to resize the table as well
+ if ( tableWidth )
+ table.setStyle( 'width', pxUnit( tableWidth + sizeShift * ( rtl ? -1 : 1 ) ) );
+ }
+ , 0,
+ this, [
+ leftCell, leftCell && getWidth( leftCell ),
+ rightCell, rightCell && getWidth( rightCell ),
+ ( !leftCell || !rightCell ) && ( getWidth( table ) + getBorderWidth( table, 'left' ) + getBorderWidth( table, 'right' ) ),
+ currentShift ] );
+ }
+ }
+
+ function onMouseDown( evt )
+ {
+ cancel( evt );
+
+ resizeStart();
+
+ document.on( 'mouseup', onMouseUp, this );
+ }
+
+ function onMouseUp( evt )
+ {
+ evt.removeListener();
+
+ resizeEnd();
+ }
+
+ function onMouseMove( evt )
+ {
+ move( evt.data.$.clientX );
+ }
+
+ document = editor.document;
+
+ resizer = CKEDITOR.dom.element.createFromHtml(
+ '<div data-cke-temp=1 contenteditable=false unselectable=on '+
+ 'style="position:absolute;cursor:col-resize;filter:alpha(opacity=0);opacity:0;' +
+ 'padding:0;background-color:#004;background-image:none;border:0px none;z-index:10"></div>', document );
+
+ // Except on IE6/7 (#5890), place the resizer after body to prevent it
+ // from being editable.
+ if ( !needsIEHacks )
+ document.getDocumentElement().append( resizer );
+
+ this.attachTo = function( targetPillar )
+ {
+ // Accept only one pillar at a time.
+ if ( isResizing )
+ return;
+
+ // On IE6/7, we append the resizer everytime we need it. (#5890)
+ if ( needsIEHacks )
+ {
+ document.getBody().append( resizer );
+ currentShift = 0;
+ }
+
+ pillar = targetPillar;
+
+ resizer.setStyles(
+ {
+ width: pxUnit( targetPillar.width ),
+ height : pxUnit( targetPillar.height ),
+ left : pxUnit( targetPillar.x ),
+ top : pxUnit( targetPillar.y )
+ });
+
+ // In IE6/7, it's not possible to have custom cursors for floating
+ // elements in an editable document. Show the resizer in that case,
+ // to give the user a visual clue.
+ needsIEHacks && resizer.setOpacity( 0.25 );
+
+ resizer.on( 'mousedown', onMouseDown, this );
+
+ document.getBody().setStyle( 'cursor', 'col-resize' );
+
+ // Display the resizer to receive events but don't show it,
+ // only change the cursor to resizable shape.
+ resizer.show();
+ };
+
+ var move = this.move = function( posX )
+ {
+ if ( !pillar )
+ return 0;
+
+ if ( !isResizing && ( posX < pillar.x || posX > ( pillar.x + pillar.width ) ) )
+ {
+ detach();
+ return 0;
+ }
+
+ var resizerNewPosition = posX - Math.round( resizer.$.offsetWidth / 2 );
+
+ if ( isResizing )
+ {
+ if ( resizerNewPosition == leftShiftBoundary || resizerNewPosition == rightShiftBoundary )
+ return 1;
+
+ resizerNewPosition = Math.max( resizerNewPosition, leftShiftBoundary );
+ resizerNewPosition = Math.min( resizerNewPosition, rightShiftBoundary );
+
+ currentShift = resizerNewPosition - startOffset;
+ }
+
+ resizer.setStyle( 'left', pxUnit( resizerNewPosition ) );
+
+ return 1;
+ };
+ }
+
+ function clearPillarsCache( evt )
+ {
+ var target = evt.data.getTarget();
+
+ if ( evt.name == 'mouseout' )
+ {
+ // Bypass interal mouse move.
+ if ( !target.is ( 'table' ) )
+ return;
+
+ var dest = new CKEDITOR.dom.element( evt.data.$.relatedTarget || evt.data.$.toElement );
+ while( dest && dest.$ && !dest.equals( target ) && !dest.is( 'body' ) )
+ dest = dest.getParent();
+ if ( !dest || dest.equals( target ) )
+ return;
+ }
+
+ target.getAscendant( 'table', 1 ).removeCustomData( '_cke_table_pillars' );
+ evt.removeListener();
+ }
+
+ CKEDITOR.plugins.add( 'tableresize',
+ {
+ requires : [ 'tabletools' ],
+ init : function( editor )
+ {
+ editor.on( 'contentDom', function()
+ {
+ var resizer;
+
+ editor.document.getBody().on( 'mousemove', function( evt )
+ {
+ evt = evt.data;
+
+ // If we're already attached to a pillar, simply move the
+ // resizer.
+ if ( resizer && resizer.move( evt.$.clientX ) )
+ {
+ cancel( evt );
+ return;
+ }
+
+ // Considering table, tr, td, tbody but nothing else.
+ var target = evt.getTarget(),
+ table,
+ pillars;
+
+ if ( !target.is( 'table' ) && !target.getAscendant( 'tbody', 1 ) )
+ return;
+
+ table = target.getAscendant( 'table', 1 );
+
+ if ( !( pillars = table.getCustomData( '_cke_table_pillars' ) ) )
+ {
+ // Cache table pillars calculation result.
+ table.setCustomData( '_cke_table_pillars', ( pillars = buildTableColumnPillars( table ) ) );
+ table.on( 'mouseout', clearPillarsCache );
+ table.on( 'mousedown', clearPillarsCache );
+ }
+
+ var pillar = getPillarAtPosition( pillars, evt.$.clientX );
+ if ( pillar )
+ {
+ !resizer && ( resizer = new columnResizer( editor ) );
+ resizer.attachTo( pillar );
+ }
+ });
+ });
+ }
+ });
+
+})();
diff --git a/_source/plugins/tabletools/dialogs/tableCell.js b/_source/plugins/tabletools/dialogs/tableCell.js index c4eaf4e..06c7ec4 100644 --- a/_source/plugins/tabletools/dialogs/tableCell.js +++ b/_source/plugins/tabletools/dialogs/tableCell.js @@ -1,528 +1,520 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -CKEDITOR.dialog.add( 'cellProperties', function( editor ) - { - var langTable = editor.lang.table; - var langCell = langTable.cell; - var langCommon = editor.lang.common; - var validate = CKEDITOR.dialog.validate; - var widthPattern = /^(\d+(?:\.\d+)?)(px|%)$/, - heightPattern = /^(\d+(?:\.\d+)?)px$/; - var bind = CKEDITOR.tools.bind; - - function spacer() - { - return { type : 'html', html : ' ' }; - } - - /** - * - * @param dialogName - * @param callback [ childDialog ] - */ - function getDialogValue( dialogName, callback ) - { - var onOk = function() - { - releaseHandlers( this ); - callback( this ); - }; - var onCancel = function() - { - releaseHandlers( this ); - }; - var bindToDialog = function( dialog ) - { - dialog.on( 'ok', onOk ); - dialog.on( 'cancel', onCancel ); - }; - var releaseHandlers = function( dialog ) - { - dialog.removeListener( 'ok', onOk ); - dialog.removeListener( 'cancel', onCancel ); - }; - 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 ); - }; - } ); - }); - } - } - - return { - title : langCell.title, - minWidth : CKEDITOR.env.ie && CKEDITOR.env.quirks ? 550 : 480, - minHeight : CKEDITOR.env.ie ? ( CKEDITOR.env.quirks ? 180 : 150 ) : 140, - contents : [ - { - id : 'info', - label : langCell.title, - accessKey : 'I', - elements : - [ - { - type : 'hbox', - widths : [ '40%', '5%', '40%' ], - children : - [ - { - type : 'vbox', - padding : 0, - children : - [ - { - type : 'hbox', - widths : [ '70%', '30%' ], - children : - [ - { - type : 'text', - id : 'width', - label : langTable.width, - widths : [ '71%', '29%' ], - labelLayout : 'horizontal', - validate : validate[ 'number' ]( langCell.invalidWidth ), - - // Extra labelling of width unit type. - onLoad : function() - { - var widthType = this.getDialog().getContentElement( 'info', 'widthType' ), - labelElement = widthType.getElement(), - inputElement = this.getInputElement(), - ariaLabelledByAttr = inputElement.getAttribute( 'aria-labelledby' ); - - inputElement.setAttribute( 'aria-labelledby', [ ariaLabelledByAttr, labelElement.$.id ].join( ' ' ) ); - }, - - setup : function( element ) - { - var widthAttr = parseInt( element.getAttribute( 'width' ), 10 ), - widthStyle = parseInt( element.getStyle( 'width' ), 10 ); - - !isNaN( widthAttr ) && this.setValue( widthAttr ); - !isNaN( widthStyle ) && this.setValue( widthStyle ); - }, - commit : function( element ) - { - var value = parseInt( this.getValue(), 10 ), - unit = this.getDialog().getValueOf( 'info', 'widthType' ); - - if ( !isNaN( value ) ) - element.setStyle( 'width', value + unit ); - else - element.removeStyle( 'width' ); - - element.removeAttribute( 'width' ); - }, - 'default' : '' - }, - { - type : 'select', - id : 'widthType', - labelLayout : 'horizontal', - widths : [ '0%', '100%' ], - label : editor.lang.table.widthUnit, - labelStyle: 'display:none', - 'default' : 'px', - items : - [ - [ langTable.widthPx, 'px' ], - [ langTable.widthPc, '%' ] - ], - setup : function( selectedCell ) - { - var widthMatch = widthPattern.exec( selectedCell.$.style.width ); - if ( widthMatch ) - this.setValue( widthMatch[2] ); - } - } - ] - }, - { - type : 'hbox', - widths : [ '70%', '30%' ], - children : - [ - { - type : 'text', - id : 'height', - label : langTable.height, - 'default' : '', - widths : [ '71%', '29%' ], - labelLayout : 'horizontal', - validate : validate[ 'number' ]( langCell.invalidHeight ), - - // Extra labelling of height unit type. - onLoad : function() - { - var heightType = this.getDialog().getContentElement( 'info', 'htmlHeightType' ), - labelElement = heightType.getElement(), - inputElement = this.getInputElement(), - ariaLabelledByAttr = inputElement.getAttribute( 'aria-labelledby' ); - - inputElement.setAttribute( 'aria-labelledby', [ ariaLabelledByAttr, labelElement.$.id ].join( ' ' ) ); - }, - - setup : function( element ) - { - var heightAttr = parseInt( element.getAttribute( 'height' ), 10 ), - heightStyle = parseInt( element.getStyle( 'height' ), 10 ); - - !isNaN( heightAttr ) && this.setValue( heightAttr ); - !isNaN( heightStyle ) && this.setValue( heightStyle ); - }, - commit : function( element ) - { - var value = parseInt( this.getValue(), 10 ); - - if ( !isNaN( value ) ) - element.setStyle( 'height', CKEDITOR.tools.cssLength( value ) ); - else - element.removeStyle( 'height' ); - - element.removeAttribute( 'height' ); - } - }, - { - id : 'htmlHeightType', - type : 'html', - html : langTable.widthPx - } - ] - }, - spacer(), - { - type : 'select', - id : 'wordWrap', - labelLayout : 'horizontal', - label : langCell.wordWrap, - widths : [ '50%', '50%' ], - 'default' : 'yes', - items : - [ - [ langCell.yes, 'yes' ], - [ langCell.no, 'no' ] - ], - setup : function( element ) - { - var wordWrapAttr = element.getAttribute( 'noWrap' ), - wordWrapStyle = element.getStyle( 'white-space' ); - - if ( wordWrapStyle == 'nowrap' || wordWrapAttr ) - this.setValue( 'no' ); - }, - commit : function( element ) - { - if ( this.getValue() == 'no' ) - element.setStyle( 'white-space', 'nowrap' ); - else - element.removeStyle( 'white-space' ); - - element.removeAttribute( 'noWrap' ); - } - }, - spacer(), - { - type : 'select', - id : 'hAlign', - labelLayout : 'horizontal', - label : langCell.hAlign, - widths : [ '50%', '50%' ], - 'default' : '', - items : - [ - [ langCommon.notSet, '' ], - [ langTable.alignLeft, 'left' ], - [ langTable.alignCenter, 'center' ], - [ langTable.alignRight, 'right' ] - ], - setup : function( element ) - { - var alignAttr = element.getAttribute( 'align' ), - textAlignStyle = element.getStyle( 'text-align'); - - this.setValue( textAlignStyle || alignAttr || '' ); - }, - commit : function( selectedCell ) - { - var value = this.getValue(); - - if ( value ) - selectedCell.setStyle( 'text-align', value ); - else - selectedCell.removeStyle( 'text-align' ); - - selectedCell.removeAttribute( 'align' ); - } - }, - { - type : 'select', - id : 'vAlign', - labelLayout : 'horizontal', - label : langCell.vAlign, - widths : [ '50%', '50%' ], - 'default' : '', - items : - [ - [ langCommon.notSet, '' ], - [ langCell.alignTop, 'top' ], - [ langCell.alignMiddle, 'middle' ], - [ langCell.alignBottom, 'bottom' ], - [ langCell.alignBaseline, 'baseline' ] - ], - setup : function( element ) - { - var vAlignAttr = element.getAttribute( 'vAlign' ), - vAlignStyle = element.getStyle( 'vertical-align' ); - - switch( vAlignStyle ) - { - // Ignore all other unrelated style values.. - case 'top': - case 'middle': - case 'bottom': - case 'baseline': - break; - default: - vAlignStyle = ''; - } - - this.setValue( vAlignStyle || vAlignAttr || '' ); - }, - commit : function( element ) - { - var value = this.getValue(); - - if ( value ) - element.setStyle( 'vertical-align', value ); - else - element.removeStyle( 'vertical-align' ); - - element.removeAttribute( 'vAlign' ); - } - } - ] - }, - spacer(), - { - type : 'vbox', - padding : 0, - children : - [ - { - type : 'select', - id : 'cellType', - label : langCell.cellType, - labelLayout : 'horizontal', - widths : [ '50%', '50%' ], - 'default' : 'td', - items : - [ - [ langCell.data, 'td' ], - [ langCell.header, 'th' ] - ], - setup : function( selectedCell ) - { - this.setValue( selectedCell.getName() ); - }, - commit : function( selectedCell ) - { - selectedCell.renameNode( this.getValue() ); - } - }, - spacer(), - { - type : 'text', - id : 'rowSpan', - label : langCell.rowSpan, - labelLayout : 'horizontal', - widths : [ '50%', '50%' ], - 'default' : '', - validate : validate.integer( langCell.invalidRowSpan ), - setup : function( selectedCell ) - { - var attrVal = parseInt( selectedCell.getAttribute( 'rowSpan' ), 10 ); - if ( attrVal && attrVal != 1 ) - this.setValue( attrVal ); - }, - commit : function( selectedCell ) - { - var value = parseInt( this.getValue(), 10 ); - if ( value && value != 1 ) - selectedCell.setAttribute( 'rowSpan', this.getValue() ); - else - selectedCell.removeAttribute( 'rowSpan' ); - } - }, - { - type : 'text', - id : 'colSpan', - label : langCell.colSpan, - labelLayout : 'horizontal', - widths : [ '50%', '50%' ], - 'default' : '', - validate : validate.integer( langCell.invalidColSpan ), - setup : function( element ) - { - var attrVal = parseInt( element.getAttribute( 'colSpan' ), 10 ); - if ( attrVal && attrVal != 1 ) - this.setValue( attrVal ); - }, - commit : function( selectedCell ) - { - var value = parseInt( this.getValue(), 10 ); - if ( value && value != 1 ) - selectedCell.setAttribute( 'colSpan', this.getValue() ); - else - selectedCell.removeAttribute( 'colSpan' ); - } - }, - spacer(), - { - type : 'hbox', - padding : 0, - widths : [ '80%', '20%' ], - children : - [ - { - type : 'text', - id : 'bgColor', - label : langCell.bgColor, - labelLayout : 'horizontal', - widths : [ '70%', '30%' ], - 'default' : '', - setup : function( element ) - { - var bgColorAttr = element.getAttribute( 'bgColor' ), - bgColorStyle = element.getStyle( 'background-color' ); - - this.setValue( bgColorStyle || bgColorAttr ); - }, - commit : function( selectedCell ) - { - var value = this.getValue(); - - if ( value ) - selectedCell.setStyle( 'background-color', this.getValue() ); - else - selectedCell.removeStyle( 'background-color' ); - - selectedCell.removeAttribute( 'bgColor'); - } - }, - { - type : 'button', - id : 'bgColorChoose', - label : langCell.chooseColor, - style : 'margin-left: 10px', - onClick : function() - { - var self = this; - getDialogValue( 'colordialog', function( colorDialog ) - { - self.getDialog().getContentElement( 'info', 'bgColor' ).setValue( - colorDialog.getContentElement( 'picker', 'selectedColor' ).getValue() - ); - } ); - } - } - ] - }, - spacer(), - { - type : 'hbox', - padding : 0, - widths : [ '80%', '20%' ], - children : - [ - { - type : 'text', - id : 'borderColor', - label : langCell.borderColor, - labelLayout : 'horizontal', - widths : [ '70%', '30%' ], - 'default' : '', - setup : function( element ) - { - var borderColorAttr = element.getAttribute( 'borderColor' ), - borderColorStyle = element.getStyle( 'border-color' ); - - this.setValue( borderColorStyle || borderColorAttr ); - }, - commit : function( selectedCell ) - { - var value = this.getValue(); - if ( value ) - selectedCell.setStyle( 'border-color', this.getValue() ); - else - selectedCell.removeStyle( 'border-color' ); - - selectedCell.removeAttribute( 'borderColor'); - } - }, - { - type : 'button', - id : 'borderColorChoose', - label : langCell.chooseColor, - style : 'margin-left: 10px', - onClick : function() - { - var self = this; - getDialogValue( 'colordialog', function( colorDialog ) - { - self.getDialog().getContentElement( 'info', 'borderColor' ).setValue( - colorDialog.getContentElement( 'picker', 'selectedColor' ).getValue() - ); - } ); - } - } - ] - } - ] - } - ] - } - ] - } - ], - onShow : function() - { - this.cells = CKEDITOR.plugins.tabletools.getSelectedCells( - this._.editor.getSelection() ); - this.setupContent( this.cells[ 0 ] ); - }, - onOk : function() - { - var selection = this._.editor.getSelection(), - bookmarks = selection.createBookmarks(); - - var cells = this.cells; - for ( var i = 0 ; i < cells.length ; i++ ) - this.commitContent( cells[ i ] ); - - selection.selectBookmarks( bookmarks ); - } - }; - } ); +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.dialog.add( 'cellProperties', function( editor )
+ {
+ var langTable = editor.lang.table,
+ langCell = langTable.cell,
+ langCommon = editor.lang.common,
+ validate = CKEDITOR.dialog.validate,
+ widthPattern = /^(\d+(?:\.\d+)?)(px|%)$/,
+ heightPattern = /^(\d+(?:\.\d+)?)px$/,
+ bind = CKEDITOR.tools.bind,
+ spacer = { type : 'html', html : ' ' },
+ rtl = editor.lang.dir == 'rtl';
+
+ /**
+ *
+ * @param dialogName
+ * @param callback [ childDialog ]
+ */
+ function getDialogValue( dialogName, callback )
+ {
+ var onOk = function()
+ {
+ releaseHandlers( this );
+ callback( this, this._.parentDialog );
+ this._.parentDialog.changeFocus();
+ };
+ var onCancel = function()
+ {
+ releaseHandlers( this );
+ this._.parentDialog.changeFocus();
+ };
+ var releaseHandlers = function( dialog )
+ {
+ dialog.removeListener( 'ok', onOk );
+ dialog.removeListener( 'cancel', onCancel );
+ };
+ var bindToDialog = function( dialog )
+ {
+ dialog.on( 'ok', onOk );
+ dialog.on( 'cancel', onCancel );
+ };
+ 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 );
+ };
+ } );
+ });
+ }
+ }
+
+ return {
+ title : langCell.title,
+ minWidth : CKEDITOR.env.ie && CKEDITOR.env.quirks? 450 : 410,
+ minHeight : CKEDITOR.env.ie && ( CKEDITOR.env.ie7Compat || CKEDITOR.env.quirks )? 230 : 200,
+ contents : [
+ {
+ id : 'info',
+ label : langCell.title,
+ accessKey : 'I',
+ elements :
+ [
+ {
+ type : 'hbox',
+ widths : [ '40%', '5%', '40%' ],
+ children :
+ [
+ {
+ type : 'vbox',
+ padding : 0,
+ children :
+ [
+ {
+ type : 'hbox',
+ widths : [ '70%', '30%' ],
+ children :
+ [
+ {
+ type : 'text',
+ id : 'width',
+ width: '100px',
+ label : langCommon.width,
+ validate : validate[ 'number' ]( langCell.invalidWidth ),
+
+ // Extra labelling of width unit type.
+ onLoad : function()
+ {
+ var widthType = this.getDialog().getContentElement( 'info', 'widthType' ),
+ labelElement = widthType.getElement(),
+ inputElement = this.getInputElement(),
+ ariaLabelledByAttr = inputElement.getAttribute( 'aria-labelledby' );
+
+ inputElement.setAttribute( 'aria-labelledby', [ ariaLabelledByAttr, labelElement.$.id ].join( ' ' ) );
+ },
+
+ setup : function( element )
+ {
+ var widthAttr = parseInt( element.getAttribute( 'width' ), 10 ),
+ widthStyle = parseInt( element.getStyle( 'width' ), 10 );
+
+ !isNaN( widthAttr ) && this.setValue( widthAttr );
+ !isNaN( widthStyle ) && this.setValue( widthStyle );
+ },
+ commit : function( element )
+ {
+ var value = parseInt( this.getValue(), 10 ),
+ unit = this.getDialog().getValueOf( 'info', 'widthType' );
+
+ if ( !isNaN( value ) )
+ element.setStyle( 'width', value + unit );
+ else
+ element.removeStyle( 'width' );
+
+ element.removeAttribute( 'width' );
+ },
+ 'default' : ''
+ },
+ {
+ type : 'select',
+ id : 'widthType',
+ label : editor.lang.table.widthUnit,
+ labelStyle: 'visibility:hidden',
+ 'default' : 'px',
+ items :
+ [
+ [ langTable.widthPx, 'px' ],
+ [ langTable.widthPc, '%' ]
+ ],
+ setup : function( selectedCell )
+ {
+ var widthMatch = widthPattern.exec( selectedCell.getStyle( 'width' ) || selectedCell.getAttribute( 'width' ) );
+ if ( widthMatch )
+ this.setValue( widthMatch[2] );
+ }
+ }
+ ]
+ },
+ {
+ type : 'hbox',
+ widths : [ '70%', '30%' ],
+ children :
+ [
+ {
+ type : 'text',
+ id : 'height',
+ label : langCommon.height,
+ width: '100px',
+ 'default' : '',
+ validate : validate[ 'number' ]( langCell.invalidHeight ),
+
+ // Extra labelling of height unit type.
+ onLoad : function()
+ {
+ var heightType = this.getDialog().getContentElement( 'info', 'htmlHeightType' ),
+ labelElement = heightType.getElement(),
+ inputElement = this.getInputElement(),
+ ariaLabelledByAttr = inputElement.getAttribute( 'aria-labelledby' );
+
+ inputElement.setAttribute( 'aria-labelledby', [ ariaLabelledByAttr, labelElement.$.id ].join( ' ' ) );
+ },
+
+ setup : function( element )
+ {
+ var heightAttr = parseInt( element.getAttribute( 'height' ), 10 ),
+ heightStyle = parseInt( element.getStyle( 'height' ), 10 );
+
+ !isNaN( heightAttr ) && this.setValue( heightAttr );
+ !isNaN( heightStyle ) && this.setValue( heightStyle );
+ },
+ commit : function( element )
+ {
+ var value = parseInt( this.getValue(), 10 );
+
+ if ( !isNaN( value ) )
+ element.setStyle( 'height', CKEDITOR.tools.cssLength( value ) );
+ else
+ element.removeStyle( 'height' );
+
+ element.removeAttribute( 'height' );
+ }
+ },
+ {
+ id : 'htmlHeightType',
+ type : 'html',
+ html : '<br />'+ langTable.widthPx
+ }
+ ]
+ },
+ spacer,
+ {
+ type : 'select',
+ id : 'wordWrap',
+ label : langCell.wordWrap,
+ 'default' : 'yes',
+ items :
+ [
+ [ langCell.yes, 'yes' ],
+ [ langCell.no, 'no' ]
+ ],
+ setup : function( element )
+ {
+ var wordWrapAttr = element.getAttribute( 'noWrap' ),
+ wordWrapStyle = element.getStyle( 'white-space' );
+
+ if ( wordWrapStyle == 'nowrap' || wordWrapAttr )
+ this.setValue( 'no' );
+ },
+ commit : function( element )
+ {
+ if ( this.getValue() == 'no' )
+ element.setStyle( 'white-space', 'nowrap' );
+ else
+ element.removeStyle( 'white-space' );
+
+ element.removeAttribute( 'noWrap' );
+ }
+ },
+ spacer,
+ {
+ type : 'select',
+ id : 'hAlign',
+ label : langCell.hAlign,
+ 'default' : '',
+ items :
+ [
+ [ langCommon.notSet, '' ],
+ [ langCommon.alignLeft, 'left' ],
+ [ langCommon.alignCenter, 'center' ],
+ [ langCommon.alignRight, 'right' ]
+ ],
+ setup : function( element )
+ {
+ var alignAttr = element.getAttribute( 'align' ),
+ textAlignStyle = element.getStyle( 'text-align');
+
+ this.setValue( textAlignStyle || alignAttr || '' );
+ },
+ commit : function( selectedCell )
+ {
+ var value = this.getValue();
+
+ if ( value )
+ selectedCell.setStyle( 'text-align', value );
+ else
+ selectedCell.removeStyle( 'text-align' );
+
+ selectedCell.removeAttribute( 'align' );
+ }
+ },
+ {
+ type : 'select',
+ id : 'vAlign',
+ label : langCell.vAlign,
+ 'default' : '',
+ items :
+ [
+ [ langCommon.notSet, '' ],
+ [ langCommon.alignTop, 'top' ],
+ [ langCommon.alignMiddle, 'middle' ],
+ [ langCommon.alignBottom, 'bottom' ],
+ [ langCell.alignBaseline, 'baseline' ]
+ ],
+ setup : function( element )
+ {
+ var vAlignAttr = element.getAttribute( 'vAlign' ),
+ vAlignStyle = element.getStyle( 'vertical-align' );
+
+ switch( vAlignStyle )
+ {
+ // Ignore all other unrelated style values..
+ case 'top':
+ case 'middle':
+ case 'bottom':
+ case 'baseline':
+ break;
+ default:
+ vAlignStyle = '';
+ }
+
+ this.setValue( vAlignStyle || vAlignAttr || '' );
+ },
+ commit : function( element )
+ {
+ var value = this.getValue();
+
+ if ( value )
+ element.setStyle( 'vertical-align', value );
+ else
+ element.removeStyle( 'vertical-align' );
+
+ element.removeAttribute( 'vAlign' );
+ }
+ }
+ ]
+ },
+ spacer,
+ {
+ type : 'vbox',
+ padding : 0,
+ children :
+ [
+ {
+ type : 'select',
+ id : 'cellType',
+ label : langCell.cellType,
+ 'default' : 'td',
+ items :
+ [
+ [ langCell.data, 'td' ],
+ [ langCell.header, 'th' ]
+ ],
+ setup : function( selectedCell )
+ {
+ this.setValue( selectedCell.getName() );
+ },
+ commit : function( selectedCell )
+ {
+ selectedCell.renameNode( this.getValue() );
+ }
+ },
+ spacer,
+ {
+ type : 'text',
+ id : 'rowSpan',
+ label : langCell.rowSpan,
+ 'default' : '',
+ validate : validate.integer( langCell.invalidRowSpan ),
+ setup : function( selectedCell )
+ {
+ var attrVal = parseInt( selectedCell.getAttribute( 'rowSpan' ), 10 );
+ if ( attrVal && attrVal != 1 )
+ this.setValue( attrVal );
+ },
+ commit : function( selectedCell )
+ {
+ var value = parseInt( this.getValue(), 10 );
+ if ( value && value != 1 )
+ selectedCell.setAttribute( 'rowSpan', this.getValue() );
+ else
+ selectedCell.removeAttribute( 'rowSpan' );
+ }
+ },
+ {
+ type : 'text',
+ id : 'colSpan',
+ label : langCell.colSpan,
+ 'default' : '',
+ validate : validate.integer( langCell.invalidColSpan ),
+ setup : function( element )
+ {
+ var attrVal = parseInt( element.getAttribute( 'colSpan' ), 10 );
+ if ( attrVal && attrVal != 1 )
+ this.setValue( attrVal );
+ },
+ commit : function( selectedCell )
+ {
+ var value = parseInt( this.getValue(), 10 );
+ if ( value && value != 1 )
+ selectedCell.setAttribute( 'colSpan', this.getValue() );
+ else
+ selectedCell.removeAttribute( 'colSpan' );
+ }
+ },
+ spacer,
+ {
+ type : 'hbox',
+ padding : 0,
+ widths : [ '60%', '40%' ],
+ children :
+ [
+ {
+ type : 'text',
+ id : 'bgColor',
+ label : langCell.bgColor,
+ 'default' : '',
+ setup : function( element )
+ {
+ var bgColorAttr = element.getAttribute( 'bgColor' ),
+ bgColorStyle = element.getStyle( 'background-color' );
+
+ this.setValue( bgColorStyle || bgColorAttr );
+ },
+ commit : function( selectedCell )
+ {
+ var value = this.getValue();
+
+ if ( value )
+ selectedCell.setStyle( 'background-color', this.getValue() );
+ else
+ selectedCell.removeStyle( 'background-color' );
+
+ selectedCell.removeAttribute( 'bgColor');
+ }
+ },
+ {
+ type : 'button',
+ id : 'bgColorChoose',
+ "class" : 'colorChooser',
+ label : langCell.chooseColor,
+ onLoad : function()
+ {
+ // Stick the element to the bottom (#5587)
+ this.getElement().getParent().setStyle( 'vertical-align', 'bottom' );
+ },
+ onClick : function()
+ {
+ var self = this;
+ getDialogValue( 'colordialog', function( colorDialog )
+ {
+ self.getDialog().getContentElement( 'info', 'bgColor' ).setValue(
+ colorDialog.getContentElement( 'picker', 'selectedColor' ).getValue()
+ );
+ } );
+ }
+ }
+ ]
+ },
+ spacer,
+ {
+ type : 'hbox',
+ padding : 0,
+ widths : [ '60%', '40%' ],
+ children :
+ [
+ {
+ type : 'text',
+ id : 'borderColor',
+ label : langCell.borderColor,
+ 'default' : '',
+ setup : function( element )
+ {
+ var borderColorAttr = element.getAttribute( 'borderColor' ),
+ borderColorStyle = element.getStyle( 'border-color' );
+
+ this.setValue( borderColorStyle || borderColorAttr );
+ },
+ commit : function( selectedCell )
+ {
+ var value = this.getValue();
+ if ( value )
+ selectedCell.setStyle( 'border-color', this.getValue() );
+ else
+ selectedCell.removeStyle( 'border-color' );
+
+ selectedCell.removeAttribute( 'borderColor');
+ }
+ },
+ {
+ type : 'button',
+ id : 'borderColorChoose',
+ "class" : 'colorChooser',
+ label : langCell.chooseColor,
+ style : ( rtl ? 'margin-right' : 'margin-left' ) + ': 10px',
+ onLoad : function()
+ {
+ // Stick the element to the bottom (#5587)
+ this.getElement().getParent().setStyle( 'vertical-align', 'bottom' );
+ },
+ onClick : function()
+ {
+ var self = this;
+ getDialogValue( 'colordialog', function( colorDialog )
+ {
+ self.getDialog().getContentElement( 'info', 'borderColor' ).setValue(
+ colorDialog.getContentElement( 'picker', 'selectedColor' ).getValue()
+ );
+ } );
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ onShow : function()
+ {
+ this.cells = CKEDITOR.plugins.tabletools.getSelectedCells(
+ this._.editor.getSelection() );
+ this.setupContent( this.cells[ 0 ] );
+ },
+ onOk : function()
+ {
+ var selection = this._.editor.getSelection(),
+ bookmarks = selection.createBookmarks();
+
+ var cells = this.cells;
+ for ( var i = 0 ; i < cells.length ; i++ )
+ this.commitContent( cells[ i ] );
+
+ this._.editor.forceNextSelectionCheck();
+ selection.selectBookmarks( bookmarks );
+ this._.editor.selectionChange();
+ }
+ };
+ } );
diff --git a/_source/plugins/tabletools/plugin.js b/_source/plugins/tabletools/plugin.js index 6471793..faffb47 100644 --- a/_source/plugins/tabletools/plugin.js +++ b/_source/plugins/tabletools/plugin.js @@ -1,1116 +1,1185 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -(function() -{ - function removeRawAttribute( $node, attr ) - { - if ( CKEDITOR.env.ie ) - $node.removeAttribute( attr ); - else - delete $node[ attr ]; - } - - var cellNodeRegex = /^(?:td|th)$/; - - function getSelectedCells( selection ) - { - // Walker will try to split text nodes, which will make the current selection - // invalid. So save bookmarks before doing anything. - var bookmarks = selection.createBookmarks(); - - var ranges = selection.getRanges(); - var retval = []; - var database = {}; - - function moveOutOfCellGuard( node ) - { - // Apply to the first cell only. - if ( retval.length > 0 ) - return; - - // If we are exiting from the first </td>, then the td should definitely be - // included. - if ( node.type == CKEDITOR.NODE_ELEMENT && cellNodeRegex.test( node.getName() ) - && !node.getCustomData( 'selected_cell' ) ) - { - CKEDITOR.dom.element.setMarker( database, node, 'selected_cell', true ); - retval.push( node ); - } - } - - for ( var i = 0 ; i < ranges.length ; i++ ) - { - var range = ranges[ i ]; - - if ( range.collapsed ) - { - // Walker does not handle collapsed ranges yet - fall back to old API. - var startNode = range.getCommonAncestor(); - var nearestCell = startNode.getAscendant( 'td', true ) || startNode.getAscendant( 'th', true ); - if ( nearestCell ) - retval.push( nearestCell ); - } - else - { - var walker = new CKEDITOR.dom.walker( range ); - var node; - walker.guard = moveOutOfCellGuard; - - while ( ( node = walker.next() ) ) - { - // If may be possible for us to have a range like this: - // <td>^1</td><td>^2</td> - // The 2nd td shouldn't be included. - // - // So we have to take care to include a td we've entered only when we've - // walked into its children. - - var parent = node.getParent(); - if ( parent && cellNodeRegex.test( parent.getName() ) && !parent.getCustomData( 'selected_cell' ) ) - { - CKEDITOR.dom.element.setMarker( database, parent, 'selected_cell', true ); - retval.push( parent ); - } - } - } - } - - CKEDITOR.dom.element.clearAllMarkers( database ); - - // Restore selection position. - selection.selectBookmarks( bookmarks ); - - return retval; - } - - function getFocusElementAfterDelCells( cellsToDelete ) { - var i = 0, - last = cellsToDelete.length - 1, - database = {}, - cell,focusedCell, - tr; - - while ( ( cell = cellsToDelete[ i++ ] ) ) - CKEDITOR.dom.element.setMarker( database, cell, 'delete_cell', true ); - - // 1.first we check left or right side focusable cell row by row; - i = 0; - while ( ( cell = cellsToDelete[ i++ ] ) ) - { - if ( ( focusedCell = cell.getPrevious() ) && !focusedCell.getCustomData( 'delete_cell' ) - || ( focusedCell = cell.getNext() ) && !focusedCell.getCustomData( 'delete_cell' ) ) - { - CKEDITOR.dom.element.clearAllMarkers( database ); - return focusedCell; - } - } - - CKEDITOR.dom.element.clearAllMarkers( database ); - - // 2. then we check the toppest row (outside the selection area square) focusable cell - tr = cellsToDelete[ 0 ].getParent(); - if ( ( tr = tr.getPrevious() ) ) - return tr.getLast(); - - // 3. last we check the lowerest row focusable cell - tr = cellsToDelete[ last ].getParent(); - if ( ( tr = tr.getNext() ) ) - return tr.getChild( 0 ); - - return null; - } - - function clearRow( $tr ) - { - // Get the array of row's cells. - var $cells = $tr.cells; - - // Empty all cells. - for ( var i = 0 ; i < $cells.length ; i++ ) - { - $cells[ i ].innerHTML = ''; - - if ( !CKEDITOR.env.ie ) - ( new CKEDITOR.dom.element( $cells[ i ] ) ).appendBogus(); - } - } - - function insertRow( selection, insertBefore ) - { - // Get the row where the selection is placed in. - var row = selection.getStartElement().getAscendant( 'tr' ); - if ( !row ) - return; - - // Create a clone of the row. - var newRow = row.clone( true ); - - // Insert the new row before of it. - newRow.insertBefore( row ); - - // Clean one of the rows to produce the illusion of inserting an empty row - // before or after. - clearRow( insertBefore ? newRow.$ : row.$ ); - } - - function deleteRows( selectionOrRow ) - { - if ( selectionOrRow instanceof CKEDITOR.dom.selection ) - { - var cells = getSelectedCells( selectionOrRow ), - cellsCount = cells.length, - rowsToDelete = [], - cursorPosition, - previousRowIndex, - nextRowIndex; - - // Queue up the rows - it's possible and likely that we have duplicates. - for ( var i = 0 ; i < cellsCount ; i++ ) - { - var row = cells[ i ].getParent(), - rowIndex = row.$.rowIndex; - - !i && ( previousRowIndex = rowIndex - 1 ); - rowsToDelete[ rowIndex ] = row; - i == cellsCount - 1 && ( nextRowIndex = rowIndex + 1 ); - } - - var table = row.getAscendant( 'table' ), - rows = table.$.rows, - rowCount = rows.length; - - // Where to put the cursor after rows been deleted? - // 1. Into next sibling row if any; - // 2. Into previous sibling row if any; - // 3. Into table's parent element if it's the very last row. - cursorPosition = new CKEDITOR.dom.element( - nextRowIndex < rowCount && table.$.rows[ nextRowIndex ] || - previousRowIndex > 0 && table.$.rows[ previousRowIndex ] || - table.$.parentNode ); - - for ( i = rowsToDelete.length ; i >= 0 ; i-- ) - { - if ( rowsToDelete[ i ] ) - deleteRows( rowsToDelete[ i ] ); - } - - return cursorPosition; - } - else if ( selectionOrRow instanceof CKEDITOR.dom.element ) - { - table = selectionOrRow.getAscendant( 'table' ); - - if ( table.$.rows.length == 1 ) - table.remove(); - else - selectionOrRow.remove(); - } - - return 0; - } - - function insertColumn( selection, insertBefore ) - { - // Get the cell where the selection is placed in. - var startElement = selection.getStartElement(); - var cell = startElement.getAscendant( 'td', true ) || startElement.getAscendant( 'th', true ); - - if ( !cell ) - return; - - // Get the cell's table. - var table = cell.getAscendant( 'table' ); - var cellIndex = cell.$.cellIndex; - - // Loop through all rows available in the table. - for ( var i = 0 ; i < table.$.rows.length ; i++ ) - { - var $row = table.$.rows[ i ]; - - // If the row doesn't have enough cells, ignore it. - if ( $row.cells.length < ( cellIndex + 1 ) ) - continue; - - cell = new CKEDITOR.dom.element( $row.cells[ cellIndex ].cloneNode( false ) ); - - if ( !CKEDITOR.env.ie ) - cell.appendBogus(); - - // Get back the currently selected cell. - var baseCell = new CKEDITOR.dom.element( $row.cells[ cellIndex ] ); - if ( insertBefore ) - cell.insertBefore( baseCell ); - else - cell.insertAfter( baseCell ); - } - } - - function getFocusElementAfterDelCols( cells ) - { - var cellIndexList = [], - table = cells[ 0 ] && cells[ 0 ].getAscendant( 'table' ), - i, length, - targetIndex, targetCell; - - // get the cellIndex list of delete cells - for ( i = 0, length = cells.length; i < length; i++ ) - cellIndexList.push( cells[i].$.cellIndex ); - - // get the focusable column index - cellIndexList.sort(); - for ( i = 1, length = cellIndexList.length; i < length; i++ ) - { - if ( cellIndexList[ i ] - cellIndexList[ i - 1 ] > 1 ) - { - targetIndex = cellIndexList[ i - 1 ] + 1; - break; - } - } - - if ( !targetIndex ) - targetIndex = cellIndexList[ 0 ] > 0 ? ( cellIndexList[ 0 ] - 1 ) - : ( cellIndexList[ cellIndexList.length - 1 ] + 1 ); - - // scan row by row to get the target cell - var rows = table.$.rows; - for ( i = 0, length = rows.length; i < length ; i++ ) - { - targetCell = rows[ i ].cells[ targetIndex ]; - if ( targetCell ) - break; - } - - return targetCell ? new CKEDITOR.dom.element( targetCell ) : table.getPrevious(); - } - - function deleteColumns( selectionOrCell ) - { - if ( selectionOrCell instanceof CKEDITOR.dom.selection ) - { - var colsToDelete = getSelectedCells( selectionOrCell ), - elementToFocus = getFocusElementAfterDelCols( colsToDelete ); - - for ( var i = colsToDelete.length - 1 ; i >= 0 ; i-- ) - { - if ( colsToDelete[ i ] ) - deleteColumns( colsToDelete[ i ] ); - } - - return elementToFocus; - } - else if ( selectionOrCell instanceof CKEDITOR.dom.element ) - { - // Get the cell's table. - var table = selectionOrCell.getAscendant( 'table' ); - if ( !table ) - return null; - - // Get the cell index. - var cellIndex = selectionOrCell.$.cellIndex; - - /* - * Loop through all rows from down to up, coz it's possible that some rows - * will be deleted. - */ - for ( i = table.$.rows.length - 1 ; i >= 0 ; i-- ) - { - // Get the row. - var row = new CKEDITOR.dom.element( table.$.rows[ i ] ); - - // If the cell to be removed is the first one and the row has just one cell. - if ( !cellIndex && row.$.cells.length == 1 ) - { - deleteRows( row ); - continue; - } - - // Else, just delete the cell. - if ( row.$.cells[ cellIndex ] ) - row.$.removeChild( row.$.cells[ cellIndex ] ); - } - } - - return null; - } - - function insertCell( selection, insertBefore ) - { - var startElement = selection.getStartElement(); - var cell = startElement.getAscendant( 'td', true ) || startElement.getAscendant( 'th', true ); - - if ( !cell ) - return; - - // Create the new cell element to be added. - var newCell = cell.clone(); - if ( !CKEDITOR.env.ie ) - newCell.appendBogus(); - - if ( insertBefore ) - newCell.insertBefore( cell ); - else - newCell.insertAfter( cell ); - } - - function deleteCells( selectionOrCell ) - { - if ( selectionOrCell instanceof CKEDITOR.dom.selection ) - { - var cellsToDelete = getSelectedCells( selectionOrCell ); - var table = cellsToDelete[ 0 ] && cellsToDelete[ 0 ].getAscendant( 'table' ); - var cellToFocus = getFocusElementAfterDelCells( cellsToDelete ); - - for ( var i = cellsToDelete.length - 1 ; i >= 0 ; i-- ) - deleteCells( cellsToDelete[ i ] ); - - if ( cellToFocus ) - placeCursorInCell( cellToFocus, true ); - else if ( table ) - table.remove(); - } - else if ( selectionOrCell instanceof CKEDITOR.dom.element ) - { - var tr = selectionOrCell.getParent(); - if ( tr.getChildCount() == 1 ) - tr.remove(); - else - selectionOrCell.remove(); - } - } - - // Remove filler at end and empty spaces around the cell content. - function trimCell( cell ) - { - var bogus = cell.getBogus(); - bogus && bogus.remove(); - cell.trim(); - } - - function placeCursorInCell( cell, placeAtEnd ) - { - var range = new CKEDITOR.dom.range( cell.getDocument() ); - if ( !range[ 'moveToElementEdit' + ( placeAtEnd ? 'End' : 'Start' ) ]( cell ) ) - { - range.selectNodeContents( cell ); - range.collapse( placeAtEnd ? false : true ); - } - range.select( true ); - } - - function buildTableMap( table ) - { - - var aRows = table.$.rows ; - - // Row and Column counters. - var r = -1 ; - - var aMap = []; - - for ( var i = 0 ; i < aRows.length ; i++ ) - { - r++ ; - !aMap[r] && ( aMap[r] = [] ); - - var c = -1 ; - - for ( var j = 0 ; j < aRows[i].cells.length ; j++ ) - { - var oCell = aRows[i].cells[j] ; - - c++ ; - while ( aMap[r][c] ) - c++ ; - - var iColSpan = isNaN( oCell.colSpan ) ? 1 : oCell.colSpan ; - var iRowSpan = isNaN( oCell.rowSpan ) ? 1 : oCell.rowSpan ; - - for ( var rs = 0 ; rs < iRowSpan ; rs++ ) - { - if ( !aMap[r + rs] ) - aMap[r + rs] = new Array() ; - - for ( var cs = 0 ; cs < iColSpan ; cs++ ) - { - aMap[r + rs][c + cs] = aRows[i].cells[j] ; - } - } - - c += iColSpan - 1 ; - } - } - return aMap ; - } - - function cellInRow( tableMap, rowIndex, cell ) - { - var oRow = tableMap[ rowIndex ]; - if ( typeof cell == 'undefined' ) - return oRow; - - for ( var c = 0 ; oRow && c < oRow.length ; c++ ) - { - if ( cell.is && oRow[c] == cell.$ ) - return c; - else if ( c == cell ) - return new CKEDITOR.dom.element( oRow[ c ] ); - } - return cell.is ? -1 : null; - } - - function cellInCol( tableMap, colIndex, cell ) - { - var oCol = []; - for ( var r = 0; r < tableMap.length; r++ ) - { - var row = tableMap[ r ]; - if ( typeof cell == 'undefined' ) - oCol.push( row[ colIndex ] ); - else if ( cell.is && row[ colIndex ] == cell.$ ) - return r; - else if ( r == cell ) - return new CKEDITOR.dom.element( row[ colIndex ] ); - } - - return ( typeof cell == 'undefined' )? oCol : cell.is ? -1 : null; - } - - function mergeCells( selection, mergeDirection, isDetect ) - { - var cells = getSelectedCells( selection ); - - // Invalid merge request if: - // 1. In batch mode despite that less than two selected. - // 2. In solo mode while not exactly only one selected. - // 3. Cells distributed in different table groups (e.g. from both thead and tbody). - var commonAncestor; - if ( ( mergeDirection ? cells.length != 1 : cells.length < 2 ) - || ( commonAncestor = selection.getCommonAncestor() ) - && commonAncestor.type == CKEDITOR.NODE_ELEMENT - && commonAncestor.is( 'table' ) ) - { - return false; - } - - var cell, - firstCell = cells[ 0 ], - table = firstCell.getAscendant( 'table' ), - map = buildTableMap( table ), - mapHeight = map.length, - mapWidth = map[ 0 ].length, - startRow = firstCell.getParent().$.rowIndex, - startColumn = cellInRow( map, startRow, firstCell ); - - if ( mergeDirection ) - { - var targetCell; - try - { - targetCell = - map[ mergeDirection == 'up' ? - ( startRow - 1 ): - mergeDirection == 'down' ? ( startRow + 1 ) : startRow ] [ - mergeDirection == 'left' ? - ( startColumn - 1 ): - mergeDirection == 'right' ? ( startColumn + 1 ) : startColumn ]; - - } - catch( er ) - { - return false; - } - - // 1. No cell could be merged. - // 2. Same cell actually. - if ( !targetCell || firstCell.$ == targetCell ) - return false; - - // Sort in map order regardless of the DOM sequence. - cells[ ( mergeDirection == 'up' || mergeDirection == 'left' ) ? - 'unshift' : 'push' ]( new CKEDITOR.dom.element( targetCell ) ); - } - - // Start from here are merging way ignorance (merge up/right, batch merge). - var doc = firstCell.getDocument(), - lastRowIndex = startRow, - totalRowSpan = 0, - totalColSpan = 0, - // Use a documentFragment as buffer when appending cell contents. - frag = !isDetect && new CKEDITOR.dom.documentFragment( doc ), - dimension = 0; - - for ( var i = 0; i < cells.length; i++ ) - { - cell = cells[ i ]; - - var tr = cell.getParent(), - cellFirstChild = cell.getFirst(), - colSpan = cell.$.colSpan, - rowSpan = cell.$.rowSpan, - rowIndex = tr.$.rowIndex, - colIndex = cellInRow( map, rowIndex, cell ); - - // Accumulated the actual places taken by all selected cells. - dimension += colSpan * rowSpan; - // Accumulated the maximum virtual spans from column and row. - totalColSpan = Math.max( totalColSpan, colIndex - startColumn + colSpan ) ; - totalRowSpan = Math.max( totalRowSpan, rowIndex - startRow + rowSpan ); - - if ( !isDetect ) - { - // Trim all cell fillers and check to remove empty cells. - if ( trimCell( cell ), cell.getChildren().count() ) - { - // Merge vertically cells as two separated paragraphs. - if ( rowIndex != lastRowIndex - && cellFirstChild - && !( cellFirstChild.isBlockBoundary - && cellFirstChild.isBlockBoundary( { br : 1 } ) ) ) - { - var last = frag.getLast( CKEDITOR.dom.walker.whitespaces( true ) ); - if ( last && !( last.is && last.is( 'br' ) ) ) - frag.append( new CKEDITOR.dom.element( 'br' ) ); - } - - cell.moveChildren( frag ); - } - i ? cell.remove() : cell.setHtml( '' ); - } - lastRowIndex = rowIndex; - } - - if ( !isDetect ) - { - frag.moveChildren( firstCell ); - - if ( !CKEDITOR.env.ie ) - firstCell.appendBogus(); - - if ( totalColSpan >= mapWidth ) - firstCell.removeAttribute( 'rowSpan' ); - else - firstCell.$.rowSpan = totalRowSpan; - - if ( totalRowSpan >= mapHeight ) - firstCell.removeAttribute( 'colSpan' ); - else - firstCell.$.colSpan = totalColSpan; - - // Swip empty <tr> left at the end of table due to the merging. - var trs = new CKEDITOR.dom.nodeList( table.$.rows ), - count = trs.count(); - - for ( i = count - 1; i >= 0; i-- ) - { - var tailTr = trs.getItem( i ); - if ( !tailTr.$.cells.length ) - { - tailTr.remove(); - count++; - continue; - } - } - - return firstCell; - } - // Be able to merge cells only if actual dimension of selected - // cells equals to the caculated rectangle. - else - return ( totalRowSpan * totalColSpan ) == dimension; - } - - function verticalSplitCell ( selection, isDetect ) - { - var cells = getSelectedCells( selection ); - if ( cells.length > 1 ) - return false; - else if ( isDetect ) - return true; - - var cell = cells[ 0 ], - tr = cell.getParent(), - table = tr.getAscendant( 'table' ), - map = buildTableMap( table ), - rowIndex = tr.$.rowIndex, - colIndex = cellInRow( map, rowIndex, cell ), - rowSpan = cell.$.rowSpan, - newCell, - newRowSpan, - newCellRowSpan, - newRowIndex; - - if ( rowSpan > 1 ) - { - newRowSpan = Math.ceil( rowSpan / 2 ); - newCellRowSpan = Math.floor( rowSpan / 2 ); - newRowIndex = rowIndex + newRowSpan; - var newCellTr = new CKEDITOR.dom.element( table.$.rows[ newRowIndex ] ), - newCellRow = cellInRow( map, newRowIndex ), - candidateCell; - - newCell = cell.clone(); - - // Figure out where to insert the new cell by checking the vitual row. - for ( var c = 0; c < newCellRow.length; c++ ) - { - candidateCell = newCellRow[ c ]; - // Catch first cell actually following the column. - if ( candidateCell.parentNode == newCellTr.$ - && c > colIndex ) - { - newCell.insertBefore( new CKEDITOR.dom.element( candidateCell ) ); - break; - } - else - candidateCell = null; - } - - // The destination row is empty, append at will. - if ( !candidateCell ) - newCellTr.append( newCell, true ); - } - else - { - newCellRowSpan = newRowSpan = 1; - - newCellTr = tr.clone(); - newCellTr.insertAfter( tr ); - newCellTr.append( newCell = cell.clone() ); - - var cellsInSameRow = cellInRow( map, rowIndex ); - for ( var i = 0; i < cellsInSameRow.length; i++ ) - cellsInSameRow[ i ].rowSpan++; - } - - if ( !CKEDITOR.env.ie ) - newCell.appendBogus(); - - cell.$.rowSpan = newRowSpan; - newCell.$.rowSpan = newCellRowSpan; - if ( newRowSpan == 1 ) - cell.removeAttribute( 'rowSpan' ); - if ( newCellRowSpan == 1 ) - newCell.removeAttribute( 'rowSpan' ); - - return newCell; - } - - function horizontalSplitCell( selection, isDetect ) - { - var cells = getSelectedCells( selection ); - if ( cells.length > 1 ) - return false; - else if ( isDetect ) - return true; - - var cell = cells[ 0 ], - tr = cell.getParent(), - table = tr.getAscendant( 'table' ), - map = buildTableMap( table ), - rowIndex = tr.$.rowIndex, - colIndex = cellInRow( map, rowIndex, cell ), - colSpan = cell.$.colSpan, - newCell, - newColSpan, - newCellColSpan; - - if ( colSpan > 1 ) - { - newColSpan = Math.ceil( colSpan / 2 ); - newCellColSpan = Math.floor( colSpan / 2 ); - } - else - { - newCellColSpan = newColSpan = 1; - var cellsInSameCol = cellInCol( map, colIndex ); - for ( var i = 0; i < cellsInSameCol.length; i++ ) - cellsInSameCol[ i ].colSpan++; - } - newCell = cell.clone(); - newCell.insertAfter( cell ); - if ( !CKEDITOR.env.ie ) - newCell.appendBogus(); - - cell.$.colSpan = newColSpan; - newCell.$.colSpan = newCellColSpan; - if ( newColSpan == 1 ) - cell.removeAttribute( 'colSpan' ); - if ( newCellColSpan == 1 ) - newCell.removeAttribute( 'colSpan' ); - - return newCell; - } - // Context menu on table caption incorrect (#3834) - var contextMenuTags = { thead : 1, tbody : 1, tfoot : 1, td : 1, tr : 1, th : 1 }; - - CKEDITOR.plugins.tabletools = - { - init : function( editor ) - { - var lang = editor.lang.table; - - editor.addCommand( 'cellProperties', new CKEDITOR.dialogCommand( 'cellProperties' ) ); - CKEDITOR.dialog.add( 'cellProperties', this.path + 'dialogs/tableCell.js' ); - - editor.addCommand( 'tableDelete', - { - exec : function( editor ) - { - var selection = editor.getSelection(); - var startElement = selection && selection.getStartElement(); - var table = startElement && startElement.getAscendant( 'table', true ); - - if ( !table ) - return; - - // Maintain the selection point at where the table was deleted. - selection.selectElement( table ); - var range = selection.getRanges()[0]; - range.collapse(); - selection.selectRanges( [ range ] ); - - // If the table's parent has only one child, remove it,except body,as well.( #5416 ) - var parent = table.getParent(); - if ( parent.getChildCount() == 1 && parent.getName() != 'body' ) - parent.remove(); - else - table.remove(); - } - } ); - - editor.addCommand( 'rowDelete', - { - exec : function( editor ) - { - var selection = editor.getSelection(); - placeCursorInCell( deleteRows( selection ) ); - } - } ); - - editor.addCommand( 'rowInsertBefore', - { - exec : function( editor ) - { - var selection = editor.getSelection(); - insertRow( selection, true ); - } - } ); - - editor.addCommand( 'rowInsertAfter', - { - exec : function( editor ) - { - var selection = editor.getSelection(); - insertRow( selection ); - } - } ); - - editor.addCommand( 'columnDelete', - { - exec : function( editor ) - { - var selection = editor.getSelection(); - var element = deleteColumns( selection ); - element && placeCursorInCell( element, true ); - } - } ); - - editor.addCommand( 'columnInsertBefore', - { - exec : function( editor ) - { - var selection = editor.getSelection(); - insertColumn( selection, true ); - } - } ); - - editor.addCommand( 'columnInsertAfter', - { - exec : function( editor ) - { - var selection = editor.getSelection(); - insertColumn( selection ); - } - } ); - - editor.addCommand( 'cellDelete', - { - exec : function( editor ) - { - var selection = editor.getSelection(); - deleteCells( selection ); - } - } ); - - editor.addCommand( 'cellMerge', - { - exec : function( editor ) - { - placeCursorInCell( mergeCells( editor.getSelection() ), true ); - } - } ); - - editor.addCommand( 'cellMergeRight', - { - exec : function( editor ) - { - placeCursorInCell( mergeCells( editor.getSelection(), 'right' ), true ); - } - } ); - - editor.addCommand( 'cellMergeDown', - { - exec : function( editor ) - { - placeCursorInCell( mergeCells( editor.getSelection(), 'down' ), true ); - } - } ); - - editor.addCommand( 'cellVerticalSplit', - { - exec : function( editor ) - { - placeCursorInCell( verticalSplitCell( editor.getSelection() ) ); - } - } ); - - editor.addCommand( 'cellHorizontalSplit', - { - exec : function( editor ) - { - placeCursorInCell( horizontalSplitCell( editor.getSelection() ) ); - } - } ); - - editor.addCommand( 'cellInsertBefore', - { - exec : function( editor ) - { - var selection = editor.getSelection(); - insertCell( selection, true ); - } - } ); - - editor.addCommand( 'cellInsertAfter', - { - exec : function( editor ) - { - var selection = editor.getSelection(); - insertCell( selection ); - } - } ); - - // If the "menu" plugin is loaded, register the menu items. - if ( editor.addMenuItems ) - { - editor.addMenuItems( - { - tablecell : - { - label : lang.cell.menu, - group : 'tablecell', - order : 1, - getItems : function() - { - var selection = editor.getSelection(), - cells = getSelectedCells( selection ); - return { - tablecell_insertBefore : CKEDITOR.TRISTATE_OFF, - tablecell_insertAfter : CKEDITOR.TRISTATE_OFF, - tablecell_delete : CKEDITOR.TRISTATE_OFF, - tablecell_merge : mergeCells( selection, null, true ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED, - tablecell_merge_right : mergeCells( selection, 'right', true ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED, - tablecell_merge_down : mergeCells( selection, 'down', true ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED, - tablecell_split_vertical : verticalSplitCell( selection, true ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED, - tablecell_split_horizontal : horizontalSplitCell( selection, true ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED, - tablecell_properties : cells.length > 0 ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED - }; - } - }, - - tablecell_insertBefore : - { - label : lang.cell.insertBefore, - group : 'tablecell', - command : 'cellInsertBefore', - order : 5 - }, - - tablecell_insertAfter : - { - label : lang.cell.insertAfter, - group : 'tablecell', - command : 'cellInsertAfter', - order : 10 - }, - - tablecell_delete : - { - label : lang.cell.deleteCell, - group : 'tablecell', - command : 'cellDelete', - order : 15 - }, - - tablecell_merge : - { - label : lang.cell.merge, - group : 'tablecell', - command : 'cellMerge', - order : 16 - }, - - tablecell_merge_right : - { - label : lang.cell.mergeRight, - group : 'tablecell', - command : 'cellMergeRight', - order : 17 - }, - - tablecell_merge_down : - { - label : lang.cell.mergeDown, - group : 'tablecell', - command : 'cellMergeDown', - order : 18 - }, - - tablecell_split_horizontal : - { - label : lang.cell.splitHorizontal, - group : 'tablecell', - command : 'cellHorizontalSplit', - order : 19 - }, - - tablecell_split_vertical : - { - label : lang.cell.splitVertical, - group : 'tablecell', - command : 'cellVerticalSplit', - order : 20 - }, - - tablecell_properties : - { - label : lang.cell.title, - group : 'tablecellproperties', - command : 'cellProperties', - order : 21 - }, - - tablerow : - { - label : lang.row.menu, - group : 'tablerow', - order : 1, - getItems : function() - { - return { - tablerow_insertBefore : CKEDITOR.TRISTATE_OFF, - tablerow_insertAfter : CKEDITOR.TRISTATE_OFF, - tablerow_delete : CKEDITOR.TRISTATE_OFF - }; - } - }, - - tablerow_insertBefore : - { - label : lang.row.insertBefore, - group : 'tablerow', - command : 'rowInsertBefore', - order : 5 - }, - - tablerow_insertAfter : - { - label : lang.row.insertAfter, - group : 'tablerow', - command : 'rowInsertAfter', - order : 10 - }, - - tablerow_delete : - { - label : lang.row.deleteRow, - group : 'tablerow', - command : 'rowDelete', - order : 15 - }, - - tablecolumn : - { - label : lang.column.menu, - group : 'tablecolumn', - order : 1, - getItems : function() - { - return { - tablecolumn_insertBefore : CKEDITOR.TRISTATE_OFF, - tablecolumn_insertAfter : CKEDITOR.TRISTATE_OFF, - tablecolumn_delete : CKEDITOR.TRISTATE_OFF - }; - } - }, - - tablecolumn_insertBefore : - { - label : lang.column.insertBefore, - group : 'tablecolumn', - command : 'columnInsertBefore', - order : 5 - }, - - tablecolumn_insertAfter : - { - label : lang.column.insertAfter, - group : 'tablecolumn', - command : 'columnInsertAfter', - order : 10 - }, - - tablecolumn_delete : - { - label : lang.column.deleteColumn, - group : 'tablecolumn', - command : 'columnDelete', - order : 15 - } - }); - } - - // If the "contextmenu" plugin is laoded, register the listeners. - if ( editor.contextMenu ) - { - editor.contextMenu.addListener( function( element, selection ) - { - if ( !element ) - return null; - - while ( element ) - { - if ( element.getName() in contextMenuTags ) - { - return { - tablecell : CKEDITOR.TRISTATE_OFF, - tablerow : CKEDITOR.TRISTATE_OFF, - tablecolumn : CKEDITOR.TRISTATE_OFF - }; - } - element = element.getParent(); - } - - return null; - } ); - } - }, - - getSelectedCells : getSelectedCells - - }; - CKEDITOR.plugins.add( 'tabletools', CKEDITOR.plugins.tabletools ); -})(); +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+(function()
+{
+ var cellNodeRegex = /^(?:td|th)$/;
+
+ function getSelectedCells( selection )
+ {
+ var ranges = selection.getRanges();
+ var retval = [];
+ var database = {};
+
+ function moveOutOfCellGuard( node )
+ {
+ // Apply to the first cell only.
+ if ( retval.length > 0 )
+ return;
+
+ // If we are exiting from the first </td>, then the td should definitely be
+ // included.
+ if ( node.type == CKEDITOR.NODE_ELEMENT && cellNodeRegex.test( node.getName() )
+ && !node.getCustomData( 'selected_cell' ) )
+ {
+ CKEDITOR.dom.element.setMarker( database, node, 'selected_cell', true );
+ retval.push( node );
+ }
+ }
+
+ for ( var i = 0 ; i < ranges.length ; i++ )
+ {
+ var range = ranges[ i ];
+
+ if ( range.collapsed )
+ {
+ // Walker does not handle collapsed ranges yet - fall back to old API.
+ var startNode = range.getCommonAncestor();
+ var nearestCell = startNode.getAscendant( 'td', true ) || startNode.getAscendant( 'th', true );
+ if ( nearestCell )
+ retval.push( nearestCell );
+ }
+ else
+ {
+ var walker = new CKEDITOR.dom.walker( range );
+ var node;
+ walker.guard = moveOutOfCellGuard;
+
+ while ( ( node = walker.next() ) )
+ {
+ // If may be possible for us to have a range like this:
+ // <td>^1</td><td>^2</td>
+ // The 2nd td shouldn't be included.
+ //
+ // So we have to take care to include a td we've entered only when we've
+ // walked into its children.
+
+ var parent = node.getAscendant( 'td' ) || node.getAscendant( 'th' );
+ if ( parent && !parent.getCustomData( 'selected_cell' ) )
+ {
+ CKEDITOR.dom.element.setMarker( database, parent, 'selected_cell', true );
+ retval.push( parent );
+ }
+ }
+ }
+ }
+
+ CKEDITOR.dom.element.clearAllMarkers( database );
+
+ return retval;
+ }
+
+ function getFocusElementAfterDelCells( cellsToDelete ) {
+ var i = 0,
+ last = cellsToDelete.length - 1,
+ database = {},
+ cell,focusedCell,
+ tr;
+
+ while ( ( cell = cellsToDelete[ i++ ] ) )
+ CKEDITOR.dom.element.setMarker( database, cell, 'delete_cell', true );
+
+ // 1.first we check left or right side focusable cell row by row;
+ i = 0;
+ while ( ( cell = cellsToDelete[ i++ ] ) )
+ {
+ if ( ( focusedCell = cell.getPrevious() ) && !focusedCell.getCustomData( 'delete_cell' )
+ || ( focusedCell = cell.getNext() ) && !focusedCell.getCustomData( 'delete_cell' ) )
+ {
+ CKEDITOR.dom.element.clearAllMarkers( database );
+ return focusedCell;
+ }
+ }
+
+ CKEDITOR.dom.element.clearAllMarkers( database );
+
+ // 2. then we check the toppest row (outside the selection area square) focusable cell
+ tr = cellsToDelete[ 0 ].getParent();
+ if ( ( tr = tr.getPrevious() ) )
+ return tr.getLast();
+
+ // 3. last we check the lowerest row focusable cell
+ tr = cellsToDelete[ last ].getParent();
+ if ( ( tr = tr.getNext() ) )
+ return tr.getChild( 0 );
+
+ return null;
+ }
+
+ function insertRow( selection, insertBefore )
+ {
+ var cells = getSelectedCells( selection ),
+ firstCell = cells[ 0 ],
+ table = firstCell.getAscendant( 'table' ),
+ doc = firstCell.getDocument(),
+ startRow = cells[ 0 ].getParent(),
+ startRowIndex = startRow.$.rowIndex,
+ lastCell = cells[ cells.length - 1 ],
+ endRowIndex = lastCell.getParent().$.rowIndex + lastCell.$.rowSpan - 1,
+ endRow = new CKEDITOR.dom.element( table.$.rows[ endRowIndex ] ),
+ rowIndex = insertBefore ? startRowIndex : endRowIndex,
+ row = insertBefore ? startRow : endRow;
+
+ var map = CKEDITOR.tools.buildTableMap( table ),
+ cloneRow = map[ rowIndex ],
+ nextRow = insertBefore ? map[ rowIndex - 1 ] : map[ rowIndex + 1 ],
+ width = map[0].length;
+
+ var newRow = doc.createElement( 'tr' );
+ for ( var i = 0; cloneRow[ i ] && i < width; i++ )
+ {
+ var cell;
+ // Check whether there's a spanning row here, do not break it.
+ if ( cloneRow[ i ].rowSpan > 1 && nextRow && cloneRow[ i ] == nextRow[ i ] )
+ {
+ cell = cloneRow[ i ];
+ cell.rowSpan += 1;
+ }
+ else
+ {
+ cell = new CKEDITOR.dom.element( cloneRow[ i ] ).clone();
+ cell.removeAttribute( 'rowSpan' );
+ !CKEDITOR.env.ie && cell.appendBogus();
+ newRow.append( cell );
+ cell = cell.$;
+ }
+
+ i += cell.colSpan - 1;
+ }
+
+ insertBefore ?
+ newRow.insertBefore( row ) :
+ newRow.insertAfter( row );
+ }
+
+ function deleteRows( selectionOrRow )
+ {
+ if ( selectionOrRow instanceof CKEDITOR.dom.selection )
+ {
+ var cells = getSelectedCells( selectionOrRow ),
+ firstCell = cells[ 0 ],
+ table = firstCell.getAscendant( 'table' ),
+ map = CKEDITOR.tools.buildTableMap( table ),
+ startRow = cells[ 0 ].getParent(),
+ startRowIndex = startRow.$.rowIndex,
+ lastCell = cells[ cells.length - 1 ],
+ endRowIndex = lastCell.getParent().$.rowIndex + lastCell.$.rowSpan - 1,
+ rowsToDelete = [];
+
+ // Delete cell or reduce cell spans by checking through the table map.
+ for ( var i = startRowIndex; i <= endRowIndex; i++ )
+ {
+ var mapRow = map[ i ],
+ row = new CKEDITOR.dom.element( table.$.rows[ i ] );
+
+ for ( var j = 0; j < mapRow.length; j++ )
+ {
+ var cell = new CKEDITOR.dom.element( mapRow[ j ] ),
+ cellRowIndex = cell.getParent().$.rowIndex;
+
+ if ( cell.$.rowSpan == 1 )
+ cell.remove();
+ // Row spanned cell.
+ else
+ {
+ // Span row of the cell, reduce spanning.
+ cell.$.rowSpan -= 1;
+ // Root row of the cell, root cell to next row.
+ if ( cellRowIndex == i )
+ {
+ var nextMapRow = map[ i + 1 ];
+ nextMapRow[ j - 1 ] ?
+ cell.insertAfter( new CKEDITOR.dom.element( nextMapRow[ j - 1 ] ) )
+ : new CKEDITOR.dom.element( table.$.rows[ i + 1 ] ).append( cell, 1 );
+ }
+ }
+
+ j += cell.$.colSpan - 1;
+ }
+
+ rowsToDelete.push( row );
+ }
+
+ var rows = table.$.rows;
+
+ // Where to put the cursor after rows been deleted?
+ // 1. Into next sibling row if any;
+ // 2. Into previous sibling row if any;
+ // 3. Into table's parent element if it's the very last row.
+ var cursorPosition = new CKEDITOR.dom.element( rows[ endRowIndex + 1 ] || ( startRowIndex > 0 ? rows[ startRowIndex - 1 ] : null ) || table.$.parentNode );
+
+ for ( i = rowsToDelete.length ; i >= 0 ; i-- )
+ deleteRows( rowsToDelete[ i ] );
+
+ return cursorPosition;
+ }
+ else if ( selectionOrRow instanceof CKEDITOR.dom.element )
+ {
+ table = selectionOrRow.getAscendant( 'table' );
+
+ if ( table.$.rows.length == 1 )
+ table.remove();
+ else
+ selectionOrRow.remove();
+ }
+
+ return null;
+ }
+
+ function getCellColIndex( cell, isStart )
+ {
+ var row = cell.getParent(),
+ rowCells = row.$.cells;
+
+ var colIndex = 0;
+ for ( var i = 0; i < rowCells.length; i++ )
+ {
+ var mapCell = rowCells[ i ];
+ colIndex += isStart ? 1 : mapCell.colSpan;
+ if ( mapCell == cell.$ )
+ break;
+ }
+
+ return colIndex -1;
+ }
+
+ function getColumnsIndices( cells, isStart )
+ {
+ var retval = isStart ? Infinity : 0;
+ for ( var i = 0; i < cells.length; i++ )
+ {
+ var colIndex = getCellColIndex( cells[ i ], isStart );
+ if ( isStart ? colIndex < retval : colIndex > retval )
+ retval = colIndex;
+ }
+ return retval;
+ }
+
+ function insertColumn( selection, insertBefore )
+ {
+ var cells = getSelectedCells( selection ),
+ firstCell = cells[ 0 ],
+ table = firstCell.getAscendant( 'table' ),
+ startCol = getColumnsIndices( cells, 1 ),
+ lastCol = getColumnsIndices( cells ),
+ colIndex = insertBefore? startCol : lastCol;
+
+ var map = CKEDITOR.tools.buildTableMap( table ),
+ cloneCol = [],
+ nextCol = [],
+ height = map.length;
+
+ for ( var i = 0; i < height; i++ )
+ {
+ cloneCol.push( map[ i ][ colIndex ] );
+ var nextCell = insertBefore ? map[ i ][ colIndex - 1 ] : map[ i ][ colIndex + 1 ];
+ nextCell && nextCol.push( nextCell );
+ }
+
+ for ( i = 0; i < height; i++ )
+ {
+ var cell;
+ // Check whether there's a spanning column here, do not break it.
+ if ( cloneCol[ i ].colSpan > 1
+ && nextCol.length
+ && nextCol[ i ] == cloneCol[ i ] )
+ {
+ cell = cloneCol[ i ];
+ cell.colSpan += 1;
+ }
+ else
+ {
+ cell = new CKEDITOR.dom.element( cloneCol[ i ] ).clone();
+ cell.removeAttribute( 'colSpan' );
+ !CKEDITOR.env.ie && cell.appendBogus();
+ cell[ insertBefore? 'insertBefore' : 'insertAfter' ].call( cell, new CKEDITOR.dom.element ( cloneCol[ i ] ) );
+ cell = cell.$;
+ }
+
+ i += cell.rowSpan - 1;
+ }
+ }
+
+ function deleteColumns( selectionOrCell )
+ {
+ var cells = getSelectedCells( selectionOrCell ),
+ firstCell = cells[ 0 ],
+ lastCell = cells[ cells.length - 1 ],
+ table = firstCell.getAscendant( 'table' ),
+ map = CKEDITOR.tools.buildTableMap( table ),
+ startColIndex,
+ endColIndex,
+ rowsToDelete = [];
+
+ // Figure out selected cells' column indices.
+ for ( var i = 0, rows = map.length; i < rows; i++ )
+ {
+ for ( var j = 0, cols = map[ i ].length; j < cols; j++ )
+ {
+ if ( map[ i ][ j ] == firstCell.$ )
+ startColIndex = j;
+ if ( map[ i ][ j ] == lastCell.$ )
+ endColIndex = j;
+ }
+ }
+
+ // Delete cell or reduce cell spans by checking through the table map.
+ for ( i = startColIndex; i <= endColIndex; i++ )
+ {
+ for ( j = 0; j < map.length; j++ )
+ {
+ var mapRow = map[ j ],
+ row = new CKEDITOR.dom.element( table.$.rows[ j ] ),
+ cell = new CKEDITOR.dom.element( mapRow[ i ] );
+
+ if ( cell.$ )
+ {
+ if ( cell.$.colSpan == 1 )
+ cell.remove();
+ // Reduce the col spans.
+ else
+ cell.$.colSpan -= 1;
+
+ j += cell.$.rowSpan - 1;
+
+ if ( !row.$.cells.length )
+ rowsToDelete.push( row );
+ }
+ }
+ }
+
+ var firstRowCells = table.$.rows[ 0 ] && table.$.rows[ 0 ].cells;
+
+ // Where to put the cursor after columns been deleted?
+ // 1. Into next cell of the first row if any;
+ // 2. Into previous cell of the first row if any;
+ // 3. Into table's parent element;
+ var cursorPosition = new CKEDITOR.dom.element( firstRowCells[ startColIndex ] || ( startColIndex ? firstRowCells[ startColIndex - 1 ] : table.$.parentNode ) );
+
+ // Delete table rows only if all columns are gone (do not remove empty row).
+ if ( rowsToDelete.length == rows )
+ table.remove();
+
+ return cursorPosition;
+ }
+
+ function getFocusElementAfterDelCols( cells )
+ {
+ var cellIndexList = [],
+ table = cells[ 0 ] && cells[ 0 ].getAscendant( 'table' ),
+ i, length,
+ targetIndex, targetCell;
+
+ // get the cellIndex list of delete cells
+ for ( i = 0, length = cells.length; i < length; i++ )
+ cellIndexList.push( cells[i].$.cellIndex );
+
+ // get the focusable column index
+ cellIndexList.sort();
+ for ( i = 1, length = cellIndexList.length; i < length; i++ )
+ {
+ if ( cellIndexList[ i ] - cellIndexList[ i - 1 ] > 1 )
+ {
+ targetIndex = cellIndexList[ i - 1 ] + 1;
+ break;
+ }
+ }
+
+ if ( !targetIndex )
+ targetIndex = cellIndexList[ 0 ] > 0 ? ( cellIndexList[ 0 ] - 1 )
+ : ( cellIndexList[ cellIndexList.length - 1 ] + 1 );
+
+ // scan row by row to get the target cell
+ var rows = table.$.rows;
+ for ( i = 0, length = rows.length; i < length ; i++ )
+ {
+ targetCell = rows[ i ].cells[ targetIndex ];
+ if ( targetCell )
+ break;
+ }
+
+ return targetCell ? new CKEDITOR.dom.element( targetCell ) : table.getPrevious();
+ }
+
+ function insertCell( selection, insertBefore )
+ {
+ var startElement = selection.getStartElement();
+ var cell = startElement.getAscendant( 'td', 1 ) || startElement.getAscendant( 'th', 1 );
+
+ if ( !cell )
+ return;
+
+ // Create the new cell element to be added.
+ var newCell = cell.clone();
+ if ( !CKEDITOR.env.ie )
+ newCell.appendBogus();
+
+ if ( insertBefore )
+ newCell.insertBefore( cell );
+ else
+ newCell.insertAfter( cell );
+ }
+
+ function deleteCells( selectionOrCell )
+ {
+ if ( selectionOrCell instanceof CKEDITOR.dom.selection )
+ {
+ var cellsToDelete = getSelectedCells( selectionOrCell );
+ var table = cellsToDelete[ 0 ] && cellsToDelete[ 0 ].getAscendant( 'table' );
+ var cellToFocus = getFocusElementAfterDelCells( cellsToDelete );
+
+ for ( var i = cellsToDelete.length - 1 ; i >= 0 ; i-- )
+ deleteCells( cellsToDelete[ i ] );
+
+ if ( cellToFocus )
+ placeCursorInCell( cellToFocus, true );
+ else if ( table )
+ table.remove();
+ }
+ else if ( selectionOrCell instanceof CKEDITOR.dom.element )
+ {
+ var tr = selectionOrCell.getParent();
+ if ( tr.getChildCount() == 1 )
+ tr.remove();
+ else
+ selectionOrCell.remove();
+ }
+ }
+
+ // Remove filler at end and empty spaces around the cell content.
+ function trimCell( cell )
+ {
+ var bogus = cell.getBogus();
+ bogus && bogus.remove();
+ cell.trim();
+ }
+
+ function placeCursorInCell( cell, placeAtEnd )
+ {
+ var range = new CKEDITOR.dom.range( cell.getDocument() );
+ if ( !range[ 'moveToElementEdit' + ( placeAtEnd ? 'End' : 'Start' ) ]( cell ) )
+ {
+ range.selectNodeContents( cell );
+ range.collapse( placeAtEnd ? false : true );
+ }
+ range.select( true );
+ }
+
+ function cellInRow( tableMap, rowIndex, cell )
+ {
+ var oRow = tableMap[ rowIndex ];
+ if ( typeof cell == 'undefined' )
+ return oRow;
+
+ for ( var c = 0 ; oRow && c < oRow.length ; c++ )
+ {
+ if ( cell.is && oRow[c] == cell.$ )
+ return c;
+ else if ( c == cell )
+ return new CKEDITOR.dom.element( oRow[ c ] );
+ }
+ return cell.is ? -1 : null;
+ }
+
+ function cellInCol( tableMap, colIndex )
+ {
+ var oCol = [];
+ for ( var r = 0; r < tableMap.length; r++ )
+ {
+ var row = tableMap[ r ];
+ oCol.push( row[ colIndex ] );
+
+ // Avoid adding duplicate cells.
+ if ( row[ colIndex ].rowSpan > 1 )
+ r += row[ colIndex ].rowSpan - 1;
+ }
+ return oCol;
+ }
+
+ function mergeCells( selection, mergeDirection, isDetect )
+ {
+ var cells = getSelectedCells( selection );
+
+ // Invalid merge request if:
+ // 1. In batch mode despite that less than two selected.
+ // 2. In solo mode while not exactly only one selected.
+ // 3. Cells distributed in different table groups (e.g. from both thead and tbody).
+ var commonAncestor;
+ if ( ( mergeDirection ? cells.length != 1 : cells.length < 2 )
+ || ( commonAncestor = selection.getCommonAncestor() )
+ && commonAncestor.type == CKEDITOR.NODE_ELEMENT
+ && commonAncestor.is( 'table' ) )
+ {
+ return false;
+ }
+
+ var cell,
+ firstCell = cells[ 0 ],
+ table = firstCell.getAscendant( 'table' ),
+ map = CKEDITOR.tools.buildTableMap( table ),
+ mapHeight = map.length,
+ mapWidth = map[ 0 ].length,
+ startRow = firstCell.getParent().$.rowIndex,
+ startColumn = cellInRow( map, startRow, firstCell );
+
+ if ( mergeDirection )
+ {
+ var targetCell;
+ try
+ {
+ var rowspan = parseInt( firstCell.getAttribute( 'rowspan' ), 10 ) || 1;
+ var colspan = parseInt( firstCell.getAttribute( 'colspan' ), 10 ) || 1;
+
+ targetCell =
+ map[ mergeDirection == 'up' ?
+ ( startRow - rowspan ):
+ mergeDirection == 'down' ? ( startRow + rowspan ) : startRow ] [
+ mergeDirection == 'left' ?
+ ( startColumn - colspan ):
+ mergeDirection == 'right' ? ( startColumn + colspan ) : startColumn ];
+
+ }
+ catch( er )
+ {
+ return false;
+ }
+
+ // 1. No cell could be merged.
+ // 2. Same cell actually.
+ if ( !targetCell || firstCell.$ == targetCell )
+ return false;
+
+ // Sort in map order regardless of the DOM sequence.
+ cells[ ( mergeDirection == 'up' || mergeDirection == 'left' ) ?
+ 'unshift' : 'push' ]( new CKEDITOR.dom.element( targetCell ) );
+ }
+
+ // Start from here are merging way ignorance (merge up/right, batch merge).
+ var doc = firstCell.getDocument(),
+ lastRowIndex = startRow,
+ totalRowSpan = 0,
+ totalColSpan = 0,
+ // Use a documentFragment as buffer when appending cell contents.
+ frag = !isDetect && new CKEDITOR.dom.documentFragment( doc ),
+ dimension = 0;
+
+ for ( var i = 0; i < cells.length; i++ )
+ {
+ cell = cells[ i ];
+
+ var tr = cell.getParent(),
+ cellFirstChild = cell.getFirst(),
+ colSpan = cell.$.colSpan,
+ rowSpan = cell.$.rowSpan,
+ rowIndex = tr.$.rowIndex,
+ colIndex = cellInRow( map, rowIndex, cell );
+
+ // Accumulated the actual places taken by all selected cells.
+ dimension += colSpan * rowSpan;
+ // Accumulated the maximum virtual spans from column and row.
+ totalColSpan = Math.max( totalColSpan, colIndex - startColumn + colSpan ) ;
+ totalRowSpan = Math.max( totalRowSpan, rowIndex - startRow + rowSpan );
+
+ if ( !isDetect )
+ {
+ // Trim all cell fillers and check to remove empty cells.
+ if ( trimCell( cell ), cell.getChildren().count() )
+ {
+ // Merge vertically cells as two separated paragraphs.
+ if ( rowIndex != lastRowIndex
+ && cellFirstChild
+ && !( cellFirstChild.isBlockBoundary
+ && cellFirstChild.isBlockBoundary( { br : 1 } ) ) )
+ {
+ var last = frag.getLast( CKEDITOR.dom.walker.whitespaces( true ) );
+ if ( last && !( last.is && last.is( 'br' ) ) )
+ frag.append( 'br' );
+ }
+
+ cell.moveChildren( frag );
+ }
+ i ? cell.remove() : cell.setHtml( '' );
+ }
+ lastRowIndex = rowIndex;
+ }
+
+ if ( !isDetect )
+ {
+ frag.moveChildren( firstCell );
+
+ if ( !CKEDITOR.env.ie )
+ firstCell.appendBogus();
+
+ if ( totalColSpan >= mapWidth )
+ firstCell.removeAttribute( 'rowSpan' );
+ else
+ firstCell.$.rowSpan = totalRowSpan;
+
+ if ( totalRowSpan >= mapHeight )
+ firstCell.removeAttribute( 'colSpan' );
+ else
+ firstCell.$.colSpan = totalColSpan;
+
+ // Swip empty <tr> left at the end of table due to the merging.
+ var trs = new CKEDITOR.dom.nodeList( table.$.rows ),
+ count = trs.count();
+
+ for ( i = count - 1; i >= 0; i-- )
+ {
+ var tailTr = trs.getItem( i );
+ if ( !tailTr.$.cells.length )
+ {
+ tailTr.remove();
+ count++;
+ continue;
+ }
+ }
+
+ return firstCell;
+ }
+ // Be able to merge cells only if actual dimension of selected
+ // cells equals to the caculated rectangle.
+ else
+ return ( totalRowSpan * totalColSpan ) == dimension;
+ }
+
+ function verticalSplitCell ( selection, isDetect )
+ {
+ var cells = getSelectedCells( selection );
+ if ( cells.length > 1 )
+ return false;
+ else if ( isDetect )
+ return true;
+
+ var cell = cells[ 0 ],
+ tr = cell.getParent(),
+ table = tr.getAscendant( 'table' ),
+ map = CKEDITOR.tools.buildTableMap( table ),
+ rowIndex = tr.$.rowIndex,
+ colIndex = cellInRow( map, rowIndex, cell ),
+ rowSpan = cell.$.rowSpan,
+ newCell,
+ newRowSpan,
+ newCellRowSpan,
+ newRowIndex;
+
+ if ( rowSpan > 1 )
+ {
+ newRowSpan = Math.ceil( rowSpan / 2 );
+ newCellRowSpan = Math.floor( rowSpan / 2 );
+ newRowIndex = rowIndex + newRowSpan;
+ var newCellTr = new CKEDITOR.dom.element( table.$.rows[ newRowIndex ] ),
+ newCellRow = cellInRow( map, newRowIndex ),
+ candidateCell;
+
+ newCell = cell.clone();
+
+ // Figure out where to insert the new cell by checking the vitual row.
+ for ( var c = 0; c < newCellRow.length; c++ )
+ {
+ candidateCell = newCellRow[ c ];
+ // Catch first cell actually following the column.
+ if ( candidateCell.parentNode == newCellTr.$
+ && c > colIndex )
+ {
+ newCell.insertBefore( new CKEDITOR.dom.element( candidateCell ) );
+ break;
+ }
+ else
+ candidateCell = null;
+ }
+
+ // The destination row is empty, append at will.
+ if ( !candidateCell )
+ newCellTr.append( newCell, true );
+ }
+ else
+ {
+ newCellRowSpan = newRowSpan = 1;
+
+ newCellTr = tr.clone();
+ newCellTr.insertAfter( tr );
+ newCellTr.append( newCell = cell.clone() );
+
+ var cellsInSameRow = cellInRow( map, rowIndex );
+ for ( var i = 0; i < cellsInSameRow.length; i++ )
+ cellsInSameRow[ i ].rowSpan++;
+ }
+
+ if ( !CKEDITOR.env.ie )
+ newCell.appendBogus();
+
+ cell.$.rowSpan = newRowSpan;
+ newCell.$.rowSpan = newCellRowSpan;
+ if ( newRowSpan == 1 )
+ cell.removeAttribute( 'rowSpan' );
+ if ( newCellRowSpan == 1 )
+ newCell.removeAttribute( 'rowSpan' );
+
+ return newCell;
+ }
+
+ function horizontalSplitCell( selection, isDetect )
+ {
+ var cells = getSelectedCells( selection );
+ if ( cells.length > 1 )
+ return false;
+ else if ( isDetect )
+ return true;
+
+ var cell = cells[ 0 ],
+ tr = cell.getParent(),
+ table = tr.getAscendant( 'table' ),
+ map = CKEDITOR.tools.buildTableMap( table ),
+ rowIndex = tr.$.rowIndex,
+ colIndex = cellInRow( map, rowIndex, cell ),
+ colSpan = cell.$.colSpan,
+ newCell,
+ newColSpan,
+ newCellColSpan;
+
+ if ( colSpan > 1 )
+ {
+ newColSpan = Math.ceil( colSpan / 2 );
+ newCellColSpan = Math.floor( colSpan / 2 );
+ }
+ else
+ {
+ newCellColSpan = newColSpan = 1;
+ var cellsInSameCol = cellInCol( map, colIndex );
+ for ( var i = 0; i < cellsInSameCol.length; i++ )
+ cellsInSameCol[ i ].colSpan++;
+ }
+ newCell = cell.clone();
+ newCell.insertAfter( cell );
+ if ( !CKEDITOR.env.ie )
+ newCell.appendBogus();
+
+ cell.$.colSpan = newColSpan;
+ newCell.$.colSpan = newCellColSpan;
+ if ( newColSpan == 1 )
+ cell.removeAttribute( 'colSpan' );
+ if ( newCellColSpan == 1 )
+ newCell.removeAttribute( 'colSpan' );
+
+ return newCell;
+ }
+ // Context menu on table caption incorrect (#3834)
+ var contextMenuTags = { thead : 1, tbody : 1, tfoot : 1, td : 1, tr : 1, th : 1 };
+
+ CKEDITOR.plugins.tabletools =
+ {
+ init : function( editor )
+ {
+ var lang = editor.lang.table;
+
+ editor.addCommand( 'cellProperties', new CKEDITOR.dialogCommand( 'cellProperties' ) );
+ CKEDITOR.dialog.add( 'cellProperties', this.path + 'dialogs/tableCell.js' );
+
+ editor.addCommand( 'tableDelete',
+ {
+ exec : function( editor )
+ {
+ var selection = editor.getSelection(),
+ startElement = selection && selection.getStartElement(),
+ table = startElement && startElement.getAscendant( 'table', 1 );
+
+ if ( !table )
+ return;
+
+ // If the table's parent has only one child remove it as well (unless it's the body or a table cell) (#5416, #6289)
+ var parent = table.getParent();
+ if ( parent.getChildCount() == 1 && !parent.is( 'body', 'td', 'th' ) )
+ table = parent;
+
+ var range = new CKEDITOR.dom.range( editor.document );
+ range.moveToPosition( table, CKEDITOR.POSITION_BEFORE_START );
+ table.remove();
+ range.select();
+ }
+ } );
+
+ editor.addCommand( 'rowDelete',
+ {
+ exec : function( editor )
+ {
+ var selection = editor.getSelection();
+ placeCursorInCell( deleteRows( selection ) );
+ }
+ } );
+
+ editor.addCommand( 'rowInsertBefore',
+ {
+ exec : function( editor )
+ {
+ var selection = editor.getSelection();
+ insertRow( selection, true );
+ }
+ } );
+
+ editor.addCommand( 'rowInsertAfter',
+ {
+ exec : function( editor )
+ {
+ var selection = editor.getSelection();
+ insertRow( selection );
+ }
+ } );
+
+ editor.addCommand( 'columnDelete',
+ {
+ exec : function( editor )
+ {
+ var selection = editor.getSelection();
+ var element = deleteColumns( selection );
+ element && placeCursorInCell( element, true );
+ }
+ } );
+
+ editor.addCommand( 'columnInsertBefore',
+ {
+ exec : function( editor )
+ {
+ var selection = editor.getSelection();
+ insertColumn( selection, true );
+ }
+ } );
+
+ editor.addCommand( 'columnInsertAfter',
+ {
+ exec : function( editor )
+ {
+ var selection = editor.getSelection();
+ insertColumn( selection );
+ }
+ } );
+
+ editor.addCommand( 'cellDelete',
+ {
+ exec : function( editor )
+ {
+ var selection = editor.getSelection();
+ deleteCells( selection );
+ }
+ } );
+
+ editor.addCommand( 'cellMerge',
+ {
+ exec : function( editor )
+ {
+ placeCursorInCell( mergeCells( editor.getSelection() ), true );
+ }
+ } );
+
+ editor.addCommand( 'cellMergeRight',
+ {
+ exec : function( editor )
+ {
+ placeCursorInCell( mergeCells( editor.getSelection(), 'right' ), true );
+ }
+ } );
+
+ editor.addCommand( 'cellMergeDown',
+ {
+ exec : function( editor )
+ {
+ placeCursorInCell( mergeCells( editor.getSelection(), 'down' ), true );
+ }
+ } );
+
+ editor.addCommand( 'cellVerticalSplit',
+ {
+ exec : function( editor )
+ {
+ placeCursorInCell( verticalSplitCell( editor.getSelection() ) );
+ }
+ } );
+
+ editor.addCommand( 'cellHorizontalSplit',
+ {
+ exec : function( editor )
+ {
+ placeCursorInCell( horizontalSplitCell( editor.getSelection() ) );
+ }
+ } );
+
+ editor.addCommand( 'cellInsertBefore',
+ {
+ exec : function( editor )
+ {
+ var selection = editor.getSelection();
+ insertCell( selection, true );
+ }
+ } );
+
+ editor.addCommand( 'cellInsertAfter',
+ {
+ exec : function( editor )
+ {
+ var selection = editor.getSelection();
+ insertCell( selection );
+ }
+ } );
+
+ // If the "menu" plugin is loaded, register the menu items.
+ if ( editor.addMenuItems )
+ {
+ editor.addMenuItems(
+ {
+ tablecell :
+ {
+ label : lang.cell.menu,
+ group : 'tablecell',
+ order : 1,
+ getItems : function()
+ {
+ var selection = editor.getSelection(),
+ cells = getSelectedCells( selection );
+ return {
+ tablecell_insertBefore : CKEDITOR.TRISTATE_OFF,
+ tablecell_insertAfter : CKEDITOR.TRISTATE_OFF,
+ tablecell_delete : CKEDITOR.TRISTATE_OFF,
+ tablecell_merge : mergeCells( selection, null, true ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
+ tablecell_merge_right : mergeCells( selection, 'right', true ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
+ tablecell_merge_down : mergeCells( selection, 'down', true ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
+ tablecell_split_vertical : verticalSplitCell( selection, true ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
+ tablecell_split_horizontal : horizontalSplitCell( selection, true ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED,
+ tablecell_properties : cells.length > 0 ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED
+ };
+ }
+ },
+
+ tablecell_insertBefore :
+ {
+ label : lang.cell.insertBefore,
+ group : 'tablecell',
+ command : 'cellInsertBefore',
+ order : 5
+ },
+
+ tablecell_insertAfter :
+ {
+ label : lang.cell.insertAfter,
+ group : 'tablecell',
+ command : 'cellInsertAfter',
+ order : 10
+ },
+
+ tablecell_delete :
+ {
+ label : lang.cell.deleteCell,
+ group : 'tablecell',
+ command : 'cellDelete',
+ order : 15
+ },
+
+ tablecell_merge :
+ {
+ label : lang.cell.merge,
+ group : 'tablecell',
+ command : 'cellMerge',
+ order : 16
+ },
+
+ tablecell_merge_right :
+ {
+ label : lang.cell.mergeRight,
+ group : 'tablecell',
+ command : 'cellMergeRight',
+ order : 17
+ },
+
+ tablecell_merge_down :
+ {
+ label : lang.cell.mergeDown,
+ group : 'tablecell',
+ command : 'cellMergeDown',
+ order : 18
+ },
+
+ tablecell_split_horizontal :
+ {
+ label : lang.cell.splitHorizontal,
+ group : 'tablecell',
+ command : 'cellHorizontalSplit',
+ order : 19
+ },
+
+ tablecell_split_vertical :
+ {
+ label : lang.cell.splitVertical,
+ group : 'tablecell',
+ command : 'cellVerticalSplit',
+ order : 20
+ },
+
+ tablecell_properties :
+ {
+ label : lang.cell.title,
+ group : 'tablecellproperties',
+ command : 'cellProperties',
+ order : 21
+ },
+
+ tablerow :
+ {
+ label : lang.row.menu,
+ group : 'tablerow',
+ order : 1,
+ getItems : function()
+ {
+ return {
+ tablerow_insertBefore : CKEDITOR.TRISTATE_OFF,
+ tablerow_insertAfter : CKEDITOR.TRISTATE_OFF,
+ tablerow_delete : CKEDITOR.TRISTATE_OFF
+ };
+ }
+ },
+
+ tablerow_insertBefore :
+ {
+ label : lang.row.insertBefore,
+ group : 'tablerow',
+ command : 'rowInsertBefore',
+ order : 5
+ },
+
+ tablerow_insertAfter :
+ {
+ label : lang.row.insertAfter,
+ group : 'tablerow',
+ command : 'rowInsertAfter',
+ order : 10
+ },
+
+ tablerow_delete :
+ {
+ label : lang.row.deleteRow,
+ group : 'tablerow',
+ command : 'rowDelete',
+ order : 15
+ },
+
+ tablecolumn :
+ {
+ label : lang.column.menu,
+ group : 'tablecolumn',
+ order : 1,
+ getItems : function()
+ {
+ return {
+ tablecolumn_insertBefore : CKEDITOR.TRISTATE_OFF,
+ tablecolumn_insertAfter : CKEDITOR.TRISTATE_OFF,
+ tablecolumn_delete : CKEDITOR.TRISTATE_OFF
+ };
+ }
+ },
+
+ tablecolumn_insertBefore :
+ {
+ label : lang.column.insertBefore,
+ group : 'tablecolumn',
+ command : 'columnInsertBefore',
+ order : 5
+ },
+
+ tablecolumn_insertAfter :
+ {
+ label : lang.column.insertAfter,
+ group : 'tablecolumn',
+ command : 'columnInsertAfter',
+ order : 10
+ },
+
+ tablecolumn_delete :
+ {
+ label : lang.column.deleteColumn,
+ group : 'tablecolumn',
+ command : 'columnDelete',
+ order : 15
+ }
+ });
+ }
+
+ // If the "contextmenu" plugin is laoded, register the listeners.
+ if ( editor.contextMenu )
+ {
+ editor.contextMenu.addListener( function( element, selection )
+ {
+ if ( !element || element.isReadOnly() )
+ return null;
+
+ while ( element )
+ {
+ if ( element.getName() in contextMenuTags )
+ {
+ return {
+ tablecell : CKEDITOR.TRISTATE_OFF,
+ tablerow : CKEDITOR.TRISTATE_OFF,
+ tablecolumn : CKEDITOR.TRISTATE_OFF
+ };
+ }
+ element = element.getParent();
+ }
+
+ return null;
+ } );
+ }
+ },
+
+ getSelectedCells : getSelectedCells
+
+ };
+ CKEDITOR.plugins.add( 'tabletools', CKEDITOR.plugins.tabletools );
+})();
+
+/**
+ * Create a two-dimension array that reflects the actual layout of table cells,
+ * with cell spans, with mappings to the original td elements.
+ * @param table {CKEDITOR.dom.element}
+ */
+CKEDITOR.tools.buildTableMap = function ( table )
+{
+ var aRows = table.$.rows ;
+
+ // Row and Column counters.
+ var r = -1 ;
+
+ var aMap = [];
+
+ for ( var i = 0 ; i < aRows.length ; i++ )
+ {
+ r++ ;
+ !aMap[r] && ( aMap[r] = [] );
+
+ var c = -1 ;
+
+ for ( var j = 0 ; j < aRows[i].cells.length ; j++ )
+ {
+ var oCell = aRows[i].cells[j] ;
+
+ c++ ;
+ while ( aMap[r][c] )
+ c++ ;
+
+ var iColSpan = isNaN( oCell.colSpan ) ? 1 : oCell.colSpan ;
+ var iRowSpan = isNaN( oCell.rowSpan ) ? 1 : oCell.rowSpan ;
+
+ for ( var rs = 0 ; rs < iRowSpan ; rs++ )
+ {
+ if ( !aMap[r + rs] )
+ aMap[r + rs] = [];
+
+ for ( var cs = 0 ; cs < iColSpan ; cs++ )
+ {
+ aMap[r + rs][c + cs] = aRows[i].cells[j] ;
+ }
+ }
+
+ c += iColSpan - 1 ;
+ }
+ }
+ return aMap ;
+};
diff --git a/_source/plugins/templates/dialogs/templates.js b/_source/plugins/templates/dialogs/templates.js index 6f26a44..1fed05c 100644 --- a/_source/plugins/templates/dialogs/templates.js +++ b/_source/plugins/templates/dialogs/templates.js @@ -1,231 +1,234 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -(function() -{ - var doc = CKEDITOR.document; - - CKEDITOR.dialog.add( 'templates', function( editor ) - { - // Constructs the HTML view of the specified templates data. - function renderTemplatesList( container, templatesDefinitions ) - { - // clear loading wait text. - container.setHtml( '' ); - - for ( var i = 0 ; i < templatesDefinitions.length ; i++ ) - { - var definition = CKEDITOR.getTemplates( templatesDefinitions[ i ] ), - imagesPath = definition.imagesPath, - templates = definition.templates, - count = templates.length; - - for ( var j = 0 ; j < count ; j++ ) - { - var template = templates[ j ], - item = createTemplateItem( template, imagesPath ); - item.setAttribute( 'aria-posinset', j + 1 ); - item.setAttribute( 'aria-setsize', count ); - container.append( item ); - } - } - } - - function createTemplateItem( template, imagesPath ) - { - var item = CKEDITOR.dom.element.createFromHtml( - '<a href="javascript:void(0)" tabIndex="-1" role="option" >' + - '<div class="cke_tpl_item"></div>' + - '</a>' ); - - // Build the inner HTML of our new item DIV. - var html = '<table style="width:350px;" class="cke_tpl_preview" role="presentation"><tr>'; - - if ( template.image && imagesPath ) - html += '<td class="cke_tpl_preview_img"><img src="' + CKEDITOR.getUrl( imagesPath + template.image ) + '"' + ( CKEDITOR.env.ie6Compat? ' onload="this.width=this.width"' : '' ) + ' alt="" title=""></td>'; - - html += '<td style="white-space:normal;"><span class="cke_tpl_title">' + template.title + '</span><br/>'; - - if ( template.description ) - html += '<span>' + template.description + '</span>'; - - html += '</td></tr></table>'; - - item.getFirst().setHtml( html ); - - item.on( 'click', function() { insertTemplate( template.html ); } ); - - return item; - } - - /** - * Insert the specified template content into editor. - * @param {Number} index - */ - function insertTemplate( html ) - { - var dialog = CKEDITOR.dialog.getCurrent(), - isInsert = dialog.getValueOf( 'selectTpl', 'chkInsertOpt' ); - - if ( isInsert ) - { - // Everything should happen after the document is loaded (#4073). - editor.on( 'contentDom', function( evt ) - { - evt.removeListener(); - dialog.hide(); - - // Place the cursor at the first editable place. - var range = new CKEDITOR.dom.range( editor.document ); - range.moveToElementEditStart( editor.document.getBody() ); - range.select( true ); - setTimeout( function () - { - editor.fire( 'saveSnapshot' ); - }, 0 ); - } ); - - editor.fire( 'saveSnapshot' ); - editor.setData( html ); - } - else - { - editor.insertHtml( html ); - dialog.hide(); - } - } - - function keyNavigation( evt ) - { - var target = evt.data.getTarget(), - onList = listContainer.equals( target ); - - // Keyboard navigation for template list. - if ( onList || listContainer.contains( target ) ) - { - var keystroke = evt.data.getKeystroke(), - items = listContainer.getElementsByTag( 'a' ), - focusItem; - - if ( items ) - { - // Focus not yet onto list items? - if ( onList ) - focusItem = items.getItem( 0 ); - else - { - switch ( keystroke ) - { - case 40 : // ARROW-DOWN - focusItem = target.getNext(); - break; - - case 38 : // ARROW-UP - focusItem = target.getPrevious(); - break; - - case 13 : // ENTER - case 32 : // SPACE - target.fire( 'click' ); - } - } - - if ( focusItem ) - { - focusItem.focus(); - evt.data.preventDefault(); - } - } - } - } - - // Load skin at first. - CKEDITOR.skins.load( editor, 'templates' ); - - var listContainer; - - var templateListLabelId = 'cke_tpl_list_label_' + CKEDITOR.tools.getNextNumber(); - return { - title :editor.lang.templates.title, - - minWidth : CKEDITOR.env.ie ? 440 : 400, - minHeight : 340, - - contents : - [ - { - id :'selectTpl', - label : editor.lang.templates.title, - elements : - [ - { - type : 'vbox', - padding : 5, - children : - [ - { - type : 'html', - html : - '<span>' + - editor.lang.templates.selectPromptMsg + - '</span>' - }, - { - id : "templatesList", - type : 'html', - focus: true, - html : - '<div class="cke_tpl_list" tabIndex="-1" role="listbox" aria-labelledby="' + templateListLabelId+ '">' + - '<div class="cke_tpl_loading"><span></span></div>' + - '</div>' + - '<span class="cke_voice_label" id="' + templateListLabelId + '">' + editor.lang.templates.options+ '</span>' - }, - { - id : 'chkInsertOpt', - type : 'checkbox', - label : editor.lang.templates.insertOption, - 'default' : editor.config.templates_replaceContent - } - ] - } - ] - } - ], - - buttons : [ CKEDITOR.dialog.cancelButton ], - - onShow : function() - { - var templatesListField = this.getContentElement( 'selectTpl' , 'templatesList' ); - listContainer = templatesListField.getElement(); - - CKEDITOR.loadTemplates( editor.config.templates_files, function() - { - var templates = editor.config.templates.split( ',' ); - - if ( templates.length ) - { - renderTemplatesList( listContainer, templates ); - templatesListField.focus(); - } - else - { - listContainer.setHtml( - '<div class="cke_tpl_empty">' + - '<span>' + editor.lang.templates.emptyListMsg + '</span>' + - '</div>' ); - } - }); - - this._.element.on( 'keydown', keyNavigation ); - }, - - onHide : function () - { - this._.element.removeListener( 'keydown', keyNavigation ); - } - }; - }); -})(); +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+(function()
+{
+ var doc = CKEDITOR.document;
+
+ CKEDITOR.dialog.add( 'templates', function( editor )
+ {
+ // Constructs the HTML view of the specified templates data.
+ function renderTemplatesList( container, templatesDefinitions )
+ {
+ // clear loading wait text.
+ container.setHtml( '' );
+
+ for ( var i = 0, totalDefs = templatesDefinitions.length ; i < totalDefs ; i++ )
+ {
+ var definition = CKEDITOR.getTemplates( templatesDefinitions[ i ] ),
+ imagesPath = definition.imagesPath,
+ templates = definition.templates,
+ count = templates.length;
+
+ for ( var j = 0 ; j < count ; j++ )
+ {
+ var template = templates[ j ],
+ item = createTemplateItem( template, imagesPath );
+ item.setAttribute( 'aria-posinset', j + 1 );
+ item.setAttribute( 'aria-setsize', count );
+ container.append( item );
+ }
+ }
+ }
+
+ function createTemplateItem( template, imagesPath )
+ {
+ var item = CKEDITOR.dom.element.createFromHtml(
+ '<a href="javascript:void(0)" tabIndex="-1" role="option" >' +
+ '<div class="cke_tpl_item"></div>' +
+ '</a>' );
+
+ // Build the inner HTML of our new item DIV.
+ var html = '<table style="width:350px;" class="cke_tpl_preview" role="presentation"><tr>';
+
+ if ( template.image && imagesPath )
+ html += '<td class="cke_tpl_preview_img"><img src="' + CKEDITOR.getUrl( imagesPath + template.image ) + '"' + ( CKEDITOR.env.ie6Compat ? ' onload="this.width=this.width"' : '' ) + ' alt="" title=""></td>';
+
+ html += '<td style="white-space:normal;"><span class="cke_tpl_title">' + template.title + '</span><br/>';
+
+ if ( template.description )
+ html += '<span>' + template.description + '</span>';
+
+ html += '</td></tr></table>';
+
+ item.getFirst().setHtml( html );
+
+ item.on( 'click', function() { insertTemplate( template.html ); } );
+
+ return item;
+ }
+
+ /**
+ * Insert the specified template content into editor.
+ * @param {Number} index
+ */
+ function insertTemplate( html )
+ {
+ var dialog = CKEDITOR.dialog.getCurrent(),
+ isInsert = dialog.getValueOf( 'selectTpl', 'chkInsertOpt' );
+
+ if ( isInsert )
+ {
+ // Everything should happen after the document is loaded (#4073).
+ editor.on( 'contentDom', function( evt )
+ {
+ evt.removeListener();
+ dialog.hide();
+
+ // Place the cursor at the first editable place.
+ var range = new CKEDITOR.dom.range( editor.document );
+ range.moveToElementEditStart( editor.document.getBody() );
+ range.select( 1 );
+ setTimeout( function()
+ {
+ editor.fire( 'saveSnapshot' );
+ }, 0 );
+ });
+
+ editor.fire( 'saveSnapshot' );
+ editor.setData( html );
+ }
+ else
+ {
+ editor.insertHtml( html );
+ dialog.hide();
+ }
+ }
+
+ function keyNavigation( evt )
+ {
+ var target = evt.data.getTarget(),
+ onList = listContainer.equals( target );
+
+ // Keyboard navigation for template list.
+ if ( onList || listContainer.contains( target ) )
+ {
+ var keystroke = evt.data.getKeystroke(),
+ items = listContainer.getElementsByTag( 'a' ),
+ focusItem;
+
+ if ( items )
+ {
+ // Focus not yet onto list items?
+ if ( onList )
+ focusItem = items.getItem( 0 );
+ else
+ {
+ switch ( keystroke )
+ {
+ case 40 : // ARROW-DOWN
+ focusItem = target.getNext();
+ break;
+
+ case 38 : // ARROW-UP
+ focusItem = target.getPrevious();
+ break;
+
+ case 13 : // ENTER
+ case 32 : // SPACE
+ target.fire( 'click' );
+ }
+ }
+
+ if ( focusItem )
+ {
+ focusItem.focus();
+ evt.data.preventDefault();
+ }
+ }
+ }
+ }
+
+ // Load skin at first.
+ CKEDITOR.skins.load( editor, 'templates' );
+
+ var listContainer;
+
+ var templateListLabelId = 'cke_tpl_list_label_' + CKEDITOR.tools.getNextNumber(),
+ lang = editor.lang.templates,
+ config = editor.config;
+ return {
+ title :editor.lang.templates.title,
+
+ minWidth : CKEDITOR.env.ie ? 440 : 400,
+ minHeight : 340,
+
+ contents :
+ [
+ {
+ id :'selectTpl',
+ label : lang.title,
+ elements :
+ [
+ {
+ type : 'vbox',
+ padding : 5,
+ children :
+ [
+ {
+ id : 'selectTplText',
+ type : 'html',
+ html :
+ '<span>' +
+ lang.selectPromptMsg +
+ '</span>'
+ },
+ {
+ id : 'templatesList',
+ type : 'html',
+ focus: true,
+ html :
+ '<div class="cke_tpl_list" tabIndex="-1" role="listbox" aria-labelledby="' + templateListLabelId+ '">' +
+ '<div class="cke_tpl_loading"><span></span></div>' +
+ '</div>' +
+ '<span class="cke_voice_label" id="' + templateListLabelId + '">' + lang.options+ '</span>'
+ },
+ {
+ id : 'chkInsertOpt',
+ type : 'checkbox',
+ label : lang.insertOption,
+ 'default' : config.templates_replaceContent
+ }
+ ]
+ }
+ ]
+ }
+ ],
+
+ buttons : [ CKEDITOR.dialog.cancelButton ],
+
+ onShow : function()
+ {
+ var templatesListField = this.getContentElement( 'selectTpl' , 'templatesList' );
+ listContainer = templatesListField.getElement();
+
+ CKEDITOR.loadTemplates( config.templates_files, function()
+ {
+ var templates = ( config.templates || 'default' ).split( ',' );
+
+ if ( templates.length )
+ {
+ renderTemplatesList( listContainer, templates );
+ templatesListField.focus();
+ }
+ else
+ {
+ listContainer.setHtml(
+ '<div class="cke_tpl_empty">' +
+ '<span>' + lang.emptyListMsg + '</span>' +
+ '</div>' );
+ }
+ });
+
+ this._.element.on( 'keydown', keyNavigation );
+ },
+
+ onHide : function()
+ {
+ this._.element.removeListener( 'keydown', keyNavigation );
+ }
+ };
+ });
+})();
diff --git a/_source/plugins/templates/plugin.js b/_source/plugins/templates/plugin.js index 64d70e0..6226159 100644 --- a/_source/plugins/templates/plugin.js +++ b/_source/plugins/templates/plugin.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
@@ -42,7 +42,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license var toLoad = [];
// Look for pending template files to get loaded.
- for ( var i = 0 ; i < templateFiles.length ; i++ )
+ for ( var i = 0, count = templateFiles.length ; i < count ; i++ )
{
if ( !loadedTemplatesFiles[ templateFiles[ i ] ] )
{
@@ -51,7 +51,7 @@ For licensing, see LICENSE.html or http://ckeditor.com/license }
}
- if ( toLoad.length > 0 )
+ if ( toLoad.length )
CKEDITOR.scriptLoader.load( toLoad, callback );
else
setTimeout( callback, 0 );
@@ -65,10 +65,10 @@ For licensing, see LICENSE.html or http://ckeditor.com/license * comma. It must match definitions loaded with the templates_files setting.
* @type String
* @default 'default'
+ * @name CKEDITOR.config.templates
* @example
* config.templates = 'my_templates';
*/
-CKEDITOR.config.templates = 'default';
/**
* The list of templates definition files to load.
diff --git a/_source/plugins/templates/templates/default.js b/_source/plugins/templates/templates/default.js index 69f8ec5..495b948 100644 --- a/_source/plugins/templates/templates/default.js +++ b/_source/plugins/templates/templates/default.js @@ -1,94 +1,94 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -// Register a templates definition set named "default". -CKEDITOR.addTemplates( 'default', -{ - // The name of sub folder which hold the shortcut preview images of the - // templates. - imagesPath : CKEDITOR.getUrl( CKEDITOR.plugins.getPath( 'templates' ) + 'templates/images/' ), - - // The templates definitions. - templates : - [ - { - title: 'Image and Title', - image: 'template1.gif', - description: 'One main image with a title and text that surround the image.', - html: - '<h3>' + - '<img style="margin-right: 10px" height="100" width="100" align="left"/>' + - 'Type the title here'+ - '</h3>' + - '<p>' + - 'Type the text here' + - '</p>' - }, - { - title: 'Strange Template', - image: 'template2.gif', - description: 'A template that defines two colums, each one with a title, and some text.', - html: - '<table cellspacing="0" cellpadding="0" style="width:100%" border="0">' + - '<tr>' + - '<td style="width:50%">' + - '<h3>Title 1</h3>' + - '</td>' + - '<td></td>' + - '<td style="width:50%">' + - '<h3>Title 2</h3>' + - '</td>' + - '</tr>' + - '<tr>' + - '<td>' + - 'Text 1' + - '</td>' + - '<td></td>' + - '<td>' + - 'Text 2' + - '</td>' + - '</tr>' + - '</table>' + - '<p>' + - 'More text goes here.' + - '</p>' - }, - { - title: 'Text and Table', - image: 'template3.gif', - description: 'A title with some text and a table.', - html: - '<div style="width: 80%">' + - '<h3>' + - 'Title goes here' + - '</h3>' + - '<table style="width:150px;float: right" cellspacing="0" cellpadding="0" border="1">' + - '<caption style="border:solid 1px black">' + - '<strong>Table title</strong>' + - '</caption>' + - '</tr>' + - '<tr>' + - '<td> </td>' + - '<td> </td>' + - '<td> </td>' + - '</tr>' + - '<tr>' + - '<td> </td>' + - '<td> </td>' + - '<td> </td>' + - '</tr>' + - '<tr>' + - '<td> </td>' + - '<td> </td>' + - '<td> </td>' + - '</tr>' + - '</table>' + - '<p>' + - 'Type the text here' + - '</p>' + - '</div>' - } - ] -}); +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+// Register a templates definition set named "default".
+CKEDITOR.addTemplates( 'default',
+{
+ // The name of sub folder which hold the shortcut preview images of the
+ // templates.
+ imagesPath : CKEDITOR.getUrl( CKEDITOR.plugins.getPath( 'templates' ) + 'templates/images/' ),
+
+ // The templates definitions.
+ templates :
+ [
+ {
+ title: 'Image and Title',
+ image: 'template1.gif',
+ description: 'One main image with a title and text that surround the image.',
+ html:
+ '<h3>' +
+ '<img style="margin-right: 10px" height="100" width="100" align="left"/>' +
+ 'Type the title here'+
+ '</h3>' +
+ '<p>' +
+ 'Type the text here' +
+ '</p>'
+ },
+ {
+ title: 'Strange Template',
+ image: 'template2.gif',
+ description: 'A template that defines two colums, each one with a title, and some text.',
+ html:
+ '<table cellspacing="0" cellpadding="0" style="width:100%" border="0">' +
+ '<tr>' +
+ '<td style="width:50%">' +
+ '<h3>Title 1</h3>' +
+ '</td>' +
+ '<td></td>' +
+ '<td style="width:50%">' +
+ '<h3>Title 2</h3>' +
+ '</td>' +
+ '</tr>' +
+ '<tr>' +
+ '<td>' +
+ 'Text 1' +
+ '</td>' +
+ '<td></td>' +
+ '<td>' +
+ 'Text 2' +
+ '</td>' +
+ '</tr>' +
+ '</table>' +
+ '<p>' +
+ 'More text goes here.' +
+ '</p>'
+ },
+ {
+ title: 'Text and Table',
+ image: 'template3.gif',
+ description: 'A title with some text and a table.',
+ html:
+ '<div style="width: 80%">' +
+ '<h3>' +
+ 'Title goes here' +
+ '</h3>' +
+ '<table style="width:150px;float: right" cellspacing="0" cellpadding="0" border="1">' +
+ '<caption style="border:solid 1px black">' +
+ '<strong>Table title</strong>' +
+ '</caption>' +
+ '</tr>' +
+ '<tr>' +
+ '<td> </td>' +
+ '<td> </td>' +
+ '<td> </td>' +
+ '</tr>' +
+ '<tr>' +
+ '<td> </td>' +
+ '<td> </td>' +
+ '<td> </td>' +
+ '</tr>' +
+ '<tr>' +
+ '<td> </td>' +
+ '<td> </td>' +
+ '<td> </td>' +
+ '</tr>' +
+ '</table>' +
+ '<p>' +
+ 'Type the text here' +
+ '</p>' +
+ '</div>'
+ }
+ ]
+});
diff --git a/_source/plugins/toolbar/plugin.js b/_source/plugins/toolbar/plugin.js index ada234f..c21e6f2 100644 --- a/_source/plugins/toolbar/plugin.js +++ b/_source/plugins/toolbar/plugin.js @@ -1,479 +1,545 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -/** - * @fileOverview The "toolbar" plugin. Renders the default toolbar interface in - * the editor. - */ - -(function() -{ - var toolbox = function() - { - this.toolbars = []; - this.focusCommandExecuted = false; - }; - - toolbox.prototype.focus = function() - { - for ( var t = 0, toolbar ; toolbar = this.toolbars[ t++ ] ; ) - { - for ( var i = 0, item ; item = toolbar.items[ i++ ] ; ) - { - if ( item.focus ) - { - item.focus(); - return; - } - } - } - }; - - var commands = - { - toolbarFocus : - { - modes : { wysiwyg : 1, source : 1 }, - - exec : function( editor ) - { - if ( editor.toolbox ) - { - editor.toolbox.focusCommandExecuted = true; - - // Make the first button focus accessible. (#3417) - if ( CKEDITOR.env.ie ) - setTimeout( function(){ editor.toolbox.focus(); }, 100 ); - else - editor.toolbox.focus(); - } - } - } - }; - - CKEDITOR.plugins.add( 'toolbar', - { - init : function( editor ) - { - var itemKeystroke = function( item, keystroke ) - { - var next, nextToolGroup, groupItemsCount; - var rtl = editor.lang.dir == 'rtl'; - - switch ( keystroke ) - { - case rtl ? 37 : 39 : // RIGHT-ARROW - case 9 : // TAB - do - { - // Look for the next item in the toolbar. - next = item.next; - - if ( !next ) - { - nextToolGroup = item.toolbar.next; - groupItemsCount = nextToolGroup && nextToolGroup.items.length; - - // Bypass the empty toolgroups. - while ( groupItemsCount === 0 ) - { - nextToolGroup = nextToolGroup.next; - groupItemsCount = nextToolGroup && nextToolGroup.items.length; - } - - if ( nextToolGroup ) - next = nextToolGroup.items[ 0 ]; - } - - item = next; - } - while ( item && !item.focus ) - - // If available, just focus it, otherwise focus the - // first one. - if ( item ) - item.focus(); - else - editor.toolbox.focus(); - - return false; - - case rtl ? 39 : 37 : // LEFT-ARROW - case CKEDITOR.SHIFT + 9 : // SHIFT + TAB - do - { - // Look for the previous item in the toolbar. - next = item.previous; - - if ( !next ) - { - nextToolGroup = item.toolbar.previous; - groupItemsCount = nextToolGroup && nextToolGroup.items.length; - - // Bypass the empty toolgroups. - while ( groupItemsCount === 0 ) - { - nextToolGroup = nextToolGroup.previous; - groupItemsCount = nextToolGroup && nextToolGroup.items.length; - } - - if ( nextToolGroup ) - next = nextToolGroup.items[ groupItemsCount - 1 ]; - } - - item = next; - } - while ( item && !item.focus ) - - // If available, just focus it, otherwise focus the - // last one. - if ( item ) - item.focus(); - else - { - var lastToolbarItems = editor.toolbox.toolbars[ editor.toolbox.toolbars.length - 1 ].items; - lastToolbarItems[ lastToolbarItems.length - 1 ].focus(); - } - - return false; - - case 27 : // ESC - editor.focus(); - return false; - - case 13 : // ENTER - case 32 : // SPACE - item.execute(); - return false; - } - return true; - }; - - editor.on( 'themeSpace', function( event ) - { - if ( event.data.space == editor.config.toolbarLocation ) - { - editor.toolbox = new toolbox(); - - var labelId = 'cke_' + CKEDITOR.tools.getNextNumber(); - - var output = [ '<div class="cke_toolbox" role="toolbar" aria-labelledby="', labelId, '"' ], - expanded = editor.config.toolbarStartupExpanded !== false, - groupStarted; - - output.push( expanded ? '>' : ' style="display:none">' ); - - // Sends the ARIA label. - output.push( '<span id="', labelId, '" class="cke_voice_label">', editor.lang.toolbar, '</span>' ); - - var toolbars = editor.toolbox.toolbars, - toolbar = - ( editor.config.toolbar instanceof Array ) ? - editor.config.toolbar - : - editor.config[ 'toolbar_' + editor.config.toolbar ]; - - for ( var r = 0 ; r < toolbar.length ; r++ ) - { - var row = toolbar[ r ]; - - // It's better to check if the row object is really - // available because it's a common mistake to leave - // an extra comma in the toolbar definition - // settings, which leads on the editor not loading - // at all in IE. (#3983) - if ( !row ) - continue; - - var toolbarId = 'cke_' + CKEDITOR.tools.getNextNumber(), - toolbarObj = { id : toolbarId, items : [] }; - - if ( groupStarted ) - { - output.push( '</div>' ); - groupStarted = 0; - } - - if ( row === '/' ) - { - output.push( '<div class="cke_break"></div>' ); - continue; - } - - output.push( '<span id="', toolbarId, '" class="cke_toolbar" role="presentation"><span class="cke_toolbar_start"></span>' ); - - // Add the toolbar to the "editor.toolbox.toolbars" - // array. - var index = toolbars.push( toolbarObj ) - 1; - - // Create the next/previous reference. - if ( index > 0 ) - { - toolbarObj.previous = toolbars[ index - 1 ]; - toolbarObj.previous.next = toolbarObj; - } - - // Create all items defined for this toolbar. - for ( var i = 0 ; i < row.length ; i++ ) - { - var item, - itemName = row[ i ]; - - if ( itemName == '-' ) - item = CKEDITOR.ui.separator; - else - item = editor.ui.create( itemName ); - - if ( item ) - { - if ( item.canGroup ) - { - if ( !groupStarted ) - { - output.push( '<span class="cke_toolgroup" role="presentation">' ); - groupStarted = 1; - } - } - else if ( groupStarted ) - { - output.push( '</span>' ); - groupStarted = 0; - } - - var itemObj = item.render( editor, output ); - index = toolbarObj.items.push( itemObj ) - 1; - - if ( index > 0 ) - { - itemObj.previous = toolbarObj.items[ index - 1 ]; - itemObj.previous.next = itemObj; - } - - itemObj.toolbar = toolbarObj; - itemObj.onkey = itemKeystroke; - - /* - * Fix for #3052: - * Prevent JAWS from focusing the toolbar after document load. - */ - itemObj.onfocus = function() - { - if ( !editor.toolbox.focusCommandExecuted ) - editor.focus(); - }; - } - } - - if ( groupStarted ) - { - output.push( '</span>' ); - groupStarted = 0; - } - - output.push( '<span class="cke_toolbar_end"></span></span>' ); - } - - output.push( '</div>' ); - - if ( editor.config.toolbarCanCollapse ) - { - var collapserFn = CKEDITOR.tools.addFunction( - function() - { - editor.execCommand( 'toolbarCollapse' ); - } ); - - editor.on( 'destroy', function () { - CKEDITOR.tools.removeFunction( collapserFn ); - } ); - - var collapserId = 'cke_' + CKEDITOR.tools.getNextNumber(); - - editor.addCommand( 'toolbarCollapse', - { - exec : function( editor ) - { - var collapser = CKEDITOR.document.getById( collapserId ); - var toolbox = collapser.getPrevious(); - var contents = editor.getThemeSpace( 'contents' ); - var toolboxContainer = toolbox.getParent(); - var contentHeight = parseInt( contents.$.style.height, 10 ); - var previousHeight = toolboxContainer.$.offsetHeight; - var collapsed = !toolbox.isVisible(); - - if ( !collapsed ) - { - toolbox.hide(); - collapser.addClass( 'cke_toolbox_collapser_min' ); - collapser.setAttribute( 'title', editor.lang.toolbarExpand ); - } - else - { - toolbox.show(); - collapser.removeClass( 'cke_toolbox_collapser_min' ); - collapser.setAttribute( 'title', editor.lang.toolbarCollapse ); - } - - // Update collapser symbol. - collapser.getFirst().setText( collapsed ? - '\u25B2' : // BLACK UP-POINTING TRIANGLE - '\u25C0' ); // BLACK LEFT-POINTING TRIANGLE - - var dy = toolboxContainer.$.offsetHeight - previousHeight; - contents.setStyle( 'height', ( contentHeight - dy ) + 'px' ); - - editor.fire( 'resize' ); - }, - - modes : { wysiwyg : 1, source : 1 } - } ); - - output.push( '<a title="' + ( expanded ? editor.lang.toolbarCollapse : editor.lang.toolbarExpand ) - + '" id="' + collapserId + '" tabIndex="-1" class="cke_toolbox_collapser' ); - - if ( !expanded ) - output.push( ' cke_toolbox_collapser_min' ); - - output.push( '" onclick="CKEDITOR.tools.callFunction(' + collapserFn + ')">', - '<span>▲</span>', // BLACK UP-POINTING TRIANGLE - '</a>' ); - } - - event.data.html += output.join( '' ); - } - }); - - editor.addCommand( 'toolbarFocus', commands.toolbarFocus ); - } - }); -})(); - -/** - * The UI element that renders a toolbar separator. - * @type Object - * @example - */ -CKEDITOR.ui.separator = -{ - render : function( editor, output ) - { - output.push( '<span class="cke_separator" role="separator"></span>' ); - return {}; - } -}; - -/** - * The "theme space" to which rendering the toolbar. For the default theme, - * the recommended options are "top" and "bottom". - * @type String - * @default 'top' - * @see CKEDITOR.config.theme - * @example - * config.toolbarLocation = 'bottom'; - */ -CKEDITOR.config.toolbarLocation = 'top'; - -/** - * The toolbar definition. It is an array of toolbars (strips), - * each one being also an array, containing a list of UI items. - * Note that this setting is composed by "toolbar_" added by the toolbar name, - * which in this case is called "Basic". This second part of the setting name - * can be anything. You must use this name in the - * {@link CKEDITOR.config.toolbar} setting, so you instruct the editor which - * toolbar_(name) setting to you. - * @type Array - * @example - * // Defines a toolbar with only one strip containing the "Source" button, a - * // separator and the "Bold" and "Italic" buttons. - * <b>config.toolbar_Basic = - * [ - * [ 'Source', '-', 'Bold', 'Italic' ] - * ]</b>; - * config.toolbar = 'Basic'; - */ -CKEDITOR.config.toolbar_Basic = -[ - ['Bold', 'Italic', '-', 'NumberedList', 'BulletedList', '-', 'Link', 'Unlink','-','About'] -]; - -/** - * This is the default toolbar definition used by the editor. It contains all - * editor features. - * @type Array - * @default (see example) - * @example - * // This is actually the default value. - * config.toolbar_Full = - * [ - * ['Source','-','Save','NewPage','Preview','-','Templates'], - * ['Cut','Copy','Paste','PasteText','PasteFromWord','-','Print', 'SpellChecker', 'Scayt'], - * ['Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat'], - * ['Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton', 'HiddenField'], - * '/', - * ['Bold','Italic','Underline','Strike','-','Subscript','Superscript'], - * ['NumberedList','BulletedList','-','Outdent','Indent','Blockquote','CreateDiv'], - * ['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'], - * ['Link','Unlink','Anchor'], - * ['Image','Flash','Table','HorizontalRule','Smiley','SpecialChar','PageBreak'], - * '/', - * ['Styles','Format','Font','FontSize'], - * ['TextColor','BGColor'], - * ['Maximize', 'ShowBlocks','-','About'] - * ]; - */ -CKEDITOR.config.toolbar_Full = -[ - ['Source','-','Save','NewPage','Preview','-','Templates'], - ['Cut','Copy','Paste','PasteText','PasteFromWord','-','Print', 'SpellChecker', 'Scayt'], - ['Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat'], - ['Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton', 'HiddenField'], - '/', - ['Bold','Italic','Underline','Strike','-','Subscript','Superscript'], - ['NumberedList','BulletedList','-','Outdent','Indent','Blockquote','CreateDiv'], - ['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'], - ['Link','Unlink','Anchor'], - ['Image','Flash','Table','HorizontalRule','Smiley','SpecialChar','PageBreak'], - '/', - ['Styles','Format','Font','FontSize'], - ['TextColor','BGColor'], - ['Maximize', 'ShowBlocks','-','About'] -]; - -/** - * The toolbox (alias toolbar) definition. It is a toolbar name or an array of - * toolbars (strips), each one being also an array, containing a list of UI items. - * @type Array|String - * @default 'Full' - * @example - * // Defines a toolbar with only one strip containing the "Source" button, a - * // separator and the "Bold" and "Italic" buttons. - * config.toolbar = - * [ - * [ 'Source', '-', 'Bold', 'Italic' ] - * ]; - * @example - * // Load toolbar_Name where Name = Basic. - * config.toolbar = 'Basic'; - */ -CKEDITOR.config.toolbar = 'Full'; - -/** - * Whether the toolbar can be collapsed by the user. If disabled, the collapser - * button will not be displayed. - * @type Boolean - * @default true - * @example - * config.toolbarCanCollapse = false; - */ -CKEDITOR.config.toolbarCanCollapse = true; - -/** - * Whether the toolbar must start expanded when the editor is loaded. - * @name CKEDITOR.config.toolbarStartupExpanded - * @type Boolean - * @default true - * @example - * config.toolbarStartupExpanded = false; - */ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview The "toolbar" plugin. Renders the default toolbar interface in
+ * the editor.
+ */
+
+(function()
+{
+ var toolbox = function()
+ {
+ this.toolbars = [];
+ this.focusCommandExecuted = false;
+ };
+
+ toolbox.prototype.focus = function()
+ {
+ for ( var t = 0, toolbar ; toolbar = this.toolbars[ t++ ] ; )
+ {
+ for ( var i = 0, item ; item = toolbar.items[ i++ ] ; )
+ {
+ if ( item.focus )
+ {
+ item.focus();
+ return;
+ }
+ }
+ }
+ };
+
+ var commands =
+ {
+ toolbarFocus :
+ {
+ modes : { wysiwyg : 1, source : 1 },
+ readOnly : 1,
+
+ exec : function( editor )
+ {
+ if ( editor.toolbox )
+ {
+ editor.toolbox.focusCommandExecuted = true;
+
+ // Make the first button focus accessible for IE. (#3417)
+ // Adobe AIR instead need while of delay.
+ if ( CKEDITOR.env.ie || CKEDITOR.env.air )
+ setTimeout( function(){ editor.toolbox.focus(); }, 100 );
+ else
+ editor.toolbox.focus();
+ }
+ }
+ }
+ };
+
+ CKEDITOR.plugins.add( 'toolbar',
+ {
+ init : function( editor )
+ {
+ var endFlag;
+
+ var itemKeystroke = function( item, keystroke )
+ {
+ var next, toolbar;
+ var rtl = editor.lang.dir == 'rtl',
+ toolbarGroupCycling = editor.config.toolbarGroupCycling;
+
+ toolbarGroupCycling = toolbarGroupCycling === undefined || toolbarGroupCycling;
+
+ switch ( keystroke )
+ {
+ case 9 : // TAB
+ case CKEDITOR.SHIFT + 9 : // SHIFT + TAB
+ // Cycle through the toolbars, starting from the one
+ // closest to the current item.
+ while ( !toolbar || !toolbar.items.length )
+ {
+ toolbar = keystroke == 9 ?
+ ( ( toolbar ? toolbar.next : item.toolbar.next ) || editor.toolbox.toolbars[ 0 ] ) :
+ ( ( toolbar ? toolbar.previous : item.toolbar.previous ) || editor.toolbox.toolbars[ editor.toolbox.toolbars.length - 1 ] );
+
+ // Look for the first item that accepts focus.
+ if ( toolbar.items.length )
+ {
+ item = toolbar.items[ endFlag ? ( toolbar.items.length - 1 ) : 0 ];
+ while ( item && !item.focus )
+ {
+ item = endFlag ? item.previous : item.next;
+
+ if ( !item )
+ toolbar = 0;
+ }
+ }
+ }
+
+ if ( item )
+ item.focus();
+
+ return false;
+
+ case rtl ? 37 : 39 : // RIGHT-ARROW
+ case 40 : // DOWN-ARROW
+ next = item;
+ do
+ {
+ // Look for the next item in the toolbar.
+ next = next.next;
+
+ // If it's the last item, cycle to the first one.
+ if ( !next && toolbarGroupCycling )
+ next = item.toolbar.items[ 0 ];
+ }
+ while ( next && !next.focus )
+
+ // If available, just focus it, otherwise focus the
+ // first one.
+ if ( next )
+ next.focus();
+ else
+ // Send a TAB.
+ itemKeystroke( item, 9 );
+
+ return false;
+
+ case rtl ? 39 : 37 : // LEFT-ARROW
+ case 38 : // UP-ARROW
+ next = item;
+ do
+ {
+ // Look for the previous item in the toolbar.
+ next = next.previous;
+
+ // If it's the first item, cycle to the last one.
+ if ( !next && toolbarGroupCycling )
+ next = item.toolbar.items[ item.toolbar.items.length - 1 ];
+ }
+ while ( next && !next.focus )
+
+ // If available, just focus it, otherwise focus the
+ // last one.
+ if ( next )
+ next.focus();
+ else
+ {
+ endFlag = 1;
+ // Send a SHIFT + TAB.
+ itemKeystroke( item, CKEDITOR.SHIFT + 9 );
+ endFlag = 0;
+ }
+
+ return false;
+
+ case 27 : // ESC
+ editor.focus();
+ return false;
+
+ case 13 : // ENTER
+ case 32 : // SPACE
+ item.execute();
+ return false;
+ }
+ return true;
+ };
+
+ editor.on( 'themeSpace', function( event )
+ {
+ if ( event.data.space == editor.config.toolbarLocation )
+ {
+ editor.toolbox = new toolbox();
+
+ var labelId = CKEDITOR.tools.getNextId();
+
+ var output = [ '<div class="cke_toolbox" role="group" aria-labelledby="', labelId, '" onmousedown="return false;"' ],
+ expanded = editor.config.toolbarStartupExpanded !== false,
+ groupStarted;
+
+ output.push( expanded ? '>' : ' style="display:none">' );
+
+ // Sends the ARIA label.
+ output.push( '<span id="', labelId, '" class="cke_voice_label">', editor.lang.toolbars, '</span>' );
+
+ var toolbars = editor.toolbox.toolbars,
+ toolbar =
+ ( editor.config.toolbar instanceof Array ) ?
+ editor.config.toolbar
+ :
+ editor.config[ 'toolbar_' + editor.config.toolbar ];
+
+ for ( var r = 0 ; r < toolbar.length ; r++ )
+ {
+ var toolbarId,
+ toolbarObj = 0,
+ toolbarName,
+ row = toolbar[ r ],
+ items;
+
+ // It's better to check if the row object is really
+ // available because it's a common mistake to leave
+ // an extra comma in the toolbar definition
+ // settings, which leads on the editor not loading
+ // at all in IE. (#3983)
+ if ( !row )
+ continue;
+
+ if ( groupStarted )
+ {
+ output.push( '</div>' );
+ groupStarted = 0;
+ }
+
+ if ( row === '/' )
+ {
+ output.push( '<div class="cke_break"></div>' );
+ continue;
+ }
+
+ items = row.items || row;
+
+ // Create all items defined for this toolbar.
+ for ( var i = 0 ; i < items.length ; i++ )
+ {
+ var item,
+ itemName = items[ i ],
+ canGroup;
+
+ item = editor.ui.create( itemName );
+
+ if ( item )
+ {
+ canGroup = item.canGroup !== false;
+
+ // Initialize the toolbar first, if needed.
+ if ( !toolbarObj )
+ {
+ // Create the basic toolbar object.
+ toolbarId = CKEDITOR.tools.getNextId();
+ toolbarObj = { id : toolbarId, items : [] };
+ toolbarName = row.name && ( editor.lang.toolbarGroups[ row.name ] || row.name );
+
+ // Output the toolbar opener.
+ output.push( '<span id="', toolbarId, '" class="cke_toolbar"',
+ ( toolbarName ? ' aria-labelledby="'+ toolbarId + '_label"' : '' ),
+ ' role="toolbar">' );
+
+ // If a toolbar name is available, send the voice label.
+ toolbarName && output.push( '<span id="', toolbarId, '_label" class="cke_voice_label">', toolbarName, '</span>' );
+
+ output.push( '<span class="cke_toolbar_start"></span>' );
+
+ // Add the toolbar to the "editor.toolbox.toolbars"
+ // array.
+ var index = toolbars.push( toolbarObj ) - 1;
+
+ // Create the next/previous reference.
+ if ( index > 0 )
+ {
+ toolbarObj.previous = toolbars[ index - 1 ];
+ toolbarObj.previous.next = toolbarObj;
+ }
+ }
+
+ if ( canGroup )
+ {
+ if ( !groupStarted )
+ {
+ output.push( '<span class="cke_toolgroup" role="presentation">' );
+ groupStarted = 1;
+ }
+ }
+ else if ( groupStarted )
+ {
+ output.push( '</span>' );
+ groupStarted = 0;
+ }
+
+ var itemObj = item.render( editor, output );
+ index = toolbarObj.items.push( itemObj ) - 1;
+
+ if ( index > 0 )
+ {
+ itemObj.previous = toolbarObj.items[ index - 1 ];
+ itemObj.previous.next = itemObj;
+ }
+
+ itemObj.toolbar = toolbarObj;
+ itemObj.onkey = itemKeystroke;
+
+ /*
+ * Fix for #3052:
+ * Prevent JAWS from focusing the toolbar after document load.
+ */
+ itemObj.onfocus = function()
+ {
+ if ( !editor.toolbox.focusCommandExecuted )
+ editor.focus();
+ };
+ }
+ }
+
+ if ( groupStarted )
+ {
+ output.push( '</span>' );
+ groupStarted = 0;
+ }
+
+ if ( toolbarObj )
+ output.push( '<span class="cke_toolbar_end"></span></span>' );
+ }
+
+ output.push( '</div>' );
+
+ if ( editor.config.toolbarCanCollapse )
+ {
+ var collapserFn = CKEDITOR.tools.addFunction(
+ function()
+ {
+ editor.execCommand( 'toolbarCollapse' );
+ });
+
+ editor.on( 'destroy', function () {
+ CKEDITOR.tools.removeFunction( collapserFn );
+ });
+
+ var collapserId = CKEDITOR.tools.getNextId();
+
+ editor.addCommand( 'toolbarCollapse',
+ {
+ readOnly : 1,
+ exec : function( editor )
+ {
+ var collapser = CKEDITOR.document.getById( collapserId ),
+ toolbox = collapser.getPrevious(),
+ contents = editor.getThemeSpace( 'contents' ),
+ toolboxContainer = toolbox.getParent(),
+ contentHeight = parseInt( contents.$.style.height, 10 ),
+ previousHeight = toolboxContainer.$.offsetHeight,
+ collapsed = !toolbox.isVisible();
+
+ if ( !collapsed )
+ {
+ toolbox.hide();
+ collapser.addClass( 'cke_toolbox_collapser_min' );
+ collapser.setAttribute( 'title', editor.lang.toolbarExpand );
+ }
+ else
+ {
+ toolbox.show();
+ collapser.removeClass( 'cke_toolbox_collapser_min' );
+ collapser.setAttribute( 'title', editor.lang.toolbarCollapse );
+ }
+
+ // Update collapser symbol.
+ collapser.getFirst().setText( collapsed ?
+ '\u25B2' : // BLACK UP-POINTING TRIANGLE
+ '\u25C0' ); // BLACK LEFT-POINTING TRIANGLE
+
+ var dy = toolboxContainer.$.offsetHeight - previousHeight;
+ contents.setStyle( 'height', ( contentHeight - dy ) + 'px' );
+
+ editor.fire( 'resize' );
+ },
+
+ modes : { wysiwyg : 1, source : 1 }
+ } );
+
+ output.push( '<a title="' + ( expanded ? editor.lang.toolbarCollapse : editor.lang.toolbarExpand )
+ + '" id="' + collapserId + '" tabIndex="-1" class="cke_toolbox_collapser' );
+
+ if ( !expanded )
+ output.push( ' cke_toolbox_collapser_min' );
+
+ output.push( '" onclick="CKEDITOR.tools.callFunction(' + collapserFn + ')">',
+ '<span>▲</span>', // BLACK UP-POINTING TRIANGLE
+ '</a>' );
+ }
+
+ event.data.html += output.join( '' );
+ }
+ });
+
+ editor.on( 'destroy', function()
+ {
+ var toolbars, index = 0, i,
+ items, instance;
+ toolbars = this.toolbox.toolbars;
+ for ( ; index < toolbars.length; index++ )
+ {
+ items = toolbars[ index ].items;
+ for ( i = 0; i < items.length; i++ )
+ {
+ instance = items[ i ];
+ if ( instance.clickFn ) CKEDITOR.tools.removeFunction( instance.clickFn );
+ if ( instance.keyDownFn ) CKEDITOR.tools.removeFunction( instance.keyDownFn );
+ }
+ }
+ });
+
+ editor.addCommand( 'toolbarFocus', commands.toolbarFocus );
+
+ editor.ui.add( '-', CKEDITOR.UI_SEPARATOR, {} );
+ editor.ui.addHandler( CKEDITOR.UI_SEPARATOR,
+ {
+ create: function()
+ {
+ return {
+ render : function( editor, output )
+ {
+ output.push( '<span class="cke_separator" role="separator"></span>' );
+ return {};
+ }
+ };
+ }
+ });
+ }
+ });
+})();
+
+CKEDITOR.UI_SEPARATOR = 'separator';
+
+/**
+ * The "theme space" to which rendering the toolbar. For the default theme,
+ * the recommended options are "top" and "bottom".
+ * @type String
+ * @default 'top'
+ * @see CKEDITOR.config.theme
+ * @example
+ * config.toolbarLocation = 'bottom';
+ */
+CKEDITOR.config.toolbarLocation = 'top';
+
+/**
+ * The toolbar definition. It is an array of toolbars (strips),
+ * each one being also an array, containing a list of UI items.
+ * Note that this setting is composed by "toolbar_" added by the toolbar name,
+ * which in this case is called "Basic". This second part of the setting name
+ * can be anything. You must use this name in the
+ * {@link CKEDITOR.config.toolbar} setting, so you instruct the editor which
+ * toolbar_(name) setting to you.
+ * @type Array
+ * @example
+ * // Defines a toolbar with only one strip containing the "Source" button, a
+ * // separator and the "Bold" and "Italic" buttons.
+ * <b>config.toolbar_Basic =
+ * [
+ * [ 'Source', '-', 'Bold', 'Italic' ]
+ * ]</b>;
+ * config.toolbar = 'Basic';
+ */
+CKEDITOR.config.toolbar_Basic =
+[
+ ['Bold', 'Italic', '-', 'NumberedList', 'BulletedList', '-', 'Link', 'Unlink','-','About']
+];
+
+/**
+ * This is the default toolbar definition used by the editor. It contains all
+ * editor features.
+ * @type Array
+ * @default (see example)
+ * @example
+ * // This is actually the default value.
+ * config.toolbar_Full =
+ * [
+ * { name: 'document', items : [ 'Source','-','Save','NewPage','DocProps','Preview','Print','-','Templates' ] },
+ * { name: 'clipboard', items : [ 'Cut','Copy','Paste','PasteText','PasteFromWord','-','Undo','Redo' ] },
+ * { name: 'editing', items : [ 'Find','Replace','-','SelectAll','-','SpellChecker', 'Scayt' ] },
+ * { name: 'forms', items : [ 'Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton', 'HiddenField' ] },
+ * '/',
+ * { name: 'basicstyles', items : [ 'Bold','Italic','Underline','Strike','Subscript','Superscript','-','RemoveFormat' ] },
+ * { name: 'paragraph', items : [ 'NumberedList','BulletedList','-','Outdent','Indent','-','Blockquote','CreateDiv','-','JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock','-','BidiLtr','BidiRtl' ] },
+ * { name: 'links', items : [ 'Link','Unlink','Anchor' ] },
+ * { name: 'insert', items : [ 'Image','Flash','Table','HorizontalRule','Smiley','SpecialChar','PageBreak' ] },
+ * '/',
+ * { name: 'styles', items : [ 'Styles','Format','Font','FontSize' ] },
+ * { name: 'colors', items : [ 'TextColor','BGColor' ] },
+ * { name: 'tools', items : [ 'Maximize', 'ShowBlocks','-','About' ] }
+ * ];
+ */
+CKEDITOR.config.toolbar_Full =
+[
+ { name: 'document', items : [ 'Source','-','Save','NewPage','DocProps','Preview','Print','-','Templates' ] },
+ { name: 'clipboard', items : [ 'Cut','Copy','Paste','PasteText','PasteFromWord','-','Undo','Redo' ] },
+ { name: 'editing', items : [ 'Find','Replace','-','SelectAll','-','SpellChecker', 'Scayt' ] },
+ { name: 'forms', items : [ 'Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton', 'HiddenField' ] },
+ '/',
+ { name: 'basicstyles', items : [ 'Bold','Italic','Underline','Strike','Subscript','Superscript','-','RemoveFormat' ] },
+ { name: 'paragraph', items : [ 'NumberedList','BulletedList','-','Outdent','Indent','-','Blockquote','CreateDiv','-','JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock','-','BidiLtr','BidiRtl' ] },
+ { name: 'links', items : [ 'Link','Unlink','Anchor' ] },
+ { name: 'insert', items : [ 'Image','Flash','Table','HorizontalRule','Smiley','SpecialChar','PageBreak','Iframe' ] },
+ '/',
+ { name: 'styles', items : [ 'Styles','Format','Font','FontSize' ] },
+ { name: 'colors', items : [ 'TextColor','BGColor' ] },
+ { name: 'tools', items : [ 'Maximize', 'ShowBlocks','-','About' ] }
+];
+
+/**
+ * The toolbox (alias toolbar) definition. It is a toolbar name or an array of
+ * toolbars (strips), each one being also an array, containing a list of UI items.
+ * @type Array|String
+ * @default 'Full'
+ * @example
+ * // Defines a toolbar with only one strip containing the "Source" button, a
+ * // separator and the "Bold" and "Italic" buttons.
+ * config.toolbar =
+ * [
+ * [ 'Source', '-', 'Bold', 'Italic' ]
+ * ];
+ * @example
+ * // Load toolbar_Name where Name = Basic.
+ * config.toolbar = 'Basic';
+ */
+CKEDITOR.config.toolbar = 'Full';
+
+/**
+ * Whether the toolbar can be collapsed by the user. If disabled, the collapser
+ * button will not be displayed.
+ * @type Boolean
+ * @default true
+ * @example
+ * config.toolbarCanCollapse = false;
+ */
+CKEDITOR.config.toolbarCanCollapse = true;
+
+/**
+ * Whether the toolbar must start expanded when the editor is loaded.
+ * @name CKEDITOR.config.toolbarStartupExpanded
+ * @type Boolean
+ * @default true
+ * @example
+ * config.toolbarStartupExpanded = false;
+ */
+
+/**
+ * When enabled, makes the arrow keys navigation cycle within the current
+ * toolbar group. Otherwise the arrows will move trought all items available in
+ * the toolbar. The TAB key will still be used to quickly jump among the
+ * toolbar groups.
+ * @name CKEDITOR.config.toolbarGroupCycling
+ * @since 3.6
+ * @type Boolean
+ * @default true
+ * @example
+ * config.toolbarGroupCycling = false;
+ */
diff --git a/_source/plugins/uicolor/dialogs/uicolor.js b/_source/plugins/uicolor/dialogs/uicolor.js index aad0fc7..cc8589d 100644 --- a/_source/plugins/uicolor/dialogs/uicolor.js +++ b/_source/plugins/uicolor/dialogs/uicolor.js @@ -1,205 +1,205 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -CKEDITOR.dialog.add( 'uicolor', function( editor ) -{ - var dialog, picker, pickerContents, - // Actual UI color value. - uiColor = editor.getUiColor(), - pickerId = 'cke_uicolor_picker' + CKEDITOR.tools.getNextNumber(); - - function setNewPickerColor( color ) - { - // Convert HEX representation to RGB, stripping # char. - if ( /^#/.test( color ) ) - color = window.YAHOO.util.Color.hex2rgb( color.substr( 1 ) ); - picker.setValue( color, true ); - // Refresh picker UI. - picker.refresh( pickerId ); - } - - function setNewUiColor( color, force ) - { - if ( force || dialog._.contents.tab1.livePeview.getValue() ) - editor.setUiColor( color ); - // Write new config string into textbox. - dialog._.contents.tab1.configBox.setValue( - 'config.uiColor = "#' + picker.get( "hex" ) + '"' - ); - } - - pickerContents = - { - id : 'yuiColorPicker', - type : 'html', - html : "<div id='" + pickerId + "' class='cke_uicolor_picker' style='width: 360px; height: 200px; position: relative;'></div>", - onLoad : function( event ) - { - var url = CKEDITOR.getUrl( - '_source/' + // @Packager.RemoveLine - 'plugins/uicolor/yui/' - ); - - // Create new color picker widget. - picker = new window.YAHOO.widget.ColorPicker( pickerId, - { - showhsvcontrols : true, - showhexcontrols : true, - images : - { - PICKER_THUMB : url + "assets/picker_thumb.png", - HUE_THUMB : url + "assets/hue_thumb.png" - } - }); - - // Set actual UI color to the picker. - if ( uiColor ) - setNewPickerColor( uiColor ); - - // Subscribe to the rgbChange event. - picker.on( "rgbChange", function() - { - // Reset predefined box. - dialog._.contents.tab1.predefined.setValue( '' ); - setNewUiColor( '#' + picker.get( 'hex' ) ); - }); - - // Fix input class names. - var inputs = new CKEDITOR.dom.nodeList( picker.getElementsByTagName( 'input' ) ); - for ( var i = 0; i < inputs.count() ; i++ ) - inputs.getItem( i ).addClass( 'cke_dialog_ui_input_text' ); - } - }; - - var skipPreviewChange = true; - - return { - title : editor.lang.uicolor.title, - minWidth : 360, - minHeight : 320, - onLoad : function() - { - dialog = this; - this.setupContent(); - - // #3808 - if ( CKEDITOR.env.ie7Compat ) - dialog.parts.contents.setStyle( 'overflow', 'hidden' ); - }, - contents : [ - { - id : 'tab1', - label : '', - title : '', - expand : true, - padding : 0, - elements : [ - pickerContents, - { - id : 'tab1', - type : 'vbox', - children : - [ - { - id : 'livePeview', - type : 'checkbox', - label : editor.lang.uicolor.preview, - 'default' : 1, - onLoad : function() - { - skipPreviewChange = true; - }, - onChange : function() - { - if ( skipPreviewChange ) - return; - var on = this.getValue(), - color = on ? '#' + picker.get( 'hex' ) : uiColor; - setNewUiColor( color, true ); - } - }, - { - type : 'hbox', - children : - [ - { - id : 'predefined', - type : 'select', - 'default' : '', - label : editor.lang.uicolor.predefined, - items : - [ - [ '' ], - [ 'Light blue', '#9AB8F3' ], - [ 'Sand', '#D2B48C' ], - [ 'Metallic', '#949AAA' ], - [ 'Purple', '#C2A3C7' ], - [ 'Olive', '#A2C980' ], - [ 'Happy green', '#9BD446' ], - [ 'Jezebel Blue', '#14B8C4' ], - [ 'Burn', '#FF893A' ], - [ 'Easy red', '#FF6969' ], - [ 'Pisces 3', '#48B4F2' ], - [ 'Aquarius 5', '#487ED4' ], - [ 'Absinthe', '#A8CF76' ], - [ 'Scrambled Egg', '#C7A622' ], - [ 'Hello monday', '#8E8D80' ], - [ 'Lovely sunshine', '#F1E8B1' ], - [ 'Recycled air', '#B3C593' ], - [ 'Down', '#BCBCA4' ], - [ 'Mark Twain', '#CFE91D' ], - [ 'Specks of dust', '#D1B596' ], - [ 'Lollipop', '#F6CE23' ] - ], - onChange : function() - { - var color = this.getValue(); - if ( color ) - { - setNewPickerColor( color ); - setNewUiColor( color ); - // Refresh predefined preview box. - CKEDITOR.document.getById( 'predefinedPreview' ).setStyle( 'background', color ); - } - else - CKEDITOR.document.getById( 'predefinedPreview' ).setStyle( 'background', '' ); - }, - onShow : function() - { - var color = editor.getUiColor(); - if ( color ) - this.setValue( color ); - } - }, - { - id : 'predefinedPreview', - type : 'html', - html : '<div id="cke_uicolor_preview" style="border: 1px solid black; padding: 3px; width: 30px;">' + - '<div id="predefinedPreview" style="width: 30px; height: 30px;"> </div>' + - '</div>' - } - ] - }, - { - id : 'configBox', - type : 'text', - label : editor.lang.uicolor.config, - onShow : function() - { - var color = editor.getUiColor(); - if ( color ) - this.setValue( - 'config.uiColor = "' + color + '"' - ); - } - } - ] - } - ] - } - ], - buttons : [ CKEDITOR.dialog.okButton ] - }; -} ); +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.dialog.add( 'uicolor', function( editor )
+{
+ var dialog, picker, pickerContents,
+ // Actual UI color value.
+ uiColor = editor.getUiColor(),
+ pickerId = 'cke_uicolor_picker' + CKEDITOR.tools.getNextNumber();
+
+ function setNewPickerColor( color )
+ {
+ // Convert HEX representation to RGB, stripping # char.
+ if ( /^#/.test( color ) )
+ color = window.YAHOO.util.Color.hex2rgb( color.substr( 1 ) );
+ picker.setValue( color, true );
+ // Refresh picker UI.
+ picker.refresh( pickerId );
+ }
+
+ function setNewUiColor( color, force )
+ {
+ if ( force || dialog._.contents.tab1.livePeview.getValue() )
+ editor.setUiColor( color );
+ // Write new config string into textbox.
+ dialog._.contents.tab1.configBox.setValue(
+ 'config.uiColor = "#' + picker.get( "hex" ) + '"'
+ );
+ }
+
+ pickerContents =
+ {
+ id : 'yuiColorPicker',
+ type : 'html',
+ html : "<div id='" + pickerId + "' class='cke_uicolor_picker' style='width: 360px; height: 200px; position: relative;'></div>",
+ onLoad : function( event )
+ {
+ var url = CKEDITOR.getUrl(
+ '_source/' + // @Packager.RemoveLine
+ 'plugins/uicolor/yui/'
+ );
+
+ // Create new color picker widget.
+ picker = new window.YAHOO.widget.ColorPicker( pickerId,
+ {
+ showhsvcontrols : true,
+ showhexcontrols : true,
+ images :
+ {
+ PICKER_THUMB : url + "assets/picker_thumb.png",
+ HUE_THUMB : url + "assets/hue_thumb.png"
+ }
+ });
+
+ // Set actual UI color to the picker.
+ if ( uiColor )
+ setNewPickerColor( uiColor );
+
+ // Subscribe to the rgbChange event.
+ picker.on( "rgbChange", function()
+ {
+ // Reset predefined box.
+ dialog._.contents.tab1.predefined.setValue( '' );
+ setNewUiColor( '#' + picker.get( 'hex' ) );
+ });
+
+ // Fix input class names.
+ var inputs = new CKEDITOR.dom.nodeList( picker.getElementsByTagName( 'input' ) );
+ for ( var i = 0; i < inputs.count() ; i++ )
+ inputs.getItem( i ).addClass( 'cke_dialog_ui_input_text' );
+ }
+ };
+
+ var skipPreviewChange = true;
+
+ return {
+ title : editor.lang.uicolor.title,
+ minWidth : 360,
+ minHeight : 320,
+ onLoad : function()
+ {
+ dialog = this;
+ this.setupContent();
+
+ // #3808
+ if ( CKEDITOR.env.ie7Compat )
+ dialog.parts.contents.setStyle( 'overflow', 'hidden' );
+ },
+ contents : [
+ {
+ id : 'tab1',
+ label : '',
+ title : '',
+ expand : true,
+ padding : 0,
+ elements : [
+ pickerContents,
+ {
+ id : 'tab1',
+ type : 'vbox',
+ children :
+ [
+ {
+ id : 'livePeview',
+ type : 'checkbox',
+ label : editor.lang.uicolor.preview,
+ 'default' : 1,
+ onLoad : function()
+ {
+ skipPreviewChange = true;
+ },
+ onChange : function()
+ {
+ if ( skipPreviewChange )
+ return;
+ var on = this.getValue(),
+ color = on ? '#' + picker.get( 'hex' ) : uiColor;
+ setNewUiColor( color, true );
+ }
+ },
+ {
+ type : 'hbox',
+ children :
+ [
+ {
+ id : 'predefined',
+ type : 'select',
+ 'default' : '',
+ label : editor.lang.uicolor.predefined,
+ items :
+ [
+ [ '' ],
+ [ 'Light blue', '#9AB8F3' ],
+ [ 'Sand', '#D2B48C' ],
+ [ 'Metallic', '#949AAA' ],
+ [ 'Purple', '#C2A3C7' ],
+ [ 'Olive', '#A2C980' ],
+ [ 'Happy green', '#9BD446' ],
+ [ 'Jezebel Blue', '#14B8C4' ],
+ [ 'Burn', '#FF893A' ],
+ [ 'Easy red', '#FF6969' ],
+ [ 'Pisces 3', '#48B4F2' ],
+ [ 'Aquarius 5', '#487ED4' ],
+ [ 'Absinthe', '#A8CF76' ],
+ [ 'Scrambled Egg', '#C7A622' ],
+ [ 'Hello monday', '#8E8D80' ],
+ [ 'Lovely sunshine', '#F1E8B1' ],
+ [ 'Recycled air', '#B3C593' ],
+ [ 'Down', '#BCBCA4' ],
+ [ 'Mark Twain', '#CFE91D' ],
+ [ 'Specks of dust', '#D1B596' ],
+ [ 'Lollipop', '#F6CE23' ]
+ ],
+ onChange : function()
+ {
+ var color = this.getValue();
+ if ( color )
+ {
+ setNewPickerColor( color );
+ setNewUiColor( color );
+ // Refresh predefined preview box.
+ CKEDITOR.document.getById( 'predefinedPreview' ).setStyle( 'background', color );
+ }
+ else
+ CKEDITOR.document.getById( 'predefinedPreview' ).setStyle( 'background', '' );
+ },
+ onShow : function()
+ {
+ var color = editor.getUiColor();
+ if ( color )
+ this.setValue( color );
+ }
+ },
+ {
+ id : 'predefinedPreview',
+ type : 'html',
+ html : '<div id="cke_uicolor_preview" style="border: 1px solid black; padding: 3px; width: 30px;">' +
+ '<div id="predefinedPreview" style="width: 30px; height: 30px;"> </div>' +
+ '</div>'
+ }
+ ]
+ },
+ {
+ id : 'configBox',
+ type : 'text',
+ label : editor.lang.uicolor.config,
+ onShow : function()
+ {
+ var color = editor.getUiColor();
+ if ( color )
+ this.setValue(
+ 'config.uiColor = "' + color + '"'
+ );
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ buttons : [ CKEDITOR.dialog.okButton ]
+ };
+} );
diff --git a/_source/plugins/uicolor/lang/_translationstatus.txt b/_source/plugins/uicolor/lang/_translationstatus.txt new file mode 100644 index 0000000..e8fabc8 --- /dev/null +++ b/_source/plugins/uicolor/lang/_translationstatus.txt @@ -0,0 +1,27 @@ +Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+
+bg.js Found: 4 Missing: 0
+cs.js Found: 4 Missing: 0
+cy.js Found: 4 Missing: 0
+da.js Found: 4 Missing: 0
+de.js Found: 4 Missing: 0
+el.js Found: 4 Missing: 0
+eo.js Found: 4 Missing: 0
+et.js Found: 4 Missing: 0
+fa.js Found: 4 Missing: 0
+fi.js Found: 4 Missing: 0
+fr.js Found: 4 Missing: 0
+he.js Found: 4 Missing: 0
+hr.js Found: 4 Missing: 0
+it.js Found: 4 Missing: 0
+mk.js Found: 4 Missing: 0
+nb.js Found: 4 Missing: 0
+nl.js Found: 4 Missing: 0
+no.js Found: 4 Missing: 0
+pl.js Found: 4 Missing: 0
+tr.js Found: 4 Missing: 0
+ug.js Found: 4 Missing: 0
+uk.js Found: 4 Missing: 0
+vi.js Found: 4 Missing: 0
+zh-cn.js Found: 4 Missing: 0
diff --git a/_source/plugins/uicolor/lang/bg.js b/_source/plugins/uicolor/lang/bg.js new file mode 100644 index 0000000..8b41839 --- /dev/null +++ b/_source/plugins/uicolor/lang/bg.js @@ -0,0 +1,15 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'uicolor', 'bg',
+{
+ uicolor :
+ {
+ title : 'ПИ избор на цвят',
+ preview : 'Преглед',
+ config : 'Вмъкнете този низ във Вашия config.js fajl',
+ predefined : 'Предефинирани цветови палитри'
+ }
+});
diff --git a/_source/plugins/uicolor/lang/cs.js b/_source/plugins/uicolor/lang/cs.js new file mode 100644 index 0000000..778f206 --- /dev/null +++ b/_source/plugins/uicolor/lang/cs.js @@ -0,0 +1,15 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'uicolor', 'cs',
+{
+ uicolor :
+ {
+ title : 'Výběr barvy rozhraní',
+ preview : 'Živý náhled',
+ config : 'Vložte tento řetězec do Vašeho souboru config.js',
+ predefined : 'Přednastavené sady barev'
+ }
+});
diff --git a/_source/plugins/uicolor/lang/cy.js b/_source/plugins/uicolor/lang/cy.js new file mode 100644 index 0000000..5033e8d --- /dev/null +++ b/_source/plugins/uicolor/lang/cy.js @@ -0,0 +1,15 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'uicolor', 'cy',
+{
+ uicolor :
+ {
+ title : 'Dewisydd Lliwiau\'r UI',
+ preview : 'Rhagolwg Byw',
+ config : 'Gludwch y llinyn hwn i\'ch ffeil config.js',
+ predefined : 'Setiau lliw wedi\'u cyn-ddiffinio'
+ }
+});
diff --git a/_source/plugins/uicolor/lang/da.js b/_source/plugins/uicolor/lang/da.js new file mode 100644 index 0000000..a2c9701 --- /dev/null +++ b/_source/plugins/uicolor/lang/da.js @@ -0,0 +1,15 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'uicolor', 'da',
+{
+ uicolor :
+ {
+ title : 'Brugerflade på farvevælger',
+ preview : 'Vis liveeksempel',
+ config : 'Indsæt denne streng i din config.js fil',
+ predefined : 'Prædefinerede farveskemaer'
+ }
+});
diff --git a/_source/plugins/uicolor/lang/de.js b/_source/plugins/uicolor/lang/de.js new file mode 100644 index 0000000..d59d653 --- /dev/null +++ b/_source/plugins/uicolor/lang/de.js @@ -0,0 +1,15 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'uicolor', 'de',
+{
+ uicolor :
+ {
+ title : 'UI Pipette',
+ preview : 'Live-Vorschau',
+ config : 'Fügen Sie diese Zeichenfolge in die \'config.js\' Datei.',
+ predefined : 'Vordefinierte Farbsätze'
+ }
+});
diff --git a/_source/plugins/uicolor/lang/el.js b/_source/plugins/uicolor/lang/el.js new file mode 100644 index 0000000..c73334e --- /dev/null +++ b/_source/plugins/uicolor/lang/el.js @@ -0,0 +1,15 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'uicolor', 'el',
+{
+ uicolor :
+ {
+ title : 'Διεπαφή Επιλογέα Χρωμάτων',
+ preview : 'Ζωντανή Προεπισκόπηση',
+ config : 'Επικολλήστε αυτό το κείμενο στο αρχείο config.js',
+ predefined : 'Προκαθορισμένα σύνολα χρωμάτων'
+ }
+});
diff --git a/_source/plugins/uicolor/lang/en.js b/_source/plugins/uicolor/lang/en.js index 9c47822..7acb22c 100644 --- a/_source/plugins/uicolor/lang/en.js +++ b/_source/plugins/uicolor/lang/en.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
diff --git a/_source/plugins/uicolor/lang/eo.js b/_source/plugins/uicolor/lang/eo.js new file mode 100644 index 0000000..cce1447 --- /dev/null +++ b/_source/plugins/uicolor/lang/eo.js @@ -0,0 +1,15 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'uicolor', 'eo',
+{
+ uicolor :
+ {
+ title : 'UI Kolorselektilo',
+ preview : 'Vidigi la aspekton',
+ config : 'Gluu tiun signoĉenon en vian dosieron config.js',
+ predefined : 'Antaŭdifinita koloraro'
+ }
+});
diff --git a/_source/plugins/uicolor/lang/et.js b/_source/plugins/uicolor/lang/et.js new file mode 100644 index 0000000..46dd929 --- /dev/null +++ b/_source/plugins/uicolor/lang/et.js @@ -0,0 +1,15 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'uicolor', 'et',
+{
+ uicolor :
+ {
+ title : 'Värvivalija kasutajaliides',
+ preview : 'Automaatne eelvaade',
+ config : 'Aseta see sõne oma config.js faili.',
+ predefined : 'Eelmääratud värvikomplektid'
+ }
+});
diff --git a/_source/plugins/uicolor/lang/fa.js b/_source/plugins/uicolor/lang/fa.js new file mode 100644 index 0000000..223fc2c --- /dev/null +++ b/_source/plugins/uicolor/lang/fa.js @@ -0,0 +1,15 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'uicolor', 'fa',
+{
+ uicolor :
+ {
+ title : 'انتخاب رنگ UI',
+ preview : 'پیشنمایش زنده',
+ config : 'این رشته را در فایل config.js خود بچسبانید.',
+ predefined : 'مجموعه رنگ از پیش تعریف شده'
+ }
+});
diff --git a/_source/plugins/uicolor/lang/fi.js b/_source/plugins/uicolor/lang/fi.js new file mode 100644 index 0000000..5e781fc --- /dev/null +++ b/_source/plugins/uicolor/lang/fi.js @@ -0,0 +1,15 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'uicolor', 'fi',
+{
+ uicolor :
+ {
+ title : 'Käyttöliittymän värivalitsin',
+ preview : 'Esikatsele',
+ config : 'Liitä tämä merkkijono config.js tiedostoosi',
+ predefined : 'Esimääritellyt värijoukot'
+ }
+});
diff --git a/_source/plugins/uicolor/lang/fr.js b/_source/plugins/uicolor/lang/fr.js new file mode 100644 index 0000000..fb0abf9 --- /dev/null +++ b/_source/plugins/uicolor/lang/fr.js @@ -0,0 +1,15 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'uicolor', 'fr',
+{
+ uicolor :
+ {
+ title : 'UI Sélecteur de couleur',
+ preview : 'Aperçu',
+ config : 'Collez cette chaîne de caractères dans votre fichier config.js',
+ predefined : 'Palettes de couleurs prédéfinies'
+ }
+});
diff --git a/_source/plugins/uicolor/lang/he.js b/_source/plugins/uicolor/lang/he.js new file mode 100644 index 0000000..dfb3569 --- /dev/null +++ b/_source/plugins/uicolor/lang/he.js @@ -0,0 +1,15 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'uicolor', 'he',
+{
+ uicolor :
+ {
+ title : 'בחירת צבע ממשק משתמש',
+ preview : 'תצוגה מקדימה',
+ config : 'הדבק את הטקסט הבא לתוך הקובץ config.js',
+ predefined : 'קבוצות צבעים מוגדרות מראש'
+ }
+});
diff --git a/_source/plugins/uicolor/lang/hr.js b/_source/plugins/uicolor/lang/hr.js new file mode 100644 index 0000000..0125b4d --- /dev/null +++ b/_source/plugins/uicolor/lang/hr.js @@ -0,0 +1,15 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'uicolor', 'hr',
+{
+ uicolor :
+ {
+ title : 'UI odabir boja',
+ preview : 'Pregled uživo',
+ config : 'Zalijepite ovaj tekst u Vašu config.js datoteku.',
+ predefined : 'Već postavljeni setovi boja'
+ }
+});
diff --git a/_source/plugins/uicolor/lang/it.js b/_source/plugins/uicolor/lang/it.js new file mode 100644 index 0000000..48c5892 --- /dev/null +++ b/_source/plugins/uicolor/lang/it.js @@ -0,0 +1,15 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'uicolor', 'it',
+{
+ uicolor :
+ {
+ title : 'Selettore Colore UI',
+ preview : 'Anteprima Live',
+ config : 'Incolla questa stringa nel tuo file config.js',
+ predefined : 'Set di colori predefiniti'
+ }
+});
diff --git a/_source/plugins/uicolor/lang/mk.js b/_source/plugins/uicolor/lang/mk.js new file mode 100644 index 0000000..61c4715 --- /dev/null +++ b/_source/plugins/uicolor/lang/mk.js @@ -0,0 +1,15 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'uicolor', 'mk',
+{
+ uicolor :
+ {
+ title : 'Палета со бои',
+ preview : 'Преглед',
+ config : 'Залепи го овој текст во config.js датотеката',
+ predefined : 'Предефинирани множества на бои'
+ }
+});
diff --git a/_source/plugins/uicolor/lang/nb.js b/_source/plugins/uicolor/lang/nb.js new file mode 100644 index 0000000..d0d675a --- /dev/null +++ b/_source/plugins/uicolor/lang/nb.js @@ -0,0 +1,15 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'uicolor', 'nb',
+{
+ uicolor :
+ {
+ title : 'Fargevelger for brukergrensesnitt',
+ preview : 'Forhåndsvisning i sanntid',
+ config : 'Lim inn følgende tekst i din config.js-fil',
+ predefined : 'Forhåndsdefinerte fargesett'
+ }
+});
diff --git a/_source/plugins/uicolor/lang/nl.js b/_source/plugins/uicolor/lang/nl.js new file mode 100644 index 0000000..27a1ffb --- /dev/null +++ b/_source/plugins/uicolor/lang/nl.js @@ -0,0 +1,15 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'uicolor', 'nl',
+{
+ uicolor :
+ {
+ title : 'UI Kleurenkiezer',
+ preview : 'Live voorbeeld',
+ config : 'Plak deze tekst in jouw config.js bestand',
+ predefined : 'Voorgedefinieerde kleurensets'
+ }
+});
diff --git a/_source/plugins/uicolor/lang/no.js b/_source/plugins/uicolor/lang/no.js new file mode 100644 index 0000000..990bc38 --- /dev/null +++ b/_source/plugins/uicolor/lang/no.js @@ -0,0 +1,15 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'uicolor', 'no',
+{
+ uicolor :
+ {
+ title : 'Fargevelger for brukergrensesnitt',
+ preview : 'Forhåndsvisning i sanntid',
+ config : 'Lim inn følgende tekst i din config.js-fil',
+ predefined : 'Forhåndsdefinerte fargesett'
+ }
+});
diff --git a/_source/plugins/uicolor/lang/pl.js b/_source/plugins/uicolor/lang/pl.js new file mode 100644 index 0000000..6686b0a --- /dev/null +++ b/_source/plugins/uicolor/lang/pl.js @@ -0,0 +1,15 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'uicolor', 'pl',
+{
+ uicolor :
+ {
+ title : 'Wybór koloru interfejsu',
+ preview : 'Podgląd na żywo',
+ config : 'Wklej poniższy łańcuch znaków do pliku config.js:',
+ predefined : 'Predefiniowane zestawy kolorów'
+ }
+});
diff --git a/_source/plugins/uicolor/lang/tr.js b/_source/plugins/uicolor/lang/tr.js new file mode 100644 index 0000000..124fd3d --- /dev/null +++ b/_source/plugins/uicolor/lang/tr.js @@ -0,0 +1,15 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'uicolor', 'tr',
+{
+ uicolor :
+ {
+ title : 'UI Renk Seçicisi',
+ preview : 'Canlı önizleme',
+ config : 'Bu dizeyi config.js dosyasının içine yapıştırın',
+ predefined : 'Önceden tanımlanmış renk kümeleri'
+ }
+});
diff --git a/_source/plugins/uicolor/lang/ug.js b/_source/plugins/uicolor/lang/ug.js new file mode 100644 index 0000000..3425557 --- /dev/null +++ b/_source/plugins/uicolor/lang/ug.js @@ -0,0 +1,15 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'uicolor', 'ug',
+{
+ uicolor :
+ {
+ title : 'ئىشلەتكۈچى ئارايۈزى رەڭ تاللىغۇچ',
+ preview : 'شۇئان ئالدىن كۆزىتىش',
+ config : 'بۇ ھەرپ تىزىقىنى config.js ھۆججەتكە چاپلايدۇ',
+ predefined : 'ئالدىن بەلگىلەنگەن رەڭلەر'
+ }
+});
diff --git a/_source/plugins/uicolor/lang/uk.js b/_source/plugins/uicolor/lang/uk.js new file mode 100644 index 0000000..be16ea8 --- /dev/null +++ b/_source/plugins/uicolor/lang/uk.js @@ -0,0 +1,15 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'uicolor', 'uk',
+{
+ uicolor :
+ {
+ title : 'Color Picker Інтерфейс',
+ preview : 'Перегляд наживо',
+ config : 'Вставте цей рядок у файл config.js',
+ predefined : 'Стандартний набір кольорів'
+ }
+});
diff --git a/_source/plugins/uicolor/lang/vi.js b/_source/plugins/uicolor/lang/vi.js new file mode 100644 index 0000000..b4e9190 --- /dev/null +++ b/_source/plugins/uicolor/lang/vi.js @@ -0,0 +1,15 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'uicolor', 'vi',
+{
+ uicolor :
+ {
+ title : 'Giao diện người dùng Color Picker',
+ preview : 'Xem trước trực tiếp',
+ config : 'Dán chuỗi này vào tập tin config.js của bạn',
+ predefined : 'Tập màu định nghĩa sẵn'
+ }
+});
diff --git a/_source/plugins/uicolor/lang/zh-cn.js b/_source/plugins/uicolor/lang/zh-cn.js new file mode 100644 index 0000000..0365ebb --- /dev/null +++ b/_source/plugins/uicolor/lang/zh-cn.js @@ -0,0 +1,15 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.plugins.setLang( 'uicolor', 'zh-cn',
+{
+ uicolor :
+ {
+ title : '用户界面颜色选择器',
+ preview : '即时预览',
+ config : '粘贴此字符串到你的 config.js 文件',
+ predefined : '预定义颜色集'
+ }
+});
diff --git a/_source/plugins/uicolor/plugin.js b/_source/plugins/uicolor/plugin.js index 007f59f..2a5aba7 100644 --- a/_source/plugins/uicolor/plugin.js +++ b/_source/plugins/uicolor/plugin.js @@ -1,12 +1,12 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.plugins.add( 'uicolor',
{
requires : [ 'dialog' ],
- lang : [ 'en' ],
+ lang : [ 'bg', 'cs', 'cy', 'da', 'de', 'el', 'en', 'eo', 'et', 'fa', 'fi', 'fr', 'he', 'hr', 'it', 'mk', 'nb', 'nl', 'no', 'pl', 'tr', 'ug', 'uk', 'vi', 'zh-cn' ],
init : function( editor )
{
diff --git a/_source/plugins/uicolor/yui/assets/yui.css b/_source/plugins/uicolor/yui/assets/yui.css index 1bb7c75..e7dbd79 100644 --- a/_source/plugins/uicolor/yui/assets/yui.css +++ b/_source/plugins/uicolor/yui/assets/yui.css @@ -1,15 +1,15 @@ -/* -Copyright (c) 2009, Yahoo! Inc. All rights reserved. -Code licensed under the BSD License: -http://developer.yahoo.net/yui/license.txt -version: 2.7.0 -*/ -.yui-h-slider,.yui-v-slider{position:relative;}.yui-h-slider .yui-slider-thumb,.yui-v-slider .yui-slider-thumb{position:absolute;cursor:default;}.yui-skin-sam .yui-h-slider{background:url(bg-h.gif) no-repeat 5px 0;height:28px;width:228px;}.yui-skin-sam .yui-h-slider .yui-slider-thumb{top:4px;}.yui-skin-sam .yui-v-slider{background:url(bg-v.gif) no-repeat 12px 0;height:228px;width:48px;} - -/* -Copyright (c) 2009, Yahoo! Inc. All rights reserved. -Code licensed under the BSD License: -http://developer.yahoo.net/yui/license.txt -version: 2.7.0 -*/ -.cke_uicolor_picker .yui-picker-panel{background:#e3e3e3;border-color:#888;}.cke_uicolor_picker .yui-picker-panel .hd{background-color:#ccc;font-size:100%;line-height:100%;border:1px solid #e3e3e3;font-weight:bold;overflow:hidden;padding:6px;color:#000;}.cke_uicolor_picker .yui-picker-panel .bd{background:#e8e8e8;margin:1px;height:200px;}.cke_uicolor_picker .yui-picker-panel .ft{background:#e8e8e8;margin:1px;padding:1px;}.cke_uicolor_picker .yui-picker{position:relative;}.cke_uicolor_picker .yui-picker-hue-thumb{cursor:default;width:18px;height:18px;top:-8px;left:-2px;z-index:9;position:absolute;}.cke_uicolor_picker .yui-picker-hue-bg{-moz-outline:none;outline:0 none;position:absolute;left:200px;height:183px;width:14px;background:url(hue_bg.png) no-repeat;top:4px;}.cke_uicolor_picker .yui-picker-bg{-moz-outline:none;outline:0 none;position:absolute;top:4px;left:4px;height:182px;width:182px;background-color:#F00;background-image:url(picker_mask.png);}*html .cke_uicolor_picker .yui-picker-bg{background-image:none;filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='picker_mask.png',sizingMethod='scale');}.cke_uicolor_picker .yui-picker-mask{position:absolute;z-index:1;top:0;left:0;}.cke_uicolor_picker .yui-picker-thumb{cursor:default;width:11px;height:11px;z-index:9;position:absolute;top:-4px;left:-4px;}.cke_uicolor_picker .yui-picker-swatch{position:absolute;left:240px;top:4px;height:60px;width:55px;border:1px solid #888;}.cke_uicolor_picker .yui-picker-websafe-swatch{position:absolute;left:304px;top:4px;height:24px;width:24px;border:1px solid #888;}.cke_uicolor_picker .yui-picker-controls{position:absolute;top:72px;left:226px;font:1em monospace;}.cke_uicolor_picker .yui-picker-controls .hd{background:transparent;border-width:0!important;}.cke_uicolor_picker .yui-picker-controls .bd{height:100px;border-width:0!important;}.cke_uicolor_picker .yui-picker-controls ul{float:left;padding:0 2px 0 0;margin:0;}.cke_uicolor_picker .yui-picker-controls li{padding:2px;list-style:none;margin:0;}.cke_uicolor_picker .yui-picker-controls input{font-size:.85em;width:2.4em;}.cke_uicolor_picker .yui-picker-hex-controls{clear:both;padding:2px;}.cke_uicolor_picker .yui-picker-hex-controls input{width:4.6em;}.cke_uicolor_picker .yui-picker-controls a{font:1em arial,helvetica,clean,sans-serif;display:block;*display:inline-block;padding:0;color:#000;} +/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.7.0
+*/
+.yui-h-slider,.yui-v-slider{position:relative;}.yui-h-slider .yui-slider-thumb,.yui-v-slider .yui-slider-thumb{position:absolute;cursor:default;}.yui-skin-sam .yui-h-slider{background:url(bg-h.gif) no-repeat 5px 0;height:28px;width:228px;}.yui-skin-sam .yui-h-slider .yui-slider-thumb{top:4px;}.yui-skin-sam .yui-v-slider{background:url(bg-v.gif) no-repeat 12px 0;height:228px;width:48px;}
+
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.7.0
+*/
+.cke_uicolor_picker .yui-picker-panel{background:#e3e3e3;border-color:#888;}.cke_uicolor_picker .yui-picker-panel .hd{background-color:#ccc;font-size:100%;line-height:100%;border:1px solid #e3e3e3;font-weight:bold;overflow:hidden;padding:6px;color:#000;}.cke_uicolor_picker .yui-picker-panel .bd{background:#e8e8e8;margin:1px;height:200px;}.cke_uicolor_picker .yui-picker-panel .ft{background:#e8e8e8;margin:1px;padding:1px;}.cke_uicolor_picker .yui-picker{position:relative;}.cke_uicolor_picker .yui-picker-hue-thumb{cursor:default;width:18px;height:18px;top:-8px;left:-2px;z-index:9;position:absolute;}.cke_uicolor_picker .yui-picker-hue-bg{-moz-outline:none;outline:0 none;position:absolute;left:200px;height:183px;width:14px;background:url(hue_bg.png) no-repeat;top:4px;}.cke_uicolor_picker .yui-picker-bg{-moz-outline:none;outline:0 none;position:absolute;top:4px;left:4px;height:182px;width:182px;background-color:#F00;background-image:url(picker_mask.png);}*html .cke_uicolor_picker .yui-picker-bg{background-image:none;filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='picker_mask.png',sizingMethod='scale');}.cke_uicolor_picker .yui-picker-mask{position:absolute;z-index:1;top:0;left:0;}.cke_uicolor_picker .yui-picker-thumb{cursor:default;width:11px;height:11px;z-index:9;position:absolute;top:-4px;left:-4px;}.cke_uicolor_picker .yui-picker-swatch{position:absolute;left:240px;top:4px;height:60px;width:55px;border:1px solid #888;}.cke_uicolor_picker .yui-picker-websafe-swatch{position:absolute;left:304px;top:4px;height:24px;width:24px;border:1px solid #888;}.cke_uicolor_picker .yui-picker-controls{position:absolute;top:72px;left:226px;font:1em monospace;}.cke_uicolor_picker .yui-picker-controls .hd{background:transparent;border-width:0!important;}.cke_uicolor_picker .yui-picker-controls .bd{height:100px;border-width:0!important;}.cke_uicolor_picker .yui-picker-controls ul{float:left;padding:0 2px 0 0;margin:0;}.cke_uicolor_picker .yui-picker-controls li{padding:2px;list-style:none;margin:0;}.cke_uicolor_picker .yui-picker-controls input{font-size:.85em;width:2.4em;}.cke_uicolor_picker .yui-picker-hex-controls{clear:both;padding:2px;}.cke_uicolor_picker .yui-picker-hex-controls input{width:4.6em;}.cke_uicolor_picker .yui-picker-controls a{font:1em arial,helvetica,clean,sans-serif;display:block;*display:inline-block;padding:0;color:#000;}
diff --git a/_source/plugins/undo/plugin.js b/_source/plugins/undo/plugin.js index 635d6e4..f4ad1a3 100644 --- a/_source/plugins/undo/plugin.js +++ b/_source/plugins/undo/plugin.js @@ -1,555 +1,593 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -/** - * @fileOverview Undo/Redo system for saving shapshot for document modification - * and other recordable changes. - */ - -(function() -{ - CKEDITOR.plugins.add( 'undo', - { - requires : [ 'selection', 'wysiwygarea' ], - - init : function( editor ) - { - var undoManager = new UndoManager( editor ); - - var undoCommand = editor.addCommand( 'undo', - { - exec : function() - { - if ( undoManager.undo() ) - { - editor.selectionChange(); - this.fire( 'afterUndo' ); - } - }, - state : CKEDITOR.TRISTATE_DISABLED, - canUndo : false - }); - - var redoCommand = editor.addCommand( 'redo', - { - exec : function() - { - if ( undoManager.redo() ) - { - editor.selectionChange(); - this.fire( 'afterRedo' ); - } - }, - state : CKEDITOR.TRISTATE_DISABLED, - canUndo : false - }); - - undoManager.onChange = function() - { - undoCommand.setState( undoManager.undoable() ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED ); - redoCommand.setState( undoManager.redoable() ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED ); - }; - - function recordCommand( event ) - { - // If the command hasn't been marked to not support undo. - if ( undoManager.enabled && event.data.command.canUndo !== false ) - undoManager.save(); - } - - // We'll save snapshots before and after executing a command. - editor.on( 'beforeCommandExec', recordCommand ); - editor.on( 'afterCommandExec', recordCommand ); - - // Save snapshots before doing custom changes. - editor.on( 'saveSnapshot', function() - { - undoManager.save(); - }); - - // Registering keydown on every document recreation.(#3844) - editor.on( 'contentDom', function() - { - editor.document.on( 'keydown', function( event ) - { - // Do not capture CTRL hotkeys. - if ( !event.data.$.ctrlKey && !event.data.$.metaKey ) - undoManager.type( event ); - }); - }); - - // Always save an undo snapshot - the previous mode might have - // changed editor contents. - editor.on( 'beforeModeUnload', function() - { - editor.mode == 'wysiwyg' && undoManager.save( true ); - }); - - // Make the undo manager available only in wysiwyg mode. - editor.on( 'mode', function() - { - undoManager.enabled = editor.mode == 'wysiwyg'; - undoManager.onChange(); - }); - - editor.ui.addButton( 'Undo', - { - label : editor.lang.undo, - command : 'undo' - }); - - editor.ui.addButton( 'Redo', - { - label : editor.lang.redo, - command : 'redo' - }); - - editor.resetUndo = function() - { - // Reset the undo stack. - undoManager.reset(); - - // Create the first image. - editor.fire( 'saveSnapshot' ); - }; - - /** - * Update the undo stacks with any subsequent DOM changes after this call. - * @name CKEDITOR.editor#updateUndo - * @example - * function() - * { - * editor.fire( 'updateSnapshot' ); - * ... - * // Ask to include subsequent (in this call stack) DOM changes to be - * // considered as part of the first snapshot. - * editor.fire( 'updateSnapshot' ); - * editor.document.body.append(...); - * ... - * } - */ - editor.on( 'updateSnapshot', function() - { - if ( undoManager.currentImage && new Image( editor ).equals( undoManager.currentImage ) ) - setTimeout( function () { undoManager.update(); }, 0 ); - }); - } - }); - - CKEDITOR.plugins.undo = {}; - - /** - * Undo snapshot which represents the current document status. - * @name CKEDITOR.plugins.undo.Image - * @param editor The editor instance on which the image is created. - */ - var Image = CKEDITOR.plugins.undo.Image = function( editor ) - { - this.editor = editor; - var contents = editor.getSnapshot(), - selection = contents && editor.getSelection(); - - // In IE, we need to remove the expando attributes. - CKEDITOR.env.ie && contents && ( contents = contents.replace( /\s+_cke_expando=".*?"/g, '' ) ); - - this.contents = contents; - this.bookmarks = selection && selection.createBookmarks2( true ); - }; - - // Attributes that browser may changing them when setting via innerHTML. - var protectedAttrs = /\b(?:href|src|name)="[^"]*?"/gi; - - Image.prototype = - { - equals : function( otherImage, contentOnly ) - { - - var thisContents = this.contents, - otherContents = otherImage.contents; - - // For IE6/7 : Comparing only the protected attribute values but not the original ones.(#4522) - if ( CKEDITOR.env.ie && ( CKEDITOR.env.ie7Compat || CKEDITOR.env.ie6Compat ) ) - { - thisContents = thisContents.replace( protectedAttrs, '' ); - otherContents = otherContents.replace( protectedAttrs, '' ); - } - - if ( thisContents != otherContents ) - return false; - - if ( contentOnly ) - return true; - - var bookmarksA = this.bookmarks, - bookmarksB = otherImage.bookmarks; - - if ( bookmarksA || bookmarksB ) - { - if ( !bookmarksA || !bookmarksB || bookmarksA.length != bookmarksB.length ) - return false; - - for ( var i = 0 ; i < bookmarksA.length ; i++ ) - { - var bookmarkA = bookmarksA[ i ], - bookmarkB = bookmarksB[ i ]; - - if ( - bookmarkA.startOffset != bookmarkB.startOffset || - bookmarkA.endOffset != bookmarkB.endOffset || - !CKEDITOR.tools.arrayCompare( bookmarkA.start, bookmarkB.start ) || - !CKEDITOR.tools.arrayCompare( bookmarkA.end, bookmarkB.end ) ) - { - return false; - } - } - } - - return true; - } - }; - - /** - * @constructor Main logic for Redo/Undo feature. - */ - function UndoManager( editor ) - { - this.editor = editor; - - // Reset the undo stack. - this.reset(); - } - - - var editingKeyCodes = { /*Backspace*/ 8:1, /*Delete*/ 46:1 }, - modifierKeyCodes = { /*Shift*/ 16:1, /*Ctrl*/ 17:1, /*Alt*/ 18:1 }, - navigationKeyCodes = { 37:1, 38:1, 39:1, 40:1 }; // Arrows: L, T, R, B - - UndoManager.prototype = - { - /** - * Process undo system regard keystrikes. - * @param {CKEDITOR.dom.event} event - */ - type : function( event ) - { - var keystroke = event && event.data.getKey(), - isModifierKey = keystroke in modifierKeyCodes, - isEditingKey = keystroke in editingKeyCodes, - wasEditingKey = this.lastKeystroke in editingKeyCodes, - sameAsLastEditingKey = isEditingKey && keystroke == this.lastKeystroke, - // Keystrokes which navigation through contents. - isReset = keystroke in navigationKeyCodes, - wasReset = this.lastKeystroke in navigationKeyCodes, - - // Keystrokes which just introduce new contents. - isContent = ( !isEditingKey && !isReset ), - - // Create undo snap for every different modifier key. - modifierSnapshot = ( isEditingKey && !sameAsLastEditingKey ), - // Create undo snap on the following cases: - // 1. Just start to type . - // 2. Typing some content after a modifier. - // 3. Typing some content after make a visible selection. - startedTyping = !( isModifierKey || this.typing ) - || ( isContent && ( wasEditingKey || wasReset ) ); - - if ( startedTyping || modifierSnapshot ) - { - var beforeTypeImage = new Image( this.editor ); - - // Use setTimeout, so we give the necessary time to the - // browser to insert the character into the DOM. - CKEDITOR.tools.setTimeout( function() - { - var currentSnapshot = this.editor.getSnapshot(); - - // In IE, we need to remove the expando attributes. - if ( CKEDITOR.env.ie ) - currentSnapshot = currentSnapshot.replace( /\s+_cke_expando=".*?"/g, '' ); - - if ( beforeTypeImage.contents != currentSnapshot ) - { - // It's safe to now indicate typing state. - this.typing = true; - - // This's a special save, with specified snapshot - // and without auto 'fireChange'. - if ( !this.save( false, beforeTypeImage, false ) ) - // Drop future snapshots. - this.snapshots.splice( this.index + 1, this.snapshots.length - this.index - 1 ); - - this.hasUndo = true; - this.hasRedo = false; - - this.typesCount = 1; - this.modifiersCount = 1; - - this.onChange(); - } - }, - 0, this - ); - } - - this.lastKeystroke = keystroke; - - // Create undo snap after typed too much (over 25 times). - if ( isEditingKey ) - { - this.typesCount = 0; - this.modifiersCount++; - - if ( this.modifiersCount > 25 ) - { - this.save( false, null, false ); - this.modifiersCount = 1; - } - } - else if ( !isReset ) - { - this.modifiersCount = 0; - this.typesCount++; - - if ( this.typesCount > 25 ) - { - this.save( false, null, false ); - this.typesCount = 1; - } - } - - }, - - reset : function() // Reset the undo stack. - { - /** - * Remember last pressed key. - */ - this.lastKeystroke = 0; - - /** - * Stack for all the undo and redo snapshots, they're always created/removed - * in consistency. - */ - this.snapshots = []; - - /** - * Current snapshot history index. - */ - this.index = -1; - - this.limit = this.editor.config.undoStackSize; - - this.currentImage = null; - - this.hasUndo = false; - this.hasRedo = false; - - this.resetType(); - }, - - /** - * Reset all states about typing. - * @see UndoManager.type - */ - resetType : function() - { - this.typing = false; - delete this.lastKeystroke; - this.typesCount = 0; - this.modifiersCount = 0; - }, - fireChange : function() - { - this.hasUndo = !!this.getNextImage( true ); - this.hasRedo = !!this.getNextImage( false ); - // Reset typing - this.resetType(); - this.onChange(); - }, - - /** - * Save a snapshot of document image for later retrieve. - */ - save : function( onContentOnly, image, autoFireChange ) - { - var snapshots = this.snapshots; - - // Get a content image. - if ( !image ) - image = new Image( this.editor ); - - // Do nothing if it was not possible to retrieve an image. - if ( image.contents === false ) - return false; - - // Check if this is a duplicate. In such case, do nothing. - if ( this.currentImage && image.equals( this.currentImage, onContentOnly ) ) - return false; - - // Drop future snapshots. - snapshots.splice( this.index + 1, snapshots.length - this.index - 1 ); - - // If we have reached the limit, remove the oldest one. - if ( snapshots.length == this.limit ) - snapshots.shift(); - - // Add the new image, updating the current index. - this.index = snapshots.push( image ) - 1; - - this.currentImage = image; - - if ( autoFireChange !== false ) - this.fireChange(); - return true; - }, - - restoreImage : function( image ) - { - this.editor.loadSnapshot( image.contents ); - - if ( image.bookmarks ) - this.editor.getSelection().selectBookmarks( image.bookmarks ); - else if ( CKEDITOR.env.ie ) - { - // IE BUG: If I don't set the selection to *somewhere* after setting - // document contents, then IE would create an empty paragraph at the bottom - // the next time the document is modified. - var $range = this.editor.document.getBody().$.createTextRange(); - $range.collapse( true ); - $range.select(); - } - - this.index = image.index; - - // Update current image with the actual editor - // content, since actualy content may differ from - // the original snapshot due to dom change. (#4622) - this.update(); - this.fireChange(); - }, - - // Get the closest available image. - getNextImage : function( isUndo ) - { - var snapshots = this.snapshots, - currentImage = this.currentImage, - image, i; - - if ( currentImage ) - { - if ( isUndo ) - { - for ( i = this.index - 1 ; i >= 0 ; i-- ) - { - image = snapshots[ i ]; - if ( !currentImage.equals( image, true ) ) - { - image.index = i; - return image; - } - } - } - else - { - for ( i = this.index + 1 ; i < snapshots.length ; i++ ) - { - image = snapshots[ i ]; - if ( !currentImage.equals( image, true ) ) - { - image.index = i; - return image; - } - } - } - } - - return null; - }, - - /** - * Check the current redo state. - * @return {Boolean} Whether the document has previous state to - * retrieve. - */ - redoable : function() - { - return this.enabled && this.hasRedo; - }, - - /** - * Check the current undo state. - * @return {Boolean} Whether the document has future state to restore. - */ - undoable : function() - { - return this.enabled && this.hasUndo; - }, - - /** - * Perform undo on current index. - */ - undo : function() - { - if ( this.undoable() ) - { - this.save( true ); - - var image = this.getNextImage( true ); - if ( image ) - return this.restoreImage( image ), true; - } - - return false; - }, - - /** - * Perform redo on current index. - */ - redo : function() - { - if ( this.redoable() ) - { - // Try to save. If no changes have been made, the redo stack - // will not change, so it will still be redoable. - this.save( true ); - - // If instead we had changes, we can't redo anymore. - if ( this.redoable() ) - { - var image = this.getNextImage( false ); - if ( image ) - return this.restoreImage( image ), true; - } - } - - return false; - }, - - /** - * Update the last snapshot of the undo stack with the current editor content. - */ - update : function() - { - this.snapshots.splice( this.index, 1, ( this.currentImage = new Image( this.editor ) ) ); - } - }; -})(); - -/** - * The number of undo steps to be saved. The higher this setting value the more - * memory is used for it. - * @type Number - * @default 20 - * @example - * config.undoStackSize = 50; - */ -CKEDITOR.config.undoStackSize = 20; - -/** - * Fired when the editor is about to save an undo snapshot. This event can be - * fired by plugins and customizations to make the editor saving undo snapshots. - * @name CKEDITOR.editor#saveSnapshot - * @event - */ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Undo/Redo system for saving shapshot for document modification
+ * and other recordable changes.
+ */
+
+(function()
+{
+ CKEDITOR.plugins.add( 'undo',
+ {
+ requires : [ 'selection', 'wysiwygarea' ],
+
+ init : function( editor )
+ {
+ var undoManager = new UndoManager( editor );
+
+ var undoCommand = editor.addCommand( 'undo',
+ {
+ exec : function()
+ {
+ if ( undoManager.undo() )
+ {
+ editor.selectionChange();
+ this.fire( 'afterUndo' );
+ }
+ },
+ state : CKEDITOR.TRISTATE_DISABLED,
+ canUndo : false
+ });
+
+ var redoCommand = editor.addCommand( 'redo',
+ {
+ exec : function()
+ {
+ if ( undoManager.redo() )
+ {
+ editor.selectionChange();
+ this.fire( 'afterRedo' );
+ }
+ },
+ state : CKEDITOR.TRISTATE_DISABLED,
+ canUndo : false
+ });
+
+ undoManager.onChange = function()
+ {
+ undoCommand.setState( undoManager.undoable() ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED );
+ redoCommand.setState( undoManager.redoable() ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED );
+ };
+
+ function recordCommand( event )
+ {
+ // If the command hasn't been marked to not support undo.
+ if ( undoManager.enabled && event.data.command.canUndo !== false )
+ undoManager.save();
+ }
+
+ // We'll save snapshots before and after executing a command.
+ editor.on( 'beforeCommandExec', recordCommand );
+ editor.on( 'afterCommandExec', recordCommand );
+
+ // Save snapshots before doing custom changes.
+ editor.on( 'saveSnapshot', function( evt )
+ {
+ undoManager.save( evt.data && evt.data.contentOnly );
+ });
+
+ // Registering keydown on every document recreation.(#3844)
+ editor.on( 'contentDom', function()
+ {
+ editor.document.on( 'keydown', function( event )
+ {
+ // Do not capture CTRL hotkeys.
+ if ( !event.data.$.ctrlKey && !event.data.$.metaKey )
+ undoManager.type( event );
+ });
+ });
+
+ // Always save an undo snapshot - the previous mode might have
+ // changed editor contents.
+ editor.on( 'beforeModeUnload', function()
+ {
+ editor.mode == 'wysiwyg' && undoManager.save( true );
+ });
+
+ // Make the undo manager available only in wysiwyg mode.
+ editor.on( 'mode', function()
+ {
+ undoManager.enabled = editor.readOnly ? false : editor.mode == 'wysiwyg';
+ undoManager.onChange();
+ });
+
+ editor.ui.addButton( 'Undo',
+ {
+ label : editor.lang.undo,
+ command : 'undo'
+ });
+
+ editor.ui.addButton( 'Redo',
+ {
+ label : editor.lang.redo,
+ command : 'redo'
+ });
+
+ editor.resetUndo = function()
+ {
+ // Reset the undo stack.
+ undoManager.reset();
+
+ // Create the first image.
+ editor.fire( 'saveSnapshot' );
+ };
+
+ /**
+ * Amend the top of undo stack (last undo image) with the current DOM changes.
+ * @name CKEDITOR.editor#updateUndo
+ * @example
+ * function()
+ * {
+ * editor.fire( 'saveSnapshot' );
+ * editor.document.body.append(...);
+ * // Make new changes following the last undo snapshot part of it.
+ * editor.fire( 'updateSnapshot' );
+ * ...
+ * }
+ */
+ editor.on( 'updateSnapshot', function()
+ {
+ if ( undoManager.currentImage )
+ undoManager.update();
+ });
+ }
+ });
+
+ CKEDITOR.plugins.undo = {};
+
+ /**
+ * Undo snapshot which represents the current document status.
+ * @name CKEDITOR.plugins.undo.Image
+ * @param editor The editor instance on which the image is created.
+ */
+ var Image = CKEDITOR.plugins.undo.Image = function( editor )
+ {
+ this.editor = editor;
+
+ editor.fire( 'beforeUndoImage' );
+
+ var contents = editor.getSnapshot(),
+ selection = contents && editor.getSelection();
+
+ // In IE, we need to remove the expando attributes.
+ CKEDITOR.env.ie && contents && ( contents = contents.replace( /\s+data-cke-expando=".*?"/g, '' ) );
+
+ this.contents = contents;
+ this.bookmarks = selection && selection.createBookmarks2( true );
+
+ editor.fire( 'afterUndoImage' );
+ };
+
+ // Attributes that browser may changing them when setting via innerHTML.
+ var protectedAttrs = /\b(?:href|src|name)="[^"]*?"/gi;
+
+ Image.prototype =
+ {
+ equals : function( otherImage, contentOnly )
+ {
+
+ var thisContents = this.contents,
+ otherContents = otherImage.contents;
+
+ // For IE6/7 : Comparing only the protected attribute values but not the original ones.(#4522)
+ if ( CKEDITOR.env.ie && ( CKEDITOR.env.ie7Compat || CKEDITOR.env.ie6Compat ) )
+ {
+ thisContents = thisContents.replace( protectedAttrs, '' );
+ otherContents = otherContents.replace( protectedAttrs, '' );
+ }
+
+ if ( thisContents != otherContents )
+ return false;
+
+ if ( contentOnly )
+ return true;
+
+ var bookmarksA = this.bookmarks,
+ bookmarksB = otherImage.bookmarks;
+
+ if ( bookmarksA || bookmarksB )
+ {
+ if ( !bookmarksA || !bookmarksB || bookmarksA.length != bookmarksB.length )
+ return false;
+
+ for ( var i = 0 ; i < bookmarksA.length ; i++ )
+ {
+ var bookmarkA = bookmarksA[ i ],
+ bookmarkB = bookmarksB[ i ];
+
+ if (
+ bookmarkA.startOffset != bookmarkB.startOffset ||
+ bookmarkA.endOffset != bookmarkB.endOffset ||
+ !CKEDITOR.tools.arrayCompare( bookmarkA.start, bookmarkB.start ) ||
+ !CKEDITOR.tools.arrayCompare( bookmarkA.end, bookmarkB.end ) )
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+ };
+
+ /**
+ * @constructor Main logic for Redo/Undo feature.
+ */
+ function UndoManager( editor )
+ {
+ this.editor = editor;
+
+ // Reset the undo stack.
+ this.reset();
+ }
+
+
+ var editingKeyCodes = { /*Backspace*/ 8:1, /*Delete*/ 46:1 },
+ modifierKeyCodes = { /*Shift*/ 16:1, /*Ctrl*/ 17:1, /*Alt*/ 18:1 },
+ navigationKeyCodes = { 37:1, 38:1, 39:1, 40:1 }; // Arrows: L, T, R, B
+
+ UndoManager.prototype =
+ {
+ /**
+ * Process undo system regard keystrikes.
+ * @param {CKEDITOR.dom.event} event
+ */
+ type : function( event )
+ {
+ var keystroke = event && event.data.getKey(),
+ isModifierKey = keystroke in modifierKeyCodes,
+ isEditingKey = keystroke in editingKeyCodes,
+ wasEditingKey = this.lastKeystroke in editingKeyCodes,
+ sameAsLastEditingKey = isEditingKey && keystroke == this.lastKeystroke,
+ // Keystrokes which navigation through contents.
+ isReset = keystroke in navigationKeyCodes,
+ wasReset = this.lastKeystroke in navigationKeyCodes,
+
+ // Keystrokes which just introduce new contents.
+ isContent = ( !isEditingKey && !isReset ),
+
+ // Create undo snap for every different modifier key.
+ modifierSnapshot = ( isEditingKey && !sameAsLastEditingKey ),
+ // Create undo snap on the following cases:
+ // 1. Just start to type .
+ // 2. Typing some content after a modifier.
+ // 3. Typing some content after make a visible selection.
+ startedTyping = !( isModifierKey || this.typing )
+ || ( isContent && ( wasEditingKey || wasReset ) );
+
+ if ( startedTyping || modifierSnapshot )
+ {
+ var beforeTypeImage = new Image( this.editor ),
+ beforeTypeCount = this.snapshots.length;
+
+ // Use setTimeout, so we give the necessary time to the
+ // browser to insert the character into the DOM.
+ CKEDITOR.tools.setTimeout( function()
+ {
+ var currentSnapshot = this.editor.getSnapshot();
+
+ // In IE, we need to remove the expando attributes.
+ if ( CKEDITOR.env.ie )
+ currentSnapshot = currentSnapshot.replace( /\s+data-cke-expando=".*?"/g, '' );
+
+ // If changes have taken place, while not been captured yet (#8459),
+ // compensate the snapshot.
+ if ( beforeTypeImage.contents != currentSnapshot &&
+ beforeTypeCount == this.snapshots.length )
+ {
+ // It's safe to now indicate typing state.
+ this.typing = true;
+
+ // This's a special save, with specified snapshot
+ // and without auto 'fireChange'.
+ if ( !this.save( false, beforeTypeImage, false ) )
+ // Drop future snapshots.
+ this.snapshots.splice( this.index + 1, this.snapshots.length - this.index - 1 );
+
+ this.hasUndo = true;
+ this.hasRedo = false;
+
+ this.typesCount = 1;
+ this.modifiersCount = 1;
+
+ this.onChange();
+ }
+ },
+ 0, this
+ );
+ }
+
+ this.lastKeystroke = keystroke;
+
+ // Create undo snap after typed too much (over 25 times).
+ if ( isEditingKey )
+ {
+ this.typesCount = 0;
+ this.modifiersCount++;
+
+ if ( this.modifiersCount > 25 )
+ {
+ this.save( false, null, false );
+ this.modifiersCount = 1;
+ }
+ }
+ else if ( !isReset )
+ {
+ this.modifiersCount = 0;
+ this.typesCount++;
+
+ if ( this.typesCount > 25 )
+ {
+ this.save( false, null, false );
+ this.typesCount = 1;
+ }
+ }
+
+ },
+
+ reset : function() // Reset the undo stack.
+ {
+ /**
+ * Remember last pressed key.
+ */
+ this.lastKeystroke = 0;
+
+ /**
+ * Stack for all the undo and redo snapshots, they're always created/removed
+ * in consistency.
+ */
+ this.snapshots = [];
+
+ /**
+ * Current snapshot history index.
+ */
+ this.index = -1;
+
+ this.limit = this.editor.config.undoStackSize || 20;
+
+ this.currentImage = null;
+
+ this.hasUndo = false;
+ this.hasRedo = false;
+
+ this.resetType();
+ },
+
+ /**
+ * Reset all states about typing.
+ * @see UndoManager.type
+ */
+ resetType : function()
+ {
+ this.typing = false;
+ delete this.lastKeystroke;
+ this.typesCount = 0;
+ this.modifiersCount = 0;
+ },
+ fireChange : function()
+ {
+ this.hasUndo = !!this.getNextImage( true );
+ this.hasRedo = !!this.getNextImage( false );
+ // Reset typing
+ this.resetType();
+ this.onChange();
+ },
+
+ /**
+ * Save a snapshot of document image for later retrieve.
+ */
+ save : function( onContentOnly, image, autoFireChange )
+ {
+ var snapshots = this.snapshots;
+
+ // Get a content image.
+ if ( !image )
+ image = new Image( this.editor );
+
+ // Do nothing if it was not possible to retrieve an image.
+ if ( image.contents === false )
+ return false;
+
+ // Check if this is a duplicate. In such case, do nothing.
+ if ( this.currentImage && image.equals( this.currentImage, onContentOnly ) )
+ return false;
+
+ // Drop future snapshots.
+ snapshots.splice( this.index + 1, snapshots.length - this.index - 1 );
+
+ // If we have reached the limit, remove the oldest one.
+ if ( snapshots.length == this.limit )
+ snapshots.shift();
+
+ // Add the new image, updating the current index.
+ this.index = snapshots.push( image ) - 1;
+
+ this.currentImage = image;
+
+ if ( autoFireChange !== false )
+ this.fireChange();
+ return true;
+ },
+
+ restoreImage : function( image )
+ {
+ // Bring editor focused to restore selection.
+ var editor = this.editor,
+ sel;
+
+ if ( image.bookmarks )
+ {
+ editor.focus();
+ // Retrieve the selection beforehand. (#8324)
+ sel = editor.getSelection();
+ }
+
+ this.editor.loadSnapshot( image.contents );
+
+ if ( image.bookmarks )
+ sel.selectBookmarks( image.bookmarks );
+ else if ( CKEDITOR.env.ie )
+ {
+ // IE BUG: If I don't set the selection to *somewhere* after setting
+ // document contents, then IE would create an empty paragraph at the bottom
+ // the next time the document is modified.
+ var $range = this.editor.document.getBody().$.createTextRange();
+ $range.collapse( true );
+ $range.select();
+ }
+
+ this.index = image.index;
+
+ // Update current image with the actual editor
+ // content, since actualy content may differ from
+ // the original snapshot due to dom change. (#4622)
+ this.update();
+ this.fireChange();
+ },
+
+ // Get the closest available image.
+ getNextImage : function( isUndo )
+ {
+ var snapshots = this.snapshots,
+ currentImage = this.currentImage,
+ image, i;
+
+ if ( currentImage )
+ {
+ if ( isUndo )
+ {
+ for ( i = this.index - 1 ; i >= 0 ; i-- )
+ {
+ image = snapshots[ i ];
+ if ( !currentImage.equals( image, true ) )
+ {
+ image.index = i;
+ return image;
+ }
+ }
+ }
+ else
+ {
+ for ( i = this.index + 1 ; i < snapshots.length ; i++ )
+ {
+ image = snapshots[ i ];
+ if ( !currentImage.equals( image, true ) )
+ {
+ image.index = i;
+ return image;
+ }
+ }
+ }
+ }
+
+ return null;
+ },
+
+ /**
+ * Check the current redo state.
+ * @return {Boolean} Whether the document has previous state to
+ * retrieve.
+ */
+ redoable : function()
+ {
+ return this.enabled && this.hasRedo;
+ },
+
+ /**
+ * Check the current undo state.
+ * @return {Boolean} Whether the document has future state to restore.
+ */
+ undoable : function()
+ {
+ return this.enabled && this.hasUndo;
+ },
+
+ /**
+ * Perform undo on current index.
+ */
+ undo : function()
+ {
+ if ( this.undoable() )
+ {
+ this.save( true );
+
+ var image = this.getNextImage( true );
+ if ( image )
+ return this.restoreImage( image ), true;
+ }
+
+ return false;
+ },
+
+ /**
+ * Perform redo on current index.
+ */
+ redo : function()
+ {
+ if ( this.redoable() )
+ {
+ // Try to save. If no changes have been made, the redo stack
+ // will not change, so it will still be redoable.
+ this.save( true );
+
+ // If instead we had changes, we can't redo anymore.
+ if ( this.redoable() )
+ {
+ var image = this.getNextImage( false );
+ if ( image )
+ return this.restoreImage( image ), true;
+ }
+ }
+
+ return false;
+ },
+
+ /**
+ * Update the last snapshot of the undo stack with the current editor content.
+ */
+ update : function()
+ {
+ this.snapshots.splice( this.index, 1, ( this.currentImage = new Image( this.editor ) ) );
+ }
+ };
+})();
+
+/**
+ * The number of undo steps to be saved. The higher this setting value the more
+ * memory is used for it.
+ * @name CKEDITOR.config.undoStackSize
+ * @type Number
+ * @default 20
+ * @example
+ * config.undoStackSize = 50;
+ */
+
+/**
+ * Fired when the editor is about to save an undo snapshot. This event can be
+ * fired by plugins and customizations to make the editor saving undo snapshots.
+ * @name CKEDITOR.editor#saveSnapshot
+ * @event
+ */
+
+/**
+ * Fired before an undo image is to be taken. An undo image represents the
+ * editor state at some point. It's saved into an undo store, so the editor is
+ * able to recover the editor state on undo and redo operations.
+ * @name CKEDITOR.editor#beforeUndoImage
+ * @since 3.5.3
+ * @see CKEDITOR.editor#afterUndoImage
+ * @event
+ */
+
+/**
+ * Fired after an undo image is taken. An undo image represents the
+ * editor state at some point. It's saved into an undo store, so the editor is
+ * able to recover the editor state on undo and redo operations.
+ * @name CKEDITOR.editor#afterUndoImage
+ * @since 3.5.3
+ * @see CKEDITOR.editor#beforeUndoImage
+ * @event
+ */
diff --git a/_source/plugins/wsc/dialogs/ciframe.html b/_source/plugins/wsc/dialogs/ciframe.html index 0a96d82..292297d 100644 --- a/_source/plugins/wsc/dialogs/ciframe.html +++ b/_source/plugins/wsc/dialogs/ciframe.html @@ -1,6 +1,6 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<!--
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
-->
<html>
diff --git a/_source/plugins/wsc/dialogs/tmpFrameset.html b/_source/plugins/wsc/dialogs/tmpFrameset.html index 41abd6e..88e1afe 100644 --- a/_source/plugins/wsc/dialogs/tmpFrameset.html +++ b/_source/plugins/wsc/dialogs/tmpFrameset.html @@ -1,6 +1,6 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
<!--
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
-->
<html>
diff --git a/_source/plugins/wsc/dialogs/wsc.css b/_source/plugins/wsc/dialogs/wsc.css index 456cdd4..e163948 100644 --- a/_source/plugins/wsc/dialogs/wsc.css +++ b/_source/plugins/wsc/dialogs/wsc.css @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
@@ -70,7 +70,6 @@ body, td, input, select, textarea margin-top: 1px;
border-bottom: #d5d59d 1px solid;
cursor: pointer;
- cursor: hand;
}
.PopupTabSelected
diff --git a/_source/plugins/wsc/dialogs/wsc.js b/_source/plugins/wsc/dialogs/wsc.js index b084384..83b6dbe 100644 --- a/_source/plugins/wsc/dialogs/wsc.js +++ b/_source/plugins/wsc/dialogs/wsc.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
@@ -23,7 +23,7 @@ CKEDITOR.dialog.add( 'checkspell', function( editor ) ' style="display:none;color:red;font-size:16px;font-weight:bold;padding-top:160px;text-align:center;z-index:11;">' +
'</div><iframe' +
' src=""' +
- ' style="width:485px;background-color:#f1f1e3;height:380px"' +
+ ' style="width:100%;background-color:#f1f1e3;"' +
' frameborder="0"' +
' name="' + iframeId + '"' +
' id="' + iframeId + '"' +
@@ -31,7 +31,7 @@ CKEDITOR.dialog.add( 'checkspell', function( editor ) '</iframe>';
var wscCoreUrl = editor.config.wsc_customLoaderScript || ( protocol +
- '//loader.spellchecker.net/sproxy_fck/sproxy.php'
+ '//loader.webspellchecker.net/sproxy_fck/sproxy.php'
+ '?plugin=fck2'
+ '&customerid=' + editor.config.wsc_customerId
+ '&cmd=script&doc=wsc&schema=22'
@@ -129,6 +129,7 @@ CKEDITOR.dialog.add( 'checkspell', function( editor ) {
var contentArea = this.getContentElement( 'general', 'content' ).getElement();
contentArea.setHtml( pasteArea );
+ contentArea.getChild( 2 ).setStyle( 'height', this._.contentSize.height + 'px' );
if ( typeof( window.doSpell ) != 'function' )
{
@@ -166,11 +167,26 @@ CKEDITOR.dialog.add( 'checkspell', function( editor ) {
type : 'html',
id : 'content',
- style : 'width:485;height:380px',
- html : '<div></div>'
+ html : ''
}
]
}
]
};
});
+
+// Expand the spell-check frame when dialog resized. (#6829)
+CKEDITOR.dialog.on( 'resize', function( evt )
+{
+ var data = evt.data,
+ dialog = data.dialog;
+
+ if ( dialog._.name == 'checkspell' )
+ {
+ var content = dialog.getContentElement( 'general', 'content' ).getElement(),
+ iframe = content && content.getChild( 2 );
+
+ iframe && iframe.setSize( 'height', data.height );
+ iframe && iframe.setSize( 'width', data.width );
+ }
+});
diff --git a/_source/plugins/wsc/plugin.js b/_source/plugins/wsc/plugin.js index 3edb26d..27bb988 100644 --- a/_source/plugins/wsc/plugin.js +++ b/_source/plugins/wsc/plugin.js @@ -1,5 +1,5 @@ /*
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
@@ -18,7 +18,7 @@ CKEDITOR.plugins.add( 'wsc', var command = editor.addCommand( commandName, new CKEDITOR.dialogCommand( commandName ) );
// SpellChecker doesn't work in Opera and with custom domain
- command.modes = { wysiwyg : ( !CKEDITOR.env.opera && document.domain == window.location.hostname ) };
+ command.modes = { wysiwyg : ( !CKEDITOR.env.opera && !CKEDITOR.env.air && document.domain == window.location.hostname ) };
editor.ui.addButton( 'SpellChecker',
{
diff --git a/_source/plugins/wysiwygarea/plugin.js b/_source/plugins/wysiwygarea/plugin.js index 968a2e1..7657a19 100644 --- a/_source/plugins/wysiwygarea/plugin.js +++ b/_source/plugins/wysiwygarea/plugin.js @@ -1,978 +1,1346 @@ -/* -Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. -For licensing, see LICENSE.html or http://ckeditor.com/license -*/ - -/** - * @fileOverview The "wysiwygarea" plugin. It registers the "wysiwyg" editing - * mode, which handles the main editing area space. - */ - -(function() -{ - // List of elements in which has no way to move editing focus outside. - var nonExitableElementNames = { table:1,pre:1 }; - - // Matching an empty paragraph at the end of document. - var emptyParagraphRegexp = /\s*<(p|div|address|h\d|center)[^>]*>\s*(?:<br[^>]*>| |\u00A0| )?\s*(:?<\/\1>)?\s*(?=$|<\/body>)/gi; - - function onInsertHtml( evt ) - { - if ( this.mode == 'wysiwyg' ) - { - this.focus(); - this.fire( 'saveSnapshot' ); - - var selection = this.getSelection(), - data = evt.data; - - if ( this.dataProcessor ) - data = this.dataProcessor.toHtml( data ); - - if ( CKEDITOR.env.ie ) - { - var selIsLocked = selection.isLocked; - - if ( selIsLocked ) - selection.unlock(); - - var $sel = selection.getNative(); - if ( $sel.type == 'Control' ) - $sel.clear(); - $sel.createRange().pasteHTML( data ); - - if ( selIsLocked ) - this.getSelection().lock(); - } - else - this.document.$.execCommand( 'inserthtml', false, data ); - - CKEDITOR.tools.setTimeout( function() - { - this.fire( 'saveSnapshot' ); - }, 0, this ); - } - } - - function onInsertElement( evt ) - { - if ( this.mode == 'wysiwyg' ) - { - this.focus(); - this.fire( 'saveSnapshot' ); - - var element = evt.data, - elementName = element.getName(), - isBlock = CKEDITOR.dtd.$block[ elementName ]; - - var selection = this.getSelection(), - ranges = selection.getRanges(); - - var selIsLocked = selection.isLocked; - - if ( selIsLocked ) - selection.unlock(); - - var range, clone, lastElement, bookmark; - - for ( var i = ranges.length - 1 ; i >= 0 ; i-- ) - { - range = ranges[ i ]; - - // Remove the original contents. - range.deleteContents(); - - clone = !i && element || element.clone( true ); - - // If we're inserting a block at dtd-violated position, split - // the parent blocks until we reach blockLimit. - var current, dtd; - if ( isBlock ) - { - while ( ( current = range.getCommonAncestor( false, true ) ) - && ( dtd = CKEDITOR.dtd[ current.getName() ] ) - && !( dtd && dtd [ elementName ] ) ) - { - // Split up inline elements. - if ( current.getName() in CKEDITOR.dtd.span ) - range.splitElement( current ); - // If we're in an empty block which indicate a new paragraph, - // simply replace it with the inserting block.(#3664) - else if ( range.checkStartOfBlock() - && range.checkEndOfBlock() ) - { - range.setStartBefore( current ); - range.collapse( true ); - current.remove(); - } - else - range.splitBlock(); - } - } - - // Insert the new node. - range.insertNode( clone ); - - // Save the last element reference so we can make the - // selection later. - if ( !lastElement ) - lastElement = clone; - } - - range.moveToPosition( lastElement, CKEDITOR.POSITION_AFTER_END ); - - var next = lastElement.getNextSourceNode( true ); - if ( next && next.type == CKEDITOR.NODE_ELEMENT ) - range.moveToElementEditStart( next ); - - selection.selectRanges( [ range ] ); - - if ( selIsLocked ) - this.getSelection().lock(); - - // Save snaps after the whole execution completed. - // This's a workaround for make DOM modification's happened after - // 'insertElement' to be included either, e.g. Form-based dialogs' 'commitContents' - // call. - CKEDITOR.tools.setTimeout( function(){ - this.fire( 'saveSnapshot' ); - }, 0, this ); - } - } - - // DOM modification here should not bother dirty flag.(#4385) - function restoreDirty( editor ) - { - if ( !editor.checkDirty() ) - setTimeout( function(){ editor.resetDirty(); } ); - } - - var isNotWhitespace = CKEDITOR.dom.walker.whitespaces( true ), - isNotBookmark = CKEDITOR.dom.walker.bookmark( false, true ); - - function isNotEmpty( node ) - { - return isNotWhitespace( node ) && isNotBookmark( node ); - } - - function isNbsp( node ) - { - return node.type == CKEDITOR.NODE_TEXT - && CKEDITOR.tools.trim( node.getText() ).match( /^(?: |\xa0)$/ ); - } - - function restoreSelection( selection ) - { - if ( selection.isLocked ) - { - selection.unlock(); - setTimeout( function() { selection.lock(); }, 0 ); - } - } - - function isBlankParagraph( block ) - { - return block.getOuterHtml().match( emptyParagraphRegexp ); - } - - isNotWhitespace = CKEDITOR.dom.walker.whitespaces( true ); - - /** - * Auto-fixing block-less content by wrapping paragraph (#3190), prevent - * non-exitable-block by padding extra br.(#3189) - */ - function onSelectionChangeFixBody( evt ) - { - var editor = evt.editor, - path = evt.data.path, - blockLimit = path.blockLimit, - selection = evt.data.selection, - range = selection.getRanges()[0], - body = editor.document.getBody(), - enterMode = editor.config.enterMode; - - // When enterMode set to block, we'll establing new paragraph only if we're - // selecting inline contents right under body. (#3657) - if ( enterMode != CKEDITOR.ENTER_BR - && range.collapsed - && blockLimit.getName() == 'body' - && !path.block ) - { - editor.fire( 'updateSnapshot' ); - restoreDirty( editor ); - CKEDITOR.env.ie && restoreSelection( selection ); - - var fixedBlock = range.fixBlock( true, - editor.config.enterMode == CKEDITOR.ENTER_DIV ? 'div' : 'p' ); - - // For IE, we should remove any filler node which was introduced before. - if ( CKEDITOR.env.ie ) - { - var first = fixedBlock.getFirst( isNotEmpty ); - first && isNbsp( first ) && first.remove(); - } - - // If the fixed block is actually blank and is already followed by an exitable blank - // block, we should revert the fix and move into the existed one. (#3684) - if ( isBlankParagraph( fixedBlock ) ) - { - var previousElement = fixedBlock.getPrevious( isNotWhitespace ), - nextElement = fixedBlock.getNext( isNotWhitespace ); - - if ( previousElement && previousElement.getName - && !( previousElement.getName() in nonExitableElementNames ) - && isBlankParagraph( previousElement ) - && range.moveToElementEditStart( previousElement ) - || nextElement && nextElement.getName - && !( nextElement.getName() in nonExitableElementNames ) - && isBlankParagraph( nextElement ) - && range.moveToElementEditStart( nextElement ) ) - { - fixedBlock.remove(); - } - } - - range.select(); - // Notify non-IE that selection has changed. - if ( !CKEDITOR.env.ie ) - editor.selectionChange(); - } - - // All browsers are incapable to moving cursor out of certain non-exitable - // blocks (e.g. table, list, pre) at the end of document, make this happen by - // place a bogus node there, which would be later removed by dataprocessor. - var walkerRange = new CKEDITOR.dom.range( editor.document ), - walker = new CKEDITOR.dom.walker( walkerRange ); - walkerRange.selectNodeContents( body ); - walker.evaluator = function( node ) - { - return node.type == CKEDITOR.NODE_ELEMENT && ( node.getName() in nonExitableElementNames ); - }; - walker.guard = function( node, isMoveout ) - { - return !( ( node.type == CKEDITOR.NODE_TEXT && isNotWhitespace( node ) ) || isMoveout ); - }; - - if ( walker.previous() ) - { - editor.fire( 'updateSnapshot' ); - restoreDirty( editor ); - CKEDITOR.env.ie && restoreSelection( selection ); - - var paddingBlock; - if ( enterMode != CKEDITOR.ENTER_BR ) - paddingBlock = body.append( new CKEDITOR.dom.element( enterMode == CKEDITOR.ENTER_P ? 'p' : 'div' ) ); - else - paddingBlock = body; - - if ( !CKEDITOR.env.ie ) - paddingBlock.appendBogus(); - } - } - - CKEDITOR.plugins.add( 'wysiwygarea', - { - requires : [ 'editingblock' ], - - init : function( editor ) - { - var fixForBody = ( editor.config.enterMode != CKEDITOR.ENTER_BR ) - ? editor.config.enterMode == CKEDITOR.ENTER_DIV ? 'div' : 'p' : false; - - var frameLabel = editor.lang.editorTitle.replace( '%1', editor.name ); - - var contentDomReadyHandler; - editor.on( 'editingBlockReady', function() - { - var mainElement, - iframe, - isLoadingData, - isPendingFocus, - frameLoaded, - fireMode; - - - // Support for custom document.domain in IE. - var isCustomDomain = CKEDITOR.env.isCustomDomain(); - - // Creates the iframe that holds the editable document. - var createIFrame = function( data ) - { - if ( iframe ) - iframe.remove(); - - - var srcScript = - 'document.open();' + - - // The document domain must be set any time we - // call document.open(). - ( isCustomDomain ? ( 'document.domain="' + document.domain + '";' ) : '' ) + - - 'document.close();'; - - iframe = CKEDITOR.dom.element.createFromHtml( '<iframe' + - ' style="width:100%;height:100%"' + - ' frameBorder="0"' + - ' title="' + frameLabel + '"' + - // With IE, the custom domain has to be taken care at first, - // for other browers, the 'src' attribute should be left empty to - // trigger iframe's 'load' event. - ' src="' + ( CKEDITOR.env.ie ? 'javascript:void(function(){' + encodeURIComponent( srcScript ) + '}())' : '' ) + '"' + - ' tabIndex="' + ( CKEDITOR.env.webkit? -1 : editor.tabIndex ) + '"' + - ' allowTransparency="true"' + - '></iframe>' ); - - // With FF, it's better to load the data on iframe.load. (#3894,#4058) - iframe.on( 'load', function( ev ) - { - frameLoaded = 1; - ev.removeListener(); - - var doc = iframe.getFrameDocument().$; - - // Don't leave any history log in IE. (#5657) - doc.open( "text/html","replace" ); - doc.write( data ); - doc.close(); - }); - - mainElement.append( iframe ); - }; - - // The script that launches the bootstrap logic on 'domReady', so the document - // is fully editable even before the editing iframe is fully loaded (#4455). - contentDomReadyHandler = CKEDITOR.tools.addFunction( contentDomReady ); - var activationScript = - '<script id="cke_actscrpt" type="text/javascript" cke_temp="1">' + - ( isCustomDomain ? ( 'document.domain="' + document.domain + '";' ) : '' ) + - 'window.parent.CKEDITOR.tools.callFunction( ' + contentDomReadyHandler + ', window );' + - '</script>'; - - // Editing area bootstrap code. - function contentDomReady( domWindow ) - { - if ( !frameLoaded ) - return; - frameLoaded = 0; - - editor.fire( 'ariaWidget', iframe ); - - var domDocument = domWindow.document, - body = domDocument.body; - - // Remove this script from the DOM. - var script = domDocument.getElementById( "cke_actscrpt" ); - script.parentNode.removeChild( script ); - - body.spellcheck = !editor.config.disableNativeSpellChecker; - - if ( CKEDITOR.env.ie ) - { - // Don't display the focus border. - body.hideFocus = true; - - // Disable and re-enable the body to avoid IE from - // taking the editing focus at startup. (#141 / #523) - body.disabled = true; - body.contentEditable = true; - body.removeAttribute( 'disabled' ); - } - else - { - // Avoid opening design mode in a frame window thread, - // which will cause host page scrolling.(#4397) - setTimeout( function() - { - // Prefer 'contentEditable' instead of 'designMode'. (#3593) - if ( CKEDITOR.env.gecko && CKEDITOR.env.version >= 10900 - || CKEDITOR.env.opera ) - domDocument.$.body.contentEditable = true; - else if ( CKEDITOR.env.webkit ) - domDocument.$.body.parentNode.contentEditable = true; - else - domDocument.$.designMode = 'on'; - }, 0 ); - } - - // Gecko need a key event to 'wake up' the editing - // ability when document is empty.(#3864) - if ( CKEDITOR.env.gecko && !body.childNodes.length ) - { - setTimeout( function() - { - restoreDirty( editor ); - - // Simulating keyboard character input by dispatching a keydown of white-space text. - var keyEventSimulate = domDocument.$.createEvent( "KeyEvents" ); - keyEventSimulate.initKeyEvent( 'keypress', true, true, domWindow.$, false, - false, false, false, 0, 32 ); - domDocument.$.dispatchEvent( keyEventSimulate ); - - // Restore the original document status by placing the cursor before a bogus br created (#5021). - domDocument.createElement( 'br', { attributes: { '_moz_editor_bogus_node' : 'TRUE', '_moz_dirty' : "" } } ) - .replace( domDocument.getBody().getFirst() ); - var nativeRange = new CKEDITOR.dom.range( domDocument ); - nativeRange.setStartAt( new CKEDITOR.dom.element( body ) , CKEDITOR.POSITION_AFTER_START ); - nativeRange.select(); - }, 0 ); - } - - // IE, Opera and Safari may not support it and throw - // errors. - try { domDocument.execCommand( 'enableObjectResizing', false, !editor.config.disableObjectResizing ) ; } catch(e) {} - try { domDocument.execCommand( 'enableInlineTableEditing', false, !editor.config.disableNativeTableHandles ) ; } catch(e) {} - - domWindow = editor.window = new CKEDITOR.dom.window( domWindow ); - domDocument = editor.document = new CKEDITOR.dom.document( domDocument ); - - domDocument.on( 'dblclick', function( evt ) - { - var element = evt.data.getTarget(), - data = { element : element, dialog : '' }; - editor.fire( 'doubleclick', data ); - data.dialog && editor.openDialog( data.dialog ); - }); - - // Gecko/Webkit need some help when selecting control type elements. (#3448) - if ( !( CKEDITOR.env.ie || CKEDITOR.env.opera) ) - { - domDocument.on( 'mousedown', function( ev ) - { - var control = ev.data.getTarget(); - if ( control.is( 'img', 'hr', 'input', 'textarea', 'select' ) ) - editor.getSelection().selectElement( control ); - } ); - } - - // Webkit: avoid from editing form control elements content. - if ( CKEDITOR.env.webkit ) - { - // Prevent from tick checkbox/radiobox/select - domDocument.on( 'click', function( ev ) - { - if ( ev.data.getTarget().is( 'input', 'select' ) ) - ev.data.preventDefault(); - } ); - - // Prevent from editig textfield/textarea value. - domDocument.on( 'mouseup', function( ev ) - { - if ( ev.data.getTarget().is( 'input', 'textarea' ) ) - ev.data.preventDefault(); - } ); - } - - // IE standard compliant in editing frame doesn't focus the editor when - // clicking outside actual content, manually apply the focus. (#1659) - if ( CKEDITOR.env.ie - && domDocument.$.compatMode == 'CSS1Compat' - || CKEDITOR.env.gecko - || CKEDITOR.env.opera ) - { - var htmlElement = domDocument.getDocumentElement(); - htmlElement.on( 'mousedown', function( evt ) - { - // Setting focus directly on editor doesn't work, we - // have to use here a temporary element to 'redirect' - // the focus. - if ( evt.data.getTarget().equals( htmlElement ) ) - { - if ( CKEDITOR.env.gecko && CKEDITOR.env.version >= 10900 ) - blinkCursor(); - focusGrabber.focus(); - } - } ); - } - - domWindow.on( 'blur', function() - { - editor.focusManager.blur(); - }); - - domWindow.on( 'focus', function() - { - var doc = editor.document; - - if ( CKEDITOR.env.gecko && CKEDITOR.env.version >= 10900 ) - blinkCursor(); - else if ( CKEDITOR.env.opera ) - doc.getBody().focus(); - else if ( CKEDITOR.env.webkit ) - { - // Selection will get lost after move focus - // to document element, save it first. - var sel = editor.getSelection(), - type = sel.getType(), - range = ( type != CKEDITOR.SELECTION_NONE ) && sel.getRanges()[ 0 ]; - - doc.getDocumentElement().focus(); - range && range.select(); - } - - editor.focusManager.focus(); - }); - - var keystrokeHandler = editor.keystrokeHandler; - if ( keystrokeHandler ) - keystrokeHandler.attach( domDocument ); - - if ( CKEDITOR.env.ie ) - { - domDocument.getDocumentElement().addClass( domDocument.$.compatMode ); - // Override keystrokes which should have deletion behavior - // on control types in IE . (#4047) - domDocument.on( 'keydown', function( evt ) - { - var keyCode = evt.data.getKeystroke(); - - // Backspace OR Delete. - if ( keyCode in { 8 : 1, 46 : 1 } ) - { - var sel = editor.getSelection(), - control = sel.getSelectedElement(); - - if ( control ) - { - // Make undo snapshot. - editor.fire( 'saveSnapshot' ); - - // Delete any element that 'hasLayout' (e.g. hr,table) in IE8 will - // break up the selection, safely manage it here. (#4795) - var bookmark = sel.getRanges()[ 0 ].createBookmark(); - // Remove the control manually. - control.remove(); - sel.selectBookmarks( [ bookmark ] ); - - editor.fire( 'saveSnapshot' ); - - evt.data.preventDefault(); - } - } - } ); - - // PageUp/PageDown scrolling is broken in document - // with standard doctype, manually fix it. (#4736) - if ( domDocument.$.compatMode == 'CSS1Compat' ) - { - var pageUpDownKeys = { 33 : 1, 34 : 1 }; - domDocument.on( 'keydown', function( evt ) - { - if ( evt.data.getKeystroke() in pageUpDownKeys ) - { - setTimeout( function () - { - editor.getSelection().scrollIntoView(); - }, 0 ); - } - } ); - } - } - - // Adds the document body as a context menu target. - if ( editor.contextMenu ) - editor.contextMenu.addTarget( domDocument, editor.config.browserContextMenuOnCtrl !== false ); - - setTimeout( function() - { - editor.fire( 'contentDom' ); - - if ( fireMode ) - { - editor.mode = 'wysiwyg'; - editor.fire( 'mode' ); - fireMode = false; - } - - isLoadingData = false; - - if ( isPendingFocus ) - { - editor.focus(); - isPendingFocus = false; - } - setTimeout( function() - { - editor.fire( 'dataReady' ); - }, 0 ); - - /* - * IE BUG: IE might have rendered the iframe with invisible contents. - * (#3623). Push some inconsequential CSS style changes to force IE to - * refresh it. - * - * Also, for some unknown reasons, short timeouts (e.g. 100ms) do not - * fix the problem. :( - */ - if ( CKEDITOR.env.ie ) - { - setTimeout( function() - { - if ( editor.document ) - { - var $body = editor.document.$.body; - $body.runtimeStyle.marginBottom = '0px'; - $body.runtimeStyle.marginBottom = ''; - } - }, 1000 ); - } - }, - 0 ); - } - - editor.addMode( 'wysiwyg', - { - load : function( holderElement, data, isSnapshot ) - { - mainElement = holderElement; - - if ( CKEDITOR.env.ie && CKEDITOR.env.quirks ) - holderElement.setStyle( 'position', 'relative' ); - - // The editor data "may be dirty" after this - // point. - editor.mayBeDirty = true; - - fireMode = true; - - if ( isSnapshot ) - this.loadSnapshotData( data ); - else - this.loadData( data ); - }, - - loadData : function( data ) - { - isLoadingData = true; - - var config = editor.config, - fullPage = config.fullPage, - docType = config.docType; - - // Build the additional stuff to be included into <head>. - var headExtra = - '<style type="text/css" cke_temp="1">' + - editor._.styles.join( '\n' ) + - '</style>'; - - !fullPage && ( headExtra = - CKEDITOR.tools.buildStyleHtml( editor.config.contentsCss ) + - headExtra ); - - var baseTag = config.baseHref ? '<base href="' + config.baseHref + '" cke_temp="1" />' : ''; - - if ( fullPage ) - { - // Search and sweep out the doctype declaration. - data = data.replace( /<!DOCTYPE[^>]*>/i, function( match ) - { - editor.docType = docType = match; - return ''; - }); - } - - // Get the HTML version of the data. - if ( editor.dataProcessor ) - data = editor.dataProcessor.toHtml( data, fixForBody ); - - if ( fullPage ) - { - // Check if the <body> tag is available. - if ( !(/<body[\s|>]/).test( data ) ) - data = '<body>' + data; - - // Check if the <html> tag is available. - if ( !(/<html[\s|>]/).test( data ) ) - data = '<html>' + data + '</html>'; - - // Check if the <head> tag is available. - if ( !(/<head[\s|>]/).test( data ) ) - data = data.replace( /<html[^>]*>/, '$&<head><title></title></head>' ) ; - else if ( !(/<title[\s|>]/).test( data ) ) - data = data.replace( /<head[^>]*>/, '$&<title></title>' ) ; - - // The base must be the first tag in the HEAD, e.g. to get relative - // links on styles. - baseTag && ( data = data.replace( /<head>/, '$&' + baseTag ) ); - - // Inject the extra stuff into <head>. - // Attention: do not change it before testing it well. (V2) - // This is tricky... if the head ends with <meta ... content type>, - // Firefox will break. But, it works if we place our extra stuff as - // the last elements in the HEAD. - data = data.replace( /<\/head\s*>/, headExtra + '$&' ); - - // Add the DOCTYPE back to it. - data = docType + data; - } - else - { - data = - config.docType + - '<html dir="' + config.contentsLangDirection + '"' + - ' lang="' + ( config.contentsLanguage || editor.langCode ) + '">' + - '<head>' + - '<title>' + frameLabel + '</title>' + - baseTag + - headExtra + - '</head>' + - '<body' + ( config.bodyId ? ' id="' + config.bodyId + '"' : '' ) + - ( config.bodyClass ? ' class="' + config.bodyClass + '"' : '' ) + - '>' + - data + - '</html>'; - } - - data += activationScript; - - - // The iframe is recreated on each call of setData, so we need to clear DOM objects - this.onDispose(); - createIFrame( data ); - }, - - getData : function() - { - var config = editor.config, - fullPage = config.fullPage, - docType = fullPage && editor.docType, - doc = iframe.getFrameDocument(); - - var data = fullPage - ? doc.getDocumentElement().getOuterHtml() - : doc.getBody().getHtml(); - - if ( editor.dataProcessor ) - data = editor.dataProcessor.toDataFormat( data, fixForBody ); - - // Strip the last blank paragraph within document. - if ( config.ignoreEmptyParagraph ) - data = data.replace( emptyParagraphRegexp, '' ); - - if ( docType ) - data = docType + '\n' + data; - - return data; - }, - - getSnapshotData : function() - { - return iframe.getFrameDocument().getBody().getHtml(); - }, - - loadSnapshotData : function( data ) - { - iframe.getFrameDocument().getBody().setHtml( data ); - }, - - onDispose : function() - { - if ( !editor.document ) - return; - - editor.document.getDocumentElement().clearCustomData(); - editor.document.getBody().clearCustomData(); - - editor.window.clearCustomData(); - editor.document.clearCustomData(); - - iframe.clearCustomData(); - }, - - unload : function( holderElement ) - { - this.onDispose(); - - editor.window = editor.document = iframe = mainElement = isPendingFocus = null; - - editor.fire( 'contentDomUnload' ); - }, - - focus : function() - { - if ( isLoadingData ) - isPendingFocus = true; - else if ( editor.window ) - { - editor.window.focus(); - - editor.selectionChange(); - } - } - }); - - editor.on( 'insertHtml', onInsertHtml, null, null, 20 ); - editor.on( 'insertElement', onInsertElement, null, null, 20 ); - // Auto fixing on some document structure weakness to enhance usabilities. (#3190 and #3189) - editor.on( 'selectionChange', onSelectionChangeFixBody, null, null, 1 ); - }); - - var titleBackup; - // Setting voice label as window title, backup the original one - // and restore it before running into use. - editor.on( 'contentDom', function () - { - var title = editor.document.getElementsByTag( 'title' ).getItem( 0 ); - title.setAttribute( '_cke_title', editor.document.$.title ); - editor.document.$.title = frameLabel; - }); - - // IE8 stricts mode doesn't have 'contentEditable' in effect - // on element unless it has layout. (#5562) - if ( CKEDITOR.env.ie8Compat ) - { - editor.addCss( 'html.CSS1Compat [contenteditable=false]{ min-height:0 !important;}' ); - - var selectors = []; - for ( var tag in CKEDITOR.dtd.$removeEmpty ) - selectors.push( 'html.CSS1Compat ' + tag + '[contenteditable=false]' ); - editor.addCss( selectors.join( ',' ) + '{ display:inline-block;}' ); - } - - // Switch on design mode for a short while and close it after then. - function blinkCursor( retry ) - { - CKEDITOR.tools.tryThese( - function() - { - editor.document.$.designMode = 'on'; - setTimeout( function () - { - editor.document.$.designMode = 'off'; - editor.document.getBody().focus(); - }, 50 ); - }, - function() - { - // The above call is known to fail when parent DOM - // tree layout changes may break design mode. (#5782) - // Refresh the 'contentEditable' is a cue to this. - editor.document.$.designMode = 'off'; - var body = editor.document.getBody(); - body.setAttribute( 'contentEditable', false ); - body.setAttribute( 'contentEditable', true ); - // Try it again once.. - !retry && blinkCursor( 1 ); - }); - } - - // Create an invisible element to grab focus. - if ( CKEDITOR.env.gecko || CKEDITOR.env.ie || CKEDITOR.env.opera ) - { - var focusGrabber; - editor.on( 'uiReady', function() - { - focusGrabber = editor.container.append( CKEDITOR.dom.element.createFromHtml( - // Use 'span' instead of anything else to fly under the screen-reader radar. (#5049) - '<span tabindex="-1" style="position:absolute; left:-10000" role="presentation"></span>' ) ); - - focusGrabber.on( 'focus', function() - { - editor.focus(); - } ); - } ); - editor.on( 'destroy', function() - { - CKEDITOR.tools.removeFunction( contentDomReadyHandler ); - focusGrabber.clearCustomData(); - } ); - } - - // Disable form elements editing mode provided by some browers. (#5746) - editor.on( 'insertElement', function ( evt ) - { - var element = evt.data; - if ( element.type = CKEDITOR.NODE_ELEMENT - && ( element.is( 'input' ) || element.is( 'textarea' ) ) ) - { - element.setAttribute( 'contentEditable', false ); - } - }); - - } - }); - - // Fixing Firefox 'Back-Forward Cache' break design mode. (#4514) - if ( CKEDITOR.env.gecko ) - { - ( function () - { - var body = document.body; - - if ( !body ) - window.addEventListener( 'load', arguments.callee, false ); - else - { - var currentHandler = body.getAttribute( 'onpageshow' ); - body.setAttribute( 'onpageshow', ( currentHandler ? currentHandler + ';' : '') + - 'event.persisted && (function(){' + - 'var allInstances = CKEDITOR.instances, editor, doc;' + - 'for ( var i in allInstances )' + - '{' + - ' editor = allInstances[ i ];' + - ' doc = editor.document;' + - ' if ( doc )' + - ' {' + - ' doc.$.designMode = "off";' + - ' doc.$.designMode = "on";' + - ' }' + - '}' + - '})();' ); - } - } )(); - - } -})(); - -/** - * Disables the ability of resize objects (image and tables) in the editing - * area. - * @type Boolean - * @default false - * @example - * config.disableObjectResizing = true; - */ -CKEDITOR.config.disableObjectResizing = false; - -/** - * Disables the "table tools" offered natively by the browser (currently - * Firefox only) to make quick table editing operations, like adding or - * deleting rows and columns. - * @type Boolean - * @default true - * @example - * config.disableNativeTableHandles = false; - */ -CKEDITOR.config.disableNativeTableHandles = true; - -/** - * Disables the built-in spell checker while typing natively available in the - * browser (currently Firefox and Safari only).<br /><br /> - * - * Even if word suggestions will not appear in the CKEditor context menu, this - * feature is useful to help quickly identifying misspelled words.<br /><br /> - * - * This setting is currently compatible with Firefox only due to limitations in - * other browsers. - * @type Boolean - * @default true - * @example - * config.disableNativeSpellChecker = false; - */ -CKEDITOR.config.disableNativeSpellChecker = true; - -/** - * Whether the editor must output an empty value ("") if it's contents is made - * by an empty paragraph only. - * @type Boolean - * @default true - * @example - * config.ignoreEmptyParagraph = false; - */ -CKEDITOR.config.ignoreEmptyParagraph = true; - -/** - * Fired when data is loaded and ready for retrieval in an editor instance. - * @name CKEDITOR.editor#dataReady - * @event - */ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview The "wysiwygarea" plugin. It registers the "wysiwyg" editing
+ * mode, which handles the main editing area space.
+ */
+
+(function()
+{
+ // Matching an empty paragraph at the end of document.
+ var emptyParagraphRegexp = /(^|<body\b[^>]*>)\s*<(p|div|address|h\d|center|pre)[^>]*>\s*(?:<br[^>]*>| |\u00A0| )?\s*(:?<\/\2>)?\s*(?=$|<\/body>)/gi;
+
+ var notWhitespaceEval = CKEDITOR.dom.walker.whitespaces( true );
+
+ // Elements that could blink the cursor anchoring beside it, like hr, page-break. (#6554)
+ function nonEditable( element )
+ {
+ return element.isBlockBoundary() && CKEDITOR.dtd.$empty[ element.getName() ];
+ }
+
+
+ function onInsert( insertFunc )
+ {
+ return function( evt )
+ {
+ if ( this.mode == 'wysiwyg' )
+ {
+ this.focus();
+
+ // Since the insertion might happen from within dialog or menu
+ // where the editor selection might be locked at the moment,
+ // update the locked selection.
+ var selection = this.getSelection(),
+ selIsLocked = selection.isLocked;
+
+ selIsLocked && selection.unlock();
+
+ this.fire( 'saveSnapshot' );
+
+ insertFunc.call( this, evt.data );
+
+ selIsLocked && this.getSelection().lock();
+
+ // Save snaps after the whole execution completed.
+ // This's a workaround for make DOM modification's happened after
+ // 'insertElement' to be included either, e.g. Form-based dialogs' 'commitContents'
+ // call.
+ CKEDITOR.tools.setTimeout( function()
+ {
+ this.fire( 'saveSnapshot' );
+ }, 0, this );
+ }
+ };
+ }
+
+ function doInsertHtml( data )
+ {
+ if ( this.dataProcessor )
+ data = this.dataProcessor.toHtml( data );
+
+ if ( !data )
+ return;
+
+ // HTML insertion only considers the first range.
+ var selection = this.getSelection(),
+ range = selection.getRanges()[ 0 ];
+
+ if ( range.checkReadOnly() )
+ return;
+
+ // Opera: force block splitting when pasted content contains block. (#7801)
+ if ( CKEDITOR.env.opera )
+ {
+ var path = new CKEDITOR.dom.elementPath( range.startContainer );
+ if ( path.block )
+ {
+ var nodes = CKEDITOR.htmlParser.fragment.fromHtml( data, false ).children;
+ for ( var i = 0, count = nodes.length; i < count; i++ )
+ {
+ if ( nodes[ i ]._.isBlockLike )
+ {
+ range.splitBlock( this.enterMode == CKEDITOR.ENTER_DIV ? 'div' : 'p' );
+ range.insertNode( range.document.createText( '' ) );
+ range.select();
+ break;
+ }
+ }
+ }
+ }
+
+ if ( CKEDITOR.env.ie )
+ {
+ var $sel = selection.getNative();
+
+ // Delete control selections to avoid IE bugs on pasteHTML.
+ if ( $sel.type == 'Control' )
+ $sel.clear();
+ else if ( selection.getType() == CKEDITOR.SELECTION_TEXT )
+ {
+ // Due to IE bugs on handling contenteditable=false blocks
+ // (#6005), we need to make some checks and eventually
+ // delete the selection first.
+
+ range = selection.getRanges()[ 0 ];
+ var endContainer = range && range.endContainer;
+
+ if ( endContainer &&
+ endContainer.type == CKEDITOR.NODE_ELEMENT &&
+ endContainer.getAttribute( 'contenteditable' ) == 'false' &&
+ range.checkBoundaryOfElement( endContainer, CKEDITOR.END ) )
+ {
+ range.setEndAfter( range.endContainer );
+ range.deleteContents();
+ }
+ }
+
+ $sel.createRange().pasteHTML( data );
+ }
+ else
+ this.document.$.execCommand( 'inserthtml', false, data );
+
+ // Webkit does not scroll to the cursor position after pasting (#5558)
+ if ( CKEDITOR.env.webkit )
+ {
+ selection = this.getSelection();
+ selection.scrollIntoView();
+ }
+ }
+
+ function doInsertText( text )
+ {
+ var selection = this.getSelection(),
+ mode = selection.getStartElement().hasAscendant( 'pre', true ) ?
+ CKEDITOR.ENTER_BR : this.config.enterMode,
+ isEnterBrMode = mode == CKEDITOR.ENTER_BR;
+
+ var html = CKEDITOR.tools.htmlEncode( text.replace( /\r\n|\r/g, '\n' ) );
+
+ // Convert leading and trailing whitespaces into
+ html = html.replace( /^[ \t]+|[ \t]+$/g, function( match, offset, s )
+ {
+ if ( match.length == 1 ) // one space, preserve it
+ return ' ';
+ else if ( !offset ) // beginning of block
+ return CKEDITOR.tools.repeat( ' ', match.length - 1 ) + ' ';
+ else // end of block
+ return ' ' + CKEDITOR.tools.repeat( ' ', match.length - 1 );
+ } );
+
+ // Convert subsequent whitespaces into
+ html = html.replace( /[ \t]{2,}/g, function ( match )
+ {
+ return CKEDITOR.tools.repeat( ' ', match.length - 1 ) + ' ';
+ } );
+
+ var paragraphTag = mode == CKEDITOR.ENTER_P ? 'p' : 'div';
+
+ // Two line-breaks create one paragraph.
+ if ( !isEnterBrMode )
+ {
+ html = html.replace( /(\n{2})([\s\S]*?)(?:$|\1)/g,
+ function( match, group1, text )
+ {
+ return '<'+paragraphTag + '>' + text + '</' + paragraphTag + '>';
+ });
+ }
+
+ // One <br> per line-break.
+ html = html.replace( /\n/g, '<br>' );
+
+ // Compensate padding <br> for non-IE.
+ if ( !( isEnterBrMode || CKEDITOR.env.ie ) )
+ {
+ html = html.replace( new RegExp( '<br>(?=</' + paragraphTag + '>)' ), function( match )
+ {
+ return CKEDITOR.tools.repeat( match, 2 );
+ } );
+ }
+
+ // Inline styles have to be inherited in Firefox.
+ if ( CKEDITOR.env.gecko || CKEDITOR.env.webkit )
+ {
+ var path = new CKEDITOR.dom.elementPath( selection.getStartElement() ),
+ context = [];
+
+ for ( var i = 0; i < path.elements.length; i++ )
+ {
+ var tag = path.elements[ i ].getName();
+ if ( tag in CKEDITOR.dtd.$inline )
+ context.unshift( path.elements[ i ].getOuterHtml().match( /^<.*?>/) );
+ else if ( tag in CKEDITOR.dtd.$block )
+ break;
+ }
+
+ // Reproduce the context by preceding the pasted HTML with opening inline tags.
+ html = context.join( '' ) + html;
+ }
+
+ doInsertHtml.call( this, html );
+ }
+
+ function doInsertElement( element )
+ {
+ var selection = this.getSelection(),
+ ranges = selection.getRanges(),
+ elementName = element.getName(),
+ isBlock = CKEDITOR.dtd.$block[ elementName ];
+
+ var selIsLocked = selection.isLocked;
+
+ if ( selIsLocked )
+ selection.unlock();
+
+ var range, clone, lastElement, bookmark;
+
+ for ( var i = ranges.length - 1 ; i >= 0 ; i-- )
+ {
+ range = ranges[ i ];
+
+ if ( !range.checkReadOnly() )
+ {
+ // Remove the original contents, merge splitted nodes.
+ range.deleteContents( 1 );
+
+ clone = !i && element || element.clone( 1 );
+
+ // If we're inserting a block at dtd-violated position, split
+ // the parent blocks until we reach blockLimit.
+ var current, dtd;
+ if ( isBlock )
+ {
+ while ( ( current = range.getCommonAncestor( 0, 1 ) )
+ && ( dtd = CKEDITOR.dtd[ current.getName() ] )
+ && !( dtd && dtd [ elementName ] ) )
+ {
+ // Split up inline elements.
+ if ( current.getName() in CKEDITOR.dtd.span )
+ range.splitElement( current );
+ // If we're in an empty block which indicate a new paragraph,
+ // simply replace it with the inserting block.(#3664)
+ else if ( range.checkStartOfBlock()
+ && range.checkEndOfBlock() )
+ {
+ range.setStartBefore( current );
+ range.collapse( true );
+ current.remove();
+ }
+ else
+ range.splitBlock();
+ }
+ }
+
+ // Insert the new node.
+ range.insertNode( clone );
+
+ // Save the last element reference so we can make the
+ // selection later.
+ if ( !lastElement )
+ lastElement = clone;
+ }
+ }
+
+ if ( lastElement )
+ {
+ range.moveToPosition( lastElement, CKEDITOR.POSITION_AFTER_END );
+
+ // If we're inserting a block element immediatelly followed by
+ // another block element, the selection must move there. (#3100,#5436)
+ if ( isBlock )
+ {
+ var next = lastElement.getNext( notWhitespaceEval ),
+ nextName = next && next.type == CKEDITOR.NODE_ELEMENT && next.getName();
+
+ // Check if it's a block element that accepts text.
+ if ( nextName && CKEDITOR.dtd.$block[ nextName ] && CKEDITOR.dtd[ nextName ]['#'] )
+ range.moveToElementEditStart( next );
+ }
+ }
+
+ selection.selectRanges( [ range ] );
+
+ if ( selIsLocked )
+ this.getSelection().lock();
+ }
+
+ // DOM modification here should not bother dirty flag.(#4385)
+ function restoreDirty( editor )
+ {
+ if ( !editor.checkDirty() )
+ setTimeout( function(){ editor.resetDirty(); }, 0 );
+ }
+
+ var isNotWhitespace = CKEDITOR.dom.walker.whitespaces( true ),
+ isNotBookmark = CKEDITOR.dom.walker.bookmark( false, true );
+
+ function isNotEmpty( node )
+ {
+ return isNotWhitespace( node ) && isNotBookmark( node );
+ }
+
+ function isNbsp( node )
+ {
+ return node.type == CKEDITOR.NODE_TEXT
+ && CKEDITOR.tools.trim( node.getText() ).match( /^(?: |\xa0)$/ );
+ }
+
+ function restoreSelection( selection )
+ {
+ if ( selection.isLocked )
+ {
+ selection.unlock();
+ setTimeout( function() { selection.lock(); }, 0 );
+ }
+ }
+
+ function isBlankParagraph( block )
+ {
+ return block.getOuterHtml().match( emptyParagraphRegexp );
+ }
+
+ isNotWhitespace = CKEDITOR.dom.walker.whitespaces( true );
+
+ // Gecko need a key event to 'wake up' the editing
+ // ability when document is empty.(#3864, #5781)
+ function activateEditing( editor )
+ {
+ var win = editor.window,
+ doc = editor.document,
+ body = editor.document.getBody(),
+ bodyFirstChild = body.getFirst(),
+ bodyChildsNum = body.getChildren().count();
+
+ if ( !bodyChildsNum
+ || bodyChildsNum == 1
+ && bodyFirstChild.type == CKEDITOR.NODE_ELEMENT
+ && bodyFirstChild.hasAttribute( '_moz_editor_bogus_node' ) )
+ {
+ restoreDirty( editor );
+
+ // Memorize scroll position to restore it later (#4472).
+ var hostDocument = editor.element.getDocument();
+ var hostDocumentElement = hostDocument.getDocumentElement();
+ var scrollTop = hostDocumentElement.$.scrollTop;
+ var scrollLeft = hostDocumentElement.$.scrollLeft;
+
+ // Simulating keyboard character input by dispatching a keydown of white-space text.
+ var keyEventSimulate = doc.$.createEvent( "KeyEvents" );
+ keyEventSimulate.initKeyEvent( 'keypress', true, true, win.$, false,
+ false, false, false, 0, 32 );
+ doc.$.dispatchEvent( keyEventSimulate );
+
+ if ( scrollTop != hostDocumentElement.$.scrollTop || scrollLeft != hostDocumentElement.$.scrollLeft )
+ hostDocument.getWindow().$.scrollTo( scrollLeft, scrollTop );
+
+ // Restore the original document status by placing the cursor before a bogus br created (#5021).
+ bodyChildsNum && body.getFirst().remove();
+ doc.getBody().appendBogus();
+ var nativeRange = new CKEDITOR.dom.range( doc );
+ nativeRange.setStartAt( body , CKEDITOR.POSITION_AFTER_START );
+ nativeRange.select();
+ }
+ }
+
+ /**
+ * Auto-fixing block-less content by wrapping paragraph (#3190), prevent
+ * non-exitable-block by padding extra br.(#3189)
+ */
+ function onSelectionChangeFixBody( evt )
+ {
+ var editor = evt.editor,
+ path = evt.data.path,
+ blockLimit = path.blockLimit,
+ selection = evt.data.selection,
+ range = selection.getRanges()[0],
+ body = editor.document.getBody(),
+ enterMode = editor.config.enterMode;
+
+ if ( CKEDITOR.env.gecko )
+ {
+ activateEditing( editor );
+
+ // Ensure bogus br could help to move cursor (out of styles) to the end of block. (#7041)
+ var pathBlock = path.block || path.blockLimit,
+ lastNode = pathBlock && pathBlock.getLast( isNotEmpty );
+
+ // Check some specialities of the current path block:
+ // 1. It is really displayed as block; (#7221)
+ // 2. It doesn't end with one inner block; (#7467)
+ // 3. It doesn't have bogus br yet.
+ if ( pathBlock
+ && pathBlock.isBlockBoundary()
+ && !( lastNode && lastNode.type == CKEDITOR.NODE_ELEMENT && lastNode.isBlockBoundary() )
+ && !pathBlock.is( 'pre' )
+ && !pathBlock.getBogus() )
+ {
+ pathBlock.appendBogus();
+ }
+ }
+
+ // When we're in block enter mode, a new paragraph will be established
+ // to encapsulate inline contents right under body. (#3657)
+ if ( editor.config.autoParagraph !== false
+ && enterMode != CKEDITOR.ENTER_BR
+ && range.collapsed
+ && blockLimit.getName() == 'body'
+ && !path.block )
+ {
+ var fixedBlock = range.fixBlock( true,
+ editor.config.enterMode == CKEDITOR.ENTER_DIV ? 'div' : 'p' );
+
+ // For IE, we should remove any filler node which was introduced before.
+ if ( CKEDITOR.env.ie )
+ {
+ var first = fixedBlock.getFirst( isNotEmpty );
+ first && isNbsp( first ) && first.remove();
+ }
+
+ // If the fixed block is actually blank and is already followed by an exitable blank
+ // block, we should revert the fix and move into the existed one. (#3684)
+ if ( isBlankParagraph( fixedBlock ) )
+ {
+ var element = fixedBlock.getNext( isNotWhitespace );
+ if ( element &&
+ element.type == CKEDITOR.NODE_ELEMENT &&
+ !nonEditable( element ) )
+ {
+ range.moveToElementEditStart( element );
+ fixedBlock.remove();
+ }
+ else
+ {
+ element = fixedBlock.getPrevious( isNotWhitespace );
+ if ( element &&
+ element.type == CKEDITOR.NODE_ELEMENT &&
+ !nonEditable( element ) )
+ {
+ range.moveToElementEditEnd( element );
+ fixedBlock.remove();
+ }
+ }
+ }
+
+ range.select();
+ // Cancel this selection change in favor of the next (correct). (#6811)
+ evt.cancel();
+ }
+
+ // Browsers are incapable of moving cursor out of certain block elements (e.g. table, div, pre)
+ // at the end of document, makes it unable to continue adding content, we have to make this
+ // easier by opening an new empty paragraph.
+ var testRange = new CKEDITOR.dom.range( editor.document );
+ testRange.moveToElementEditEnd( editor.document.getBody() );
+ var testPath = new CKEDITOR.dom.elementPath( testRange.startContainer );
+ if ( !testPath.blockLimit.is( 'body') )
+ {
+ var paddingBlock;
+ if ( enterMode != CKEDITOR.ENTER_BR )
+ paddingBlock = body.append( editor.document.createElement( enterMode == CKEDITOR.ENTER_P ? 'p' : 'div' ) );
+ else
+ paddingBlock = body;
+
+ if ( !CKEDITOR.env.ie )
+ paddingBlock.appendBogus();
+ }
+ }
+
+ CKEDITOR.plugins.add( 'wysiwygarea',
+ {
+ requires : [ 'editingblock' ],
+
+ init : function( editor )
+ {
+ var fixForBody = ( editor.config.enterMode != CKEDITOR.ENTER_BR && editor.config.autoParagraph !== false )
+ ? editor.config.enterMode == CKEDITOR.ENTER_DIV ? 'div' : 'p' : false;
+
+ var frameLabel = editor.lang.editorTitle.replace( '%1', editor.name );
+
+ var win = CKEDITOR.document.getWindow();
+ var contentDomReadyHandler;
+ editor.on( 'editingBlockReady', function()
+ {
+ var mainElement,
+ iframe,
+ isLoadingData,
+ isPendingFocus,
+ frameLoaded,
+ fireMode,
+ onResize;
+
+
+ // Support for custom document.domain in IE.
+ var isCustomDomain = CKEDITOR.env.isCustomDomain();
+
+ // Creates the iframe that holds the editable document.
+ var createIFrame = function( data )
+ {
+ if ( iframe )
+ iframe.remove();
+
+ var src =
+ 'document.open();' +
+
+ // The document domain must be set any time we
+ // call document.open().
+ ( isCustomDomain ? ( 'document.domain="' + document.domain + '";' ) : '' ) +
+
+ 'document.close();';
+
+ // With IE, the custom domain has to be taken care at first,
+ // for other browers, the 'src' attribute should be left empty to
+ // trigger iframe's 'load' event.
+ src =
+ CKEDITOR.env.air ?
+ 'javascript:void(0)' :
+ CKEDITOR.env.ie ?
+ 'javascript:void(function(){' + encodeURIComponent( src ) + '}())'
+ :
+ '';
+
+ iframe = CKEDITOR.dom.element.createFromHtml( '<iframe' +
+ ' style="width:100%;height:100%"' +
+ ' frameBorder="0"' +
+ ' title="' + frameLabel + '"' +
+ ' src="' + src + '"' +
+ ' tabIndex="' + ( CKEDITOR.env.webkit? -1 : editor.tabIndex ) + '"' +
+ ' allowTransparency="true"' +
+ '></iframe>' );
+
+ // Running inside of Firefox chrome the load event doesn't bubble like in a normal page (#5689)
+ if ( document.location.protocol == 'chrome:' )
+ CKEDITOR.event.useCapture = true;
+
+ // With FF, it's better to load the data on iframe.load. (#3894,#4058)
+ iframe.on( 'load', function( ev )
+ {
+ frameLoaded = 1;
+ ev.removeListener();
+
+ var doc = iframe.getFrameDocument();
+ doc.write( data );
+
+ CKEDITOR.env.air && contentDomReady( doc.getWindow().$ );
+ });
+
+ // Reset adjustment back to default (#5689)
+ if ( document.location.protocol == 'chrome:' )
+ CKEDITOR.event.useCapture = false;
+
+ mainElement.append( iframe );
+
+ // Webkit: iframe size doesn't auto fit well. (#7360)
+ if ( CKEDITOR.env.webkit )
+ {
+ onResize = function()
+ {
+ iframe.hide();
+ iframe.setSize( 'width', mainElement.getSize( 'width' ) );
+ iframe.show();
+ };
+
+ win.on( 'resize', onResize );
+ }
+ };
+
+ // The script that launches the bootstrap logic on 'domReady', so the document
+ // is fully editable even before the editing iframe is fully loaded (#4455).
+ contentDomReadyHandler = CKEDITOR.tools.addFunction( contentDomReady );
+ var activationScript =
+ '<script id="cke_actscrpt" type="text/javascript" data-cke-temp="1">' +
+ ( isCustomDomain ? ( 'document.domain="' + document.domain + '";' ) : '' ) +
+ 'window.parent.CKEDITOR.tools.callFunction( ' + contentDomReadyHandler + ', window );' +
+ '</script>';
+
+ // Editing area bootstrap code.
+ function contentDomReady( domWindow )
+ {
+ if ( !frameLoaded )
+ return;
+ frameLoaded = 0;
+
+ editor.fire( 'ariaWidget', iframe );
+
+ var domDocument = domWindow.document,
+ body = domDocument.body;
+
+ // Remove this script from the DOM.
+ var script = domDocument.getElementById( "cke_actscrpt" );
+ script && script.parentNode.removeChild( script );
+
+ body.spellcheck = !editor.config.disableNativeSpellChecker;
+
+ var editable = !editor.readOnly;
+
+ if ( CKEDITOR.env.ie )
+ {
+ // Don't display the focus border.
+ body.hideFocus = true;
+
+ // Disable and re-enable the body to avoid IE from
+ // taking the editing focus at startup. (#141 / #523)
+ body.disabled = true;
+ body.contentEditable = editable;
+ body.removeAttribute( 'disabled' );
+ }
+ else
+ {
+ // Avoid opening design mode in a frame window thread,
+ // which will cause host page scrolling.(#4397)
+ setTimeout( function()
+ {
+ // Prefer 'contentEditable' instead of 'designMode'. (#3593)
+ if ( CKEDITOR.env.gecko && CKEDITOR.env.version >= 10900
+ || CKEDITOR.env.opera )
+ domDocument.$.body.contentEditable = editable;
+ else if ( CKEDITOR.env.webkit )
+ domDocument.$.body.parentNode.contentEditable = editable;
+ else
+ domDocument.$.designMode = editable? 'off' : 'on';
+ }, 0 );
+ }
+
+ editable && CKEDITOR.env.gecko && CKEDITOR.tools.setTimeout( activateEditing, 0, null, editor );
+
+ domWindow = editor.window = new CKEDITOR.dom.window( domWindow );
+ domDocument = editor.document = new CKEDITOR.dom.document( domDocument );
+
+ editable && domDocument.on( 'dblclick', function( evt )
+ {
+ var element = evt.data.getTarget(),
+ data = { element : element, dialog : '' };
+ editor.fire( 'doubleclick', data );
+ data.dialog && editor.openDialog( data.dialog );
+ });
+
+ // Prevent automatic submission in IE #6336
+ CKEDITOR.env.ie && domDocument.on( 'click', function( evt )
+ {
+ var element = evt.data.getTarget();
+ if ( element.is( 'input' ) )
+ {
+ var type = element.getAttribute( 'type' );
+ if ( type == 'submit' || type == 'reset' )
+ evt.data.preventDefault();
+ }
+ });
+
+ // Gecko/Webkit need some help when selecting control type elements. (#3448)
+ if ( !( CKEDITOR.env.ie || CKEDITOR.env.opera ) )
+ {
+ domDocument.on( 'mousedown', function( ev )
+ {
+ var control = ev.data.getTarget();
+ if ( control.is( 'img', 'hr', 'input', 'textarea', 'select' ) )
+ editor.getSelection().selectElement( control );
+ } );
+ }
+
+ if ( CKEDITOR.env.gecko )
+ {
+ domDocument.on( 'mouseup', function( ev )
+ {
+ if ( ev.data.$.button == 2 )
+ {
+ var target = ev.data.getTarget();
+
+ // Prevent right click from selecting an empty block even
+ // when selection is anchored inside it. (#5845)
+ if ( !target.getOuterHtml().replace( emptyParagraphRegexp, '' ) )
+ {
+ var range = new CKEDITOR.dom.range( domDocument );
+ range.moveToElementEditStart( target );
+ range.select( true );
+ }
+ }
+ } );
+ }
+
+ // Prevent the browser opening links in read-only blocks. (#6032)
+ domDocument.on( 'click', function( ev )
+ {
+ ev = ev.data;
+ if ( ev.getTarget().is( 'a' ) && ev.$.button != 2 )
+ ev.preventDefault();
+ });
+
+ // Webkit: avoid from editing form control elements content.
+ if ( CKEDITOR.env.webkit )
+ {
+ // Mark that cursor will right blinking (#7113).
+ domDocument.on( 'mousedown', function() { wasFocused = 1; } );
+ // Prevent from tick checkbox/radiobox/select
+ domDocument.on( 'click', function( ev )
+ {
+ if ( ev.data.getTarget().is( 'input', 'select' ) )
+ ev.data.preventDefault();
+ } );
+
+ // Prevent from editig textfield/textarea value.
+ domDocument.on( 'mouseup', function( ev )
+ {
+ if ( ev.data.getTarget().is( 'input', 'textarea' ) )
+ ev.data.preventDefault();
+ } );
+ }
+
+ var focusTarget = CKEDITOR.env.ie ? iframe : domWindow;
+ focusTarget.on( 'blur', function()
+ {
+ editor.focusManager.blur();
+ });
+
+ var wasFocused;
+
+ focusTarget.on( 'focus', function()
+ {
+ var doc = editor.document;
+
+ if ( editable && CKEDITOR.env.gecko && CKEDITOR.env.version >= 10900 )
+ blinkCursor();
+ else if ( CKEDITOR.env.opera )
+ doc.getBody().focus();
+ // Webkit needs focus for the first time on the HTML element. (#6153)
+ else if ( CKEDITOR.env.webkit )
+ {
+ if ( !wasFocused )
+ {
+ editor.document.getDocumentElement().focus();
+ wasFocused = 1;
+ }
+ }
+
+ editor.focusManager.focus();
+ });
+
+ var keystrokeHandler = editor.keystrokeHandler;
+ // Prevent backspace from navigating off the page.
+ keystrokeHandler.blockedKeystrokes[ 8 ] = !editable;
+ keystrokeHandler.attach( domDocument );
+
+ domDocument.getDocumentElement().addClass( domDocument.$.compatMode );
+ // Override keystroke behaviors.
+ editable && domDocument.on( 'keydown', function( evt )
+ {
+ var keyCode = evt.data.getKeystroke();
+
+ // Backspace OR Delete.
+ if ( keyCode in { 8 : 1, 46 : 1 } )
+ {
+ var sel = editor.getSelection(),
+ selected = sel.getSelectedElement(),
+ range = sel.getRanges()[ 0 ];
+
+ // Override keystrokes which should have deletion behavior
+ // on fully selected element . (#4047) (#7645)
+ if ( selected )
+ {
+ // Make undo snapshot.
+ editor.fire( 'saveSnapshot' );
+
+ // Delete any element that 'hasLayout' (e.g. hr,table) in IE8 will
+ // break up the selection, safely manage it here. (#4795)
+ range.moveToPosition( selected, CKEDITOR.POSITION_BEFORE_START );
+ // Remove the control manually.
+ selected.remove();
+ range.select();
+
+ editor.fire( 'saveSnapshot' );
+
+ evt.data.preventDefault();
+ return;
+ }
+ }
+
+ // PageUp OR PageDown
+ if ( keyCode == 33 || keyCode == 34 )
+ {
+ if ( CKEDITOR.env.gecko )
+ {
+ var body = domDocument.getBody();
+
+ // Page up/down cause editor selection to leak
+ // outside of editable thus we try to intercept
+ // the behavior, while it affects only happen
+ // when editor contents are not overflowed. (#7955)
+ if ( domWindow.$.innerHeight > body.$.offsetHeight )
+ {
+ range = new CKEDITOR.dom.range( domDocument );
+ range[ keyCode == 33 ? 'moveToElementEditStart' : 'moveToElementEditEnd']( body );
+ range.select();
+ evt.data.preventDefault();
+ }
+ }
+
+ }
+ } );
+
+ // PageUp/PageDown scrolling is broken in document
+ // with standard doctype, manually fix it. (#4736)
+ if ( CKEDITOR.env.ie && domDocument.$.compatMode == 'CSS1Compat' )
+ {
+ var pageUpDownKeys = { 33 : 1, 34 : 1 };
+ domDocument.on( 'keydown', function( evt )
+ {
+ if ( evt.data.getKeystroke() in pageUpDownKeys )
+ {
+ setTimeout( function ()
+ {
+ editor.getSelection().scrollIntoView();
+ }, 0 );
+ }
+ } );
+ }
+
+ // Prevent IE from leaving new paragraph after deleting all contents in body. (#6966)
+ if ( CKEDITOR.env.ie && editor.config.enterMode != CKEDITOR.ENTER_P )
+ {
+ domDocument.on( 'selectionchange', function()
+ {
+ var body = domDocument.getBody(),
+ sel = editor.getSelection(),
+ range = sel && sel.getRanges()[ 0 ];
+
+ if ( range && body.getHtml().match( /^<p> <\/p>$/i )
+ && range.startContainer.equals( body ) )
+ {
+ // Avoid the ambiguity from a real user cursor position.
+ setTimeout( function ()
+ {
+ range = editor.getSelection().getRanges()[ 0 ];
+ if ( !range.startContainer.equals ( 'body' ) )
+ {
+ body.getFirst().remove( 1 );
+ range.moveToElementEditEnd( body );
+ range.select( 1 );
+ }
+ }, 0 );
+ }
+ });
+ }
+
+ // Adds the document body as a context menu target.
+ if ( editor.contextMenu )
+ editor.contextMenu.addTarget( domDocument, editor.config.browserContextMenuOnCtrl !== false );
+
+ setTimeout( function()
+ {
+ editor.fire( 'contentDom' );
+
+ if ( fireMode )
+ {
+ editor.mode = 'wysiwyg';
+ editor.fire( 'mode', { previousMode : editor._.previousMode } );
+ fireMode = false;
+ }
+
+ isLoadingData = false;
+
+ if ( isPendingFocus )
+ {
+ editor.focus();
+ isPendingFocus = false;
+ }
+ setTimeout( function()
+ {
+ editor.fire( 'dataReady' );
+ }, 0 );
+
+ // IE, Opera and Safari may not support it and throw errors.
+ try { editor.document.$.execCommand( 'enableInlineTableEditing', false, !editor.config.disableNativeTableHandles ); } catch(e) {}
+ if ( editor.config.disableObjectResizing )
+ {
+ try
+ {
+ editor.document.$.execCommand( 'enableObjectResizing', false, false );
+ }
+ catch(e)
+ {
+ // For browsers in which the above method failed, we can cancel the resizing on the fly (#4208)
+ editor.document.getBody().on( CKEDITOR.env.ie ? 'resizestart' : 'resize', function( evt )
+ {
+ evt.data.preventDefault();
+ });
+ }
+ }
+
+ /*
+ * IE BUG: IE might have rendered the iframe with invisible contents.
+ * (#3623). Push some inconsequential CSS style changes to force IE to
+ * refresh it.
+ *
+ * Also, for some unknown reasons, short timeouts (e.g. 100ms) do not
+ * fix the problem. :(
+ */
+ if ( CKEDITOR.env.ie )
+ {
+ setTimeout( function()
+ {
+ if ( editor.document )
+ {
+ var $body = editor.document.$.body;
+ $body.runtimeStyle.marginBottom = '0px';
+ $body.runtimeStyle.marginBottom = '';
+ }
+ }, 1000 );
+ }
+ },
+ 0 );
+ }
+
+ editor.addMode( 'wysiwyg',
+ {
+ load : function( holderElement, data, isSnapshot )
+ {
+ mainElement = holderElement;
+
+ if ( CKEDITOR.env.ie && CKEDITOR.env.quirks )
+ holderElement.setStyle( 'position', 'relative' );
+
+ // The editor data "may be dirty" after this
+ // point.
+ editor.mayBeDirty = true;
+
+ fireMode = true;
+
+ if ( isSnapshot )
+ this.loadSnapshotData( data );
+ else
+ this.loadData( data );
+ },
+
+ loadData : function( data )
+ {
+ isLoadingData = true;
+ editor._.dataStore = { id : 1 };
+
+ var config = editor.config,
+ fullPage = config.fullPage,
+ docType = config.docType;
+
+ // Build the additional stuff to be included into <head>.
+ var headExtra =
+ '<style type="text/css" data-cke-temp="1">' +
+ editor._.styles.join( '\n' ) +
+ '</style>';
+
+ !fullPage && ( headExtra =
+ CKEDITOR.tools.buildStyleHtml( editor.config.contentsCss ) +
+ headExtra );
+
+ var baseTag = config.baseHref ? '<base href="' + config.baseHref + '" data-cke-temp="1" />' : '';
+
+ if ( fullPage )
+ {
+ // Search and sweep out the doctype declaration.
+ data = data.replace( /<!DOCTYPE[^>]*>/i, function( match )
+ {
+ editor.docType = docType = match;
+ return '';
+ }).replace( /<\?xml\s[^\?]*\?>/i, function( match )
+ {
+ editor.xmlDeclaration = match;
+ return '';
+ });
+ }
+
+ // Get the HTML version of the data.
+ if ( editor.dataProcessor )
+ data = editor.dataProcessor.toHtml( data, fixForBody );
+
+ if ( fullPage )
+ {
+ // Check if the <body> tag is available.
+ if ( !(/<body[\s|>]/).test( data ) )
+ data = '<body>' + data;
+
+ // Check if the <html> tag is available.
+ if ( !(/<html[\s|>]/).test( data ) )
+ data = '<html>' + data + '</html>';
+
+ // Check if the <head> tag is available.
+ if ( !(/<head[\s|>]/).test( data ) )
+ data = data.replace( /<html[^>]*>/, '$&<head><title></title></head>' ) ;
+ else if ( !(/<title[\s|>]/).test( data ) )
+ data = data.replace( /<head[^>]*>/, '$&<title></title>' ) ;
+
+ // The base must be the first tag in the HEAD, e.g. to get relative
+ // links on styles.
+ baseTag && ( data = data.replace( /<head>/, '$&' + baseTag ) );
+
+ // Inject the extra stuff into <head>.
+ // Attention: do not change it before testing it well. (V2)
+ // This is tricky... if the head ends with <meta ... content type>,
+ // Firefox will break. But, it works if we place our extra stuff as
+ // the last elements in the HEAD.
+ data = data.replace( /<\/head\s*>/, headExtra + '$&' );
+
+ // Add the DOCTYPE back to it.
+ data = docType + data;
+ }
+ else
+ {
+ data =
+ config.docType +
+ '<html dir="' + config.contentsLangDirection + '"' +
+ ' lang="' + ( config.contentsLanguage || editor.langCode ) + '">' +
+ '<head>' +
+ '<title>' + frameLabel + '</title>' +
+ baseTag +
+ headExtra +
+ '</head>' +
+ '<body' + ( config.bodyId ? ' id="' + config.bodyId + '"' : '' ) +
+ ( config.bodyClass ? ' class="' + config.bodyClass + '"' : '' ) +
+ '>' +
+ data +
+ '</html>';
+ }
+
+ // Distinguish bogus to normal BR at the end of document for Mozilla. (#5293).
+ if ( CKEDITOR.env.gecko )
+ data = data.replace( /<br \/>(?=\s*<\/(:?html|body)>)/, '$&<br type="_moz" />' );
+
+ data += activationScript;
+
+
+ // The iframe is recreated on each call of setData, so we need to clear DOM objects
+ this.onDispose();
+ createIFrame( data );
+ },
+
+ getData : function()
+ {
+ var config = editor.config,
+ fullPage = config.fullPage,
+ docType = fullPage && editor.docType,
+ xmlDeclaration = fullPage && editor.xmlDeclaration,
+ doc = iframe.getFrameDocument();
+
+ var data = fullPage
+ ? doc.getDocumentElement().getOuterHtml()
+ : doc.getBody().getHtml();
+
+ // BR at the end of document is bogus node for Mozilla. (#5293).
+ if ( CKEDITOR.env.gecko )
+ data = data.replace( /<br>(?=\s*(:?$|<\/body>))/, '' );
+
+ if ( editor.dataProcessor )
+ data = editor.dataProcessor.toDataFormat( data, fixForBody );
+
+ // Reset empty if the document contains only one empty paragraph.
+ if ( config.ignoreEmptyParagraph )
+ data = data.replace( emptyParagraphRegexp, function( match, lookback ) { return lookback; } );
+
+ if ( xmlDeclaration )
+ data = xmlDeclaration + '\n' + data;
+ if ( docType )
+ data = docType + '\n' + data;
+
+ return data;
+ },
+
+ getSnapshotData : function()
+ {
+ return iframe.getFrameDocument().getBody().getHtml();
+ },
+
+ loadSnapshotData : function( data )
+ {
+ iframe.getFrameDocument().getBody().setHtml( data );
+ },
+
+ onDispose : function()
+ {
+ if ( !editor.document )
+ return;
+
+ editor.document.getDocumentElement().clearCustomData();
+ editor.document.getBody().clearCustomData();
+
+ editor.window.clearCustomData();
+ editor.document.clearCustomData();
+
+ iframe.clearCustomData();
+
+ /*
+ * IE BUG: When destroying editor DOM with the selection remains inside
+ * editing area would break IE7/8's selection system, we have to put the editing
+ * iframe offline first. (#3812 and #5441)
+ */
+ iframe.remove();
+ },
+
+ unload : function( holderElement )
+ {
+ this.onDispose();
+
+ if ( onResize )
+ win.removeListener( 'resize', onResize );
+
+ editor.window = editor.document = iframe = mainElement = isPendingFocus = null;
+
+ editor.fire( 'contentDomUnload' );
+ },
+
+ focus : function()
+ {
+ var win = editor.window;
+
+ if ( isLoadingData )
+ isPendingFocus = true;
+ else if ( win )
+ {
+ // AIR needs a while to focus when moving from a link.
+ CKEDITOR.env.air ? setTimeout( function () { win.focus(); }, 0 ) : win.focus();
+ editor.selectionChange();
+ }
+ }
+ });
+
+ editor.on( 'insertHtml', onInsert( doInsertHtml ) , null, null, 20 );
+ editor.on( 'insertElement', onInsert( doInsertElement ), null, null, 20 );
+ editor.on( 'insertText', onInsert( doInsertText ), null, null, 20 );
+ // Auto fixing on some document structure weakness to enhance usabilities. (#3190 and #3189)
+ editor.on( 'selectionChange', function( evt )
+ {
+ if ( editor.readOnly )
+ return;
+
+ var sel = editor.getSelection();
+ // Do it only when selection is not locked. (#8222)
+ if ( sel && !sel.isLocked )
+ {
+ var isDirty = editor.checkDirty();
+ editor.fire( 'saveSnapshot', { contentOnly : 1 } );
+ onSelectionChangeFixBody.call( this, evt );
+ editor.fire( 'updateSnapshot' );
+ !isDirty && editor.resetDirty();
+ }
+
+ }, null, null, 1 );
+ });
+
+ var titleBackup;
+ // Setting voice label as window title, backup the original one
+ // and restore it before running into use.
+ editor.on( 'contentDom', function()
+ {
+ var title = editor.document.getElementsByTag( 'title' ).getItem( 0 );
+ title.data( 'cke-title', editor.document.$.title );
+ editor.document.$.title = frameLabel;
+ });
+
+ editor.on( 'readOnly', function()
+ {
+ if ( editor.mode == 'wysiwyg' )
+ {
+ // Symply reload the wysiwyg area. It'll take care of read-only.
+ var wysiwyg = editor.getMode();
+ wysiwyg.loadData( wysiwyg.getData() );
+ }
+ });
+
+ // IE>=8 stricts mode doesn't have 'contentEditable' in effect
+ // on element unless it has layout. (#5562)
+ if ( CKEDITOR.document.$.documentMode >= 8 )
+ {
+ editor.addCss( 'html.CSS1Compat [contenteditable=false]{ min-height:0 !important;}' );
+
+ var selectors = [];
+ for ( var tag in CKEDITOR.dtd.$removeEmpty )
+ selectors.push( 'html.CSS1Compat ' + tag + '[contenteditable=false]' );
+ editor.addCss( selectors.join( ',' ) + '{ display:inline-block;}' );
+ }
+ // Set the HTML style to 100% to have the text cursor in affect (#6341)
+ else if ( CKEDITOR.env.gecko )
+ {
+ editor.addCss( 'html { height: 100% !important; }' );
+ editor.addCss( 'img:-moz-broken { -moz-force-broken-image-icon : 1; width : 24px; height : 24px; }' );
+ }
+ // Remove the margin to avoid mouse confusion. (#8835)
+ else if ( CKEDITOR.env.ie && CKEDITOR.env.version < 8 && editor.config.contentsLangDirection == 'ltr' )
+ editor.addCss( 'body{margin-right:0;}' );
+
+ /* #3658: [IE6] Editor document has horizontal scrollbar on long lines
+ To prevent this misbehavior, we show the scrollbar always */
+ /* #6341: The text cursor must be set on the editor area. */
+ /* #6632: Avoid having "text" shape of cursor in IE7 scrollbars.*/
+ editor.addCss( 'html { _overflow-y: scroll; cursor: text; *cursor:auto;}' );
+ // Use correct cursor for these elements
+ editor.addCss( 'img, input, textarea { cursor: default;}' );
+
+ // Switch on design mode for a short while and close it after then.
+ function blinkCursor( retry )
+ {
+ if ( editor.readOnly )
+ return;
+
+ CKEDITOR.tools.tryThese(
+ function()
+ {
+ editor.document.$.designMode = 'on';
+ setTimeout( function()
+ {
+ editor.document.$.designMode = 'off';
+ if ( CKEDITOR.currentInstance == editor )
+ editor.document.getBody().focus();
+ }, 50 );
+ },
+ function()
+ {
+ // The above call is known to fail when parent DOM
+ // tree layout changes may break design mode. (#5782)
+ // Refresh the 'contentEditable' is a cue to this.
+ editor.document.$.designMode = 'off';
+ var body = editor.document.getBody();
+ body.setAttribute( 'contentEditable', false );
+ body.setAttribute( 'contentEditable', true );
+ // Try it again once..
+ !retry && blinkCursor( 1 );
+ });
+ }
+
+ // Disable form elements editing mode provided by some browers. (#5746)
+ editor.on( 'insertElement', function ( evt )
+ {
+ var element = evt.data;
+ if ( element.type == CKEDITOR.NODE_ELEMENT
+ && ( element.is( 'input' ) || element.is( 'textarea' ) ) )
+ {
+ // We should flag that the element was locked by our code so
+ // it'll be editable by the editor functions (#6046).
+ var readonly = element.getAttribute( 'contenteditable' ) == 'false';
+ if ( !readonly )
+ {
+ element.data( 'cke-editable', element.hasAttribute( 'contenteditable' ) ? 'true' : '1' );
+ element.setAttribute( 'contenteditable', false );
+ }
+ }
+ });
+
+ }
+ });
+
+ // Fixing Firefox 'Back-Forward Cache' break design mode. (#4514)
+ if ( CKEDITOR.env.gecko )
+ {
+ (function()
+ {
+ var body = document.body;
+
+ if ( !body )
+ window.addEventListener( 'load', arguments.callee, false );
+ else
+ {
+ var currentHandler = body.getAttribute( 'onpageshow' );
+ body.setAttribute( 'onpageshow', ( currentHandler ? currentHandler + ';' : '') +
+ 'event.persisted && (function(){' +
+ 'var allInstances = CKEDITOR.instances, editor, doc;' +
+ 'for ( var i in allInstances )' +
+ '{' +
+ ' editor = allInstances[ i ];' +
+ ' doc = editor.document;' +
+ ' if ( doc )' +
+ ' {' +
+ ' doc.$.designMode = "off";' +
+ ' doc.$.designMode = "on";' +
+ ' }' +
+ '}' +
+ '})();' );
+ }
+ } )();
+
+ }
+})();
+
+/**
+ * Disables the ability of resize objects (image and tables) in the editing
+ * area.
+ * @type Boolean
+ * @default false
+ * @example
+ * config.disableObjectResizing = true;
+ */
+CKEDITOR.config.disableObjectResizing = false;
+
+/**
+ * Disables the "table tools" offered natively by the browser (currently
+ * Firefox only) to make quick table editing operations, like adding or
+ * deleting rows and columns.
+ * @type Boolean
+ * @default true
+ * @example
+ * config.disableNativeTableHandles = false;
+ */
+CKEDITOR.config.disableNativeTableHandles = true;
+
+/**
+ * Disables the built-in words spell checker if browser provides one.<br /><br />
+ *
+ * <strong>Note:</strong> Although word suggestions provided by browsers (natively) will not appear in CKEditor's default context menu,
+ * users can always reach the native context menu by holding the <em>Ctrl</em> key when right-clicking if {@link CKEDITOR.config.browserContextMenuOnCtrl}
+ * is enabled or you're simply not using the context menu plugin.
+ *
+ * @type Boolean
+ * @default true
+ * @example
+ * config.disableNativeSpellChecker = false;
+ */
+CKEDITOR.config.disableNativeSpellChecker = true;
+
+/**
+ * Whether the editor must output an empty value ("") if it's contents is made
+ * by an empty paragraph only.
+ * @type Boolean
+ * @default true
+ * @example
+ * config.ignoreEmptyParagraph = false;
+ */
+CKEDITOR.config.ignoreEmptyParagraph = true;
+
+/**
+ * Fired when data is loaded and ready for retrieval in an editor instance.
+ * @name CKEDITOR.editor#dataReady
+ * @event
+ */
+
+/**
+ * Whether automatically create wrapping blocks around inline contents inside document body,
+ * this helps to ensure the integrality of the block enter mode.
+ * <strong>Note:</strong> Changing the default value might introduce unpredictable usability issues.
+ * @name CKEDITOR.config.autoParagraph
+ * @since 3.6
+ * @type Boolean
+ * @default true
+ * @example
+ * config.autoParagraph = false;
+ */
+
+/**
+ * Fired when some elements are added to the document
+ * @name CKEDITOR.editor#ariaWidget
+ * @event
+ * @param {Object} element The element being added
+ */
diff --git a/_source/plugins/xml/plugin.js b/_source/plugins/xml/plugin.js new file mode 100644 index 0000000..5581e8f --- /dev/null +++ b/_source/plugins/xml/plugin.js @@ -0,0 +1,170 @@ +/*
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the {@link CKEDITOR.xml} class, which represents a
+ * loaded XML document.
+ */
+
+(function()
+{
+ CKEDITOR.plugins.add( 'xml', {});
+
+ /**
+ * Represents a loaded XML document.
+ * @constructor
+ * @param {object|string} xmlObjectOrData A native XML (DOM document) object or
+ * a string containing the XML definition to be loaded.
+ * @example
+ * var xml = <b>new CKEDITOR.xml( '<books><book title="My Book" /></books>' )</b>;
+ */
+ CKEDITOR.xml = function( xmlObjectOrData )
+ {
+ var baseXml = null;
+
+ if ( typeof xmlObjectOrData == 'object' )
+ baseXml = xmlObjectOrData;
+ else
+ {
+ var data = ( xmlObjectOrData || '' ).replace( / /g, '\xA0' );
+ if ( window.DOMParser )
+ baseXml = (new DOMParser()).parseFromString( data, 'text/xml' );
+ else if ( window.ActiveXObject )
+ {
+ try { baseXml = new ActiveXObject( 'MSXML2.DOMDocument' ); }
+ catch(e)
+ {
+ try { baseXml = new ActiveXObject( 'Microsoft.XmlDom' ); } catch(e) {}
+ }
+
+ if ( baseXml )
+ {
+ baseXml.async = false;
+ baseXml.resolveExternals = false;
+ baseXml.validateOnParse = false;
+ baseXml.loadXML( data );
+ }
+ }
+ }
+
+ /**
+ * The native XML (DOM document) used by the class instance.
+ * @type object
+ * @example
+ */
+ this.baseXml = baseXml;
+ };
+
+ CKEDITOR.xml.prototype =
+ {
+ /**
+ * Get a single node from the XML document, based on a XPath query.
+ * @param {String} xpath The XPath query to execute.
+ * @param {Object} [contextNode] The XML DOM node to be used as the context
+ * for the XPath query. The document root is used by default.
+ * @returns {Object} A XML node element or null if the query has no results.
+ * @example
+ * // Create the XML instance.
+ * var xml = new CKEDITOR.xml( '<list><item id="test1" /><item id="test2" /></list>' );
+ * // Get the first <item> node.
+ * var itemNode = <b>xml.selectSingleNode( 'list/item' )</b>;
+ * // Alert "item".
+ * alert( itemNode.nodeName );
+ */
+ selectSingleNode : function( xpath, contextNode )
+ {
+ var baseXml = this.baseXml;
+
+ if ( contextNode || ( contextNode = baseXml ) )
+ {
+ if ( CKEDITOR.env.ie || contextNode.selectSingleNode ) // IE
+ return contextNode.selectSingleNode( xpath );
+ else if ( baseXml.evaluate ) // Others
+ {
+ var result = baseXml.evaluate( xpath, contextNode, null, 9, null);
+ return ( result && result.singleNodeValue ) || null;
+ }
+ }
+
+ return null;
+ },
+
+ /**
+ * Gets a list node from the XML document, based on a XPath query.
+ * @param {String} xpath The XPath query to execute.
+ * @param {Object} [contextNode] The XML DOM node to be used as the context
+ * for the XPath query. The document root is used by default.
+ * @returns {ArrayLike} An array containing all matched nodes. The array will
+ * be empty if the query has no results.
+ * @example
+ * // Create the XML instance.
+ * var xml = new CKEDITOR.xml( '<list><item id="test1" /><item id="test2" /></list>' );
+ * // Get the first <item> node.
+ * var itemNodes = xml.selectSingleNode( 'list/item' );
+ * // Alert "item" twice, one for each <item>.
+ * for ( var i = 0 ; i < itemNodes.length ; i++ )
+ * alert( itemNodes[i].nodeName );
+ */
+ selectNodes : function( xpath, contextNode )
+ {
+ var baseXml = this.baseXml,
+ nodes = [];
+
+ if ( contextNode || ( contextNode = baseXml ) )
+ {
+ if ( CKEDITOR.env.ie || contextNode.selectNodes ) // IE
+ return contextNode.selectNodes( xpath );
+ else if ( baseXml.evaluate ) // Others
+ {
+ var result = baseXml.evaluate( xpath, contextNode, null, 5, null);
+
+ if ( result )
+ {
+ var node;
+ while ( ( node = result.iterateNext() ) )
+ nodes.push( node );
+ }
+ }
+ }
+
+ return nodes;
+ },
+
+ /**
+ * Gets the string representation of hte inner contents of a XML node,
+ * based on a XPath query.
+ * @param {String} xpath The XPath query to execute.
+ * @param {Object} [contextNode] The XML DOM node to be used as the context
+ * for the XPath query. The document root is used by default.
+ * @returns {String} The textual representation of the inner contents of
+ * the node or null if the query has no results.
+ * @example
+ * // Create the XML instance.
+ * var xml = new CKEDITOR.xml( '<list><item id="test1" /><item id="test2" /></list>' );
+ * // Alert "<item id="test1" /><item id="test2" />".
+ * alert( xml.getInnerXml( 'list' ) );
+ */
+ getInnerXml : function( xpath, contextNode )
+ {
+ var node = this.selectSingleNode( xpath, contextNode ),
+ xml = [];
+ if ( node )
+ {
+ node = node.firstChild;
+ while ( node )
+ {
+ if ( node.xml ) // IE
+ xml.push( node.xml );
+ else if ( window.XMLSerializer ) // Others
+ xml.push( ( new XMLSerializer() ).serializeToString( node ) );
+
+ node = node.nextSibling;
+ }
+ }
+
+ return xml.length ? xml.join( '' ) : null;
+ }
+ };
+})();
|
