From 7adcb81e4f83f98c468889aaa5a85558ba88c770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isma=C3=ABl=20Bouya?= Date: Mon, 25 Jan 2016 17:45:33 +0100 Subject: Initial commit --- sources/plugins/magicline/dev/magicline.html | 594 +++++++ .../plugins/magicline/images/hidpi/icon-rtl.png | Bin 0 -> 176 bytes sources/plugins/magicline/images/hidpi/icon.png | Bin 0 -> 199 bytes sources/plugins/magicline/images/icon-rtl.png | Bin 0 -> 138 bytes sources/plugins/magicline/images/icon.png | Bin 0 -> 133 bytes sources/plugins/magicline/lang/af.js | 8 + sources/plugins/magicline/lang/ar.js | 8 + sources/plugins/magicline/lang/bg.js | 8 + sources/plugins/magicline/lang/ca.js | 8 + sources/plugins/magicline/lang/cs.js | 8 + sources/plugins/magicline/lang/cy.js | 8 + sources/plugins/magicline/lang/da.js | 8 + sources/plugins/magicline/lang/de.js | 8 + sources/plugins/magicline/lang/el.js | 8 + sources/plugins/magicline/lang/en-gb.js | 8 + sources/plugins/magicline/lang/en.js | 8 + sources/plugins/magicline/lang/eo.js | 8 + sources/plugins/magicline/lang/es.js | 8 + sources/plugins/magicline/lang/et.js | 8 + sources/plugins/magicline/lang/eu.js | 8 + sources/plugins/magicline/lang/fa.js | 8 + sources/plugins/magicline/lang/fi.js | 8 + sources/plugins/magicline/lang/fr-ca.js | 8 + sources/plugins/magicline/lang/fr.js | 8 + sources/plugins/magicline/lang/gl.js | 8 + sources/plugins/magicline/lang/he.js | 8 + sources/plugins/magicline/lang/hr.js | 8 + sources/plugins/magicline/lang/hu.js | 8 + sources/plugins/magicline/lang/id.js | 8 + sources/plugins/magicline/lang/it.js | 8 + sources/plugins/magicline/lang/ja.js | 8 + sources/plugins/magicline/lang/km.js | 8 + sources/plugins/magicline/lang/ko.js | 8 + sources/plugins/magicline/lang/ku.js | 8 + sources/plugins/magicline/lang/lv.js | 8 + sources/plugins/magicline/lang/nb.js | 8 + sources/plugins/magicline/lang/nl.js | 8 + sources/plugins/magicline/lang/no.js | 8 + sources/plugins/magicline/lang/pl.js | 8 + sources/plugins/magicline/lang/pt-br.js | 8 + sources/plugins/magicline/lang/pt.js | 8 + sources/plugins/magicline/lang/ru.js | 8 + sources/plugins/magicline/lang/si.js | 8 + sources/plugins/magicline/lang/sk.js | 8 + sources/plugins/magicline/lang/sl.js | 8 + sources/plugins/magicline/lang/sq.js | 8 + sources/plugins/magicline/lang/sv.js | 8 + sources/plugins/magicline/lang/tr.js | 8 + sources/plugins/magicline/lang/tt.js | 8 + sources/plugins/magicline/lang/ug.js | 8 + sources/plugins/magicline/lang/uk.js | 8 + sources/plugins/magicline/lang/vi.js | 8 + sources/plugins/magicline/lang/zh-cn.js | 8 + sources/plugins/magicline/lang/zh.js | 8 + sources/plugins/magicline/plugin.js | 1874 ++++++++++++++++++++ sources/plugins/magicline/samples/magicline.html | 209 +++ 56 files changed, 3069 insertions(+) create mode 100644 sources/plugins/magicline/dev/magicline.html create mode 100644 sources/plugins/magicline/images/hidpi/icon-rtl.png create mode 100644 sources/plugins/magicline/images/hidpi/icon.png create mode 100644 sources/plugins/magicline/images/icon-rtl.png create mode 100644 sources/plugins/magicline/images/icon.png create mode 100644 sources/plugins/magicline/lang/af.js create mode 100644 sources/plugins/magicline/lang/ar.js create mode 100644 sources/plugins/magicline/lang/bg.js create mode 100644 sources/plugins/magicline/lang/ca.js create mode 100644 sources/plugins/magicline/lang/cs.js create mode 100644 sources/plugins/magicline/lang/cy.js create mode 100644 sources/plugins/magicline/lang/da.js create mode 100644 sources/plugins/magicline/lang/de.js create mode 100644 sources/plugins/magicline/lang/el.js create mode 100644 sources/plugins/magicline/lang/en-gb.js create mode 100644 sources/plugins/magicline/lang/en.js create mode 100644 sources/plugins/magicline/lang/eo.js create mode 100644 sources/plugins/magicline/lang/es.js create mode 100644 sources/plugins/magicline/lang/et.js create mode 100644 sources/plugins/magicline/lang/eu.js create mode 100644 sources/plugins/magicline/lang/fa.js create mode 100644 sources/plugins/magicline/lang/fi.js create mode 100644 sources/plugins/magicline/lang/fr-ca.js create mode 100644 sources/plugins/magicline/lang/fr.js create mode 100644 sources/plugins/magicline/lang/gl.js create mode 100644 sources/plugins/magicline/lang/he.js create mode 100644 sources/plugins/magicline/lang/hr.js create mode 100644 sources/plugins/magicline/lang/hu.js create mode 100644 sources/plugins/magicline/lang/id.js create mode 100644 sources/plugins/magicline/lang/it.js create mode 100644 sources/plugins/magicline/lang/ja.js create mode 100644 sources/plugins/magicline/lang/km.js create mode 100644 sources/plugins/magicline/lang/ko.js create mode 100644 sources/plugins/magicline/lang/ku.js create mode 100644 sources/plugins/magicline/lang/lv.js create mode 100644 sources/plugins/magicline/lang/nb.js create mode 100644 sources/plugins/magicline/lang/nl.js create mode 100644 sources/plugins/magicline/lang/no.js create mode 100644 sources/plugins/magicline/lang/pl.js create mode 100644 sources/plugins/magicline/lang/pt-br.js create mode 100644 sources/plugins/magicline/lang/pt.js create mode 100644 sources/plugins/magicline/lang/ru.js create mode 100644 sources/plugins/magicline/lang/si.js create mode 100644 sources/plugins/magicline/lang/sk.js create mode 100644 sources/plugins/magicline/lang/sl.js create mode 100644 sources/plugins/magicline/lang/sq.js create mode 100644 sources/plugins/magicline/lang/sv.js create mode 100644 sources/plugins/magicline/lang/tr.js create mode 100644 sources/plugins/magicline/lang/tt.js create mode 100644 sources/plugins/magicline/lang/ug.js create mode 100644 sources/plugins/magicline/lang/uk.js create mode 100644 sources/plugins/magicline/lang/vi.js create mode 100644 sources/plugins/magicline/lang/zh-cn.js create mode 100644 sources/plugins/magicline/lang/zh.js create mode 100644 sources/plugins/magicline/plugin.js create mode 100644 sources/plugins/magicline/samples/magicline.html (limited to 'sources/plugins/magicline') diff --git a/sources/plugins/magicline/dev/magicline.html b/sources/plugins/magicline/dev/magicline.html new file mode 100644 index 00000000..84e59474 --- /dev/null +++ b/sources/plugins/magicline/dev/magicline.html @@ -0,0 +1,594 @@ + + + + + + Magicline muddy trenches – CKEditor Sample + + + + + +

+ CKEditor Sample — magicline muddy trenches +

+ +

Various cases

+ + +

Odd case: first (last) element at the very beginning, short editable

+ + +

Large document, put everywhere

+ + +

Deeply nested divs

+ + +

Line custom look

+
+

+ Little Red Riding Hood

+

+ "Little Red Riding Hood" is a famous fairy tale about a young girl's encounter with a wolf. The story has been changed considerably in its history and subject to numerous modern adaptations and readings.

+ + + + + + + + + + + + + + + + +
+ International Names
+ Chinese + 小紅帽
+ Italian + Cappuccetto Rosso
+ Spanish + Caperucita Roja
+
+
+

+ The version most widely known today is based on the Brothers Grimm variant. It is about a girl called Little Red Riding Hood, after the red hooded cape or cloak she wears. The girl walks through the woods to deliver food to her sick grandmother.

+

+ A wolf wants to eat the girl but is afraid to do so in public. He approaches the girl, and she naïvely tells him where she is going. He suggests the girl pick some flowers, which she does. In the meantime, he goes to the grandmother's house and gains entry by pretending to be the girl. He swallows the grandmother whole, and waits for the girl, disguised as the grandmother.

+

+ When the girl arrives, she notices he looks very strange to be her grandma. In most retellings, this eventually culminates with Little Red Riding Hood saying, "My, what big teeth you have!"
+ To which the wolf replies, "The better to eat you with," and swallows her whole, too.

+

+ A hunter, however, comes to the rescue and cuts the wolf open. Little Red Riding Hood and her grandmother emerge unharmed. They fill the wolf's body with heavy stones, which drown him when he falls into a well. Other versions of the story have had the grandmother shut in the closet instead of eaten, and some have Little Red Riding Hood saved by the hunter as the wolf advances on her rather than after she is eaten.

+

+ The tale makes the clearest contrast between the safe world of the village and the dangers of the forest, conventional antitheses that are essentially medieval, though no written versions are as old as that.

+
+ +

Extreme inline editing

+
+
Lorem ipsum dolor sit amet enim. Etiam ullamcorper. Suspendisse a pellentesque dui, non felis. Maecenas malesuada elit lectus felis, malesuada ultricies. Curabitur et ligula. Ut molestie a, ultricies porta urna. Vestibulum commodo volutpat a, convallis ac, laoreet enim.
+
+ Position static +
foo
+
+
+
Key
Value
+
+
Whatever
+
+

Lorem ipsum dolor sit amet enim. Etiam ullamcorper. Suspendisse a pellentesque dui, non felis. Maecenas malesuada elit lectus felis, malesuada ultricies

+
+
+

Lorem ipsum dolor sit amet enim. Etiam ullamcorper. Suspendisse a pellentesque dui, non felis. Maecenas malesuada elit lectus felis, malesuada ultricies

+
foo
+
+ +

Enter mode: BR

+ + +
+

+ Mouse over: + Mouse Y-pos.: +

+

+ + + + + +

Time:

+

Hidden state:

+
+ + + diff --git a/sources/plugins/magicline/images/hidpi/icon-rtl.png b/sources/plugins/magicline/images/hidpi/icon-rtl.png new file mode 100644 index 00000000..4a8d2bfd Binary files /dev/null and b/sources/plugins/magicline/images/hidpi/icon-rtl.png differ diff --git a/sources/plugins/magicline/images/hidpi/icon.png b/sources/plugins/magicline/images/hidpi/icon.png new file mode 100644 index 00000000..b981bb5c Binary files /dev/null and b/sources/plugins/magicline/images/hidpi/icon.png differ diff --git a/sources/plugins/magicline/images/icon-rtl.png b/sources/plugins/magicline/images/icon-rtl.png new file mode 100644 index 00000000..55b5b5f9 Binary files /dev/null and b/sources/plugins/magicline/images/icon-rtl.png differ diff --git a/sources/plugins/magicline/images/icon.png b/sources/plugins/magicline/images/icon.png new file mode 100644 index 00000000..e0634336 Binary files /dev/null and b/sources/plugins/magicline/images/icon.png differ diff --git a/sources/plugins/magicline/lang/af.js b/sources/plugins/magicline/lang/af.js new file mode 100644 index 00000000..0bca7589 --- /dev/null +++ b/sources/plugins/magicline/lang/af.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'af', { + title: 'Voeg paragraaf hier in' +} ); diff --git a/sources/plugins/magicline/lang/ar.js b/sources/plugins/magicline/lang/ar.js new file mode 100644 index 00000000..806bde8e --- /dev/null +++ b/sources/plugins/magicline/lang/ar.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'ar', { + title: 'إدراج فقرة هنا' +} ); diff --git a/sources/plugins/magicline/lang/bg.js b/sources/plugins/magicline/lang/bg.js new file mode 100644 index 00000000..235e415e --- /dev/null +++ b/sources/plugins/magicline/lang/bg.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'bg', { + title: 'Вмъкнете параграф тук' +} ); diff --git a/sources/plugins/magicline/lang/ca.js b/sources/plugins/magicline/lang/ca.js new file mode 100644 index 00000000..8ecc84c1 --- /dev/null +++ b/sources/plugins/magicline/lang/ca.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'ca', { + title: 'Insereix el paràgraf aquí' +} ); diff --git a/sources/plugins/magicline/lang/cs.js b/sources/plugins/magicline/lang/cs.js new file mode 100644 index 00000000..7dedde35 --- /dev/null +++ b/sources/plugins/magicline/lang/cs.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'cs', { + title: 'zde vložit odstavec' +} ); diff --git a/sources/plugins/magicline/lang/cy.js b/sources/plugins/magicline/lang/cy.js new file mode 100644 index 00000000..cce06efd --- /dev/null +++ b/sources/plugins/magicline/lang/cy.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'cy', { + title: 'Mewnosod paragraff yma' +} ); diff --git a/sources/plugins/magicline/lang/da.js b/sources/plugins/magicline/lang/da.js new file mode 100644 index 00000000..87885710 --- /dev/null +++ b/sources/plugins/magicline/lang/da.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'da', { + title: 'Indsæt afsnit' +} ); diff --git a/sources/plugins/magicline/lang/de.js b/sources/plugins/magicline/lang/de.js new file mode 100644 index 00000000..ff7ca340 --- /dev/null +++ b/sources/plugins/magicline/lang/de.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'de', { + title: 'Absatz hier einfügen' +} ); diff --git a/sources/plugins/magicline/lang/el.js b/sources/plugins/magicline/lang/el.js new file mode 100644 index 00000000..e1de7d82 --- /dev/null +++ b/sources/plugins/magicline/lang/el.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'el', { + title: 'Εισάγετε παράγραφο εδώ' +} ); diff --git a/sources/plugins/magicline/lang/en-gb.js b/sources/plugins/magicline/lang/en-gb.js new file mode 100644 index 00000000..3c469afa --- /dev/null +++ b/sources/plugins/magicline/lang/en-gb.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'en-gb', { + title: 'Insert paragraph here' +} ); diff --git a/sources/plugins/magicline/lang/en.js b/sources/plugins/magicline/lang/en.js new file mode 100644 index 00000000..180e0c83 --- /dev/null +++ b/sources/plugins/magicline/lang/en.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'en', { + title: 'Insert paragraph here' +} ); diff --git a/sources/plugins/magicline/lang/eo.js b/sources/plugins/magicline/lang/eo.js new file mode 100644 index 00000000..08a9c952 --- /dev/null +++ b/sources/plugins/magicline/lang/eo.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'eo', { + title: 'Enmeti paragrafon ĉi-tien' +} ); diff --git a/sources/plugins/magicline/lang/es.js b/sources/plugins/magicline/lang/es.js new file mode 100644 index 00000000..6a01bf5c --- /dev/null +++ b/sources/plugins/magicline/lang/es.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'es', { + title: 'Insertar párrafo aquí' +} ); diff --git a/sources/plugins/magicline/lang/et.js b/sources/plugins/magicline/lang/et.js new file mode 100644 index 00000000..fd93f8f5 --- /dev/null +++ b/sources/plugins/magicline/lang/et.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'et', { + title: 'Sisesta siia lõigu tekst' +} ); diff --git a/sources/plugins/magicline/lang/eu.js b/sources/plugins/magicline/lang/eu.js new file mode 100644 index 00000000..c528787e --- /dev/null +++ b/sources/plugins/magicline/lang/eu.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'eu', { + title: 'Txertatu paragrafoa hemen' +} ); diff --git a/sources/plugins/magicline/lang/fa.js b/sources/plugins/magicline/lang/fa.js new file mode 100644 index 00000000..73756d64 --- /dev/null +++ b/sources/plugins/magicline/lang/fa.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'fa', { + title: 'قرار دادن بند در اینجا' +} ); diff --git a/sources/plugins/magicline/lang/fi.js b/sources/plugins/magicline/lang/fi.js new file mode 100644 index 00000000..7f2eb8d5 --- /dev/null +++ b/sources/plugins/magicline/lang/fi.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'fi', { + title: 'Lisää kappale tähän.' +} ); diff --git a/sources/plugins/magicline/lang/fr-ca.js b/sources/plugins/magicline/lang/fr-ca.js new file mode 100644 index 00000000..aed21a8b --- /dev/null +++ b/sources/plugins/magicline/lang/fr-ca.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'fr-ca', { + title: 'Insérer le paragraphe ici' +} ); diff --git a/sources/plugins/magicline/lang/fr.js b/sources/plugins/magicline/lang/fr.js new file mode 100644 index 00000000..b14996c6 --- /dev/null +++ b/sources/plugins/magicline/lang/fr.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'fr', { + title: 'Insérez un paragraphe ici' +} ); diff --git a/sources/plugins/magicline/lang/gl.js b/sources/plugins/magicline/lang/gl.js new file mode 100644 index 00000000..a6d0cabc --- /dev/null +++ b/sources/plugins/magicline/lang/gl.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'gl', { + title: 'Inserir aquí o parágrafo' +} ); diff --git a/sources/plugins/magicline/lang/he.js b/sources/plugins/magicline/lang/he.js new file mode 100644 index 00000000..b6f90e50 --- /dev/null +++ b/sources/plugins/magicline/lang/he.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'he', { + title: 'הכנס פסקה כאן' +} ); diff --git a/sources/plugins/magicline/lang/hr.js b/sources/plugins/magicline/lang/hr.js new file mode 100644 index 00000000..503051d5 --- /dev/null +++ b/sources/plugins/magicline/lang/hr.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'hr', { + title: 'Ubaci paragraf ovdje' +} ); diff --git a/sources/plugins/magicline/lang/hu.js b/sources/plugins/magicline/lang/hu.js new file mode 100644 index 00000000..0e83cf8f --- /dev/null +++ b/sources/plugins/magicline/lang/hu.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'hu', { + title: 'Szúrja be a bekezdést ide' +} ); diff --git a/sources/plugins/magicline/lang/id.js b/sources/plugins/magicline/lang/id.js new file mode 100644 index 00000000..5594ca60 --- /dev/null +++ b/sources/plugins/magicline/lang/id.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'id', { + title: 'Masukkan paragraf disini' +} ); diff --git a/sources/plugins/magicline/lang/it.js b/sources/plugins/magicline/lang/it.js new file mode 100644 index 00000000..bc478261 --- /dev/null +++ b/sources/plugins/magicline/lang/it.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'it', { + title: 'Inserisci paragrafo qui' +} ); diff --git a/sources/plugins/magicline/lang/ja.js b/sources/plugins/magicline/lang/ja.js new file mode 100644 index 00000000..2f2efe1f --- /dev/null +++ b/sources/plugins/magicline/lang/ja.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'ja', { + title: 'ここに段落を挿入' +} ); diff --git a/sources/plugins/magicline/lang/km.js b/sources/plugins/magicline/lang/km.js new file mode 100644 index 00000000..f9c47321 --- /dev/null +++ b/sources/plugins/magicline/lang/km.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'km', { + title: 'បញ្ចូល​កថាខណ្ឌ​នៅ​ទីនេះ' +} ); diff --git a/sources/plugins/magicline/lang/ko.js b/sources/plugins/magicline/lang/ko.js new file mode 100644 index 00000000..902060b6 --- /dev/null +++ b/sources/plugins/magicline/lang/ko.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'ko', { + title: '여기에 단락 삽입' +} ); diff --git a/sources/plugins/magicline/lang/ku.js b/sources/plugins/magicline/lang/ku.js new file mode 100644 index 00000000..94e8c760 --- /dev/null +++ b/sources/plugins/magicline/lang/ku.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'ku', { + title: 'بڕگە لێرە دابنێ' +} ); diff --git a/sources/plugins/magicline/lang/lv.js b/sources/plugins/magicline/lang/lv.js new file mode 100644 index 00000000..6dcfb82c --- /dev/null +++ b/sources/plugins/magicline/lang/lv.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'lv', { + title: 'Ievietot šeit rindkopu' +} ); diff --git a/sources/plugins/magicline/lang/nb.js b/sources/plugins/magicline/lang/nb.js new file mode 100644 index 00000000..36a3ad55 --- /dev/null +++ b/sources/plugins/magicline/lang/nb.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'nb', { + title: 'Sett inn nytt avsnitt her' +} ); diff --git a/sources/plugins/magicline/lang/nl.js b/sources/plugins/magicline/lang/nl.js new file mode 100644 index 00000000..95d76caf --- /dev/null +++ b/sources/plugins/magicline/lang/nl.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'nl', { + title: 'Hier paragraaf invoeren' +} ); diff --git a/sources/plugins/magicline/lang/no.js b/sources/plugins/magicline/lang/no.js new file mode 100644 index 00000000..e0f414cb --- /dev/null +++ b/sources/plugins/magicline/lang/no.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'no', { + title: 'Sett inn nytt avsnitt her' +} ); diff --git a/sources/plugins/magicline/lang/pl.js b/sources/plugins/magicline/lang/pl.js new file mode 100644 index 00000000..6c1c6d0d --- /dev/null +++ b/sources/plugins/magicline/lang/pl.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'pl', { + title: 'Wstaw nowy akapit' +} ); diff --git a/sources/plugins/magicline/lang/pt-br.js b/sources/plugins/magicline/lang/pt-br.js new file mode 100644 index 00000000..bee3c383 --- /dev/null +++ b/sources/plugins/magicline/lang/pt-br.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'pt-br', { + title: 'Insera um parágrafo aqui' +} ); diff --git a/sources/plugins/magicline/lang/pt.js b/sources/plugins/magicline/lang/pt.js new file mode 100644 index 00000000..0f40e616 --- /dev/null +++ b/sources/plugins/magicline/lang/pt.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'pt', { + title: 'Insira aqui o parágrafo' +} ); diff --git a/sources/plugins/magicline/lang/ru.js b/sources/plugins/magicline/lang/ru.js new file mode 100644 index 00000000..645d50da --- /dev/null +++ b/sources/plugins/magicline/lang/ru.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'ru', { + title: 'Вставить здесь параграф' +} ); diff --git a/sources/plugins/magicline/lang/si.js b/sources/plugins/magicline/lang/si.js new file mode 100644 index 00000000..4c134131 --- /dev/null +++ b/sources/plugins/magicline/lang/si.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'si', { + title: 'චේදය ඇතුලත් කරන්න' +} ); diff --git a/sources/plugins/magicline/lang/sk.js b/sources/plugins/magicline/lang/sk.js new file mode 100644 index 00000000..8473e68d --- /dev/null +++ b/sources/plugins/magicline/lang/sk.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'sk', { + title: 'Sem vložte paragraf' +} ); diff --git a/sources/plugins/magicline/lang/sl.js b/sources/plugins/magicline/lang/sl.js new file mode 100644 index 00000000..fab392c5 --- /dev/null +++ b/sources/plugins/magicline/lang/sl.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'sl', { + title: 'Vstavite odstavek tukaj' +} ); diff --git a/sources/plugins/magicline/lang/sq.js b/sources/plugins/magicline/lang/sq.js new file mode 100644 index 00000000..4b83c92a --- /dev/null +++ b/sources/plugins/magicline/lang/sq.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'sq', { + title: 'Vendos paragraf këtu' +} ); diff --git a/sources/plugins/magicline/lang/sv.js b/sources/plugins/magicline/lang/sv.js new file mode 100644 index 00000000..aa0e5487 --- /dev/null +++ b/sources/plugins/magicline/lang/sv.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'sv', { + title: 'Infoga paragraf här' +} ); diff --git a/sources/plugins/magicline/lang/tr.js b/sources/plugins/magicline/lang/tr.js new file mode 100644 index 00000000..cf12ff28 --- /dev/null +++ b/sources/plugins/magicline/lang/tr.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'tr', { + title: 'Parağrafı buraya ekle' +} ); diff --git a/sources/plugins/magicline/lang/tt.js b/sources/plugins/magicline/lang/tt.js new file mode 100644 index 00000000..bc2c94a9 --- /dev/null +++ b/sources/plugins/magicline/lang/tt.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'tt', { + title: 'Бирегә параграф өстәү' +} ); diff --git a/sources/plugins/magicline/lang/ug.js b/sources/plugins/magicline/lang/ug.js new file mode 100644 index 00000000..653bc5fb --- /dev/null +++ b/sources/plugins/magicline/lang/ug.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'ug', { + title: 'بۇ جايغا ئابزاس قىستۇر' +} ); diff --git a/sources/plugins/magicline/lang/uk.js b/sources/plugins/magicline/lang/uk.js new file mode 100644 index 00000000..efd589a6 --- /dev/null +++ b/sources/plugins/magicline/lang/uk.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'uk', { + title: 'Вставити абзац' +} ); diff --git a/sources/plugins/magicline/lang/vi.js b/sources/plugins/magicline/lang/vi.js new file mode 100644 index 00000000..29156c3b --- /dev/null +++ b/sources/plugins/magicline/lang/vi.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'vi', { + title: 'Chèn đoạn vào đây' +} ); diff --git a/sources/plugins/magicline/lang/zh-cn.js b/sources/plugins/magicline/lang/zh-cn.js new file mode 100644 index 00000000..2990f181 --- /dev/null +++ b/sources/plugins/magicline/lang/zh-cn.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'zh-cn', { + title: '在这插入段落' +} ); diff --git a/sources/plugins/magicline/lang/zh.js b/sources/plugins/magicline/lang/zh.js new file mode 100644 index 00000000..3c06265b --- /dev/null +++ b/sources/plugins/magicline/lang/zh.js @@ -0,0 +1,8 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +CKEDITOR.plugins.setLang( 'magicline', 'zh', { + title: '在此插入段落' +} ); diff --git a/sources/plugins/magicline/plugin.js b/sources/plugins/magicline/plugin.js new file mode 100644 index 00000000..0a2d9fbe --- /dev/null +++ b/sources/plugins/magicline/plugin.js @@ -0,0 +1,1874 @@ +/** + * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or http://ckeditor.com/license + */ + +/** + * @fileOverview The [Magic Line](http://ckeditor.com/addon/magicline) plugin that makes it easier to access some document areas that + * are difficult to focus. + */ + +'use strict'; + +( function() { + CKEDITOR.plugins.add( 'magicline', { + lang: 'af,ar,bg,ca,cs,cy,da,de,el,en,en-gb,eo,es,et,eu,fa,fi,fr,fr-ca,gl,he,hr,hu,id,it,ja,km,ko,ku,lv,nb,nl,no,pl,pt,pt-br,ru,si,sk,sl,sq,sv,tr,tt,ug,uk,vi,zh,zh-cn', // %REMOVE_LINE_CORE% + init: initPlugin + } ); + + // Activates the box inside of an editor. + function initPlugin( editor ) { + // Configurables + var config = editor.config, + triggerOffset = config.magicline_triggerOffset || 30, + enterMode = config.enterMode, + that = { + // Global stuff is being initialized here. + editor: editor, + enterMode: enterMode, + triggerOffset: triggerOffset, + holdDistance: 0 | triggerOffset * ( config.magicline_holdDistance || 0.5 ), + boxColor: config.magicline_color || '#ff0000', + rtl: config.contentsLangDirection == 'rtl', + tabuList: [ 'data-cke-hidden-sel' ].concat( config.magicline_tabuList || [] ), + triggers: config.magicline_everywhere ? DTD_BLOCK : { table: 1, hr: 1, div: 1, ul: 1, ol: 1, dl: 1, form: 1, blockquote: 1 } + }, + scrollTimeout, checkMouseTimeoutPending, checkMouseTimer; + + // %REMOVE_START% + // Internal DEBUG uses tools located in the topmost window. + + // (#9701) Due to security limitations some browsers may throw + // errors when accessing window.top object. Do it safely first then. + try { + that.debug = window.top.DEBUG; + } + catch ( e ) {} + + that.debug = that.debug || { + groupEnd: function() {}, + groupStart: function() {}, + log: function() {}, + logElements: function() {}, + logElementsEnd: function() {}, + logEnd: function() {}, + mousePos: function() {}, + showHidden: function() {}, + showTrigger: function() {}, + startTimer: function() {}, + stopTimer: function() {} + }; + // %REMOVE_END% + + // Simple irrelevant elements filter. + that.isRelevant = function( node ) { + return isHtml( node ) && // -> Node must be an existing HTML element. + !isLine( that, node ) && // -> Node can be neither the box nor its child. + !isFlowBreaker( node ); // -> Node can be neither floated nor positioned nor aligned. + }; + + editor.on( 'contentDom', addListeners, this ); + + function addListeners() { + var editable = editor.editable(), + doc = editor.document, + win = editor.window; + + // Global stuff is being initialized here. + extend( that, { + editable: editable, + inInlineMode: editable.isInline(), + doc: doc, + win: win, + hotNode: null + }, true ); + + // This is the boundary of the editor. For inline the boundary is editable itself. + // For classic (`iframe`-based) editor, the HTML element is a real boundary. + that.boundary = that.inInlineMode ? that.editable : that.doc.getDocumentElement(); + + // Enabling the box inside of inline editable is pointless. + // There's no need to access spaces inside paragraphs, links, spans, etc. + if ( editable.is( dtd.$inline ) ) + return; + + // Handle in-line editing by setting appropriate position. + // If current position is static, make it relative and clear top/left coordinates. + if ( that.inInlineMode && !isPositioned( editable ) ) { + editable.setStyles( { + position: 'relative', + top: null, + left: null + } ); + } + // Enable the box. Let it produce children elements, initialize + // event handlers and own methods. + initLine.call( this, that ); + + // Get view dimensions and scroll positions. + // At this stage (before any checkMouse call) it is used mostly + // by tests. Nevertheless it a crucial thing. + updateWindowSize( that ); + + // Remove the box before an undo image is created. + // This is important. If we didn't do that, the *undo thing* would revert the box into an editor. + // Thanks to that, undo doesn't even know about the existence of the box. + editable.attachListener( editor, 'beforeUndoImage', function() { + that.line.detach(); + } ); + + // Removes the box HTML from editor data string if getData is called. + // Thanks to that, an editor never yields data polluted by the box. + // Listen with very high priority, so line will be removed before other + // listeners will see it. + editable.attachListener( editor, 'beforeGetData', function() { + // If the box is in editable, remove it. + if ( that.line.wrap.getParent() ) { + that.line.detach(); + + // Restore line in the last listener for 'getData'. + editor.once( 'getData', function() { + that.line.attach(); + }, null, null, 1000 ); + } + }, null, null, 0 ); + + // Hide the box on mouseout if mouse leaves document. + editable.attachListener( that.inInlineMode ? doc : doc.getWindow().getFrame(), 'mouseout', function( event ) { + if ( editor.mode != 'wysiwyg' ) + return; + + // Check for inline-mode editor. If so, check mouse position + // and remove the box if mouse outside of an editor. + if ( that.inInlineMode ) { + var mouse = { + x: event.data.$.clientX, + y: event.data.$.clientY + }; + + updateWindowSize( that ); + updateEditableSize( that, true ); + + var size = that.view.editable, + scroll = that.view.scroll; + + // If outside of an editor... + if ( !inBetween( mouse.x, size.left - scroll.x, size.right - scroll.x ) || !inBetween( mouse.y, size.top - scroll.y, size.bottom - scroll.y ) ) { + clearTimeout( checkMouseTimer ); + checkMouseTimer = null; + that.line.detach(); + } + } + + else { + clearTimeout( checkMouseTimer ); + checkMouseTimer = null; + that.line.detach(); + } + } ); + + // This one deactivates hidden mode of an editor which + // prevents the box from being shown. + editable.attachListener( editable, 'keyup', function() { + that.hiddenMode = 0; + that.debug.showHidden( that.hiddenMode ); // %REMOVE_LINE% + } ); + + editable.attachListener( editable, 'keydown', function( event ) { + if ( editor.mode != 'wysiwyg' ) + return; + + var keyStroke = event.data.getKeystroke(); + + switch ( keyStroke ) { + // Shift pressed + case 2228240: // IE + case 16: + that.hiddenMode = 1; + that.line.detach(); + } + + that.debug.showHidden( that.hiddenMode ); // %REMOVE_LINE% + } ); + + // This method ensures that checkMouse aren't executed + // in parallel and no more frequently than specified in timeout function. + // In classic (`iframe`-based) editor, document is used as a trigger, to provide magicline + // functionality when mouse is below the body (short content, short body). + editable.attachListener( that.inInlineMode ? editable : doc, 'mousemove', function( event ) { + checkMouseTimeoutPending = true; + + if ( editor.mode != 'wysiwyg' || editor.readOnly || checkMouseTimer ) + return; + + // IE<9 requires this event-driven object to be created + // outside of the setTimeout statement. + // Otherwise it loses the event object with its properties. + var mouse = { + x: event.data.$.clientX, + y: event.data.$.clientY + }; + + checkMouseTimer = setTimeout( function() { + checkMouse( mouse ); + }, 30 ); // balances performance and accessibility + } ); + + // This one removes box on scroll event. + // It is to avoid box displacement. + editable.attachListener( win, 'scroll', function() { + if ( editor.mode != 'wysiwyg' ) + return; + + that.line.detach(); + + // To figure this out just look at the mouseup + // event handler below. + if ( env.webkit ) { + that.hiddenMode = 1; + + clearTimeout( scrollTimeout ); + scrollTimeout = setTimeout( function() { + // Don't leave hidden mode until mouse remains pressed and + // scroll is being used, i.e. when dragging something. + if ( !that.mouseDown ) + that.hiddenMode = 0; + that.debug.showHidden( that.hiddenMode ); // %REMOVE_LINE% + }, 50 ); + + that.debug.showHidden( that.hiddenMode ); // %REMOVE_LINE% + } + } ); + + // Those event handlers remove the box on mousedown + // and don't reveal it until the mouse is released. + // It is to prevent box insertion e.g. while scrolling + // (w/ scrollbar), selecting and so on. + editable.attachListener( env_ie8 ? doc : win, 'mousedown', function() { + if ( editor.mode != 'wysiwyg' ) + return; + + that.line.detach(); + that.hiddenMode = 1; + that.mouseDown = 1; + + that.debug.showHidden( that.hiddenMode ); // %REMOVE_LINE% + } ); + + // Google Chrome doesn't trigger this on the scrollbar (since 2009...) + // so it is totally useless to check for scroll finish + // see: http://code.google.com/p/chromium/issues/detail?id=14204 + editable.attachListener( env_ie8 ? doc : win, 'mouseup', function() { + that.hiddenMode = 0; + that.mouseDown = 0; + that.debug.showHidden( that.hiddenMode ); // %REMOVE_LINE% + } ); + + // Editor commands for accessing difficult focus spaces. + editor.addCommand( 'accessPreviousSpace', accessFocusSpaceCmd( that ) ); + editor.addCommand( 'accessNextSpace', accessFocusSpaceCmd( that, true ) ); + + editor.setKeystroke( [ + [ config.magicline_keystrokePrevious, 'accessPreviousSpace' ], + [ config.magicline_keystrokeNext, 'accessNextSpace' ] + ] ); + + // Revert magicline hot node on undo/redo. + editor.on( 'loadSnapshot', function() { + var elements, element, i; + + for ( var t in { p: 1, br: 1, div: 1 } ) { + // document.find is not available in QM (#11149). + elements = editor.document.getElementsByTag( t ); + + for ( i = elements.count(); i--; ) { + if ( ( element = elements.getItem( i ) ).data( 'cke-magicline-hot' ) ) { + // Restore hotNode + that.hotNode = element; + // Restore last access direction + that.lastCmdDirection = element.data( 'cke-magicline-dir' ) === 'true' ? true : false; + + return; + } + } + } + } ); + + // This method handles mousemove mouse for box toggling. + // It uses mouse position to determine underlying element, then + // it tries to use different trigger type in order to place the box + // in correct place. The following procedure is executed periodically. + function checkMouse( mouse ) { + that.debug.groupStart( 'CheckMouse' ); // %REMOVE_LINE% + that.debug.startTimer(); // %REMOVE_LINE% + + that.mouse = mouse; + that.trigger = null; + + checkMouseTimer = null; + updateWindowSize( that ); + + if ( + checkMouseTimeoutPending && // There must be an event pending. + !that.hiddenMode && // Can't be in hidden mode. + editor.focusManager.hasFocus && // Editor must have focus. + !that.line.mouseNear() && // Mouse pointer can't be close to the box. + ( that.element = elementFromMouse( that, true ) ) // There must be valid element. + ) { + // If trigger exists, and trigger is correct -> show the box. + // Don't show the line if trigger is a descendant of some tabu-list element. + if ( ( that.trigger = triggerEditable( that ) || triggerEdge( that ) || triggerExpand( that ) ) && + !isInTabu( that, that.trigger.upper || that.trigger.lower ) ) { + that.line.attach().place(); + } + + // Otherwise remove the box + else { + that.trigger = null; + that.line.detach(); + } + + that.debug.showTrigger( that.trigger ); // %REMOVE_LINE% + that.debug.mousePos( mouse.y, that.element ); // %REMOVE_LINE% + + checkMouseTimeoutPending = false; + } + + that.debug.stopTimer(); // %REMOVE_LINE% + that.debug.groupEnd(); // %REMOVE_LINE% + } + + // This one allows testing and debugging. It reveals some + // inner methods to the world. + this.backdoor = { + accessFocusSpace: accessFocusSpace, + boxTrigger: boxTrigger, + isLine: isLine, + getAscendantTrigger: getAscendantTrigger, + getNonEmptyNeighbour: getNonEmptyNeighbour, + getSize: getSize, + that: that, + triggerEdge: triggerEdge, + triggerEditable: triggerEditable, + triggerExpand: triggerExpand + }; + } + } + + // Some shorthands for common methods to save bytes + var extend = CKEDITOR.tools.extend, + newElement = CKEDITOR.dom.element, + newElementFromHtml = newElement.createFromHtml, + env = CKEDITOR.env, + env_ie8 = CKEDITOR.env.ie && CKEDITOR.env.version < 9, + dtd = CKEDITOR.dtd, + + // Global object associating enter modes with elements. + enterElements = {}, + + // Constant values, types and so on. + EDGE_TOP = 128, + EDGE_BOTTOM = 64, + EDGE_MIDDLE = 32, + TYPE_EDGE = 16, + TYPE_EXPAND = 8, + LOOK_TOP = 4, + LOOK_BOTTOM = 2, + LOOK_NORMAL = 1, + WHITE_SPACE = '\u00A0', + DTD_LISTITEM = dtd.$listItem, + DTD_TABLECONTENT = dtd.$tableContent, + DTD_NONACCESSIBLE = extend( {}, dtd.$nonEditable, dtd.$empty ), + DTD_BLOCK = dtd.$block, + + // Minimum time that must elapse between two update*Size calls. + // It prevents constant getComuptedStyle calls and improves performance. + CACHE_TIME = 100, + + // Shared CSS stuff for box elements + CSS_COMMON = 'width:0px;height:0px;padding:0px;margin:0px;display:block;' + 'z-index:9999;color:#fff;position:absolute;font-size: 0px;line-height:0px;', + CSS_TRIANGLE = CSS_COMMON + 'border-color:transparent;display:block;border-style:solid;', + TRIANGLE_HTML = '' + WHITE_SPACE + ''; + + enterElements[ CKEDITOR.ENTER_BR ] = 'br'; + enterElements[ CKEDITOR.ENTER_P ] = 'p'; + enterElements[ CKEDITOR.ENTER_DIV ] = 'div'; + + function areSiblings( that, upper, lower ) { + return isHtml( upper ) && isHtml( lower ) && lower.equals( upper.getNext( function( node ) { + return !( isEmptyTextNode( node ) || isComment( node ) || isFlowBreaker( node ) ); + } ) ); + } + + // boxTrigger is an abstract type which describes + // the relationship between elements that may result + // in showing the box. + // + // The following type is used by numerous methods + // to share information about the hypothetical box placement + // and look by referring to boxTrigger properties. + function boxTrigger( triggerSetup ) { + this.upper = triggerSetup[ 0 ]; + this.lower = triggerSetup[ 1 ]; + this.set.apply( this, triggerSetup.slice( 2 ) ); + } + + boxTrigger.prototype = { + set: function( edge, type, look ) { + this.properties = edge + type + ( look || LOOK_NORMAL ); + return this; + }, + + is: function( property ) { + return ( this.properties & property ) == property; + } + }; + + var elementFromMouse = ( function() { + function elementFromPoint( doc, mouse ) { + var pointedElement = doc.$.elementFromPoint( mouse.x, mouse.y ); + + // IE9QM: from times to times it will return an empty object on scroll bar hover. (#12185) + return pointedElement && pointedElement.nodeType ? + new CKEDITOR.dom.element( pointedElement ) : + null; + } + + return function( that, ignoreBox, forceMouse ) { + if ( !that.mouse ) + return null; + + var doc = that.doc, + lineWrap = that.line.wrap, + mouse = forceMouse || that.mouse, + // Note: element might be null. + element = elementFromPoint( doc, mouse ); + + // If ignoreBox is set and element is the box, it means that we + // need to hide the box for a while, repeat elementFromPoint + // and show it again. + if ( ignoreBox && isLine( that, element ) ) { + lineWrap.hide(); + element = elementFromPoint( doc, mouse ); + lineWrap.show(); + } + + // Return nothing if: + // \-> Element is not HTML. + if ( !( element && element.type == CKEDITOR.NODE_ELEMENT && element.$ ) ) + return null; + + // Also return nothing if: + // \-> We're IE<9 and element is out of the top-level element (editable for inline and HTML for classic (`iframe`-based)). + // This is due to the bug which allows IE<9 firing mouse events on element + // with contenteditable=true while doing selection out (far, away) of the element. + // Thus we must always be sure that we stay in editable or HTML. + if ( env.ie && env.version < 9 ) { + if ( !( that.boundary.equals( element ) || that.boundary.contains( element ) ) ) + return null; + } + + return element; + }; + } )(); + + // Gets the closest parent node that belongs to triggers group. + function getAscendantTrigger( that ) { + var node = that.element, + trigger; + + if ( node && isHtml( node ) ) { + trigger = node.getAscendant( that.triggers, true ); + + // If trigger is an element, neither editable nor editable's ascendant. + if ( trigger && that.editable.contains( trigger ) ) { + // Check for closest editable limit. + // Don't consider trigger as a limit as it may be nested editable (includeSelf=false) (#12009). + var limit = getClosestEditableLimit( trigger ); + + // Trigger in nested editable area. + if ( limit.getAttribute( 'contenteditable' ) == 'true' ) + return trigger; + // Trigger in non-editable area. + else if ( limit.is( that.triggers ) ) + return limit; + else + return null; + } else { + return null; + } + } + + return null; + } + + function getMidpoint( that, upper, lower ) { + updateSize( that, upper ); + updateSize( that, lower ); + + var upperSizeBottom = upper.size.bottom, + lowerSizeTop = lower.size.top; + + return upperSizeBottom && lowerSizeTop ? 0 | ( upperSizeBottom + lowerSizeTop ) / 2 : upperSizeBottom || lowerSizeTop; + } + + // Get nearest node (either text or HTML), but: + // \-> Omit all empty text nodes (containing white characters only). + // \-> Omit BR elements + // \-> Omit flow breakers. + function getNonEmptyNeighbour( that, node, goBack ) { + node = node[ goBack ? 'getPrevious' : 'getNext' ]( function( node ) { + return ( isTextNode( node ) && !isEmptyTextNode( node ) ) || + ( isHtml( node ) && !isFlowBreaker( node ) && !isLine( that, node ) ); + } ); + + return node; + } + + function inBetween( val, lower, upper ) { + return val > lower && val < upper; + } + + // Returns the closest ancestor that has contenteditable attribute. + // Such ancestor is the limit of (non-)editable DOM branch that element + // belongs to. This method omits editor editable. + function getClosestEditableLimit( element, includeSelf ) { + if ( element.data( 'cke-editable' ) ) + return null; + + if ( !includeSelf ) + element = element.getParent(); + + while ( element ) { + if ( element.data( 'cke-editable' ) ) + return null; + + if ( element.hasAttribute( 'contenteditable' ) ) + return element; + + element = element.getParent(); + } + + return null; + } + + // Access space line consists of a few elements (spans): + // \-> Line wrapper. + // \-> Line. + // \-> Line triangles: left triangle (LT), right triangle (RT). + // \-> Button handler (BTN). + // + // +--------------------------------------------------- line.wrap (span) -----+ + // | +---------------------------------------------------- line (span) -----+ | + // | | +- LT \ +- BTN -+ / RT -+ | | + // | | | \ | | | / | | | + // | | | / | <__| | \ | | | + // | | +-----/ +-------+ \-----+ | | + // | +----------------------------------------------------------------------+ | + // +--------------------------------------------------------------------------+ + // + function initLine( that ) { + var doc = that.doc, + // This the main box element that holds triangles and the insertion button + line = newElementFromHtml( '', doc ), + iconPath = CKEDITOR.getUrl( this.path + 'images/' + ( env.hidpi ? 'hidpi/' : '' ) + 'icon' + ( that.rtl ? '-rtl' : '' ) + '.png' ); + + extend( line, { + + attach: function() { + // Only if not already attached + if ( !this.wrap.getParent() ) + this.wrap.appendTo( that.editable, true ); + + return this; + }, + + // Looks are as follows: [ LOOK_TOP, LOOK_BOTTOM, LOOK_NORMAL ]. + lineChildren: [ + extend( + newElementFromHtml( + '', doc + ), { + base: CSS_COMMON + 'height:17px;width:17px;' + ( that.rtl ? 'left' : 'right' ) + ':17px;' + + 'background:url(' + iconPath + ') center no-repeat ' + that.boxColor + ';cursor:pointer;' + + ( env.hc ? 'font-size: 15px;line-height:14px;border:1px solid #fff;text-align:center;' : '' ) + + ( env.hidpi ? 'background-size: 9px 10px;' : '' ), + looks: [ + 'top:-8px; border-radius: 2px;', + 'top:-17px; border-radius: 2px 2px 0px 0px;', + 'top:-1px; border-radius: 0px 0px 2px 2px;' + ] + } + ), + extend( newElementFromHtml( TRIANGLE_HTML, doc ), { + base: CSS_TRIANGLE + 'left:0px;border-left-color:' + that.boxColor + ';', + looks: [ + 'border-width:8px 0 8px 8px;top:-8px', + 'border-width:8px 0 0 8px;top:-8px', + 'border-width:0 0 8px 8px;top:0px' + ] + } ), + extend( newElementFromHtml( TRIANGLE_HTML, doc ), { + base: CSS_TRIANGLE + 'right:0px;border-right-color:' + that.boxColor + ';', + looks: [ + 'border-width:8px 8px 8px 0;top:-8px', + 'border-width:8px 8px 0 0;top:-8px', + 'border-width:0 8px 8px 0;top:0px' + ] + } ) + ], + + detach: function() { + // Detach only if already attached. + if ( this.wrap.getParent() ) + this.wrap.remove(); + + return this; + }, + + // Checks whether mouseY is around an element by comparing boundaries and considering + // an offset distance. + mouseNear: function() { + that.debug.groupStart( 'mouseNear' ); // %REMOVE_LINE% + + updateSize( that, this ); + var offset = that.holdDistance, + size = this.size; + + // Determine neighborhood by element dimensions and offsets. + if ( size && inBetween( that.mouse.y, size.top - offset, size.bottom + offset ) && inBetween( that.mouse.x, size.left - offset, size.right + offset ) ) { + that.debug.logEnd( 'Mouse is near.' ); // %REMOVE_LINE% + return true; + } + + that.debug.logEnd( 'Mouse isn\'t near.' ); // %REMOVE_LINE% + return false; + }, + + // Adjusts position of the box according to the trigger properties. + // If also affects look of the box depending on the type of the trigger. + place: function() { + var view = that.view, + editable = that.editable, + trigger = that.trigger, + upper = trigger.upper, + lower = trigger.lower, + any = upper || lower, + parent = any.getParent(), + styleSet = {}; + + // Save recent trigger for further insertion. + // It is necessary due to the fact, that that.trigger may + // contain different boxTrigger at the moment of insertion + // or may be even null. + this.trigger = trigger; + + upper && updateSize( that, upper, true ); + lower && updateSize( that, lower, true ); + updateSize( that, parent, true ); + + // Yeah, that's gonna be useful in inline-mode case. + if ( that.inInlineMode ) + updateEditableSize( that, true ); + + // Set X coordinate (left, right, width). + if ( parent.equals( editable ) ) { + styleSet.left = view.scroll.x; + styleSet.right = -view.scroll.x; + styleSet.width = ''; + } else { + styleSet.left = any.size.left - any.size.margin.left + view.scroll.x - ( that.inInlineMode ? view.editable.left + view.editable.border.left : 0 ); + styleSet.width = any.size.outerWidth + any.size.margin.left + any.size.margin.right + view.scroll.x; + styleSet.right = ''; + } + + // Set Y coordinate (top) for trigger consisting of two elements. + if ( upper && lower ) { + // No margins at all or they're equal. Place box right between. + if ( upper.size.margin.bottom === lower.size.margin.top ) + styleSet.top = 0 | ( upper.size.bottom + upper.size.margin.bottom / 2 ); + else { + // Upper margin < lower margin. Place at lower margin. + if ( upper.size.margin.bottom < lower.size.margin.top ) + styleSet.top = upper.size.bottom + upper.size.margin.bottom; + // Upper margin > lower margin. Place at upper margin - lower margin. + else + styleSet.top = upper.size.bottom + upper.size.margin.bottom - lower.size.margin.top; + } + } + // Set Y coordinate (top) for single-edge trigger. + else if ( !upper ) + styleSet.top = lower.size.top - lower.size.margin.top; + else if ( !lower ) { + styleSet.top = upper.size.bottom + upper.size.margin.bottom; + } + + // Set box button modes if close to the viewport horizontal edge + // or look forced by the trigger. + if ( trigger.is( LOOK_TOP ) || inBetween( styleSet.top, view.scroll.y - 15, view.scroll.y + 5 ) ) { + styleSet.top = that.inInlineMode ? 0 : view.scroll.y; + this.look( LOOK_TOP ); + } else if ( trigger.is( LOOK_BOTTOM ) || inBetween( styleSet.top, view.pane.bottom - 5, view.pane.bottom + 15 ) ) { + styleSet.top = that.inInlineMode ? ( + view.editable.height + view.editable.padding.top + view.editable.padding.bottom + ) : ( + view.pane.bottom - 1 + ); + + this.look( LOOK_BOTTOM ); + } else { + if ( that.inInlineMode ) + styleSet.top -= view.editable.top + view.editable.border.top; + + this.look( LOOK_NORMAL ); + } + + if ( that.inInlineMode ) { + // 1px bug here... + styleSet.top--; + + // Consider the editable to be an element with overflow:scroll + // and non-zero scrollTop/scrollLeft value. + // For example: divarea editable. (#9383) + styleSet.top += view.editable.scroll.top; + styleSet.left += view.editable.scroll.left; + } + + // Append `px` prefixes. + for ( var style in styleSet ) + styleSet[ style ] = CKEDITOR.tools.cssLength( styleSet[ style ] ); + + this.setStyles( styleSet ); + }, + + // Changes look of the box according to current needs. + // Three different styles are available: [ LOOK_TOP, LOOK_BOTTOM, LOOK_NORMAL ]. + look: function( look ) { + if ( this.oldLook == look ) + return; + + for ( var i = this.lineChildren.length, child; i--; ) + ( child = this.lineChildren[ i ] ).setAttribute( 'style', child.base + child.looks[ 0 | look / 2 ] ); + + this.oldLook = look; + }, + + wrap: new newElement( 'span', that.doc ) + + } ); + + // Insert children into the box. + for ( var i = line.lineChildren.length; i--; ) + line.lineChildren[ i ].appendTo( line ); + + // Set default look of the box. + line.look( LOOK_NORMAL ); + + // Using that wrapper prevents IE (8,9) from resizing editable area at the moment + // of box insertion. This works thanks to the fact, that positioned box is wrapped by + // an inline element. So much tricky. + line.appendTo( line.wrap ); + + // Make the box unselectable. + line.unselectable(); + + // Handle accessSpace node insertion. + line.lineChildren[ 0 ].on( 'mouseup', function( event ) { + line.detach(); + + accessFocusSpace( that, function( accessNode ) { + // Use old trigger that was saved by 'place' method. Look: line.place + var trigger = that.line.trigger; + + accessNode[ trigger.is( EDGE_TOP ) ? 'insertBefore' : 'insertAfter' ]( + trigger.is( EDGE_TOP ) ? trigger.lower : trigger.upper ); + }, true ); + + that.editor.focus(); + + if ( !env.ie && that.enterMode != CKEDITOR.ENTER_BR ) + that.hotNode.scrollIntoView(); + + event.data.preventDefault( true ); + } ); + + // Prevents IE9 from displaying the resize box and disables drag'n'drop functionality. + line.on( 'mousedown', function( event ) { + event.data.preventDefault( true ); + } ); + + that.line = line; + } + + // This function allows accessing any focus space according to the insert function: + // * For enterMode ENTER_P it creates P element filled with dummy white-space. + // * For enterMode ENTER_DIV it creates DIV element filled with dummy white-space. + // * For enterMode ENTER_BR it creates BR element or   in IE. + // + // The node is being inserted according to insertFunction. Finally the method + // selects the non-breaking space making the node ready for typing. + function accessFocusSpace( that, insertFunction, doSave ) { + var range = new CKEDITOR.dom.range( that.doc ), + editor = that.editor, + accessNode; + + // IE requires text node of   in ENTER_BR mode. + if ( env.ie && that.enterMode == CKEDITOR.ENTER_BR ) + accessNode = that.doc.createText( WHITE_SPACE ); + + // In other cases a regular element is used. + else { + // Use the enterMode of editable's limit or editor's + // enter mode if not in nested editable. + var limit = getClosestEditableLimit( that.element, true ), + + // This is an enter mode for the context. We cannot use + // editor.activeEnterMode because the focused nested editable will + // have a different enterMode as editor but magicline will be inserted + // directly into editor's editable. + enterMode = limit && limit.data( 'cke-enter-mode' ) || that.enterMode; + + accessNode = new newElement( enterElements[ enterMode ], that.doc ); + + if ( !accessNode.is( 'br' ) ) { + var dummy = that.doc.createText( WHITE_SPACE ); + dummy.appendTo( accessNode ); + } + } + + doSave && editor.fire( 'saveSnapshot' ); + + insertFunction( accessNode ); + //dummy.appendTo( accessNode ); + range.moveToPosition( accessNode, CKEDITOR.POSITION_AFTER_START ); + editor.getSelection().selectRanges( [ range ] ); + that.hotNode = accessNode; + + doSave && editor.fire( 'saveSnapshot' ); + } + + // Access focus space on demand by taking an element under the caret as a reference. + // The space is accessed provided the element under the caret is trigger AND: + // + // 1. First/last-child of its parent: + // +----------------------- Parent element -+ + // | +------------------------------ DIV -+ | <-- Access before + // | | Foo^ | | + // | | | | + // | +------------------------------------+ | <-- Access after + // +----------------------------------------+ + // + // OR + // + // 2. It has a direct sibling element, which is also a trigger: + // +-------------------------------- DIV#1 -+ + // | Foo^ | + // | | + // +----------------------------------------+ + // <-- Access here + // +-------------------------------- DIV#2 -+ + // | Bar | + // | | + // +----------------------------------------+ + // + // OR + // + // 3. It has a direct sibling, which is a trigger and has a valid neighbour trigger, + // but belongs to dtd.$.empty/nonEditable: + // +------------------------------------ P -+ + // | Foo^ | + // | | + // +----------------------------------------+ + // +----------------------------------- HR -+ + // <-- Access here + // +-------------------------------- DIV#2 -+ + // | Bar | + // | | + // +----------------------------------------+ + // + function accessFocusSpaceCmd( that, insertAfter ) { + return { + canUndo: true, + modes: { wysiwyg: 1 }, + exec: ( function() { + + // Inserts line (accessNode) at the position by taking target node as a reference. + function doAccess( target ) { + // Remove old hotNode under certain circumstances. + var hotNodeChar = ( env.ie && env.version < 9 ? ' ' : WHITE_SPACE ), + removeOld = that.hotNode && // Old hotNode must exist. + that.hotNode.getText() == hotNodeChar && // Old hotNode hasn't been changed. + that.element.equals( that.hotNode ) && // Caret is inside old hotNode. + // Command is executed in the same direction. + that.lastCmdDirection === !!insertAfter; // jshint ignore:line + + accessFocusSpace( that, function( accessNode ) { + if ( removeOld && that.hotNode ) + that.hotNode.remove(); + + accessNode[ insertAfter ? 'insertAfter' : 'insertBefore' ]( target ); + + // Make this element distinguishable. Also remember the direction + // it's been inserted into document. + accessNode.setAttributes( { + 'data-cke-magicline-hot': 1, + 'data-cke-magicline-dir': !!insertAfter + } ); + + // Save last direction of the command (is insertAfter?). + that.lastCmdDirection = !!insertAfter; + } ); + + if ( !env.ie && that.enterMode != CKEDITOR.ENTER_BR ) + that.hotNode.scrollIntoView(); + + // Detach the line if was visible (previously triggered by mouse). + that.line.detach(); + } + + return function( editor ) { + var selected = editor.getSelection().getStartElement(), + limit; + + // (#9833) Go down to the closest non-inline element in DOM structure + // since inline elements don't participate in in magicline. + selected = selected.getAscendant( DTD_BLOCK, 1 ); + + // Stop if selected is a child of a tabu-list element. + if ( isInTabu( that, selected ) ) + return; + + // Sometimes it may happen that there's no parent block below selected element + // or, for example, getAscendant reaches editable or editable parent. + // We must avoid such pathological cases. + if ( !selected || selected.equals( that.editable ) || selected.contains( that.editable ) ) + return; + + // Executing the command directly in nested editable should + // access space before/after it. + if ( ( limit = getClosestEditableLimit( selected ) ) && limit.getAttribute( 'contenteditable' ) == 'false' ) + selected = limit; + + // That holds element from mouse. Replace it with the + // element under the caret. + that.element = selected; + + // (3.) Handle the following cases where selected neighbour + // is a trigger inaccessible for the caret AND: + // - Is first/last-child + // OR + // - Has a sibling, which is also a trigger. + var neighbor = getNonEmptyNeighbour( that, selected, !insertAfter ), + neighborSibling; + + // Check for a neighbour that belongs to triggers. + // Consider only non-accessible elements (they cannot have any children) + // since they cannot be given a caret inside, to run the command + // the regular way (1. & 2.). + if ( + isHtml( neighbor ) && neighbor.is( that.triggers ) && neighbor.is( DTD_NONACCESSIBLE ) && + ( + // Check whether neighbor is first/last-child. + !getNonEmptyNeighbour( that, neighbor, !insertAfter ) || + // Check for a sibling of a neighbour that also is a trigger. + ( + ( neighborSibling = getNonEmptyNeighbour( that, neighbor, !insertAfter ) ) && + isHtml( neighborSibling ) && + neighborSibling.is( that.triggers ) + ) + ) + ) { + doAccess( neighbor ); + return; + } + + // Look for possible target element DOWN "selected" DOM branch (towards editable) + // that belong to that.triggers + var target = getAscendantTrigger( that, selected ); + + // No HTML target -> no access. + if ( !isHtml( target ) ) + return; + + // (1.) Target is first/last child -> access. + if ( !getNonEmptyNeighbour( that, target, !insertAfter ) ) { + doAccess( target ); + return; + } + + var sibling = getNonEmptyNeighbour( that, target, !insertAfter ); + + // (2.) Target has a sibling that belongs to that.triggers -> access. + if ( sibling && isHtml( sibling ) && sibling.is( that.triggers ) ) { + doAccess( target ); + return; + } + }; + } )() + }; + } + + function isLine( that, node ) { + if ( !( node && node.type == CKEDITOR.NODE_ELEMENT && node.$ ) ) + return false; + + var line = that.line; + + return line.wrap.equals( node ) || line.wrap.contains( node ); + } + + // Is text node containing white-spaces only? + var isEmptyTextNode = CKEDITOR.dom.walker.whitespaces(); + + // Is fully visible HTML node? + function isHtml( node ) { + return node && node.type == CKEDITOR.NODE_ELEMENT && node.$; // IE requires that + } + + function isFloated( element ) { + if ( !isHtml( element ) ) + return false; + + var options = { left: 1, right: 1, center: 1 }; + + return !!( options[ element.getComputedStyle( 'float' ) ] || options[ element.getAttribute( 'align' ) ] ); + } + + function isFlowBreaker( element ) { + if ( !isHtml( element ) ) + return false; + + return isPositioned( element ) || isFloated( element ); + } + + // Isn't node of NODE_COMMENT type? + var isComment = CKEDITOR.dom.walker.nodeType( CKEDITOR.NODE_COMMENT ); + + function isPositioned( element ) { + return !!{ absolute: 1, fixed: 1 }[ element.getComputedStyle( 'position' ) ]; + } + + // Is text node? + function isTextNode( node ) { + return node && node.type == CKEDITOR.NODE_TEXT; + } + + function isTrigger( that, element ) { + return isHtml( element ) ? element.is( that.triggers ) : null; + } + + function isInTabu( that, element ) { + if ( !element ) + return false; + + var parents = element.getParents( 1 ); + + for ( var i = parents.length ; i-- ; ) { + for ( var j = that.tabuList.length ; j-- ; ) { + if ( parents[ i ].hasAttribute( that.tabuList[ j ] ) ) + return true; + } + } + + return false; + } + + // This function checks vertically is there's a relevant child between element's edge + // and the pointer. + // \-> Table contents are omitted. + function isChildBetweenPointerAndEdge( that, parent, edgeBottom ) { + var edgeChild = parent[ edgeBottom ? 'getLast' : 'getFirst' ]( function( node ) { + return that.isRelevant( node ) && !node.is( DTD_TABLECONTENT ); + } ); + + if ( !edgeChild ) + return false; + + updateSize( that, edgeChild ); + + return edgeBottom ? edgeChild.size.top > that.mouse.y : edgeChild.size.bottom < that.mouse.y; + } + + // This method handles edge cases: + // \-> Mouse is around upper or lower edge of view pane. + // \-> Also scroll position is either minimal or maximal. + // \-> It's OK to show LOOK_TOP(BOTTOM) type line. + // + // This trigger doesn't need additional post-filtering. + // + // +----------------------------- Editable -+ /-- + // | +---------------------- First child -+ | | <-- Top edge (first child) + // | | | | | + // | | | | | * Mouse activation area * + // | | | | | + // | | ... | | \-- Top edge + trigger offset + // | . . | + // | | + // | . . | + // | | ... | | /-- Bottom edge - trigger offset + // | | | | | + // | | | | | * Mouse activation area * + // | | | | | + // | +----------------------- Last child -+ | | <-- Bottom edge (last child) + // +----------------------------------------+ \-- + // + function triggerEditable( that ) { + that.debug.groupStart( 'triggerEditable' ); // %REMOVE_LINE% + + var editable = that.editable, + mouse = that.mouse, + view = that.view, + triggerOffset = that.triggerOffset, + triggerLook; + + // Update editable dimensions. + updateEditableSize( that ); + + // This flag determines whether checking bottom trigger. + var bottomTrigger = mouse.y > ( + that.inInlineMode ? ( + view.editable.top + view.editable.height / 2 + ) : ( + // This is to handle case when editable.height / 2 <<< pane.height. + Math.min( view.editable.height, view.pane.height ) / 2 + ) + ), + + // Edge node according to bottomTrigger. + edgeNode = editable[ bottomTrigger ? 'getLast' : 'getFirst' ]( function( node ) { + return !( isEmptyTextNode( node ) || isComment( node ) ); + } ); + + // There's no edge node. Abort. + if ( !edgeNode ) { + that.debug.logEnd( 'ABORT. No edge node found.' ); // %REMOVE_LINE% + return null; + } + + // If the edgeNode in editable is ML, get the next one. + if ( isLine( that, edgeNode ) ) { + edgeNode = that.line.wrap[ bottomTrigger ? 'getPrevious' : 'getNext' ]( function( node ) { + return !( isEmptyTextNode( node ) || isComment( node ) ); + } ); + } + + // Exclude bad nodes (no ML needed then): + // \-> Edge node is text. + // \-> Edge node is floated, etc. + // + // Edge node *must be* a valid trigger at this stage as well. + if ( !isHtml( edgeNode ) || isFlowBreaker( edgeNode ) || !isTrigger( that, edgeNode ) ) { + that.debug.logEnd( 'ABORT. Invalid edge node.' ); // %REMOVE_LINE% + return null; + } + + // Update size of edge node. Dimensions will be necessary. + updateSize( that, edgeNode ); + + // Return appropriate trigger according to bottomTrigger. + // \-> Top edge trigger case first. + if ( !bottomTrigger && // Top trigger case. + edgeNode.size.top >= 0 && // Check if the first element is fully visible. + inBetween( mouse.y, 0, edgeNode.size.top + triggerOffset ) ) { // Check if mouse in [0, edgeNode.top + triggerOffset]. + + // Determine trigger look. + triggerLook = that.inInlineMode || view.scroll.y === 0 ? + LOOK_TOP : LOOK_NORMAL; + + that.debug.logEnd( 'SUCCESS. Created box trigger. EDGE_TOP.' ); // %REMOVE_LINE% + + return new boxTrigger( [ null, edgeNode, + EDGE_TOP, + TYPE_EDGE, + triggerLook + ] ); + } + + // \-> Bottom case. + else if ( bottomTrigger && + edgeNode.size.bottom <= view.pane.height && // Check if the last element is fully visible + inBetween( mouse.y, // Check if mouse in... + edgeNode.size.bottom - triggerOffset, view.pane.height ) ) { // [ edgeNode.bottom - triggerOffset, paneHeight ] + + // Determine trigger look. + triggerLook = that.inInlineMode || + inBetween( edgeNode.size.bottom, view.pane.height - triggerOffset, view.pane.height ) ? + LOOK_BOTTOM : LOOK_NORMAL; + + that.debug.logEnd( 'SUCCESS. Created box trigger. EDGE_BOTTOM.' ); // %REMOVE_LINE% + + return new boxTrigger( [ edgeNode, null, + EDGE_BOTTOM, + TYPE_EDGE, + triggerLook + ] ); + } + + that.debug.logEnd( 'ABORT. No trigger created.' ); // %REMOVE_LINE% + return null; + } + + // This method covers cases *inside* of an element: + // \-> The pointer is in the top (bottom) area of an element and there's + // HTML node before (after) this element. + // \-> An element being the first or last child of its parent. + // + // +----------------------- Parent element -+ + // | +----------------------- Element #1 -+ | /-- + // | | | | | * Mouse activation area (as first child) * + // | | | | \-- + // | | | | /-- + // | | | | | * Mouse activation area (Element #2) * + // | +------------------------------------+ | \-- + // | | + // | +----------------------- Element #2 -+ | /-- + // | | | | | * Mouse activation area (Element #1) * + // | | | | \-- + // | | | | + // | +------------------------------------+ | + // | | + // | Text node is here. | + // | | + // | +----------------------- Element #3 -+ | + // | | | | + // | | | | + // | | | | /-- + // | | | | | * Mouse activation area (as last child) * + // | +------------------------------------+ | \-- + // +----------------------------------------+ + // + function triggerEdge( that ) { + that.debug.groupStart( 'triggerEdge' ); // %REMOVE_LINE% + + var mouse = that.mouse, + view = that.view, + triggerOffset = that.triggerOffset; + + // Get the ascendant trigger basing on elementFromMouse. + var element = getAscendantTrigger( that ); + + that.debug.logElements( [ element ], [ 'Ascendant trigger' ], 'First stage' ); // %REMOVE_LINE% + + // Abort if there's no appropriate element. + if ( !element ) { + that.debug.logEnd( 'ABORT. No element, element is editable or element contains editable.' ); // %REMOVE_LINE% + return null; + } + + // Dimensions will be necessary. + updateSize( that, element ); + + // If triggerOffset is larger than a half of element's height, + // use an offset of 1/2 of element's height. If the offset wasn't reduced, + // top area would cover most (all) cases. + var fixedOffset = Math.min( triggerOffset, + 0 | ( element.size.outerHeight / 2 ) ), + + // This variable will hold the trigger to be returned. + triggerSetup = [], + triggerLook, + + // This flag determines whether dealing with a bottom trigger. + bottomTrigger; + + // \-> Top trigger. + if ( inBetween( mouse.y, element.size.top - 1, element.size.top + fixedOffset ) ) + bottomTrigger = false; + // \-> Bottom trigger. + else if ( inBetween( mouse.y, element.size.bottom - fixedOffset, element.size.bottom + 1 ) ) + bottomTrigger = true; + // \-> Abort. Not in a valid trigger space. + else { + that.debug.logEnd( 'ABORT. Not around of any edge.' ); // %REMOVE_LINE% + return null; + } + + // Reject wrong elements. + // \-> Reject an element which is a flow breaker. + // \-> Reject an element which has a child above/below the mouse pointer. + // \-> Reject an element which belongs to list items. + if ( + isFlowBreaker( element ) || + isChildBetweenPointerAndEdge( that, element, bottomTrigger ) || + element.getParent().is( DTD_LISTITEM ) + ) { + that.debug.logEnd( 'ABORT. element is wrong', element ); // %REMOVE_LINE% + return null; + } + + // Get sibling according to bottomTrigger. + var elementSibling = getNonEmptyNeighbour( that, element, !bottomTrigger ); + + // No sibling element. + // This is a first or last child case. + if ( !elementSibling ) { + // No need to reject the element as it has already been done before. + // Prepare a trigger. + + // Determine trigger look. + if ( element.equals( that.editable[ bottomTrigger ? 'getLast' : 'getFirst' ]( that.isRelevant ) ) ) { + updateEditableSize( that ); + + if ( + bottomTrigger && inBetween( mouse.y, + element.size.bottom - fixedOffset, view.pane.height ) && + inBetween( element.size.bottom, view.pane.height - fixedOffset, view.pane.height ) + ) { + triggerLook = LOOK_BOTTOM; + } else if ( inBetween( mouse.y, 0, element.size.top + fixedOffset ) ) { + triggerLook = LOOK_TOP; + } + } else { + triggerLook = LOOK_NORMAL; + } + + triggerSetup = [ null, element ][ bottomTrigger ? 'reverse' : 'concat' ]().concat( [ + bottomTrigger ? EDGE_BOTTOM : EDGE_TOP, + TYPE_EDGE, + triggerLook, + element.equals( that.editable[ bottomTrigger ? 'getLast' : 'getFirst' ]( that.isRelevant ) ) ? + ( bottomTrigger ? LOOK_BOTTOM : LOOK_TOP ) : LOOK_NORMAL + ] ); + + that.debug.log( 'Configured edge trigger of ' + ( bottomTrigger ? 'EDGE_BOTTOM' : 'EDGE_TOP' ) ); // %REMOVE_LINE% + } + + // Abort. Sibling is a text element. + else if ( isTextNode( elementSibling ) ) { + that.debug.logEnd( 'ABORT. Sibling is non-empty text element' ); // %REMOVE_LINE% + return null; + } + + // Check if the sibling is a HTML element. + // If so, create an TYPE_EDGE, EDGE_MIDDLE trigger. + else if ( isHtml( elementSibling ) ) { + // Reject wrong elementSiblings. + // \-> Reject an elementSibling which is a flow breaker. + // \-> Reject an elementSibling which isn't a trigger. + // \-> Reject an elementSibling which belongs to list items. + if ( + isFlowBreaker( elementSibling ) || + !isTrigger( that, elementSibling ) || + elementSibling.getParent().is( DTD_LISTITEM ) + ) { + that.debug.logEnd( 'ABORT. elementSibling is wrong', elementSibling ); // %REMOVE_LINE% + return null; + } + + // Prepare a trigger. + triggerSetup = [ elementSibling, element ][ bottomTrigger ? 'reverse' : 'concat' ]().concat( [ + EDGE_MIDDLE, + TYPE_EDGE + ] ); + + that.debug.log( 'Configured edge trigger of EDGE_MIDDLE' ); // %REMOVE_LINE% + } + + if ( 0 in triggerSetup ) { + that.debug.logEnd( 'SUCCESS. Returning a trigger.' ); // %REMOVE_LINE% + return new boxTrigger( triggerSetup ); + } + + that.debug.logEnd( 'ABORT. No trigger generated.' ); // %REMOVE_LINE% + return null; + } + + // Checks iteratively up and down in search for elements using elementFromMouse method. + // Useful if between two triggers. + // + // +----------------------- Parent element -+ + // | +----------------------- Element #1 -+ | + // | | | | + // | | | | + // | | | | + // | +------------------------------------+ | + // | | /-- + // | . | | + // | . +-- Floated -+ | | + // | | | | | | * Mouse activation area * + // | | | IGNORE | | | + // | X | | | | Method searches vertically for sibling elements. + // | | +------------+ | | Start point is X (mouse-y coordinate). + // | | | | Floated elements, comments and empty text nodes are omitted. + // | . | | + // | . | | + // | | \-- + // | +----------------------- Element #2 -+ | + // | | | | + // | | | | + // | | | | + // | | | | + // | +------------------------------------+ | + // +----------------------------------------+ + // + var triggerExpand = ( function() { + // The heart of the procedure. This method creates triggers that are + // filtered by expandFilter method. + function expandEngine( that ) { + that.debug.groupStart( 'expandEngine' ); // %REMOVE_LINE% + + var startElement = that.element, + upper, lower, trigger; + + if ( !isHtml( startElement ) || startElement.contains( that.editable ) ) { + that.debug.logEnd( 'ABORT. No start element, or start element contains editable.' ); // %REMOVE_LINE% + return null; + } + + // Stop searching if element is in non-editable branch of DOM. + if ( startElement.isReadOnly() ) + return null; + + trigger = verticalSearch( that, + function( current, startElement ) { + return !startElement.equals( current ); // stop when start element and the current one differ + }, function( that, mouse ) { + return elementFromMouse( that, true, mouse ); + }, startElement ), + + upper = trigger.upper, + lower = trigger.lower; + + that.debug.logElements( [ upper, lower ], [ 'Upper', 'Lower' ], 'Pair found' ); // %REMOVE_LINE% + + // Success: two siblings have been found + if ( areSiblings( that, upper, lower ) ) { + that.debug.logEnd( 'SUCCESS. Expand trigger created.' ); // %REMOVE_LINE% + return trigger.set( EDGE_MIDDLE, TYPE_EXPAND ); + } + + that.debug.logElements( [ startElement, upper, lower ], // %REMOVE_LINE% + [ 'Start', 'Upper', 'Lower' ], 'Post-processing' ); // %REMOVE_LINE% + + // Danger. Dragons ahead. + // No siblings have been found during previous phase, post-processing may be necessary. + // We can traverse DOM until a valid pair of elements around the pointer is found. + + // Prepare for post-processing: + // 1. Determine if upper and lower are children of startElement. + // 1.1. If so, find their ascendants that are closest to startElement (one level deeper than startElement). + // 1.2. Otherwise use first/last-child of the startElement as upper/lower. Why?: + // a) upper/lower belongs to another branch of the DOM tree. + // b) verticalSearch encountered an edge of the viewport and failed. + // 1.3. Make sure upper and lower still exist. Why?: + // a) Upper and lower may be not belong to the branch of the startElement (may not exist at all) and + // startElement has no children. + // 2. Perform the post-processing. + // 2.1. Gather dimensions of an upper element. + // 2.2. Abort if lower edge of upper is already under the mouse pointer. Why?: + // a) We expect upper to be above and lower below the mouse pointer. + // 3. Perform iterative search while upper != lower. + // 3.1. Find the upper-next element. If there's no such element, break current search. Why?: + // a) There's no point in further search if there are only text nodes ahead. + // 3.2. Calculate the distance between the middle point of ( upper, upperNext ) and mouse-y. + // 3.3. If the distance is shorter than the previous best, save it (save upper, upperNext as well). + // 3.4. If the optimal pair is found, assign it back to the trigger. + + // 1.1., 1.2. + if ( upper && startElement.contains( upper ) ) { + while ( !upper.getParent().equals( startElement ) ) + upper = upper.getParent(); + } else { + upper = startElement.getFirst( function( node ) { + return expandSelector( that, node ); + } ); + } + + if ( lower && startElement.contains( lower ) ) { + while ( !lower.getParent().equals( startElement ) ) + lower = lower.getParent(); + } else { + lower = startElement.getLast( function( node ) { + return expandSelector( that, node ); + } ); + } + + // 1.3. + if ( !upper || !lower ) { + that.debug.logEnd( 'ABORT. There is no upper or no lower element.' ); // %REMOVE_LINE% + return null; + } + + // 2.1. + updateSize( that, upper ); + updateSize( that, lower ); + + if ( !checkMouseBetweenElements( that, upper, lower ) ) { + that.debug.logEnd( 'ABORT. Mouse is already above upper or below lower.' ); // %REMOVE_LINE% + return null; + } + + var minDistance = Number.MAX_VALUE, + currentDistance, upperNext, minElement, minElementNext; + + while ( lower && !lower.equals( upper ) ) { + // 3.1. + if ( !( upperNext = upper.getNext( that.isRelevant ) ) ) + break; + + // 3.2. + currentDistance = Math.abs( getMidpoint( that, upper, upperNext ) - that.mouse.y ); + + // 3.3. + if ( currentDistance < minDistance ) { + minDistance = currentDistance; + minElement = upper; + minElementNext = upperNext; + } + + upper = upperNext; + updateSize( that, upper ); + } + + that.debug.logElements( [ minElement, minElementNext ], // %REMOVE_LINE% + [ 'Min', 'MinNext' ], 'Post-processing results' ); // %REMOVE_LINE% + + // 3.4. + if ( !minElement || !minElementNext ) { + that.debug.logEnd( 'ABORT. No Min or MinNext' ); // %REMOVE_LINE% + return null; + } + + if ( !checkMouseBetweenElements( that, minElement, minElementNext ) ) { + that.debug.logEnd( 'ABORT. Mouse is already above minElement or below minElementNext.' ); // %REMOVE_LINE% + return null; + } + + // An element of minimal distance has been found. Assign it to the trigger. + trigger.upper = minElement; + trigger.lower = minElementNext; + + // Success: post-processing revealed a pair of elements. + that.debug.logEnd( 'SUCCESSFUL post-processing. Trigger created.' ); // %REMOVE_LINE% + return trigger.set( EDGE_MIDDLE, TYPE_EXPAND ); + } + + // This is default element selector used by the engine. + function expandSelector( that, node ) { + return !( isTextNode( node ) || + isComment( node ) || + isFlowBreaker( node ) || + isLine( that, node ) || + ( node.type == CKEDITOR.NODE_ELEMENT && node.$ && node.is( 'br' ) ) ); + } + + // This method checks whether mouse-y is between the top edge of upper + // and bottom edge of lower. + // + // NOTE: This method assumes that updateSize has already been called + // for the elements and is up-to-date. + // + // +---------------------------- Upper -+ /-- + // | | | + // +------------------------------------+ | + // | + // ... | + // | + // X | * Return true for mouse-y in this range * + // | + // ... | + // | + // +---------------------------- Lower -+ | + // | | | + // +------------------------------------+ \-- + // + function checkMouseBetweenElements( that, upper, lower ) { + return inBetween( that.mouse.y, upper.size.top, lower.size.bottom ); + } + + // A method for trigger filtering. Accepts or rejects trigger pairs + // by their location in DOM etc. + function expandFilter( that, trigger ) { + that.debug.groupStart( 'expandFilter' ); // %REMOVE_LINE% + + var upper = trigger.upper, + lower = trigger.lower; + + if ( + !upper || !lower || // NOT: EDGE_MIDDLE trigger ALWAYS has two elements. + isFlowBreaker( lower ) || isFlowBreaker( upper ) || // NOT: one of the elements is floated or positioned + lower.equals( upper ) || upper.equals( lower ) || // NOT: two trigger elements, one equals another. + lower.contains( upper ) || upper.contains( lower ) + ) { // NOT: two trigger elements, one contains another. + that.debug.logEnd( 'REJECTED. No upper or no lower or they contain each other.' ); // %REMOVE_LINE% + + return false; + } + + // YES: two trigger elements, pure siblings. + else if ( isTrigger( that, upper ) && isTrigger( that, lower ) && areSiblings( that, upper, lower ) ) { + that.debug.logElementsEnd( [ upper, lower ], // %REMOVE_LINE% + [ 'upper', 'lower' ], 'APPROVED EDGE_MIDDLE' ); // %REMOVE_LINE% + + return true; + } + + that.debug.logElementsEnd( [ upper, lower ], // %REMOVE_LINE% + [ 'upper', 'lower' ], 'Rejected unknown pair' ); // %REMOVE_LINE% + + return false; + } + + // Simple wrapper for expandEngine and expandFilter. + return function( that ) { + that.debug.groupStart( 'triggerExpand' ); // %REMOVE_LINE% + + var trigger = expandEngine( that ); + + that.debug.groupEnd(); // %REMOVE_LINE% + return trigger && expandFilter( that, trigger ) ? trigger : null; + }; + } )(); + + // Collects dimensions of an element. + var sizePrefixes = [ 'top', 'left', 'right', 'bottom' ]; + + function getSize( that, element, ignoreScroll, force ) { + var docPosition = element.getDocumentPosition(), + border = {}, + margin = {}, + padding = {}, + box = {}; + + for ( var i = sizePrefixes.length; i--; ) { + border[ sizePrefixes[ i ] ] = parseInt( getStyle( 'border-' + sizePrefixes[ i ] + '-width' ), 10 ) || 0; + padding[ sizePrefixes[ i ] ] = parseInt( getStyle( 'padding-' + sizePrefixes[ i ] ), 10 ) || 0; + margin[ sizePrefixes[ i ] ] = parseInt( getStyle( 'margin-' + sizePrefixes[ i ] ), 10 ) || 0; + } + + // updateWindowSize if forced to do so OR NOT ignoring scroll. + if ( !ignoreScroll || force ) + updateWindowSize( that, force ); + + box.top = docPosition.y - ( ignoreScroll ? 0 : that.view.scroll.y ), box.left = docPosition.x - ( ignoreScroll ? 0 : that.view.scroll.x ), + + // w/ borders and paddings. + box.outerWidth = element.$.offsetWidth, box.outerHeight = element.$.offsetHeight, + + // w/o borders and paddings. + box.height = box.outerHeight - ( padding.top + padding.bottom + border.top + border.bottom ), box.width = box.outerWidth - ( padding.left + padding.right + border.left + border.right ), + + box.bottom = box.top + box.outerHeight, box.right = box.left + box.outerWidth; + + if ( that.inInlineMode ) { + box.scroll = { + top: element.$.scrollTop, + left: element.$.scrollLeft + }; + } + + return extend( { + border: border, + padding: padding, + margin: margin, + ignoreScroll: ignoreScroll + }, box, true ); + + function getStyle( propertyName ) { + return element.getComputedStyle.call( element, propertyName ); + } + } + + function updateSize( that, element, ignoreScroll ) { + if ( !isHtml( element ) ) // i.e. an element is hidden + return ( element.size = null ); // -> reset size to make it useless for other methods + + if ( !element.size ) + element.size = {}; + + // Abort if there was a similar query performed recently. + // This kind of caching provides great performance improvement. + else if ( element.size.ignoreScroll == ignoreScroll && element.size.date > new Date() - CACHE_TIME ) { + that.debug.log( 'element.size: get from cache' ); // %REMOVE_LINE% + return null; + } + + that.debug.log( 'element.size: capture' ); // %REMOVE_LINE% + + return extend( element.size, getSize( that, element, ignoreScroll ), { + date: +new Date() + }, true ); + } + + // Updates that.view.editable object. + // This one must be called separately outside of updateWindowSize + // to prevent cyclic dependency getSize<->updateWindowSize. + // It calls getSize with force flag to avoid getWindowSize cache (look: getSize). + function updateEditableSize( that, ignoreScroll ) { + that.view.editable = getSize( that, that.editable, ignoreScroll, true ); + } + + function updateWindowSize( that, force ) { + if ( !that.view ) + that.view = {}; + + var view = that.view; + + if ( !force && view && view.date > new Date() - CACHE_TIME ) { + that.debug.log( 'win.size: get from cache' ); // %REMOVE_LINE% + return; + } + + that.debug.log( 'win.size: capturing' ); // %REMOVE_LINE% + + var win = that.win, + scroll = win.getScrollPosition(), + paneSize = win.getViewPaneSize(); + + extend( that.view, { + scroll: { + x: scroll.x, + y: scroll.y, + width: that.doc.$.documentElement.scrollWidth - paneSize.width, + height: that.doc.$.documentElement.scrollHeight - paneSize.height + }, + pane: { + width: paneSize.width, + height: paneSize.height, + bottom: paneSize.height + scroll.y + }, + date: +new Date() + }, true ); + } + + // This method searches document vertically using given + // select criterion until stop criterion is fulfilled. + function verticalSearch( that, stopCondition, selectCriterion, startElement ) { + var upper = startElement, + lower = startElement, + mouseStep = 0, + upperFound = false, + lowerFound = false, + viewPaneHeight = that.view.pane.height, + mouse = that.mouse; + + while ( mouse.y + mouseStep < viewPaneHeight && mouse.y - mouseStep > 0 ) { + if ( !upperFound ) + upperFound = stopCondition( upper, startElement ); + + if ( !lowerFound ) + lowerFound = stopCondition( lower, startElement ); + + // Still not found... + if ( !upperFound && mouse.y - mouseStep > 0 ) + upper = selectCriterion( that, { x: mouse.x, y: mouse.y - mouseStep } ); + + if ( !lowerFound && mouse.y + mouseStep < viewPaneHeight ) + lower = selectCriterion( that, { x: mouse.x, y: mouse.y + mouseStep } ); + + if ( upperFound && lowerFound ) + break; + + // Instead of ++ to reduce the number of invocations by half. + // It's trades off accuracy in some edge cases for improved performance. + mouseStep += 2; + } + + return new boxTrigger( [ upper, lower, null, null ] ); + } + +} )(); + +/** + * Sets the default vertical distance between the edge of the element and the mouse pointer that + * causes the magic line to appear. This option accepts a value in pixels, without the unit (for example: + * `15` for 15 pixels). + * + * Read more in the [documentation](#!/guide/dev_magicline) + * and see the [SDK sample](http://sdk.ckeditor.com/samples/magicline.html). + * + * // Changes the offset to 15px. + * CKEDITOR.config.magicline_triggerOffset = 15; + * + * @cfg {Number} [magicline_triggerOffset=30] + * @member CKEDITOR.config + * @see CKEDITOR.config#magicline_holdDistance + */ + +/** + * Defines the distance between the mouse pointer and the box within + * which the magic line stays revealed and no other focus space is offered to be accessed. + * This value is relative to {@link #magicline_triggerOffset}. + * + * Read more in the [documentation](#!/guide/dev_magicline) + * and see the [SDK sample](http://sdk.ckeditor.com/samples/magicline.html). + * + * // Increases the distance to 80% of CKEDITOR.config.magicline_triggerOffset. + * CKEDITOR.config.magicline_holdDistance = .8; + * + * @cfg {Number} [magicline_holdDistance=0.5] + * @member CKEDITOR.config + * @see CKEDITOR.config#magicline_triggerOffset + */ + +/** + * Defines the default keystroke that accesses the closest unreachable focus space **before** + * the caret (start of the selection). If there is no focus space available, the selection remains unchanged. + * + * Read more in the [documentation](#!/guide/dev_magicline) + * and see the [SDK sample](http://sdk.ckeditor.com/samples/magicline.html). + * + * // Changes the default keystroke to "Ctrl + ,". + * CKEDITOR.config.magicline_keystrokePrevious = CKEDITOR.CTRL + 188; + * + * @cfg {Number} [magicline_keystrokePrevious=CKEDITOR.CTRL + CKEDITOR.SHIFT + 51 (CTRL + SHIFT + 3)] + * @member CKEDITOR.config + */ +CKEDITOR.config.magicline_keystrokePrevious = CKEDITOR.CTRL + CKEDITOR.SHIFT + 51; // CTRL + SHIFT + 3 + +/** + * Defines the default keystroke that accesses the closest unreachable focus space **after** + * the caret (start of the selection). If there is no focus space available, the selection remains unchanged. + * + * Read more in the [documentation](#!/guide/dev_magicline) + * and see the [SDK sample](http://sdk.ckeditor.com/samples/magicline.html). + * + * // Changes keystroke to "Ctrl + .". + * CKEDITOR.config.magicline_keystrokeNext = CKEDITOR.CTRL + 190; + * + * @cfg {Number} [magicline_keystrokeNext=CKEDITOR.CTRL + CKEDITOR.SHIFT + 52 (CTRL + SHIFT + 4)] + * @member CKEDITOR.config + */ +CKEDITOR.config.magicline_keystrokeNext = CKEDITOR.CTRL + CKEDITOR.SHIFT + 52; // CTRL + SHIFT + 4 + +/** + * Defines a list of attributes that, if assigned to some elements, prevent the magic line from being + * used within these elements. + * + * Read more in the [documentation](#!/guide/dev_magicline) + * and see the [SDK sample](http://sdk.ckeditor.com/samples/magicline.html). + * + * // Adds the "data-tabu" attribute to the magic line tabu list. + * CKEDITOR.config.magicline_tabuList = [ 'data-tabu' ]; + * + * @cfg {Number} [magicline_tabuList=[ 'data-widget-wrapper' ]] + * @member CKEDITOR.config + */ + +/** + * Defines the color of the magic line. The color may be adjusted to enhance readability. + * + * Read more in the [documentation](#!/guide/dev_magicline) + * and see the [SDK sample](http://sdk.ckeditor.com/samples/magicline.html). + * + * // Changes magic line color to blue. + * CKEDITOR.config.magicline_color = '#0000FF'; + * + * @cfg {String} [magicline_color='#FF0000'] + * @member CKEDITOR.config + */ + +/** + * Activates the special all-encompassing mode that considers all focus spaces between + * {@link CKEDITOR.dtd#$block} elements as accessible by the magic line. + * + * Read more in the [documentation](#!/guide/dev_magicline) + * and see the [SDK sample](http://sdk.ckeditor.com/samples/magicline.html). + * + * // Enables the greedy "put everywhere" mode. + * CKEDITOR.config.magicline_everywhere = true; + * + * @cfg {Boolean} [magicline_everywhere=false] + * @member CKEDITOR.config + */ diff --git a/sources/plugins/magicline/samples/magicline.html b/sources/plugins/magicline/samples/magicline.html new file mode 100644 index 00000000..21d1d071 --- /dev/null +++ b/sources/plugins/magicline/samples/magicline.html @@ -0,0 +1,209 @@ + + + + + + Using Magicline plugin — CKEditor Sample + + + + + + + +

+ CKEditor Samples » Using Magicline plugin +

+
+ This sample is not maintained anymore. Check out its brand new version in CKEditor SDK. +
+
+

+ This sample shows the advantages of Magicline plugin + which is to enhance the editing process. Thanks to this plugin, + a number of difficult focus spaces which are inaccessible due to + browser issues can now be focused. +

+

+ Magicline plugin shows a red line with a handler + which, when clicked, inserts a paragraph and allows typing. To see this, + focus an editor and move your mouse above the focus space you want + to access. The plugin is enabled by default so no additional + configuration is necessary. +

+
+
+ +
+

+ This editor uses a default Magicline setup. +

+
+ + +
+
+
+ +
+

+ This editor is using a blue line. +

+
+CKEDITOR.replace( 'editor2', {
+	magicline_color: 'blue'
+});
+
+ + +
+ + + -- cgit v1.2.3