forked from Wavyzz/dolibarr
Merge branch 'develop' of https://github.com/Dolibarr/dolibarr into 3.9_NEW_multityppeeventfilter
This commit is contained in:
8
.gitignore
vendored
8
.gitignore
vendored
@@ -17,4 +17,10 @@ doxygen_warnings.log
|
||||
*.iml
|
||||
Thumbs.db
|
||||
# Vagrant generated files
|
||||
.vagrant
|
||||
.vagrant
|
||||
# Composer installed repositories
|
||||
/htdocs/includes/**/.git
|
||||
# Composer autoloader and unwanted files
|
||||
htdocs/includes/autoload.php
|
||||
htdocs/includes/bin/
|
||||
htdocs/includes/composer/
|
||||
|
||||
@@ -38,7 +38,7 @@ We officially support versions N, N − 1 and N − 2 for N the latest version a
|
||||
Choose your base branch accordingly.
|
||||
|
||||
### General rules
|
||||
Please don't edit the ChangeLog file. A project manager will update it from your commit messages.
|
||||
Please don't edit the ChangeLog file. File will be generated from your commit messages during release process by the project manager.
|
||||
|
||||
### <a name=commits></a>Commits
|
||||
Use clear commit messages with the following structure:
|
||||
@@ -50,7 +50,7 @@ LONGDESC
|
||||
```
|
||||
|
||||
#### Keyword
|
||||
In uppercase to appear in ChangeLog when generated.
|
||||
In uppercase if you want to have the log comment appears into the generated ChangeLog file.
|
||||
|
||||
The keyword can be ommitted if your commit does not fit in any of the following categories:
|
||||
- Fix: for a bug fix
|
||||
@@ -97,6 +97,10 @@ When submitting a pull request, use same rule as [Commits](#commits) for the mes
|
||||
If your pull request only contains one commit, GitHub will be smart enough to fill it for you.
|
||||
Otherwise, please be a bit verbose about what you're providing.
|
||||
|
||||
You Pull Request must pass the Continuous Integration checks.
|
||||
Also, if you want to include a new external library (into htdocs/includes directory), please ask before to the project
|
||||
leader to see if such a library can be accepted.
|
||||
|
||||
### Resources
|
||||
[Developer documentation](http://wiki.dolibarr.org/index.php/Developer_documentation)
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ Dolibarr uses some external libraries released under different licenses. This is
|
||||
Component Version License GPL Compatible Usage
|
||||
-------------------------------------------------------------------------------------
|
||||
PHP libraries:
|
||||
AdoDb-Date 0.33 Modified BSD License Yes Date convertion (not into rpm package)
|
||||
AdoDb-Date 0.36 Modified BSD License Yes Date convertion (not into rpm package)
|
||||
ChromePHP 4.1.0 Apache Software License 2.0 Yes Return server log to chrome browser console
|
||||
CKEditor 4.3.3 LGPL-2.1+ Yes Editor WYSIWYG
|
||||
EvalMath 1.0 BSD Yes Safe math expressions evaluation
|
||||
@@ -24,7 +24,7 @@ Mobiledetect 2.8.3 MIT License Yes
|
||||
NuSoap 0.9.5 LGPL 2.1+ Yes Library to develop SOAP Web services (not into rpm and deb package)
|
||||
PEAR Mail_MIME 1.8.9 BSD Yes NuSoap dependency
|
||||
odtPHP 1.0.1 GPL-2+ b Yes Library to build/edit ODT files
|
||||
PHPExcel 1.8.0 LGPL-2.1+ Yes Read/Write XLS files, read ODS files
|
||||
PHPExcel 1.8.1 LGPL-2.1+ Yes Read/Write XLS files, read ODS files
|
||||
php-iban 1.4.7 LGPL-3+ Yes Parse and validate IBAN (and IIBAN) bank account information in PHP
|
||||
PHPoAuthLib 0.8.2 MIT License Yes Library to provide oauth1 and oauth2 to different service
|
||||
PHPPrintIPP 1.3 GPL-2+ Yes Library to send print IPP requests
|
||||
@@ -54,7 +54,7 @@ jQuery TableDnD 0.6 GPL and MIT License Yes
|
||||
jQuery Timepicker 1.1.0 GPL and MIT License Yes JS library Timepicker addon for Datepicker
|
||||
jQuery Tiptip 1.3 GPL and MIT License Yes JS library for tooltips
|
||||
jsGantt 1.2 BSD License Yes JS library (to build Gantt reports)
|
||||
JsTimezoneDetect 1.0.4 MIT License Yes JS library to detect user timezone
|
||||
JsTimezoneDetect 1.0.6 MIT License Yes JS library to detect user timezone
|
||||
Raven.js 1.1.19 MIT License Yes Used for client-side error logging with Sentry logger
|
||||
|
||||
For licenses compatibility informations:
|
||||
|
||||
17
README-FR.md
17
README-FR.md
@@ -133,12 +133,19 @@ Voici un liste de fonctionnalites pas encore gérées par Dolibarr:
|
||||
- Dolibarr ne fait pas le café (pas encore).
|
||||
|
||||
|
||||
## RESEAUX SOCIAUX
|
||||
## ACTUALITES ET RESEAUX SOCIAUX
|
||||
|
||||
Suivez le projet Dolibarr project sur
|
||||
Suivez le projet Dolibarr project sur les réseaux francophones
|
||||
|
||||
Facebook: <https://www.facebook.com/dolibarr.fr>
|
||||
- Facebook: <https://www.facebook.com/dolibarr.fr>
|
||||
- Google+: <https://plus.google.com/+DolibarrFrance>
|
||||
- Twitter: <http://www.twitter.com/dolibarr_france>
|
||||
|
||||
Google+: <https://plus.google.com/+DolibarrFrance>
|
||||
ou sur les réseaux anglophones
|
||||
|
||||
Twitter: <http://www.twitter.com/dolibarr_france>
|
||||
- [Facebook](https://www.facebook.com/dolibarr)
|
||||
- [Google+](https://plus.google.com/+DolibarrOrg)
|
||||
- [Twitter](http://www.twitter.com/dolibarr)
|
||||
- [LinkedIn](https://www.linkedin.com/company/association-dolibarr)
|
||||
- [YouTube](https://www.youtube.com/user/DolibarrERPCRM)
|
||||
- [GitHub](https://github.com/Dolibarr/dolibarr)
|
||||
|
||||
14
README.md
14
README.md
@@ -1,6 +1,6 @@
|
||||
# DOLIBARR ERP & CRM
|
||||
|
||||
 
|
||||
 
|
||||
|
||||
Dolibarr ERP & CRM is a modern software to manage your organization's activity (contacts, suppliers, invoices, orders, stocks, agenda, ...).
|
||||
|
||||
@@ -16,9 +16,9 @@ You can use it as a standalone application or as a web application to be able to
|
||||
|
||||
Dolibarr is released under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version (GPL-3+).
|
||||
|
||||
See the [COPYING](COPYING) file for a full copy of the license.
|
||||
See the [COPYING](https://github.com/Dolibarr/dolibarr/blob/develop/COPYING) file for a full copy of the license.
|
||||
|
||||
Other licenses apply for some included dependencies. See [COPYRIGHT](COPYRIGHT) for a full list.
|
||||
Other licenses apply for some included dependencies. See [COPYRIGHT](https://github.com/Dolibarr/dolibarr/blob/develop/COPYRIGHT) for a full list.
|
||||
|
||||
## INSTALLING
|
||||
|
||||
@@ -65,9 +65,9 @@ You can use a Web server and a supported database (MySQL recommended) to install
|
||||
|
||||
*Note: migration process can safely be done multiple times.*
|
||||
|
||||
## NEWS
|
||||
## WHAT'S NEW
|
||||
|
||||
See the [ChangeLog](ChangeLog) file.
|
||||
See the [ChangeLog](https://github.com/Dolibarr/dolibarr/blob/develop/ChangeLog) file.
|
||||
|
||||
## FEATURES
|
||||
|
||||
@@ -153,9 +153,9 @@ Administrator, user, developer and translator's documentations are available alo
|
||||
|
||||
Dolibarr is the work of many contributors over the years and uses some fine libraries.
|
||||
|
||||
See [COPYRIGHT](COPYRIGHT) file.
|
||||
See [COPYRIGHT](https://github.com/Dolibarr/dolibarr/blob/develop/COPYRIGHT) file.
|
||||
|
||||
## SOCIAL NETWORKS
|
||||
## NEWS AND SOCIAL NETWORKS
|
||||
|
||||
Follow Dolibarr project on:
|
||||
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
"php": ">=5.3.0",
|
||||
"ext-curl": "*",
|
||||
"ccampbell/chromephp": "^4.1",
|
||||
"ckeditor/ckeditor": "4.3.3",
|
||||
"ckeditor/ckeditor": "dev-full/4.3.x#0b7c3f1",
|
||||
"mike42/escpos-php": "dev-master",
|
||||
"mobiledetect/mobiledetectlib": "2.8.3",
|
||||
"phpoffice/phpexcel": "1.8.0",
|
||||
"phpoffice/phpexcel": "1.8.1",
|
||||
"restler/framework": "^3.0",
|
||||
"tecnick.com/tcpdf": "6.2.6",
|
||||
"tecnickcom/tcpdf": "6.2.6",
|
||||
"raven/raven": "^0.12.0",
|
||||
"firephp/firephp-core": "^0.4.0"
|
||||
},
|
||||
|
||||
27
composer.lock
generated
27
composer.lock
generated
@@ -4,8 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"hash": "d88b5c84a9ea1af354d867ba2e2ab251",
|
||||
"content-hash": "85e60ec7f8ab593387c7bd10d8db860d",
|
||||
"hash": "335eb7bd5c2eb116fd2da80b4f48e857",
|
||||
"packages": [
|
||||
{
|
||||
"name": "ccampbell/chromephp",
|
||||
@@ -52,16 +51,16 @@
|
||||
},
|
||||
{
|
||||
"name": "ckeditor/ckeditor",
|
||||
"version": "4.3.3",
|
||||
"version": "dev-full/4.3.x",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ckeditor/ckeditor-releases.git",
|
||||
"reference": "0068dd540ce8bf1815abb7b5455c55354bc56334"
|
||||
"reference": "0b7c3f1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ckeditor/ckeditor-releases/zipball/0068dd540ce8bf1815abb7b5455c55354bc56334",
|
||||
"reference": "0068dd540ce8bf1815abb7b5455c55354bc56334",
|
||||
"url": "https://api.github.com/repos/ckeditor/ckeditor-releases/zipball/252e512e911f21d880ea542fe162c4643885b317",
|
||||
"reference": "0b7c3f1",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
@@ -89,7 +88,7 @@
|
||||
"text",
|
||||
"wysiwyg"
|
||||
],
|
||||
"time": "2014-02-26 15:34:37"
|
||||
"time": "2014-02-26 15:43:10"
|
||||
},
|
||||
{
|
||||
"name": "firephp/firephp-core",
|
||||
@@ -239,16 +238,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpoffice/phpexcel",
|
||||
"version": "1.8.0",
|
||||
"version": "1.8.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PHPOffice/PHPExcel.git",
|
||||
"reference": "e69a5e4d0ffa7fb6f171859e0a04346e580df30b"
|
||||
"reference": "372c7cbb695a6f6f1e62649381aeaa37e7e70b32"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PHPOffice/PHPExcel/zipball/e69a5e4d0ffa7fb6f171859e0a04346e580df30b",
|
||||
"reference": "e69a5e4d0ffa7fb6f171859e0a04346e580df30b",
|
||||
"url": "https://api.github.com/repos/PHPOffice/PHPExcel/zipball/372c7cbb695a6f6f1e62649381aeaa37e7e70b32",
|
||||
"reference": "372c7cbb695a6f6f1e62649381aeaa37e7e70b32",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -292,7 +291,7 @@
|
||||
"xls",
|
||||
"xlsx"
|
||||
],
|
||||
"time": "2014-03-02 15:22:49"
|
||||
"time": "2015-05-01 07:00:55"
|
||||
},
|
||||
{
|
||||
"name": "raven/raven",
|
||||
@@ -423,7 +422,7 @@
|
||||
"time": "2015-08-04 07:52:49"
|
||||
},
|
||||
{
|
||||
"name": "tecnick.com/tcpdf",
|
||||
"name": "tecnickcom/tcpdf",
|
||||
"version": "6.2.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
@@ -483,7 +482,6 @@
|
||||
"pdf417",
|
||||
"qrcode"
|
||||
],
|
||||
"abandoned": "tecnickcom/tcpdf",
|
||||
"time": "2015-01-28 18:51:40"
|
||||
}
|
||||
],
|
||||
@@ -491,6 +489,7 @@
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {
|
||||
"ckeditor/ckeditor": 20,
|
||||
"mike42/escpos-php": 20
|
||||
},
|
||||
"prefer-stable": false,
|
||||
|
||||
@@ -69,7 +69,7 @@ Add tcpdi.php
|
||||
Add tcpdi_parser.php and replace:
|
||||
require_once(dirname(__FILE__).'/include/tcpdf_filters.php');
|
||||
with:
|
||||
require_once(dirname(__FILE__).'/../tecnick.com/tcpdf/include/tcpdf_filters.php');
|
||||
require_once(dirname(__FILE__).'/../tecnickcom/tcpdf/include/tcpdf_filters.php');
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -96,6 +96,23 @@ if (($id > 0 || ! empty($ref)) && $action != 'add')
|
||||
if ($result < 0) dol_print_error($db);
|
||||
}
|
||||
|
||||
// Definition of fields for list
|
||||
$arrayfields=array(
|
||||
't.field1'=>array('label'=>$langs->trans("Field1"), 'checked'=>1),
|
||||
't.field2'=>array('label'=>$langs->trans("Field2"), 'checked'=>1),
|
||||
//'t.entity'=>array('label'=>$langs->trans("Entity"), 'checked'=>1, 'enabled'=>(! empty($conf->multicompany->enabled) && empty($conf->multicompany->transverse_mode))),
|
||||
't.datec'=>array('label'=>$langs->trans("DateCreation"), 'checked'=>0, 'position'=>500),
|
||||
't.tms'=>array('label'=>$langs->trans("DateModificationShort"), 'checked'=>0, 'position'=>500),
|
||||
//'t.statut'=>array('label'=>$langs->trans("Status"), 'checked'=>1, 'position'=>1000),
|
||||
);
|
||||
// Extra fields
|
||||
if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label))
|
||||
{
|
||||
foreach($extrafields->attribute_label as $key => $val)
|
||||
{
|
||||
$arrayfields["ef.".$key]=array('label'=>$extrafields->attribute_label[$key], 'checked'=>$extrafields->attribute_list[$key], 'position'=>$extrafields->attribute_pos[$key], 'enabled'=>$extrafields->attribute_perms[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -267,22 +284,6 @@ if ($resql)
|
||||
print '</div>';
|
||||
}
|
||||
|
||||
$arrayfields=array(
|
||||
't.field1'=>array('label'=>$langs->trans("Field1"), 'checked'=>1),
|
||||
't.field2'=>array('label'=>$langs->trans("Field2"), 'checked'=>1),
|
||||
//'t.entity'=>array('label'=>$langs->trans("Entity"), 'checked'=>1, 'enabled'=>(! empty($conf->multicompany->enabled) && empty($conf->multicompany->transverse_mode))),
|
||||
't.datec'=>array('label'=>$langs->trans("DateCreation"), 'checked'=>0, 'position'=>500),
|
||||
't.tms'=>array('label'=>$langs->trans("DateModificationShort"), 'checked'=>0, 'position'=>500),
|
||||
//'t.statut'=>array('label'=>$langs->trans("Status"), 'checked'=>1, 'position'=>1000),
|
||||
);
|
||||
// Extra fields
|
||||
if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label))
|
||||
{
|
||||
foreach($extrafields->attribute_label as $key => $val)
|
||||
{
|
||||
$arrayfields["ef.".$key]=array('label'=>$extrafields->attribute_label[$key], 'checked'=>$extrafields->attribute_list[$key], 'position'=>$extrafields->attribute_pos[$key], 'enabled'=>$extrafields->attribute_perms[$key]);
|
||||
}
|
||||
}
|
||||
$varpage=empty($contextpage)?$_SERVER["PHP_SELF"]:$contextpage;
|
||||
$selectedfields=$form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage); // This also change content of $arrayfields
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
* @since 2008-03-04
|
||||
*/
|
||||
|
||||
require_once('../../htdocs/includes/tecnick.com/tcpdf/config/tcpdf_config.php');
|
||||
require_once('../../htdocs/includes/tecnick.com/tcpdf/tcpdf.php');
|
||||
require_once('../../htdocs/includes/tecnickcom/tcpdf/config/tcpdf_config.php');
|
||||
require_once('../../htdocs/includes/tecnickcom/tcpdf/tcpdf.php');
|
||||
|
||||
// create new PDF document
|
||||
$pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
|
||||
|
||||
@@ -47,8 +47,8 @@ print 'Files has been created. Check its name from your explorer'."\n";
|
||||
* @since 2008-09-15
|
||||
*/
|
||||
|
||||
require_once('../../htdocs/includes/tecnick.com/tcpdf/config/tcpdf_config.php');
|
||||
require_once('../../htdocs/includes/tecnick.com/tcpdf/tcpdf.php');
|
||||
require_once('../../htdocs/includes/tecnickcom/tcpdf/config/tcpdf_config.php');
|
||||
require_once('../../htdocs/includes/tecnickcom/tcpdf/tcpdf.php');
|
||||
|
||||
// create new PDF document
|
||||
$pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
|
||||
|
||||
@@ -139,7 +139,7 @@ if (empty($reshook))
|
||||
$object->fetch($id);
|
||||
$object->fk_prospectlevel=GETPOST('prospect_level_id','alpha');
|
||||
$result=$object->set_prospect_level($user);
|
||||
if ($result < 0) setEventMessage($object->error,$object->errors,'errors');
|
||||
if ($result < 0) setEventMessages($object->error, $object->errors, 'errors');
|
||||
}
|
||||
|
||||
// set communication status
|
||||
@@ -148,7 +148,7 @@ if (empty($reshook))
|
||||
$object->fetch($id);
|
||||
$object->stcomm_id=dol_getIdFromCode($db, GETPOST('stcomm','alpha'), 'c_stcomm');
|
||||
$result=$object->set_commnucation_level($user);
|
||||
if ($result < 0) setEventMessages($object->error,$object->errors,'errors');
|
||||
if ($result < 0) setEventMessages($object->error, $object->errors, 'errors');
|
||||
}
|
||||
|
||||
// update outstandng limit
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
* Copyright (C) 2005-2015 Regis Houssin <regis.houssin@capnetworks.com>
|
||||
* Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
|
||||
* Copyright (C) 2010-2013 Juanjo Menent <jmenent@2byte.es>
|
||||
* Copyright (C) 2011 Philippe Grand <philippe.grand@atoo-net.com>
|
||||
* Copyright (C) 2011-2015 Philippe Grand <philippe.grand@atoo-net.com>
|
||||
* Copyright (C) 2012-2013 Christophe Battarel <christophe.battarel@altairis.fr>
|
||||
* Copyright (C) 2012 Marcos García <marcosgdf@gmail.com>
|
||||
* Copyright (C) 2012 Cedric Salvador <csalvador@gpcsolutions.fr>
|
||||
* Copyright (C) 2012 Cedric Salvador <csalvador@gpcsolutions.fr>
|
||||
* Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
|
||||
* Copyright (C) 2014 Ferran Marcet <fmarcet@2byte.es>
|
||||
* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
|
||||
@@ -123,7 +123,7 @@ if (empty($reshook))
|
||||
{
|
||||
if (1==0 && ! GETPOST('clone_content') && ! GETPOST('clone_receivers'))
|
||||
{
|
||||
setEventMessage($langs->trans("NoCloneOptionsSpecified"), 'errors');
|
||||
setEventMessages($langs->trans("NoCloneOptionsSpecified"), null, 'errors');
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -140,7 +140,7 @@ if (empty($reshook))
|
||||
}
|
||||
else
|
||||
{
|
||||
setEventMessage($object->error, 'errors');
|
||||
setEventMessages($object->error, $object->errors, 'errors');
|
||||
$object = $orig;
|
||||
$action='';
|
||||
}
|
||||
@@ -156,12 +156,11 @@ if (empty($reshook))
|
||||
$result = $object->set_reopen($user);
|
||||
if ($result > 0)
|
||||
{
|
||||
header('Location: '.$_SERVER["PHP_SELF"].'?id='.$object->id);
|
||||
exit;
|
||||
setEventMessages($langs->trans('OrderReopened', $object->ref), null);
|
||||
}
|
||||
else
|
||||
{
|
||||
setEventMessage($object->error, 'errors');
|
||||
setEventMessages($object->error, $object->errors, 'errors');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -177,7 +176,7 @@ if (empty($reshook))
|
||||
}
|
||||
else
|
||||
{
|
||||
setEventMessage($object->error, 'errors');
|
||||
setEventMessages($object->error, $object->errors, 'errors');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,7 +207,7 @@ if (empty($reshook))
|
||||
}
|
||||
else
|
||||
{
|
||||
setEventMessage($object->error, 'errors');
|
||||
setEventMessages($object->error, $object->errors, 'errors');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +224,7 @@ if (empty($reshook))
|
||||
$datelivraison = dol_mktime(12, 0, 0, GETPOST('liv_month'), GETPOST('liv_day'), GETPOST('liv_year'));
|
||||
|
||||
if ($datecommande == '') {
|
||||
setEventMessage($langs->trans('ErrorFieldRequired', $langs->transnoentities('Date')), 'errors');
|
||||
setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentities('Date')), null, 'errors');
|
||||
$action = 'create';
|
||||
$error++;
|
||||
}
|
||||
@@ -374,11 +373,11 @@ if (empty($reshook))
|
||||
if ($reshook < 0)
|
||||
$error++;
|
||||
} else {
|
||||
setEventMessage($srcobject->error, 'errors');
|
||||
setEventMessages($srcobject->error, $srcobject->errors, 'errors');
|
||||
$error++;
|
||||
}
|
||||
} else {
|
||||
setEventMessage($object->error, 'errors');
|
||||
setEventMessages($object->error, $object->errors, 'errors');
|
||||
$error++;
|
||||
}
|
||||
} else {
|
||||
@@ -414,7 +413,7 @@ if (empty($reshook))
|
||||
{
|
||||
$result = $object->add_contact(GETPOST('contactid'), 'CUSTOMER', 'external');
|
||||
if ($result < 0) {
|
||||
setEventMessage($langs->trans("ErrorFailedToAddContact"), 'errors');
|
||||
setEventMessages($langs->trans("ErrorFailedToAddContact"), null, 'errors');
|
||||
$error++;
|
||||
}
|
||||
}
|
||||
@@ -432,7 +431,7 @@ if (empty($reshook))
|
||||
} else {
|
||||
$db->rollback();
|
||||
$action = 'create';
|
||||
setEventMessage($object->error, 'errors');
|
||||
setEventMessages($object->error, $object->errors, 'errors');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -442,7 +441,7 @@ if (empty($reshook))
|
||||
$ret=$object->classifyBilled();
|
||||
|
||||
if ($ret < 0) {
|
||||
setEventMessage($object->error, 'errors');
|
||||
setEventMessages($object->error, $object->errors, 'errors');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -471,7 +470,7 @@ if (empty($reshook))
|
||||
|
||||
$result = $object->set_date($user, $date);
|
||||
if ($result < 0) {
|
||||
setEventMessage($object->error, 'errors');
|
||||
setEventMessages($object->error, $object->errors, 'errors');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -481,7 +480,7 @@ if (empty($reshook))
|
||||
|
||||
$result = $object->set_date_livraison($user, $datelivraison);
|
||||
if ($result < 0) {
|
||||
setEventMessage($object->error, 'errors');
|
||||
setEventMessages($object->error, $object->errors, 'errors');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -529,16 +528,25 @@ if (empty($reshook))
|
||||
elseif ($action == 'set_incoterms' && !empty($conf->incoterm->enabled))
|
||||
{
|
||||
$result = $object->setIncoterms(GETPOST('incoterm_id', 'int'), GETPOST('location_incoterms', 'alpha'));
|
||||
if ($result < 0) {
|
||||
setEventMessages($object->error, $object->errors, 'errors');
|
||||
}
|
||||
}
|
||||
|
||||
// bank account
|
||||
else if ($action == 'setbankaccount' && $user->rights->commande->creer) {
|
||||
$result=$object->setBankAccount(GETPOST('fk_account', 'int'));
|
||||
if ($result < 0) {
|
||||
setEventMessages($object->error, $object->errors, 'errors');
|
||||
}
|
||||
}
|
||||
|
||||
// shipping method
|
||||
else if ($action == 'setshippingmethod' && $user->rights->commande->creer) {
|
||||
$result = $object->setShippingMethod(GETPOST('shipping_method_id', 'int'));
|
||||
if ($result < 0) {
|
||||
setEventMessages($object->error, $object->errors, 'errors');
|
||||
}
|
||||
}
|
||||
|
||||
else if ($action == 'setremisepercent' && $user->rights->commande->creer) {
|
||||
@@ -586,11 +594,11 @@ if (empty($reshook))
|
||||
}
|
||||
|
||||
if (empty($idprod) && ($price_ht < 0) && ($qty < 0)) {
|
||||
setEventMessage($langs->trans('ErrorBothFieldCantBeNegative', $langs->transnoentitiesnoconv('UnitPriceHT'), $langs->transnoentitiesnoconv('Qty')), 'errors');
|
||||
setEventMessages($langs->trans('ErrorBothFieldCantBeNegative', $langs->transnoentitiesnoconv('UnitPriceHT'), $langs->transnoentitiesnoconv('Qty')), null, 'errors');
|
||||
$error++;
|
||||
}
|
||||
if (GETPOST('prod_entry_mode') == 'free' && empty($idprod) && GETPOST('type') < 0) {
|
||||
setEventMessage($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Type')), 'errors');
|
||||
setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Type')), null, 'errors');
|
||||
$error++;
|
||||
}
|
||||
if (GETPOST('prod_entry_mode') == 'free' && empty($idprod) && (! ($price_ht >= 0) || $price_ht == '')) // Unit price can be 0 but not ''
|
||||
@@ -599,11 +607,11 @@ if (empty($reshook))
|
||||
$error++;
|
||||
}
|
||||
if ($qty == '') {
|
||||
setEventMessage($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Qty')), 'errors');
|
||||
setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Qty')), null, 'errors');
|
||||
$error++;
|
||||
}
|
||||
if (GETPOST('prod_entry_mode') == 'free' && empty($idprod) && empty($product_desc)) {
|
||||
setEventMessage($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Description')), 'errors');
|
||||
setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Description')), null, 'errors');
|
||||
$error++;
|
||||
}
|
||||
|
||||
@@ -664,7 +672,7 @@ if (empty($reshook))
|
||||
}
|
||||
else
|
||||
{
|
||||
setEventMessage($prodcustprice->error,'errors');
|
||||
setEventMessages($prodcustprice->error, $prodcustprice->errors, 'errors');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -748,7 +756,7 @@ if (empty($reshook))
|
||||
|
||||
if (! empty($price_min) && (price2num($pu_ht) * (1 - price2num($remise_percent) / 100) < price2num($price_min))) {
|
||||
$mesg = $langs->trans("CantBeLessThanMinPrice", price(price2num($price_min, 'MU'), 0, $langs, 0, 0, - 1, $conf->currency));
|
||||
setEventMessage($mesg, 'errors');
|
||||
setEventMessages($mesg, null, 'errors');
|
||||
} else {
|
||||
// Insert line
|
||||
$result = $object->addline($desc, $pu_ht, $qty, $tva_tx, $localtax1_tx, $localtax2_tx, $idprod, $remise_percent, $info_bits, 0, $price_base_type, $pu_ttc, $date_start, $date_end, $type, - 1, 0, GETPOST('fk_parent_line'), $fournprice, $buyingprice, $label, $array_options, $fk_unit);
|
||||
@@ -802,7 +810,7 @@ if (empty($reshook))
|
||||
unset($_POST['date_endmonth']);
|
||||
unset($_POST['date_endyear']);
|
||||
} else {
|
||||
setEventMessage($object->error, 'errors');
|
||||
setEventMessages($object->error, $object->errors, 'errors');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -866,7 +874,7 @@ if (empty($reshook))
|
||||
$label = ((GETPOST('update_label') && GETPOST('product_label')) ? GETPOST('product_label') : '');
|
||||
|
||||
if ($price_min && (price2num($pu_ht) * (1 - price2num(GETPOST('remise_percent')) / 100) < price2num($price_min))) {
|
||||
setEventMessage($langs->trans("CantBeLessThanMinPrice", price(price2num($price_min, 'MU'), 0, $langs, 0, 0, - 1, $conf->currency)), 'errors');
|
||||
setEventMessages($langs->trans("CantBeLessThanMinPrice", price(price2num($price_min, 'MU'), 0, $langs, 0, 0, - 1, $conf->currency)), null, 'errors');
|
||||
$error++;
|
||||
}
|
||||
} else {
|
||||
@@ -914,7 +922,7 @@ if (empty($reshook))
|
||||
unset($_POST['fournprice']);
|
||||
unset($_POST['buying_price']);
|
||||
} else {
|
||||
setEventMessage($object->error, 'errors');
|
||||
setEventMessages($object->error, $object->errors, 'errors');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -947,7 +955,7 @@ if (empty($reshook))
|
||||
if (! $idwarehouse || $idwarehouse == -1)
|
||||
{
|
||||
$error++;
|
||||
setEventMessage($langs->trans('ErrorFieldRequired',$langs->transnoentitiesnoconv("Warehouse")), 'errors');
|
||||
setEventMessages($langs->trans('ErrorFieldRequired',$langs->transnoentitiesnoconv("Warehouse")), null, 'errors');
|
||||
$action='';
|
||||
}
|
||||
}
|
||||
@@ -1000,7 +1008,7 @@ if (empty($reshook))
|
||||
if (! $idwarehouse || $idwarehouse == -1)
|
||||
{
|
||||
$error++;
|
||||
setEventMessage($langs->trans('ErrorFieldRequired',$langs->transnoentitiesnoconv("Warehouse")), 'errors');
|
||||
setEventMessages($langs->trans('ErrorFieldRequired',$langs->transnoentitiesnoconv("Warehouse")), null, 'errors');
|
||||
$action='';
|
||||
}
|
||||
}
|
||||
@@ -1032,7 +1040,7 @@ if (empty($reshook))
|
||||
else if ($action == 'confirm_shipped' && $confirm == 'yes' && $user->rights->commande->cloturer) {
|
||||
$result = $object->cloture($user);
|
||||
if ($result < 0) {
|
||||
setEventMessage($object->error, 'errors');
|
||||
setEventMessages($object->error, $object->errors, 'errors');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1059,7 +1067,7 @@ if (empty($reshook))
|
||||
if (! $idwarehouse || $idwarehouse == -1)
|
||||
{
|
||||
$error++;
|
||||
setEventMessage($langs->trans('ErrorFieldRequired',$langs->transnoentitiesnoconv("Warehouse")), 'errors');
|
||||
setEventMessages($langs->trans('ErrorFieldRequired',$langs->transnoentitiesnoconv("Warehouse")), null, 'errors');
|
||||
$action='';
|
||||
}
|
||||
}
|
||||
@@ -1068,7 +1076,7 @@ if (empty($reshook))
|
||||
$result = $object->cancel($idwarehouse);
|
||||
|
||||
if ($result < 0) {
|
||||
setEventMessage($object->error, 'errors');
|
||||
setEventMessages($object->error, $object->errors, 'errors');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1115,9 +1123,9 @@ if (empty($reshook))
|
||||
$file = $upload_dir . '/' . GETPOST('file');
|
||||
$ret = dol_delete_file($file, 0, 0, 0, $object);
|
||||
if ($ret)
|
||||
setEventMessage($langs->trans("FileWasRemoved", GETPOST('urlfile')));
|
||||
setEventMessages($langs->trans("FileWasRemoved", GETPOST('urlfile')), null, 'mesgs');
|
||||
else
|
||||
setEventMessage($langs->trans("ErrorFailToDeleteFile", GETPOST('urlfile')), 'errors');
|
||||
setEventMessages($langs->trans("ErrorFailToDeleteFile", GETPOST('urlfile')), null, 'errors');
|
||||
$action = '';
|
||||
}
|
||||
}
|
||||
@@ -1181,9 +1189,9 @@ if (empty($reshook))
|
||||
} else {
|
||||
if ($object->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
|
||||
$langs->load("errors");
|
||||
setEventMessage($langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType"), 'errors');
|
||||
setEventMessages($langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType"), null, 'errors');
|
||||
} else {
|
||||
setEventMessage($object->error, 'errors');
|
||||
setEventMessages($object->error, $object->errors, 'errors');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
/* Copyright (C) 2005 Patrick Rouillon <patrick@rouillon.net>
|
||||
* Copyright (C) 2005-2011 Laurent Destailleur <eldy@users.sourceforge.net>
|
||||
* Copyright (C) 2005-2012 Regis Houssin <regis.houssin@capnetworks.com>
|
||||
* Copyright (C) 2011-2012 Philippe Grand <philippe.grand@atoo-net.com>
|
||||
* Copyright (C) 2011-2015 Philippe Grand <philippe.grand@atoo-net.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -69,11 +69,11 @@ if ($action == 'addcontact' && $user->rights->commande->creer)
|
||||
if ($object->error == 'DB_ERROR_RECORD_ALREADY_EXISTS')
|
||||
{
|
||||
$langs->load("errors");
|
||||
setEventMessage($langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType"), 'errors');
|
||||
setEventMessages($langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType"), null, 'errors');
|
||||
}
|
||||
else
|
||||
{
|
||||
setEventMessage($object->error, 'errors');
|
||||
setEventMessages($object->error, $object->errors, 'errors');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ if ($action == 'create')
|
||||
if (is_array($selected) == false)
|
||||
{
|
||||
$error++;
|
||||
setEventMessage($langs->trans('Error_OrderNotChecked'), 'errors');
|
||||
setEventMessages($langs->trans('Error_OrderNotChecked'), null, 'errors');
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -251,7 +251,7 @@ if (($action == 'create' || $action == 'add') && !$error)
|
||||
}
|
||||
else
|
||||
{
|
||||
setEventMessage($discount->error, 'errors');
|
||||
setEventMessages($discount->error, $discount->errors, 'errors');
|
||||
$error++;
|
||||
break;
|
||||
}
|
||||
@@ -320,7 +320,7 @@ if (($action == 'create' || $action == 'add') && !$error)
|
||||
}
|
||||
else
|
||||
{
|
||||
setEventMessage($objectsrc->error, 'errors');
|
||||
setEventMessages($objectsrc->error, $objectsrc->errors, 'errors');
|
||||
$error++;
|
||||
}
|
||||
$ii++;
|
||||
@@ -328,7 +328,7 @@ if (($action == 'create' || $action == 'add') && !$error)
|
||||
}
|
||||
else
|
||||
{
|
||||
setEventMessage($object->error, 'errors');
|
||||
setEventMessages($object->error, $object->errors, 'errors');
|
||||
$error++;
|
||||
}
|
||||
}
|
||||
@@ -348,7 +348,7 @@ if (($action == 'create' || $action == 'add') && !$error)
|
||||
$action='create';
|
||||
$_GET["origin"]=$_POST["origin"];
|
||||
$_GET["originid"]=$_POST["originid"];
|
||||
setEventMessage($object->error, 'errors');
|
||||
setEventMessages($object->error, $object->errors, 'errors');
|
||||
$error++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,13 +152,13 @@ if ($action == 'add' && $id && ! isset($_POST["cancel"]) && $user->rights->banqu
|
||||
$insertid = $object->addline($dateop, $operation, $label, $amount, $num_chq, $cat1, $user);
|
||||
if ($insertid > 0)
|
||||
{
|
||||
setEventMessage($langs->trans("RecordSaved"));
|
||||
setEventMessages($langs->trans("RecordSaved"), null, 'mesgs');
|
||||
header("Location: ".$_SERVER['PHP_SELF']."?id=".$id."&action=addline");
|
||||
exit;
|
||||
}
|
||||
else
|
||||
{
|
||||
setEventMessage($object->error, 'errors');
|
||||
setEventMessages($object->error, $object->errors, 'errors');
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -291,7 +291,7 @@ if ($result < 0)
|
||||
{
|
||||
$langs->load("errors");
|
||||
$error++;
|
||||
setEventMessage($langs->trans("ErrorFailedToCreateDir"), 'errors');
|
||||
setEventMessages($langs->trans("ErrorFailedToCreateDir"), null, 'errors');
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Copyright (C) 2004-2015 Laurent Destailleur <eldy@users.sourceforge.net>
|
||||
* Copyright (C) 2005-2009 Regis Houssin <regis.houssin@capnetworks.com>
|
||||
* Copyright (C) 2014-2015 Alexandre Spangaro <aspangaro.dolibarr@gmail.com>
|
||||
* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
|
||||
* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -102,19 +102,19 @@ if ($_POST["action"] == 'add')
|
||||
|
||||
if ($conf->global->MAIN_BANK_ACCOUNTANCY_CODE_ALWAYS_REQUIRED && empty($account->account_number))
|
||||
{
|
||||
setEventMessage($langs->transnoentitiesnoconv("ErrorFieldRequired",$langs->transnoentitiesnoconv("AccountancyCode")), 'error');
|
||||
setEventMessages($langs->transnoentitiesnoconv("ErrorFieldRequired",$langs->transnoentitiesnoconv("AccountancyCode")), null, 'error');
|
||||
$action='create'; // Force chargement page en mode creation
|
||||
$error++;
|
||||
}
|
||||
if (empty($account->ref))
|
||||
{
|
||||
setEventMessage($langs->transnoentitiesnoconv("ErrorFieldRequired",$langs->transnoentitiesnoconv("Ref")), 'errors');
|
||||
setEventMessages($langs->transnoentitiesnoconv("ErrorFieldRequired",$langs->transnoentitiesnoconv("Ref")), null, 'errors');
|
||||
$action='create'; // Force chargement page en mode creation
|
||||
$error++;
|
||||
}
|
||||
if (empty($account->label))
|
||||
{
|
||||
setEventMessage($langs->transnoentitiesnoconv("ErrorFieldRequired",$langs->transnoentitiesnoconv("LabelBankCashAccount")), 'errors');
|
||||
setEventMessages($langs->transnoentitiesnoconv("ErrorFieldRequired",$langs->transnoentitiesnoconv("LabelBankCashAccount")), null, 'errors');
|
||||
$action='create'; // Force chargement page en mode creation
|
||||
$error++;
|
||||
}
|
||||
@@ -130,7 +130,7 @@ if ($_POST["action"] == 'add')
|
||||
$_GET["id"]=$id; // Force chargement page en mode visu
|
||||
}
|
||||
else {
|
||||
setEventMessage($account->error,'errors');
|
||||
setEventMessages($account->error, $account->errors, 'errors');
|
||||
$action='create'; // Force chargement page en mode creation
|
||||
}
|
||||
}
|
||||
@@ -177,19 +177,19 @@ if ($_POST["action"] == 'update' && ! $_POST["cancel"])
|
||||
|
||||
if ($conf->global->MAIN_BANK_ACCOUNTANCY_CODE_ALWAYS_REQUIRED && empty($account->account_number))
|
||||
{
|
||||
setEventMessage($langs->transnoentitiesnoconv("ErrorFieldRequired",$langs->transnoentitiesnoconv("AccountancyCode")), 'error');
|
||||
setEventMessages($langs->transnoentitiesnoconv("ErrorFieldRequired",$langs->transnoentitiesnoconv("AccountancyCode")), null, 'error');
|
||||
$action='edit'; // Force chargement page en mode creation
|
||||
$error++;
|
||||
}
|
||||
if (empty($account->ref))
|
||||
{
|
||||
setEventMessage($langs->transnoentitiesnoconv("ErrorFieldRequired",$langs->transnoentitiesnoconv("Ref")), 'errors');
|
||||
setEventMessages($langs->transnoentitiesnoconv("ErrorFieldRequired",$langs->transnoentitiesnoconv("Ref")), null, 'errors');
|
||||
$action='edit'; // Force chargement page en mode creation
|
||||
$error++;
|
||||
}
|
||||
if (empty($account->label))
|
||||
{
|
||||
setEventMessage($langs->transnoentitiesnoconv("ErrorFieldRequired",$langs->transnoentitiesnoconv("LabelBankCashAccount")), 'errors');
|
||||
setEventMessages($langs->transnoentitiesnoconv("ErrorFieldRequired",$langs->transnoentitiesnoconv("LabelBankCashAccount")), null, 'errors');
|
||||
$action='edit'; // Force chargement page en mode creation
|
||||
$error++;
|
||||
}
|
||||
@@ -206,7 +206,7 @@ if ($_POST["action"] == 'update' && ! $_POST["cancel"])
|
||||
}
|
||||
else
|
||||
{
|
||||
setEventMessage($account->error, 'errors');
|
||||
setEventMessages($account->error, $account->errors, 'errors');
|
||||
$action='edit'; // Force chargement page edition
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,9 +133,9 @@ else if ($action == 'confirm_deletefile' && $confirm == 'yes') {
|
||||
|
||||
$ret = dol_delete_file($file, 0, 0, 0, $object);
|
||||
if ($ret) {
|
||||
setEventMessage($langs->trans("FileWasRemoved", GETPOST('urlfile')));
|
||||
setEventMessages($langs->trans("FileWasRemoved", GETPOST('urlfile')), null, 'mesgs');
|
||||
} else {
|
||||
setEventMessage($langs->trans("ErrorFailToDeleteFile", GETPOST('urlfile')), 'errors');
|
||||
setEventMessages($langs->trans("ErrorFailToDeleteFile", GETPOST('urlfile')), null, 'errors');
|
||||
}
|
||||
|
||||
Header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $id);
|
||||
|
||||
@@ -81,7 +81,7 @@ if ($result < 0)
|
||||
{
|
||||
$langs->load("errors");
|
||||
$error++;
|
||||
setEventMessage($langs->trans("ErrorFailedToCreateDir"), 'errors');
|
||||
setEventMessages($langs->trans("ErrorFailedToCreateDir"), null, 'errors');
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -113,7 +113,7 @@ else
|
||||
|
||||
if ($mode == 'standard')
|
||||
{
|
||||
// Chargement du tableau $amounts
|
||||
// Loading table $amounts
|
||||
$amounts = array();
|
||||
|
||||
$monthnext = $month+1;
|
||||
@@ -153,7 +153,7 @@ else
|
||||
dol_print_error($db);
|
||||
}
|
||||
|
||||
// Calcul de $solde avant le debut du graphe
|
||||
// Calculation of $solde before the start of the graph
|
||||
$solde = 0;
|
||||
|
||||
$sql = "SELECT SUM(b.amount)";
|
||||
@@ -265,7 +265,7 @@ else
|
||||
|
||||
if ($mode == 'standard')
|
||||
{
|
||||
// Chargement du tableau $amounts
|
||||
// Loading table $amounts
|
||||
$amounts = array();
|
||||
$sql = "SELECT date_format(b.datev,'%Y%m%d')";
|
||||
$sql.= ", SUM(b.amount)";
|
||||
@@ -296,7 +296,7 @@ else
|
||||
dol_print_error($db);
|
||||
}
|
||||
|
||||
// Calcul de $solde avant le debut du graphe
|
||||
// Calculation of $solde before the start of the graph
|
||||
$solde = 0;
|
||||
|
||||
$sql = "SELECT SUM(b.amount)";
|
||||
@@ -403,7 +403,7 @@ else
|
||||
|
||||
if ($mode == 'showalltime')
|
||||
{
|
||||
// Chargement du tableau $amounts
|
||||
// Loading table $amounts
|
||||
$amounts = array();
|
||||
|
||||
$sql = "SELECT date_format(b.datev,'%Y%m%d')";
|
||||
|
||||
@@ -113,7 +113,7 @@ foreach ($accounts as $key=>$type)
|
||||
{
|
||||
$result=$acc->load_board($user,$acc->id);
|
||||
if ($result<0) {
|
||||
setEventMessage($acc->error, 'errors');
|
||||
setEventMessages($acc->error, $acc->errors, 'errors');
|
||||
} else {
|
||||
print $result->nbtodo;
|
||||
if ($result->nbtodolate) print ' ('.$result->nbtodolate.img_warning($langs->trans("Late")).')';
|
||||
@@ -229,7 +229,7 @@ foreach ($accounts as $key=>$type)
|
||||
{
|
||||
$result=$acc->load_board($user,$acc->id);
|
||||
if ($result<0) {
|
||||
setEventMessage($acc->error, 'errors');
|
||||
setEventMessages($acc->error, $acc->errors, 'errors');
|
||||
} else {
|
||||
print $result->nbtodo;
|
||||
if ($result->nbtodolate) print ' ('.$result->nbtodolate.img_warning($langs->trans("Late")).')';
|
||||
|
||||
@@ -104,7 +104,7 @@ if ($user->rights->banque->modifier && $action == "update")
|
||||
|
||||
if ($ac->courant == 2 && $_POST['value'] != 'LIQ')
|
||||
{
|
||||
setEventMessage($langs->trans("ErrorCashAccountAcceptsOnlyCashMoney"), 'errors');
|
||||
setEventMessages($langs->trans("ErrorCashAccountAcceptsOnlyCashMoney"), null, 'errors');
|
||||
$error++;
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ if ($user->rights->banque->modifier && $action == "update")
|
||||
$result = $db->query($sql);
|
||||
if ($result)
|
||||
{
|
||||
setEventMessage($langs->trans("RecordSaved"));
|
||||
setEventMessages($langs->trans("RecordSaved"), null, 'mesgs');
|
||||
$db->commit();
|
||||
}
|
||||
else
|
||||
@@ -184,7 +184,7 @@ if ($user->rights->banque->consolidate && ($action == 'num_releve' || $action ==
|
||||
$result = $db->query($sql);
|
||||
if ($result)
|
||||
{
|
||||
setEventMessage($langs->trans("RecordSaved"));
|
||||
setEventMessages($langs->trans("RecordSaved"), null, 'mesgs');
|
||||
$db->commit();
|
||||
}
|
||||
else
|
||||
@@ -205,7 +205,7 @@ $form = new Form($db);
|
||||
|
||||
llxHeader();
|
||||
|
||||
// On initialise la liste des categories
|
||||
// The list of categories is initialized
|
||||
$sql = "SELECT rowid, label";
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX."bank_categ";
|
||||
$sql.= " ORDER BY label";
|
||||
|
||||
@@ -71,7 +71,7 @@ if ($action == 'rappro' && $user->rights->banque->consolidate)
|
||||
$result=$bankline->update_conciliation($user,$_POST["cat"]);
|
||||
if ($result < 0)
|
||||
{
|
||||
setEventMessage($bankline->error, 'errors');
|
||||
setEventMessages($bankline->error, $bankline->errors, 'errors');
|
||||
$error++;
|
||||
break;
|
||||
}
|
||||
@@ -83,7 +83,7 @@ if ($action == 'rappro' && $user->rights->banque->consolidate)
|
||||
{
|
||||
$error++;
|
||||
$langs->load("errors");
|
||||
setEventMessage($langs->trans("ErrorPleaseTypeBankTransactionReportName"), 'errors');
|
||||
setEventMessages($langs->trans("ErrorPleaseTypeBankTransactionReportName"), null, 'errors');
|
||||
}
|
||||
|
||||
if (! $error)
|
||||
|
||||
@@ -113,18 +113,18 @@ if ($action == 'add')
|
||||
if (! $error)
|
||||
{
|
||||
$mesgs = $langs->trans("TransferFromToDone","<a href=\"account.php?account=".$accountfrom->id."\">".$accountfrom->label."</a>","<a href=\"account.php?account=".$accountto->id."\">".$accountto->label."</a>",$amount,$langs->transnoentities("Currency".$conf->currency));
|
||||
setEventMessage($mesgs);
|
||||
setEventMessages($mesgs, null, 'mesgs');
|
||||
$db->commit();
|
||||
}
|
||||
else
|
||||
{
|
||||
setEventMessage($accountfrom->error.' '.$accountto->error, 'errors');
|
||||
setEventMessages($accountfrom->error.' '.$accountto->error, null, 'errors');
|
||||
$db->rollback();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setEventMessage($langs->trans("ErrorFromToAccountsMustDiffers"), 'errors');
|
||||
setEventMessages($langs->trans("ErrorFromToAccountsMustDiffers"), null, 'errors');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -132,7 +132,7 @@ if ($action == 'add')
|
||||
|
||||
|
||||
/*
|
||||
* Affichage
|
||||
* View
|
||||
*/
|
||||
|
||||
llxHeader();
|
||||
|
||||
@@ -122,6 +122,34 @@ $fieldstosearchall = array(
|
||||
's.nom'=>"ThirdParty",
|
||||
);
|
||||
|
||||
// Definition of fields for list
|
||||
$arrayfields=array(
|
||||
'p.lastname'=>array('label'=>$langs->trans("Lastname"), 'checked'=>1),
|
||||
'p.firstname'=>array('label'=>$langs->trans("Firsname"), 'checked'=>1),
|
||||
'p.poste'=>array('label'=>$langs->trans("Post"), 'checked'=>1),
|
||||
'p.town'=>array('label'=>$langs->trans("Town"), 'checked'=>0),
|
||||
'p.zip'=>array('label'=>$langs->trans("Zip"), 'checked'=>0),
|
||||
'p.phone'=>array('label'=>$langs->trans("PhonePro"), 'checked'=>1),
|
||||
'p.phone_perso'=>array('label'=>$langs->trans("PhonePerso"), 'checked'=>0),
|
||||
'p.phone_mobile'=>array('label'=>$langs->trans("PhoneMobile"), 'checked'=>1),
|
||||
'p.fax'=>array('label'=>$langs->trans("Fax"), 'checked'=>1),
|
||||
'p.email'=>array('label'=>$langs->trans("Email"), 'checked'=>1),
|
||||
'p.skype'=>array('label'=>$langs->trans("Skype"), 'checked'=>1, 'enabled'=>(! empty($conf->skype->enabled))),
|
||||
'p.thirdparty'=>array('label'=>$langs->trans("ThirdParty"), 'checked'=>1, 'enabled'=>empty($conf->global->SOCIETE_DISABLE_CONTACTS)),
|
||||
'p.priv'=>array('label'=>$langs->trans("ContactVisibility"), 'checked'=>1, 'position'=>200),
|
||||
'p.datec'=>array('label'=>$langs->trans("DateCreation"), 'checked'=>0, 'position'=>500),
|
||||
'p.tms'=>array('label'=>$langs->trans("DateModificationShort"), 'checked'=>0, 'position'=>500),
|
||||
'p.statut'=>array('label'=>$langs->trans("Status"), 'checked'=>1, 'position'=>1000),
|
||||
);
|
||||
// Extra fields
|
||||
if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label))
|
||||
{
|
||||
foreach($extrafields->attribute_label as $key => $val)
|
||||
{
|
||||
$arrayfields["ef.".$key]=array('label'=>$extrafields->attribute_label[$key], 'checked'=>$extrafields->attribute_list[$key], 'position'=>$extrafields->attribute_pos[$key], 'enabled'=>$extrafields->attribute_perms[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Actions
|
||||
@@ -386,35 +414,10 @@ if ($result)
|
||||
print '</div>';
|
||||
}
|
||||
|
||||
$arrayfields=array(
|
||||
'p.lastname'=>array('label'=>$langs->trans("Lastname"), 'checked'=>1),
|
||||
'p.firstname'=>array('label'=>$langs->trans("Firsname"), 'checked'=>1),
|
||||
'p.poste'=>array('label'=>$langs->trans("Post"), 'checked'=>1),
|
||||
'p.town'=>array('label'=>$langs->trans("Town"), 'checked'=>0),
|
||||
'p.zip'=>array('label'=>$langs->trans("Zip"), 'checked'=>0),
|
||||
'p.phone'=>array('label'=>$langs->trans("PhonePro"), 'checked'=>1),
|
||||
'p.phone_perso'=>array('label'=>$langs->trans("PhonePerso"), 'checked'=>0),
|
||||
'p.phone_mobile'=>array('label'=>$langs->trans("PhoneMobile"), 'checked'=>1),
|
||||
'p.fax'=>array('label'=>$langs->trans("Fax"), 'checked'=>1),
|
||||
'p.email'=>array('label'=>$langs->trans("Email"), 'checked'=>1),
|
||||
'p.skype'=>array('label'=>$langs->trans("Skype"), 'checked'=>1, 'enabled'=>(! empty($conf->skype->enabled))),
|
||||
'p.thirdparty'=>array('label'=>$langs->trans("ThirdParty"), 'checked'=>1, 'enabled'=>empty($conf->global->SOCIETE_DISABLE_CONTACTS)),
|
||||
'p.priv'=>array('label'=>$langs->trans("ContactVisibility"), 'checked'=>1, 'position'=>200),
|
||||
'p.datec'=>array('label'=>$langs->trans("DateCreation"), 'checked'=>0, 'position'=>500),
|
||||
'p.tms'=>array('label'=>$langs->trans("DateModificationShort"), 'checked'=>0, 'position'=>500),
|
||||
'p.statut'=>array('label'=>$langs->trans("Status"), 'checked'=>1, 'position'=>1000),
|
||||
);
|
||||
// Extra fields
|
||||
if (is_array($extrafields->attribute_label) && count($extrafields->attribute_label))
|
||||
{
|
||||
foreach($extrafields->attribute_label as $key => $val)
|
||||
{
|
||||
$arrayfields["ef.".$key]=array('label'=>$extrafields->attribute_label[$key], 'checked'=>$extrafields->attribute_list[$key], 'position'=>$extrafields->attribute_pos[$key], 'enabled'=>$extrafields->attribute_perms[$key]);
|
||||
}
|
||||
}
|
||||
$varpage=empty($contextpage)?$_SERVER["PHP_SELF"]:$contextpage;
|
||||
$selectedfields=$form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage); // This also change content of $arrayfields
|
||||
print '<table class="liste '.($moreforfilter?"listwithfilterbefore":"").'">';
|
||||
|
||||
print '<table class="liste '.($moreforfilter?"listwithfilterbefore":"").'">';
|
||||
|
||||
// Ligne des titres
|
||||
print '<tr class="liste_titre">';
|
||||
|
||||
@@ -2866,6 +2866,7 @@ abstract class CommonObject
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->errors[] = $this->db->lasterror();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -2903,6 +2904,7 @@ abstract class CommonObject
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->errors[] = $this->db->lasterror();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ class Form
|
||||
* @param string $htmlname Name of select field ('edit' prefix will be added)
|
||||
* @param string $preselected Name of Value to show/edit (not used in this function)
|
||||
* @param object $object Object
|
||||
* @param boolean $perm Permission to allow button to edit parameter
|
||||
* @param boolean $perm Permission to allow button to edit parameter. Set it to 0 to have a not edited field.
|
||||
* @param string $typeofdata Type of data ('string' by default, 'email', 'numeric:99', 'text' or 'textarea:rows:cols', 'day' or 'datepicker', 'ckeditor:dolibarr_zzz:width:height:savemethod:1:rows:cols', 'select;xxx[:class]'...)
|
||||
* @param string $moreparam More param to add on a href URL
|
||||
* @return string HTML edit field
|
||||
@@ -104,11 +104,11 @@ class Form
|
||||
}
|
||||
else
|
||||
{
|
||||
$ret.='<table class="nobordernopadding" width="100%"><tr><td class="nowrap">';
|
||||
if (GETPOST('action') != 'edit'.$htmlname && $perm) $ret.='<table class="nobordernopadding" width="100%"><tr><td class="nowrap">';
|
||||
$ret.=$langs->trans($text);
|
||||
$ret.='</td>';
|
||||
if (GETPOST('action') != 'edit'.$htmlname && $perm) $ret.='</td>';
|
||||
if (GETPOST('action') != 'edit'.$htmlname && $perm) $ret.='<td align="right"><a href="'.$_SERVER["PHP_SELF"].'?action=edit'.$htmlname.'&id='.$object->id.$moreparam.'">'.img_edit($langs->trans('Edit'),1).'</a></td>';
|
||||
$ret.='</tr></table>';
|
||||
if (GETPOST('action') != 'edit'.$htmlname && $perm) $ret.='</tr></table>';
|
||||
}
|
||||
|
||||
return $ret;
|
||||
|
||||
@@ -98,4 +98,18 @@ class Menu
|
||||
if (count($this->liste) > 1) array_pop($this->liste);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return number of visible entries (gray or not)
|
||||
*
|
||||
* @return int Number of visible (gray or not) menu entries
|
||||
*/
|
||||
function getNbOfVisibleMenuEntries()
|
||||
{
|
||||
$nb=0;
|
||||
foreach($this->liste as $val)
|
||||
{
|
||||
if (! empty($val['enabled'])) $nb++;
|
||||
}
|
||||
return $nb;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,16 +52,13 @@ function ajax_autocompleter($selected, $htmlname, $url, $urloption='', $minLengt
|
||||
// Remove product id before select another product
|
||||
// use keyup instead change to avoid loosing the product id
|
||||
$("input#search_'.$htmlname.'").keydown(function() {
|
||||
//console.log(\'purge_id_after_keydown\');
|
||||
$("#'.$htmlname.'").val("");
|
||||
});
|
||||
$("input#search_'.$htmlname.'").change(function() {
|
||||
//console.log(\'change\');
|
||||
$("#'.$htmlname.'").trigger("change");
|
||||
});
|
||||
// Check when keyup
|
||||
$("input#search_'.$htmlname.'").keyup(function() {
|
||||
//console.log(\'keyup\');
|
||||
if ($(this).val().length == 0)
|
||||
{
|
||||
$("#search_'.$htmlname.'").val("");
|
||||
@@ -127,7 +124,6 @@ function ajax_autocompleter($selected, $htmlname, $url, $urloption='', $minLengt
|
||||
dataType: "json",
|
||||
minLength: '.$minLength.',
|
||||
select: function( event, ui ) { // Function ran when new value is selected into javascript combo
|
||||
//console.log(\'set value of id with \'+ui.item.id);
|
||||
$("#'.$htmlname.'").val(ui.item.id).trigger("change"); // Select new value
|
||||
// Disable an element
|
||||
if (options.option_disabled) {
|
||||
@@ -171,8 +167,6 @@ function ajax_autocompleter($selected, $htmlname, $url, $urloption='', $minLengt
|
||||
}
|
||||
});
|
||||
}
|
||||
console.log("ajax_autocompleter new value selected, we trigger change");
|
||||
$("#search_'.$htmlname.'").trigger("change"); // To tell that input text field was modified
|
||||
}
|
||||
,delay: 500
|
||||
}).data("ui-autocomplete")._renderItem = function( ul, item ) {
|
||||
|
||||
@@ -157,9 +157,9 @@ class MenuManager
|
||||
/**
|
||||
* Show menu
|
||||
*
|
||||
* @param string $mode 'top', 'left', 'jmobile'
|
||||
* @param string $mode 'top', 'left', 'jmobile'
|
||||
* @param array $moredata An array with more data to output
|
||||
* @return string
|
||||
* @return int 0 or nb of top menu entries if $mode = 'topnb'
|
||||
*/
|
||||
function showmenu($mode, $moredata=null)
|
||||
{
|
||||
@@ -169,8 +169,8 @@ class MenuManager
|
||||
|
||||
if ($this->type_user == 1)
|
||||
{
|
||||
$conf->global->MAIN_SEARCHFORM_SOCIETE=0;
|
||||
$conf->global->MAIN_SEARCHFORM_CONTACT=0;
|
||||
$conf->global->MAIN_SEARCHFORM_SOCIETE_DISABLED=1;
|
||||
$conf->global->MAIN_SEARCHFORM_CONTACT_DISABLED=1;
|
||||
}
|
||||
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/menu.class.php';
|
||||
@@ -178,6 +178,13 @@ class MenuManager
|
||||
|
||||
if ($mode == 'top') print_auguria_menu($this->db,$this->atarget,$this->type_user,$this->tabMenu,$this->menu,0);
|
||||
if ($mode == 'left') print_left_auguria_menu($this->db,$this->menu_array,$this->menu_array_after,$this->tabMenu,$this->menu,0,'','',$moredata);
|
||||
|
||||
if ($mode == 'topnb')
|
||||
{
|
||||
print_auguria_menu($this->db,$this->atarget,$this->type_user,$this->tabMenu,$this->menu,1);
|
||||
return $this->menu->getNbOfVisibleMenuEntries();
|
||||
}
|
||||
|
||||
if ($mode == 'jmobile')
|
||||
{
|
||||
print_auguria_menu($this->db,$this->atarget,$this->type_user,$this->tabMenu,$this->menu,1);
|
||||
|
||||
@@ -113,7 +113,7 @@ class MenuManager
|
||||
*
|
||||
* @param string $mode 'top', 'left', 'jmobile'
|
||||
* @param array $moredata An array with more data to output
|
||||
* @return string
|
||||
* @return int 0 or nb of top menu entries if $mode = 'topnb'
|
||||
*/
|
||||
function showmenu($mode, $moredata=null)
|
||||
{
|
||||
@@ -123,8 +123,8 @@ class MenuManager
|
||||
|
||||
if ($this->type_user == 1)
|
||||
{
|
||||
$conf->global->MAIN_SEARCHFORM_SOCIETE=0;
|
||||
$conf->global->MAIN_SEARCHFORM_CONTACT=0;
|
||||
$conf->global->MAIN_SEARCHFORM_SOCIETE_DISABLED=0;
|
||||
$conf->global->MAIN_SEARCHFORM_CONTACT_DISABLED=1;
|
||||
}
|
||||
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/class/menu.class.php';
|
||||
@@ -142,6 +142,12 @@ class MenuManager
|
||||
if ($mode == 'left') print_eldy_menu($this->db,$this->atarget,$this->type_user,$this->tabMenu,$this->menu,0);
|
||||
}
|
||||
|
||||
if ($mode == 'topnb')
|
||||
{
|
||||
print_eldy_menu($this->db,$this->atarget,$this->type_user,$this->tabMenu,$this->menu,1);
|
||||
return $this->menu->getNbOfVisibleMenuEntries();
|
||||
}
|
||||
|
||||
if ($mode == 'jmobile')
|
||||
{
|
||||
print_eldy_menu($this->db,$this->atarget,$this->type_user,$this->tabMenu,$this->menu,1);
|
||||
@@ -238,6 +244,7 @@ class MenuManager
|
||||
unset($this->menu);
|
||||
|
||||
//print 'xx'.$mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ class MenuManager
|
||||
*
|
||||
* @param string $mode 'top', 'left', 'jmobile'
|
||||
* @param array $moredata An array with more data to output
|
||||
* @return string
|
||||
* @return int 0 or nb of top menu entries if $mode = 'topnb'
|
||||
*/
|
||||
function showmenu($mode, $moredata=null)
|
||||
{
|
||||
@@ -78,6 +78,11 @@ class MenuManager
|
||||
$noout=0;
|
||||
if ($mode == 'jmobile') $noout=1;
|
||||
|
||||
if ($mode == 'topnb')
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ($mode == 'top' || $mode == 'jmobile')
|
||||
{
|
||||
if (empty($noout)) print_start_menu_array_empty();
|
||||
|
||||
@@ -523,7 +523,6 @@ jQuery(document).ready(function() {
|
||||
/* When changing predefined product, we reload list of supplier prices */
|
||||
$("#idprod, #idprodfournprice").change(function()
|
||||
{
|
||||
console.log("change #idprod, #idprodfournprice, conf->global->MARGIN_TYPE=<?php echo $conf->global->MARGIN_TYPE ?>");
|
||||
setforpredef();
|
||||
jQuery('#trlinefordates').show();
|
||||
|
||||
|
||||
@@ -208,7 +208,7 @@ define('MAIN_DB_PREFIX',$dolibarr_main_db_prefix);
|
||||
// Path to root libraries
|
||||
if (! defined('ADODB_PATH')) { define('ADODB_PATH', (!isset($dolibarr_lib_ADODB_PATH))?DOL_DOCUMENT_ROOT.'/includes/adodbtime/':(empty($dolibarr_lib_ADODB_PATH)?'':$dolibarr_lib_ADODB_PATH.'/')); }
|
||||
if (! defined('FPDF_PATH')) { define('FPDF_PATH', (empty($dolibarr_lib_FPDF_PATH))?DOL_DOCUMENT_ROOT.'/includes/fpdf/':$dolibarr_lib_FPDF_PATH.'/'); } // Used only for package that can't include tcpdf
|
||||
if (! defined('TCPDF_PATH')) { define('TCPDF_PATH', (empty($dolibarr_lib_TCPDF_PATH))?DOL_DOCUMENT_ROOT.'/includes/tecnick.com/tcpdf/':$dolibarr_lib_TCPDF_PATH.'/'); }
|
||||
if (! defined('TCPDF_PATH')) { define('TCPDF_PATH', (empty($dolibarr_lib_TCPDF_PATH))?DOL_DOCUMENT_ROOT.'/includes/tecnickcom/tcpdf/':$dolibarr_lib_TCPDF_PATH.'/'); }
|
||||
if (! defined('FPDI_PATH')) { define('FPDI_PATH', (empty($dolibarr_lib_FPDI_PATH))?DOL_DOCUMENT_ROOT.'/includes/fpdfi/':$dolibarr_lib_FPDI_PATH.'/'); }
|
||||
if (! defined('TCPDI_PATH')) { define('TCPDI_PATH', (empty($dolibarr_lib_TCPDI_PATH))?DOL_DOCUMENT_ROOT.'/includes/tcpdi/':$dolibarr_lib_TCPDI_PATH.'/'); }
|
||||
if (! defined('NUSOAP_PATH')) { define('NUSOAP_PATH', (!isset($dolibarr_lib_NUSOAP_PATH))?DOL_DOCUMENT_ROOT.'/includes/nusoap/lib/':(empty($dolibarr_lib_NUSOAP_PATH)?'':$dolibarr_lib_NUSOAP_PATH.'/')); }
|
||||
|
||||
@@ -33,9 +33,13 @@ if (! $user->admin)
|
||||
|
||||
$error=0;
|
||||
|
||||
$action = GETPOST('action', 'alpha');
|
||||
|
||||
$object = new Establishment($db);
|
||||
// List of statut
|
||||
static $tmpstatus2label=array(
|
||||
'0'=>'OpenEtablishment',
|
||||
'1'=>'CloseEtablishment'
|
||||
);
|
||||
$status2label=array('');
|
||||
foreach ($tmpstatus2label as $key => $val) $status2label[$key]=$langs->trans($val);
|
||||
|
||||
/*
|
||||
* Actions
|
||||
@@ -44,10 +48,24 @@ $object = new Establishment($db);
|
||||
/*
|
||||
* View
|
||||
*/
|
||||
$page_name = "Establishments";
|
||||
llxHeader('', $langs->trans($page_name));
|
||||
llxHeader('', $langs->trans("Establishments"));
|
||||
|
||||
$sortorder = GETPOST("sortorder");
|
||||
$sortfield = GETPOST("sortfield");
|
||||
if (!$sortorder) $sortorder="DESC";
|
||||
if (!$sortfield) $sortfield="e.rowid";
|
||||
|
||||
if ($page == -1) {
|
||||
$page = 0 ;
|
||||
}
|
||||
|
||||
$offset = $conf->liste_limit * $page;
|
||||
$pageprev = $page - 1;
|
||||
$pagenext = $page + 1;
|
||||
$limit = $conf->liste_limit;
|
||||
|
||||
$form = new Form($db);
|
||||
$establishmenttmp=new Establishment($db);
|
||||
|
||||
dol_htmloutput_mesg($mesg);
|
||||
|
||||
@@ -62,43 +80,50 @@ dol_fiche_head($head, 'establishments', $langs->trans("HRM"), 0, "user");
|
||||
$sql = "SELECT e.rowid, e.name, e.address, e.zip, e.town, e.status";
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX."establishment as e";
|
||||
$sql.= " WHERE e.entity = ".$conf->entity;
|
||||
$sql.= $db->order($sortfield,$sortorder);
|
||||
$sql.= $db->plimit($limit+1, $offset);
|
||||
|
||||
$result = $db->query($sql);
|
||||
if ($result)
|
||||
{
|
||||
$var=false;
|
||||
$num = $db->num_rows($result);
|
||||
|
||||
$i = 0;
|
||||
$num = $db->num_rows($result);
|
||||
$i = 0;
|
||||
|
||||
// Load attribute_label
|
||||
print '<table class="noborder" width="100%">';
|
||||
print '<tr class="liste_titre">';
|
||||
print '<td>'.$langs->trans("Ref").'</td>';
|
||||
print '<td>'.$langs->trans("Name").'</td>';
|
||||
print '<td>'.$langs->trans("Address").'</td>';
|
||||
print '<td>'.$langs->trans("Zipcode").'</td>';
|
||||
print '<td>'.$langs->trans("Town").'</td>';
|
||||
print '<td align="right">'.$langs->trans("Status").'</td>';
|
||||
print '</tr>';
|
||||
print "<tr class=\"liste_titre\">";
|
||||
print_liste_field_titre($langs->trans("Name"),$_SERVER["PHP_SELF"],"e.name","","","",$sortfield,$sortorder);
|
||||
print_liste_field_titre($langs->trans("Address"),$_SERVER["PHP_SELF"],"e.address","","","",$sortfield,$sortorder);
|
||||
print_liste_field_titre($langs->trans("Zipcode"),$_SERVER["PHP_SELF"],"e.zip","","","",$sortfield,$sortorder);
|
||||
print_liste_field_titre($langs->trans("Town"),$_SERVER["PHP_SELF"],"e.town","","","",$sortfield,$sortorder);
|
||||
print_liste_field_titre($langs->trans("Status"),$_SERVER["PHP_SELF"],"e.status","","",'align="right"',$sortfield,$sortorder);
|
||||
print "</tr>\n";
|
||||
|
||||
if ($num)
|
||||
$var=true;
|
||||
|
||||
if ($num > 0)
|
||||
{
|
||||
$establishmentstatic=new Establishment($db);
|
||||
|
||||
while ($i < $num && $i < $max)
|
||||
{
|
||||
while ($i < min($num,$limit))
|
||||
{
|
||||
$obj = $db->fetch_object($result);
|
||||
$fiscalyearstatic->id=$obj->rowid;
|
||||
print '<tr '.$bc[$var].'>';
|
||||
print '<td><a href="admin_establishment_card.php?id='.$obj->rowid.'">'.img_object($langs->trans("ShowEstablishment"),"building").' '.$obj->rowid.'</a></td>';
|
||||
print '<td align="left">'.$obj->name.'</td>';
|
||||
|
||||
$establishmentstatic->id=$obj->rowid;
|
||||
$establishmentstatic->name=$obj->name;
|
||||
|
||||
$var=!$var;
|
||||
print '<tr '.$bc[$var].'>';
|
||||
print '<td>'.$establishmentstatic->getNomUrl(1).'</td>';
|
||||
print '<td align="left">'.$obj->address.'</td>';
|
||||
print '<td align="left">'.$obj->zip.'</td>';
|
||||
print '<td align="left">'.$obj->town.'</td>';
|
||||
print '<td align="right">'.$establishmentstatic->LibStatut($obj->status,5).'</td>';
|
||||
print '</tr>';
|
||||
$var=!$var;
|
||||
|
||||
print '<td align="right">';
|
||||
print $establishmenttmp->getLibStatus(5);
|
||||
print '</td>';
|
||||
print "</tr>\n";
|
||||
|
||||
$i++;
|
||||
}
|
||||
|
||||
|
||||
@@ -67,26 +67,41 @@ class Employee extends CommonObject
|
||||
* @param int $id Id of record to load
|
||||
* @return int <0 if KO, >0 if OK
|
||||
*/
|
||||
function fetch($id)
|
||||
function fetch($id='')
|
||||
{
|
||||
$sql = "SELECT rowid, firstname, lastname, status, fk_user";
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX."user";
|
||||
global $conf, $user;
|
||||
|
||||
$sql = "SELECT e.rowid, e.firstname, e.lastname, e.gender, e.email, e.statut, e.entity, e.fk_user";
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX."user as e";
|
||||
$sql.= " WHERE rowid = ".$id;
|
||||
|
||||
dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
|
||||
$result = $this->db->query($sql);
|
||||
if ( $result )
|
||||
if ($result)
|
||||
{
|
||||
$obj = $this->db->fetch_object($result);
|
||||
if ($obj)
|
||||
{
|
||||
$this->id = $obj->rowid;
|
||||
$this->lastname = $obj->lastname;
|
||||
$this->firstname = $obj->firstname;
|
||||
$this->gender = $obj->gender;
|
||||
$this->address = $obj->address;
|
||||
$this->zip = $obj->zip;
|
||||
$this->town = $obj->town;
|
||||
$this->statut = $obj->statut;
|
||||
$this->photo = $obj->photo;
|
||||
|
||||
$this->id = $obj->rowid;
|
||||
$this->name = $obj->name;
|
||||
$this->address = $obj->address;
|
||||
$this->zip = $obj->zip;
|
||||
$this->town = $obj->town;
|
||||
$this->status = $obj->status;
|
||||
$this->db->free($result);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->error="EMPLOYEENOTFOUND";
|
||||
dol_syslog(get_class($this)."::fetch user not found", LOG_DEBUG);
|
||||
|
||||
return 1;
|
||||
$this->db->free($result);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -34,6 +34,7 @@ class Establishment extends CommonObject
|
||||
public $fk_element = 'fk_establishment';
|
||||
protected $ismultientitymanaged = 1; // 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
|
||||
|
||||
var $id;
|
||||
var $rowid;
|
||||
|
||||
var $name;
|
||||
@@ -43,6 +44,8 @@ class Establishment extends CommonObject
|
||||
var $status; // 0=open, 1=closed
|
||||
var $entity;
|
||||
|
||||
var $country_id;
|
||||
|
||||
var $statuts=array();
|
||||
var $statuts_short=array();
|
||||
|
||||
@@ -69,12 +72,18 @@ class Establishment extends CommonObject
|
||||
*/
|
||||
function create($user)
|
||||
{
|
||||
global $conf;
|
||||
global $conf, $langs;
|
||||
|
||||
$error = 0;
|
||||
|
||||
$ret = 0;
|
||||
$now=dol_now();
|
||||
|
||||
// Clean parameters
|
||||
$this->address=($this->address>0?$this->address:$this->address);
|
||||
$this->zip=($this->zip>0?$this->zip:$this->zip);
|
||||
$this->town=($this->town>0?$this->town:$this->town);
|
||||
$this->country_id=($this->country_id>0?$this->country_id:$this->country_id);
|
||||
|
||||
$this->db->begin();
|
||||
|
||||
$sql = "INSERT INTO ".MAIN_DB_PREFIX."establishment (";
|
||||
@@ -83,22 +92,22 @@ class Establishment extends CommonObject
|
||||
$sql.= ", zip";
|
||||
$sql.= ", town";
|
||||
$sql.= ", status";
|
||||
$sql.= ", fk_country";
|
||||
$sql.= ", entity";
|
||||
$sql.= ", datec";
|
||||
$sql.= ", fk_user_author";
|
||||
$sql.= ") VALUES (";
|
||||
$sql.= " '".$this->name."'";
|
||||
$sql.= ", '".$this->address."'";
|
||||
$sql.= ", '".$this->zip."'";
|
||||
$sql.= ", '".$this->town."'";
|
||||
$sql.= " '".$this->db->escape($this->name)."'";
|
||||
$sql.= ", '".$this->db->escape($this->address)."'";
|
||||
$sql.= ", '".$this->db->escape($this->zip)."'";
|
||||
$sql.= ", '".$this->db->escape($this->town)."'";
|
||||
$sql.= ", ".$this->country_id;
|
||||
$sql.= ", ".$this->status;
|
||||
$sql.= ", ".$conf->entity;
|
||||
$sql.= ", '".$this->db->idate($now)."'";
|
||||
$sql.= ", ". $user->id;
|
||||
$sql.= ")";
|
||||
|
||||
$this->db->begin();
|
||||
|
||||
dol_syslog(get_class($this)."::create", LOG_DEBUG);
|
||||
$resql = $this->db->query($sql);
|
||||
if (! $resql) {
|
||||
@@ -148,6 +157,7 @@ class Establishment extends CommonObject
|
||||
$sql .= ", address = '".$this->address."'";
|
||||
$sql .= ", zip = '".$this->zip."'";
|
||||
$sql .= ", town = '".$this->town."'";
|
||||
|
||||
$sql .= ", status = '".$this->status."'";
|
||||
$sql .= ", fk_user_mod = " . $user->id;
|
||||
$sql .= " WHERE rowid = ".$this->id;
|
||||
@@ -172,9 +182,11 @@ class Establishment extends CommonObject
|
||||
*/
|
||||
function fetch($id)
|
||||
{
|
||||
$sql = "SELECT rowid, name, address, zip, town, status";
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX."establishment";
|
||||
$sql.= " WHERE rowid = ".$id;
|
||||
$sql = "SELECT e.rowid, e.name, e.address, e.zip, e.town, e.status, e.fk_country as country_id,";
|
||||
$sql.= ' c.code as country_code, c.label as country';
|
||||
$sql.= " FROM ".MAIN_DB_PREFIX."establishment as e";
|
||||
$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_country as c ON e.fk_country = c.rowid';
|
||||
$sql.= " WHERE e.rowid = ".$id;
|
||||
|
||||
dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
|
||||
$result = $this->db->query($sql);
|
||||
@@ -189,6 +201,10 @@ class Establishment extends CommonObject
|
||||
$this->town = $obj->town;
|
||||
$this->status = $obj->status;
|
||||
|
||||
$this->country_id = $obj->country_id;
|
||||
$this->country_code = $obj->country_code;
|
||||
$this->country = $obj->country;
|
||||
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
@@ -257,23 +273,23 @@ class Establishment extends CommonObject
|
||||
}
|
||||
if ($mode == 2)
|
||||
{
|
||||
if ($status==0) return img_picto($langs->trans($this->statuts_short[$status]),'status4').' '.$langs->trans($this->statuts_short[$status]);
|
||||
if ($status==1) return img_picto($langs->trans($this->statuts_short[$status]),'status8').' '.$langs->trans($this->statuts_short[$status]);
|
||||
if ($status==0) return img_picto($langs->trans($this->statuts_short[$status]),'statut4').' '.$langs->trans($this->statuts_short[$status]);
|
||||
if ($status==1) return img_picto($langs->trans($this->statuts_short[$status]),'statut8').' '.$langs->trans($this->statuts_short[$status]);
|
||||
}
|
||||
if ($mode == 3)
|
||||
{
|
||||
if ($status==0 && ! empty($this->statuts_short[$status])) return img_picto($langs->trans($this->statuts_short[$status]),'status4');
|
||||
if ($status==1 && ! empty($this->statuts_short[$status])) return img_picto($langs->trans($this->statuts_short[$status]),'status8');
|
||||
if ($status==0 && ! empty($this->statuts_short[$status])) return img_picto($langs->trans($this->statuts_short[$status]),'statut4');
|
||||
if ($status==1 && ! empty($this->statuts_short[$status])) return img_picto($langs->trans($this->statuts_short[$status]),'statut8');
|
||||
}
|
||||
if ($mode == 4)
|
||||
{
|
||||
if ($status==0 && ! empty($this->statuts_short[$status])) return img_picto($langs->trans($this->statuts_short[$status]),'status4').' '.$langs->trans($this->statuts[$status]);
|
||||
if ($status==1 && ! empty($this->statuts_short[$status])) return img_picto($langs->trans($this->statuts_short[$status]),'status8').' '.$langs->trans($this->statuts[$status]);
|
||||
if ($status==0 && ! empty($this->statuts_short[$status])) return img_picto($langs->trans($this->statuts_short[$status]),'statut4').' '.$langs->trans($this->statuts[$status]);
|
||||
if ($status==1 && ! empty($this->statuts_short[$status])) return img_picto($langs->trans($this->statuts_short[$status]),'statut8').' '.$langs->trans($this->statuts[$status]);
|
||||
}
|
||||
if ($mode == 5)
|
||||
{
|
||||
if ($status==0 && ! empty($this->statuts_short[$status])) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'status4');
|
||||
if ($status==1 && ! empty($this->statuts_short[$status])) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'status8');
|
||||
if ($status==0 && ! empty($this->statuts_short[$status])) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut4');
|
||||
if ($status==1 && ! empty($this->statuts_short[$status])) return $langs->trans($this->statuts_short[$status]).' '.img_picto($langs->trans($this->statuts_short[$status]),'statut8');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,20 +314,22 @@ class Establishment extends CommonObject
|
||||
{
|
||||
$obj = $this->db->fetch_object($result);
|
||||
$this->id = $obj->rowid;
|
||||
|
||||
$this->date_creation = $this->db->jdate($obj->datec);
|
||||
if ($obj->fk_user_author)
|
||||
{
|
||||
$cuser = new User($this->db);
|
||||
$cuser->fetch($obj->fk_user_author);
|
||||
$this->user_creation = $cuser;
|
||||
}
|
||||
if ($obj->fk_user_modif)
|
||||
if ($obj->fk_user_mod)
|
||||
{
|
||||
$muser = new User($this->db);
|
||||
$muser->fetch($obj->fk_user_mod);
|
||||
$this->user_modification = $muser;
|
||||
|
||||
$this->date_modification = $this->db->jdate($obj->tms);
|
||||
}
|
||||
$this->date_creation = $this->db->jdate($obj->datec);
|
||||
$this->date_modification = $this->db->jdate($obj->tms);
|
||||
}
|
||||
$this->db->free($result);
|
||||
}
|
||||
@@ -321,4 +339,46 @@ class Establishment extends CommonObject
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return clicable name (with picto eventually)
|
||||
*
|
||||
* @param int $withpicto 0=No picto, 1=Include picto into link, 2=Only picto
|
||||
* @return string String with URL
|
||||
*/
|
||||
function getNomUrl($withpicto=0)
|
||||
{
|
||||
global $langs;
|
||||
|
||||
$result='';
|
||||
|
||||
$link = '<a href="'.DOL_URL_ROOT.'/hrm/establishment/card.php?id='.$this->id.'">';
|
||||
$linkend='</a>';
|
||||
|
||||
$picto='building';
|
||||
|
||||
$label=$langs->trans("Show").': '.$this->name;
|
||||
|
||||
if ($withpicto) $result.=($link.img_object($label,$picto).$linkend);
|
||||
if ($withpicto && $withpicto != 2) $result.=' ';
|
||||
if ($withpicto != 2) $result.=$link.$this->name.$linkend;
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return account country code
|
||||
*
|
||||
* @return string country code
|
||||
*/
|
||||
function getCountryCode()
|
||||
{
|
||||
global $mysoc;
|
||||
|
||||
// We return country code of bank account
|
||||
if (! empty($this->country_code)) return $this->country_code;
|
||||
|
||||
// We return country code of managed company
|
||||
if (! empty($mysoc->country_code)) return $mysoc->country_code;
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,8 +169,8 @@ if ($result)
|
||||
$employeestatic->email=$obj->email;
|
||||
$employeestatic->gender=$obj->gender;
|
||||
$employeestatic->societe_id=$obj->fk_soc;
|
||||
$employeestatic->firstname='';
|
||||
$employeestatic->lastname=$obj->login;
|
||||
$employeestatic->firstname=$obj->firstname;
|
||||
$employeestatic->lastname=$obj->lastname;
|
||||
|
||||
$li=$employeestatic->getNomUrl(1,'',0,0,24,1);
|
||||
|
||||
@@ -203,8 +203,7 @@ if ($result)
|
||||
print '</td>';
|
||||
|
||||
// Statut
|
||||
$employeestatic->statut=$obj->statut;
|
||||
print '<td align="right">'.$employeestatic->getLibStatut(5).'</td>';
|
||||
print '<td align="center">'.$employeestatic->getLibStatut(5).'</td>';
|
||||
print '<td> </td>';
|
||||
print "</tr>\n";
|
||||
$i++;
|
||||
|
||||
@@ -81,34 +81,27 @@ else if ($action == 'add')
|
||||
|
||||
if (empty($error))
|
||||
{
|
||||
$tmparray=getCountry(GETPOST('country_id','int'),'all',$db,$langs,0);
|
||||
if (! empty($tmparray['id']))
|
||||
{
|
||||
$object->country_id =$tmparray['id'];
|
||||
$object->country_code =$tmparray['code'];
|
||||
$object->country_label=$tmparray['label'];
|
||||
}
|
||||
|
||||
$object->address = GETPOST('address', 'alpha');
|
||||
$object->zip = GETPOST('zipcode', 'alpha');
|
||||
$object->town = GETPOST('town', 'alpha');
|
||||
$object->fk_pays = $object->country_id;
|
||||
$object->country_id = $_POST["country_id"];
|
||||
$object->status = GETPOST('status','int');
|
||||
$object->fk_user_author = $user->id;
|
||||
$object->datec = dol_now();
|
||||
|
||||
|
||||
|
||||
$id = $object->create($user);
|
||||
|
||||
if ($id > 0)
|
||||
{
|
||||
header("Location: " . $_SERVER["PHP_SELF"] . "?id=" . $id);
|
||||
exit;
|
||||
}
|
||||
else
|
||||
{
|
||||
setEventMessage($object->error, 'errors');
|
||||
$action='create';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setEventMessages($object->error, $object->errors, 'errors');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -131,12 +124,7 @@ else if ($action == 'update')
|
||||
|
||||
$name = GETPOST('name', 'alpha');
|
||||
if (empty($name)) {
|
||||
setEventMessage($langs->trans('ErrorFieldRequired', $langs->trans('NameProperty')), 'errors');
|
||||
$error ++;
|
||||
}
|
||||
$typeid = GETPOST('typeid', 'int');
|
||||
if (empty($typeid)) {
|
||||
setEventMessage($langs->trans('ErrorFieldRequired', $langs->trans('TypeProperty')), 'errors');
|
||||
setEventMessage($langs->trans('ErrorFieldRequired', $langs->trans('Name')), 'errors');
|
||||
$error ++;
|
||||
}
|
||||
|
||||
@@ -145,25 +133,23 @@ else if ($action == 'update')
|
||||
$object->address = GETPOST('address', 'alpha');
|
||||
$object->zip = GETPOST('zipcode', 'alpha');
|
||||
$object->town = GETPOST('town', 'alpha');
|
||||
$object->fk_pays = GETPOST('country_id', 'int');
|
||||
$object->rowid = GETPOST('id');
|
||||
$object->country_id = $_POST["country_id"];
|
||||
$object->fk_user_mod = $user->id;
|
||||
|
||||
$id = $object->update();
|
||||
$result = $object->update();
|
||||
|
||||
if ($id > 0)
|
||||
if ($result > 0)
|
||||
{
|
||||
header("Location: " . $_SERVER["PHP_SELF"] . "?id=" . $id);
|
||||
header("Location: " . $_SERVER["PHP_SELF"] . "?id=" . $_POST['id']);
|
||||
exit;
|
||||
}
|
||||
else
|
||||
{
|
||||
setEventMessage($object->error, 'errors');
|
||||
$action='create';
|
||||
}
|
||||
else
|
||||
{
|
||||
setEventMessages($object->error, $object->errors, 'errors');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
header("Location: card.php?id=" . $id);
|
||||
header("Location: " . $_SERVER["PHP_SELF"] . "?id=" . $_POST['id']);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
@@ -193,11 +179,11 @@ if ($action == 'create')
|
||||
print '<table class="border" width="100%">';
|
||||
|
||||
// Name
|
||||
print '<tr><td class="fieldrequired"><label for="name">'.$langs->trans("Name").'</label></td><td><input name="name" id="name" size="32" value="' . GETPOST("name") . '"></td></tr>';
|
||||
print '<tr><td>'. fieldLabel('Name','name',1).'</td><td><input name="name" id="name" size="32" value="' . GETPOST("name") . '"></td></tr>';
|
||||
|
||||
// Address
|
||||
print '<tr>';
|
||||
print '<td><label for="address">' . $langs->trans("Address") . '</label></td>';
|
||||
print '<td>'.fieldLabel('Address','address',0).'</td>';
|
||||
print '<td>';
|
||||
print '<input name="address" id="address" size="32" value="' . $object->address . '">';
|
||||
print '</td>';
|
||||
@@ -205,7 +191,7 @@ if ($action == 'create')
|
||||
|
||||
// Zipcode
|
||||
print '<tr>';
|
||||
print '<td><label for="zipcode">' . $langs->trans('Zip') . '</label></td>';
|
||||
print '<td>'.fieldLabel('Zip','zipcode',0).'</td>';
|
||||
print '<td>';
|
||||
print $formcompany->select_ziptown(GETPOST('zipcode', 'alpha'), 'zipcode', array (
|
||||
'town',
|
||||
@@ -216,7 +202,7 @@ if ($action == 'create')
|
||||
|
||||
// Town
|
||||
print '<tr>';
|
||||
print '<td><label for="town">' . $langs->trans('Town') . '</label></td>';
|
||||
print '<td>'.fieldLabel('Town','town',0).'</td>';
|
||||
print '<td>';
|
||||
print $formcompany->select_ziptown(GETPOST('town', 'alpha'), 'town', array (
|
||||
'zipcode',
|
||||
@@ -227,7 +213,7 @@ if ($action == 'create')
|
||||
|
||||
// Country
|
||||
print '<tr>';
|
||||
print '<td><label for="selectcountry_id">' . $langs->trans("Country") . '</label></td>';
|
||||
print '<td>'.fieldLabel('Country','selectcountry_id',0).'</td>';
|
||||
print '<td class="maxwidthonsmartphone">';
|
||||
print $form->select_country($mysoc->country_id,'country_id');
|
||||
if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"),1);
|
||||
@@ -236,7 +222,7 @@ if ($action == 'create')
|
||||
|
||||
// Status
|
||||
print '<tr>';
|
||||
print '<td class="fieldrequired"><label for="status">'.$langs->trans("Status").'</label></td>';
|
||||
print '<td>'.fieldLabel('Status','status',1).'</td>';
|
||||
print '<td>';
|
||||
print $form->selectarray('status',$status2label,GETPOST('status'));
|
||||
print '</td></tr>';
|
||||
@@ -273,46 +259,43 @@ else if ($id)
|
||||
|
||||
// Ref
|
||||
print "<tr>";
|
||||
print '<td width="20%">'.$langs->trans("Ref").'</td><td>';
|
||||
print $object->rowid;
|
||||
print '<td width="25%">'.$langs->trans("Ref").'</td><td>';
|
||||
print $object->id;
|
||||
print '</td></tr>';
|
||||
|
||||
// Name
|
||||
print '<tr><td class="fieldrequired"><label for="name">'.$langs->trans("Name").'</label></td><td>';
|
||||
print '<tr><td>'.fieldLabel('Name','name',1).'</td><td>';
|
||||
print '<input name="name" id="name" class="flat" size="32" value="'.$object->name.'">';
|
||||
print '</td></tr>';
|
||||
|
||||
// Address
|
||||
print '<tr>';
|
||||
print '<td><label for="address">' . $langs->trans("Address") . '</label></td>';
|
||||
print '<tr><td>'.fieldLabel('Address','address',0).'</td>';
|
||||
print '<td>';
|
||||
print '<input name="address" id="address" size="32" value="' . $object->address . '">';
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
print '</td></tr>';
|
||||
|
||||
// Zipcode / Town
|
||||
print '<tr><td><label for="zipcode">' . $langs->trans('Zip') . '</label></td><td>';
|
||||
print '<tr><td>'.fieldLabel('Zip','zipcode',0).'</td><td>';
|
||||
print $formcompany->select_ziptown($object->zip, 'zipcode', array (
|
||||
'town',
|
||||
'selectcountry_id'
|
||||
), 6) . '</tr>';
|
||||
print '<tr><td><label for="town">' . $langs->trans('Town') . '</label></td><td>';
|
||||
print '<tr><td>'.fieldLabel('Town','town',0).'</td><td>';
|
||||
print $formcompany->select_ziptown($object->town, 'town', array (
|
||||
'zipcode',
|
||||
'selectcountry_id'
|
||||
)) . '</td></tr>';
|
||||
|
||||
// Country
|
||||
print '<tr>';
|
||||
print '<td><label for="selectcountry_id">' . $langs->trans("Country") . '</label></td>';
|
||||
print '<tr><td>'.fieldLabel('Country','selectcountry_id',0).'</td>';
|
||||
print '<td class="maxwidthonsmartphone">';
|
||||
print $form->select_country($object->fk_pays,'country_id');
|
||||
print $form->select_country($object->fk_country,'country_id');
|
||||
if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"),1);
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
|
||||
// Status
|
||||
print '<tr><td><label for="status">'.$langs->trans("Status").'</label></td><td>';
|
||||
print '<tr><td>'.fieldLabel('Status','status',1).'</td><td>';
|
||||
print $form->selectarray('status',$status2label,$object->status);
|
||||
print '</td></tr>';
|
||||
|
||||
@@ -347,7 +330,7 @@ else if ($id)
|
||||
|
||||
// Ref
|
||||
print '<tr><td width="25%">'.$langs->trans("Ref").'</td><td width="50%">';
|
||||
print $object->rowid;
|
||||
print $object->id;
|
||||
print '</td><td width="25%">';
|
||||
print $linkback;
|
||||
print '</td></tr>';
|
||||
@@ -379,7 +362,14 @@ else if ($id)
|
||||
// Country
|
||||
print '<tr>';
|
||||
print '<td>'.$langs->trans("Country").'</td>';
|
||||
print '<td colspan="2">'.getCountry($object->fk_pays,1).'</td>';
|
||||
print '<td colspan="2">';
|
||||
if ($object->country_id > 0)
|
||||
{
|
||||
$img=picto_from_langcode($object->country_code);
|
||||
print $img?$img.' ':'';
|
||||
print getCountry($object->getCountryCode(),0,$db);
|
||||
}
|
||||
print '</td>';
|
||||
print '</tr>';
|
||||
|
||||
// Status
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
require('../../main.inc.php');
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/hrm.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
|
||||
require_once DOL_DOCUMENT_ROOT.'/hrm/class/establishment.class.php';
|
||||
|
||||
$langs->load("admin");
|
||||
|
||||
@@ -57,7 +57,7 @@ adodb_mktime(0,0,0,10,15,1582) - adodb_mktime(0,0,0,10,4,1582)
|
||||
|
||||
COPYRIGHT
|
||||
|
||||
(c) 2003-2005 John Lim and released under BSD-style license except for code by
|
||||
(c) 2003-2014 John Lim and released under BSD-style license except for code by
|
||||
jackbbs, which includes adodb_mktime, adodb_get_gmt_diff, adodb_is_leap_year
|
||||
and originally found at http://www.php.net/manual/en/function.mktime.php
|
||||
|
||||
@@ -73,6 +73,9 @@ These should be posted to the ADOdb forums at
|
||||
|
||||
FUNCTION DESCRIPTIONS
|
||||
|
||||
** FUNCTION adodb_time()
|
||||
|
||||
Returns the current time measured in the number of seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) as an unsigned integer.
|
||||
|
||||
** FUNCTION adodb_getdate($date=false)
|
||||
|
||||
@@ -241,6 +244,14 @@ b. Implement daylight savings, which looks awfully complicated, see
|
||||
|
||||
|
||||
CHANGELOG
|
||||
- 16 Jan 2011 0.36
|
||||
Added adodb_time() which returns current time. If > 2038, will return as float
|
||||
|
||||
- 7 Feb 2011 0.35
|
||||
Changed adodb_date to be symmetric with adodb_mktime. See $jan1_71. fix for bc.
|
||||
|
||||
- 13 July 2010 0.34
|
||||
Changed adodb_get_gm_diff to use DateTimeZone().
|
||||
|
||||
- 11 Feb 2008 0.33
|
||||
* Bug in 0.32 fix for hour handling. Fixed.
|
||||
@@ -386,7 +397,7 @@ First implementation.
|
||||
/*
|
||||
Version Number
|
||||
*/
|
||||
define('ADODB_DATE_VERSION',0.33);
|
||||
define('ADODB_DATE_VERSION',0.35);
|
||||
|
||||
$ADODB_DATETIME_CLASS = (PHP_VERSION >= 5.2);
|
||||
|
||||
@@ -601,6 +612,12 @@ function adodb_date_test()
|
||||
else print "<p><b>Failed</b> :-(</p>";
|
||||
}
|
||||
|
||||
function adodb_time()
|
||||
{
|
||||
$d = new DateTime();
|
||||
return $d->format('U');
|
||||
}
|
||||
|
||||
/**
|
||||
Returns day of week, 0 = Sunday,... 6=Saturday.
|
||||
Algorithm from PEAR::Date_Calc
|
||||
@@ -729,9 +746,17 @@ global $ADODB_DATETIME_CLASS;
|
||||
} else {
|
||||
if (isset($TZ)) return $TZ;
|
||||
$y = date('Y');
|
||||
$TZ = mktime(0,0,0,12,2,$y) - gmmktime(0,0,0,12,2,$y);
|
||||
/*
|
||||
if (function_exists('date_default_timezone_get') && function_exists('timezone_offset_get')) {
|
||||
$tzonename = date_default_timezone_get();
|
||||
if ($tzonename) {
|
||||
$tobj = new DateTimeZone($tzonename);
|
||||
$TZ = -timezone_offset_get($tobj,new DateTime("now",$tzo));
|
||||
}
|
||||
}
|
||||
*/
|
||||
if (empty($TZ)) $TZ = mktime(0,0,0,12,2,$y) - gmmktime(0,0,0,12,2,$y);
|
||||
}
|
||||
|
||||
return $TZ;
|
||||
}
|
||||
|
||||
@@ -1006,7 +1031,6 @@ function adodb_tz_offset($gmt,$isphp5)
|
||||
return sprintf('%s%02d%02d',($gmt<=0)?'+':'-',floor($zhrs),($zhrs-$hrs)*60);
|
||||
else
|
||||
return sprintf('%s%02d%02d',($gmt<0)?'+':'-',floor($zhrs),($zhrs-$hrs)*60);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -1041,11 +1065,19 @@ function adodb_date($fmt,$d=false,$is_gmt=false)
|
||||
{
|
||||
static $daylight;
|
||||
global $ADODB_DATETIME_CLASS;
|
||||
static $jan1_1971;
|
||||
|
||||
|
||||
if (!isset($daylight)) {
|
||||
$daylight = function_exists('adodb_daylight_sv');
|
||||
if (empty($jan1_1971)) $jan1_1971 = mktime(0,0,0,1,1,1971); // we only use date() when > 1970 as adodb_mktime() only uses mktime() when > 1970
|
||||
}
|
||||
|
||||
if ($d === false) return ($is_gmt)? @gmdate($fmt): @date($fmt);
|
||||
if (!defined('ADODB_TEST_DATES')) {
|
||||
if ((abs($d) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
|
||||
if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= 0) // if windows, must be +ve integer
|
||||
|
||||
if (!defined('ADODB_NO_NEGATIVE_TS') || $d >= $jan1_1971) // if windows, must be +ve integer
|
||||
return ($is_gmt)? @gmdate($fmt,$d): @date($fmt,$d);
|
||||
|
||||
}
|
||||
@@ -1054,7 +1086,6 @@ global $ADODB_DATETIME_CLASS;
|
||||
|
||||
$arr = _adodb_getdate($d,true,$is_gmt);
|
||||
|
||||
if (!isset($daylight)) $daylight = function_exists('adodb_daylight_sv');
|
||||
if ($daylight) adodb_daylight_sv($arr, $is_gmt);
|
||||
|
||||
$year = $arr['year'];
|
||||
@@ -1075,6 +1106,9 @@ global $ADODB_DATETIME_CLASS;
|
||||
*/
|
||||
for ($i=0; $i < $max; $i++) {
|
||||
switch($fmt[$i]) {
|
||||
case 'e':
|
||||
$dates .= date('e');
|
||||
break;
|
||||
case 'T':
|
||||
if ($ADODB_DATETIME_CLASS) {
|
||||
$dt = new DateTime();
|
||||
@@ -1215,7 +1249,7 @@ function adodb_mktime($hr,$min,$sec,$mon=false,$day=false,$year=false,$is_dst=fa
|
||||
|
||||
// for windows, we don't check 1970 because with timezone differences,
|
||||
// 1 Jan 1970 could generate negative timestamp, which is illegal
|
||||
$usephpfns = (1971 < $year && $year < 2038
|
||||
$usephpfns = (1970 < $year && $year < 2038
|
||||
|| !defined('ADODB_NO_NEGATIVE_TS') && (1901 < $year && $year < 2038)
|
||||
);
|
||||
|
||||
@@ -1422,5 +1456,3 @@ global $ADODB_DATE_LOCALE;
|
||||
$ret = adodb_date($fmtdate, $ts, $is_gmt);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -1,39 +1,82 @@
|
||||
CKEditor 4
|
||||
==========
|
||||
CKEditor 4 - Releases
|
||||
=====================
|
||||
|
||||
Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
http://ckeditor.com - See LICENSE.md for license information.
|
||||
## Releases Code
|
||||
|
||||
CKEditor is a text editor to be used inside web pages. It's not a replacement
|
||||
for desktop text editors like Word or OpenOffice, but a component to be used as
|
||||
part of web applications and websites.
|
||||
This repository contains the official release versions of [CKEditor](http://ckeditor.com).
|
||||
|
||||
There are four versions for each release — `standard-all`, `basic`, `standard`, and `full`.
|
||||
They differ in the number of plugins that are compiled into the main `ckeditor.js` file as well as the toolbar configuration.
|
||||
|
||||
See the [comparison](http://ckeditor.com/presets) of the `basic`, `standard`, and `full` installation presets for more details.
|
||||
|
||||
The `standard-all` build includes all official CKSource plugins with only those from the `standard` installation preset compiled into the `ckeditor.js` file and enabled in the configuration.
|
||||
|
||||
All versions available in this repository were built using [CKBuilder](http://ckeditor.com/builder), so they are optimized and ready to be used in a production environment.
|
||||
|
||||
## Documentation
|
||||
|
||||
The full editor documentation is available online at the following address:
|
||||
http://docs.ckeditor.com
|
||||
Developer documentation for CKEditor is available online at: <http://docs.ckeditor.com>.
|
||||
|
||||
## Installation
|
||||
|
||||
Installing CKEditor is an easy task. Just follow these simple steps:
|
||||
### Git clone
|
||||
|
||||
1. **Download** the latest version from the CKEditor website:
|
||||
http://ckeditor.com. You should have already completed this step, but be
|
||||
sure you have the very latest version.
|
||||
2. **Extract** (decompress) the downloaded file into the root of your website.
|
||||
To install one of the available releases, just clone this repository and switch to the respective branch (see next section):
|
||||
|
||||
**Note:** CKEditor is by default installed in the `ckeditor` folder. You can
|
||||
place the files in whichever you want though.
|
||||
git clone -b <release branch> git://github.com/ckeditor/ckeditor-releases.git
|
||||
|
||||
### Git submodule
|
||||
|
||||
If you are using git for your project and you want to integrate CKEditor, we recommend to add this repository as a
|
||||
[submodule](http://git-scm.com/book/en/Git-Tools-Submodules).
|
||||
|
||||
git submodule add -b <release branch> git://github.com/ckeditor/ckeditor-releases.git <clone dir>
|
||||
git commit -m "Added CKEditor submodule in <clone dir> directory."
|
||||
|
||||
### Using Package Managers
|
||||
|
||||
See the [Installing CKEditor with Package Managers](http://docs.ckeditor.com/#!/guide/dev_package_managers) article for more details about installing CKEditor with Bower and Composer.
|
||||
|
||||
## Repository Structure
|
||||
|
||||
### Branches
|
||||
|
||||
This repository contains the following branches:
|
||||
|
||||
- `master` and `latest` – the latest release of the `standard-all` preset (including betas).
|
||||
- `stable` – the latest stable release of the `standard-all` preset (non-beta).
|
||||
- `A.B.x` (e.g. `4.3.x`) – the latest release of the `standard-all` preset in the `A.B` branch.
|
||||
- `(basic|standard|full)/stable` – the latest stable release tag point (non-beta).
|
||||
- `(basic|standard|full)/latest` – the latest release tag point (including betas).
|
||||
- `(basic|standard|full)/A.B.x` (e.g. `basic/4.0.x`) – the latest releases in the `A.B` branch.
|
||||
|
||||
### Tags
|
||||
|
||||
**Since version 4.3.3** this repository uses the following tag naming rules:
|
||||
|
||||
- `x.y.z` – contains the `standard-all` editor build, e.g. `4.3.3`, `4.4.0` etc.
|
||||
- `(basic|standard|full)/x.y.z` – contains the editor build with a given preset, e.g. `basic/4.3.3`.
|
||||
|
||||
The version numbers follow the [Semantic Versioning 2.0.0](http://semver.org/) scheme.
|
||||
|
||||
Up to version **4.3.2** the tags were released in the following form `x.y[.z]/(basic|standard|full)`.
|
||||
For example: `4.0/basic`, `4.0.1/standard`. This convention was changed in CKEditor 4.3.3 to conform to the Semantic Versioning scheme.
|
||||
|
||||
## Checking Your Installation
|
||||
|
||||
The editor comes with a few sample pages that can be used to verify that
|
||||
installation proceeded properly. Take a look at the `samples` directory.
|
||||
The editor comes with a few sample pages that can be used to verify if the installation succeeded. Take a look at the `samples` directory.
|
||||
|
||||
To test your installation, just call the following page at your website:
|
||||
To test your installation, just call the following page for your website:
|
||||
|
||||
http://<your site>/<CKEditor installation path>/samples/index.html
|
||||
|
||||
For example:
|
||||
|
||||
http://www.example.com/ckeditor/samples/index.html
|
||||
|
||||
### License
|
||||
|
||||
Licensed under the GPL, LGPL, and MPL licenses, at your choice.
|
||||
|
||||
Please check the `LICENSE.md` file for more information about the license.
|
||||
@@ -1,442 +0,0 @@
|
||||
CKEditor 4 Changelog
|
||||
====================
|
||||
|
||||
## CKEditor 4.3.3
|
||||
|
||||
Fixed Issues:
|
||||
|
||||
* [#11500](http://dev.ckeditor.com/ticket/11500): [Webkit/Blink] Fixed: Selection lost when setting data in another inline editor. Additionally, [`selection.removeAllRanges()`](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.selection-method-removeAllRanges) is now scoped to selection's [root](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.selection-property-root).
|
||||
* [#11104](http://dev.ckeditor.com/ticket/11104): [IE] Fixed: Various issues with scrolling and selection when focusing widgets.
|
||||
* [#11487](http://dev.ckeditor.com/ticket/11487): Moving mouse over the [Enhanced Image](http://ckeditor.com/addon/image2) widget will no longer change the value returned by the [`editor.checkDirty()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-checkDirty) method.
|
||||
* [#8673](http://dev.ckeditor.com/ticket/8673): [WebKit] Fixed: Cannot select and remove the [Page Break](http://ckeditor.com/addon/pagebreak).
|
||||
* [#11413](http://dev.ckeditor.com/ticket/11413): Fixed: Incorrect [`editor.execCommand()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-execCommand) behavior.
|
||||
* [#11438](http://dev.ckeditor.com/ticket/11438): Splitting table cells vertically is no longer changing table structure.
|
||||
* [#8899](http://dev.ckeditor.com/ticket/8899): Fixed: Links in the [About CKEditor](http://ckeditor.com/addon/about) dialog window now open in a new browser window or tab.
|
||||
* [#11490](http://dev.ckeditor.com/ticket/11490): Fixed: [Menu button](http://ckeditor.com/addon/menubutton) panel not showing in the source mode.
|
||||
* [#11417](http://dev.ckeditor.com/ticket/11417): The [`widget.doubleclick`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget-event-doubleclick) event is not canceled anymore after editing was triggered.
|
||||
* [#11253](http://dev.ckeditor.com/ticket/11253): [IE] Fixed: Clipped upload button in the [Enhanced Image](http://ckeditor.com/addon/image2) dialog window.
|
||||
* [#11359](http://dev.ckeditor.com/ticket/11359): Standardized the way anchors are discovered by the [Link](http://ckeditor.com/addon/link) plugin.
|
||||
* [#11058](http://dev.ckeditor.com/ticket/11058): [IE8] Fixed: Error when deleting a table row.
|
||||
* [#11508](http://dev.ckeditor.com/ticket/11508): Fixed: [`htmlDataProcessor`](http://docs.ckeditor.com/#!/api/CKEDITOR.htmlDataProcessor) discovering protected attributes within other attributes' values.
|
||||
* [#11533](http://dev.ckeditor.com/ticket/11533): Widgets: Avoid recurring upcasts if the DOM structure was modified during an upcast.
|
||||
* [#11400](http://dev.ckeditor.com/ticket/11400): Fixed: The [`domObject.removeAllListeners()`](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.domObject-method-removeAllListeners) method does not remove custom listeners completely.
|
||||
* [#11493](http://dev.ckeditor.com/ticket/11493): Fixed: The [`selection.getRanges()`](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.selection-method-getRanges) method does not override cached ranges when used with the `onlyEditables` argument.
|
||||
* [#11390](http://dev.ckeditor.com/ticket/11390): [IE] All [XML](http://ckeditor.com/addon/xml) plugin [methods](http://docs.ckeditor.com/#!/api/CKEDITOR.xml) now work in IE10+.
|
||||
* [#11542](http://dev.ckeditor.com/ticket/11542): [IE11] Fixed: Blurry toolbar icons when Right-to-Left UI language is set.
|
||||
* [#11504](http://dev.ckeditor.com/ticket/11504): Fixed: When [`config.fullPage`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-fullPage) is set to `true`, entities are not encoded in editor output.
|
||||
* [#11004](http://dev.ckeditor.com/ticket/11004): Integrated [Enhanced Image](http://ckeditor.com/addon/image2) dialog window with [Advanced Content Filter](http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter).
|
||||
* [#11439](http://dev.ckeditor.com/ticket/11439): Fixed: Properties get cloned in the Cell Properties dialog window if multiple cells are selected.
|
||||
|
||||
## CKEditor 4.3.2
|
||||
|
||||
Fixed Issues:
|
||||
|
||||
* [#11331](http://dev.ckeditor.com/ticket/11331): A menu button will have a changed label when selected instead of using the `aria-pressed` attribute.
|
||||
* [#11177](http://dev.ckeditor.com/ticket/11177): Widget drag handler improvements:
|
||||
* [#11176](http://dev.ckeditor.com/ticket/11176): Fixed: Initial position is not updated when the widget data object is empty.
|
||||
* [#11001](http://dev.ckeditor.com/ticket/11001): Fixed: Multiple synchronous layout recalculations are caused by initial drag handler positioning causing performance issues.
|
||||
* [#11161](http://dev.ckeditor.com/ticket/11161): Fixed: Drag handler is not repositioned in various situations.
|
||||
* [#11281](http://dev.ckeditor.com/ticket/11281): Fixed: Drag handler and mask are duplicated after widget reinitialization.
|
||||
* [#11207](http://dev.ckeditor.com/ticket/11207): [Firefox] Fixed: Misplaced [Enhanced Image](http://ckeditor.com/addon/image2) resizer in the inline editor.
|
||||
* [#11102](http://dev.ckeditor.com/ticket/11102): `CKEDITOR.template` improvements:
|
||||
* [#11102](http://dev.ckeditor.com/ticket/11102): Added newline character support.
|
||||
* [#11216](http://dev.ckeditor.com/ticket/11216): Added "\\'" substring support.
|
||||
* [#11121](http://dev.ckeditor.com/ticket/11121): [Firefox] Fixed: High Contrast mode is enabled when the editor is loaded in a hidden iframe.
|
||||
* [#11350](http://dev.ckeditor.com/ticket/11350): The default value of [`config.contentsCss`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-contentsCss) is affected by [`CKEDITOR.getUrl()`](http://docs.ckeditor.com/#!/api/CKEDITOR-method-getUrl).
|
||||
* [#11097](http://dev.ckeditor.com/ticket/11097): Improved the [Autogrow](http://ckeditor.com/addon/autogrow) plugin performance when dealing with very big tables.
|
||||
* [#11290](http://dev.ckeditor.com/ticket/11290): Removed redundant code in the [Source Dialog](http://ckeditor.com/addon/sourcedialog) plugin.
|
||||
* [#11133](http://dev.ckeditor.com/ticket/11133): [Page Break](http://ckeditor.com/addon/pagebreak) becomes editable if pasted.
|
||||
* [#11126](http://dev.ckeditor.com/ticket/11126): Fixed: Native Undo executed once the bottom of the snapshot stack is reached.
|
||||
* [#11131](http://dev.ckeditor.com/ticket/11131): [Div Editing Area](http://ckeditor.com/addon/divarea): Fixed: Error thrown when switching to source mode if the selection was in widget's nested editable.
|
||||
* [#11139](http://dev.ckeditor.com/ticket/11139): [Div Editing Area](http://ckeditor.com/addon/divarea): Fixed: Elements Path is not cleared after switching to source mode.
|
||||
* [#10778](http://dev.ckeditor.com/ticket/10778): Fixed a bug with range enlargement. The range no longer expands to visible whitespace.
|
||||
* [#11146](http://dev.ckeditor.com/ticket/11146): [IE] Fixed: Preview window switches Internet Explorer to Quirks Mode.
|
||||
* [#10762](http://dev.ckeditor.com/ticket/10762): [IE] Fixed: JavaScript code displayed in preview window's URL bar.
|
||||
* [#11186](http://dev.ckeditor.com/ticket/11186): Introduced the [`widgets.repository.addUpcastCallback()`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget.repository-method-addUpcastCallback) method that allows to block upcasting given element to a widget.
|
||||
* [#11307](http://dev.ckeditor.com/ticket/11307): Fixed: Paste as Plain Text conflict with the [MooTools](http://mootools.net) library.
|
||||
* [#11140](http://dev.ckeditor.com/ticket/11140): [IE11] Fixed: Anchors are not draggable.
|
||||
* [#11379](http://dev.ckeditor.com/ticket/11379): Changed default contents `line-height` to unitless values to avoid huge text overlapping (like in [#9696](http://dev.ckeditor.com/ticket/9696)).
|
||||
* [#10787](http://dev.ckeditor.com/ticket/10787): [Firefox] Fixed: Broken replacement of text while pasting into `div`-based editor.
|
||||
* [#10884](http://dev.ckeditor.com/ticket/10884): Widgets integration with the [Show Blocks](http://ckeditor.com/addon/showblocks) plugin.
|
||||
* [#11021](http://dev.ckeditor.com/ticket/11021): Fixed: An error thrown when selecting entire editable contents while fake selection is on.
|
||||
* [#11086](http://dev.ckeditor.com/ticket/11086): [IE8] Re-enable inline widgets drag&drop in Internet Explorer 8.
|
||||
* [#11372](http://dev.ckeditor.com/ticket/11372): Widgets: Special characters encoded twice in nested editables.
|
||||
* [#10068](http://dev.ckeditor.com/ticket/10068): Fixed: Support for protocol-relative URLs.
|
||||
* [#11283](http://dev.ckeditor.com/ticket/11283): [Enhanced Image](http://ckeditor.com/addon/image2): A `<div>` element with `text-align: center` and an image inside is not recognised correctly.
|
||||
* [#11196](http://dev.ckeditor.com/ticket/11196): [Accessibility Instructions](http://ckeditor.com/addon/a11yhelp): Allowed additional keyboard button labels to be translated in the dialog window.
|
||||
|
||||
## CKEditor 4.3.1
|
||||
|
||||
**Important Notes:**
|
||||
|
||||
* To match the naming convention, the `language` button is now `Language` ([#11201](http://dev.ckeditor.com/ticket/11201)).
|
||||
* [Enhanced Image](http://ckeditor.com/addon/image2) button, context menu, command, and icon names match those of the [Image](http://ckeditor.com/addon/image) plugin ([#11222](http://dev.ckeditor.com/ticket/11222)).
|
||||
|
||||
Fixed Issues:
|
||||
|
||||
* [#11244](http://dev.ckeditor.com/ticket/11244): Changed: The [`widget.repository.checkWidgets()`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget.repository-method-checkWidgets) method now fires the [`widget.repository.checkWidgets`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget.repository-event-checkWidgets) event, so from CKEditor 4.3.1 it is preferred to use the method rather than fire the event.
|
||||
* [#11171](http://dev.ckeditor.com/ticket/11171): Fixed: [`editor.insertElement()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-insertElement) and [`editor.insertText()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-insertText) methods do not call the [`widget.repository.checkWidgets()`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget.repository-method-checkWidgets) method.
|
||||
* [#11085](http://dev.ckeditor.com/ticket/11085): [IE8] Replaced preview generated by the [Mathematical Formulas](http://ckeditor.com/addon/mathjax) widget with a placeholder.
|
||||
* [#11044](http://dev.ckeditor.com/ticket/11044): Enhanced WAI-ARIA support for the [Language](http://ckeditor.com/addon/language) plugin drop-down menu.
|
||||
* [#11075](http://dev.ckeditor.com/ticket/11075): With drop-down menu button focused, pressing the *Down Arrow* key will now open the menu and focus its first option.
|
||||
* [#11165](http://dev.ckeditor.com/ticket/11165): Fixed: The [File Browser](http://ckeditor.com/addon/filebrowser) plugin cannot be removed from the editor.
|
||||
* [#11159](http://dev.ckeditor.com/ticket/11159): [IE9-10] [Enhanced Image](http://ckeditor.com/addon/image2): Fixed buggy discovery of image dimensions.
|
||||
* [#11101](http://dev.ckeditor.com/ticket/11101): Drop-down lists no longer break when given double quotes.
|
||||
* [#11077](http://dev.ckeditor.com/ticket/11077): [Enhanced Image](http://ckeditor.com/addon/image2): Empty undo step recorded when resizing the image.
|
||||
* [#10853](http://dev.ckeditor.com/ticket/10853): [Enhanced Image](http://ckeditor.com/addon/image2): Widget has paragraph wrapper when de-captioning unaligned image.
|
||||
* [#11198](http://dev.ckeditor.com/ticket/11198): Widgets: Drag handler is not fully visible when an inline widget is in a heading.
|
||||
* [#11132](http://dev.ckeditor.com/ticket/11132): [Firefox] Fixed: Caret is lost after drag and drop of an inline widget.
|
||||
* [#11182](http://dev.ckeditor.com/ticket/11182): [IE10-11] Fixed: Editor crashes (IE11) or works with minor issues (IE10) if a page is loaded in Quirks Mode. See [`env.quirks`](http://docs.ckeditor.com/#!/api/CKEDITOR.env-property-quirks) for more details.
|
||||
* [#11204](http://dev.ckeditor.com/ticket/11204): Added `figure` and `figcaption` styles to the `contents.css` file so [Enhanced Image](http://ckeditor.com/addon/image2) looks nicer.
|
||||
* [#11202](http://dev.ckeditor.com/ticket/11202): Fixed: No newline in [BBCode](http://ckeditor.com/addon/bbcode) mode.
|
||||
* [#10890](http://dev.ckeditor.com/ticket/10890): Fixed: Error thrown when pressing the *Delete* key in a list item.
|
||||
* [#10055](http://dev.ckeditor.com/ticket/10055): [IE8-10] Fixed: *Delete* pressed on a selected image causes the browser to go back.
|
||||
* [#11183](http://dev.ckeditor.com/ticket/11183): Fixed: Inserting a horizontal rule or a table in multiple row selection causes a browser crash. Additionally, the [`editor.insertElement()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-insertElement) method does not insert the element into every range of a selection any more.
|
||||
* [#11042](http://dev.ckeditor.com/ticket/11042): Fixed: Selection made on an element containing a non-editable element was not auto faked.
|
||||
* [#11125](http://dev.ckeditor.com/ticket/11125): Fixed: Keyboard navigation through menu and drop-down items will now cycle.
|
||||
* [#11011](http://dev.ckeditor.com/ticket/11011): Fixed: The [`editor.applyStyle()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-applyStyle) method removes attributes from nested elements.
|
||||
* [#11179](http://dev.ckeditor.com/ticket/11179): Fixed: [`editor.destroy()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-destroy) does not cleanup content generated by the [Table Resize](http://ckeditor.com/addon/tableresize) plugin for inline editors.
|
||||
* [#11237](http://dev.ckeditor.com/ticket/11237): Fixed: Table border attribute value is deleted when pasting content from Microsoft Word.
|
||||
* [#11250](http://dev.ckeditor.com/ticket/11250): Fixed: HTML entities inside the `<textarea>` element are not encoded.
|
||||
* [#11260](http://dev.ckeditor.com/ticket/11260): Fixed: Initially disabled buttons are not read by JAWS as disabled.
|
||||
* [#11200](http://dev.ckeditor.com/ticket/11200): Added [Clipboard](http://ckeditor.com/addon/clipboard) plugin as a dependency for [Widget](http://ckeditor.com/addon/widget) to fix drag and drop.
|
||||
|
||||
## CKEditor 4.3
|
||||
|
||||
New Features:
|
||||
|
||||
* [#10612](http://dev.ckeditor.com/ticket/10612): Internet Explorer 11 support.
|
||||
* [#10869](http://dev.ckeditor.com/ticket/10869): Widgets: Added better integration with the [Elements Path](http://ckeditor.com/addon/elementspath) plugin.
|
||||
* [#10886](http://dev.ckeditor.com/ticket/10886): Widgets: Added tooltip to the drag handle.
|
||||
* [#10933](http://dev.ckeditor.com/ticket/10933): Widgets: Introduced drag and drop of block widgets with the [Line Utilities](http://ckeditor.com/addon/lineutils) plugin.
|
||||
* [#10936](http://dev.ckeditor.com/ticket/10936): Widget System changes for easier integration with other dialog systems.
|
||||
* [#10895](http://dev.ckeditor.com/ticket/10895): [Enhanced Image](http://ckeditor.com/addon/image2): Added file browser integration.
|
||||
* [#11002](http://dev.ckeditor.com/ticket/11002): Added the [`draggable`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget.definition-property-draggable) option to disable drag and drop support for widgets.
|
||||
* [#10937](http://dev.ckeditor.com/ticket/10937): [Mathematical Formulas](http://ckeditor.com/addon/mathjax) widget improvements:
|
||||
* loading indicator ([#10948](http://dev.ckeditor.com/ticket/10948)),
|
||||
* applying paragraph changes (like font color change) to iframe ([#10841](http://dev.ckeditor.com/ticket/10841)),
|
||||
* Firefox and IE9 clipboard fixes ([#10857](http://dev.ckeditor.com/ticket/10857)),
|
||||
* fixing same origin policy issue ([#10840](http://dev.ckeditor.com/ticket/10840)),
|
||||
* fixing undo bugs ([#10842](http://dev.ckeditor.com/ticket/10842), [#10930](http://dev.ckeditor.com/ticket/10930)),
|
||||
* fixing other minor bugs.
|
||||
* [#10862](http://dev.ckeditor.com/ticket/10862): [Placeholder](http://ckeditor.com/addon/placeholder) plugin was rewritten as a widget.
|
||||
* [#10822](http://dev.ckeditor.com/ticket/10822): Added styles system integration with non-editable elements (for example widgets) and their nested editables. Styles cannot change non-editable content and are applied in nested editable only if allowed by its type and content filter.
|
||||
* [#10856](http://dev.ckeditor.com/ticket/10856): Menu buttons will now toggle the visibility of their panels when clicked multiple times. [Language](http://ckeditor.com/addon/language) plugin fixes: Added active language highlighting, added an option to remove the language.
|
||||
* [#10028](http://dev.ckeditor.com/ticket/10028): New [`config.dialog_noConfirmCancel`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-dialog_noConfirmCancel) configuration option that eliminates the need to confirm closing of a dialog window when the user changed any of its fields.
|
||||
* [#10848](http://dev.ckeditor.com/ticket/10848): Integrate remaining plugins ([Styles](http://ckeditor.com/addon/stylescombo), [Format](http://ckeditor.com/addon/format), [Font](http://ckeditor.com/addon/font), [Color Button](http://ckeditor.com/addon/colorbutton), [Language](http://ckeditor.com/addon/language) and [Indent](http://ckeditor.com/addon/indent)) with [active filter](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-property-activeFilter).
|
||||
* [#10855](http://dev.ckeditor.com/ticket/10855): Change the extension of emoticons in the [BBCode](http://ckeditor.com/addon/bbcode) sample from GIF to PNG.
|
||||
|
||||
Fixed Issues:
|
||||
|
||||
* [#10831](http://dev.ckeditor.com/ticket/10831): [Enhanced Image](http://ckeditor.com/addon/image2): Merged `image2inline` and `image2block` into one `image2` widget.
|
||||
* [#10835](http://dev.ckeditor.com/ticket/10835): [Enhanced Image](http://ckeditor.com/addon/image2): Improved visibility of the resize handle.
|
||||
* [#10836](http://dev.ckeditor.com/ticket/10836): [Enhanced Image](http://ckeditor.com/addon/image2): Preserve custom mouse cursor while resizing the image.
|
||||
* [#10939](http://dev.ckeditor.com/ticket/10939): [Firefox] [Enhanced Image](http://ckeditor.com/addon/image2): hovering the image causes it to change.
|
||||
* [#10866](http://dev.ckeditor.com/ticket/10866): Fixed: Broken *Tab* key navigation in the [Enhanced Image](http://ckeditor.com/addon/image2) dialog window.
|
||||
* [#10833](http://dev.ckeditor.com/ticket/10833): Fixed: *Lock ratio* option should be on by default in the [Enhanced Image](http://ckeditor.com/addon/image2) dialog window.
|
||||
* [#10881](http://dev.ckeditor.com/ticket/10881): Various improvements to *Enter* key behavior in nested editables.
|
||||
* [#10879](http://dev.ckeditor.com/ticket/10879): [Remove Format](http://ckeditor.com/addon/removeformat) should not leak from a nested editable.
|
||||
* [#10877](http://dev.ckeditor.com/ticket/10877): Fixed: [WebSpellChecker](http://ckeditor.com/addon/wsc) fails to apply changes if a nested editable was focused.
|
||||
* [#10877](http://dev.ckeditor.com/ticket/10877): Fixed: [SCAYT](http://ckeditor.com/addon/wsc) blocks typing in nested editables.
|
||||
* [#11079](http://dev.ckeditor.com/ticket/11079): Add button icons to the [Placeholder](http://ckeditor.com/addon/placeholder) sample.
|
||||
* [#10870](http://dev.ckeditor.com/ticket/10870): The `paste` command is no longer being disabled when the clipboard is empty.
|
||||
* [#10854](http://dev.ckeditor.com/ticket/10854): Fixed: Firefox prepends `<br>` to `<body>`, so it is stripped by the HTML data processor.
|
||||
* [#10823](http://dev.ckeditor.com/ticket/10823): Fixed: [Link](http://ckeditor.com/addon/link) plugin does not work with non-editable content.
|
||||
* [#10828](http://dev.ckeditor.com/ticket/10828): [Magic Line](http://ckeditor.com/addon/magicline) integration with the Widget System.
|
||||
* [#10865](http://dev.ckeditor.com/ticket/10865): Improved hiding copybin, so copying widgets works smoothly.
|
||||
* [#11066](http://dev.ckeditor.com/ticket/11066): Widget's private parts use CSS reset.
|
||||
* [#11027](http://dev.ckeditor.com/ticket/11027): Fixed: Block commands break on widgets; added the [`contentDomInvalidated`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-event-contentDomInvalidated) event.
|
||||
* [#10430](http://dev.ckeditor.com/ticket/10430): Resolve dependence of the [Image](http://ckeditor.com/addon/image) plugin on the [Form Elements](http://ckeditor.com/addon/forms) plugin.
|
||||
* [#10911](http://dev.ckeditor.com/ticket/10911): Fixed: Browser *Alt* hotkeys will no longer be blocked while a widget is focused.
|
||||
* [#11082](http://dev.ckeditor.com/ticket/11082): Fixed: Selected widget is not copied or cut when using toolbar buttons or context menu.
|
||||
* [#11083](http://dev.ckeditor.com/ticket/11083): Fixed list and div element application to block widgets.
|
||||
* [#10887](http://dev.ckeditor.com/ticket/10887): Internet Explorer 8 compatibility issues related to the Widget System.
|
||||
* [#11074](http://dev.ckeditor.com/ticket/11074): Temporarily disabled inline widget drag and drop, because of seriously buggy native `range#moveToPoint` method.
|
||||
* [#11098](http://dev.ckeditor.com/ticket/11098): Fixed: Wrong selection position after undoing widget drag and drop.
|
||||
* [#11110](http://dev.ckeditor.com/ticket/11110): Fixed: IFrame and Flash objects are being incorrectly pasted in certain conditions.
|
||||
* [#11129](http://dev.ckeditor.com/ticket/11129): Page break is lost when loading data.
|
||||
* [#11123](http://dev.ckeditor.com/ticket/11123): [Firefox] Widget is destroyed after being dragged outside of `<body>`.
|
||||
* [#11124](http://dev.ckeditor.com/ticket/11124): Fixed the [Elements Path](http://ckeditor.com/addon/elementspath) in an editor using the [Div Editing Area](http://ckeditor.com/addon/divarea).
|
||||
|
||||
## CKEditor 4.3 Beta
|
||||
|
||||
New Features:
|
||||
|
||||
* [#9764](http://dev.ckeditor.com/ticket/9764): Widget System.
|
||||
* [Widget plugin](http://ckeditor.com/addon/widget) introducing the [Widget API](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget).
|
||||
* New [`editor.enterMode`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-property-enterMode) and [`editor.shiftEnterMode`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-property-shiftEnterMode) properties – normalized versions of [`config.enterMode`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-enterMode) and [`config.shiftEnterMode`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-shiftEnterMode).
|
||||
* Dynamic editor settings. Starting from CKEditor 4.3 Beta, *Enter* mode values and [content filter](http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter) instances may be changed dynamically (for example when the caret was placed in an element in which editor features should be adjusted). When you are implementing a new editor feature, you should base its behavior on [dynamic](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-property-activeEnterMode) or [static](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-property-enterMode) *Enter* mode values depending on whether this feature works in selection context or globally on editor content.
|
||||
* Dynamic *Enter* mode values – [`editor.setActiveEnterMode()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-setActiveEnterMode) method, [`editor.activeEnterModeChange`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-event-activeEnterModeChange) event, and two properties: [`editor.activeEnterMode`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-property-activeEnterMode) and [`editor.activeShiftEnterMode`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-property-activeShiftEnterMode).
|
||||
* Dynamic content filter instances – [`editor.setActiveFilter()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-setActiveFilter) method, [`editor.activeFilterChange`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-event-activeFilterChange) event, and [`editor.activeFilter`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-property-activeFilter) property.
|
||||
* "Fake" selection was introduced. It makes it possible to virtually select any element when the real selection remains hidden. See the [`selection.fake()`](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.selection-method-fake) method.
|
||||
* Default [`htmlParser.filter`](http://docs.ckeditor.com/#!/api/CKEDITOR.htmlParser.filter) rules are not applied to non-editable elements (elements with `contenteditable` attribute set to `false` and their descendants) anymore. To add a rule which will be applied to all elements you need to pass an additional argument to the [`filter.addRules()`](http://docs.ckeditor.com/#!/api/CKEDITOR.htmlParser.filter-method-addRules) method.
|
||||
* Dozens of new methods were introduced – most interesting ones:
|
||||
* [`document.find()`](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.document-method-find),
|
||||
* [`document.findOne()`](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.document-method-findOne),
|
||||
* [`editable.insertElementIntoRange()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editable-method-insertElementIntoRange),
|
||||
* [`range.moveToClosestEditablePosition()`](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.range-method-moveToClosestEditablePosition),
|
||||
* New methods for [`htmlParser.node`](http://docs.ckeditor.com/#!/api/CKEDITOR.htmlParser.node) and [`htmlParser.element`](http://docs.ckeditor.com/#!/api/CKEDITOR.htmlParser.element).
|
||||
* [#10659](http://dev.ckeditor.com/ticket/10659): New [Enhanced Image](http://ckeditor.com/addon/image2) plugin that introduces a widget with integrated image captions, an option to center images, and dynamic "click and drag" resizing.
|
||||
* [#10664](http://dev.ckeditor.com/ticket/10664): New [Mathematical Formulas](http://ckeditor.com/addon/mathjax) plugin that introduces the MathJax widget.
|
||||
* [#7987](https://dev.ckeditor.com/ticket/7987): New [Language](http://ckeditor.com/addon/language) plugin that implements Language toolbar button to support [WCAG 3.1.2 Language of Parts](http://www.w3.org/TR/UNDERSTANDING-WCAG20/meaning-other-lang-id.html).
|
||||
* [#10708](http://dev.ckeditor.com/ticket/10708): New [smileys](http://ckeditor.com/addon/smiley).
|
||||
|
||||
## CKEditor 4.2.3
|
||||
|
||||
Fixed Issues:
|
||||
|
||||
* [#10994](http://dev.ckeditor.com/ticket/10994): Fixed: Loading external jQuery library when opening the [jQuery Adapter](http://docs.ckeditor.com/#!/guide/dev_jquery) sample directly from file.
|
||||
* [#10975](http://dev.ckeditor.com/ticket/10975): [IE] Fixed: Error thrown while opening the color palette.
|
||||
* [#9929](http://dev.ckeditor.com/ticket/9929): [Blink/WebKit] Fixed: A non-breaking space is created once a character is deleted and a regular space is typed.
|
||||
* [#10963](http://dev.ckeditor.com/ticket/10963): Fixed: JAWS issue with the keyboard shortcut for [Magic Line](http://ckeditor.com/addon/magicline).
|
||||
* [#11096](http://dev.ckeditor.com/ticket/11096): Fixed: TypeError: Object has no method 'is'.
|
||||
|
||||
## CKEditor 4.2.2
|
||||
|
||||
Fixed Issues:
|
||||
|
||||
* [#9314](http://dev.ckeditor.com/ticket/9314): Fixed: Incorrect error message on closing a dialog window without saving changs.
|
||||
* [#10308](http://dev.ckeditor.com/ticket/10308): [IE10] Fixed: Unspecified error when deleting a row.
|
||||
* [#10945](http://dev.ckeditor.com/ticket/10945): [Chrome] Fixed: Clicking with a mouse inside the editor does not show the caret.
|
||||
* [#10912](http://dev.ckeditor.com/ticket/10912): Prevent default action when content of a non-editable link is clicked.
|
||||
* [#10913](http://dev.ckeditor.com/ticket/10913): Fixed [`CKEDITOR.plugins.addExternal()`](http://docs.ckeditor.com/#!/api/CKEDITOR.resourceManager-method-addExternal) not handling paths including file name specified.
|
||||
* [#10666](http://dev.ckeditor.com/ticket/10666): Fixed [`CKEDITOR.tools.isArray()`](http://docs.ckeditor.com/#!/api/CKEDITOR.tools-method-isArray) not working cross frame.
|
||||
* [#10910](http://dev.ckeditor.com/ticket/10910): [IE9] Fixed JavaScript error thrown in Compatibility Mode when clicking and/or typing in the editing area.
|
||||
* [#10868](http://dev.ckeditor.com/ticket/10868): [IE8] Prevent the browser from crashing when applying the Inline Quotation style.
|
||||
* [#10915](http://dev.ckeditor.com/ticket/10915): Fixed: Invalid CSS filter in the Kama skin.
|
||||
* [#10914](http://dev.ckeditor.com/ticket/10914): Plugins [Indent List](http://ckeditor.com/addon/indentlist) and [Indent Block](http://ckeditor.com/addon/indentblock) are now included in the build configuration.
|
||||
* [#10812](http://dev.ckeditor.com/ticket/10812): Fixed [`range.createBookmark2()`](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.range-method-createBookmark2) incorrectly normalizing offsets. This bug was causing many issues: [#10850](http://dev.ckeditor.com/ticket/10850), [#10842](http://dev.ckeditor.com/ticket/10842).
|
||||
* [#10951](http://dev.ckeditor.com/ticket/10951): Reviewed and optimized focus handling on panels (combo, menu buttons, color buttons, and context menu) to enhance accessibility. Fixed [#10705](http://dev.ckeditor.com/ticket/10705), [#10706](http://dev.ckeditor.com/ticket/10706) and [#10707](http://dev.ckeditor.com/ticket/10707).
|
||||
* [#10704](http://dev.ckeditor.com/ticket/10704): Fixed a JAWS issue with the Select Color dialog window title not being announced.
|
||||
* [#10753](http://dev.ckeditor.com/ticket/10753): The floating toolbar in inline instances now has a dedicated accessibility label.
|
||||
|
||||
## CKEditor 4.2.1
|
||||
|
||||
Fixed Issues:
|
||||
|
||||
* [#10301](http://dev.ckeditor.com/ticket/10301): [IE9-10] Undo fails after 3+ consecutive paste actions with a JavaScript error.
|
||||
* [#10689](http://dev.ckeditor.com/ticket/10689): Save toolbar button saves only the first editor instance.
|
||||
* [#10368](http://dev.ckeditor.com/ticket/10368): Move language reading direction definition (`dir`) from main language file to core.
|
||||
* [#9330](http://dev.ckeditor.com/ticket/9330): Fixed pasting anchors from MS Word.
|
||||
* [#8103](http://dev.ckeditor.com/ticket/8103): Fixed pasting nested lists from MS Word.
|
||||
* [#9958](http://dev.ckeditor.com/ticket/9958): [IE9] Pressing the "OK" button will trigger the `onbeforeunload` event in the popup dialog.
|
||||
* [#10662](http://dev.ckeditor.com/ticket/10662): Fixed styles from the Styles drop-down list not registering to the ACF in case when the [Shared Spaces plugin](http://ckeditor.com/addon/sharedspace) is used.
|
||||
* [#9654](http://dev.ckeditor.com/ticket/9654): Problems with Internet Explorer 10 Quirks Mode.
|
||||
* [#9816](http://dev.ckeditor.com/ticket/9816): Floating toolbar does not reposition vertically in several cases.
|
||||
* [#10646](http://dev.ckeditor.com/ticket/10646): Removing a selected sublist or nested table with *Backspace/Delete* removes the parent element.
|
||||
* [#10623](http://dev.ckeditor.com/ticket/10623): [WebKit] Page is scrolled when opening a drop-down list.
|
||||
* [#10004](http://dev.ckeditor.com/ticket/10004): [ChromeVox] Button names are not announced.
|
||||
* [#10731](http://dev.ckeditor.com/ticket/10731): [WebSpellChecker](http://ckeditor.com/addon/wsc) plugin breaks cloning of editor configuration.
|
||||
* It is now possible to set per instance [WebSpellChecker](http://ckeditor.com/addon/wsc) plugin configuration instead of setting the configuration globally.
|
||||
|
||||
## CKEditor 4.2
|
||||
|
||||
**Important Notes:**
|
||||
|
||||
* Dropped compatibility support for Internet Explorer 7 and Firefox 3.6.
|
||||
|
||||
* Both the Basic and the Standard distribution packages will not contain the new [Indent Block](http://ckeditor.com/addon/indentblock) plugin. Because of this the [Advanced Content Filter](http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter) might remove block indentations from existing contents. If you want to prevent this, either [add an appropriate ACF rule to your filter](http://docs.ckeditor.com/#!/guide/dev_allowed_content_rules) or create a custom build based on the Basic/Standard package and add the Indent Block plugin in [CKBuilder](http://ckeditor.com/builder).
|
||||
|
||||
New Features:
|
||||
|
||||
* [#10027](http://dev.ckeditor.com/ticket/10027): Separated list and block indentation into two plugins: [Indent List](http://ckeditor.com/addon/indentlist) and [Indent Block](http://ckeditor.com/addon/indentblock).
|
||||
* [#8244](http://dev.ckeditor.com/ticket/8244): Use *(Shift+)Tab* to indent and outdent lists.
|
||||
* [#10281](http://dev.ckeditor.com/ticket/10281): The [jQuery Adapter](http://docs.ckeditor.com/#!/guide/dev_jquery) is now available. Several jQuery-related issues fixed: [#8261](http://dev.ckeditor.com/ticket/8261), [#9077](http://dev.ckeditor.com/ticket/9077), [#8710](http://dev.ckeditor.com/ticket/8710), [#8530](http://dev.ckeditor.com/ticket/8530), [#9019](http://dev.ckeditor.com/ticket/9019), [#6181](http://dev.ckeditor.com/ticket/6181), [#7876](http://dev.ckeditor.com/ticket/7876), [#6906](http://dev.ckeditor.com/ticket/6906).
|
||||
* [#10042](http://dev.ckeditor.com/ticket/10042): Introduced [`config.title`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-title) setting to change the human-readable title of the editor.
|
||||
* [#9794](http://dev.ckeditor.com/ticket/9794): Added [`editor.onChange`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-event-change) event.
|
||||
* [#9923](http://dev.ckeditor.com/ticket/9923): HiDPI support in the editor UI. HiDPI icons for [Moono skin](http://ckeditor.com/addon/moono) added.
|
||||
* [#8031](http://dev.ckeditor.com/ticket/8031): Handle `required` attributes on `<textarea>` elements — introduced [`editor.required`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-event-required) event.
|
||||
* [#10280](http://dev.ckeditor.com/ticket/10280): Ability to replace `<textarea>` elements with the inline editor.
|
||||
|
||||
Fixed Issues:
|
||||
|
||||
* [#10599](http://dev.ckeditor.com/ticket/10599): [Indent](http://ckeditor.com/addon/indent) plugin is no longer required by the [List](http://ckeditor.com/addon/list) plugin.
|
||||
* [#10370](http://dev.ckeditor.com/ticket/10370): Inconsistency in data events between framed and inline editors.
|
||||
* [#10438](http://dev.ckeditor.com/ticket/10438): [FF, IE] No selection is done on an editable element on executing [`editor.setData()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-setData).
|
||||
|
||||
## CKEditor 4.1.3
|
||||
|
||||
New Features:
|
||||
|
||||
* Added new translation: Indonesian.
|
||||
|
||||
Fixed Issues:
|
||||
|
||||
* [#10644](http://dev.ckeditor.com/ticket/10644): Fixed a critical bug when pasting plain text in Blink-based browsers.
|
||||
* [#5189](http://dev.ckeditor.com/ticket/5189): [Find/Replace](http://ckeditor.com/addon/find) dialog window: rename "Cancel" button to "Close".
|
||||
* [#10562](http://dev.ckeditor.com/ticket/10562): [Housekeeping] Unified CSS gradient filter formats in the [Moono](http://ckeditor.com/addon/moono) skin.
|
||||
* [#10537](http://dev.ckeditor.com/ticket/10537): Advanced Content Filter should register a default rule for [`config.shiftEnterMode`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-shiftEnterMode).
|
||||
* [#10610](http://dev.ckeditor.com/ticket/10610): [`CKEDITOR.dialog.addIframe()`](http://docs.ckeditor.com/#!/api/CKEDITOR.dialog-static-method-addIframe) incorrectly sets the iframe size in dialog windows.
|
||||
|
||||
## CKEditor 4.1.2
|
||||
|
||||
New Features:
|
||||
|
||||
* Added new translation: Sinhala.
|
||||
|
||||
Fixed Issues:
|
||||
|
||||
* [#10339](http://dev.ckeditor.com/ticket/10339): Fixed: Error thrown when inserted data was totally stripped out after filtering and processing.
|
||||
* [#10298](http://dev.ckeditor.com/ticket/10298): Fixed: Data processor breaks attributes containing protected parts.
|
||||
* [#10367](http://dev.ckeditor.com/ticket/10367): Fixed: [`editable.insertText()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editable-method-insertText) loses characters when `RegExp` replace controls are being inserted.
|
||||
* [#10165](http://dev.ckeditor.com/ticket/10165): [IE] Access denied error when `document.domain` has been altered.
|
||||
* [#9761](http://dev.ckeditor.com/ticket/9761): Update the *Backspace* key state in [`keystrokeHandler.blockedKeystrokes`](http://docs.ckeditor.com/#!/api/CKEDITOR.keystrokeHandler-property-blockedKeystrokes) when calling [`editor.setReadOnly()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-setReadOnly).
|
||||
* [#6504](http://dev.ckeditor.com/ticket/6504): Fixed: Race condition while loading several [`config.customConfig`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-customConfig) files.
|
||||
* [#10146](http://dev.ckeditor.com/ticket/10146): [Firefox] Empty lines are being removed while [`config.enterMode`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-enterMode) is [`CKEDITOR.ENTER_BR`](http://docs.ckeditor.com/#!/api/CKEDITOR-property-ENTER_BR).
|
||||
* [#10360](http://dev.ckeditor.com/ticket/10360): Fixed: ARIA `role="application"` should not be used for dialog windows.
|
||||
* [#10361](http://dev.ckeditor.com/ticket/10361): Fixed: ARIA `role="application"` should not be used for floating panels.
|
||||
* [#10510](http://dev.ckeditor.com/ticket/10510): Introduced unique voice labels to differentiate between different editor instances.
|
||||
* [#9945](http://dev.ckeditor.com/ticket/9945): [iOS] Scrolling not possible on iPad.
|
||||
* [#10389](http://dev.ckeditor.com/ticket/10389): Fixed: Invalid HTML in the "Text and Table" template.
|
||||
* [WebSpellChecker](http://ckeditor.com/addon/wsc) plugin user interface was changed to match CKEditor 4 style.
|
||||
|
||||
## CKEditor 4.1.1
|
||||
|
||||
New Features:
|
||||
|
||||
* Added new translation: Albanian.
|
||||
|
||||
Fixed Issues:
|
||||
|
||||
* [#10172](http://dev.ckeditor.com/ticket/10172): Pressing *Delete* or *Backspace* in an empty table cell moves the cursor to the next/previous cell.
|
||||
* [#10219](http://dev.ckeditor.com/ticket/10219): Error thrown when destroying an editor instance in parallel with a `mouseup` event.
|
||||
* [#10265](http://dev.ckeditor.com/ticket/10265): Wrong loop type in the [File Browser](http://ckeditor.com/addon/filebrowser) plugin.
|
||||
* [#10249](http://dev.ckeditor.com/ticket/10249): Wrong undo/redo states at start.
|
||||
* [#10268](http://dev.ckeditor.com/ticket/10268): [Show Blocks](http://ckeditor.com/addon/showblocks) does not recover after switching to Source view.
|
||||
* [#9995](http://dev.ckeditor.com/ticket/9995): HTML code in the `<textarea>` should not be modified by the [`htmlDataProcessor`](http://docs.ckeditor.com/#!/api/CKEDITOR.htmlDataProcessor).
|
||||
* [#10320](http://dev.ckeditor.com/ticket/10320): [Justify](http://ckeditor.com/addon/justify) plugin should add elements to Advanced Content Filter based on current [Enter mode](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-enterMode).
|
||||
* [#10260](http://dev.ckeditor.com/ticket/10260): Fixed: Advanced Content Filter blocks [`tabSpaces`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-tabSpaces). Unified `data-cke-*` attributes filtering.
|
||||
* [#10315](http://dev.ckeditor.com/ticket/10315): [WebKit] [Undo manager](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.undo.UndoManager) should not record snapshots after a filling character was added/removed.
|
||||
* [#10291](http://dev.ckeditor.com/ticket/10291): [WebKit] Space after a filling character should be secured.
|
||||
* [#10330](http://dev.ckeditor.com/ticket/10330): [WebKit] The filling character is not removed on `keydown` in specific cases.
|
||||
* [#10285](http://dev.ckeditor.com/ticket/10285): Fixed: Styled text pasted from MS Word causes an infinite loop.
|
||||
* [#10131](http://dev.ckeditor.com/ticket/10131): Fixed: [`undoManager.update()`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.undo.UndoManager-method-update) does not refresh the command state.
|
||||
* [#10337](http://dev.ckeditor.com/ticket/10337): Fixed: Unable to remove `<s>` using [Remove Format](http://ckeditor.com/addon/removeformat).
|
||||
|
||||
## CKEditor 4.1
|
||||
|
||||
Fixed Issues:
|
||||
|
||||
* [#10192](http://dev.ckeditor.com/ticket/10192): Closing lists with the *Enter* key does not work with [Advanced Content Filter](http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter) in several cases.
|
||||
* [#10191](http://dev.ckeditor.com/ticket/10191): Fixed allowed content rules unification, so the [`filter.allowedContent`](http://docs.ckeditor.com/#!/api/CKEDITOR.filter-property-allowedContent) property always contains rules in the same format.
|
||||
* [#10224](http://dev.ckeditor.com/ticket/10224): Advanced Content Filter does not remove non-empty `<a>` elements anymore.
|
||||
* Minor issues in plugin integration with Advanced Content Filter:
|
||||
* [#10166](http://dev.ckeditor.com/ticket/10166): Added transformation from the `align` attribute to `float` style to preserve backward compatibility after the introduction of Advanced Content Filter.
|
||||
* [#10195](http://dev.ckeditor.com/ticket/10195): [Image](http://ckeditor.com/addon/image) plugin no longer registers rules for links to Advanced Content Filter.
|
||||
* [#10213](http://dev.ckeditor.com/ticket/10213): [Justify](http://ckeditor.com/addon/justify) plugin is now correctly registering rules to Advanced Content Filter when [`config.justifyClasses`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-justifyClasses) is defined.
|
||||
|
||||
## CKEditor 4.1 RC
|
||||
|
||||
New Features:
|
||||
|
||||
* [#9829](http://dev.ckeditor.com/ticket/9829): Advanced Content Filter - data and features activation based on editor configuration.
|
||||
|
||||
Brand new data filtering system that works in 2 modes:
|
||||
|
||||
* Based on loaded features (toolbar items, plugins) - the data will be filtered according to what the editor in its
|
||||
current configuration can handle.
|
||||
* Based on [`config.allowedContent`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-allowedContent) rules - the data
|
||||
will be filtered and the editor features (toolbar items, commands, keystrokes) will be enabled if they are allowed.
|
||||
|
||||
See the `datafiltering.html` sample, [guides](http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter) and [`CKEDITOR.filter` API documentation](http://docs.ckeditor.com/#!/api/CKEDITOR.filter).
|
||||
* [#9387](http://dev.ckeditor.com/ticket/9387): Reintroduced [Shared Spaces](http://ckeditor.com/addon/sharedspace) - the ability to display toolbar and bottom editor space in selected locations and to share them by different editor instances.
|
||||
* [#9907](http://dev.ckeditor.com/ticket/9907): Added the [`contentPreview`](http://docs.ckeditor.com/#!/api/CKEDITOR-event-contentPreview) event for preview data manipulation.
|
||||
* [#9713](http://dev.ckeditor.com/ticket/9713): Introduced the [Source Dialog](http://ckeditor.com/addon/sourcedialog) plugin that brings raw HTML editing for inline editor instances.
|
||||
* Included in [#9829](http://dev.ckeditor.com/ticket/9829): Introduced new events, [`toHtml`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-event-toHtml) and [`toDataFormat`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-event-toDataFormat), allowing for better integration with data processing.
|
||||
* [#9981](http://dev.ckeditor.com/ticket/9981): Added ability to filter [`htmlParser.fragment`](http://docs.ckeditor.com/#!/api/CKEDITOR.htmlParser.fragment), [`htmlParser.element`](http://docs.ckeditor.com/#!/api/CKEDITOR.htmlParser.element) etc. by many [`htmlParser.filter`](http://docs.ckeditor.com/#!/api/CKEDITOR.htmlParser.filter)s before writing structure to an HTML string.
|
||||
* Included in [#10103](http://dev.ckeditor.com/ticket/10103):
|
||||
* Introduced the [`editor.status`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-property-status) property to make it easier to check the current status of the editor.
|
||||
* Default [`command`](http://docs.ckeditor.com/#!/api/CKEDITOR.command) state is now [`CKEDITOR.TRISTATE_DISABLE`](http://docs.ckeditor.com/#!/api/CKEDITOR-property-TRISTATE_DISABLED). It will be activated on [`editor.instanceReady`](http://docs.ckeditor.com/#!/api/CKEDITOR-event-instanceReady) or immediately after being added if the editor is already initialized.
|
||||
* [#9796](http://dev.ckeditor.com/ticket/9796): Introduced `<s>` as a default tag for strikethrough, which replaces obsolete `<strike>` in HTML5.
|
||||
|
||||
## CKEditor 4.0.3
|
||||
|
||||
Fixed Issues:
|
||||
|
||||
* [#10196](http://dev.ckeditor.com/ticket/10196): Fixed context menus not opening with keyboard shortcuts when [Autogrow](http://ckeditor.com/addon/autogrow) is enabled.
|
||||
* [#10212](http://dev.ckeditor.com/ticket/10212): [IE7-10] Undo command throws errors after multiple switches between Source and WYSIWYG view.
|
||||
* [#10219](http://dev.ckeditor.com/ticket/10219): [Inline editor] Error thrown after calling [`editor.destroy()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-destroy).
|
||||
|
||||
## CKEditor 4.0.2
|
||||
|
||||
Fixed Issues:
|
||||
|
||||
* [#9779](http://dev.ckeditor.com/ticket/9779): Fixed overriding [`CKEDITOR.getUrl()`](http://docs.ckeditor.com/#!/api/CKEDITOR-method-getUrl) with `CKEDITOR_GETURL`.
|
||||
* [#9772](http://dev.ckeditor.com/ticket/9772): Custom buttons in the dialog window footer have different look and size ([Moono](http://ckeditor.com/addon/moono), [Kama](http://ckeditor.com/addon/kama) skins).
|
||||
* [#9029](http://dev.ckeditor.com/ticket/9029): Custom styles added with the [`stylesSet.add()`](http://docs.ckeditor.com/#!/api/CKEDITOR.stylesSet-method-add) are displayed in the wrong order.
|
||||
* [#9887](http://dev.ckeditor.com/ticket/9887): Disable [Magic Line](http://ckeditor.com/addon/magicline) when [`editor.readOnly`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-property-readOnly) is set.
|
||||
* [#9882](http://dev.ckeditor.com/ticket/9882): Fixed empty document title on [`editor.getData()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-getData) if set via the Document Properties dialog window.
|
||||
* [#9773](http://dev.ckeditor.com/ticket/9773): Fixed rendering problems with selection fields in the Kama skin.
|
||||
* [#9851](http://dev.ckeditor.com/ticket/9851): The [`selectionChange`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-event-selectionChange) event is not fired when mouse selection ended outside editable.
|
||||
* [#9903](http://dev.ckeditor.com/ticket/9903): [Inline editor] Bad positioning of floating space with page horizontal scroll.
|
||||
* [#9872](http://dev.ckeditor.com/ticket/9872): [`editor.checkDirty()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-checkDirty) returns `true` when called onload. Removed the obsolete `editor.mayBeDirty` flag.
|
||||
* [#9893](http://dev.ckeditor.com/ticket/9893): [IE] Fixed broken toolbar when editing mixed direction content in Quirks mode.
|
||||
* [#9845](http://dev.ckeditor.com/ticket/9845): Fixed TAB navigation in the [Link](http://ckeditor.com/addon/link) dialog window when the Anchor option is used and no anchors are available.
|
||||
* [#9883](http://dev.ckeditor.com/ticket/9883): Maximizing was making the entire page editable with [divarea](http://ckeditor.com/addon/divarea)-based editors.
|
||||
* [#9940](http://dev.ckeditor.com/ticket/9940): [Firefox] Navigating back to a page with the editor was making the entire page editable.
|
||||
* [#9966](http://dev.ckeditor.com/ticket/9966): Fixed: Unable to type square brackets with French keyboard layout. Changed [Magic Line](http://ckeditor.com/addon/magicline) keystrokes.
|
||||
* [#9507](http://dev.ckeditor.com/ticket/9507): [Firefox] Selection is moved before editable position when the editor is focused for the first time.
|
||||
* [#9947](http://dev.ckeditor.com/ticket/9947): [WebKit] Editor overflows parent container in some edge cases.
|
||||
* [#10105](http://dev.ckeditor.com/ticket/10105): Fixed: Broken [sourcearea](http://ckeditor.com/addon/sourcearea) view when an RTL language is set.
|
||||
* [#10123](http://dev.ckeditor.com/ticket/10123): [WebKit] Fixed: Several dialog windows have broken layout since the latest WebKit release.
|
||||
* [#10152](http://dev.ckeditor.com/ticket/10152): Fixed: Invalid ARIA property used on menu items.
|
||||
|
||||
## CKEditor 4.0.1.1
|
||||
|
||||
Fixed Issues:
|
||||
|
||||
* Security update: Added protection against XSS attack and possible path disclosure in the PHP sample.
|
||||
|
||||
## CKEditor 4.0.1
|
||||
|
||||
Fixed Issues:
|
||||
|
||||
* [#9655](http://dev.ckeditor.com/ticket/9655): Support for IE Quirks Mode in the new [Moono skin](http://ckeditor.com/addon/moono).
|
||||
* Accessibility issues (mainly in inline editor): [#9364](http://dev.ckeditor.com/ticket/9364), [#9368](http://dev.ckeditor.com/ticket/9368), [#9369](http://dev.ckeditor.com/ticket/9369), [#9370](http://dev.ckeditor.com/ticket/9370), [#9541](http://dev.ckeditor.com/ticket/9541), [#9543](http://dev.ckeditor.com/ticket/9543), [#9841](http://dev.ckeditor.com/ticket/9841), [#9844](http://dev.ckeditor.com/ticket/9844).
|
||||
* [Magic Line](http://ckeditor.com/addon/magicline) plugin:
|
||||
* [#9481](http://dev.ckeditor.com/ticket/9481): Added accessibility support for Magic Line.
|
||||
* [#9509](http://dev.ckeditor.com/ticket/9509): Added Magic Line support for forms.
|
||||
* [#9573](http://dev.ckeditor.com/ticket/9573): Magic Line does not disappear on `mouseout` in a specific case.
|
||||
* [#9754](http://dev.ckeditor.com/ticket/9754): [WebKit] Cutting & pasting simple unformatted text generates an inline wrapper in WebKit browsers.
|
||||
* [#9456](http://dev.ckeditor.com/ticket/9456): [Chrome] Properly paste bullet list style from MS Word.
|
||||
* [#9699](http://dev.ckeditor.com/ticket/9699), [#9758](http://dev.ckeditor.com/ticket/9758): Improved selection locking when selecting by dragging.
|
||||
* Context menu:
|
||||
* [#9712](http://dev.ckeditor.com/ticket/9712): Opening the context menu destroys editor focus.
|
||||
* [#9366](http://dev.ckeditor.com/ticket/9366): Context menu should be displayed over the floating toolbar.
|
||||
* [#9706](http://dev.ckeditor.com/ticket/9706): Context menu generates a JavaScript error in inline mode when the editor is attached to a header element.
|
||||
* [#9800](http://dev.ckeditor.com/ticket/9800): Hide float panel when resizing the window.
|
||||
* [#9721](http://dev.ckeditor.com/ticket/9721): Padding in content of div-based editor puts the editing area under the bottom UI space.
|
||||
* [#9528](http://dev.ckeditor.com/ticket/9528): Host page `box-sizing` style should not influence the editor UI elements.
|
||||
* [#9503](http://dev.ckeditor.com/ticket/9503): [Form Elements](http://ckeditor.com/addon/forms) plugin adds context menu listeners only on supported input types. Added support for `tel`, `email`, `search` and `url` input types.
|
||||
* [#9769](http://dev.ckeditor.com/ticket/9769): Improved floating toolbar positioning in a narrow window.
|
||||
* [#9875](http://dev.ckeditor.com/ticket/9875): Table dialog window does not populate width correctly.
|
||||
* [#8675](http://dev.ckeditor.com/ticket/8675): Deleting cells in a nested table removes the outer table cell.
|
||||
* [#9815](http://dev.ckeditor.com/ticket/9815): Cannot edit dialog window fields in an editor initialized in the jQuery UI modal dialog.
|
||||
* [#8888](http://dev.ckeditor.com/ticket/8888): CKEditor dialog windows do not show completely in a small window.
|
||||
* [#9360](http://dev.ckeditor.com/ticket/9360): [Inline editor] Blocks shown for a `<div>` element stay permanently even after the user exits editing the `<div>`.
|
||||
* [#9531](http://dev.ckeditor.com/ticket/9531): [Firefox & Inline editor] Toolbar is lost when closing the Format drop-down list by clicking its button.
|
||||
* [#9553](http://dev.ckeditor.com/ticket/9553): Table width incorrectly set when the `border-width` style is specified.
|
||||
* [#9594](http://dev.ckeditor.com/ticket/9594): Cannot tab past CKEditor when it is in read-only mode.
|
||||
* [#9658](http://dev.ckeditor.com/ticket/9658): [IE9] Justify not working on selected images.
|
||||
* [#9686](http://dev.ckeditor.com/ticket/9686): Added missing contents styles for `<pre>` elements.
|
||||
* [#9709](http://dev.ckeditor.com/ticket/9709): [Paste from Word](http://ckeditor.com/addon/pastefromword) should not depend on configuration from other styles.
|
||||
* [#9726](http://dev.ckeditor.com/ticket/9726): Removed [Color Dialog](http://ckeditor.com/addon/colordialog) plugin dependency from [Table Tools](http://ckeditor.com/addon/tabletools).
|
||||
* [#9765](http://dev.ckeditor.com/ticket/9765): Toolbar Collapse command documented incorrectly in the [Accessibility Instructions](http://ckeditor.com/addon/a11yhelp) dialog window.
|
||||
* [#9771](http://dev.ckeditor.com/ticket/9771): [WebKit & Opera] Fixed scrolling issues when pasting.
|
||||
* [#9787](http://dev.ckeditor.com/ticket/9787): [IE9] `onChange` is not fired for checkboxes in dialogs.
|
||||
* [#9842](http://dev.ckeditor.com/ticket/9842): [Firefox 17] When opening a toolbar menu for the first time and pressing the *Down Arrow* key, focus goes to the next toolbar button instead of the menu options.
|
||||
* [#9847](http://dev.ckeditor.com/ticket/9847): [Elements Path](http://ckeditor.com/addon/elementspath) should not be initialized in the inline editor.
|
||||
* [#9853](http://dev.ckeditor.com/ticket/9853): [`editor.addRemoveFormatFilter()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-addRemoveFormatFilter) is exposed before it really works.
|
||||
* [#8893](http://dev.ckeditor.com/ticket/8893): Value of the [`pasteFromWordCleanupFile`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-pasteFromWordCleanupFile) configuration option is now taken from the instance configuration.
|
||||
* [#9693](http://dev.ckeditor.com/ticket/9693): Removed "Live Preview" checkbox from UI color picker.
|
||||
|
||||
|
||||
## CKEditor 4.0
|
||||
|
||||
The first stable release of the new CKEditor 4 code line.
|
||||
|
||||
The CKEditor JavaScript API has been kept compatible with CKEditor 4, whenever
|
||||
possible. The list of relevant changes can be found in the [API Changes page of
|
||||
the CKEditor 4 documentation][1].
|
||||
|
||||
[1]: http://docs.ckeditor.com/#!/guide/dev_api_changes "API Changes"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,39 +0,0 @@
|
||||
CKEditor 4
|
||||
==========
|
||||
|
||||
Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
http://ckeditor.com - See LICENSE.md for license information.
|
||||
|
||||
CKEditor is a text editor to be used inside web pages. It's not a replacement
|
||||
for desktop text editors like Word or OpenOffice, but a component to be used as
|
||||
part of web applications and websites.
|
||||
|
||||
## Documentation
|
||||
|
||||
The full editor documentation is available online at the following address:
|
||||
http://docs.ckeditor.com
|
||||
|
||||
## Installation
|
||||
|
||||
Installing CKEditor is an easy task. Just follow these simple steps:
|
||||
|
||||
1. **Download** the latest version from the CKEditor website:
|
||||
http://ckeditor.com. You should have already completed this step, but be
|
||||
sure you have the very latest version.
|
||||
2. **Extract** (decompress) the downloaded file into the root of your website.
|
||||
|
||||
**Note:** CKEditor is by default installed in the `ckeditor` folder. You can
|
||||
place the files in whichever you want though.
|
||||
|
||||
## Checking Your Installation
|
||||
|
||||
The editor comes with a few sample pages that can be used to verify that
|
||||
installation proceeded properly. Take a look at the `samples` directory.
|
||||
|
||||
To test your installation, just call the following page at your website:
|
||||
|
||||
http://<your site>/<CKEditor installation path>/samples/index.html
|
||||
|
||||
For example:
|
||||
|
||||
http://www.example.com/ckeditor/samples/index.html
|
||||
@@ -1,165 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* This file was added automatically by CKEditor builder.
|
||||
* You may re-use it at any time to build CKEditor again.
|
||||
*
|
||||
* If you would like to build CKEditor online again
|
||||
* (for example to upgrade), visit one the following links:
|
||||
*
|
||||
* (1) http://ckeditor.com/builder
|
||||
* Visit online builder to build CKEditor from scratch.
|
||||
*
|
||||
* (2) http://ckeditor.com/builder/6df6f619a26d5e0aab9c5392e3ab2c31
|
||||
* Visit online builder to build CKEditor, starting with the same setup as before.
|
||||
*
|
||||
* (3) http://ckeditor.com/builder/download/6df6f619a26d5e0aab9c5392e3ab2c31
|
||||
* Straight download link to the latest version of CKEditor (Optimized) with the same setup as before.
|
||||
*
|
||||
* NOTE:
|
||||
* This file is not used by CKEditor, you may remove it.
|
||||
* Changing this file will not change your CKEditor configuration.
|
||||
*/
|
||||
|
||||
var CKBUILDER_CONFIG = {
|
||||
skin: 'moonocolor',
|
||||
preset: 'full',
|
||||
ignore: [
|
||||
'dev',
|
||||
'.gitignore',
|
||||
'.gitattributes',
|
||||
'README.md',
|
||||
'.mailmap'
|
||||
],
|
||||
plugins : {
|
||||
'a11yhelp' : 1,
|
||||
'about' : 1,
|
||||
'basicstyles' : 1,
|
||||
'bidi' : 1,
|
||||
'blockquote' : 1,
|
||||
'clipboard' : 1,
|
||||
'colorbutton' : 1,
|
||||
'colordialog' : 1,
|
||||
'contextmenu' : 1,
|
||||
'dialogadvtab' : 1,
|
||||
'div' : 1,
|
||||
'elementspath' : 1,
|
||||
'enterkey' : 1,
|
||||
'entities' : 1,
|
||||
'filebrowser' : 1,
|
||||
'find' : 1,
|
||||
'flash' : 1,
|
||||
'floatingspace' : 1,
|
||||
'font' : 1,
|
||||
'format' : 1,
|
||||
'forms' : 1,
|
||||
'horizontalrule' : 1,
|
||||
'htmlwriter' : 1,
|
||||
'iframe' : 1,
|
||||
'image' : 1,
|
||||
'indentblock' : 1,
|
||||
'indentlist' : 1,
|
||||
'justify' : 1,
|
||||
'language' : 1,
|
||||
'link' : 1,
|
||||
'list' : 1,
|
||||
'liststyle' : 1,
|
||||
'magicline' : 1,
|
||||
'maximize' : 1,
|
||||
'newpage' : 1,
|
||||
'pagebreak' : 1,
|
||||
'pastefromword' : 1,
|
||||
'pastetext' : 1,
|
||||
'preview' : 1,
|
||||
'print' : 1,
|
||||
'removeformat' : 1,
|
||||
'resize' : 1,
|
||||
'save' : 1,
|
||||
'scayt' : 1,
|
||||
'selectall' : 1,
|
||||
'showblocks' : 1,
|
||||
'showborders' : 1,
|
||||
'smiley' : 1,
|
||||
'sourcearea' : 1,
|
||||
'specialchar' : 1,
|
||||
'stylescombo' : 1,
|
||||
'tab' : 1,
|
||||
'table' : 1,
|
||||
'tabletools' : 1,
|
||||
'templates' : 1,
|
||||
'toolbar' : 1,
|
||||
'undo' : 1,
|
||||
'wsc' : 1,
|
||||
'wysiwygarea' : 1
|
||||
},
|
||||
languages : {
|
||||
'af' : 1,
|
||||
'ar' : 1,
|
||||
'bg' : 1,
|
||||
'bn' : 1,
|
||||
'bs' : 1,
|
||||
'ca' : 1,
|
||||
'cs' : 1,
|
||||
'cy' : 1,
|
||||
'da' : 1,
|
||||
'de' : 1,
|
||||
'el' : 1,
|
||||
'en' : 1,
|
||||
'en-au' : 1,
|
||||
'en-ca' : 1,
|
||||
'en-gb' : 1,
|
||||
'eo' : 1,
|
||||
'es' : 1,
|
||||
'et' : 1,
|
||||
'eu' : 1,
|
||||
'fa' : 1,
|
||||
'fi' : 1,
|
||||
'fo' : 1,
|
||||
'fr' : 1,
|
||||
'fr-ca' : 1,
|
||||
'gl' : 1,
|
||||
'gu' : 1,
|
||||
'he' : 1,
|
||||
'hi' : 1,
|
||||
'hr' : 1,
|
||||
'hu' : 1,
|
||||
'id' : 1,
|
||||
'is' : 1,
|
||||
'it' : 1,
|
||||
'ja' : 1,
|
||||
'ka' : 1,
|
||||
'km' : 1,
|
||||
'ko' : 1,
|
||||
'ku' : 1,
|
||||
'lt' : 1,
|
||||
'lv' : 1,
|
||||
'mk' : 1,
|
||||
'mn' : 1,
|
||||
'ms' : 1,
|
||||
'nb' : 1,
|
||||
'nl' : 1,
|
||||
'no' : 1,
|
||||
'pl' : 1,
|
||||
'pt' : 1,
|
||||
'pt-br' : 1,
|
||||
'ro' : 1,
|
||||
'ru' : 1,
|
||||
'si' : 1,
|
||||
'sk' : 1,
|
||||
'sl' : 1,
|
||||
'sq' : 1,
|
||||
'sr' : 1,
|
||||
'sr-latn' : 1,
|
||||
'sv' : 1,
|
||||
'th' : 1,
|
||||
'tr' : 1,
|
||||
'ug' : 1,
|
||||
'uk' : 1,
|
||||
'vi' : 1,
|
||||
'zh' : 1,
|
||||
'zh-cn' : 1
|
||||
}
|
||||
};
|
||||
@@ -1,42 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
// Compressed version of core/ckeditor_base.js. See original for instructions.
|
||||
/*jsl:ignore*/
|
||||
window.CKEDITOR||(window.CKEDITOR=function(){var b={timestamp:"",version:"%VERSION%",revision:"%REV%",rnd:Math.floor(900*Math.random())+100,_:{pending:[]},status:"unloaded",basePath:function(){var a=window.CKEDITOR_BASEPATH||"";if(!a)for(var b=document.getElementsByTagName("script"),c=0;c<b.length;c++){var d=b[c].src.match(/(^|.*[\\\/])ckeditor(?:_basic)?(?:_source)?.js(?:\?.*)?$/i);if(d){a=d[1];break}}-1==a.indexOf(":/")&&"//"!=a.slice(0,2)&&(a=0===a.indexOf("/")?location.href.match(/^.*?:\/\/[^\/]*/)[0]+a:location.href.match(/^[^\?]*\/(?:)/)[0]+a);if(!a)throw'The CKEditor installation path could not be automatically detected. Please set the global variable "CKEDITOR_BASEPATH" before creating editor instances.';return a}(),getUrl:function(a){-1==a.indexOf(":/")&&0!==a.indexOf("/")&&(a=this.basePath+a);this.timestamp&&"/"!=a.charAt(a.length-1)&&!/[&?]t=/.test(a)&&(a+=(0<=a.indexOf("?")?"&":"?")+"t="+this.timestamp);return a},domReady:function(){function a(){try{document.addEventListener?(document.removeEventListener("DOMContentLoaded",a,!1),b()):document.attachEvent&&"complete"===document.readyState&&(document.detachEvent("onreadystatechange",a),b())}catch(d){}}function b(){for(var a;a=c.shift();)a()}var c=[];return function(b){c.push(b);"complete"===document.readyState&&setTimeout(a,1);if(1==c.length)if(document.addEventListener)document.addEventListener("DOMContentLoaded",a,!1),window.addEventListener("load",a,!1);else if(document.attachEvent){document.attachEvent("onreadystatechange",a);window.attachEvent("onload",a);b=!1;try{b=!window.frameElement}catch(e){}if(document.documentElement.doScroll&&b){var f=function(){try{document.documentElement.doScroll("left")}catch(b){setTimeout(f,1);return}a()};f()}}}}()},e=window.CKEDITOR_GETURL;if(e){var g=b.getUrl;b.getUrl=function(a){return e.call(b,a)||g.call(b,a)}}return b}());
|
||||
/*jsl:end*/
|
||||
|
||||
if ( CKEDITOR.loader )
|
||||
CKEDITOR.loader.load( 'ckeditor' );
|
||||
else {
|
||||
// Set the script name to be loaded by the loader.
|
||||
CKEDITOR._autoLoad = 'ckeditor';
|
||||
|
||||
// Include the loader script.
|
||||
if ( document.body && ( !document.readyState || document.readyState == 'complete' ) ) {
|
||||
var script = document.createElement( 'script' );
|
||||
script.type = 'text/javascript';
|
||||
script.src = CKEDITOR.getUrl( 'core/loader.js' );
|
||||
document.body.appendChild( script );
|
||||
} else
|
||||
document.write( '<script type="text/javascript" src="' + CKEDITOR.getUrl( 'core/loader.js' ) + '"></script>' );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The skin to load for all created instances, it may be the name of the skin
|
||||
* folder inside the editor installation path, or the name and the path separated
|
||||
* by a comma.
|
||||
*
|
||||
* **Note:** This is a global configuration that applies to all instances.
|
||||
*
|
||||
* CKEDITOR.skinName = 'moono';
|
||||
*
|
||||
* CKEDITOR.skinName = 'myskin,/customstuff/myskin/';
|
||||
*
|
||||
* @cfg {String} [skinName='moono']
|
||||
* @member CKEDITOR
|
||||
*/
|
||||
CKEDITOR.skinName = 'moono';
|
||||
@@ -1,17 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.html or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
CKEDITOR.editorConfig = function( config ) {
|
||||
|
||||
// %REMOVE_START%
|
||||
// The configuration options below are needed when running CKEditor from source files.
|
||||
config.plugins = 'dialogui,dialog,about,a11yhelp,dialogadvtab,basicstyles,bidi,blockquote,clipboard,button,panelbutton,panel,floatpanel,colorbutton,colordialog,templates,menu,contextmenu,div,resize,toolbar,elementspath,enterkey,entities,popup,filebrowser,find,fakeobjects,flash,floatingspace,listblock,richcombo,font,forms,format,horizontalrule,htmlwriter,iframe,wysiwygarea,image,indent,indentblock,indentlist,smiley,justify,menubutton,language,link,list,liststyle,magicline,maximize,newpage,pagebreak,pastetext,pastefromword,preview,print,removeformat,save,selectall,showblocks,showborders,sourcearea,specialchar,scayt,stylescombo,tab,table,tabletools,undo,wsc';
|
||||
config.skin = 'moonocolor';
|
||||
// %REMOVE_END%
|
||||
|
||||
// Define changes to default configuration here. For example:
|
||||
// config.language = 'fr';
|
||||
// config.uiColor = '#AADC6E';
|
||||
};
|
||||
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
body
|
||||
{
|
||||
/* Font */
|
||||
font-family: sans-serif, Arial, Verdana, "Trebuchet MS";
|
||||
font-size: 12px;
|
||||
|
||||
/* Text color */
|
||||
color: #333;
|
||||
|
||||
/* Remove the background color to make it transparent */
|
||||
background-color: #fff;
|
||||
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.cke_editable
|
||||
{
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
blockquote
|
||||
{
|
||||
font-style: italic;
|
||||
font-family: Georgia, Times, "Times New Roman", serif;
|
||||
padding: 2px 0;
|
||||
border-style: solid;
|
||||
border-color: #ccc;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
.cke_contents_ltr blockquote
|
||||
{
|
||||
padding-left: 20px;
|
||||
padding-right: 8px;
|
||||
border-left-width: 5px;
|
||||
}
|
||||
|
||||
.cke_contents_rtl blockquote
|
||||
{
|
||||
padding-left: 8px;
|
||||
padding-right: 20px;
|
||||
border-right-width: 5px;
|
||||
}
|
||||
|
||||
a
|
||||
{
|
||||
color: #0782C1;
|
||||
}
|
||||
|
||||
ol,ul,dl
|
||||
{
|
||||
/* IE7: reset rtl list margin. (#7334) */
|
||||
*margin-right: 0px;
|
||||
/* preserved spaces for list items with text direction other than the list. (#6249,#8049)*/
|
||||
padding: 0 40px;
|
||||
}
|
||||
|
||||
h1,h2,h3,h4,h5,h6
|
||||
{
|
||||
font-weight: normal;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
hr
|
||||
{
|
||||
border: 0px;
|
||||
border-top: 1px solid #ccc;
|
||||
}
|
||||
|
||||
img.right
|
||||
{
|
||||
border: 1px solid #ccc;
|
||||
float: right;
|
||||
margin-left: 15px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
img.left
|
||||
{
|
||||
border: 1px solid #ccc;
|
||||
float: left;
|
||||
margin-right: 15px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
pre
|
||||
{
|
||||
white-space: pre-wrap; /* CSS 2.1 */
|
||||
word-wrap: break-word; /* IE7 */
|
||||
}
|
||||
|
||||
.marker
|
||||
{
|
||||
background-color: Yellow;
|
||||
}
|
||||
|
||||
span[lang]
|
||||
{
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
figure
|
||||
{
|
||||
text-align: center;
|
||||
border: solid 1px #ccc;
|
||||
border-radius: 2px;
|
||||
background: rgba(0,0,0,0.05);
|
||||
padding: 10px;
|
||||
margin: 10px 20px;
|
||||
display: block; /* For IE8 */
|
||||
}
|
||||
|
||||
figure figcaption
|
||||
{
|
||||
text-align: center;
|
||||
display: block; /* For IE8 */
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileOverview API initialization code.
|
||||
*/
|
||||
|
||||
( function() {
|
||||
// Disable HC detection in WebKit. (#5429)
|
||||
if ( CKEDITOR.env.webkit )
|
||||
CKEDITOR.env.hc = false;
|
||||
else {
|
||||
// Check whether high contrast is active by creating a colored border.
|
||||
var hcDetect = CKEDITOR.dom.element.createFromHtml( '<div style="width:0;height:0;position:absolute;left:-10000px;' +
|
||||
'border:1px solid;border-color:red blue"></div>', CKEDITOR.document );
|
||||
|
||||
hcDetect.appendTo( CKEDITOR.document.getHead() );
|
||||
|
||||
// Update CKEDITOR.env.
|
||||
// Catch exception needed sometimes for FF. (#4230)
|
||||
try {
|
||||
var top = hcDetect.getComputedStyle( 'border-top-color' ),
|
||||
right = hcDetect.getComputedStyle( 'border-right-color' );
|
||||
|
||||
// We need to check if getComputedStyle returned any value, because on FF
|
||||
// it returnes empty string if CKEditor is loaded in hidden iframe. (#11121)
|
||||
CKEDITOR.env.hc = !!( top && top == right );
|
||||
} catch ( e ) {
|
||||
CKEDITOR.env.hc = false;
|
||||
}
|
||||
|
||||
hcDetect.remove();
|
||||
}
|
||||
|
||||
if ( CKEDITOR.env.hc )
|
||||
CKEDITOR.env.cssClass += ' cke_hc';
|
||||
|
||||
// Initially hide UI spaces when relevant skins are loading, later restored by skin css.
|
||||
CKEDITOR.document.appendStyleText( '.cke{visibility:hidden;}' );
|
||||
|
||||
// Mark the editor as fully loaded.
|
||||
CKEDITOR.status = 'loaded';
|
||||
CKEDITOR.fireOnce( 'loaded' );
|
||||
|
||||
// Process all instances created by the "basic" implementation.
|
||||
var pending = CKEDITOR._.pending;
|
||||
if ( pending ) {
|
||||
delete CKEDITOR._.pending;
|
||||
|
||||
for ( var i = 0; i < pending.length; i++ ) {
|
||||
CKEDITOR.editor.prototype.constructor.apply( pending[ i ][ 0 ], pending[ i ][ 1 ] );
|
||||
CKEDITOR.add( pending[ i ][ 0 ] );
|
||||
}
|
||||
}
|
||||
} )();
|
||||
|
||||
/**
|
||||
* Indicates that CKEditor is running on a High Contrast environment.
|
||||
*
|
||||
* if ( CKEDITOR.env.hc )
|
||||
* alert( 'You\'re running on High Contrast mode. The editor interface will get adapted to provide you a better experience.' );
|
||||
*
|
||||
* @property {Boolean} hc
|
||||
* @member CKEDITOR.env
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fired when a CKEDITOR core object is fully loaded and ready for interaction.
|
||||
*
|
||||
* @event loaded
|
||||
* @member CKEDITOR
|
||||
*/
|
||||
@@ -1,204 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileOverview Contains the third and last part of the {@link CKEDITOR} object
|
||||
* definition.
|
||||
*/
|
||||
|
||||
/** @class CKEDITOR */
|
||||
|
||||
// Remove the CKEDITOR.loadFullCore reference defined on ckeditor_basic.
|
||||
delete CKEDITOR.loadFullCore;
|
||||
|
||||
/**
|
||||
* Stores references to all editor instances created. The name of the properties
|
||||
* in this object correspond to instance names, and their values contain the
|
||||
* {@link CKEDITOR.editor} object representing them.
|
||||
*
|
||||
* alert( CKEDITOR.instances.editor1.name ); // 'editor1'
|
||||
*
|
||||
* @property {Object}
|
||||
*/
|
||||
CKEDITOR.instances = {};
|
||||
|
||||
/**
|
||||
* The document of the window storing the CKEDITOR object.
|
||||
*
|
||||
* alert( CKEDITOR.document.getBody().getName() ); // 'body'
|
||||
*
|
||||
* @property {CKEDITOR.dom.document}
|
||||
*/
|
||||
CKEDITOR.document = new CKEDITOR.dom.document( document );
|
||||
|
||||
/**
|
||||
* Adds an editor instance to the global {@link CKEDITOR} object. This function
|
||||
* is available for internal use mainly.
|
||||
*
|
||||
* @param {CKEDITOR.editor} editor The editor instance to be added.
|
||||
*/
|
||||
CKEDITOR.add = function( editor ) {
|
||||
CKEDITOR.instances[ editor.name ] = editor;
|
||||
|
||||
editor.on( 'focus', function() {
|
||||
if ( CKEDITOR.currentInstance != editor ) {
|
||||
CKEDITOR.currentInstance = editor;
|
||||
CKEDITOR.fire( 'currentInstance' );
|
||||
}
|
||||
} );
|
||||
|
||||
editor.on( 'blur', function() {
|
||||
if ( CKEDITOR.currentInstance == editor ) {
|
||||
CKEDITOR.currentInstance = null;
|
||||
CKEDITOR.fire( 'currentInstance' );
|
||||
}
|
||||
} );
|
||||
|
||||
CKEDITOR.fire( 'instance', null, editor );
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes an editor instance from the global {@link CKEDITOR} object. This function
|
||||
* is available for internal use only. External code must use {@link CKEDITOR.editor#method-destroy}.
|
||||
*
|
||||
* @private
|
||||
* @param {CKEDITOR.editor} editor The editor instance to be removed.
|
||||
*/
|
||||
CKEDITOR.remove = function( editor ) {
|
||||
delete CKEDITOR.instances[ editor.name ];
|
||||
};
|
||||
|
||||
( function() {
|
||||
var tpls = {};
|
||||
|
||||
/**
|
||||
* Adds a named {@link CKEDITOR.template} instance to be reused among all editors.
|
||||
* This will return the existing one if a template with same name is already
|
||||
* defined. Additionally, it fires the "template" event to allow template source customization.
|
||||
*
|
||||
* @param {String} name The name which identifies a UI template.
|
||||
* @param {String} source The source string for constructing this template.
|
||||
* @returns {CKEDITOR.template} The created template instance.
|
||||
*/
|
||||
CKEDITOR.addTemplate = function( name, source ) {
|
||||
var tpl = tpls[ name ];
|
||||
if ( tpl )
|
||||
return tpl;
|
||||
|
||||
// Make it possible to customize the template through event.
|
||||
var params = { name: name, source: source };
|
||||
CKEDITOR.fire( 'template', params );
|
||||
|
||||
return ( tpls[ name ] = new CKEDITOR.template( params.source ) );
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves a defined template created with {@link CKEDITOR#addTemplate}.
|
||||
*
|
||||
* @param {String} name The template name.
|
||||
*/
|
||||
CKEDITOR.getTemplate = function( name ) {
|
||||
return tpls[ name ];
|
||||
};
|
||||
} )();
|
||||
|
||||
( function() {
|
||||
var styles = [];
|
||||
|
||||
/**
|
||||
* Adds CSS rules to be appended to the editor document.
|
||||
* This method is mostly used by plugins to add custom styles to the editor
|
||||
* document. For basic content styling the `contents.css` file should be
|
||||
* used instead.
|
||||
*
|
||||
* **Note:** This function should be called before the creation of editor instances.
|
||||
*
|
||||
* // Add styles for all headings inside editable contents.
|
||||
* CKEDITOR.addCss( '.cke_editable h1,.cke_editable h2,.cke_editable h3 { border-bottom: 1px dotted red }' );
|
||||
*
|
||||
* @param {String} css The style rules to be appended.
|
||||
* @see CKEDITOR.config#contentsCss
|
||||
*/
|
||||
CKEDITOR.addCss = function( css ) {
|
||||
styles.push( css );
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a string will all CSS rules passed to the {@link CKEDITOR#addCss} method.
|
||||
*
|
||||
* @returns {String} A string containing CSS rules.
|
||||
*/
|
||||
CKEDITOR.getCss = function() {
|
||||
return styles.join( '\n' );
|
||||
};
|
||||
} )();
|
||||
|
||||
// Perform global clean up to free as much memory as possible
|
||||
// when there are no instances left
|
||||
CKEDITOR.on( 'instanceDestroyed', function() {
|
||||
if ( CKEDITOR.tools.isEmpty( this.instances ) )
|
||||
CKEDITOR.fire( 'reset' );
|
||||
} );
|
||||
|
||||
// Load the bootstrap script.
|
||||
CKEDITOR.loader.load( '_bootstrap' ); // %REMOVE_LINE%
|
||||
|
||||
// Tri-state constants.
|
||||
/**
|
||||
* Used to indicate the ON or ACTIVE state.
|
||||
*
|
||||
* @readonly
|
||||
* @property {Number} [=1]
|
||||
*/
|
||||
CKEDITOR.TRISTATE_ON = 1;
|
||||
|
||||
/**
|
||||
* Used to indicate the OFF or INACTIVE state.
|
||||
*
|
||||
* @readonly
|
||||
* @property {Number} [=2]
|
||||
*/
|
||||
CKEDITOR.TRISTATE_OFF = 2;
|
||||
|
||||
/**
|
||||
* Used to indicate the DISABLED state.
|
||||
*
|
||||
* @readonly
|
||||
* @property {Number} [=0]
|
||||
*/
|
||||
CKEDITOR.TRISTATE_DISABLED = 0;
|
||||
|
||||
/**
|
||||
* The editor which is currently active (has user focus).
|
||||
*
|
||||
* function showCurrentEditorName() {
|
||||
* if ( CKEDITOR.currentInstance )
|
||||
* alert( CKEDITOR.currentInstance.name );
|
||||
* else
|
||||
* alert( 'Please focus an editor first.' );
|
||||
* }
|
||||
*
|
||||
* @property {CKEDITOR.editor} currentInstance
|
||||
* @see CKEDITOR#event-currentInstance
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fired when the CKEDITOR.currentInstance object reference changes. This may
|
||||
* happen when setting the focus on different editor instances in the page.
|
||||
*
|
||||
* var editor; // A variable to store a reference to the current editor.
|
||||
* CKEDITOR.on( 'currentInstance', function() {
|
||||
* editor = CKEDITOR.currentInstance;
|
||||
* } );
|
||||
*
|
||||
* @event currentInstance
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fired when the last instance has been destroyed. This event is used to perform
|
||||
* global memory cleanup.
|
||||
*
|
||||
* @event reset
|
||||
*/
|
||||
@@ -1,315 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileOverview Contains the first and essential part of the {@link CKEDITOR}
|
||||
* object definition.
|
||||
*/
|
||||
|
||||
// #### Compressed Code
|
||||
// Must be updated on changes in the script as well as updated in the ckeditor.js file.
|
||||
|
||||
// window.CKEDITOR||(window.CKEDITOR=function(){var b={timestamp:"",version:"%VERSION%",revision:"%REV%",rnd:Math.floor(900*Math.random())+100,_:{pending:[]},status:"unloaded",basePath:function(){var a=window.CKEDITOR_BASEPATH||"";if(!a)for(var b=document.getElementsByTagName("script"),c=0;c<b.length;c++){var d=b[c].src.match(/(^|.*[\\\/])ckeditor(?:_basic)?(?:_source)?.js(?:\?.*)?$/i);if(d){a=d[1];break}}-1==a.indexOf(":/")&&"//"!=a.slice(0,2)&&(a=0===a.indexOf("/")?location.href.match(/^.*?:\/\/[^\/]*/)[0]+a:location.href.match(/^[^\?]*\/(?:)/)[0]+a);if(!a)throw'The CKEditor installation path could not be automatically detected. Please set the global variable "CKEDITOR_BASEPATH" before creating editor instances.';return a}(),getUrl:function(a){-1==a.indexOf(":/")&&0!==a.indexOf("/")&&(a=this.basePath+a);this.timestamp&&"/"!=a.charAt(a.length-1)&&!/[&?]t=/.test(a)&&(a+=(0<=a.indexOf("?")?"&":"?")+"t="+this.timestamp);return a},domReady:function(){function a(){try{document.addEventListener?(document.removeEventListener("DOMContentLoaded",a,!1),b()):document.attachEvent&&"complete"===document.readyState&&(document.detachEvent("onreadystatechange",a),b())}catch(d){}}function b(){for(var a;a=c.shift();)a()}var c=[];return function(b){c.push(b);"complete"===document.readyState&&setTimeout(a,1);if(1==c.length)if(document.addEventListener)document.addEventListener("DOMContentLoaded",a,!1),window.addEventListener("load",a,!1);else if(document.attachEvent){document.attachEvent("onreadystatechange",a);window.attachEvent("onload",a);b=!1;try{b=!window.frameElement}catch(e){}if(document.documentElement.doScroll&&b){var f=function(){try{document.documentElement.doScroll("left")}catch(b){setTimeout(f,1);return}a()};f()}}}}()},e=window.CKEDITOR_GETURL;if(e){var g=b.getUrl;b.getUrl=function(a){return e.call(b,a)||g.call(b,a)}}return b}());
|
||||
// The Closure Compiler online service should be used when updating this manually:
|
||||
// http://closure-compiler.appspot.com/
|
||||
|
||||
// #### Raw code
|
||||
// ATTENTION: read the above "Compressed Code" notes when changing this code.
|
||||
|
||||
if ( !window.CKEDITOR ) {
|
||||
/**
|
||||
* This is the API entry point. The entire CKEditor code runs under this object.
|
||||
* @class CKEDITOR
|
||||
* @singleton
|
||||
*/
|
||||
window.CKEDITOR = ( function() {
|
||||
var CKEDITOR = {
|
||||
|
||||
/**
|
||||
* A constant string unique for each release of CKEditor. Its value
|
||||
* is used, by default, to build the URL for all resources loaded
|
||||
* by the editor code, guaranteeing clean cache results when
|
||||
* upgrading.
|
||||
*
|
||||
* alert( CKEDITOR.timestamp ); // e.g. '87dm'
|
||||
*/
|
||||
timestamp: '', // %REMOVE_LINE%
|
||||
/* // %REMOVE_LINE%
|
||||
// The production implementation contains a fixed timestamp, unique
|
||||
// for each release and generated by the releaser.
|
||||
// (Base 36 value of each component of YYMMDDHH - 4 chars total - e.g. 87bm == 08071122)
|
||||
timestamp: '%TIMESTAMP%',
|
||||
*/ // %REMOVE_LINE%
|
||||
|
||||
/**
|
||||
* Contains the CKEditor version number.
|
||||
*
|
||||
* alert( CKEDITOR.version ); // e.g. 'CKEditor 3.4.1'
|
||||
*/
|
||||
version: '%VERSION%',
|
||||
|
||||
/**
|
||||
* Contains the CKEditor revision number.
|
||||
* The revision number is incremented automatically, following each
|
||||
* modification to the CKEditor source code.
|
||||
*
|
||||
* alert( CKEDITOR.revision ); // e.g. '3975'
|
||||
*/
|
||||
revision: '%REV%',
|
||||
|
||||
/**
|
||||
* A 3-digit random integer, valid for the entire life of the CKEDITOR object.
|
||||
*
|
||||
* alert( CKEDITOR.rnd ); // e.g. 319
|
||||
*
|
||||
* @property {Number}
|
||||
*/
|
||||
rnd: Math.floor( Math.random() * ( 999 /*Max*/ - 100 /*Min*/ + 1 ) ) + 100 /*Min*/,
|
||||
|
||||
/**
|
||||
* Private object used to hold core stuff. It should not be used outside of
|
||||
* the API code as properties defined here may change at any time
|
||||
* without notice.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_: {
|
||||
pending: []
|
||||
},
|
||||
|
||||
/**
|
||||
* Indicates the API loading status. The following statuses are available:
|
||||
*
|
||||
* * **unloaded**: the API is not yet loaded.
|
||||
* * **basic_loaded**: the basic API features are available.
|
||||
* * **basic_ready**: the basic API is ready to load the full core code.
|
||||
* * **loaded**: the API can be fully used.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* if ( CKEDITOR.status == 'loaded' ) {
|
||||
* // The API can now be fully used.
|
||||
* doSomething();
|
||||
* } else {
|
||||
* // Wait for the full core to be loaded and fire its loading.
|
||||
* CKEDITOR.on( 'load', doSomething );
|
||||
* CKEDITOR.loadFullCore && CKEDITOR.loadFullCore();
|
||||
* }
|
||||
*/
|
||||
status: 'unloaded',
|
||||
|
||||
/**
|
||||
* The full URL for the CKEditor installation directory.
|
||||
* It is possible to manually provide the base path by setting a
|
||||
* global variable named `CKEDITOR_BASEPATH`. This global variable
|
||||
* must be set **before** the editor script loading.
|
||||
*
|
||||
* alert( CKEDITOR.basePath ); // e.g. 'http://www.example.com/ckeditor/'
|
||||
*
|
||||
* @property {String}
|
||||
*/
|
||||
basePath: ( function() {
|
||||
// ATTENTION: fixes to this code must be ported to
|
||||
// var basePath in "core/loader.js".
|
||||
|
||||
// Find out the editor directory path, based on its <script> tag.
|
||||
var path = window.CKEDITOR_BASEPATH || '';
|
||||
|
||||
if ( !path ) {
|
||||
var scripts = document.getElementsByTagName( 'script' );
|
||||
|
||||
for ( var i = 0; i < scripts.length; i++ ) {
|
||||
var match = scripts[ i ].src.match( /(^|.*[\\\/])ckeditor(?:_basic)?(?:_source)?.js(?:\?.*)?$/i );
|
||||
|
||||
if ( match ) {
|
||||
path = match[ 1 ];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// In IE (only) the script.src string is the raw value entered in the
|
||||
// HTML source. Other browsers return the full resolved URL instead.
|
||||
if ( path.indexOf( ':/' ) == -1 && path.slice( 0, 2 ) != '//' ) {
|
||||
// Absolute path.
|
||||
if ( path.indexOf( '/' ) === 0 )
|
||||
path = location.href.match( /^.*?:\/\/[^\/]*/ )[ 0 ] + path;
|
||||
// Relative path.
|
||||
else
|
||||
path = location.href.match( /^[^\?]*\/(?:)/ )[ 0 ] + path;
|
||||
}
|
||||
|
||||
if ( !path )
|
||||
throw 'The CKEditor installation path could not be automatically detected. Please set the global variable "CKEDITOR_BASEPATH" before creating editor instances.';
|
||||
|
||||
return path;
|
||||
} )(),
|
||||
|
||||
/**
|
||||
* Gets the full URL for CKEditor resources. By default, URLs
|
||||
* returned by this function contain a querystring parameter ("t")
|
||||
* set to the {@link CKEDITOR#timestamp} value.
|
||||
*
|
||||
* It is possible to provide a custom implementation of this
|
||||
* function by setting a global variable named `CKEDITOR_GETURL`.
|
||||
* This global variable must be set **before** the editor script
|
||||
* loading. If the custom implementation returns nothing (`==null`), the
|
||||
* default implementation is used.
|
||||
*
|
||||
* // e.g. 'http://www.example.com/ckeditor/skins/default/editor.css?t=87dm'
|
||||
* alert( CKEDITOR.getUrl( 'skins/default/editor.css' ) );
|
||||
*
|
||||
* // e.g. 'http://www.example.com/skins/default/editor.css?t=87dm'
|
||||
* alert( CKEDITOR.getUrl( '/skins/default/editor.css' ) );
|
||||
*
|
||||
* // e.g. 'http://www.somesite.com/skins/default/editor.css?t=87dm'
|
||||
* alert( CKEDITOR.getUrl( 'http://www.somesite.com/skins/default/editor.css' ) );
|
||||
*
|
||||
* @param {String} resource The resource whose full URL we want to get.
|
||||
* It may be a full, absolute, or relative URL.
|
||||
* @returns {String} The full URL.
|
||||
*/
|
||||
getUrl: function( resource ) {
|
||||
// If this is not a full or absolute path.
|
||||
if ( resource.indexOf( ':/' ) == -1 && resource.indexOf( '/' ) !== 0 )
|
||||
resource = this.basePath + resource;
|
||||
|
||||
// Add the timestamp, except for directories.
|
||||
if ( this.timestamp && resource.charAt( resource.length - 1 ) != '/' && !( /[&?]t=/ ).test( resource ) )
|
||||
resource += ( resource.indexOf( '?' ) >= 0 ? '&' : '?' ) + 't=' + this.timestamp;
|
||||
|
||||
return resource;
|
||||
},
|
||||
|
||||
/**
|
||||
* Specify a function to execute when the DOM is fully loaded.
|
||||
*
|
||||
* If called after the DOM has been initialized, the function passed in will
|
||||
* be executed immediately.
|
||||
*
|
||||
* @method
|
||||
* @todo
|
||||
*/
|
||||
domReady: ( function() {
|
||||
// Based on the original jQuery code.
|
||||
|
||||
var callbacks = [];
|
||||
|
||||
function onReady() {
|
||||
try {
|
||||
// Cleanup functions for the document ready method
|
||||
if ( document.addEventListener ) {
|
||||
document.removeEventListener( 'DOMContentLoaded', onReady, false );
|
||||
executeCallbacks();
|
||||
}
|
||||
// Make sure body exists, at least, in case IE gets a little overzealous.
|
||||
else if ( document.attachEvent && document.readyState === 'complete' ) {
|
||||
document.detachEvent( 'onreadystatechange', onReady );
|
||||
executeCallbacks();
|
||||
}
|
||||
} catch ( er ) {}
|
||||
}
|
||||
|
||||
function executeCallbacks() {
|
||||
var i;
|
||||
while ( ( i = callbacks.shift() ) )
|
||||
i();
|
||||
}
|
||||
|
||||
return function( fn ) {
|
||||
callbacks.push( fn );
|
||||
|
||||
// Catch cases where this is called after the
|
||||
// browser event has already occurred.
|
||||
if ( document.readyState === 'complete' )
|
||||
// Handle it asynchronously to allow scripts the opportunity to delay ready
|
||||
setTimeout( onReady, 1 );
|
||||
|
||||
// Run below once on demand only.
|
||||
if ( callbacks.length != 1 )
|
||||
return;
|
||||
|
||||
// For IE>8, Firefox, Opera and Webkit.
|
||||
if ( document.addEventListener ) {
|
||||
// Use the handy event callback
|
||||
document.addEventListener( 'DOMContentLoaded', onReady, false );
|
||||
|
||||
// A fallback to window.onload, that will always work
|
||||
window.addEventListener( 'load', onReady, false );
|
||||
|
||||
}
|
||||
// If old IE event model is used
|
||||
else if ( document.attachEvent ) {
|
||||
// ensure firing before onload,
|
||||
// maybe late but safe also for iframes
|
||||
document.attachEvent( 'onreadystatechange', onReady );
|
||||
|
||||
// A fallback to window.onload, that will always work
|
||||
window.attachEvent( 'onload', onReady );
|
||||
|
||||
// If IE and not a frame
|
||||
// continually check to see if the document is ready
|
||||
// use the trick by Diego Perini
|
||||
// http://javascript.nwbox.com/IEContentLoaded/
|
||||
var toplevel = false;
|
||||
|
||||
try {
|
||||
toplevel = !window.frameElement;
|
||||
} catch ( e ) {}
|
||||
|
||||
if ( document.documentElement.doScroll && toplevel ) {
|
||||
function scrollCheck() {
|
||||
try {
|
||||
document.documentElement.doScroll( 'left' );
|
||||
} catch ( e ) {
|
||||
setTimeout( scrollCheck, 1 );
|
||||
return;
|
||||
}
|
||||
onReady();
|
||||
}
|
||||
scrollCheck();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} )()
|
||||
};
|
||||
|
||||
// Make it possible to override the "url" function with a custom
|
||||
// implementation pointing to a global named CKEDITOR_GETURL.
|
||||
var newGetUrl = window.CKEDITOR_GETURL;
|
||||
if ( newGetUrl ) {
|
||||
var originalGetUrl = CKEDITOR.getUrl;
|
||||
CKEDITOR.getUrl = function( resource ) {
|
||||
return newGetUrl.call( CKEDITOR, resource ) || originalGetUrl.call( CKEDITOR, resource );
|
||||
};
|
||||
}
|
||||
|
||||
return CKEDITOR;
|
||||
} )();
|
||||
}
|
||||
|
||||
/**
|
||||
* Function called upon loading a custom configuration file that can
|
||||
* modify the editor instance configuration ({@link CKEDITOR.editor#config}).
|
||||
* It is usually defined inside the custom configuration files that can
|
||||
* include developer defined settings.
|
||||
*
|
||||
* // This is supposed to be placed in the config.js file.
|
||||
* CKEDITOR.editorConfig = function( config ) {
|
||||
* // Define changes to default configuration here. For example:
|
||||
* config.language = 'fr';
|
||||
* config.uiColor = '#AADC6E';
|
||||
* };
|
||||
*
|
||||
* @method editorConfig
|
||||
* @param {CKEDITOR.config} config A configuration object containing the
|
||||
* settings defined for a {@link CKEDITOR.editor} instance up to this
|
||||
* function call. Note that not all settings may still be available. See
|
||||
* [Configuration Loading Order](http://docs.cksource.com/CKEditor_3.x/Developers_Guide/Setting_Configurations#Configuration_Loading_Order)
|
||||
* for details.
|
||||
*/
|
||||
|
||||
// PACKAGER_RENAME( CKEDITOR )
|
||||
@@ -1,94 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileOverview Contains the second part of the {@link CKEDITOR} object
|
||||
* definition, which defines the basic editor features to be available in
|
||||
* the root ckeditor_basic.js file.
|
||||
*/
|
||||
|
||||
if ( CKEDITOR.status == 'unloaded' ) {
|
||||
( function() {
|
||||
CKEDITOR.event.implementOn( CKEDITOR );
|
||||
|
||||
/**
|
||||
* Forces the full CKEditor core code, in the case only the basic code has been
|
||||
* loaded (`ckeditor_basic.js`). This method self-destroys (becomes undefined) in
|
||||
* the first call or as soon as the full code is available.
|
||||
*
|
||||
* // Check if the full core code has been loaded and load it.
|
||||
* if ( CKEDITOR.loadFullCore )
|
||||
* CKEDITOR.loadFullCore();
|
||||
*
|
||||
* @member CKEDITOR
|
||||
*/
|
||||
CKEDITOR.loadFullCore = function() {
|
||||
// If the basic code is not ready, just mark it to be loaded.
|
||||
if ( CKEDITOR.status != 'basic_ready' ) {
|
||||
CKEDITOR.loadFullCore._load = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
// Destroy this function.
|
||||
delete CKEDITOR.loadFullCore;
|
||||
|
||||
// Append the script to the head.
|
||||
var script = document.createElement( 'script' );
|
||||
script.type = 'text/javascript';
|
||||
script.src = CKEDITOR.basePath + 'ckeditor.js';
|
||||
script.src = CKEDITOR.basePath + 'ckeditor_source.js'; // %REMOVE_LINE%
|
||||
|
||||
document.getElementsByTagName( 'head' )[ 0 ].appendChild( script );
|
||||
};
|
||||
|
||||
/**
|
||||
* The time to wait (in seconds) to load the full editor code after the
|
||||
* page load, if the "ckeditor_basic" file is used. If set to zero, the
|
||||
* editor is loaded on demand, as soon as an instance is created.
|
||||
*
|
||||
* This value must be set on the page before the page load completion.
|
||||
*
|
||||
* // Loads the full source after five seconds.
|
||||
* CKEDITOR.loadFullCoreTimeout = 5;
|
||||
*
|
||||
* @property
|
||||
* @member CKEDITOR
|
||||
*/
|
||||
CKEDITOR.loadFullCoreTimeout = 0;
|
||||
|
||||
// Documented at ckeditor.js.
|
||||
CKEDITOR.add = function( editor ) {
|
||||
// For now, just put the editor in the pending list. It will be
|
||||
// processed as soon as the full code gets loaded.
|
||||
var pending = this._.pending || ( this._.pending = [] );
|
||||
pending.push( editor );
|
||||
};
|
||||
|
||||
( function() {
|
||||
var onload = function() {
|
||||
var loadFullCore = CKEDITOR.loadFullCore,
|
||||
loadFullCoreTimeout = CKEDITOR.loadFullCoreTimeout;
|
||||
|
||||
if ( !loadFullCore )
|
||||
return;
|
||||
|
||||
CKEDITOR.status = 'basic_ready';
|
||||
|
||||
if ( loadFullCore && loadFullCore._load )
|
||||
loadFullCore();
|
||||
else if ( loadFullCoreTimeout ) {
|
||||
setTimeout( function() {
|
||||
if ( CKEDITOR.loadFullCore )
|
||||
CKEDITOR.loadFullCore();
|
||||
}, loadFullCoreTimeout * 1000 );
|
||||
}
|
||||
};
|
||||
|
||||
CKEDITOR.domReady( onload );
|
||||
} )();
|
||||
|
||||
CKEDITOR.status = 'basic_loaded';
|
||||
} )();
|
||||
}
|
||||
@@ -1,271 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a command that can be executed on an editor instance.
|
||||
*
|
||||
* var command = new CKEDITOR.command( editor, {
|
||||
* exec: function( editor ) {
|
||||
* alert( editor.document.getBody().getHtml() );
|
||||
* }
|
||||
* } );
|
||||
*
|
||||
* @class
|
||||
* @mixins CKEDITOR.event
|
||||
* @constructor Creates a command class instance.
|
||||
* @param {CKEDITOR.editor} editor The editor instance this command will be
|
||||
* related to.
|
||||
* @param {CKEDITOR.commandDefinition} commandDefinition The command
|
||||
* definition.
|
||||
*/
|
||||
CKEDITOR.command = function( editor, commandDefinition ) {
|
||||
/**
|
||||
* Lists UI items that are associated to this command. This list can be
|
||||
* used to interact with the UI on command execution (by the execution code
|
||||
* itself, for example).
|
||||
*
|
||||
* alert( 'Number of UI items associated to this command: ' + command.uiItems.length );
|
||||
*/
|
||||
this.uiItems = [];
|
||||
|
||||
/**
|
||||
* Executes the command.
|
||||
*
|
||||
* command.exec(); // The command gets executed.
|
||||
*
|
||||
* @param {Object} [data] Any data to pass to the command. Depends on the
|
||||
* command implementation and requirements.
|
||||
* @returns {Boolean} A boolean indicating that the command has been successfully executed.
|
||||
*/
|
||||
this.exec = function( data ) {
|
||||
if ( this.state == CKEDITOR.TRISTATE_DISABLED || !this.checkAllowed() )
|
||||
return false;
|
||||
|
||||
if ( this.editorFocus ) // Give editor focus if necessary (#4355).
|
||||
editor.focus();
|
||||
|
||||
if ( this.fire( 'exec' ) === false )
|
||||
return true;
|
||||
|
||||
return ( commandDefinition.exec.call( this, editor, data ) !== false );
|
||||
};
|
||||
|
||||
/**
|
||||
* Explicitly update the status of the command, by firing the {@link CKEDITOR.command#event-refresh} event,
|
||||
* as well as invoke the {@link CKEDITOR.commandDefinition#refresh} method if defined, this method
|
||||
* is to allow different parts of the editor code to contribute in command status resolution.
|
||||
*
|
||||
* @param {CKEDITOR.editor} editor The editor instance.
|
||||
* @param {CKEDITOR.dom.elementPath} path
|
||||
*/
|
||||
this.refresh = function( editor, path ) {
|
||||
// Do nothing is we're on read-only and this command doesn't support it.
|
||||
// We don't need to disabled the command explicitely here, because this
|
||||
// is already done by the "readOnly" event listener.
|
||||
if ( !this.readOnly && editor.readOnly )
|
||||
return true;
|
||||
|
||||
// Disable commands that are not allowed in the current selection path context.
|
||||
if ( this.context && !path.isContextFor( this.context ) ) {
|
||||
this.disable();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Disable commands that are not allowed by the active filter.
|
||||
if ( !this.checkAllowed( true ) ) {
|
||||
this.disable();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Make the "enabled" state a default for commands enabled from start.
|
||||
if ( !this.startDisabled )
|
||||
this.enable();
|
||||
|
||||
// Disable commands which shouldn't be enabled in this mode.
|
||||
if ( this.modes && !this.modes[ editor.mode ] )
|
||||
this.disable();
|
||||
|
||||
if ( this.fire( 'refresh', { editor: editor, path: path } ) === false )
|
||||
return true;
|
||||
|
||||
return ( commandDefinition.refresh && commandDefinition.refresh.apply( this, arguments ) !== false );
|
||||
};
|
||||
|
||||
var allowed;
|
||||
|
||||
/**
|
||||
* Checks whether this command is allowed by the active allowed
|
||||
* content filter ({@link CKEDITOR.editor#activeFilter}). This means
|
||||
* that if command implements {@link CKEDITOR.feature} interface it will be tested
|
||||
* by the {@link CKEDITOR.filter#checkFeature} method.
|
||||
*
|
||||
* @since 4.1
|
||||
* @param {Boolean} [noCache] Skip cache for example due to active filter change. Since CKEditor 4.2.
|
||||
* @returns {Boolean} Whether this command is allowed.
|
||||
*/
|
||||
this.checkAllowed = function( noCache ) {
|
||||
if ( !noCache && typeof allowed == 'boolean' )
|
||||
return allowed;
|
||||
|
||||
return allowed = editor.activeFilter.checkFeature( this );
|
||||
};
|
||||
|
||||
CKEDITOR.tools.extend( this, commandDefinition, {
|
||||
/**
|
||||
* The editor modes within which the command can be executed. The
|
||||
* execution will have no action if the current mode is not listed
|
||||
* in this property.
|
||||
*
|
||||
* // Enable the command in both WYSIWYG and Source modes.
|
||||
* command.modes = { wysiwyg:1,source:1 };
|
||||
*
|
||||
* // Enable the command in Source mode only.
|
||||
* command.modes = { source:1 };
|
||||
*
|
||||
* @see CKEDITOR.editor#mode
|
||||
*/
|
||||
modes: { wysiwyg: 1 },
|
||||
|
||||
/**
|
||||
* Indicates that the editor will get the focus before executing
|
||||
* the command.
|
||||
*
|
||||
* // Do not force the editor to have focus when executing the command.
|
||||
* command.editorFocus = false;
|
||||
*
|
||||
* @property {Boolean} [=true]
|
||||
*/
|
||||
editorFocus: 1,
|
||||
|
||||
/**
|
||||
* Indicates that this command is sensible to the selection context.
|
||||
* If `true`, the {@link CKEDITOR.command#method-refresh} method will be
|
||||
* called for this command on the {@link CKEDITOR.editor#event-selectionChange} event.
|
||||
*
|
||||
* @property {Boolean} [=false]
|
||||
*/
|
||||
contextSensitive: !!commandDefinition.context,
|
||||
|
||||
/**
|
||||
* Indicates the editor state. Possible values are:
|
||||
*
|
||||
* * {@link CKEDITOR#TRISTATE_DISABLED}: the command is
|
||||
* disabled. It's execution will have no effect. Same as {@link #disable}.
|
||||
* * {@link CKEDITOR#TRISTATE_ON}: the command is enabled
|
||||
* and currently active in the editor (for context sensitive commands, for example).
|
||||
* * {@link CKEDITOR#TRISTATE_OFF}: the command is enabled
|
||||
* and currently inactive in the editor (for context sensitive commands, for example).
|
||||
*
|
||||
* Do not set this property directly, using the {@link #setState} method instead.
|
||||
*
|
||||
* if ( command.state == CKEDITOR.TRISTATE_DISABLED )
|
||||
* alert( 'This command is disabled' );
|
||||
*
|
||||
* @property {Number} [=CKEDITOR.TRISTATE_DISABLED]
|
||||
*/
|
||||
state: CKEDITOR.TRISTATE_DISABLED
|
||||
} );
|
||||
|
||||
// Call the CKEDITOR.event constructor to initialize this instance.
|
||||
CKEDITOR.event.call( this );
|
||||
};
|
||||
|
||||
CKEDITOR.command.prototype = {
|
||||
/**
|
||||
* Enables the command for execution. The command state (see
|
||||
* {@link CKEDITOR.command#property-state}) available before disabling it is restored.
|
||||
*
|
||||
* command.enable();
|
||||
* command.exec(); // Execute the command.
|
||||
*/
|
||||
enable: function() {
|
||||
if ( this.state == CKEDITOR.TRISTATE_DISABLED && this.checkAllowed() )
|
||||
this.setState( ( !this.preserveState || ( typeof this.previousState == 'undefined' ) ) ? CKEDITOR.TRISTATE_OFF : this.previousState );
|
||||
},
|
||||
|
||||
/**
|
||||
* Disables the command for execution. The command state (see
|
||||
* {@link CKEDITOR.command#property-state}) will be set to {@link CKEDITOR#TRISTATE_DISABLED}.
|
||||
*
|
||||
* command.disable();
|
||||
* command.exec(); // "false" - Nothing happens.
|
||||
*/
|
||||
disable: function() {
|
||||
this.setState( CKEDITOR.TRISTATE_DISABLED );
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the command state.
|
||||
*
|
||||
* command.setState( CKEDITOR.TRISTATE_ON );
|
||||
* command.exec(); // Execute the command.
|
||||
* command.setState( CKEDITOR.TRISTATE_DISABLED );
|
||||
* command.exec(); // 'false' - Nothing happens.
|
||||
* command.setState( CKEDITOR.TRISTATE_OFF );
|
||||
* command.exec(); // Execute the command.
|
||||
*
|
||||
* @param {Number} newState The new state. See {@link #property-state}.
|
||||
* @returns {Boolean} Returns `true` if the command state changed.
|
||||
*/
|
||||
setState: function( newState ) {
|
||||
// Do nothing if there is no state change.
|
||||
if ( this.state == newState )
|
||||
return false;
|
||||
|
||||
if ( newState != CKEDITOR.TRISTATE_DISABLED && !this.checkAllowed() )
|
||||
return false;
|
||||
|
||||
this.previousState = this.state;
|
||||
|
||||
// Set the new state.
|
||||
this.state = newState;
|
||||
|
||||
// Fire the "state" event, so other parts of the code can react to the
|
||||
// change.
|
||||
this.fire( 'state' );
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggles the on/off (active/inactive) state of the command. This is
|
||||
* mainly used internally by context sensitive commands.
|
||||
*
|
||||
* command.toggleState();
|
||||
*/
|
||||
toggleState: function() {
|
||||
if ( this.state == CKEDITOR.TRISTATE_OFF )
|
||||
this.setState( CKEDITOR.TRISTATE_ON );
|
||||
else if ( this.state == CKEDITOR.TRISTATE_ON )
|
||||
this.setState( CKEDITOR.TRISTATE_OFF );
|
||||
}
|
||||
};
|
||||
|
||||
CKEDITOR.event.implementOn( CKEDITOR.command.prototype );
|
||||
|
||||
/**
|
||||
* Indicates the previous command state.
|
||||
*
|
||||
* alert( command.previousState );
|
||||
*
|
||||
* @property {Number} previousState
|
||||
* @see #state
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fired when the command state changes.
|
||||
*
|
||||
* command.on( 'state', function() {
|
||||
* // Alerts the new state.
|
||||
* alert( this.state );
|
||||
* } );
|
||||
*
|
||||
* @event state
|
||||
*/
|
||||
|
||||
/**
|
||||
* @event refresh
|
||||
* @todo
|
||||
*/
|
||||
@@ -1,139 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileOverview Defines the "virtual" {@link CKEDITOR.commandDefinition} class,
|
||||
* which contains the defintion of a command. This file is for
|
||||
* documentation purposes only.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Virtual class that illustrates the features of command objects to be
|
||||
* passed to the {@link CKEDITOR.editor#addCommand} function.
|
||||
*
|
||||
* @class CKEDITOR.commandDefinition
|
||||
* @abstract
|
||||
*/
|
||||
|
||||
/**
|
||||
* The function to be fired when the commend is executed.
|
||||
*
|
||||
* editorInstance.addCommand( 'sample', {
|
||||
* exec: function( editor ) {
|
||||
* alert( 'Executing a command for the editor name "' + editor.name + '"!' );
|
||||
* }
|
||||
* } );
|
||||
*
|
||||
* @method exec
|
||||
* @param {CKEDITOR.editor} editor The editor within which run the command.
|
||||
* @param {Object} [data] Additional data to be used to execute the command.
|
||||
* @returns {Boolean} Whether the command has been successfully executed.
|
||||
* Defaults to `true`, if nothing is returned.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Whether the command need to be hooked into the redo/undo system.
|
||||
*
|
||||
* editorInstance.addCommand( 'alertName', {
|
||||
* exec: function( editor ) {
|
||||
* alert( editor.name );
|
||||
* },
|
||||
* canUndo: false // No support for undo/redo.
|
||||
* } );
|
||||
*
|
||||
* @property {Boolean} [canUndo=true]
|
||||
*/
|
||||
|
||||
/**
|
||||
* Whether the command is asynchronous, which means that the
|
||||
* {@link CKEDITOR.editor#event-afterCommandExec} event will be fired by the
|
||||
* command itself manually, and that the return value of this command is not to
|
||||
* be returned by the {@link #exec} function.
|
||||
*
|
||||
* editorInstance.addCommand( 'loadOptions', {
|
||||
* exec: function( editor ) {
|
||||
* // Asynchronous operation below.
|
||||
* CKEDITOR.ajax.loadXml( 'data.xml', function() {
|
||||
* editor.fire( 'afterCommandExec' );
|
||||
* } );
|
||||
* },
|
||||
* async: true // The command need some time to complete after exec function returns.
|
||||
* } );
|
||||
*
|
||||
* @property {Boolean} [async=false]
|
||||
*/
|
||||
|
||||
/**
|
||||
* Whether the command should give focus to the editor before execution.
|
||||
*
|
||||
* editorInstance.addCommand( 'maximize', {
|
||||
* exec: function( editor ) {
|
||||
* // ...
|
||||
* },
|
||||
* editorFocus: false // The command doesn't require focusing the editing document.
|
||||
* } );
|
||||
*
|
||||
* @property {Boolean} [editorFocus=true]
|
||||
* @see CKEDITOR.command#editorFocus
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Whether the command state should be set to {@link CKEDITOR#TRISTATE_DISABLED} on startup.
|
||||
*
|
||||
* editorInstance.addCommand( 'unlink', {
|
||||
* exec: function( editor ) {
|
||||
* // ...
|
||||
* },
|
||||
* startDisabled: true // Command is unavailable until selection is inside a link.
|
||||
* } );
|
||||
*
|
||||
* @property {Boolean} [startDisabled=false]
|
||||
*/
|
||||
|
||||
/**
|
||||
* Indicates that this command is sensible to the selection context.
|
||||
* If `true`, the {@link CKEDITOR.command#method-refresh} method will be
|
||||
* called for this command on selection changes, with a single parameter
|
||||
* representing the current elements path.
|
||||
*
|
||||
* @property {Boolean} [contextSensitive=true]
|
||||
*/
|
||||
|
||||
/**
|
||||
* Defined by command definition a function to determinate the command state, it will be invoked
|
||||
* when editor has it's `states` or `selection` changed.
|
||||
*
|
||||
* **Note:** The function provided must be calling {@link CKEDITOR.command#setState} in all circumstance,
|
||||
* if it is intended to update the command state.
|
||||
*
|
||||
* @method refresh
|
||||
* @param {CKEDITOR.editor} editor
|
||||
* @param {CKEDITOR.dom.elementPath} path
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sets the element name used to reflect the command state on selection changes.
|
||||
* If the selection is in a place where the element is not allowed, the command
|
||||
* will be disabled.
|
||||
* Setting this property overrides {@link #contextSensitive} to `true`.
|
||||
*
|
||||
* @property {Boolean} [context=true]
|
||||
*/
|
||||
|
||||
/**
|
||||
* The editor modes within which the command can be executed. The execution
|
||||
* will have no action if the current mode is not listed in this property.
|
||||
*
|
||||
* editorInstance.addCommand( 'link', {
|
||||
* exec: function( editor ) {
|
||||
* // ...
|
||||
* },
|
||||
* modes: { wysiwyg:1 } // Command is available in wysiwyg mode only.
|
||||
* } );
|
||||
*
|
||||
* @property {Object} [modes={ wysiwyg:1 }]
|
||||
* @see CKEDITOR.command#modes
|
||||
*/
|
||||
@@ -1,383 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileOverview Defines the {@link CKEDITOR.config} object that stores the
|
||||
* default configuration settings.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Used in conjunction with {@link CKEDITOR.config#enterMode}
|
||||
* and {@link CKEDITOR.config#shiftEnterMode} configuration
|
||||
* settings to make the editor produce `<p>` tags when
|
||||
* using the *Enter* key.
|
||||
*
|
||||
* @readonly
|
||||
* @property {Number} [=1]
|
||||
* @member CKEDITOR
|
||||
*/
|
||||
CKEDITOR.ENTER_P = 1;
|
||||
|
||||
/**
|
||||
* Used in conjunction with {@link CKEDITOR.config#enterMode}
|
||||
* and {@link CKEDITOR.config#shiftEnterMode} configuration
|
||||
* settings to make the editor produce `<br>` tags when
|
||||
* using the *Enter* key.
|
||||
*
|
||||
* @readonly
|
||||
* @property {Number} [=2]
|
||||
* @member CKEDITOR
|
||||
*/
|
||||
CKEDITOR.ENTER_BR = 2;
|
||||
|
||||
/**
|
||||
* Used in conjunction with {@link CKEDITOR.config#enterMode}
|
||||
* and {@link CKEDITOR.config#shiftEnterMode} configuration
|
||||
* settings to make the editor produce `<div>` tags when
|
||||
* using the *Enter* key.
|
||||
*
|
||||
* @readonly
|
||||
* @property {Number} [=3]
|
||||
* @member CKEDITOR
|
||||
*/
|
||||
CKEDITOR.ENTER_DIV = 3;
|
||||
|
||||
/**
|
||||
* Stores default configuration settings. Changes to this object are
|
||||
* reflected in all editor instances, if not specified otherwise for a particular
|
||||
* instance.
|
||||
*
|
||||
* @class
|
||||
* @singleton
|
||||
*/
|
||||
CKEDITOR.config = {
|
||||
/**
|
||||
* The URL path for the custom configuration file to be loaded. If not
|
||||
* overloaded with inline configuration, it defaults to the `config.js`
|
||||
* file present in the root of the CKEditor installation directory.
|
||||
*
|
||||
* CKEditor will recursively load custom configuration files defined inside
|
||||
* other custom configuration files.
|
||||
*
|
||||
* // Load a specific configuration file.
|
||||
* CKEDITOR.replace( 'myfield', { customConfig: '/myconfig.js' } );
|
||||
*
|
||||
* // Do not load any custom configuration file.
|
||||
* CKEDITOR.replace( 'myfield', { customConfig: '' } );
|
||||
*
|
||||
* @cfg {String} [="<CKEditor folder>/config.js"]
|
||||
*/
|
||||
customConfig: 'config.js',
|
||||
|
||||
/**
|
||||
* Whether the replaced element (usually a `<textarea>`)
|
||||
* is to be updated automatically when posting the form containing the editor.
|
||||
*
|
||||
* @cfg
|
||||
*/
|
||||
autoUpdateElement: true,
|
||||
|
||||
/**
|
||||
* The user interface language localization to use. If left empty, the editor
|
||||
* will automatically be localized to the user language. If the user language is not supported,
|
||||
* the language specified in the {@link CKEDITOR.config#defaultLanguage}
|
||||
* configuration setting is used.
|
||||
*
|
||||
* // Load the German interface.
|
||||
* config.language = 'de';
|
||||
*
|
||||
* @cfg
|
||||
*/
|
||||
language: '',
|
||||
|
||||
/**
|
||||
* The language to be used if the {@link CKEDITOR.config#language}
|
||||
* setting is left empty and it is not possible to localize the editor to the user language.
|
||||
*
|
||||
* config.defaultLanguage = 'it';
|
||||
*
|
||||
* @cfg
|
||||
*/
|
||||
defaultLanguage: 'en',
|
||||
|
||||
/**
|
||||
* The writting direction of the language used to write the editor
|
||||
* contents. Allowed values are:
|
||||
*
|
||||
* * `''` (empty string) - indicate content direction will be the same with either the editor
|
||||
* UI direction or page element direction depending on the creators:
|
||||
* * Themed UI: The same with user interface language direction;
|
||||
* * Inline: The same with the editable element text direction;
|
||||
* * `'ltr'` - for Left-To-Right language (like English);
|
||||
* * `'rtl'` - for Right-To-Left languages (like Arabic).
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* config.contentsLangDirection = 'rtl';
|
||||
*
|
||||
* @cfg
|
||||
*/
|
||||
contentsLangDirection: '',
|
||||
|
||||
/**
|
||||
* Sets the behavior of the *Enter* key. It also determines other behavior
|
||||
* rules of the editor, like whether the `<br>` element is to be used
|
||||
* as a paragraph separator when indenting text.
|
||||
* The allowed values are the following constants that cause the behavior outlined below:
|
||||
*
|
||||
* * {@link CKEDITOR#ENTER_P} (1) – new `<p>` paragraphs are created;
|
||||
* * {@link CKEDITOR#ENTER_BR} (2) – lines are broken with `<br>` elements;
|
||||
* * {@link CKEDITOR#ENTER_DIV} (3) – new `<div>` blocks are created.
|
||||
*
|
||||
* **Note**: It is recommended to use the {@link CKEDITOR#ENTER_P} setting because of
|
||||
* its semantic value and correctness. The editor is optimized for this setting.
|
||||
*
|
||||
* // Not recommended.
|
||||
* config.enterMode = CKEDITOR.ENTER_BR;
|
||||
*
|
||||
* @cfg {Number} [=CKEDITOR.ENTER_P]
|
||||
*/
|
||||
enterMode: CKEDITOR.ENTER_P,
|
||||
|
||||
/**
|
||||
* Force the use of {@link CKEDITOR.config#enterMode} as line break regardless
|
||||
* of the context. If, for example, {@link CKEDITOR.config#enterMode} is set
|
||||
* to {@link CKEDITOR#ENTER_P}, pressing the *Enter* key inside a
|
||||
* `<div>` element will create a new paragraph with `<p>`
|
||||
* instead of a `<div>`.
|
||||
*
|
||||
* // Not recommended.
|
||||
* config.forceEnterMode = true;
|
||||
*
|
||||
* @since 3.2.1
|
||||
* @cfg
|
||||
*/
|
||||
forceEnterMode: false,
|
||||
|
||||
/**
|
||||
* Similarly to the {@link CKEDITOR.config#enterMode} setting, it defines the behavior
|
||||
* of the *Shift+Enter* key combination.
|
||||
*
|
||||
* The allowed values are the following constants the behavior outlined below:
|
||||
*
|
||||
* * {@link CKEDITOR#ENTER_P} (1) – new `<p>` paragraphs are created;
|
||||
* * {@link CKEDITOR#ENTER_BR} (2) – lines are broken with `<br>` elements;
|
||||
* * {@link CKEDITOR#ENTER_DIV} (3) – new `<div>` blocks are created.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* config.shiftEnterMode = CKEDITOR.ENTER_P;
|
||||
*
|
||||
* @cfg {Number} [=CKEDITOR.ENTER_BR]
|
||||
*/
|
||||
shiftEnterMode: CKEDITOR.ENTER_BR,
|
||||
|
||||
/**
|
||||
* Sets the `DOCTYPE` to be used when loading the editor content as HTML.
|
||||
*
|
||||
* // Set the DOCTYPE to the HTML 4 (Quirks) mode.
|
||||
* config.docType = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">';
|
||||
*
|
||||
* @cfg
|
||||
*/
|
||||
docType: '<!DOCTYPE html>',
|
||||
|
||||
/**
|
||||
* Sets the `id` attribute to be used on the `body` element
|
||||
* of the editing area. This can be useful when you intend to reuse the original CSS
|
||||
* file you are using on your live website and want to assign the editor the same ID
|
||||
* as the section that will include the contents. In this way ID-specific CSS rules will
|
||||
* be enabled.
|
||||
*
|
||||
* config.bodyId = 'contents_id';
|
||||
*
|
||||
* @since 3.1
|
||||
* @cfg
|
||||
*/
|
||||
bodyId: '',
|
||||
|
||||
/**
|
||||
* Sets the `class` attribute to be used on the `body` element
|
||||
* of the editing area. This can be useful when you intend to reuse the original CSS
|
||||
* file you are using on your live website and want to assign the editor the same class
|
||||
* as the section that will include the contents. In this way class-specific CSS rules will
|
||||
* be enabled.
|
||||
*
|
||||
* config.bodyClass = 'contents';
|
||||
*
|
||||
* **Note:** Editor needs to load stylesheets containing contents styles. You can either
|
||||
* copy them to the `contents.css` file that editor loads by default or set the {@link #contentsCss}
|
||||
* option.
|
||||
*
|
||||
* **Note:** This setting applies only to the classic editor (the one that uses `iframe`).
|
||||
*
|
||||
* @since 3.1
|
||||
* @cfg
|
||||
*/
|
||||
bodyClass: '',
|
||||
|
||||
/**
|
||||
* Indicates whether the contents to be edited are being input as a full HTML page.
|
||||
* A full page includes the `<html>`, `<head>`, and `<body>` elements.
|
||||
* The final output will also reflect this setting, including the
|
||||
* `<body>` contents only if this setting is disabled.
|
||||
*
|
||||
* config.fullPage = true;
|
||||
*
|
||||
* @since 3.1
|
||||
* @cfg
|
||||
*/
|
||||
fullPage: false,
|
||||
|
||||
/**
|
||||
* The height of the editing area (that includes the editor content). This
|
||||
* can be an integer, for pixel sizes, or any CSS-defined length unit.
|
||||
*
|
||||
* **Note:** Percent units (%) are not supported.
|
||||
*
|
||||
* config.height = 500; // 500 pixels.
|
||||
* config.height = '25em'; // CSS length.
|
||||
* config.height = '300px'; // CSS length.
|
||||
*
|
||||
* @cfg {Number/String}
|
||||
*/
|
||||
height: 200,
|
||||
|
||||
/**
|
||||
* Comma separated list of plugins to be used for an editor instance,
|
||||
* besides, the actual plugins that to be loaded could be still affected by two other settings:
|
||||
* {@link CKEDITOR.config#extraPlugins} and {@link CKEDITOR.config#removePlugins}.
|
||||
*
|
||||
* @cfg {String} [="<default list of plugins>"]
|
||||
*/
|
||||
plugins: '', // %REMOVE_LINE%
|
||||
|
||||
/**
|
||||
* A list of additional plugins to be loaded. This setting makes it easier
|
||||
* to add new plugins without having to touch {@link CKEDITOR.config#plugins} setting.
|
||||
*
|
||||
* config.extraPlugins = 'myplugin,anotherplugin';
|
||||
*
|
||||
* @cfg
|
||||
*/
|
||||
extraPlugins: '',
|
||||
|
||||
/**
|
||||
* A list of plugins that must not be loaded. This setting makes it possible
|
||||
* to avoid loading some plugins defined in the {@link CKEDITOR.config#plugins}
|
||||
* setting, without having to touch it.
|
||||
*
|
||||
* **Note:** Plugin required by other plugin cannot be removed (error will be thrown).
|
||||
* So e.g. if `contextmenu` is required by `tabletools`, then it can be removed
|
||||
* only if `tabletools` isn't loaded.
|
||||
*
|
||||
* config.removePlugins = 'elementspath,save,font';
|
||||
*
|
||||
* @cfg
|
||||
*/
|
||||
removePlugins: '',
|
||||
|
||||
/**
|
||||
* List of regular expressions to be executed on input HTML,
|
||||
* indicating HTML source code that when matched, must **not** be available in the WYSIWYG
|
||||
* mode for editing.
|
||||
*
|
||||
* config.protectedSource.push( /<\?[\s\S]*?\?>/g ); // PHP code
|
||||
* config.protectedSource.push( /<%[\s\S]*?%>/g ); // ASP code
|
||||
* config.protectedSource.push( /(<asp:[^\>]+>[\s|\S]*?<\/asp:[^\>]+>)|(<asp:[^\>]+\/>)/gi ); // ASP.Net code
|
||||
*
|
||||
* @cfg
|
||||
*/
|
||||
protectedSource: [],
|
||||
|
||||
/**
|
||||
* The editor `tabindex` value.
|
||||
*
|
||||
* config.tabIndex = 1;
|
||||
*
|
||||
* @cfg
|
||||
*/
|
||||
tabIndex: 0,
|
||||
|
||||
/**
|
||||
* The editor UI outer width. This can be an integer, for pixel sizes, or
|
||||
* any CSS-defined unit.
|
||||
*
|
||||
* Unlike the {@link CKEDITOR.config#height} setting, this
|
||||
* one will set the outer width of the entire editor UI, not for the
|
||||
* editing area only.
|
||||
*
|
||||
* config.width = 850; // 850 pixels wide.
|
||||
* config.width = '75%'; // CSS unit.
|
||||
*
|
||||
* @cfg {String/Number}
|
||||
*/
|
||||
width: '',
|
||||
|
||||
/**
|
||||
* The base Z-index for floating dialog windows and popups.
|
||||
*
|
||||
* config.baseFloatZIndex = 2000;
|
||||
*
|
||||
* @cfg
|
||||
*/
|
||||
baseFloatZIndex: 10000,
|
||||
|
||||
/**
|
||||
* The keystrokes that are blocked by default as the browser implementation
|
||||
* is buggy. These default keystrokes are handled by the editor.
|
||||
*
|
||||
* // Default setting.
|
||||
* config.blockedKeystrokes = [
|
||||
* CKEDITOR.CTRL + 66, // CTRL+B
|
||||
* CKEDITOR.CTRL + 73, // CTRL+I
|
||||
* CKEDITOR.CTRL + 85, // CTRL+U
|
||||
* CKEDITOR.CTRL + 89, // CTRL+Y
|
||||
* CKEDITOR.CTRL + 90, // CTRL+Z
|
||||
* CKEDITOR.CTRL + CKEDITOR.SHIFT + 90 // CTRL+SHIFT+Z
|
||||
* ];
|
||||
*
|
||||
* @cfg {Array} [blockedKeystrokes=see example]
|
||||
*/
|
||||
blockedKeystrokes: [
|
||||
CKEDITOR.CTRL + 66, // CTRL+B
|
||||
CKEDITOR.CTRL + 73, // CTRL+I
|
||||
CKEDITOR.CTRL + 85, // CTRL+U
|
||||
|
||||
CKEDITOR.CTRL + 89, // CTRL+Y
|
||||
CKEDITOR.CTRL + 90, // CTRL+Z
|
||||
CKEDITOR.CTRL + CKEDITOR.SHIFT + 90 // CTRL+SHIFT+Z
|
||||
]
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicates that some of the editor features, like alignment and text
|
||||
* direction, should use the "computed value" of the feature to indicate its
|
||||
* on/off state instead of using the "real value".
|
||||
*
|
||||
* If enabled in a Left-To-Right written document, the "Left Justify"
|
||||
* alignment button will be shown as active, even if the alignment style is not
|
||||
* explicitly applied to the current paragraph in the editor.
|
||||
*
|
||||
* config.useComputedState = false;
|
||||
*
|
||||
* @since 3.4
|
||||
* @cfg {Boolean} [useComputedState=true]
|
||||
*/
|
||||
|
||||
/**
|
||||
* The base user interface color to be used by the editor. Not all skins are
|
||||
* compatible with this setting.
|
||||
*
|
||||
* // Using a color code.
|
||||
* config.uiColor = '#AADC6E';
|
||||
*
|
||||
* // Using an HTML color name.
|
||||
* config.uiColor = 'Gold';
|
||||
*
|
||||
* @cfg {String} uiColor
|
||||
*/
|
||||
|
||||
// PACKAGER_RENAME( CKEDITOR.config )
|
||||
@@ -1,153 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
( function() {
|
||||
/** @class CKEDITOR */
|
||||
|
||||
/**
|
||||
* Turns a DOM element with `contenteditable` attribute set to `true` into a
|
||||
* CKEditor instance. Check {@link CKEDITOR.dtd#$editable} for the list of
|
||||
* allowed element names.
|
||||
*
|
||||
* <div contenteditable="true" id="content">...</div>
|
||||
* ...
|
||||
* CKEDITOR.inline( 'content' );
|
||||
*
|
||||
* It is also possible to create an inline editor from the `<textarea>` element.
|
||||
* If you do so, an additional `<div>` element with editable content will be created
|
||||
* directly after the `<textarea>` element and the `<textarea>` element will be hidden.
|
||||
*
|
||||
* @param {Object/String} element The DOM element or its ID.
|
||||
* @param {Object} [instanceConfig] The specific configurations to apply to this editor instance.
|
||||
* See {@link CKEDITOR.config}.
|
||||
* @returns {CKEDITOR.editor} The editor instance created.
|
||||
*/
|
||||
CKEDITOR.inline = function( element, instanceConfig ) {
|
||||
if ( !CKEDITOR.env.isCompatible )
|
||||
return null;
|
||||
|
||||
element = CKEDITOR.dom.element.get( element );
|
||||
|
||||
// Avoid multiple inline editor instances on the same element.
|
||||
if ( element.getEditor() )
|
||||
throw 'The editor instance "' + element.getEditor().name + '" is already attached to the provided element.';
|
||||
|
||||
var editor = new CKEDITOR.editor( instanceConfig, element, CKEDITOR.ELEMENT_MODE_INLINE ),
|
||||
textarea = element.is( 'textarea' ) ? element : null;
|
||||
|
||||
if ( textarea ) {
|
||||
editor.setData( textarea.getValue(), null, true );
|
||||
|
||||
//Change element from textarea to div
|
||||
element = CKEDITOR.dom.element.createFromHtml(
|
||||
'<div contenteditable="' + !!editor.readOnly + '" class="cke_textarea_inline">' +
|
||||
textarea.getValue() +
|
||||
'</div>',
|
||||
CKEDITOR.document );
|
||||
|
||||
element.insertAfter( textarea );
|
||||
textarea.hide();
|
||||
|
||||
// Attaching the concrete form.
|
||||
if ( textarea.$.form )
|
||||
editor._attachToForm();
|
||||
} else {
|
||||
// Initial editor data is simply loaded from the page element content to make
|
||||
// data retrieval possible immediately after the editor creation.
|
||||
editor.setData( element.getHtml(), null, true );
|
||||
}
|
||||
|
||||
// Once the editor is loaded, start the UI.
|
||||
editor.on( 'loaded', function() {
|
||||
editor.fire( 'uiReady' );
|
||||
|
||||
// Enable editing on the element.
|
||||
editor.editable( element );
|
||||
|
||||
// Editable itself is the outermost element.
|
||||
editor.container = element;
|
||||
|
||||
// Load and process editor data.
|
||||
editor.setData( editor.getData( 1 ) );
|
||||
|
||||
// Clean on startup.
|
||||
editor.resetDirty();
|
||||
|
||||
editor.fire( 'contentDom' );
|
||||
|
||||
// Inline editing defaults to "wysiwyg" mode, so plugins don't
|
||||
// need to make special handling for this "mode-less" environment.
|
||||
editor.mode = 'wysiwyg';
|
||||
editor.fire( 'mode' );
|
||||
|
||||
// The editor is completely loaded for interaction.
|
||||
editor.status = 'ready';
|
||||
editor.fireOnce( 'instanceReady' );
|
||||
CKEDITOR.fire( 'instanceReady', null, editor );
|
||||
|
||||
// give priority to plugins that relay on editor#loaded for bootstrapping.
|
||||
}, null, null, 10000 );
|
||||
|
||||
// Handle editor destroying.
|
||||
editor.on( 'destroy', function() {
|
||||
// Remove container from DOM if inline-textarea editor.
|
||||
// Show <textarea> back again.
|
||||
if ( textarea ) {
|
||||
editor.container.clearCustomData();
|
||||
editor.container.remove();
|
||||
textarea.show();
|
||||
}
|
||||
|
||||
editor.element.clearCustomData();
|
||||
|
||||
delete editor.element;
|
||||
} );
|
||||
|
||||
return editor;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calls {@link CKEDITOR#inline} for all page elements with
|
||||
* `contenteditable` attribute set to `true`.
|
||||
*
|
||||
*/
|
||||
CKEDITOR.inlineAll = function() {
|
||||
var el, data;
|
||||
|
||||
for ( var name in CKEDITOR.dtd.$editable ) {
|
||||
var elements = CKEDITOR.document.getElementsByTag( name );
|
||||
|
||||
for ( var i = 0, len = elements.count(); i < len; i++ ) {
|
||||
el = elements.getItem( i );
|
||||
|
||||
if ( el.getAttribute( 'contenteditable' ) == 'true' ) {
|
||||
// Fire the "inline" event, making it possible to customize
|
||||
// the instance settings and eventually cancel the creation.
|
||||
|
||||
data = {
|
||||
element: el,
|
||||
config: {}
|
||||
};
|
||||
|
||||
if ( CKEDITOR.fire( 'inline', data ) !== false )
|
||||
CKEDITOR.inline( el, data.config );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
CKEDITOR.domReady( function() {
|
||||
!CKEDITOR.disableAutoInline && CKEDITOR.inlineAll();
|
||||
} );
|
||||
} )();
|
||||
|
||||
/**
|
||||
* Disables creating the inline editor automatically for elements with
|
||||
* `contenteditable` attribute set to the `true`.
|
||||
*
|
||||
* CKEDITOR.disableAutoInline = true;
|
||||
*
|
||||
* @cfg {Boolean} [disableAutoInline=false]
|
||||
*/
|
||||
@@ -1,457 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/** @class CKEDITOR */
|
||||
|
||||
/**
|
||||
* The class name used to identify `<textarea>` elements to be replaced
|
||||
* by CKEditor instances. Set it to empty/`null` to disable this feature.
|
||||
*
|
||||
* CKEDITOR.replaceClass = 'rich_editor';
|
||||
*
|
||||
* @cfg {String} [replaceClass='ckeditor']
|
||||
*/
|
||||
CKEDITOR.replaceClass = 'ckeditor';
|
||||
|
||||
( function() {
|
||||
/**
|
||||
* Replaces a `<textarea>` or a DOM element (`<div>`) with a CKEditor
|
||||
* instance. For textareas, the initial value in the editor will be the
|
||||
* textarea value. For DOM elements, their `innerHTML` will be used
|
||||
* instead. We recommend using `<textarea>` and `<div>` elements only.
|
||||
*
|
||||
* <textarea id="myfield" name="myfield"></textarea>
|
||||
* ...
|
||||
* CKEDITOR.replace( 'myfield' );
|
||||
*
|
||||
* var textarea = document.body.appendChild( document.createElement( 'textarea' ) );
|
||||
* CKEDITOR.replace( textarea );
|
||||
*
|
||||
* @param {Object/String} element The DOM element (textarea), its ID, or name.
|
||||
* @param {Object} [config] The specific configuration to apply to this
|
||||
* editor instance. Configuration set here will override the global CKEditor settings
|
||||
* (see {@link CKEDITOR.config}).
|
||||
* @returns {CKEDITOR.editor} The editor instance created.
|
||||
*/
|
||||
CKEDITOR.replace = function( element, config ) {
|
||||
return createInstance( element, config, null, CKEDITOR.ELEMENT_MODE_REPLACE );
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new editor instance at the end of a specific DOM element.
|
||||
*
|
||||
* <div id="editorSpace"></div>
|
||||
* ...
|
||||
* CKEDITOR.appendTo( 'editorSpace' );
|
||||
*
|
||||
* @param {Object/String} element The DOM element, its ID, or name.
|
||||
* @param {Object} [config] The specific configuration to apply to this
|
||||
* editor instance. Configuration set here will override the global CKEditor settings
|
||||
* (see {@link CKEDITOR.config}).
|
||||
* @param {String} [data] Since 3.3. Initial value for the instance.
|
||||
* @returns {CKEDITOR.editor} The editor instance created.
|
||||
*/
|
||||
CKEDITOR.appendTo = function( element, config, data )
|
||||
{
|
||||
return createInstance( element, config, data, CKEDITOR.ELEMENT_MODE_APPENDTO );
|
||||
};
|
||||
|
||||
/**
|
||||
* Replaces all `<textarea>` elements available in the document with
|
||||
* editor instances.
|
||||
*
|
||||
* // Replace all <textarea> elements in the page.
|
||||
* CKEDITOR.replaceAll();
|
||||
*
|
||||
* // Replace all <textarea class="myClassName"> elements in the page.
|
||||
* CKEDITOR.replaceAll( 'myClassName' );
|
||||
*
|
||||
* // Selectively replace <textarea> elements, based on custom assertions.
|
||||
* CKEDITOR.replaceAll( function( textarea, config ) {
|
||||
* // An assertion function that needs to be evaluated for the <textarea>
|
||||
* // to be replaced. It must explicitely return "false" to ignore a
|
||||
* // specific <textarea>.
|
||||
* // You can also customize the editor instance by having the function
|
||||
* // modify the "config" parameter.
|
||||
* } );
|
||||
*
|
||||
* @param {String} [className] The `<textarea>` class name.
|
||||
* @param {Function} [function] An assertion function that must return `true` for a `<textarea>`
|
||||
* to be replaced with the editor. If the function returns `false`, the `<textarea>` element
|
||||
* will not be replaced.
|
||||
*/
|
||||
CKEDITOR.replaceAll = function() {
|
||||
var textareas = document.getElementsByTagName( 'textarea' );
|
||||
|
||||
for ( var i = 0; i < textareas.length; i++ ) {
|
||||
var config = null,
|
||||
textarea = textareas[ i ];
|
||||
|
||||
// The "name" and/or "id" attribute must exist.
|
||||
if ( !textarea.name && !textarea.id )
|
||||
continue;
|
||||
|
||||
if ( typeof arguments[ 0 ] == 'string' ) {
|
||||
// The textarea class name could be passed as the function
|
||||
// parameter.
|
||||
|
||||
var classRegex = new RegExp( '(?:^|\\s)' + arguments[ 0 ] + '(?:$|\\s)' );
|
||||
|
||||
if ( !classRegex.test( textarea.className ) )
|
||||
continue;
|
||||
} else if ( typeof arguments[ 0 ] == 'function' ) {
|
||||
// An assertion function could be passed as the function parameter.
|
||||
// It must explicitly return "false" to ignore a specific <textarea>.
|
||||
config = {};
|
||||
if ( arguments[ 0 ]( textarea, config ) === false )
|
||||
continue;
|
||||
}
|
||||
|
||||
this.replace( textarea, config );
|
||||
}
|
||||
};
|
||||
|
||||
/** @class CKEDITOR.editor */
|
||||
|
||||
/**
|
||||
* Registers an editing mode. This function is to be used mainly by plugins.
|
||||
*
|
||||
* @param {String} mode The mode name.
|
||||
* @param {Function} exec The function that performs the actual mode change.
|
||||
*/
|
||||
CKEDITOR.editor.prototype.addMode = function( mode, exec ) {
|
||||
( this._.modes || ( this._.modes = {} ) )[ mode ] = exec;
|
||||
};
|
||||
|
||||
/**
|
||||
* Changes the editing mode of this editor instance.
|
||||
*
|
||||
* **Note:** The mode switch could be asynchronous depending on the mode provider.
|
||||
* Use the `callback` to hook subsequent code.
|
||||
*
|
||||
* // Switch to "source" view.
|
||||
* CKEDITOR.instances.editor1.setMode( 'source' );
|
||||
* // Switch to "wysiwyg" view and be notified on completion.
|
||||
* CKEDITOR.instances.editor1.setMode( 'wysiwyg', function() { alert( 'wysiwyg mode loaded!' ); } );
|
||||
*
|
||||
* @param {String} [newMode] If not specified, the {@link CKEDITOR.config#startupMode} will be used.
|
||||
* @param {Function} [callback] Optional callback function which is invoked once the mode switch has succeeded.
|
||||
*/
|
||||
CKEDITOR.editor.prototype.setMode = function( newMode, callback ) {
|
||||
var editor = this;
|
||||
|
||||
var modes = this._.modes;
|
||||
|
||||
// Mode loading quickly fails.
|
||||
if ( newMode == editor.mode || !modes || !modes[ newMode ] )
|
||||
return;
|
||||
|
||||
editor.fire( 'beforeSetMode', newMode );
|
||||
|
||||
if ( editor.mode ) {
|
||||
var isDirty = editor.checkDirty();
|
||||
|
||||
editor._.previousMode = editor.mode;
|
||||
|
||||
editor.fire( 'beforeModeUnload' );
|
||||
|
||||
// Detach the current editable.
|
||||
editor.editable( 0 );
|
||||
|
||||
// Clear up the mode space.
|
||||
editor.ui.space( 'contents' ).setHtml( '' );
|
||||
|
||||
editor.mode = '';
|
||||
}
|
||||
|
||||
// Fire the mode handler.
|
||||
this._.modes[ newMode ]( function() {
|
||||
// Set the current mode.
|
||||
editor.mode = newMode;
|
||||
|
||||
if ( isDirty !== undefined )
|
||||
!isDirty && editor.resetDirty();
|
||||
|
||||
// Delay to avoid race conditions (setMode inside setMode).
|
||||
setTimeout( function() {
|
||||
editor.fire( 'mode' );
|
||||
callback && callback.call( editor );
|
||||
}, 0 );
|
||||
} );
|
||||
};
|
||||
|
||||
/**
|
||||
* Resizes the editor interface.
|
||||
*
|
||||
* editor.resize( 900, 300 );
|
||||
*
|
||||
* editor.resize( '100%', 450, true );
|
||||
*
|
||||
* @param {Number/String} width The new width. It can be an integer denoting a value
|
||||
* in pixels or a CSS size value with unit.
|
||||
* @param {Number/String} height The new height. It can be an integer denoting a value
|
||||
* in pixels or a CSS size value with unit.
|
||||
* @param {Boolean} [isContentHeight] Indicates that the provided height is to
|
||||
* be applied to the editor content area, and not to the entire editor
|
||||
* interface. Defaults to `false`.
|
||||
* @param {Boolean} [resizeInner] Indicates that it is the inner interface
|
||||
* element that must be resized, not the outer element. The default theme
|
||||
* defines the editor interface inside a pair of `<span>` elements
|
||||
* (`<span><span>...</span></span>`). By default the first,
|
||||
* outer `<span>` element receives the sizes. If this parameter is set to
|
||||
* `true`, the second, inner `<span>` is resized instead.
|
||||
*/
|
||||
CKEDITOR.editor.prototype.resize = function( width, height, isContentHeight, resizeInner ) {
|
||||
var container = this.container,
|
||||
contents = this.ui.space( 'contents' ),
|
||||
contentsFrame = CKEDITOR.env.webkit && this.document && this.document.getWindow().$.frameElement,
|
||||
outer = resizeInner ? container.getChild( 1 ) : container;
|
||||
|
||||
// Set as border box width. (#5353)
|
||||
outer.setSize( 'width', width, true );
|
||||
|
||||
// WebKit needs to refresh the iframe size to avoid rendering issues. (1/2) (#8348)
|
||||
contentsFrame && ( contentsFrame.style.width = '1%' );
|
||||
|
||||
// Get the height delta between the outer table and the content area.
|
||||
// If we're setting the content area's height, then we don't need the delta.
|
||||
var delta = isContentHeight ? 0 : ( outer.$.offsetHeight || 0 ) - ( contents.$.clientHeight || 0 );
|
||||
contents.setStyle( 'height', Math.max( height - delta, 0 ) + 'px' );
|
||||
|
||||
// WebKit needs to refresh the iframe size to avoid rendering issues. (2/2) (#8348)
|
||||
contentsFrame && ( contentsFrame.style.width = '100%' );
|
||||
|
||||
// Emit a resize event.
|
||||
this.fire( 'resize' );
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the element that can be used to check the editor size. This method
|
||||
* is mainly used by the `resize` plugin, which adds a UI handle that can be used
|
||||
* to resize the editor.
|
||||
*
|
||||
* @param {Boolean} forContents Whether to return the "contents" part of the theme instead of the container.
|
||||
* @returns {CKEDITOR.dom.element} The resizable element.
|
||||
*/
|
||||
CKEDITOR.editor.prototype.getResizable = function( forContents ) {
|
||||
return forContents ? this.ui.space( 'contents' ) : this.container;
|
||||
};
|
||||
|
||||
function createInstance( element, config, data, mode ) {
|
||||
if ( !CKEDITOR.env.isCompatible )
|
||||
return null;
|
||||
|
||||
element = CKEDITOR.dom.element.get( element );
|
||||
|
||||
// Avoid multiple inline editor instances on the same element.
|
||||
if ( element.getEditor() )
|
||||
throw 'The editor instance "' + element.getEditor().name + '" is already attached to the provided element.';
|
||||
|
||||
// Create the editor instance.
|
||||
var editor = new CKEDITOR.editor( config, element, mode );
|
||||
|
||||
if ( mode == CKEDITOR.ELEMENT_MODE_REPLACE ) {
|
||||
// Do not replace the textarea right now, just hide it. The effective
|
||||
// replacement will be done later in the editor creation lifecycle.
|
||||
element.setStyle( 'visibility', 'hidden' );
|
||||
|
||||
// #8031 Remember if textarea was required and remove the attribute.
|
||||
editor._.required = element.hasAttribute( 'required' );
|
||||
element.removeAttribute( 'required' );
|
||||
}
|
||||
|
||||
data && editor.setData( data, null, true );
|
||||
|
||||
// Once the editor is loaded, start the UI.
|
||||
editor.on( 'loaded', function() {
|
||||
loadTheme( editor );
|
||||
|
||||
if ( mode == CKEDITOR.ELEMENT_MODE_REPLACE && editor.config.autoUpdateElement && element.$.form )
|
||||
editor._attachToForm();
|
||||
|
||||
editor.setMode( editor.config.startupMode, function() {
|
||||
// Clean on startup.
|
||||
editor.resetDirty();
|
||||
|
||||
// Editor is completely loaded for interaction.
|
||||
editor.status = 'ready';
|
||||
editor.fireOnce( 'instanceReady' );
|
||||
CKEDITOR.fire( 'instanceReady', null, editor );
|
||||
} );
|
||||
} );
|
||||
|
||||
editor.on( 'destroy', destroy );
|
||||
return editor;
|
||||
}
|
||||
|
||||
function destroy() {
|
||||
var editor = this,
|
||||
container = editor.container,
|
||||
element = editor.element;
|
||||
|
||||
if ( container ) {
|
||||
container.clearCustomData();
|
||||
container.remove();
|
||||
}
|
||||
|
||||
if ( element ) {
|
||||
element.clearCustomData();
|
||||
if ( editor.elementMode == CKEDITOR.ELEMENT_MODE_REPLACE ) {
|
||||
element.show();
|
||||
if ( editor._.required )
|
||||
element.setAttribute( 'required', 'required' );
|
||||
}
|
||||
delete editor.element;
|
||||
}
|
||||
}
|
||||
|
||||
var themedTpl;
|
||||
|
||||
function loadTheme( editor ) {
|
||||
var name = editor.name,
|
||||
element = editor.element,
|
||||
elementMode = editor.elementMode;
|
||||
|
||||
// Get the HTML for the predefined spaces.
|
||||
var topHtml = editor.fire( 'uiSpace', { space: 'top', html: '' } ).html;
|
||||
var bottomHtml = editor.fire( 'uiSpace', { space: 'bottom', html: '' } ).html;
|
||||
|
||||
if ( !themedTpl ) {
|
||||
themedTpl = CKEDITOR.addTemplate( 'maincontainer', '<{outerEl}' +
|
||||
' id="cke_{name}"' +
|
||||
' class="{id} cke cke_reset cke_chrome cke_editor_{name} cke_{langDir} ' + CKEDITOR.env.cssClass + '" ' +
|
||||
' dir="{langDir}"' +
|
||||
' lang="{langCode}"' +
|
||||
' role="application"' +
|
||||
' aria-labelledby="cke_{name}_arialbl">' +
|
||||
'<span id="cke_{name}_arialbl" class="cke_voice_label">{voiceLabel}</span>' +
|
||||
'<{outerEl} class="cke_inner cke_reset" role="presentation">' +
|
||||
'{topHtml}' +
|
||||
'<{outerEl} id="{contentId}" class="cke_contents cke_reset" role="presentation"></{outerEl}>' +
|
||||
'{bottomHtml}' +
|
||||
'</{outerEl}>' +
|
||||
'</{outerEl}>' );
|
||||
}
|
||||
|
||||
var container = CKEDITOR.dom.element.createFromHtml( themedTpl.output( {
|
||||
id: editor.id,
|
||||
name: name,
|
||||
langDir: editor.lang.dir,
|
||||
langCode: editor.langCode,
|
||||
voiceLabel: [ editor.lang.editor, editor.name ].join( ', ' ),
|
||||
topHtml: topHtml ? '<span id="' + editor.ui.spaceId( 'top' ) + '" class="cke_top cke_reset_all" role="presentation" style="height:auto">' + topHtml + '</span>' : '',
|
||||
contentId: editor.ui.spaceId( 'contents' ),
|
||||
bottomHtml: bottomHtml ? '<span id="' + editor.ui.spaceId( 'bottom' ) + '" class="cke_bottom cke_reset_all" role="presentation">' + bottomHtml + '</span>' : '',
|
||||
outerEl: CKEDITOR.env.ie ? 'span' : 'div' // #9571
|
||||
} ) );
|
||||
|
||||
if ( elementMode == CKEDITOR.ELEMENT_MODE_REPLACE ) {
|
||||
element.hide();
|
||||
container.insertAfter( element );
|
||||
} else
|
||||
element.append( container );
|
||||
|
||||
editor.container = container;
|
||||
|
||||
// Make top and bottom spaces unelectable, but not content space,
|
||||
// otherwise the editable area would be affected.
|
||||
topHtml && editor.ui.space( 'top' ).unselectable();
|
||||
bottomHtml && editor.ui.space( 'bottom' ).unselectable();
|
||||
|
||||
var width = editor.config.width, height = editor.config.height;
|
||||
if ( width )
|
||||
container.setStyle( 'width', CKEDITOR.tools.cssLength( width ) );
|
||||
|
||||
// The editor height is applied to the contents space.
|
||||
if ( height )
|
||||
editor.ui.space( 'contents' ).setStyle( 'height', CKEDITOR.tools.cssLength( height ) );
|
||||
|
||||
// Disable browser context menu for editor's chrome.
|
||||
container.disableContextMenu();
|
||||
|
||||
// Redirect the focus into editor for webkit. (#5713)
|
||||
CKEDITOR.env.webkit && container.on( 'focus', function() {
|
||||
editor.focus();
|
||||
} );
|
||||
|
||||
editor.fireOnce( 'uiReady' );
|
||||
}
|
||||
|
||||
// Replace all textareas with the default class name.
|
||||
CKEDITOR.domReady( function() {
|
||||
CKEDITOR.replaceClass && CKEDITOR.replaceAll( CKEDITOR.replaceClass );
|
||||
} );
|
||||
} )();
|
||||
|
||||
/**
|
||||
* The current editing mode. An editing mode basically provides
|
||||
* different ways of editing or viewing the contents.
|
||||
*
|
||||
* alert( CKEDITOR.instances.editor1.mode ); // (e.g.) 'wysiwyg'
|
||||
*
|
||||
* @readonly
|
||||
* @property {String} mode
|
||||
*/
|
||||
|
||||
/**
|
||||
* The mode to load at the editor startup. It depends on the plugins
|
||||
* loaded. By default, the `wysiwyg` and `source` modes are available.
|
||||
*
|
||||
* config.startupMode = 'source';
|
||||
*
|
||||
* @cfg {String} [startupMode='wysiwyg']
|
||||
* @member CKEDITOR.config
|
||||
*/
|
||||
CKEDITOR.config.startupMode = 'wysiwyg';
|
||||
|
||||
/**
|
||||
* Fired after the editor instance is resized through
|
||||
* the {@link CKEDITOR.editor#method-resize CKEDITOR.resize} method.
|
||||
*
|
||||
* @event resize
|
||||
* @param {CKEDITOR.editor} editor This editor instance.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fired before changing the editing mode. See also
|
||||
* {@link #beforeSetMode} and {@link #event-mode}.
|
||||
*
|
||||
* @event beforeModeUnload
|
||||
* @param {CKEDITOR.editor} editor This editor instance.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fired before the editor mode is set. See also
|
||||
* {@link #event-mode} and {@link #beforeModeUnload}.
|
||||
*
|
||||
* @since 3.5.3
|
||||
* @event beforeSetMode
|
||||
* @param {CKEDITOR.editor} editor This editor instance.
|
||||
* @param {String} data The name of the mode which is about to be set.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fired after setting the editing mode. See also {@link #beforeSetMode} and {@link #beforeModeUnload}
|
||||
*
|
||||
* @event mode
|
||||
* @param {CKEDITOR.editor} editor This editor instance.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fired when the editor (replacing a `<textarea>` which has a `required` attribute) is empty during form submission.
|
||||
*
|
||||
* This event replaces native required fields validation that the browsers cannot
|
||||
* perform when CKEditor replaces `<textarea>` elements.
|
||||
*
|
||||
* You can cancel this event to prevent the page from submitting data.
|
||||
*
|
||||
* editor.on( 'required', function( evt ) {
|
||||
* alert( 'Article content is required.' );
|
||||
* evt.cancel();
|
||||
* } );
|
||||
*
|
||||
* @event required
|
||||
* @param {CKEDITOR.editor} editor This editor instance.
|
||||
*/
|
||||
@@ -1,70 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileOverview Defines the "virtual" {@link CKEDITOR.dataProcessor} class, which
|
||||
* defines the basic structure of data processor objects to be
|
||||
* set to {@link CKEDITOR.editor.dataProcessor}.
|
||||
*/
|
||||
|
||||
/**
|
||||
* If defined, points to the data processor which is responsible to translate
|
||||
* and transform the editor data on input and output.
|
||||
* Generaly it will point to an instance of {@link CKEDITOR.htmlDataProcessor},
|
||||
* which handles HTML data. The editor may also handle other data formats by
|
||||
* using different data processors provided by specific plugins.
|
||||
*
|
||||
* @property {CKEDITOR.dataProcessor} dataProcessor
|
||||
* @member CKEDITOR.editor
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a data processor, which is responsible to translate and
|
||||
* transform the editor data on input and output.
|
||||
*
|
||||
* This class is here for documentation purposes only and is not really part of
|
||||
* the API. It serves as the base ("interface") for data processors implementation.
|
||||
*
|
||||
* @class CKEDITOR.dataProcessor
|
||||
* @abstract
|
||||
*/
|
||||
|
||||
/**
|
||||
* Transforms input data into HTML to be loaded in the editor.
|
||||
* While the editor is able to handle non HTML data (like BBCode), at runtime
|
||||
* it can handle HTML data only. The role of the data processor is transforming
|
||||
* the input data into HTML through this function.
|
||||
*
|
||||
* // Tranforming BBCode data, having a custom BBCode data processor.
|
||||
* var data = 'This is [b]an example[/b].';
|
||||
* var html = editor.dataProcessor.toHtml( data ); // '<p>This is <b>an example</b>.</p>'
|
||||
*
|
||||
* @method toHtml
|
||||
* @param {String} data The input data to be transformed.
|
||||
* @param {String} [fixForBody] The tag name to be used if the data must be
|
||||
* fixed because it is supposed to be loaded direcly into the `<body>`
|
||||
* tag. This is generally not used by non-HTML data processors.
|
||||
* @todo fixForBody type - compare to htmlDataProcessor.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Transforms HTML into data to be outputted by the editor, in the format
|
||||
* expected by the data processor.
|
||||
*
|
||||
* While the editor is able to handle non HTML data (like BBCode), at runtime
|
||||
* it can handle HTML data only. The role of the data processor is transforming
|
||||
* the HTML data containined by the editor into a specific data format through
|
||||
* this function.
|
||||
*
|
||||
* // Tranforming into BBCode data, having a custom BBCode data processor.
|
||||
* var html = '<p>This is <b>an example</b>.</p>';
|
||||
* var data = editor.dataProcessor.toDataFormat( html ); // 'This is [b]an example[/b].'
|
||||
*
|
||||
* @method toDataFormat
|
||||
* @param {String} html The HTML to be transformed.
|
||||
* @param {String} fixForBody The tag name to be used if the output data is
|
||||
* coming from `<body>` and may be eventually fixed for it. This is
|
||||
* generally not used by non-HTML data processors.
|
||||
*/
|
||||
@@ -1,13 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileOverview Defines the {@link CKEDITOR.dom} object, which contains DOM
|
||||
* manipulation objects and function.
|
||||
*/
|
||||
|
||||
CKEDITOR.dom = {};
|
||||
|
||||
// PACKAGER_RENAME( CKEDITOR.dom )
|
||||
@@ -1,53 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileOverview Defines the {@link CKEDITOR.dom.comment} class, which represents
|
||||
* a DOM comment node.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a DOM comment node.
|
||||
*
|
||||
* var nativeNode = document.createComment( 'Example' );
|
||||
* var comment = new CKEDITOR.dom.comment( nativeNode );
|
||||
*
|
||||
* var comment = new CKEDITOR.dom.comment( 'Example' );
|
||||
*
|
||||
* @class
|
||||
* @extends CKEDITOR.dom.node
|
||||
* @constructor Creates a comment class instance.
|
||||
* @param {Object/String} comment A native DOM comment node or a string containing
|
||||
* the text to use to create a new comment node.
|
||||
* @param {CKEDITOR.dom.document} [ownerDocument] The document that will contain
|
||||
* the node in case of new node creation. Defaults to the current document.
|
||||
*/
|
||||
CKEDITOR.dom.comment = function( comment, ownerDocument ) {
|
||||
if ( typeof comment == 'string' )
|
||||
comment = ( ownerDocument ? ownerDocument.$ : document ).createComment( comment );
|
||||
|
||||
CKEDITOR.dom.domObject.call( this, comment );
|
||||
};
|
||||
|
||||
CKEDITOR.dom.comment.prototype = new CKEDITOR.dom.node();
|
||||
|
||||
CKEDITOR.tools.extend( CKEDITOR.dom.comment.prototype, {
|
||||
/**
|
||||
* The node type. This is a constant value set to {@link CKEDITOR#NODE_COMMENT}.
|
||||
*
|
||||
* @readonly
|
||||
* @property {Number} [=CKEDITOR.NODE_COMMENT]
|
||||
*/
|
||||
type: CKEDITOR.NODE_COMMENT,
|
||||
|
||||
/**
|
||||
* Gets the outer HTML of this comment.
|
||||
*
|
||||
* @returns {String} The HTML `<!-- comment value -->`.
|
||||
*/
|
||||
getOuterHtml: function() {
|
||||
return '<!--' + this.$.nodeValue + '-->';
|
||||
}
|
||||
} );
|
||||
@@ -1,316 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileOverview Defines the {@link CKEDITOR.dom.document} class, which
|
||||
* represents a DOM document.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a DOM document.
|
||||
*
|
||||
* var document = new CKEDITOR.dom.document( document );
|
||||
*
|
||||
* @class
|
||||
* @extends CKEDITOR.dom.domObject
|
||||
* @constructor Creates a document class instance.
|
||||
* @param {Object} domDocument A native DOM document.
|
||||
*/
|
||||
CKEDITOR.dom.document = function( domDocument ) {
|
||||
CKEDITOR.dom.domObject.call( this, domDocument );
|
||||
};
|
||||
|
||||
// PACKAGER_RENAME( CKEDITOR.dom.document )
|
||||
|
||||
CKEDITOR.dom.document.prototype = new CKEDITOR.dom.domObject();
|
||||
|
||||
CKEDITOR.tools.extend( CKEDITOR.dom.document.prototype, {
|
||||
/**
|
||||
* The node type. This is a constant value set to {@link CKEDITOR#NODE_DOCUMENT}.
|
||||
*
|
||||
* @readonly
|
||||
* @property {Number} [=CKEDITOR.NODE_DOCUMENT]
|
||||
*/
|
||||
type: CKEDITOR.NODE_DOCUMENT,
|
||||
|
||||
/**
|
||||
* Appends a CSS file to the document.
|
||||
*
|
||||
* CKEDITOR.document.appendStyleSheet( '/mystyles.css' );
|
||||
*
|
||||
* @param {String} cssFileUrl The CSS file URL.
|
||||
*/
|
||||
appendStyleSheet: function( cssFileUrl ) {
|
||||
if ( this.$.createStyleSheet )
|
||||
this.$.createStyleSheet( cssFileUrl );
|
||||
else {
|
||||
var link = new CKEDITOR.dom.element( 'link' );
|
||||
link.setAttributes( {
|
||||
rel: 'stylesheet',
|
||||
type: 'text/css',
|
||||
href: cssFileUrl
|
||||
} );
|
||||
|
||||
this.getHead().append( link );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a CSS style sheet and inserts it into the document.
|
||||
*
|
||||
* @param cssStyleText {String} CSS style text.
|
||||
* @returns {Object} The created DOM native style sheet object.
|
||||
*/
|
||||
appendStyleText: function( cssStyleText ) {
|
||||
if ( this.$.createStyleSheet ) {
|
||||
var styleSheet = this.$.createStyleSheet( "" );
|
||||
styleSheet.cssText = cssStyleText;
|
||||
} else {
|
||||
var style = new CKEDITOR.dom.element( 'style', this );
|
||||
style.append( new CKEDITOR.dom.text( cssStyleText, this ) );
|
||||
this.getHead().append( style );
|
||||
}
|
||||
|
||||
return styleSheet || style.$.sheet;
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates {@link CKEDITOR.dom.element} instance in this document.
|
||||
*
|
||||
* @returns {CKEDITOR.dom.element}
|
||||
* @todo
|
||||
*/
|
||||
createElement: function( name, attribsAndStyles ) {
|
||||
var element = new CKEDITOR.dom.element( name, this );
|
||||
|
||||
if ( attribsAndStyles ) {
|
||||
if ( attribsAndStyles.attributes )
|
||||
element.setAttributes( attribsAndStyles.attributes );
|
||||
|
||||
if ( attribsAndStyles.styles )
|
||||
element.setStyles( attribsAndStyles.styles );
|
||||
}
|
||||
|
||||
return element;
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates {@link CKEDITOR.dom.text} instance in this document.
|
||||
*
|
||||
* @param {String} text Value of the text node.
|
||||
* @returns {CKEDITOR.dom.element}
|
||||
*/
|
||||
createText: function( text ) {
|
||||
return new CKEDITOR.dom.text( text, this );
|
||||
},
|
||||
|
||||
/**
|
||||
* Moves the selection focus to this document's window.
|
||||
*/
|
||||
focus: function() {
|
||||
this.getWindow().focus();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the element that is currently designated as the active element in the document.
|
||||
*
|
||||
* **Note:** Only one element can be active at a time in a document.
|
||||
* An active element does not necessarily have focus,
|
||||
* but an element with focus is always the active element in a document.
|
||||
*
|
||||
* @returns {CKEDITOR.dom.element}
|
||||
*/
|
||||
getActive: function() {
|
||||
return new CKEDITOR.dom.element( this.$.activeElement );
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets an element based on its id.
|
||||
*
|
||||
* var element = CKEDITOR.document.getById( 'myElement' );
|
||||
* alert( element.getId() ); // 'myElement'
|
||||
*
|
||||
* @param {String} elementId The element id.
|
||||
* @returns {CKEDITOR.dom.element} The element instance, or null if not found.
|
||||
*/
|
||||
getById: function( elementId ) {
|
||||
var $ = this.$.getElementById( elementId );
|
||||
return $ ? new CKEDITOR.dom.element( $ ) : null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets a node based on its address. See {@link CKEDITOR.dom.node#getAddress}.
|
||||
*
|
||||
* @param {Array} address
|
||||
* @param {Boolean} [normalized=false]
|
||||
*/
|
||||
getByAddress: function( address, normalized ) {
|
||||
var $ = this.$.documentElement;
|
||||
|
||||
for ( var i = 0; $ && i < address.length; i++ ) {
|
||||
var target = address[ i ];
|
||||
|
||||
if ( !normalized ) {
|
||||
$ = $.childNodes[ target ];
|
||||
continue;
|
||||
}
|
||||
|
||||
var currentIndex = -1;
|
||||
|
||||
for ( var j = 0; j < $.childNodes.length; j++ ) {
|
||||
var candidate = $.childNodes[ j ];
|
||||
|
||||
if ( normalized === true && candidate.nodeType == 3 && candidate.previousSibling && candidate.previousSibling.nodeType == 3 )
|
||||
continue;
|
||||
|
||||
currentIndex++;
|
||||
|
||||
if ( currentIndex == target ) {
|
||||
$ = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $ ? new CKEDITOR.dom.node( $ ) : null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets elements list based on given tag name.
|
||||
*
|
||||
* @param {String} tagName The element tag name.
|
||||
* @returns {CKEDITOR.dom.nodeList} The nodes list.
|
||||
*/
|
||||
getElementsByTag: function( tagName, namespace ) {
|
||||
if ( !( CKEDITOR.env.ie && !( document.documentMode > 8 ) ) && namespace )
|
||||
tagName = namespace + ':' + tagName;
|
||||
return new CKEDITOR.dom.nodeList( this.$.getElementsByTagName( tagName ) );
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the `<head>` element for this document.
|
||||
*
|
||||
* var element = CKEDITOR.document.getHead();
|
||||
* alert( element.getName() ); // 'head'
|
||||
*
|
||||
* @returns {CKEDITOR.dom.element} The `<head>` element.
|
||||
*/
|
||||
getHead: function() {
|
||||
var head = this.$.getElementsByTagName( 'head' )[ 0 ];
|
||||
if ( !head )
|
||||
head = this.getDocumentElement().append( new CKEDITOR.dom.element( 'head' ), true );
|
||||
else
|
||||
head = new CKEDITOR.dom.element( head );
|
||||
|
||||
return head;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the `<body>` element for this document.
|
||||
*
|
||||
* var element = CKEDITOR.document.getBody();
|
||||
* alert( element.getName() ); // 'body'
|
||||
*
|
||||
* @returns {CKEDITOR.dom.element} The `<body>` element.
|
||||
*/
|
||||
getBody: function() {
|
||||
return new CKEDITOR.dom.element( this.$.body );
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the DOM document element for this document.
|
||||
*
|
||||
* @returns {CKEDITOR.dom.element} The DOM document element.
|
||||
*/
|
||||
getDocumentElement: function() {
|
||||
return new CKEDITOR.dom.element( this.$.documentElement );
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the window object that holds this document.
|
||||
*
|
||||
* @returns {CKEDITOR.dom.window} The window object.
|
||||
*/
|
||||
getWindow: function() {
|
||||
return new CKEDITOR.dom.window( this.$.parentWindow || this.$.defaultView );
|
||||
},
|
||||
|
||||
/**
|
||||
* Defines the document contents through document.write. Note that the
|
||||
* previous document contents will be lost (cleaned).
|
||||
*
|
||||
* document.write(
|
||||
* '<html>' +
|
||||
* '<head><title>Sample Doc</title></head>' +
|
||||
* '<body>Document contents created by code</body>' +
|
||||
* '</html>'
|
||||
* );
|
||||
*
|
||||
* @since 3.5
|
||||
* @param {String} html The HTML defining the document contents.
|
||||
*/
|
||||
write: function( html ) {
|
||||
// Don't leave any history log in IE. (#5657)
|
||||
this.$.open( 'text/html', 'replace' );
|
||||
|
||||
// Support for custom document.domain in IE.
|
||||
//
|
||||
// The script must be appended because if placed before the
|
||||
// doctype, IE will go into quirks mode and mess with
|
||||
// the editable, e.g. by changing its default height.
|
||||
if ( CKEDITOR.env.ie )
|
||||
html = html.replace( /(?:^\s*<!DOCTYPE[^>]*?>)|^/i, '$&\n<script data-cke-temp="1">(' + CKEDITOR.tools.fixDomain + ')();</script>' );
|
||||
|
||||
this.$.write( html );
|
||||
this.$.close();
|
||||
},
|
||||
|
||||
/**
|
||||
* Wrapper for `querySelectorAll`. Returns a list of elements within this document that match
|
||||
* specified `selector`.
|
||||
*
|
||||
* **Note:** returned list is not a live collection (like a result of native `querySelectorAll`).
|
||||
*
|
||||
* @since 4.3
|
||||
* @param {String} selector
|
||||
* @returns {CKEDITOR.dom.nodeList}
|
||||
*/
|
||||
find: function( selector ) {
|
||||
return new CKEDITOR.dom.nodeList( this.$.querySelectorAll( selector ) );
|
||||
},
|
||||
|
||||
/**
|
||||
* Wrapper for `querySelector`. Returns first element within this document that matches
|
||||
* specified `selector`.
|
||||
*
|
||||
* @since 4.3
|
||||
* @param {String} selector
|
||||
* @returns {CKEDITOR.dom.element}
|
||||
*/
|
||||
findOne: function( selector ) {
|
||||
var el = this.$.querySelector( selector );
|
||||
|
||||
return el ? new CKEDITOR.dom.element( el ) : null;
|
||||
},
|
||||
|
||||
/**
|
||||
* IE8 only method. It returns document fragment which has all HTML5 elements enabled.
|
||||
*
|
||||
* @since 4.3
|
||||
* @private
|
||||
* @returns DocumentFragment
|
||||
*/
|
||||
_getHtml5ShivFrag: function() {
|
||||
var $frag = this.getCustomData( 'html5ShivFrag' );
|
||||
|
||||
if ( !$frag ) {
|
||||
$frag = this.$.createDocumentFragment();
|
||||
CKEDITOR.tools.enableHtml5Elements( $frag, true );
|
||||
this.setCustomData( 'html5ShivFrag', $frag );
|
||||
}
|
||||
|
||||
return $frag;
|
||||
}
|
||||
} );
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* DocumentFragment is a "lightweight" or "minimal" Document object. It is
|
||||
* commonly used to extract a portion of a document's tree or to create a new
|
||||
* fragment of a document. Various operations may take DocumentFragment objects
|
||||
* as arguments and results in all the child nodes of the DocumentFragment being
|
||||
* moved to the child list of this node.
|
||||
*
|
||||
* @class
|
||||
* @constructor Creates a document fragment class instance.
|
||||
* @param {Object} nodeOrDoc
|
||||
* @todo example and param doc
|
||||
*/
|
||||
CKEDITOR.dom.documentFragment = function( nodeOrDoc ) {
|
||||
nodeOrDoc = nodeOrDoc || CKEDITOR.document;
|
||||
|
||||
if ( nodeOrDoc.type == CKEDITOR.NODE_DOCUMENT )
|
||||
this.$ = nodeOrDoc.$.createDocumentFragment();
|
||||
else
|
||||
this.$ = nodeOrDoc;
|
||||
};
|
||||
|
||||
CKEDITOR.tools.extend( CKEDITOR.dom.documentFragment.prototype, CKEDITOR.dom.element.prototype, {
|
||||
/**
|
||||
* The node type. This is a constant value set to {@link CKEDITOR#NODE_DOCUMENT_FRAGMENT}.
|
||||
*
|
||||
* @readonly
|
||||
* @property {Number} [=CKEDITOR.NODE_DOCUMENT_FRAGMENT]
|
||||
*/
|
||||
type: CKEDITOR.NODE_DOCUMENT_FRAGMENT,
|
||||
|
||||
/**
|
||||
* Inserts document fragment's contents after specified node.
|
||||
*
|
||||
* @param {CKEDITOR.dom.node} node
|
||||
*/
|
||||
insertAfterNode: function( node ) {
|
||||
node = node.$;
|
||||
node.parentNode.insertBefore( this.$, node.nextSibling );
|
||||
}
|
||||
}, true, { 'append': 1, 'appendBogus': 1, 'getFirst': 1, 'getLast': 1, 'getParent': 1, 'getNext': 1, 'getPrevious': 1, 'appendTo': 1, 'moveChildren': 1, 'insertBefore': 1, 'insertAfterNode': 1, 'replace': 1, 'trim': 1, 'type': 1, 'ltrim': 1, 'rtrim': 1, 'getDocument': 1, 'getChildCount': 1, 'getChild': 1, 'getChildren': 1 } );
|
||||
@@ -1,262 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileOverview Defines the {@link CKEDITOR.editor} class, which is the base
|
||||
* for other classes representing DOM objects.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a DOM object. This class is not intended to be used directly. It
|
||||
* serves as the base class for other classes representing specific DOM
|
||||
* objects.
|
||||
*
|
||||
* @class
|
||||
* @mixins CKEDITOR.event
|
||||
* @constructor Creates a domObject class instance.
|
||||
* @param {Object} nativeDomObject A native DOM object.
|
||||
*/
|
||||
CKEDITOR.dom.domObject = function( nativeDomObject ) {
|
||||
if ( nativeDomObject ) {
|
||||
/**
|
||||
* The native DOM object represented by this class instance.
|
||||
*
|
||||
* var element = new CKEDITOR.dom.element( 'span' );
|
||||
* alert( element.$.nodeType ); // '1'
|
||||
*
|
||||
* @readonly
|
||||
* @property {Object}
|
||||
*/
|
||||
this.$ = nativeDomObject;
|
||||
}
|
||||
};
|
||||
|
||||
CKEDITOR.dom.domObject.prototype = ( function() {
|
||||
// Do not define other local variables here. We want to keep the native
|
||||
// listener closures as clean as possible.
|
||||
|
||||
var getNativeListener = function( domObject, eventName ) {
|
||||
return function( domEvent ) {
|
||||
// In FF, when reloading the page with the editor focused, it may
|
||||
// throw an error because the CKEDITOR global is not anymore
|
||||
// available. So, we check it here first. (#2923)
|
||||
if ( typeof CKEDITOR != 'undefined' )
|
||||
domObject.fire( eventName, new CKEDITOR.dom.event( domEvent ) );
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
/**
|
||||
* Get the private `_` object which is bound to the native
|
||||
* DOM object using {@link #getCustomData}.
|
||||
*
|
||||
* var elementA = new CKEDITOR.dom.element( nativeElement );
|
||||
* elementA.getPrivate().value = 1;
|
||||
* ...
|
||||
* var elementB = new CKEDITOR.dom.element( nativeElement );
|
||||
* elementB.getPrivate().value; // 1
|
||||
*
|
||||
* @returns {Object} The private object.
|
||||
*/
|
||||
getPrivate: function() {
|
||||
var priv;
|
||||
|
||||
// Get the main private object from the custom data. Create it if not defined.
|
||||
if ( !( priv = this.getCustomData( '_' ) ) )
|
||||
this.setCustomData( '_', ( priv = {} ) );
|
||||
|
||||
return priv;
|
||||
},
|
||||
|
||||
// Docs inherited from event.
|
||||
on: function( eventName ) {
|
||||
// We customize the "on" function here. The basic idea is that we'll have
|
||||
// only one listener for a native event, which will then call all listeners
|
||||
// set to the event.
|
||||
|
||||
// Get the listeners holder object.
|
||||
var nativeListeners = this.getCustomData( '_cke_nativeListeners' );
|
||||
|
||||
if ( !nativeListeners ) {
|
||||
nativeListeners = {};
|
||||
this.setCustomData( '_cke_nativeListeners', nativeListeners );
|
||||
}
|
||||
|
||||
// Check if we have a listener for that event.
|
||||
if ( !nativeListeners[ eventName ] ) {
|
||||
var listener = nativeListeners[ eventName ] = getNativeListener( this, eventName );
|
||||
|
||||
if ( this.$.addEventListener )
|
||||
this.$.addEventListener( eventName, listener, !!CKEDITOR.event.useCapture );
|
||||
else if ( this.$.attachEvent )
|
||||
this.$.attachEvent( 'on' + eventName, listener );
|
||||
}
|
||||
|
||||
// Call the original implementation.
|
||||
return CKEDITOR.event.prototype.on.apply( this, arguments );
|
||||
},
|
||||
|
||||
// Docs inherited from event.
|
||||
removeListener: function( eventName ) {
|
||||
// Call the original implementation.
|
||||
CKEDITOR.event.prototype.removeListener.apply( this, arguments );
|
||||
|
||||
// If we don't have listeners for this event, clean the DOM up.
|
||||
if ( !this.hasListeners( eventName ) ) {
|
||||
var nativeListeners = this.getCustomData( '_cke_nativeListeners' );
|
||||
var listener = nativeListeners && nativeListeners[ eventName ];
|
||||
if ( listener ) {
|
||||
if ( this.$.removeEventListener )
|
||||
this.$.removeEventListener( eventName, listener, false );
|
||||
else if ( this.$.detachEvent )
|
||||
this.$.detachEvent( 'on' + eventName, listener );
|
||||
|
||||
delete nativeListeners[ eventName ];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes any listener set on this object.
|
||||
*
|
||||
* To avoid memory leaks we must assure that there are no
|
||||
* references left after the object is no longer needed.
|
||||
*/
|
||||
removeAllListeners: function() {
|
||||
var nativeListeners = this.getCustomData( '_cke_nativeListeners' );
|
||||
for ( var eventName in nativeListeners ) {
|
||||
var listener = nativeListeners[ eventName ];
|
||||
if ( this.$.detachEvent )
|
||||
this.$.detachEvent( 'on' + eventName, listener );
|
||||
else if ( this.$.removeEventListener )
|
||||
this.$.removeEventListener( eventName, listener, false );
|
||||
|
||||
delete nativeListeners[ eventName ];
|
||||
}
|
||||
|
||||
// Remove events from events object so fire() method will not call
|
||||
// listeners (#11400).
|
||||
CKEDITOR.event.prototype.removeAllListeners.call( this );
|
||||
}
|
||||
};
|
||||
} )();
|
||||
|
||||
( function( domObjectProto ) {
|
||||
var customData = {};
|
||||
|
||||
CKEDITOR.on( 'reset', function() {
|
||||
customData = {};
|
||||
} );
|
||||
|
||||
/**
|
||||
* Determines whether the specified object is equal to the current object.
|
||||
*
|
||||
* var doc = new CKEDITOR.dom.document( document );
|
||||
* alert( doc.equals( CKEDITOR.document ) ); // true
|
||||
* alert( doc == CKEDITOR.document ); // false
|
||||
*
|
||||
* @param {Object} object The object to compare with the current object.
|
||||
* @returns {Boolean} `true` if the object is equal.
|
||||
*/
|
||||
domObjectProto.equals = function( object ) {
|
||||
// Try/Catch to avoid IE permission error when object is from different document.
|
||||
try {
|
||||
return ( object && object.$ === this.$ );
|
||||
} catch ( er ) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets a data slot value for this object. These values are shared by all
|
||||
* instances pointing to that same DOM object.
|
||||
*
|
||||
* **Note:** The created data slot is only guarantied to be available on this unique dom node,
|
||||
* thus any wish to continue access it from other element clones (either created by
|
||||
* clone node or from `innerHtml`) will fail, for such usage, please use
|
||||
* {@link CKEDITOR.dom.element#setAttribute} instead.
|
||||
*
|
||||
* var element = new CKEDITOR.dom.element( 'span' );
|
||||
* element.setCustomData( 'hasCustomData', true );
|
||||
*
|
||||
* @param {String} key A key used to identify the data slot.
|
||||
* @param {Object} value The value to set to the data slot.
|
||||
* @returns {CKEDITOR.dom.domObject} This DOM object instance.
|
||||
* @chainable
|
||||
*/
|
||||
domObjectProto.setCustomData = function( key, value ) {
|
||||
var expandoNumber = this.getUniqueId(),
|
||||
dataSlot = customData[ expandoNumber ] || ( customData[ expandoNumber ] = {} );
|
||||
|
||||
dataSlot[ key ] = value;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the value set to a data slot in this object.
|
||||
*
|
||||
* var element = new CKEDITOR.dom.element( 'span' );
|
||||
* alert( element.getCustomData( 'hasCustomData' ) ); // e.g. 'true'
|
||||
* alert( element.getCustomData( 'nonExistingKey' ) ); // null
|
||||
*
|
||||
* @param {String} key The key used to identify the data slot.
|
||||
* @returns {Object} This value set to the data slot.
|
||||
*/
|
||||
domObjectProto.getCustomData = function( key ) {
|
||||
var expandoNumber = this.$[ 'data-cke-expando' ],
|
||||
dataSlot = expandoNumber && customData[ expandoNumber ];
|
||||
|
||||
return ( dataSlot && key in dataSlot ) ? dataSlot[ key ] : null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the value in data slot under given `key`.
|
||||
*
|
||||
* @param {String} key
|
||||
* @returns {Object} Removed value or `null` if not found.
|
||||
*/
|
||||
domObjectProto.removeCustomData = function( key ) {
|
||||
var expandoNumber = this.$[ 'data-cke-expando' ],
|
||||
dataSlot = expandoNumber && customData[ expandoNumber ],
|
||||
retval, hadKey;
|
||||
|
||||
if ( dataSlot ) {
|
||||
retval = dataSlot[ key ];
|
||||
hadKey = key in dataSlot;
|
||||
delete dataSlot[ key ];
|
||||
}
|
||||
|
||||
return hadKey ? retval : null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes any data stored on this object.
|
||||
* To avoid memory leaks we must assure that there are no
|
||||
* references left after the object is no longer needed.
|
||||
*/
|
||||
domObjectProto.clearCustomData = function() {
|
||||
// Clear all event listeners
|
||||
this.removeAllListeners();
|
||||
|
||||
var expandoNumber = this.$[ 'data-cke-expando' ];
|
||||
expandoNumber && delete customData[ expandoNumber ];
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets an ID that can be used to identiquely identify this DOM object in
|
||||
* the running session.
|
||||
*
|
||||
* @returns {Number} A unique ID.
|
||||
*/
|
||||
domObjectProto.getUniqueId = function() {
|
||||
return this.$[ 'data-cke-expando' ] || ( this.$[ 'data-cke-expando' ] = CKEDITOR.tools.getNextNumber() );
|
||||
};
|
||||
|
||||
// Implement CKEDITOR.event.
|
||||
CKEDITOR.event.implementOn( domObjectProto );
|
||||
|
||||
} )( CKEDITOR.dom.domObject.prototype );
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,251 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
( function() {
|
||||
|
||||
var pathBlockLimitElements = {},
|
||||
pathBlockElements = {},
|
||||
tag;
|
||||
|
||||
// Elements that are considered the "Block limit" in an element path.
|
||||
for ( tag in CKEDITOR.dtd.$blockLimit ) {
|
||||
// Exclude from list roots.
|
||||
if ( !( tag in CKEDITOR.dtd.$list ) )
|
||||
pathBlockLimitElements[ tag ] = 1;
|
||||
}
|
||||
|
||||
// Elements that are considered the "End level Block" in an element path.
|
||||
for ( tag in CKEDITOR.dtd.$block ) {
|
||||
// Exclude block limits, and empty block element, e.g. hr.
|
||||
if ( !( tag in CKEDITOR.dtd.$blockLimit || tag in CKEDITOR.dtd.$empty ) )
|
||||
pathBlockElements[ tag ] = 1;
|
||||
}
|
||||
|
||||
// Check if an element contains any block element.
|
||||
function checkHasBlock( element ) {
|
||||
var childNodes = element.getChildren();
|
||||
|
||||
for ( var i = 0, count = childNodes.count(); i < count; i++ ) {
|
||||
var child = childNodes.getItem( i );
|
||||
|
||||
if ( child.type == CKEDITOR.NODE_ELEMENT && CKEDITOR.dtd.$block[ child.getName() ] )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the list of nodes walked from the start node up to the editable element of the editor.
|
||||
*
|
||||
* @class
|
||||
* @constructor Creates an element path class instance.
|
||||
* @param {CKEDITOR.dom.element} startNode From which the path should start.
|
||||
* @param {CKEDITOR.dom.element} root To which element the path should stop, defaults to the `body` element.
|
||||
*/
|
||||
CKEDITOR.dom.elementPath = function( startNode, root ) {
|
||||
var block = null,
|
||||
blockLimit = null,
|
||||
elements = [],
|
||||
e = startNode,
|
||||
elementName;
|
||||
|
||||
// Backward compact.
|
||||
root = root || startNode.getDocument().getBody();
|
||||
|
||||
do {
|
||||
if ( e.type == CKEDITOR.NODE_ELEMENT ) {
|
||||
elements.push( e );
|
||||
|
||||
if ( !this.lastElement ) {
|
||||
this.lastElement = e;
|
||||
|
||||
// If an object or non-editable element is fully selected at the end of the element path,
|
||||
// it must not become the block limit.
|
||||
if ( e.is( CKEDITOR.dtd.$object ) || e.getAttribute( 'contenteditable' ) == 'false' )
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( e.equals( root ) )
|
||||
break;
|
||||
|
||||
if ( !blockLimit ) {
|
||||
elementName = e.getName();
|
||||
|
||||
// First editable element becomes a block limit, because it cannot be split.
|
||||
if ( e.getAttribute( 'contenteditable' ) == 'true' )
|
||||
blockLimit = e;
|
||||
// "Else" because element cannot be both - block and block levelimit.
|
||||
else if ( !block && pathBlockElements[ elementName ] )
|
||||
block = e;
|
||||
|
||||
if ( pathBlockLimitElements[ elementName ] ) {
|
||||
// End level DIV is considered as the block, if no block is available. (#525)
|
||||
// But it must NOT be the root element (checked above).
|
||||
if ( !block && elementName == 'div' && !checkHasBlock( e ) )
|
||||
block = e;
|
||||
else
|
||||
blockLimit = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while ( ( e = e.getParent() ) );
|
||||
|
||||
// Block limit defaults to root.
|
||||
if ( !blockLimit )
|
||||
blockLimit = root;
|
||||
|
||||
/**
|
||||
* First non-empty block element which:
|
||||
*
|
||||
* * is not a {@link CKEDITOR.dtd#$blockLimit},
|
||||
* * or is a `div` which does not contain block elements and is not a `root`.
|
||||
*
|
||||
* This means a first, splittable block in elements path.
|
||||
*
|
||||
* @readonly
|
||||
* @property {CKEDITOR.dom.element}
|
||||
*/
|
||||
this.block = block;
|
||||
|
||||
/**
|
||||
* See the {@link CKEDITOR.dtd#$blockLimit} description.
|
||||
*
|
||||
* @readonly
|
||||
* @property {CKEDITOR.dom.element}
|
||||
*/
|
||||
this.blockLimit = blockLimit;
|
||||
|
||||
/**
|
||||
* The root of the elements path - `root` argument passed to class constructor or a `body` element.
|
||||
*
|
||||
* @readonly
|
||||
* @property {CKEDITOR.dom.element}
|
||||
*/
|
||||
this.root = root;
|
||||
|
||||
/**
|
||||
* An array of elements (from `startNode` to `root`) in the path.
|
||||
*
|
||||
* @readonly
|
||||
* @property {CKEDITOR.dom.element[]}
|
||||
*/
|
||||
this.elements = elements;
|
||||
|
||||
/**
|
||||
* The last element of the elements path - `startNode` or its parent.
|
||||
*
|
||||
* @readonly
|
||||
* @property {CKEDITOR.dom.element} lastElement
|
||||
*/
|
||||
};
|
||||
|
||||
} )();
|
||||
|
||||
CKEDITOR.dom.elementPath.prototype = {
|
||||
/**
|
||||
* Compares this element path with another one.
|
||||
*
|
||||
* @param {CKEDITOR.dom.elementPath} otherPath The elementPath object to be
|
||||
* compared with this one.
|
||||
* @returns {Boolean} `true` if the paths are equal, containing the same
|
||||
* number of elements and the same elements in the same order.
|
||||
*/
|
||||
compare: function( otherPath ) {
|
||||
var thisElements = this.elements;
|
||||
var otherElements = otherPath && otherPath.elements;
|
||||
|
||||
if ( !otherElements || thisElements.length != otherElements.length )
|
||||
return false;
|
||||
|
||||
for ( var i = 0; i < thisElements.length; i++ ) {
|
||||
if ( !thisElements[ i ].equals( otherElements[ i ] ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Search the path elements that meets the specified criteria.
|
||||
*
|
||||
* @param {String/Array/Function/Object/CKEDITOR.dom.element} query The criteria that can be
|
||||
* either a tag name, list (array and object) of tag names, element or an node evaluator function.
|
||||
* @param {Boolean} [excludeRoot] Not taking path root element into consideration.
|
||||
* @param {Boolean} [fromTop] Search start from the topmost element instead of bottom.
|
||||
* @returns {CKEDITOR.dom.element} The first matched dom element or `null`.
|
||||
*/
|
||||
contains: function( query, excludeRoot, fromTop ) {
|
||||
var evaluator;
|
||||
if ( typeof query == 'string' )
|
||||
evaluator = function( node ) {
|
||||
return node.getName() == query;
|
||||
};
|
||||
if ( query instanceof CKEDITOR.dom.element )
|
||||
evaluator = function( node ) {
|
||||
return node.equals( query );
|
||||
};
|
||||
else if ( CKEDITOR.tools.isArray( query ) )
|
||||
evaluator = function( node ) {
|
||||
return CKEDITOR.tools.indexOf( query, node.getName() ) > -1;
|
||||
};
|
||||
else if ( typeof query == 'function' )
|
||||
evaluator = query;
|
||||
else if ( typeof query == 'object' )
|
||||
evaluator = function( node ) {
|
||||
return node.getName() in query;
|
||||
};
|
||||
|
||||
var elements = this.elements,
|
||||
length = elements.length;
|
||||
excludeRoot && length--;
|
||||
|
||||
if ( fromTop ) {
|
||||
elements = Array.prototype.slice.call( elements, 0 );
|
||||
elements.reverse();
|
||||
}
|
||||
|
||||
for ( var i = 0; i < length; i++ ) {
|
||||
if ( evaluator( elements[ i ] ) )
|
||||
return elements[ i ];
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Check whether the elements path is the proper context for the specified
|
||||
* tag name in the DTD.
|
||||
*
|
||||
* @param {String} tag The tag name.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
isContextFor: function( tag ) {
|
||||
var holder;
|
||||
|
||||
// Check for block context.
|
||||
if ( tag in CKEDITOR.dtd.$block ) {
|
||||
// Indeterminate elements which are not subjected to be splitted or surrounded must be checked first.
|
||||
var inter = this.contains( CKEDITOR.dtd.$intermediate );
|
||||
holder = inter || ( this.root.equals( this.block ) && this.block ) || this.blockLimit;
|
||||
return !!holder.getDtd()[ tag ];
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve the text direction for this elements path.
|
||||
*
|
||||
* @returns {'ltr'/'rtl'}
|
||||
*/
|
||||
direction: function() {
|
||||
var directionNode = this.block || this.blockLimit || this.root;
|
||||
return directionNode.getDirection( 1 );
|
||||
}
|
||||
};
|
||||
@@ -1,208 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileOverview Defines the {@link CKEDITOR.dom.event} class, which
|
||||
* represents the a native DOM event object.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a native DOM event object.
|
||||
*
|
||||
* @class
|
||||
* @constructor Creates an event class instance.
|
||||
* @param {Object} domEvent A native DOM event object.
|
||||
*/
|
||||
CKEDITOR.dom.event = function( domEvent ) {
|
||||
/**
|
||||
* The native DOM event object represented by this class instance.
|
||||
*
|
||||
* @readonly
|
||||
*/
|
||||
this.$ = domEvent;
|
||||
};
|
||||
|
||||
CKEDITOR.dom.event.prototype = {
|
||||
/**
|
||||
* Gets the key code associated to the event.
|
||||
*
|
||||
* alert( event.getKey() ); // '65' is 'a' has been pressed
|
||||
*
|
||||
* @returns {Number} The key code.
|
||||
*/
|
||||
getKey: function() {
|
||||
return this.$.keyCode || this.$.which;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets a number represeting the combination of the keys pressed during the
|
||||
* event. It is the sum with the current key code and the {@link CKEDITOR#CTRL},
|
||||
* {@link CKEDITOR#SHIFT} and {@link CKEDITOR#ALT} constants.
|
||||
*
|
||||
* alert( event.getKeystroke() == 65 ); // 'a' key
|
||||
* alert( event.getKeystroke() == CKEDITOR.CTRL + 65 ); // CTRL + 'a' key
|
||||
* alert( event.getKeystroke() == CKEDITOR.CTRL + CKEDITOR.SHIFT + 65 ); // CTRL + SHIFT + 'a' key
|
||||
*
|
||||
* @returns {Number} The number representing the keys combination.
|
||||
*/
|
||||
getKeystroke: function() {
|
||||
var keystroke = this.getKey();
|
||||
|
||||
if ( this.$.ctrlKey || this.$.metaKey )
|
||||
keystroke += CKEDITOR.CTRL;
|
||||
|
||||
if ( this.$.shiftKey )
|
||||
keystroke += CKEDITOR.SHIFT;
|
||||
|
||||
if ( this.$.altKey )
|
||||
keystroke += CKEDITOR.ALT;
|
||||
|
||||
return keystroke;
|
||||
},
|
||||
|
||||
/**
|
||||
* Prevents the original behavior of the event to happen. It can optionally
|
||||
* stop propagating the event in the event chain.
|
||||
*
|
||||
* var element = CKEDITOR.document.getById( 'myElement' );
|
||||
* element.on( 'click', function( ev ) {
|
||||
* // The DOM event object is passed by the 'data' property.
|
||||
* var domEvent = ev.data;
|
||||
* // Prevent the click to chave any effect in the element.
|
||||
* domEvent.preventDefault();
|
||||
* } );
|
||||
*
|
||||
* @param {Boolean} [stopPropagation=false] Stop propagating this event in the
|
||||
* event chain.
|
||||
*/
|
||||
preventDefault: function( stopPropagation ) {
|
||||
var $ = this.$;
|
||||
if ( $.preventDefault )
|
||||
$.preventDefault();
|
||||
else
|
||||
$.returnValue = false;
|
||||
|
||||
if ( stopPropagation )
|
||||
this.stopPropagation();
|
||||
},
|
||||
|
||||
/**
|
||||
* Stops this event propagation in the event chain.
|
||||
*/
|
||||
stopPropagation: function() {
|
||||
var $ = this.$;
|
||||
if ( $.stopPropagation )
|
||||
$.stopPropagation();
|
||||
else
|
||||
$.cancelBubble = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the DOM node where the event was targeted to.
|
||||
*
|
||||
* var element = CKEDITOR.document.getById( 'myElement' );
|
||||
* element.on( 'click', function( ev ) {
|
||||
* // The DOM event object is passed by the 'data' property.
|
||||
* var domEvent = ev.data;
|
||||
* // Add a CSS class to the event target.
|
||||
* domEvent.getTarget().addClass( 'clicked' );
|
||||
* } );
|
||||
*
|
||||
* @returns {CKEDITOR.dom.node} The target DOM node.
|
||||
*/
|
||||
getTarget: function() {
|
||||
var rawNode = this.$.target || this.$.srcElement;
|
||||
return rawNode ? new CKEDITOR.dom.node( rawNode ) : null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns an integer value that indicates the current processing phase of an event.
|
||||
* For browsers that doesn't support event phase, {@link CKEDITOR#EVENT_PHASE_AT_TARGET} is always returned.
|
||||
*
|
||||
* @returns {Number} One of {@link CKEDITOR#EVENT_PHASE_CAPTURING},
|
||||
* {@link CKEDITOR#EVENT_PHASE_AT_TARGET}, or {@link CKEDITOR#EVENT_PHASE_BUBBLING}.
|
||||
*/
|
||||
getPhase: function() {
|
||||
return this.$.eventPhase || 2;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieves the coordinates of the mouse pointer relative to the top-left
|
||||
* corner of the document, in mouse related event.
|
||||
*
|
||||
* element.on( 'mousemouse', function( ev ) {
|
||||
* var pageOffset = ev.data.getPageOffset();
|
||||
* alert( pageOffset.x ); // page offset X
|
||||
* alert( pageOffset.y ); // page offset Y
|
||||
* } );
|
||||
*
|
||||
* @returns {Object} The object contains the position.
|
||||
* @returns {Number} return.x
|
||||
* @returns {Number} return.y
|
||||
*/
|
||||
getPageOffset : function() {
|
||||
var doc = this.getTarget().getDocument().$;
|
||||
var pageX = this.$.pageX || this.$.clientX + ( doc.documentElement.scrollLeft || doc.body.scrollLeft );
|
||||
var pageY = this.$.pageY || this.$.clientY + ( doc.documentElement.scrollTop || doc.body.scrollTop );
|
||||
return { x : pageX, y : pageY };
|
||||
}
|
||||
};
|
||||
|
||||
// For the followind constants, we need to go over the Unicode boundaries
|
||||
// (0x10FFFF) to avoid collision.
|
||||
|
||||
/**
|
||||
* CTRL key (0x110000).
|
||||
*
|
||||
* @readonly
|
||||
* @property {Number} [=0x110000]
|
||||
* @member CKEDITOR
|
||||
*/
|
||||
CKEDITOR.CTRL = 0x110000;
|
||||
|
||||
/**
|
||||
* SHIFT key (0x220000).
|
||||
*
|
||||
* @readonly
|
||||
* @property {Number} [=0x220000]
|
||||
* @member CKEDITOR
|
||||
*/
|
||||
CKEDITOR.SHIFT = 0x220000;
|
||||
|
||||
/**
|
||||
* ALT key (0x440000).
|
||||
*
|
||||
* @readonly
|
||||
* @property {Number} [=0x440000]
|
||||
* @member CKEDITOR
|
||||
*/
|
||||
CKEDITOR.ALT = 0x440000;
|
||||
|
||||
/**
|
||||
* Capturing phase.
|
||||
*
|
||||
* @readonly
|
||||
* @property {Number} [=1]
|
||||
* @member CKEDITOR
|
||||
*/
|
||||
CKEDITOR.EVENT_PHASE_CAPTURING = 1;
|
||||
|
||||
/**
|
||||
* Event at target.
|
||||
*
|
||||
* @readonly
|
||||
* @property {Number} [=2]
|
||||
* @member CKEDITOR
|
||||
*/
|
||||
CKEDITOR.EVENT_PHASE_AT_TARGET = 2;
|
||||
|
||||
/**
|
||||
* Bubbling phase.
|
||||
*
|
||||
* @readonly
|
||||
* @property {Number} [=3]
|
||||
* @member CKEDITOR
|
||||
*/
|
||||
CKEDITOR.EVENT_PHASE_BUBBLING = 3;
|
||||
@@ -1,500 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
* File overview: DOM iterator, which iterates over list items, lines and paragraphs.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
( function() {
|
||||
/**
|
||||
* Represents iterator class. It can be used to iterate
|
||||
* over all elements (or even text nodes in case of {@link #enlargeBr} set to `false`)
|
||||
* which establish "paragraph-like" spaces within passed range.
|
||||
*
|
||||
* // <h1>[foo</h1><p>bar]</p>
|
||||
* var iterator = range.createIterator();
|
||||
* iterator.getNextParagraph(); // h1 element
|
||||
* iterator.getNextParagraph(); // p element
|
||||
*
|
||||
* // <ul><li>[foo</li><li>bar]</li>
|
||||
* // With enforceRealBlocks set to false iterator will return two list item elements.
|
||||
* // With enforceRealBlocks set to true iterator will return two paragraphs and the DOM will be changed to:
|
||||
* // <ul><li><p>foo</p></li><li><p>bar</p></li>
|
||||
*
|
||||
* @class CKEDITOR.dom.iterator
|
||||
* @constructor Creates an iterator class instance.
|
||||
* @param {CKEDITOR.dom.range} range
|
||||
*/
|
||||
function iterator( range ) {
|
||||
if ( arguments.length < 1 )
|
||||
return;
|
||||
|
||||
this.range = range;
|
||||
this.forceBrBreak = 0;
|
||||
|
||||
// (#3730).
|
||||
/**
|
||||
* Whether include `<br>`s into the enlarged range. Should be
|
||||
* set to `false` when using iterator in {@link CKEDITOR#ENTER_BR} mode.
|
||||
*
|
||||
* @property {Boolean} [enlargeBr=true]
|
||||
*/
|
||||
this.enlargeBr = 1;
|
||||
|
||||
/**
|
||||
* Whether iterator should create transformable block
|
||||
* if the current one contains text and it cannot be transformed.
|
||||
* For example new blocks will be established in elements like
|
||||
* `<li>` or `<td>`.
|
||||
*
|
||||
* @property {Boolean} [enforceRealBlocks=false]
|
||||
*/
|
||||
this.enforceRealBlocks = 0;
|
||||
|
||||
this._ || ( this._ = {} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Default iterator's filter. It is set only for nested iterators.
|
||||
*
|
||||
* @since 4.3
|
||||
* @readonly
|
||||
* @property {CKEDITOR.filter} filter
|
||||
*/
|
||||
|
||||
/**
|
||||
* Iterator's active filter. It is set by the {@link #getNextParagraph} method
|
||||
* when it enters nested editable.
|
||||
*
|
||||
* @since 4.3
|
||||
* @readonly
|
||||
* @property {CKEDITOR.filter} activeFilter
|
||||
*/
|
||||
|
||||
var beginWhitespaceRegex = /^[\r\n\t ]+$/,
|
||||
// Ignore bookmark nodes.(#3783)
|
||||
bookmarkGuard = CKEDITOR.dom.walker.bookmark( false, true ),
|
||||
whitespacesGuard = CKEDITOR.dom.walker.whitespaces( true ),
|
||||
skipGuard = function( node ) {
|
||||
return bookmarkGuard( node ) && whitespacesGuard( node );
|
||||
};
|
||||
|
||||
// Get a reference for the next element, bookmark nodes are skipped.
|
||||
function getNextSourceNode( node, startFromSibling, lastNode ) {
|
||||
var next = node.getNextSourceNode( startFromSibling, null, lastNode );
|
||||
while ( !bookmarkGuard( next ) )
|
||||
next = next.getNextSourceNode( startFromSibling, null, lastNode );
|
||||
return next;
|
||||
}
|
||||
|
||||
iterator.prototype = {
|
||||
/**
|
||||
* Returns next paragraph-like element or `null` if reached the end of range.
|
||||
*
|
||||
* @param {String} [blockTag='p'] Name of a block element which will be established by
|
||||
* iterator in block-less elements (see {@link #enforceRealBlocks}).
|
||||
*/
|
||||
getNextParagraph: function( blockTag ) {
|
||||
// The block element to be returned.
|
||||
var block;
|
||||
|
||||
// The range object used to identify the paragraph contents.
|
||||
var range;
|
||||
|
||||
// Indicats that the current element in the loop is the last one.
|
||||
var isLast;
|
||||
|
||||
// Instructs to cleanup remaining BRs.
|
||||
var removePreviousBr, removeLastBr;
|
||||
|
||||
blockTag = blockTag || 'p';
|
||||
|
||||
// We're iterating over nested editable.
|
||||
if ( this._.nestedEditable ) {
|
||||
// Get next block from nested iterator and returns it if was found.
|
||||
block = this._.nestedEditable.iterator.getNextParagraph( blockTag );
|
||||
if ( block ) {
|
||||
// Inherit activeFilter from the nested iterator.
|
||||
this.activeFilter = this._.nestedEditable.iterator.activeFilter;
|
||||
return block;
|
||||
}
|
||||
|
||||
// No block in nested iterator means that we reached the end of the nested editable.
|
||||
// Reset the active filter to the default filter (or undefined if this iterator didn't have it).
|
||||
this.activeFilter = this.filter;
|
||||
|
||||
// Try to find next nested editable or get back to parent (this) iterator.
|
||||
if ( startNestedEditableIterator( this, blockTag, this._.nestedEditable.container, this._.nestedEditable.remaining ) ) {
|
||||
// Inherit activeFilter from the nested iterator.
|
||||
this.activeFilter = this._.nestedEditable.iterator.activeFilter;
|
||||
return this._.nestedEditable.iterator.getNextParagraph( blockTag );
|
||||
} else
|
||||
this._.nestedEditable = null;
|
||||
}
|
||||
|
||||
// Block-less range should be checked first.
|
||||
if ( !this.range.root.getDtd()[ blockTag ] )
|
||||
return null;
|
||||
|
||||
// This is the first iteration. Let's initialize it.
|
||||
if ( !this._.started )
|
||||
range = startIterator.call( this );
|
||||
|
||||
var currentNode = this._.nextNode,
|
||||
lastNode = this._.lastNode;
|
||||
|
||||
this._.nextNode = null;
|
||||
while ( currentNode ) {
|
||||
// closeRange indicates that a paragraph boundary has been found,
|
||||
// so the range can be closed.
|
||||
var closeRange = 0,
|
||||
parentPre = currentNode.hasAscendant( 'pre' );
|
||||
|
||||
// includeNode indicates that the current node is good to be part
|
||||
// of the range. By default, any non-element node is ok for it.
|
||||
var includeNode = ( currentNode.type != CKEDITOR.NODE_ELEMENT ),
|
||||
continueFromSibling = 0;
|
||||
|
||||
// If it is an element node, let's check if it can be part of the range.
|
||||
if ( !includeNode ) {
|
||||
var nodeName = currentNode.getName();
|
||||
|
||||
// Non-editable block was found - return it and move to processing
|
||||
// its nested editables if they exist.
|
||||
if ( CKEDITOR.dtd.$block[ nodeName ] && currentNode.getAttribute( 'contenteditable' ) == 'false' ) {
|
||||
block = currentNode;
|
||||
|
||||
// Setup iterator for first of nested editables.
|
||||
// If there's no editable, then algorithm will move to next element after current block.
|
||||
startNestedEditableIterator( this, blockTag, block );
|
||||
|
||||
// Gets us straight to the end of getParagraph() because block variable is set.
|
||||
break;
|
||||
} else if ( currentNode.isBlockBoundary( this.forceBrBreak && !parentPre && { br: 1 } ) ) {
|
||||
// <br> boundaries must be part of the range. It will
|
||||
// happen only if ForceBrBreak.
|
||||
if ( nodeName == 'br' )
|
||||
includeNode = 1;
|
||||
else if ( !range && !currentNode.getChildCount() && nodeName != 'hr' ) {
|
||||
// If we have found an empty block, and haven't started
|
||||
// the range yet, it means we must return this block.
|
||||
block = currentNode;
|
||||
isLast = currentNode.equals( lastNode );
|
||||
break;
|
||||
}
|
||||
|
||||
// The range must finish right before the boundary,
|
||||
// including possibly skipped empty spaces. (#1603)
|
||||
if ( range ) {
|
||||
range.setEndAt( currentNode, CKEDITOR.POSITION_BEFORE_START );
|
||||
|
||||
// The found boundary must be set as the next one at this
|
||||
// point. (#1717)
|
||||
if ( nodeName != 'br' )
|
||||
this._.nextNode = currentNode;
|
||||
}
|
||||
|
||||
closeRange = 1;
|
||||
} else {
|
||||
// If we have child nodes, let's check them.
|
||||
if ( currentNode.getFirst() ) {
|
||||
// If we don't have a range yet, let's start it.
|
||||
if ( !range ) {
|
||||
range = this.range.clone();
|
||||
range.setStartAt( currentNode, CKEDITOR.POSITION_BEFORE_START );
|
||||
}
|
||||
|
||||
currentNode = currentNode.getFirst();
|
||||
continue;
|
||||
}
|
||||
includeNode = 1;
|
||||
}
|
||||
} else if ( currentNode.type == CKEDITOR.NODE_TEXT ) {
|
||||
// Ignore normal whitespaces (i.e. not including or
|
||||
// other unicode whitespaces) before/after a block node.
|
||||
if ( beginWhitespaceRegex.test( currentNode.getText() ) )
|
||||
includeNode = 0;
|
||||
}
|
||||
|
||||
// The current node is good to be part of the range and we are
|
||||
// starting a new range, initialize it first.
|
||||
if ( includeNode && !range ) {
|
||||
range = this.range.clone();
|
||||
range.setStartAt( currentNode, CKEDITOR.POSITION_BEFORE_START );
|
||||
}
|
||||
|
||||
// The last node has been found.
|
||||
isLast = ( ( !closeRange || includeNode ) && currentNode.equals( lastNode ) );
|
||||
|
||||
// If we are in an element boundary, let's check if it is time
|
||||
// to close the range, otherwise we include the parent within it.
|
||||
if ( range && !closeRange ) {
|
||||
while ( !currentNode.getNext( skipGuard ) && !isLast ) {
|
||||
var parentNode = currentNode.getParent();
|
||||
|
||||
if ( parentNode.isBlockBoundary( this.forceBrBreak && !parentPre && { br: 1 } ) ) {
|
||||
closeRange = 1;
|
||||
includeNode = 0;
|
||||
isLast = isLast || ( parentNode.equals( lastNode ) );
|
||||
// Make sure range includes bookmarks at the end of the block. (#7359)
|
||||
range.setEndAt( parentNode, CKEDITOR.POSITION_BEFORE_END );
|
||||
break;
|
||||
}
|
||||
|
||||
currentNode = parentNode;
|
||||
includeNode = 1;
|
||||
isLast = ( currentNode.equals( lastNode ) );
|
||||
continueFromSibling = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Now finally include the node.
|
||||
if ( includeNode )
|
||||
range.setEndAt( currentNode, CKEDITOR.POSITION_AFTER_END );
|
||||
|
||||
currentNode = getNextSourceNode( currentNode, continueFromSibling, lastNode );
|
||||
isLast = !currentNode;
|
||||
|
||||
// We have found a block boundary. Let's close the range and move out of the
|
||||
// loop.
|
||||
if ( isLast || ( closeRange && range ) )
|
||||
break;
|
||||
}
|
||||
|
||||
// Now, based on the processed range, look for (or create) the block to be returned.
|
||||
if ( !block ) {
|
||||
// If no range has been found, this is the end.
|
||||
if ( !range ) {
|
||||
this._.docEndMarker && this._.docEndMarker.remove();
|
||||
this._.nextNode = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
var startPath = new CKEDITOR.dom.elementPath( range.startContainer, range.root );
|
||||
var startBlockLimit = startPath.blockLimit,
|
||||
checkLimits = { div: 1, th: 1, td: 1 };
|
||||
block = startPath.block;
|
||||
|
||||
if ( !block && startBlockLimit && !this.enforceRealBlocks && checkLimits[ startBlockLimit.getName() ] && range.checkStartOfBlock() && range.checkEndOfBlock() && !startBlockLimit.equals( range.root ) )
|
||||
block = startBlockLimit;
|
||||
else if ( !block || ( this.enforceRealBlocks && block.getName() == 'li' ) ) {
|
||||
// Create the fixed block.
|
||||
block = this.range.document.createElement( blockTag );
|
||||
|
||||
// Move the contents of the temporary range to the fixed block.
|
||||
range.extractContents().appendTo( block );
|
||||
block.trim();
|
||||
|
||||
// Insert the fixed block into the DOM.
|
||||
range.insertNode( block );
|
||||
|
||||
removePreviousBr = removeLastBr = true;
|
||||
} else if ( block.getName() != 'li' ) {
|
||||
// If the range doesn't includes the entire contents of the
|
||||
// block, we must split it, isolating the range in a dedicated
|
||||
// block.
|
||||
if ( !range.checkStartOfBlock() || !range.checkEndOfBlock() ) {
|
||||
// The resulting block will be a clone of the current one.
|
||||
block = block.clone( false );
|
||||
|
||||
// Extract the range contents, moving it to the new block.
|
||||
range.extractContents().appendTo( block );
|
||||
block.trim();
|
||||
|
||||
// Split the block. At this point, the range will be in the
|
||||
// right position for our intents.
|
||||
var splitInfo = range.splitBlock();
|
||||
|
||||
removePreviousBr = !splitInfo.wasStartOfBlock;
|
||||
removeLastBr = !splitInfo.wasEndOfBlock;
|
||||
|
||||
// Insert the new block into the DOM.
|
||||
range.insertNode( block );
|
||||
}
|
||||
} else if ( !isLast ) {
|
||||
// LIs are returned as is, with all their children (due to the
|
||||
// nested lists). But, the next node is the node right after
|
||||
// the current range, which could be an <li> child (nested
|
||||
// lists) or the next sibling <li>.
|
||||
|
||||
this._.nextNode = ( block.equals( lastNode ) ? null : getNextSourceNode( range.getBoundaryNodes().endNode, 1, lastNode ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( removePreviousBr ) {
|
||||
var previousSibling = block.getPrevious();
|
||||
if ( previousSibling && previousSibling.type == CKEDITOR.NODE_ELEMENT ) {
|
||||
if ( previousSibling.getName() == 'br' )
|
||||
previousSibling.remove();
|
||||
else if ( previousSibling.getLast() && previousSibling.getLast().$.nodeName.toLowerCase() == 'br' )
|
||||
previousSibling.getLast().remove();
|
||||
}
|
||||
}
|
||||
|
||||
if ( removeLastBr ) {
|
||||
var lastChild = block.getLast();
|
||||
if ( lastChild && lastChild.type == CKEDITOR.NODE_ELEMENT && lastChild.getName() == 'br' ) {
|
||||
// Remove br filler on browser which do not need it.
|
||||
if ( !CKEDITOR.env.needsBrFiller || lastChild.getPrevious( bookmarkGuard ) || lastChild.getNext( bookmarkGuard ) )
|
||||
lastChild.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// Get a reference for the next element. This is important because the
|
||||
// above block can be removed or changed, so we can rely on it for the
|
||||
// next interation.
|
||||
if ( !this._.nextNode )
|
||||
this._.nextNode = ( isLast || block.equals( lastNode ) || !lastNode ) ? null : getNextSourceNode( block, 1, lastNode );
|
||||
|
||||
return block;
|
||||
}
|
||||
};
|
||||
|
||||
// @context CKEDITOR.dom.iterator
|
||||
// @returns Collapsed range which will be reused when during furter processing.
|
||||
function startIterator() {
|
||||
var range = this.range.clone(),
|
||||
// Indicate at least one of the range boundaries is inside a preformat block.
|
||||
touchPre;
|
||||
|
||||
// Shrink the range to exclude harmful "noises" (#4087, #4450, #5435).
|
||||
range.shrink( CKEDITOR.SHRINK_ELEMENT, true );
|
||||
|
||||
touchPre = range.endContainer.hasAscendant( 'pre', true ) || range.startContainer.hasAscendant( 'pre', true );
|
||||
|
||||
range.enlarge( this.forceBrBreak && !touchPre || !this.enlargeBr ? CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS : CKEDITOR.ENLARGE_BLOCK_CONTENTS );
|
||||
|
||||
if ( !range.collapsed ) {
|
||||
var walker = new CKEDITOR.dom.walker( range.clone() ),
|
||||
ignoreBookmarkTextEvaluator = CKEDITOR.dom.walker.bookmark( true, true );
|
||||
// Avoid anchor inside bookmark inner text.
|
||||
walker.evaluator = ignoreBookmarkTextEvaluator;
|
||||
this._.nextNode = walker.next();
|
||||
// TODO: It's better to have walker.reset() used here.
|
||||
walker = new CKEDITOR.dom.walker( range.clone() );
|
||||
walker.evaluator = ignoreBookmarkTextEvaluator;
|
||||
var lastNode = walker.previous();
|
||||
this._.lastNode = lastNode.getNextSourceNode( true );
|
||||
|
||||
// We may have an empty text node at the end of block due to [3770].
|
||||
// If that node is the lastNode, it would cause our logic to leak to the
|
||||
// next block.(#3887)
|
||||
if ( this._.lastNode && this._.lastNode.type == CKEDITOR.NODE_TEXT && !CKEDITOR.tools.trim( this._.lastNode.getText() ) && this._.lastNode.getParent().isBlockBoundary() ) {
|
||||
var testRange = this.range.clone();
|
||||
testRange.moveToPosition( this._.lastNode, CKEDITOR.POSITION_AFTER_END );
|
||||
if ( testRange.checkEndOfBlock() ) {
|
||||
var path = new CKEDITOR.dom.elementPath( testRange.endContainer, testRange.root ),
|
||||
lastBlock = path.block || path.blockLimit;
|
||||
this._.lastNode = lastBlock.getNextSourceNode( true );
|
||||
}
|
||||
}
|
||||
|
||||
// The end of document or range.root was reached, so we need a marker node inside.
|
||||
if ( !this._.lastNode || !range.root.contains( this._.lastNode ) ) {
|
||||
this._.lastNode = this._.docEndMarker = range.document.createText( '' );
|
||||
this._.lastNode.insertAfter( lastNode );
|
||||
}
|
||||
|
||||
// Let's reuse this variable.
|
||||
range = null;
|
||||
}
|
||||
|
||||
this._.started = 1;
|
||||
|
||||
return range;
|
||||
}
|
||||
|
||||
// Does a nested editables lookup inside editablesContainer.
|
||||
// If remainingEditables is set will lookup inside this array.
|
||||
// @param {CKEDITOR.dom.element} editablesContainer
|
||||
// @param {CKEDITOR.dom.element[]} [remainingEditables]
|
||||
function getNestedEditableIn( editablesContainer, remainingEditables ) {
|
||||
if ( remainingEditables == undefined )
|
||||
remainingEditables = findNestedEditables( editablesContainer );
|
||||
|
||||
var editable;
|
||||
|
||||
while ( ( editable = remainingEditables.shift() ) ) {
|
||||
if ( isIterableEditable( editable ) )
|
||||
return { element: editable, remaining: remainingEditables };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Checkes whether we can iterate over this editable.
|
||||
function isIterableEditable( editable ) {
|
||||
// Reject blockless editables.
|
||||
return editable.getDtd().p;
|
||||
}
|
||||
|
||||
// Finds nested editables within container. Does not return
|
||||
// editables nested in another editable (twice).
|
||||
function findNestedEditables( container ) {
|
||||
var editables = [];
|
||||
|
||||
container.forEach( function( element ) {
|
||||
if ( element.getAttribute( 'contenteditable' ) == 'true' ) {
|
||||
editables.push( element );
|
||||
return false; // Skip children.
|
||||
}
|
||||
}, CKEDITOR.NODE_ELEMENT, true );
|
||||
|
||||
return editables;
|
||||
}
|
||||
|
||||
// Looks for a first nested editable after previousEditable (if passed) and creates
|
||||
// nested iterator for it.
|
||||
function startNestedEditableIterator( parentIterator, blockTag, editablesContainer, remainingEditables ) {
|
||||
var editable = getNestedEditableIn( editablesContainer, remainingEditables );
|
||||
|
||||
if ( !editable )
|
||||
return 0;
|
||||
|
||||
var filter = CKEDITOR.filter.instances[ editable.element.data( 'cke-filter' ) ];
|
||||
|
||||
// If current editable has a filter and this filter does not allow for block tag,
|
||||
// search for next nested editable in remaining ones.
|
||||
if ( filter && !filter.check( blockTag ) )
|
||||
return startNestedEditableIterator( parentIterator, blockTag, editablesContainer, editable.remaining );
|
||||
|
||||
var range = new CKEDITOR.dom.range( editable.element );
|
||||
range.selectNodeContents( editable.element );
|
||||
|
||||
var iterator = range.createIterator();
|
||||
// This setting actually does not change anything in this case,
|
||||
// because entire range contents is selected, so there're no <br>s to be included.
|
||||
// But it seems right to copy it too.
|
||||
iterator.enlargeBr = parentIterator.enlargeBr;
|
||||
// Inherit configuration from parent iterator.
|
||||
iterator.enforceRealBlocks = parentIterator.enforceRealBlocks;
|
||||
// Set the activeFilter (which can be overriden when this iteator will start nested iterator)
|
||||
// and the default filter, which will make it possible to reset to
|
||||
// current iterator's activeFilter after leaving nested editable.
|
||||
iterator.activeFilter = iterator.filter = filter;
|
||||
|
||||
parentIterator._.nestedEditable = {
|
||||
element: editable.element,
|
||||
container: editablesContainer,
|
||||
remaining: editable.remaining,
|
||||
iterator: iterator
|
||||
};
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates {CKEDITOR.dom.iterator} instance for this range.
|
||||
*
|
||||
* @member CKEDITOR.dom.range
|
||||
* @returns {CKEDITOR.dom.iterator}
|
||||
*/
|
||||
CKEDITOR.dom.range.prototype.createIterator = function() {
|
||||
return new iterator( this );
|
||||
};
|
||||
} )();
|
||||
@@ -1,748 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileOverview Defines the {@link CKEDITOR.dom.node} class which is the base
|
||||
* class for classes that represent DOM nodes.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Base class for classes representing DOM nodes. This constructor may return
|
||||
* an instance of a class that inherits from this class, like
|
||||
* {@link CKEDITOR.dom.element} or {@link CKEDITOR.dom.text}.
|
||||
*
|
||||
* @class
|
||||
* @extends CKEDITOR.dom.domObject
|
||||
* @constructor Creates a node class instance.
|
||||
* @param {Object} domNode A native DOM node.
|
||||
* @see CKEDITOR.dom.element
|
||||
* @see CKEDITOR.dom.text
|
||||
*/
|
||||
CKEDITOR.dom.node = function( domNode ) {
|
||||
if ( domNode ) {
|
||||
var type = domNode.nodeType == CKEDITOR.NODE_DOCUMENT ? 'document' : domNode.nodeType == CKEDITOR.NODE_ELEMENT ? 'element' : domNode.nodeType == CKEDITOR.NODE_TEXT ? 'text' : domNode.nodeType == CKEDITOR.NODE_COMMENT ? 'comment' : domNode.nodeType == CKEDITOR.NODE_DOCUMENT_FRAGMENT ? 'documentFragment' : 'domObject'; // Call the base constructor otherwise.
|
||||
|
||||
return new CKEDITOR.dom[ type ]( domNode );
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
CKEDITOR.dom.node.prototype = new CKEDITOR.dom.domObject();
|
||||
|
||||
/**
|
||||
* Element node type.
|
||||
*
|
||||
* @readonly
|
||||
* @property {Number} [=1]
|
||||
* @member CKEDITOR
|
||||
*/
|
||||
CKEDITOR.NODE_ELEMENT = 1;
|
||||
|
||||
/**
|
||||
* Document node type.
|
||||
*
|
||||
* @readonly
|
||||
* @property {Number} [=9]
|
||||
* @member CKEDITOR
|
||||
*/
|
||||
CKEDITOR.NODE_DOCUMENT = 9;
|
||||
|
||||
/**
|
||||
* Text node type.
|
||||
*
|
||||
* @readonly
|
||||
* @property {Number} [=3]
|
||||
* @member CKEDITOR
|
||||
*/
|
||||
CKEDITOR.NODE_TEXT = 3;
|
||||
|
||||
/**
|
||||
* Comment node type.
|
||||
*
|
||||
* @readonly
|
||||
* @property {Number} [=8]
|
||||
* @member CKEDITOR
|
||||
*/
|
||||
CKEDITOR.NODE_COMMENT = 8;
|
||||
|
||||
/**
|
||||
* Document fragment node type.
|
||||
*
|
||||
* @readonly
|
||||
* @property {Number} [=11]
|
||||
* @member CKEDITOR
|
||||
*/
|
||||
CKEDITOR.NODE_DOCUMENT_FRAGMENT = 11;
|
||||
|
||||
CKEDITOR.POSITION_IDENTICAL = 0;
|
||||
CKEDITOR.POSITION_DISCONNECTED = 1;
|
||||
CKEDITOR.POSITION_FOLLOWING = 2;
|
||||
CKEDITOR.POSITION_PRECEDING = 4;
|
||||
CKEDITOR.POSITION_IS_CONTAINED = 8;
|
||||
CKEDITOR.POSITION_CONTAINS = 16;
|
||||
|
||||
CKEDITOR.tools.extend( CKEDITOR.dom.node.prototype, {
|
||||
/**
|
||||
* Makes this node a child of another element.
|
||||
*
|
||||
* var p = new CKEDITOR.dom.element( 'p' );
|
||||
* var strong = new CKEDITOR.dom.element( 'strong' );
|
||||
* strong.appendTo( p );
|
||||
*
|
||||
* // Result: '<p><strong></strong></p>'.
|
||||
*
|
||||
* @param {CKEDITOR.dom.element} element The target element to which this node will be appended.
|
||||
* @returns {CKEDITOR.dom.element} The target element.
|
||||
*/
|
||||
appendTo: function( element, toStart ) {
|
||||
element.append( this, toStart );
|
||||
return element;
|
||||
},
|
||||
|
||||
/**
|
||||
* Clone this node.
|
||||
*
|
||||
* **Note**: Values set by {#setCustomData} won't be available in the clone.
|
||||
*
|
||||
* @param {Boolean} [includeChildren=false] If `true` then all node's
|
||||
* children will be cloned recursively.
|
||||
* @param {Boolean} [cloneId=false] Whether ID attributes should be cloned too.
|
||||
* @returns {CKEDITOR.dom.node} Clone of this node.
|
||||
*/
|
||||
clone: function( includeChildren, cloneId ) {
|
||||
var $clone = this.$.cloneNode( includeChildren );
|
||||
|
||||
var removeIds = function( node ) {
|
||||
// Reset data-cke-expando only when has been cloned (IE and only for some types of objects).
|
||||
if ( node[ 'data-cke-expando' ] )
|
||||
node[ 'data-cke-expando' ] = false;
|
||||
|
||||
if ( node.nodeType != CKEDITOR.NODE_ELEMENT )
|
||||
return;
|
||||
if ( !cloneId )
|
||||
node.removeAttribute( 'id', false );
|
||||
|
||||
if ( includeChildren ) {
|
||||
var childs = node.childNodes;
|
||||
for ( var i = 0; i < childs.length; i++ )
|
||||
removeIds( childs[ i ] );
|
||||
}
|
||||
};
|
||||
|
||||
// The "id" attribute should never be cloned to avoid duplication.
|
||||
removeIds( $clone );
|
||||
|
||||
return new CKEDITOR.dom.node( $clone );
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if node is preceded by any sibling.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
hasPrevious: function() {
|
||||
return !!this.$.previousSibling;
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if node is succeeded by any sibling.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
hasNext: function() {
|
||||
return !!this.$.nextSibling;
|
||||
},
|
||||
|
||||
/**
|
||||
* Inserts this element after a node.
|
||||
*
|
||||
* var em = new CKEDITOR.dom.element( 'em' );
|
||||
* var strong = new CKEDITOR.dom.element( 'strong' );
|
||||
* strong.insertAfter( em );
|
||||
*
|
||||
* // Result: '<em></em><strong></strong>'
|
||||
*
|
||||
* @param {CKEDITOR.dom.node} node The node that will precede this element.
|
||||
* @returns {CKEDITOR.dom.node} The node preceding this one after insertion.
|
||||
*/
|
||||
insertAfter: function( node ) {
|
||||
node.$.parentNode.insertBefore( this.$, node.$.nextSibling );
|
||||
return node;
|
||||
},
|
||||
|
||||
/**
|
||||
* Inserts this element before a node.
|
||||
*
|
||||
* var em = new CKEDITOR.dom.element( 'em' );
|
||||
* var strong = new CKEDITOR.dom.element( 'strong' );
|
||||
* strong.insertBefore( em );
|
||||
*
|
||||
* // result: '<strong></strong><em></em>'
|
||||
*
|
||||
* @param {CKEDITOR.dom.node} node The node that will succeed this element.
|
||||
* @returns {CKEDITOR.dom.node} The node being inserted.
|
||||
*/
|
||||
insertBefore: function( node ) {
|
||||
node.$.parentNode.insertBefore( this.$, node.$ );
|
||||
return node;
|
||||
},
|
||||
|
||||
/**
|
||||
* Inserts node before this node.
|
||||
*
|
||||
* var em = new CKEDITOR.dom.element( 'em' );
|
||||
* var strong = new CKEDITOR.dom.element( 'strong' );
|
||||
* strong.insertBeforeMe( em );
|
||||
*
|
||||
* // result: '<em></em><strong></strong>'
|
||||
*
|
||||
* @param {CKEDITOR.dom.node} node The node that will preceed this element.
|
||||
* @returns {CKEDITOR.dom.node} The node being inserted.
|
||||
*/
|
||||
insertBeforeMe: function( node ) {
|
||||
this.$.parentNode.insertBefore( node.$, this.$ );
|
||||
return node;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieves a uniquely identifiable tree address for this node.
|
||||
* The tree address returned is an array of integers, with each integer
|
||||
* indicating a child index of a DOM node, starting from
|
||||
* `document.documentElement`.
|
||||
*
|
||||
* For example, assuming `<body>` is the second child
|
||||
* of `<html>` (`<head>` being the first),
|
||||
* and we would like to address the third child under the
|
||||
* fourth child of `<body>`, the tree address returned would be:
|
||||
* `[1, 3, 2]`.
|
||||
*
|
||||
* The tree address cannot be used for finding back the DOM tree node once
|
||||
* the DOM tree structure has been modified.
|
||||
*
|
||||
* @param {Boolean} [normalized=false] See {@link #getIndex}.
|
||||
* @returns {Array} The address.
|
||||
*/
|
||||
getAddress: function( normalized ) {
|
||||
var address = [];
|
||||
var $documentElement = this.getDocument().$.documentElement;
|
||||
var node = this.$;
|
||||
|
||||
while ( node && node != $documentElement ) {
|
||||
var parentNode = node.parentNode;
|
||||
|
||||
if ( parentNode ) {
|
||||
// Get the node index. For performance, call getIndex
|
||||
// directly, instead of creating a new node object.
|
||||
address.unshift( this.getIndex.call( { $: node }, normalized ) );
|
||||
}
|
||||
|
||||
node = parentNode;
|
||||
}
|
||||
|
||||
return address;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the document containing this element.
|
||||
*
|
||||
* var element = CKEDITOR.document.getById( 'example' );
|
||||
* alert( element.getDocument().equals( CKEDITOR.document ) ); // true
|
||||
*
|
||||
* @returns {CKEDITOR.dom.document} The document.
|
||||
*/
|
||||
getDocument: function() {
|
||||
return new CKEDITOR.dom.document( this.$.ownerDocument || this.$.parentNode.ownerDocument );
|
||||
},
|
||||
|
||||
/**
|
||||
* Get index of a node in an array of its parent.childNodes.
|
||||
*
|
||||
* Let's assume having childNodes array:
|
||||
*
|
||||
* [ emptyText, element1, text, text, element2 ]
|
||||
* element1.getIndex(); // 1
|
||||
* element1.getIndex( true ); // 0
|
||||
* element2.getIndex(); // 4
|
||||
* element2.getIndex( true ); // 2
|
||||
*
|
||||
* @param {Boolean} normalized When `true` empty text nodes and one followed
|
||||
* by another one text node are not counted in.
|
||||
* @returns {Number} Index of a node.
|
||||
*/
|
||||
getIndex: function( normalized ) {
|
||||
// Attention: getAddress depends on this.$
|
||||
// getIndex is called on a plain object: { $ : node }
|
||||
|
||||
var current = this.$,
|
||||
index = -1,
|
||||
isNormalizing;
|
||||
|
||||
if ( !this.$.parentNode )
|
||||
return index;
|
||||
|
||||
do {
|
||||
// Bypass blank node and adjacent text nodes.
|
||||
if ( normalized && current != this.$ && current.nodeType == CKEDITOR.NODE_TEXT && ( isNormalizing || !current.nodeValue ) )
|
||||
continue;
|
||||
|
||||
index++;
|
||||
isNormalizing = current.nodeType == CKEDITOR.NODE_TEXT;
|
||||
}
|
||||
while ( ( current = current.previousSibling ) )
|
||||
|
||||
return index;
|
||||
},
|
||||
|
||||
/**
|
||||
* @todo
|
||||
*/
|
||||
getNextSourceNode: function( startFromSibling, nodeType, guard ) {
|
||||
// If "guard" is a node, transform it in a function.
|
||||
if ( guard && !guard.call ) {
|
||||
var guardNode = guard;
|
||||
guard = function( node ) {
|
||||
return !node.equals( guardNode );
|
||||
};
|
||||
}
|
||||
|
||||
var node = ( !startFromSibling && this.getFirst && this.getFirst() ),
|
||||
parent;
|
||||
|
||||
// Guarding when we're skipping the current element( no children or 'startFromSibling' ).
|
||||
// send the 'moving out' signal even we don't actually dive into.
|
||||
if ( !node ) {
|
||||
if ( this.type == CKEDITOR.NODE_ELEMENT && guard && guard( this, true ) === false )
|
||||
return null;
|
||||
node = this.getNext();
|
||||
}
|
||||
|
||||
while ( !node && ( parent = ( parent || this ).getParent() ) ) {
|
||||
// The guard check sends the "true" paramenter to indicate that
|
||||
// we are moving "out" of the element.
|
||||
if ( guard && guard( parent, true ) === false )
|
||||
return null;
|
||||
|
||||
node = parent.getNext();
|
||||
}
|
||||
|
||||
if ( !node )
|
||||
return null;
|
||||
|
||||
if ( guard && guard( node ) === false )
|
||||
return null;
|
||||
|
||||
if ( nodeType && nodeType != node.type )
|
||||
return node.getNextSourceNode( false, nodeType, guard );
|
||||
|
||||
return node;
|
||||
},
|
||||
|
||||
/**
|
||||
* @todo
|
||||
*/
|
||||
getPreviousSourceNode: function( startFromSibling, nodeType, guard ) {
|
||||
if ( guard && !guard.call ) {
|
||||
var guardNode = guard;
|
||||
guard = function( node ) {
|
||||
return !node.equals( guardNode );
|
||||
};
|
||||
}
|
||||
|
||||
var node = ( !startFromSibling && this.getLast && this.getLast() ),
|
||||
parent;
|
||||
|
||||
// Guarding when we're skipping the current element( no children or 'startFromSibling' ).
|
||||
// send the 'moving out' signal even we don't actually dive into.
|
||||
if ( !node ) {
|
||||
if ( this.type == CKEDITOR.NODE_ELEMENT && guard && guard( this, true ) === false )
|
||||
return null;
|
||||
node = this.getPrevious();
|
||||
}
|
||||
|
||||
while ( !node && ( parent = ( parent || this ).getParent() ) ) {
|
||||
// The guard check sends the "true" paramenter to indicate that
|
||||
// we are moving "out" of the element.
|
||||
if ( guard && guard( parent, true ) === false )
|
||||
return null;
|
||||
|
||||
node = parent.getPrevious();
|
||||
}
|
||||
|
||||
if ( !node )
|
||||
return null;
|
||||
|
||||
if ( guard && guard( node ) === false )
|
||||
return null;
|
||||
|
||||
if ( nodeType && node.type != nodeType )
|
||||
return node.getPreviousSourceNode( false, nodeType, guard );
|
||||
|
||||
return node;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the node that preceed this element in its parent's child list.
|
||||
*
|
||||
* var element = CKEDITOR.dom.element.createFromHtml( '<div><i>prev</i><b>Example</b></div>' );
|
||||
* var first = element.getLast().getPrev();
|
||||
* alert( first.getName() ); // 'i'
|
||||
*
|
||||
* @param {Function} [evaluator] Filtering the result node.
|
||||
* @returns {CKEDITOR.dom.node} The previous node or null if not available.
|
||||
*/
|
||||
getPrevious: function( evaluator ) {
|
||||
var previous = this.$,
|
||||
retval;
|
||||
do {
|
||||
previous = previous.previousSibling;
|
||||
|
||||
// Avoid returning the doc type node.
|
||||
// http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-412266927
|
||||
retval = previous && previous.nodeType != 10 && new CKEDITOR.dom.node( previous );
|
||||
}
|
||||
while ( retval && evaluator && !evaluator( retval ) )
|
||||
return retval;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the node that follows this element in its parent's child list.
|
||||
*
|
||||
* var element = CKEDITOR.dom.element.createFromHtml( '<div><b>Example</b><i>next</i></div>' );
|
||||
* var last = element.getFirst().getNext();
|
||||
* alert( last.getName() ); // 'i'
|
||||
*
|
||||
* @param {Function} [evaluator] Filtering the result node.
|
||||
* @returns {CKEDITOR.dom.node} The next node or null if not available.
|
||||
*/
|
||||
getNext: function( evaluator ) {
|
||||
var next = this.$,
|
||||
retval;
|
||||
do {
|
||||
next = next.nextSibling;
|
||||
retval = next && new CKEDITOR.dom.node( next );
|
||||
}
|
||||
while ( retval && evaluator && !evaluator( retval ) )
|
||||
return retval;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the parent element for this node.
|
||||
*
|
||||
* var node = editor.document.getBody().getFirst();
|
||||
* var parent = node.getParent();
|
||||
* alert( parent.getName() ); // 'body'
|
||||
*
|
||||
* @param {Boolean} [allowFragmentParent=false] Consider also parent node that is of
|
||||
* fragment type {@link CKEDITOR#NODE_DOCUMENT_FRAGMENT}.
|
||||
* @returns {CKEDITOR.dom.element} The parent element.
|
||||
*/
|
||||
getParent: function( allowFragmentParent ) {
|
||||
var parent = this.$.parentNode;
|
||||
return ( parent && ( parent.nodeType == CKEDITOR.NODE_ELEMENT || allowFragmentParent && parent.nodeType == CKEDITOR.NODE_DOCUMENT_FRAGMENT ) ) ? new CKEDITOR.dom.node( parent ) : null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns array containing node parents and node itself. By default nodes are in _descending_ order.
|
||||
*
|
||||
* // Assuming that body has paragraph as first child.
|
||||
* var node = editor.document.getBody().getFirst();
|
||||
* var parents = node.getParents();
|
||||
* alert( parents[ 0 ].getName() + ',' + parents[ 2 ].getName() ); // 'html,p'
|
||||
*
|
||||
* @param {Boolean} [closerFirst=false] Determines order of returned nodes.
|
||||
* @returns {Array} Returns array of {@link CKEDITOR.dom.node}.
|
||||
*/
|
||||
getParents: function( closerFirst ) {
|
||||
var node = this;
|
||||
var parents = [];
|
||||
|
||||
do {
|
||||
parents[ closerFirst ? 'push' : 'unshift' ]( node );
|
||||
}
|
||||
while ( ( node = node.getParent() ) )
|
||||
|
||||
return parents;
|
||||
},
|
||||
|
||||
/**
|
||||
* @todo
|
||||
*/
|
||||
getCommonAncestor: function( node ) {
|
||||
if ( node.equals( this ) )
|
||||
return this;
|
||||
|
||||
if ( node.contains && node.contains( this ) )
|
||||
return node;
|
||||
|
||||
var start = this.contains ? this : this.getParent();
|
||||
|
||||
do {
|
||||
if ( start.contains( node ) ) return start;
|
||||
}
|
||||
while ( ( start = start.getParent() ) );
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* @todo
|
||||
*/
|
||||
getPosition: function( otherNode ) {
|
||||
var $ = this.$;
|
||||
var $other = otherNode.$;
|
||||
|
||||
if ( $.compareDocumentPosition )
|
||||
return $.compareDocumentPosition( $other );
|
||||
|
||||
// IE and Safari have no support for compareDocumentPosition.
|
||||
|
||||
if ( $ == $other )
|
||||
return CKEDITOR.POSITION_IDENTICAL;
|
||||
|
||||
// Only element nodes support contains and sourceIndex.
|
||||
if ( this.type == CKEDITOR.NODE_ELEMENT && otherNode.type == CKEDITOR.NODE_ELEMENT ) {
|
||||
if ( $.contains ) {
|
||||
if ( $.contains( $other ) )
|
||||
return CKEDITOR.POSITION_CONTAINS + CKEDITOR.POSITION_PRECEDING;
|
||||
|
||||
if ( $other.contains( $ ) )
|
||||
return CKEDITOR.POSITION_IS_CONTAINED + CKEDITOR.POSITION_FOLLOWING;
|
||||
}
|
||||
|
||||
if ( 'sourceIndex' in $ )
|
||||
return ( $.sourceIndex < 0 || $other.sourceIndex < 0 ) ? CKEDITOR.POSITION_DISCONNECTED : ( $.sourceIndex < $other.sourceIndex ) ? CKEDITOR.POSITION_PRECEDING : CKEDITOR.POSITION_FOLLOWING;
|
||||
|
||||
}
|
||||
|
||||
// For nodes that don't support compareDocumentPosition, contains
|
||||
// or sourceIndex, their "address" is compared.
|
||||
|
||||
var addressOfThis = this.getAddress(),
|
||||
addressOfOther = otherNode.getAddress(),
|
||||
minLevel = Math.min( addressOfThis.length, addressOfOther.length );
|
||||
|
||||
// Determinate preceed/follow relationship.
|
||||
for ( var i = 0; i <= minLevel - 1; i++ ) {
|
||||
if ( addressOfThis[ i ] != addressOfOther[ i ] ) {
|
||||
if ( i < minLevel )
|
||||
return addressOfThis[ i ] < addressOfOther[ i ] ? CKEDITOR.POSITION_PRECEDING : CKEDITOR.POSITION_FOLLOWING;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Determinate contains/contained relationship.
|
||||
return ( addressOfThis.length < addressOfOther.length ) ? CKEDITOR.POSITION_CONTAINS + CKEDITOR.POSITION_PRECEDING : CKEDITOR.POSITION_IS_CONTAINED + CKEDITOR.POSITION_FOLLOWING;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the closest ancestor node of this node, specified by its name.
|
||||
*
|
||||
* // Suppose we have the following HTML structure:
|
||||
* // <div id="outer"><div id="inner"><p><b>Some text</b></p></div></div>
|
||||
* // If node == <b>
|
||||
* ascendant = node.getAscendant( 'div' ); // ascendant == <div id="inner">
|
||||
* ascendant = node.getAscendant( 'b' ); // ascendant == null
|
||||
* ascendant = node.getAscendant( 'b', true ); // ascendant == <b>
|
||||
* ascendant = node.getAscendant( { div:1,p:1 } ); // Searches for the first 'div' or 'p': ascendant == <div id="inner">
|
||||
*
|
||||
* @since 3.6.1
|
||||
* @param {String} reference The name of the ancestor node to search or
|
||||
* an object with the node names to search for.
|
||||
* @param {Boolean} [includeSelf] Whether to include the current
|
||||
* node in the search.
|
||||
* @returns {CKEDITOR.dom.node} The located ancestor node or null if not found.
|
||||
*/
|
||||
getAscendant: function( reference, includeSelf ) {
|
||||
var $ = this.$,
|
||||
name;
|
||||
|
||||
if ( !includeSelf )
|
||||
$ = $.parentNode;
|
||||
|
||||
while ( $ ) {
|
||||
if ( $.nodeName && ( name = $.nodeName.toLowerCase(), ( typeof reference == 'string' ? name == reference : name in reference ) ) )
|
||||
return new CKEDITOR.dom.node( $ );
|
||||
|
||||
try {
|
||||
$ = $.parentNode;
|
||||
} catch ( e ) {
|
||||
$ = null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* @todo
|
||||
*/
|
||||
hasAscendant: function( name, includeSelf ) {
|
||||
var $ = this.$;
|
||||
|
||||
if ( !includeSelf )
|
||||
$ = $.parentNode;
|
||||
|
||||
while ( $ ) {
|
||||
if ( $.nodeName && $.nodeName.toLowerCase() == name )
|
||||
return true;
|
||||
|
||||
$ = $.parentNode;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* @todo
|
||||
*/
|
||||
move: function( target, toStart ) {
|
||||
target.append( this.remove(), toStart );
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes this node from the document DOM.
|
||||
*
|
||||
* var element = CKEDITOR.document.getById( 'MyElement' );
|
||||
* element.remove();
|
||||
*
|
||||
* @param {Boolean} [preserveChildren=false] Indicates that the children
|
||||
* elements must remain in the document, removing only the outer tags.
|
||||
*/
|
||||
remove: function( preserveChildren ) {
|
||||
var $ = this.$;
|
||||
var parent = $.parentNode;
|
||||
|
||||
if ( parent ) {
|
||||
if ( preserveChildren ) {
|
||||
// Move all children before the node.
|
||||
for ( var child;
|
||||
( child = $.firstChild ); ) {
|
||||
parent.insertBefore( $.removeChild( child ), $ );
|
||||
}
|
||||
}
|
||||
|
||||
parent.removeChild( $ );
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* @todo
|
||||
*/
|
||||
replace: function( nodeToReplace ) {
|
||||
this.insertBefore( nodeToReplace );
|
||||
nodeToReplace.remove();
|
||||
},
|
||||
|
||||
/**
|
||||
* @todo
|
||||
*/
|
||||
trim: function() {
|
||||
this.ltrim();
|
||||
this.rtrim();
|
||||
},
|
||||
|
||||
/**
|
||||
* @todo
|
||||
*/
|
||||
ltrim: function() {
|
||||
var child;
|
||||
while ( this.getFirst && ( child = this.getFirst() ) ) {
|
||||
if ( child.type == CKEDITOR.NODE_TEXT ) {
|
||||
var trimmed = CKEDITOR.tools.ltrim( child.getText() ),
|
||||
originalLength = child.getLength();
|
||||
|
||||
if ( !trimmed ) {
|
||||
child.remove();
|
||||
continue;
|
||||
} else if ( trimmed.length < originalLength ) {
|
||||
child.split( originalLength - trimmed.length );
|
||||
|
||||
// IE BUG: child.remove() may raise JavaScript errors here. (#81)
|
||||
this.$.removeChild( this.$.firstChild );
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @todo
|
||||
*/
|
||||
rtrim: function() {
|
||||
var child;
|
||||
while ( this.getLast && ( child = this.getLast() ) ) {
|
||||
if ( child.type == CKEDITOR.NODE_TEXT ) {
|
||||
var trimmed = CKEDITOR.tools.rtrim( child.getText() ),
|
||||
originalLength = child.getLength();
|
||||
|
||||
if ( !trimmed ) {
|
||||
child.remove();
|
||||
continue;
|
||||
} else if ( trimmed.length < originalLength ) {
|
||||
child.split( trimmed.length );
|
||||
|
||||
// IE BUG: child.getNext().remove() may raise JavaScript errors here.
|
||||
// (#81)
|
||||
this.$.lastChild.parentNode.removeChild( this.$.lastChild );
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if ( CKEDITOR.env.needsBrFiller ) {
|
||||
child = this.$.lastChild;
|
||||
|
||||
if ( child && child.type == 1 && child.nodeName.toLowerCase() == 'br' ) {
|
||||
// Use "eChildNode.parentNode" instead of "node" to avoid IE bug (#324).
|
||||
child.parentNode.removeChild( child );
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if this node is read-only (should not be changed).
|
||||
*
|
||||
* **Note:** When `attributeCheck` is not used, this method only work for elements
|
||||
* that are already presented in the document, otherwise the result
|
||||
* is not guaranteed, it's mainly for performance consideration.
|
||||
*
|
||||
* // For the following HTML:
|
||||
* // <div contenteditable="false">Some <b>text</b></div>
|
||||
*
|
||||
* // If "ele" is the above <div>
|
||||
* element.isReadOnly(); // true
|
||||
*
|
||||
* @since 3.5
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
isReadOnly: function() {
|
||||
var element = this;
|
||||
if ( this.type != CKEDITOR.NODE_ELEMENT )
|
||||
element = this.getParent();
|
||||
|
||||
if ( element && typeof element.$.isContentEditable != 'undefined' )
|
||||
return !( element.$.isContentEditable || element.data( 'cke-editable' ) );
|
||||
else {
|
||||
// Degrade for old browsers which don't support "isContentEditable", e.g. FF3
|
||||
|
||||
while ( element ) {
|
||||
if ( element.data( 'cke-editable' ) )
|
||||
break;
|
||||
|
||||
if ( element.getAttribute( 'contentEditable' ) == 'false' )
|
||||
return true;
|
||||
else if ( element.getAttribute( 'contentEditable' ) == 'true' )
|
||||
break;
|
||||
|
||||
element = element.getParent();
|
||||
}
|
||||
|
||||
// Reached the root of DOM tree, no editable found.
|
||||
return !element;
|
||||
}
|
||||
}
|
||||
} );
|
||||
@@ -1,43 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a list of {@link CKEDITOR.dom.node} objects.
|
||||
* It's a wrapper for native nodes list.
|
||||
*
|
||||
* var nodeList = CKEDITOR.document.getBody().getChildren();
|
||||
* alert( nodeList.count() ); // number [0;N]
|
||||
*
|
||||
* @class
|
||||
* @constructor Creates a document class instance.
|
||||
* @param {Object} nativeList
|
||||
*/
|
||||
CKEDITOR.dom.nodeList = function( nativeList ) {
|
||||
this.$ = nativeList;
|
||||
};
|
||||
|
||||
CKEDITOR.dom.nodeList.prototype = {
|
||||
/**
|
||||
* Get count of nodes in this list.
|
||||
*
|
||||
* @returns {Number}
|
||||
*/
|
||||
count: function() {
|
||||
return this.$.length;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get node from the list.
|
||||
*
|
||||
* @returns {CKEDITOR.dom.node}
|
||||
*/
|
||||
getItem: function( index ) {
|
||||
if ( index < 0 || index >= this.$.length )
|
||||
return null;
|
||||
|
||||
var $node = this.$[ index ];
|
||||
return $node ? new CKEDITOR.dom.node( $node ) : null;
|
||||
}
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,201 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
( function() {
|
||||
/**
|
||||
* Represents a list os CKEDITOR.dom.range objects, which can be easily
|
||||
* iterated sequentially.
|
||||
*
|
||||
* @class
|
||||
* @extends Array
|
||||
* @constructor Creates a rangeList class instance.
|
||||
* @param {CKEDITOR.dom.range/CKEDITOR.dom.range[]} [ranges] The ranges contained on this list.
|
||||
* Note that, if an array of ranges is specified, the range sequence
|
||||
* should match its DOM order. This class will not help to sort them.
|
||||
*/
|
||||
CKEDITOR.dom.rangeList = function( ranges ) {
|
||||
if ( ranges instanceof CKEDITOR.dom.rangeList )
|
||||
return ranges;
|
||||
|
||||
if ( !ranges )
|
||||
ranges = [];
|
||||
else if ( ranges instanceof CKEDITOR.dom.range )
|
||||
ranges = [ ranges ];
|
||||
|
||||
return CKEDITOR.tools.extend( ranges, mixins );
|
||||
};
|
||||
|
||||
var mixins = {
|
||||
/**
|
||||
* Creates an instance of the rangeList iterator, it should be used
|
||||
* only when the ranges processing could be DOM intrusive, which
|
||||
* means it may pollute and break other ranges in this list.
|
||||
* Otherwise, it's enough to just iterate over this array in a for loop.
|
||||
*
|
||||
* @returns {CKEDITOR.dom.rangeListIterator}
|
||||
*/
|
||||
createIterator: function() {
|
||||
var rangeList = this,
|
||||
bookmark = CKEDITOR.dom.walker.bookmark(),
|
||||
guard = function( node ) {
|
||||
return !( node.is && node.is( 'tr' ) );
|
||||
},
|
||||
bookmarks = [],
|
||||
current;
|
||||
|
||||
return {
|
||||
/**
|
||||
* Retrieves the next range in the list.
|
||||
*
|
||||
* @member CKEDITOR.dom.rangeListIterator
|
||||
* @param {Boolean} [mergeConsequent=false] Whether join two adjacent
|
||||
* ranges into single, e.g. consequent table cells.
|
||||
*/
|
||||
getNextRange: function( mergeConsequent ) {
|
||||
current = current == undefined ? 0 : current + 1;
|
||||
|
||||
var range = rangeList[ current ];
|
||||
|
||||
// Multiple ranges might be mangled by each other.
|
||||
if ( range && rangeList.length > 1 ) {
|
||||
// Bookmarking all other ranges on the first iteration,
|
||||
// the range correctness after it doesn't matter since we'll
|
||||
// restore them before the next iteration.
|
||||
if ( !current ) {
|
||||
// Make sure bookmark correctness by reverse processing.
|
||||
for ( var i = rangeList.length - 1; i >= 0; i-- )
|
||||
bookmarks.unshift( rangeList[ i ].createBookmark( true ) );
|
||||
}
|
||||
|
||||
if ( mergeConsequent ) {
|
||||
// Figure out how many ranges should be merged.
|
||||
var mergeCount = 0;
|
||||
while ( rangeList[ current + mergeCount + 1 ] ) {
|
||||
var doc = range.document,
|
||||
found = 0,
|
||||
left = doc.getById( bookmarks[ mergeCount ].endNode ),
|
||||
right = doc.getById( bookmarks[ mergeCount + 1 ].startNode ),
|
||||
next;
|
||||
|
||||
// Check subsequent range.
|
||||
while ( 1 ) {
|
||||
next = left.getNextSourceNode( false );
|
||||
if ( !right.equals( next ) ) {
|
||||
// This could be yet another bookmark or
|
||||
// walking across block boundaries.
|
||||
if ( bookmark( next ) || ( next.type == CKEDITOR.NODE_ELEMENT && next.isBlockBoundary() ) ) {
|
||||
left = next;
|
||||
continue;
|
||||
}
|
||||
} else
|
||||
found = 1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if ( !found )
|
||||
break;
|
||||
|
||||
mergeCount++;
|
||||
}
|
||||
}
|
||||
|
||||
range.moveToBookmark( bookmarks.shift() );
|
||||
|
||||
// Merge ranges finally after moving to bookmarks.
|
||||
while ( mergeCount-- ) {
|
||||
next = rangeList[ ++current ];
|
||||
next.moveToBookmark( bookmarks.shift() );
|
||||
range.setEnd( next.endContainer, next.endOffset );
|
||||
}
|
||||
}
|
||||
|
||||
return range;
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Create bookmarks for all ranges. See {@link CKEDITOR.dom.range#createBookmark}.
|
||||
*
|
||||
* @param {Boolean} [serializable=false] See {@link CKEDITOR.dom.range#createBookmark}.
|
||||
* @returns {Array} Array of bookmarks.
|
||||
*/
|
||||
createBookmarks: function( serializable ) {
|
||||
var retval = [],
|
||||
bookmark;
|
||||
for ( var i = 0; i < this.length; i++ ) {
|
||||
retval.push( bookmark = this[ i ].createBookmark( serializable, true ) );
|
||||
|
||||
// Updating the container & offset values for ranges
|
||||
// that have been touched.
|
||||
for ( var j = i + 1; j < this.length; j++ ) {
|
||||
this[ j ] = updateDirtyRange( bookmark, this[ j ] );
|
||||
this[ j ] = updateDirtyRange( bookmark, this[ j ], true );
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
},
|
||||
|
||||
/**
|
||||
* Create "unobtrusive" bookmarks for all ranges. See {@link CKEDITOR.dom.range#createBookmark2}.
|
||||
*
|
||||
* @param {Boolean} [normalized=false] See {@link CKEDITOR.dom.range#createBookmark2}.
|
||||
* @returns {Array} Array of bookmarks.
|
||||
*/
|
||||
createBookmarks2: function( normalized ) {
|
||||
var bookmarks = [];
|
||||
|
||||
for ( var i = 0; i < this.length; i++ )
|
||||
bookmarks.push( this[ i ].createBookmark2( normalized ) );
|
||||
|
||||
return bookmarks;
|
||||
},
|
||||
|
||||
/**
|
||||
* Move each range in the list to the position specified by a list of bookmarks.
|
||||
*
|
||||
* @param {Array} bookmarks The list of bookmarks, each one matching a range in the list.
|
||||
*/
|
||||
moveToBookmarks: function( bookmarks ) {
|
||||
for ( var i = 0; i < this.length; i++ )
|
||||
this[ i ].moveToBookmark( bookmarks[ i ] );
|
||||
}
|
||||
};
|
||||
|
||||
// Update the specified range which has been mangled by previous insertion of
|
||||
// range bookmark nodes.(#3256)
|
||||
function updateDirtyRange( bookmark, dirtyRange, checkEnd ) {
|
||||
var serializable = bookmark.serializable,
|
||||
container = dirtyRange[ checkEnd ? 'endContainer' : 'startContainer' ],
|
||||
offset = checkEnd ? 'endOffset' : 'startOffset';
|
||||
|
||||
var bookmarkStart = serializable ? dirtyRange.document.getById( bookmark.startNode ) : bookmark.startNode;
|
||||
|
||||
var bookmarkEnd = serializable ? dirtyRange.document.getById( bookmark.endNode ) : bookmark.endNode;
|
||||
|
||||
if ( container.equals( bookmarkStart.getPrevious() ) ) {
|
||||
dirtyRange.startOffset = dirtyRange.startOffset - container.getLength() - bookmarkEnd.getPrevious().getLength();
|
||||
container = bookmarkEnd.getNext();
|
||||
} else if ( container.equals( bookmarkEnd.getPrevious() ) ) {
|
||||
dirtyRange.startOffset = dirtyRange.startOffset - container.getLength();
|
||||
container = bookmarkEnd.getNext();
|
||||
}
|
||||
|
||||
container.equals( bookmarkStart.getParent() ) && dirtyRange[ offset ]++;
|
||||
container.equals( bookmarkEnd.getParent() ) && dirtyRange[ offset ]++;
|
||||
|
||||
// Update and return this range.
|
||||
dirtyRange[ checkEnd ? 'endContainer' : 'startContainer' ] = container;
|
||||
return dirtyRange;
|
||||
}
|
||||
} )();
|
||||
|
||||
/**
|
||||
* (Virtual Class) Do not call this constructor. This class is not really part
|
||||
* of the API. It just describes the return type of {@link CKEDITOR.dom.rangeList#createIterator}.
|
||||
*
|
||||
* @class CKEDITOR.dom.rangeListIterator
|
||||
*/
|
||||
@@ -1,139 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileOverview Defines the {@link CKEDITOR.dom.text} class, which represents
|
||||
* a DOM text node.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a DOM text node.
|
||||
*
|
||||
* var nativeNode = document.createTextNode( 'Example' );
|
||||
* var text = CKEDITOR.dom.text( nativeNode );
|
||||
*
|
||||
* var text = CKEDITOR.dom.text( 'Example' );
|
||||
*
|
||||
* @class
|
||||
* @extends CKEDITOR.dom.node
|
||||
* @constructor Creates a text class instance.
|
||||
* @param {Object/String} text A native DOM text node or a string containing
|
||||
* the text to use to create a new text node.
|
||||
* @param {CKEDITOR.dom.document} [ownerDocument] The document that will contain
|
||||
* the node in case of new node creation. Defaults to the current document.
|
||||
*/
|
||||
CKEDITOR.dom.text = function( text, ownerDocument ) {
|
||||
if ( typeof text == 'string' )
|
||||
text = ( ownerDocument ? ownerDocument.$ : document ).createTextNode( text );
|
||||
|
||||
// Theoretically, we should call the base constructor here
|
||||
// (not CKEDITOR.dom.node though). But, IE doesn't support expando
|
||||
// properties on text node, so the features provided by domObject will not
|
||||
// work for text nodes (which is not a big issue for us).
|
||||
//
|
||||
// CKEDITOR.dom.domObject.call( this, element );
|
||||
|
||||
this.$ = text;
|
||||
};
|
||||
|
||||
CKEDITOR.dom.text.prototype = new CKEDITOR.dom.node();
|
||||
|
||||
CKEDITOR.tools.extend( CKEDITOR.dom.text.prototype, {
|
||||
/**
|
||||
* The node type. This is a constant value set to {@link CKEDITOR#NODE_TEXT}.
|
||||
*
|
||||
* @readonly
|
||||
* @property {Number} [=CKEDITOR.NODE_TEXT]
|
||||
*/
|
||||
type: CKEDITOR.NODE_TEXT,
|
||||
|
||||
/**
|
||||
* Gets length of node's value.
|
||||
*
|
||||
* @returns {Number}
|
||||
*/
|
||||
getLength: function() {
|
||||
return this.$.nodeValue.length;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets node's value.
|
||||
*
|
||||
* @returns {String}
|
||||
*/
|
||||
getText: function() {
|
||||
return this.$.nodeValue;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets node's value.
|
||||
*
|
||||
* @param {String} text
|
||||
*/
|
||||
setText: function( text ) {
|
||||
this.$.nodeValue = text;
|
||||
},
|
||||
|
||||
/**
|
||||
* Breaks this text node into two nodes at the specified offset,
|
||||
* keeping both in the tree as siblings. This node then only contains
|
||||
* all the content up to the offset point. A new text node, which is
|
||||
* inserted as the next sibling of this node, contains all the content
|
||||
* at and after the offset point. When the offset is equal to the
|
||||
* length of this node, the new node has no data.
|
||||
*
|
||||
* @param {Number} The position at which to split, starting from zero.
|
||||
* @returns {CKEDITOR.dom.text} The new text node.
|
||||
*/
|
||||
split: function( offset ) {
|
||||
|
||||
// Saved the children count and text length beforehand.
|
||||
var parent = this.$.parentNode,
|
||||
count = parent.childNodes.length,
|
||||
length = this.getLength();
|
||||
|
||||
var doc = this.getDocument();
|
||||
var retval = new CKEDITOR.dom.text( this.$.splitText( offset ), doc );
|
||||
|
||||
if ( parent.childNodes.length == count )
|
||||
{
|
||||
// If the offset is after the last char, IE creates the text node
|
||||
// on split, but don't include it into the DOM. So, we have to do
|
||||
// that manually here.
|
||||
if ( offset >= length )
|
||||
{
|
||||
retval = doc.createText( '' );
|
||||
retval.insertAfter( this );
|
||||
}
|
||||
else
|
||||
{
|
||||
// IE BUG: IE8+ does not update the childNodes array in DOM after splitText(),
|
||||
// we need to make some DOM changes to make it update. (#3436)
|
||||
var workaround = doc.createText( '' );
|
||||
workaround.insertAfter( retval );
|
||||
workaround.remove();
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
},
|
||||
|
||||
/**
|
||||
* Extracts characters from indexA up to but not including `indexB`.
|
||||
*
|
||||
* @param {Number} indexA An integer between `0` and one less than the
|
||||
* length of the text.
|
||||
* @param {Number} [indexB] An integer between `0` and the length of the
|
||||
* string. If omitted, extracts characters to the end of the text.
|
||||
*/
|
||||
substring: function( indexA, indexB ) {
|
||||
// We need the following check due to a Firefox bug
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=458886
|
||||
if ( typeof indexB != 'number' )
|
||||
return this.$.nodeValue.substr( indexA );
|
||||
else
|
||||
return this.$.nodeValue.substring( indexA, indexB );
|
||||
}
|
||||
} );
|
||||
@@ -1,616 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
( function() {
|
||||
// This function is to be called under a "walker" instance scope.
|
||||
function iterate( rtl, breakOnFalse ) {
|
||||
var range = this.range;
|
||||
|
||||
// Return null if we have reached the end.
|
||||
if ( this._.end )
|
||||
return null;
|
||||
|
||||
// This is the first call. Initialize it.
|
||||
if ( !this._.start ) {
|
||||
this._.start = 1;
|
||||
|
||||
// A collapsed range must return null at first call.
|
||||
if ( range.collapsed ) {
|
||||
this.end();
|
||||
return null;
|
||||
}
|
||||
|
||||
// Move outside of text node edges.
|
||||
range.optimize();
|
||||
}
|
||||
|
||||
var node,
|
||||
startCt = range.startContainer,
|
||||
endCt = range.endContainer,
|
||||
startOffset = range.startOffset,
|
||||
endOffset = range.endOffset,
|
||||
guard,
|
||||
userGuard = this.guard,
|
||||
type = this.type,
|
||||
getSourceNodeFn = ( rtl ? 'getPreviousSourceNode' : 'getNextSourceNode' );
|
||||
|
||||
// Create the LTR guard function, if necessary.
|
||||
if ( !rtl && !this._.guardLTR ) {
|
||||
// The node that stops walker from moving up.
|
||||
var limitLTR = endCt.type == CKEDITOR.NODE_ELEMENT ? endCt : endCt.getParent();
|
||||
|
||||
// The node that stops the walker from going to next.
|
||||
var blockerLTR = endCt.type == CKEDITOR.NODE_ELEMENT ? endCt.getChild( endOffset ) : endCt.getNext();
|
||||
|
||||
this._.guardLTR = function( node, movingOut ) {
|
||||
return ( ( !movingOut || !limitLTR.equals( node ) ) && ( !blockerLTR || !node.equals( blockerLTR ) ) && ( node.type != CKEDITOR.NODE_ELEMENT || !movingOut || !node.equals( range.root ) ) );
|
||||
};
|
||||
}
|
||||
|
||||
// Create the RTL guard function, if necessary.
|
||||
if ( rtl && !this._.guardRTL ) {
|
||||
// The node that stops walker from moving up.
|
||||
var limitRTL = startCt.type == CKEDITOR.NODE_ELEMENT ? startCt : startCt.getParent();
|
||||
|
||||
// The node that stops the walker from going to next.
|
||||
var blockerRTL = startCt.type == CKEDITOR.NODE_ELEMENT ? startOffset ? startCt.getChild( startOffset - 1 ) : null : startCt.getPrevious();
|
||||
|
||||
this._.guardRTL = function( node, movingOut ) {
|
||||
return ( ( !movingOut || !limitRTL.equals( node ) ) && ( !blockerRTL || !node.equals( blockerRTL ) ) && ( node.type != CKEDITOR.NODE_ELEMENT || !movingOut || !node.equals( range.root ) ) );
|
||||
};
|
||||
}
|
||||
|
||||
// Define which guard function to use.
|
||||
var stopGuard = rtl ? this._.guardRTL : this._.guardLTR;
|
||||
|
||||
// Make the user defined guard function participate in the process,
|
||||
// otherwise simply use the boundary guard.
|
||||
if ( userGuard ) {
|
||||
guard = function( node, movingOut ) {
|
||||
if ( stopGuard( node, movingOut ) === false )
|
||||
return false;
|
||||
|
||||
return userGuard( node, movingOut );
|
||||
};
|
||||
} else
|
||||
guard = stopGuard;
|
||||
|
||||
if ( this.current )
|
||||
node = this.current[ getSourceNodeFn ]( false, type, guard );
|
||||
else {
|
||||
// Get the first node to be returned.
|
||||
if ( rtl ) {
|
||||
node = endCt;
|
||||
|
||||
if ( node.type == CKEDITOR.NODE_ELEMENT ) {
|
||||
if ( endOffset > 0 )
|
||||
node = node.getChild( endOffset - 1 );
|
||||
else
|
||||
node = ( guard( node, true ) === false ) ? null : node.getPreviousSourceNode( true, type, guard );
|
||||
}
|
||||
} else {
|
||||
node = startCt;
|
||||
|
||||
if ( node.type == CKEDITOR.NODE_ELEMENT ) {
|
||||
if ( !( node = node.getChild( startOffset ) ) )
|
||||
node = ( guard( startCt, true ) === false ) ? null : startCt.getNextSourceNode( true, type, guard );
|
||||
}
|
||||
}
|
||||
|
||||
if ( node && guard( node ) === false )
|
||||
node = null;
|
||||
}
|
||||
|
||||
while ( node && !this._.end ) {
|
||||
this.current = node;
|
||||
|
||||
if ( !this.evaluator || this.evaluator( node ) !== false ) {
|
||||
if ( !breakOnFalse )
|
||||
return node;
|
||||
} else if ( breakOnFalse && this.evaluator )
|
||||
return false;
|
||||
|
||||
node = node[ getSourceNodeFn ]( false, type, guard );
|
||||
}
|
||||
|
||||
this.end();
|
||||
return this.current = null;
|
||||
}
|
||||
|
||||
function iterateToLast( rtl ) {
|
||||
var node,
|
||||
last = null;
|
||||
|
||||
while ( ( node = iterate.call( this, rtl ) ) )
|
||||
last = node;
|
||||
|
||||
return last;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility class to "walk" the DOM inside a range boundaries. If the
|
||||
* range starts or ends in the middle of the text node this node will
|
||||
* be included as a whole. Outside changes to the range may break the walker.
|
||||
*
|
||||
* The walker may return nodes that are not totaly included into the
|
||||
* range boundaires. Let's take the following range representation,
|
||||
* where the square brackets indicate the boundaries:
|
||||
*
|
||||
* [<p>Some <b>sample] text</b>
|
||||
*
|
||||
* While walking forward into the above range, the following nodes are
|
||||
* returned: `<p>`, `"Some "`, `<b>` and `"sample"`. Going
|
||||
* backwards instead we have: `"sample"` and `"Some "`. So note that the
|
||||
* walker always returns nodes when "entering" them, but not when
|
||||
* "leaving" them. The guard function is instead called both when
|
||||
* entering and leaving nodes.
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
CKEDITOR.dom.walker = CKEDITOR.tools.createClass( {
|
||||
/**
|
||||
* Creates a walker class instance.
|
||||
*
|
||||
* @constructor
|
||||
* @param {CKEDITOR.dom.range} range The range within which walk.
|
||||
*/
|
||||
$: function( range ) {
|
||||
this.range = range;
|
||||
|
||||
/**
|
||||
* A function executed for every matched node, to check whether
|
||||
* it's to be considered into the walk or not. If not provided, all
|
||||
* matched nodes are considered good.
|
||||
*
|
||||
* If the function returns `false` the node is ignored.
|
||||
*
|
||||
* @property {Function} evaluator
|
||||
*/
|
||||
// this.evaluator = null;
|
||||
|
||||
/**
|
||||
* A function executed for every node the walk pass by to check
|
||||
* whether the walk is to be finished. It's called when both
|
||||
* entering and exiting nodes, as well as for the matched nodes.
|
||||
*
|
||||
* If this function returns `false`, the walking ends and no more
|
||||
* nodes are evaluated.
|
||||
|
||||
* @property {Function} guard
|
||||
*/
|
||||
// this.guard = null;
|
||||
|
||||
/** @private */
|
||||
this._ = {};
|
||||
},
|
||||
|
||||
// statics :
|
||||
// {
|
||||
// /* Creates a CKEDITOR.dom.walker instance to walk inside DOM boundaries set by nodes.
|
||||
// * @param {CKEDITOR.dom.node} startNode The node from wich the walk
|
||||
// * will start.
|
||||
// * @param {CKEDITOR.dom.node} [endNode] The last node to be considered
|
||||
// * in the walk. No more nodes are retrieved after touching or
|
||||
// * passing it. If not provided, the walker stops at the
|
||||
// * <body> closing boundary.
|
||||
// * @returns {CKEDITOR.dom.walker} A DOM walker for the nodes between the
|
||||
// * provided nodes.
|
||||
// */
|
||||
// createOnNodes : function( startNode, endNode, startInclusive, endInclusive )
|
||||
// {
|
||||
// var range = new CKEDITOR.dom.range();
|
||||
// if ( startNode )
|
||||
// range.setStartAt( startNode, startInclusive ? CKEDITOR.POSITION_BEFORE_START : CKEDITOR.POSITION_AFTER_END ) ;
|
||||
// else
|
||||
// range.setStartAt( startNode.getDocument().getBody(), CKEDITOR.POSITION_AFTER_START ) ;
|
||||
//
|
||||
// if ( endNode )
|
||||
// range.setEndAt( endNode, endInclusive ? CKEDITOR.POSITION_AFTER_END : CKEDITOR.POSITION_BEFORE_START ) ;
|
||||
// else
|
||||
// range.setEndAt( startNode.getDocument().getBody(), CKEDITOR.POSITION_BEFORE_END ) ;
|
||||
//
|
||||
// return new CKEDITOR.dom.walker( range );
|
||||
// }
|
||||
// },
|
||||
//
|
||||
proto: {
|
||||
/**
|
||||
* Stops walking. No more nodes are retrieved if this function gets called.
|
||||
*/
|
||||
end: function() {
|
||||
this._.end = 1;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieves the next node (at right).
|
||||
*
|
||||
* @returns {CKEDITOR.dom.node} The next node or null if no more
|
||||
* nodes are available.
|
||||
*/
|
||||
next: function() {
|
||||
return iterate.call( this );
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieves the previous node (at left).
|
||||
*
|
||||
* @returns {CKEDITOR.dom.node} The previous node or null if no more
|
||||
* nodes are available.
|
||||
*/
|
||||
previous: function() {
|
||||
return iterate.call( this, 1 );
|
||||
},
|
||||
|
||||
/**
|
||||
* Check all nodes at right, executing the evaluation function.
|
||||
*
|
||||
* @returns {Boolean} `false` if the evaluator function returned
|
||||
* `false` for any of the matched nodes. Otherwise `true`.
|
||||
*/
|
||||
checkForward: function() {
|
||||
return iterate.call( this, 0, 1 ) !== false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Check all nodes at left, executing the evaluation function.
|
||||
*
|
||||
* @returns {Boolean} `false` if the evaluator function returned
|
||||
* `false` for any of the matched nodes. Otherwise `true`.
|
||||
*/
|
||||
checkBackward: function() {
|
||||
return iterate.call( this, 1, 1 ) !== false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Executes a full walk forward (to the right), until no more nodes
|
||||
* are available, returning the last valid node.
|
||||
*
|
||||
* @returns {CKEDITOR.dom.node} The last node at the right or null
|
||||
* if no valid nodes are available.
|
||||
*/
|
||||
lastForward: function() {
|
||||
return iterateToLast.call( this );
|
||||
},
|
||||
|
||||
/**
|
||||
* Executes a full walk backwards (to the left), until no more nodes
|
||||
* are available, returning the last valid node.
|
||||
*
|
||||
* @returns {CKEDITOR.dom.node} The last node at the left or null
|
||||
* if no valid nodes are available.
|
||||
*/
|
||||
lastBackward: function() {
|
||||
return iterateToLast.call( this, 1 );
|
||||
},
|
||||
|
||||
/**
|
||||
* Resets walker.
|
||||
*/
|
||||
reset: function() {
|
||||
delete this.current;
|
||||
this._ = {};
|
||||
}
|
||||
|
||||
}
|
||||
} );
|
||||
|
||||
// Anything whose display computed style is block, list-item, table,
|
||||
// table-row-group, table-header-group, table-footer-group, table-row,
|
||||
// table-column-group, table-column, table-cell, table-caption, or whose node
|
||||
// name is hr, br (when enterMode is br only) is a block boundary.
|
||||
var blockBoundaryDisplayMatch = { block: 1, 'list-item': 1, table: 1, 'table-row-group': 1,
|
||||
'table-header-group': 1, 'table-footer-group': 1, 'table-row': 1, 'table-column-group': 1,
|
||||
'table-column': 1, 'table-cell': 1, 'table-caption': 1 },
|
||||
outOfFlowPositions = { absolute: 1, fixed: 1 };
|
||||
|
||||
/**
|
||||
* Checks whether element is displayed as a block.
|
||||
*
|
||||
* @member CKEDITOR.dom.element
|
||||
* @param [customNodeNames] Custom list of nodes which will extend
|
||||
* default {@link CKEDITOR.dtd#$block} list.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
CKEDITOR.dom.element.prototype.isBlockBoundary = function( customNodeNames ) {
|
||||
// Whether element is in normal page flow. Floated or positioned elements are out of page flow.
|
||||
// Don't consider floated or positioned formatting as block boundary, fall back to dtd check in that case. (#6297)
|
||||
var inPageFlow = this.getComputedStyle( 'float' ) == 'none' && !( this.getComputedStyle( 'position' ) in outOfFlowPositions );
|
||||
|
||||
if ( inPageFlow && blockBoundaryDisplayMatch[ this.getComputedStyle( 'display' ) ] )
|
||||
return true;
|
||||
|
||||
// Either in $block or in customNodeNames if defined.
|
||||
return !!( this.is( CKEDITOR.dtd.$block ) || customNodeNames && this.is( customNodeNames ) );
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a function which checks whether the node is a block boundary.
|
||||
* See {@link CKEDITOR.dom.element#isBlockBoundary}.
|
||||
*
|
||||
* @static
|
||||
* @param customNodeNames
|
||||
* @returns {Function}
|
||||
*/
|
||||
CKEDITOR.dom.walker.blockBoundary = function( customNodeNames ) {
|
||||
return function( node, type ) {
|
||||
return !( node.type == CKEDITOR.NODE_ELEMENT && node.isBlockBoundary( customNodeNames ) );
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @todo
|
||||
*/
|
||||
CKEDITOR.dom.walker.listItemBoundary = function() {
|
||||
return this.blockBoundary( { br: 1 } );
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a function which checks whether the node is a bookmark node OR bookmark node
|
||||
* inner contents.
|
||||
*
|
||||
* @static
|
||||
* @param {Boolean} [contentOnly=false] Whether only test against the text content of
|
||||
* bookmark node instead of the element itself (default).
|
||||
* @param {Boolean} [isReject=false] Whether should return `false` for the bookmark
|
||||
* node instead of `true` (default).
|
||||
* @returns {Function}
|
||||
*/
|
||||
CKEDITOR.dom.walker.bookmark = function( contentOnly, isReject ) {
|
||||
function isBookmarkNode( node ) {
|
||||
return ( node && node.getName && node.getName() == 'span' && node.data( 'cke-bookmark' ) );
|
||||
}
|
||||
|
||||
return function( node ) {
|
||||
var isBookmark, parent;
|
||||
// Is bookmark inner text node?
|
||||
isBookmark = ( node && node.type != CKEDITOR.NODE_ELEMENT && ( parent = node.getParent() ) && isBookmarkNode( parent ) );
|
||||
// Is bookmark node?
|
||||
isBookmark = contentOnly ? isBookmark : isBookmark || isBookmarkNode( node );
|
||||
return !!( isReject ^ isBookmark );
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a function which checks whether the node is a text node containing only whitespaces characters.
|
||||
*
|
||||
* @static
|
||||
* @param {Boolean} [isReject=false]
|
||||
* @returns {Function}
|
||||
*/
|
||||
CKEDITOR.dom.walker.whitespaces = function( isReject ) {
|
||||
return function( node ) {
|
||||
var isWhitespace;
|
||||
if ( node && node.type == CKEDITOR.NODE_TEXT ) {
|
||||
// whitespace, as well as the text cursor filler node we used in Webkit. (#9384)
|
||||
isWhitespace = !CKEDITOR.tools.trim( node.getText() ) ||
|
||||
CKEDITOR.env.webkit && node.getText() == '\u200b';
|
||||
}
|
||||
|
||||
return !!( isReject ^ isWhitespace );
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a function which checks whether the node is invisible in wysiwyg mode.
|
||||
*
|
||||
* @static
|
||||
* @param {Boolean} [isReject=false]
|
||||
* @returns {Function}
|
||||
*/
|
||||
CKEDITOR.dom.walker.invisible = function( isReject ) {
|
||||
var whitespace = CKEDITOR.dom.walker.whitespaces();
|
||||
return function( node ) {
|
||||
var invisible;
|
||||
|
||||
if ( whitespace( node ) )
|
||||
invisible = 1;
|
||||
else {
|
||||
// Visibility should be checked on element.
|
||||
if ( node.type == CKEDITOR.NODE_TEXT )
|
||||
node = node.getParent();
|
||||
|
||||
// Nodes that take no spaces in wysiwyg:
|
||||
// 1. White-spaces but not including NBSP;
|
||||
// 2. Empty inline elements, e.g. <b></b> we're checking here
|
||||
// 'offsetHeight' instead of 'offsetWidth' for properly excluding
|
||||
// all sorts of empty paragraph, e.g. <br />.
|
||||
invisible = !node.$.offsetHeight;
|
||||
}
|
||||
|
||||
return !!( isReject ^ invisible );
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a function which checks whether node's type is equal to passed one.
|
||||
*
|
||||
* @static
|
||||
* @param {Number} type
|
||||
* @param {Boolean} [isReject=false]
|
||||
* @returns {Function}
|
||||
*/
|
||||
CKEDITOR.dom.walker.nodeType = function( type, isReject ) {
|
||||
return function( node ) {
|
||||
return !!( isReject ^ ( node.type == type ) );
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a function which checks whether node is a bogus (filler) node from
|
||||
* contenteditable element's point of view.
|
||||
*
|
||||
* @static
|
||||
* @param {Boolean} [isReject=false]
|
||||
* @returns {Function}
|
||||
*/
|
||||
CKEDITOR.dom.walker.bogus = function( isReject ) {
|
||||
function nonEmpty( node ) {
|
||||
return !isWhitespaces( node ) && !isBookmark( node );
|
||||
}
|
||||
|
||||
return function( node ) {
|
||||
var isBogus = CKEDITOR.env.needsBrFiller ? node.is && node.is( 'br' ) : node.getText && tailNbspRegex.test( node.getText() );
|
||||
|
||||
if ( isBogus ) {
|
||||
var parent = node.getParent(),
|
||||
next = node.getNext( nonEmpty );
|
||||
|
||||
isBogus = parent.isBlockBoundary() && ( !next || next.type == CKEDITOR.NODE_ELEMENT && next.isBlockBoundary() );
|
||||
}
|
||||
|
||||
return !!( isReject ^ isBogus );
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a function which checks whether node is a temporary element
|
||||
* (element with `data-cke-temp` attribute) or its child.
|
||||
*
|
||||
* @since 4.3
|
||||
* @static
|
||||
* @param {Boolean} [isReject=false] Whether should return `false` for the
|
||||
* temporary element instead of `true` (default).
|
||||
* @returns {Function}
|
||||
*/
|
||||
CKEDITOR.dom.walker.temp = function( isReject ) {
|
||||
return function( node ) {
|
||||
if ( node.type != CKEDITOR.NODE_ELEMENT )
|
||||
node = node.getParent();
|
||||
|
||||
var isTemp = node && node.hasAttribute( 'data-cke-temp' );
|
||||
|
||||
return !!( isReject ^ isTemp );
|
||||
};
|
||||
};
|
||||
|
||||
var tailNbspRegex = /^[\t\r\n ]*(?: |\xa0)$/,
|
||||
isWhitespaces = CKEDITOR.dom.walker.whitespaces(),
|
||||
isBookmark = CKEDITOR.dom.walker.bookmark(),
|
||||
isTemp = CKEDITOR.dom.walker.temp(),
|
||||
toSkip = function( node ) {
|
||||
return isBookmark( node ) ||
|
||||
isWhitespaces( node ) ||
|
||||
node.type == CKEDITOR.NODE_ELEMENT && node.is( CKEDITOR.dtd.$inline ) && !node.is( CKEDITOR.dtd.$empty );
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a function which checks whether node should be ignored in terms of "editability".
|
||||
*
|
||||
* This includes:
|
||||
*
|
||||
* * whitespaces (see {@link CKEDITOR.dom.walker#whitespaces}),
|
||||
* * bookmarks (see {@link CKEDITOR.dom.walker#bookmark}),
|
||||
* * temporary elements (see {@link CKEDITOR.dom.walker#temp}).
|
||||
*
|
||||
* @since 4.3
|
||||
* @static
|
||||
* @param {Boolean} [isReject=false] Whether should return `false` for the
|
||||
* ignored element instead of `true` (default).
|
||||
* @returns {Function}
|
||||
*/
|
||||
CKEDITOR.dom.walker.ignored = function( isReject ) {
|
||||
return function( node ) {
|
||||
var isIgnored = isWhitespaces( node ) || isBookmark( node ) || isTemp( node );
|
||||
|
||||
return !!( isReject ^ isIgnored );
|
||||
};
|
||||
};
|
||||
|
||||
var isIgnored = CKEDITOR.dom.walker.ignored();
|
||||
|
||||
function isEmpty( node ) {
|
||||
var i = 0,
|
||||
l = node.getChildCount();
|
||||
|
||||
for ( ; i < l; ++i ) {
|
||||
if ( !isIgnored( node.getChild( i ) ) )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function filterTextContainers( dtd ) {
|
||||
var hash = {},
|
||||
name;
|
||||
|
||||
for ( name in dtd ) {
|
||||
if ( CKEDITOR.dtd[ name ][ '#' ] )
|
||||
hash[ name ] = 1;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
// Block elements which can contain text nodes (without ul, ol, dl, etc.).
|
||||
var dtdTextBlock = filterTextContainers( CKEDITOR.dtd.$block );
|
||||
|
||||
function isEditable( node ) {
|
||||
// Skip temporary elements, bookmarks and whitespaces.
|
||||
if ( isIgnored( node ) )
|
||||
return false;
|
||||
|
||||
if ( node.type == CKEDITOR.NODE_TEXT )
|
||||
return true;
|
||||
|
||||
if ( node.type == CKEDITOR.NODE_ELEMENT ) {
|
||||
// All inline and non-editable elements are valid editable places.
|
||||
// Note: non-editable block has to be treated differently (should be selected entirely).
|
||||
if ( node.is( CKEDITOR.dtd.$inline ) || node.getAttribute( 'contenteditable' ) == 'false' )
|
||||
return true;
|
||||
|
||||
// Empty blocks are editable on IE.
|
||||
if ( !CKEDITOR.env.needsBrFiller && node.is( dtdTextBlock ) && isEmpty( node ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
// Skip all other nodes.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a function which checks whether node can be a container or a sibling
|
||||
* of selection end.
|
||||
*
|
||||
* This includes:
|
||||
*
|
||||
* * text nodes (but not whitespaces),
|
||||
* * inline elements,
|
||||
* * non-editable blocks (special case - such blocks cannot be containers nor
|
||||
* siblings, they need to be selected entirely),
|
||||
* * empty blocks which can contain text (IE only).
|
||||
*
|
||||
* @since 4.3
|
||||
* @static
|
||||
* @param {Boolean} [isReject=false] Whether should return `false` for the
|
||||
* ignored element instead of `true` (default).
|
||||
* @returns {Function}
|
||||
*/
|
||||
CKEDITOR.dom.walker.editable = function( isReject ) {
|
||||
return function( node ) {
|
||||
return !!( isReject ^ isEditable( node ) );
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if there's a filler node at the end of an element, and returns it.
|
||||
*
|
||||
* @member CKEDITOR.dom.element
|
||||
* @returns {CKEDITOR.dom.node/Boolean} Bogus node or `false`.
|
||||
*/
|
||||
CKEDITOR.dom.element.prototype.getBogus = function() {
|
||||
// Bogus are not always at the end, e.g. <p><a>text<br /></a></p> (#7070).
|
||||
var tail = this;
|
||||
do {
|
||||
tail = tail.getPreviousSourceNode();
|
||||
}
|
||||
while ( toSkip( tail ) )
|
||||
|
||||
if ( tail && ( CKEDITOR.env.needsBrFiller ? tail.is && tail.is( 'br' ) : tail.getText && tailNbspRegex.test( tail.getText() ) ) )
|
||||
return tail;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
} )();
|
||||
@@ -1,95 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileOverview Defines the {@link CKEDITOR.dom.document} class, which
|
||||
* represents a DOM document.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a DOM window.
|
||||
*
|
||||
* var document = new CKEDITOR.dom.window( window );
|
||||
*
|
||||
* @class
|
||||
* @extends CKEDITOR.dom.domObject
|
||||
* @constructor Creates a window class instance.
|
||||
* @param {Object} domWindow A native DOM window.
|
||||
*/
|
||||
CKEDITOR.dom.window = function( domWindow ) {
|
||||
CKEDITOR.dom.domObject.call( this, domWindow );
|
||||
};
|
||||
|
||||
CKEDITOR.dom.window.prototype = new CKEDITOR.dom.domObject();
|
||||
|
||||
CKEDITOR.tools.extend( CKEDITOR.dom.window.prototype, {
|
||||
/**
|
||||
* Moves the selection focus to this window.
|
||||
*
|
||||
* var win = new CKEDITOR.dom.window( window );
|
||||
* win.focus();
|
||||
*/
|
||||
focus: function() {
|
||||
this.$.focus();
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the width and height of this window's viewable area.
|
||||
*
|
||||
* var win = new CKEDITOR.dom.window( window );
|
||||
* var size = win.getViewPaneSize();
|
||||
* alert( size.width );
|
||||
* alert( size.height );
|
||||
*
|
||||
* @returns {Object} An object with the `width` and `height`
|
||||
* properties containing the size.
|
||||
*/
|
||||
getViewPaneSize: function() {
|
||||
var doc = this.$.document,
|
||||
stdMode = doc.compatMode == 'CSS1Compat';
|
||||
return {
|
||||
width: ( stdMode ? doc.documentElement.clientWidth : doc.body.clientWidth ) || 0,
|
||||
height: ( stdMode ? doc.documentElement.clientHeight : doc.body.clientHeight ) || 0
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the current position of the window's scroll.
|
||||
*
|
||||
* var win = new CKEDITOR.dom.window( window );
|
||||
* var pos = win.getScrollPosition();
|
||||
* alert( pos.x );
|
||||
* alert( pos.y );
|
||||
*
|
||||
* @returns {Object} An object with the `x` and `y` properties
|
||||
* containing the scroll position.
|
||||
*/
|
||||
getScrollPosition: function() {
|
||||
var $ = this.$;
|
||||
|
||||
if ( 'pageXOffset' in $ ) {
|
||||
return {
|
||||
x: $.pageXOffset || 0,
|
||||
y: $.pageYOffset || 0
|
||||
};
|
||||
} else {
|
||||
var doc = $.document;
|
||||
return {
|
||||
x: doc.documentElement.scrollLeft || doc.body.scrollLeft || 0,
|
||||
y: doc.documentElement.scrollTop || doc.body.scrollTop || 0
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the frame element containing this window context.
|
||||
*
|
||||
* @returns {CKEDITOR.dom.element} The frame element or `null` if not in a frame context.
|
||||
*/
|
||||
getFrame: function() {
|
||||
var iframe = this.$.frameElement;
|
||||
return iframe ? new CKEDITOR.dom.element.get( iframe ) : null;
|
||||
}
|
||||
} );
|
||||
@@ -1,328 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileOverview Defines the {@link CKEDITOR.dtd} object, which holds the DTD
|
||||
* mapping for XHTML 1.0 Transitional. This file was automatically
|
||||
* generated from the file: xhtml1-transitional.dtd.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Holds and object representation of the HTML DTD to be used by the
|
||||
* editor in its internal operations.
|
||||
*
|
||||
* Each element in the DTD is represented by a property in this object. Each
|
||||
* property contains the list of elements that can be contained by the element.
|
||||
* Text is represented by the `#` property.
|
||||
*
|
||||
* Several special grouping properties are also available. Their names start
|
||||
* with the `$` character.
|
||||
*
|
||||
* // Check if <div> can be contained in a <p> element.
|
||||
* alert( !!CKEDITOR.dtd[ 'p' ][ 'div' ] ); // false
|
||||
*
|
||||
* // Check if <p> can be contained in a <div> element.
|
||||
* alert( !!CKEDITOR.dtd[ 'div' ][ 'p' ] ); // true
|
||||
*
|
||||
* // Check if <p> is a block element.
|
||||
* alert( !!CKEDITOR.dtd.$block[ 'p' ] ); // true
|
||||
*
|
||||
* @class CKEDITOR.dtd
|
||||
* @singleton
|
||||
*/
|
||||
CKEDITOR.dtd = ( function() {
|
||||
'use strict';
|
||||
|
||||
var X = CKEDITOR.tools.extend,
|
||||
// Subtraction rest of sets, from the first set.
|
||||
Y = function( source, removed ) {
|
||||
var substracted = CKEDITOR.tools.clone( source );
|
||||
for ( var i = 1; i < arguments.length; i++ ) {
|
||||
removed = arguments[ i ];
|
||||
for ( var name in removed )
|
||||
delete substracted[ name ];
|
||||
}
|
||||
return substracted;
|
||||
};
|
||||
|
||||
// Phrasing elements.
|
||||
// P = { a: 1, em: 1, strong: 1, small: 1, abbr: 1, dfn: 1, i: 1, b: 1, s: 1,
|
||||
// u: 1, code: 1, 'var': 1, samp: 1, kbd: 1, sup: 1, sub: 1, q: 1, cite: 1,
|
||||
// span: 1, bdo: 1, bdi: 1, br: 1, wbr: 1, ins: 1, del: 1, img: 1, embed: 1,
|
||||
// object: 1, iframe: 1, map: 1, area: 1, script: 1, noscript: 1, ruby: 1,
|
||||
// video: 1, audio: 1, input: 1, textarea: 1, select: 1, button: 1, label: 1,
|
||||
// output: 1, keygen: 1, progress: 1, command: 1, canvas: 1, time: 1,
|
||||
// meter: 1, detalist: 1 },
|
||||
|
||||
// Flow elements.
|
||||
// F = { a: 1, p: 1, hr: 1, pre: 1, ul: 1, ol: 1, dl: 1, div: 1, h1: 1, h2: 1,
|
||||
// h3: 1, h4: 1, h5: 1, h6: 1, hgroup: 1, address: 1, blockquote: 1, ins: 1,
|
||||
// del: 1, object: 1, map: 1, noscript: 1, section: 1, nav: 1, article: 1,
|
||||
// aside: 1, header: 1, footer: 1, video: 1, audio: 1, figure: 1, table: 1,
|
||||
// form: 1, fieldset: 1, menu: 1, canvas: 1, details:1 },
|
||||
|
||||
// Text can be everywhere.
|
||||
// X( P, T );
|
||||
// Flow elements set consists of phrasing elements set.
|
||||
// X( F, P );
|
||||
|
||||
var P = {}, F = {},
|
||||
// Intersection of flow elements set and phrasing elements set.
|
||||
PF = { a: 1, abbr: 1, area: 1, audio: 1, b: 1, bdi: 1, bdo: 1, br: 1, button: 1, canvas: 1, cite: 1,
|
||||
code: 1, command: 1, datalist: 1, del: 1, dfn: 1, em: 1, embed: 1, i: 1, iframe: 1, img: 1,
|
||||
input: 1, ins: 1, kbd: 1, keygen: 1, label: 1, map: 1, mark: 1, meter: 1, noscript: 1, object: 1,
|
||||
output: 1, progress: 1, q: 1, ruby: 1, s: 1, samp: 1, script: 1, select: 1, small: 1, span: 1,
|
||||
strong: 1, sub: 1, sup: 1, textarea: 1, time: 1, u: 1, 'var': 1, video: 1, wbr: 1 },
|
||||
// F - PF (Flow Only).
|
||||
FO = { address: 1, article: 1, aside: 1, blockquote: 1, details: 1, div: 1, dl: 1, fieldset: 1,
|
||||
figure: 1, footer: 1, form: 1, h1: 1, h2: 1, h3: 1, h4: 1, h5: 1, h6: 1, header: 1, hgroup: 1,
|
||||
hr: 1, menu: 1, nav: 1, ol: 1, p: 1, pre: 1, section: 1, table: 1, ul: 1 },
|
||||
// Metadata elements.
|
||||
M = { command: 1, link: 1, meta: 1, noscript: 1, script: 1, style: 1 },
|
||||
// Empty.
|
||||
E = {},
|
||||
// Text.
|
||||
T = { '#': 1 },
|
||||
|
||||
// Deprecated phrasing elements.
|
||||
DP = { acronym: 1, applet: 1, basefont: 1, big: 1, font: 1, isindex: 1, strike: 1, style: 1, tt: 1 }, // TODO remove "style".
|
||||
// Deprecated flow only elements.
|
||||
DFO = { center: 1, dir: 1, noframes: 1 };
|
||||
|
||||
// Phrasing elements := PF + T + DP
|
||||
X( P, PF, T, DP );
|
||||
// Flow elements := FO + P + DFO
|
||||
X( F, FO, P, DFO );
|
||||
|
||||
var dtd = {
|
||||
a: Y( P, { a: 1, button: 1 } ), // Treat as normal inline element (not a transparent one).
|
||||
abbr: P,
|
||||
address: F,
|
||||
area: E,
|
||||
article: X( { style: 1 }, F ),
|
||||
aside: X( { style: 1 }, F ),
|
||||
audio: X( { source: 1, track: 1 }, F ),
|
||||
b: P,
|
||||
base: E,
|
||||
bdi: P,
|
||||
bdo: P,
|
||||
blockquote: F,
|
||||
body: F,
|
||||
br: E,
|
||||
button: Y( P, { a: 1, button: 1 } ),
|
||||
canvas: P, // Treat as normal inline element (not a transparent one).
|
||||
caption: F,
|
||||
cite: P,
|
||||
code: P,
|
||||
col: E,
|
||||
colgroup: { col: 1 },
|
||||
command: E,
|
||||
datalist: X( { option: 1 }, P ),
|
||||
dd: F,
|
||||
del: P, // Treat as normal inline element (not a transparent one).
|
||||
details: X( { summary: 1 }, F ),
|
||||
dfn: P,
|
||||
div: X( { style: 1 }, F ),
|
||||
dl: { dt: 1, dd: 1 },
|
||||
dt: F,
|
||||
em: P,
|
||||
embed: E,
|
||||
fieldset: X( { legend: 1 }, F ),
|
||||
figcaption: F,
|
||||
figure: X( { figcaption: 1 }, F ),
|
||||
footer: F,
|
||||
form: F,
|
||||
h1: P,
|
||||
h2: P,
|
||||
h3: P,
|
||||
h4: P,
|
||||
h5: P,
|
||||
h6: P,
|
||||
head: X( { title: 1, base: 1 }, M ),
|
||||
header: F,
|
||||
hgroup: { h1: 1, h2: 1, h3: 1, h4: 1, h5: 1, h6: 1 },
|
||||
hr: E,
|
||||
html: X( { head: 1, body: 1 }, F, M ), // Head and body are optional...
|
||||
i: P,
|
||||
iframe: T,
|
||||
img: E,
|
||||
input: E,
|
||||
ins: P, // Treat as normal inline element (not a transparent one).
|
||||
kbd: P,
|
||||
keygen: E,
|
||||
label: P,
|
||||
legend: P,
|
||||
li: F,
|
||||
link: E,
|
||||
map: F,
|
||||
mark: P, // Treat as normal inline element (not a transparent one).
|
||||
menu: X( { li: 1 }, F ),
|
||||
meta: E,
|
||||
meter: Y( P, { meter: 1 } ),
|
||||
nav: F,
|
||||
noscript: X( { link: 1, meta: 1, style: 1 }, P ), // Treat as normal inline element (not a transparent one).
|
||||
object: X( { param: 1 }, P ), // Treat as normal inline element (not a transparent one).
|
||||
ol: { li: 1 },
|
||||
optgroup: { option: 1 },
|
||||
option: T,
|
||||
output: P,
|
||||
p: P,
|
||||
param: E,
|
||||
pre: P,
|
||||
progress: Y( P, { progress: 1 } ),
|
||||
q: P,
|
||||
rp: P,
|
||||
rt: P,
|
||||
ruby: X( { rp: 1, rt: 1 }, P ),
|
||||
s: P,
|
||||
samp: P,
|
||||
script: T,
|
||||
section: X( { style: 1 }, F ),
|
||||
select: { optgroup: 1, option: 1 },
|
||||
small: P,
|
||||
source: E,
|
||||
span: P,
|
||||
strong: P,
|
||||
style: T,
|
||||
sub: P,
|
||||
summary: P,
|
||||
sup: P,
|
||||
table: { caption: 1, colgroup: 1, thead: 1, tfoot: 1, tbody: 1, tr: 1 },
|
||||
tbody: { tr: 1 },
|
||||
td: F,
|
||||
textarea: T,
|
||||
tfoot: { tr: 1 },
|
||||
th: F,
|
||||
thead: { tr: 1 },
|
||||
time: Y( P, { time: 1 } ),
|
||||
title: T,
|
||||
tr: { th: 1, td: 1 },
|
||||
track: E,
|
||||
u: P,
|
||||
ul: { li: 1 },
|
||||
'var': P,
|
||||
video: X( { source: 1, track: 1 }, F ),
|
||||
wbr: E,
|
||||
|
||||
// Deprecated tags.
|
||||
acronym: P,
|
||||
applet: X( { param: 1 }, F ),
|
||||
basefont: E,
|
||||
big: P,
|
||||
center: F,
|
||||
dialog: E,
|
||||
dir: { li: 1 },
|
||||
font: P,
|
||||
isindex: E,
|
||||
noframes: F,
|
||||
strike: P,
|
||||
tt: P
|
||||
};
|
||||
|
||||
X( dtd, {
|
||||
/**
|
||||
* List of block elements, like `<p>` or `<div>`.
|
||||
*/
|
||||
$block: X( { audio: 1, dd: 1, dt: 1, figcaption: 1, li: 1, video: 1 }, FO, DFO ),
|
||||
|
||||
/**
|
||||
* List of elements that contain other blocks, in which block-level operations should be limited,
|
||||
* this property is not intended to be checked directly, use {@link CKEDITOR.dom.elementPath#blockLimit} instead.
|
||||
*
|
||||
* Some examples of editor behaviors that are impacted by block limits:
|
||||
*
|
||||
* * Enter key never split a block-limit element;
|
||||
* * Style application is constraint by the block limit of the current selection.
|
||||
* * Pasted html will be inserted into the block limit of the current selection.
|
||||
*
|
||||
* **Note:** As an exception `<li>` is not considered as a block limit, as it's generally used as a text block.
|
||||
*/
|
||||
$blockLimit: { article: 1, aside: 1, audio: 1, body: 1, caption: 1, details: 1, dir: 1, div: 1, dl: 1,
|
||||
fieldset: 1, figcaption: 1, figure: 1, footer: 1, form: 1, header: 1, hgroup: 1, menu: 1, nav: 1,
|
||||
ol: 1, section: 1, table: 1, td: 1, th: 1, tr: 1, ul: 1, video: 1 },
|
||||
|
||||
/**
|
||||
* List of elements that contain character data.
|
||||
*/
|
||||
$cdata: { script: 1, style: 1 },
|
||||
|
||||
/**
|
||||
* List of elements that are accepted as inline editing hosts.
|
||||
*/
|
||||
$editable: { address: 1, article: 1, aside: 1, blockquote: 1, body: 1, details: 1, div: 1, fieldset: 1,
|
||||
figcaption: 1, footer: 1, form: 1, h1: 1, h2: 1, h3: 1, h4: 1, h5: 1, h6: 1, header: 1, hgroup: 1,
|
||||
nav: 1, p: 1, pre: 1, section: 1 },
|
||||
|
||||
/**
|
||||
* List of empty (self-closing) elements, like `<br>` or `<img>`.
|
||||
*/
|
||||
$empty: { area: 1, base: 1, basefont: 1, br: 1, col: 1, command: 1, dialog: 1, embed: 1, hr: 1, img: 1,
|
||||
input: 1, isindex: 1, keygen: 1, link: 1, meta: 1, param: 1, source: 1, track: 1, wbr: 1 },
|
||||
|
||||
/**
|
||||
* List of inline (`<span>` like) elements.
|
||||
*/
|
||||
$inline: P,
|
||||
|
||||
/**
|
||||
* List of list root elements.
|
||||
*/
|
||||
$list: { dl: 1, ol: 1, ul: 1 },
|
||||
|
||||
/**
|
||||
* List of list item elements, like `<li>` or `<dd>`.
|
||||
*/
|
||||
$listItem: { dd: 1, dt: 1, li: 1 },
|
||||
|
||||
/**
|
||||
* List of elements which may live outside body.
|
||||
*/
|
||||
$nonBodyContent: X( { body: 1, head: 1, html: 1 }, dtd.head ),
|
||||
|
||||
/**
|
||||
* Elements that accept text nodes, but are not possible to edit into the browser.
|
||||
*/
|
||||
$nonEditable: { applet: 1, audio: 1, button: 1, embed: 1, iframe: 1, map: 1, object: 1, option: 1,
|
||||
param: 1, script: 1, textarea: 1, video: 1 },
|
||||
|
||||
/**
|
||||
* Elements that are considered objects, therefore selected as a whole in the editor.
|
||||
*/
|
||||
$object: { applet: 1, audio: 1, button: 1, hr: 1, iframe: 1, img: 1, input: 1, object: 1, select: 1,
|
||||
table: 1, textarea: 1, video: 1 },
|
||||
|
||||
/**
|
||||
* List of elements that can be ignored if empty, like `<b>` or `<span>`.
|
||||
*/
|
||||
$removeEmpty: { abbr: 1, acronym: 1, b: 1, bdi: 1, bdo: 1, big: 1, cite: 1, code: 1, del: 1, dfn: 1,
|
||||
em: 1, font: 1, i: 1, ins: 1, label: 1, kbd: 1, mark: 1, meter: 1, output: 1, q: 1, ruby: 1, s: 1,
|
||||
samp: 1, small: 1, span: 1, strike: 1, strong: 1, sub: 1, sup: 1, time: 1, tt: 1, u: 1, 'var': 1 },
|
||||
|
||||
/**
|
||||
* List of elements that have tabindex set to zero by default.
|
||||
*/
|
||||
$tabIndex: { a: 1, area: 1, button: 1, input: 1, object: 1, select: 1, textarea: 1 },
|
||||
|
||||
/**
|
||||
* List of elements used inside the `<table>` element, like `<tbody>` or `<td>`.
|
||||
*/
|
||||
$tableContent: { caption: 1, col: 1, colgroup: 1, tbody: 1, td: 1, tfoot: 1, th: 1, thead: 1, tr: 1 },
|
||||
|
||||
/**
|
||||
* List of "transparent" elements. See [W3C's definition of "transparent" element](http://dev.w3.org/html5/markup/terminology.html#transparent).
|
||||
*/
|
||||
$transparent: { a: 1, audio: 1, canvas: 1, del: 1, ins: 1, map: 1, noscript: 1, object: 1, video: 1 },
|
||||
|
||||
/**
|
||||
* List of elements that are not to exist standalone that must live under it's parent element.
|
||||
*/
|
||||
$intermediate: { caption: 1, colgroup: 1, dd: 1, dt: 1, figcaption: 1, legend: 1, li: 1, optgroup: 1,
|
||||
option: 1, rp: 1, rt: 1, summary: 1, tbody: 1, td: 1, tfoot: 1, th: 1, thead: 1, tr: 1 }
|
||||
} );
|
||||
|
||||
return dtd;
|
||||
} )();
|
||||
|
||||
// PACKAGER_RENAME( CKEDITOR.dtd )
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,36 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
if ( !CKEDITOR.editor ) {
|
||||
// Documented at editor.js.
|
||||
CKEDITOR.editor = function() {
|
||||
// Push this editor to the pending list. It'll be processed later once
|
||||
// the full editor code is loaded.
|
||||
CKEDITOR._.pending.push( [ this, arguments ] );
|
||||
|
||||
// Call the CKEDITOR.event constructor to initialize this instance.
|
||||
CKEDITOR.event.call( this );
|
||||
};
|
||||
|
||||
// Both fire and fireOnce will always pass this editor instance as the
|
||||
// "editor" param in CKEDITOR.event.fire. So, we override it to do that
|
||||
// automaticaly.
|
||||
CKEDITOR.editor.prototype.fire = function( eventName, data ) {
|
||||
if ( eventName in { instanceReady: 1, loaded: 1 } )
|
||||
this[ eventName ] = true;
|
||||
|
||||
return CKEDITOR.event.prototype.fire.call( this, eventName, data, this );
|
||||
};
|
||||
|
||||
CKEDITOR.editor.prototype.fireOnce = function( eventName, data ) {
|
||||
if ( eventName in { instanceReady: 1, loaded: 1 } )
|
||||
this[ eventName ] = true;
|
||||
|
||||
return CKEDITOR.event.prototype.fireOnce.call( this, eventName, data, this );
|
||||
};
|
||||
|
||||
// "Inherit" (copy actually) from CKEDITOR.event.
|
||||
CKEDITOR.event.implementOn( CKEDITOR.editor.prototype );
|
||||
}
|
||||
@@ -1,359 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileOverview Defines the {@link CKEDITOR.env} object which contains
|
||||
* environment and browser information.
|
||||
*/
|
||||
|
||||
if ( !CKEDITOR.env ) {
|
||||
/**
|
||||
* Environment and browser information.
|
||||
*
|
||||
* @class CKEDITOR.env
|
||||
* @singleton
|
||||
*/
|
||||
CKEDITOR.env = ( function() {
|
||||
var agent = navigator.userAgent.toLowerCase();
|
||||
var opera = window.opera;
|
||||
|
||||
var env = {
|
||||
/**
|
||||
* Indicates that CKEditor is running in Internet Explorer.
|
||||
*
|
||||
* if ( CKEDITOR.env.ie )
|
||||
* alert( 'I\'m running in IE!' );
|
||||
*
|
||||
* @property {Boolean}
|
||||
*/
|
||||
ie: ( agent.indexOf( 'trident/' ) > -1 ),
|
||||
|
||||
/**
|
||||
* Indicates that CKEditor is running in Opera.
|
||||
*
|
||||
* if ( CKEDITOR.env.opera )
|
||||
* alert( 'I\'m running in Opera!' );
|
||||
*
|
||||
* @property {Boolean}
|
||||
*/
|
||||
opera: ( !!opera && opera.version ),
|
||||
|
||||
/**
|
||||
* Indicates that CKEditor is running in a WebKit-based browser, like Safari.
|
||||
*
|
||||
* if ( CKEDITOR.env.webkit )
|
||||
* alert( 'I\'m running in a WebKit browser!' );
|
||||
*
|
||||
* @property {Boolean}
|
||||
*/
|
||||
webkit: ( agent.indexOf( ' applewebkit/' ) > -1 ),
|
||||
|
||||
/**
|
||||
* Indicates that CKEditor is running in Adobe AIR.
|
||||
*
|
||||
* if ( CKEDITOR.env.air )
|
||||
* alert( 'I\'m on AIR!' );
|
||||
*
|
||||
* @property {Boolean}
|
||||
*/
|
||||
air: ( agent.indexOf( ' adobeair/' ) > -1 ),
|
||||
|
||||
/**
|
||||
* Indicates that CKEditor is running on Macintosh.
|
||||
*
|
||||
* if ( CKEDITOR.env.mac )
|
||||
* alert( 'I love apples!'' );
|
||||
*
|
||||
* @property {Boolean}
|
||||
*/
|
||||
mac: ( agent.indexOf( 'macintosh' ) > -1 ),
|
||||
|
||||
/**
|
||||
* Indicates that CKEditor is running in a Quirks Mode environment.
|
||||
*
|
||||
* if ( CKEDITOR.env.quirks )
|
||||
* alert( 'Nooooo!' );
|
||||
*
|
||||
* Internet Explorer 10 introduced the _New Quirks Mode_, which is similar to the _Quirks Mode_
|
||||
* implemented in other modern browsers and defined in the HTML5 specification. It can be handled
|
||||
* as the Standards mode, so the value of this property will be set to `false`.
|
||||
*
|
||||
* The _Internet Explorer 5 Quirks_ mode which is still available in Internet Explorer 10+
|
||||
* sets this value to `true` and {@link #version} to `7`.
|
||||
*
|
||||
* Read more: [IEBlog](http://blogs.msdn.com/b/ie/archive/2011/12/14/interoperable-html5-quirks-mode-in-ie10.aspx)
|
||||
*
|
||||
* @property {Boolean}
|
||||
*/
|
||||
quirks: ( document.compatMode == 'BackCompat' && ( !document.documentMode || document.documentMode < 10 ) ),
|
||||
|
||||
/**
|
||||
* Indicates that CKEditor is running in a mobile environemnt.
|
||||
*
|
||||
* if ( CKEDITOR.env.mobile )
|
||||
* alert( 'I\'m running with CKEditor today!' );
|
||||
*
|
||||
* @property {Boolean}
|
||||
*/
|
||||
mobile: ( agent.indexOf( 'mobile' ) > -1 ),
|
||||
|
||||
/**
|
||||
* Indicates that CKEditor is running on Apple iPhone/iPad/iPod devices.
|
||||
*
|
||||
* if ( CKEDITOR.env.iOS )
|
||||
* alert( 'I like little apples!' );
|
||||
*
|
||||
* @property {Boolean}
|
||||
*/
|
||||
iOS: /(ipad|iphone|ipod)/.test( agent ),
|
||||
|
||||
/**
|
||||
* Indicates that the browser has a custom domain enabled. This has
|
||||
* been set with `document.domain`.
|
||||
*
|
||||
* if ( CKEDITOR.env.isCustomDomain() )
|
||||
* alert( 'I\'m in a custom domain!' );
|
||||
*
|
||||
* @returns {Boolean} `true` if a custom domain is enabled.
|
||||
* @deprecated
|
||||
*/
|
||||
isCustomDomain: function() {
|
||||
if ( !this.ie )
|
||||
return false;
|
||||
|
||||
var domain = document.domain,
|
||||
hostname = window.location.hostname;
|
||||
|
||||
return domain != hostname && domain != ( '[' + hostname + ']' ); // IPv6 IP support (#5434)
|
||||
},
|
||||
|
||||
/**
|
||||
* Indicates that the page is running under an encrypted connection.
|
||||
*
|
||||
* if ( CKEDITOR.env.secure )
|
||||
* alert( 'I\'m on SSL!' );
|
||||
*
|
||||
* @returns {Boolean} `true` if the page has an encrypted connection.
|
||||
*/
|
||||
secure: location.protocol == 'https:'
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicates that CKEditor is running in a Gecko-based browser, like
|
||||
* Firefox.
|
||||
*
|
||||
* if ( CKEDITOR.env.gecko )
|
||||
* alert( 'I\'m riding a gecko!' );
|
||||
*
|
||||
* @property {Boolean}
|
||||
*/
|
||||
env.gecko = ( navigator.product == 'Gecko' && !env.webkit && !env.opera && !env.ie );
|
||||
|
||||
/**
|
||||
* Indicates that CKEditor is running in Chrome.
|
||||
*
|
||||
* if ( CKEDITOR.env.chrome )
|
||||
* alert( 'I\'m running in Chrome!' );
|
||||
*
|
||||
* @property {Boolean} chrome
|
||||
*/
|
||||
|
||||
/**
|
||||
* Indicates that CKEditor is running in Safari (including the mobile version).
|
||||
*
|
||||
* if ( CKEDITOR.env.safari )
|
||||
* alert( 'I\'m on Safari!' );
|
||||
*
|
||||
* @property {Boolean} safari
|
||||
*/
|
||||
if ( env.webkit ) {
|
||||
if ( agent.indexOf( 'chrome' ) > -1 )
|
||||
env.chrome = true;
|
||||
else
|
||||
env.safari = true;
|
||||
}
|
||||
|
||||
var version = 0;
|
||||
|
||||
// Internet Explorer 6.0+
|
||||
if ( env.ie ) {
|
||||
// We use env.version for feature detection, so set it properly.
|
||||
if ( env.quirks || !document.documentMode )
|
||||
version = parseFloat( agent.match( /msie (\d+)/ )[ 1 ] );
|
||||
else
|
||||
version = document.documentMode;
|
||||
|
||||
// Deprecated features available just for backwards compatibility.
|
||||
env.ie9Compat = version == 9;
|
||||
env.ie8Compat = version == 8;
|
||||
env.ie7Compat = version == 7;
|
||||
env.ie6Compat = version < 7 || env.quirks;
|
||||
|
||||
/**
|
||||
* Indicates that CKEditor is running in an IE6-like environment, which
|
||||
* includes IE6 itself as well as IE7, IE8 and IE9 in Quirks Mode.
|
||||
*
|
||||
* @deprecated
|
||||
* @property {Boolean} ie6Compat
|
||||
*/
|
||||
|
||||
/**
|
||||
* Indicates that CKEditor is running in an IE7-like environment, which
|
||||
* includes IE7 itself and IE8's IE7 Document Mode.
|
||||
*
|
||||
* @deprecated
|
||||
* @property {Boolean} ie7Compat
|
||||
*/
|
||||
|
||||
/**
|
||||
* Indicates that CKEditor is running in Internet Explorer 8 on
|
||||
* Standards Mode.
|
||||
*
|
||||
* @deprecated
|
||||
* @property {Boolean} ie8Compat
|
||||
*/
|
||||
|
||||
/**
|
||||
* Indicates that CKEditor is running in Internet Explorer 9 on
|
||||
* Standards Mode.
|
||||
*
|
||||
* @deprecated
|
||||
* @property {Boolean} ie9Compat
|
||||
*/
|
||||
}
|
||||
|
||||
// Gecko.
|
||||
if ( env.gecko ) {
|
||||
var geckoRelease = agent.match( /rv:([\d\.]+)/ );
|
||||
if ( geckoRelease ) {
|
||||
geckoRelease = geckoRelease[ 1 ].split( '.' );
|
||||
version = geckoRelease[ 0 ] * 10000 + ( geckoRelease[ 1 ] || 0 ) * 100 + ( geckoRelease[ 2 ] || 0 ) * 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Opera 9.50+
|
||||
if ( env.opera )
|
||||
version = parseFloat( opera.version() );
|
||||
|
||||
// Adobe AIR 1.0+
|
||||
// Checked before Safari because AIR have the WebKit rich text editor
|
||||
// features from Safari 3.0.4, but the version reported is 420.
|
||||
if ( env.air )
|
||||
version = parseFloat( agent.match( / adobeair\/(\d+)/ )[ 1 ] );
|
||||
|
||||
// WebKit 522+ (Safari 3+)
|
||||
if ( env.webkit )
|
||||
version = parseFloat( agent.match( / applewebkit\/(\d+)/ )[ 1 ] );
|
||||
|
||||
/**
|
||||
* Contains the browser version.
|
||||
*
|
||||
* For Gecko-based browsers (like Firefox) it contains the revision
|
||||
* number with first three parts concatenated with a padding zero
|
||||
* (e.g. for revision 1.9.0.2 we have 10900).
|
||||
*
|
||||
* For WebKit-based browsers (like Safari and Chrome) it contains the
|
||||
* WebKit build version (e.g. 522).
|
||||
*
|
||||
* For IE browsers, it matches the "Document Mode".
|
||||
*
|
||||
* if ( CKEDITOR.env.ie && CKEDITOR.env.version <= 6 )
|
||||
* alert( 'Ouch!' );
|
||||
*
|
||||
* @property {Number}
|
||||
*/
|
||||
env.version = version;
|
||||
|
||||
/**
|
||||
* Indicates that CKEditor is running in a compatible browser.
|
||||
*
|
||||
* if ( CKEDITOR.env.isCompatible )
|
||||
* alert( 'Your browser is pretty cool!' );
|
||||
*
|
||||
* @property {Boolean}
|
||||
*/
|
||||
env.isCompatible =
|
||||
// White list of mobile devices that CKEditor supports.
|
||||
env.iOS && version >= 534 ||
|
||||
!env.mobile && (
|
||||
( env.ie && version > 6 ) ||
|
||||
( env.gecko && version >= 10801 ) ||
|
||||
( env.opera && version >= 9.5 ) ||
|
||||
( env.air && version >= 1 ) ||
|
||||
( env.webkit && version >= 522 ) ||
|
||||
false
|
||||
);
|
||||
|
||||
/**
|
||||
* Indicates that CKEditor is running in the HiDPI environment.
|
||||
*
|
||||
* if ( CKEDITOR.env.hidpi )
|
||||
* alert( 'You are using a screen with high pixel density.' );
|
||||
*
|
||||
* @property {Boolean}
|
||||
*/
|
||||
env.hidpi = window.devicePixelRatio >= 2;
|
||||
|
||||
/**
|
||||
* Indicates that CKEditor is running in a browser which uses a bogus
|
||||
* `<br>` filler in order to correctly display caret in empty blocks.
|
||||
*
|
||||
* @since 4.3
|
||||
* @property {Boolean}
|
||||
*/
|
||||
env.needsBrFiller = env.gecko || env.webkit || ( env.ie && version > 10 );
|
||||
|
||||
/**
|
||||
* Indicates that CKEditor is running in a browser which needs a
|
||||
* non-breaking space filler in order to correctly display caret in empty blocks.
|
||||
*
|
||||
* @since 4.3
|
||||
* @property {Boolean}
|
||||
*/
|
||||
env.needsNbspFiller = env.ie && version < 11;
|
||||
|
||||
/**
|
||||
* A CSS class that denotes the browser where CKEditor runs and is appended
|
||||
* to the HTML element that contains the editor. It makes it easier to apply
|
||||
* browser-specific styles to editor instances.
|
||||
*
|
||||
* myDiv.className = CKEDITOR.env.cssClass;
|
||||
*
|
||||
* @property {String}
|
||||
*/
|
||||
env.cssClass = 'cke_browser_' + ( env.ie ? 'ie' : env.gecko ? 'gecko' : env.opera ? 'opera' : env.webkit ? 'webkit' : 'unknown' );
|
||||
|
||||
if ( env.quirks )
|
||||
env.cssClass += ' cke_browser_quirks';
|
||||
|
||||
if ( env.ie ) {
|
||||
env.cssClass += ' cke_browser_ie' + ( env.quirks || env.version < 7 ? '6' : env.version );
|
||||
|
||||
if ( env.quirks )
|
||||
env.cssClass += ' cke_browser_iequirks';
|
||||
}
|
||||
|
||||
if ( env.gecko ) {
|
||||
if ( version < 10900 )
|
||||
env.cssClass += ' cke_browser_gecko18';
|
||||
else if ( version <= 11000 )
|
||||
env.cssClass += ' cke_browser_gecko19';
|
||||
}
|
||||
|
||||
if ( env.air )
|
||||
env.cssClass += ' cke_browser_air';
|
||||
|
||||
if ( env.iOS )
|
||||
env.cssClass += ' cke_browser_ios';
|
||||
|
||||
if ( env.hidpi )
|
||||
env.cssClass += ' cke_hidpi';
|
||||
|
||||
return env;
|
||||
} )();
|
||||
}
|
||||
|
||||
// PACKAGER_RENAME( CKEDITOR.env )
|
||||
// PACKAGER_RENAME( CKEDITOR.env.ie )
|
||||
@@ -1,387 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileOverview Defines the {@link CKEDITOR.event} class, which serves as the
|
||||
* base for classes and objects that require event handling features.
|
||||
*/
|
||||
|
||||
if ( !CKEDITOR.event ) {
|
||||
/**
|
||||
* Creates an event class instance. This constructor is rearely used, being
|
||||
* the {@link #implementOn} function used in class prototypes directly
|
||||
* instead.
|
||||
*
|
||||
* This is a base class for classes and objects that require event
|
||||
* handling features.
|
||||
*
|
||||
* Do not confuse this class with {@link CKEDITOR.dom.event} which is
|
||||
* instead used for DOM events. The CKEDITOR.event class implements the
|
||||
* internal event system used by the CKEditor to fire API related events.
|
||||
*
|
||||
* @class
|
||||
* @constructor Creates an event class instance.
|
||||
*/
|
||||
CKEDITOR.event = function() {};
|
||||
|
||||
/**
|
||||
* Implements the {@link CKEDITOR.event} features in an object.
|
||||
*
|
||||
* var myObject = { message: 'Example' };
|
||||
* CKEDITOR.event.implementOn( myObject );
|
||||
*
|
||||
* myObject.on( 'testEvent', function() {
|
||||
* alert( this.message );
|
||||
* } );
|
||||
* myObject.fire( 'testEvent' ); // 'Example'
|
||||
*
|
||||
* @static
|
||||
* @param {Object} targetObject The object into which implement the features.
|
||||
*/
|
||||
CKEDITOR.event.implementOn = function( targetObject ) {
|
||||
var eventProto = CKEDITOR.event.prototype;
|
||||
|
||||
for ( var prop in eventProto ) {
|
||||
if ( targetObject[ prop ] == undefined )
|
||||
targetObject[ prop ] = eventProto[ prop ];
|
||||
}
|
||||
};
|
||||
|
||||
CKEDITOR.event.prototype = ( function() {
|
||||
// Returns the private events object for a given object.
|
||||
var getPrivate = function( obj ) {
|
||||
var _ = ( obj.getPrivate && obj.getPrivate() ) || obj._ || ( obj._ = {} );
|
||||
return _.events || ( _.events = {} );
|
||||
};
|
||||
|
||||
var eventEntry = function( eventName ) {
|
||||
this.name = eventName;
|
||||
this.listeners = [];
|
||||
};
|
||||
|
||||
eventEntry.prototype = {
|
||||
// Get the listener index for a specified function.
|
||||
// Returns -1 if not found.
|
||||
getListenerIndex: function( listenerFunction ) {
|
||||
for ( var i = 0, listeners = this.listeners; i < listeners.length; i++ ) {
|
||||
if ( listeners[ i ].fn == listenerFunction )
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
// Retrieve the event entry on the event host (create it if needed).
|
||||
function getEntry( name ) {
|
||||
// Get the event entry (create it if needed).
|
||||
var events = getPrivate( this );
|
||||
return events[ name ] || ( events[ name ] = new eventEntry( name ) );
|
||||
}
|
||||
|
||||
return {
|
||||
/**
|
||||
* Predefine some intrinsic properties on a specific event name.
|
||||
*
|
||||
* @param {String} name The event name
|
||||
* @param meta
|
||||
* @param [meta.errorProof=false] Whether the event firing should catch error thrown from a per listener call.
|
||||
*/
|
||||
define: function( name, meta ) {
|
||||
var entry = getEntry.call( this, name );
|
||||
CKEDITOR.tools.extend( entry, meta, true );
|
||||
},
|
||||
|
||||
/**
|
||||
* Registers a listener to a specific event in the current object.
|
||||
*
|
||||
* someObject.on( 'someEvent', function() {
|
||||
* alert( this == someObject ); // true
|
||||
* } );
|
||||
*
|
||||
* someObject.on( 'someEvent', function() {
|
||||
* alert( this == anotherObject ); // true
|
||||
* }, anotherObject );
|
||||
*
|
||||
* someObject.on( 'someEvent', function( event ) {
|
||||
* alert( event.listenerData ); // 'Example'
|
||||
* }, null, 'Example' );
|
||||
*
|
||||
* someObject.on( 'someEvent', function() { ... } ); // 2nd called
|
||||
* someObject.on( 'someEvent', function() { ... }, null, null, 100 ); // 3rd called
|
||||
* someObject.on( 'someEvent', function() { ... }, null, null, 1 ); // 1st called
|
||||
*
|
||||
* @param {String} eventName The event name to which listen.
|
||||
* @param {Function} listenerFunction The function listening to the
|
||||
* event. A single {@link CKEDITOR.eventInfo} object instanced
|
||||
* is passed to this function containing all the event data.
|
||||
* @param {Object} [scopeObj] The object used to scope the listener
|
||||
* call (the `this` object). If omitted, the current object is used.
|
||||
* @param {Object} [listenerData] Data to be sent as the
|
||||
* {@link CKEDITOR.eventInfo#listenerData} when calling the
|
||||
* listener.
|
||||
* @param {Number} [priority=10] The listener priority. Lower priority
|
||||
* listeners are called first. Listeners with the same priority
|
||||
* value are called in registration order.
|
||||
* @returns {Object} An object containing the `removeListener`
|
||||
* function, which can be used to remove the listener at any time.
|
||||
*/
|
||||
on: function( eventName, listenerFunction, scopeObj, listenerData, priority ) {
|
||||
// Create the function to be fired for this listener.
|
||||
function listenerFirer( editor, publisherData, stopFn, cancelFn ) {
|
||||
var ev = {
|
||||
name: eventName,
|
||||
sender: this,
|
||||
editor: editor,
|
||||
data: publisherData,
|
||||
listenerData: listenerData,
|
||||
stop: stopFn,
|
||||
cancel: cancelFn,
|
||||
removeListener: removeListener
|
||||
};
|
||||
|
||||
var ret = listenerFunction.call( scopeObj, ev );
|
||||
|
||||
return ret === false ? false : ev.data;
|
||||
}
|
||||
|
||||
function removeListener() {
|
||||
me.removeListener( eventName, listenerFunction );
|
||||
}
|
||||
|
||||
var event = getEntry.call( this, eventName );
|
||||
|
||||
if ( event.getListenerIndex( listenerFunction ) < 0 ) {
|
||||
// Get the listeners.
|
||||
var listeners = event.listeners;
|
||||
|
||||
// Fill the scope.
|
||||
if ( !scopeObj )
|
||||
scopeObj = this;
|
||||
|
||||
// Default the priority, if needed.
|
||||
if ( isNaN( priority ) )
|
||||
priority = 10;
|
||||
|
||||
var me = this;
|
||||
|
||||
listenerFirer.fn = listenerFunction;
|
||||
listenerFirer.priority = priority;
|
||||
|
||||
// Search for the right position for this new listener, based on its
|
||||
// priority.
|
||||
for ( var i = listeners.length - 1; i >= 0; i-- ) {
|
||||
// Find the item which should be before the new one.
|
||||
if ( listeners[ i ].priority <= priority ) {
|
||||
// Insert the listener in the array.
|
||||
listeners.splice( i + 1, 0, listenerFirer );
|
||||
return { removeListener: removeListener };
|
||||
}
|
||||
}
|
||||
|
||||
// If no position has been found (or zero length), put it in
|
||||
// the front of list.
|
||||
listeners.unshift( listenerFirer );
|
||||
}
|
||||
|
||||
return { removeListener: removeListener };
|
||||
},
|
||||
|
||||
/**
|
||||
* Similiar with {@link #on} but the listener will be called only once upon the next event firing.
|
||||
*
|
||||
* @see CKEDITOR.event#on
|
||||
*/
|
||||
once: function() {
|
||||
var fn = arguments[ 1 ];
|
||||
|
||||
arguments[ 1 ] = function( evt ) {
|
||||
evt.removeListener();
|
||||
return fn.apply( this, arguments );
|
||||
};
|
||||
|
||||
return this.on.apply( this, arguments );
|
||||
},
|
||||
|
||||
/**
|
||||
* @static
|
||||
* @property {Boolean} useCapture
|
||||
* @todo
|
||||
*/
|
||||
|
||||
/**
|
||||
* Register event handler under the capturing stage on supported target.
|
||||
*/
|
||||
capture: function() {
|
||||
CKEDITOR.event.useCapture = 1;
|
||||
var retval = this.on.apply( this, arguments );
|
||||
CKEDITOR.event.useCapture = 0;
|
||||
return retval;
|
||||
},
|
||||
|
||||
/**
|
||||
* Fires an specific event in the object. All registered listeners are
|
||||
* called at this point.
|
||||
*
|
||||
* someObject.on( 'someEvent', function() { ... } );
|
||||
* someObject.on( 'someEvent', function() { ... } );
|
||||
* someObject.fire( 'someEvent' ); // Both listeners are called.
|
||||
*
|
||||
* someObject.on( 'someEvent', function( event ) {
|
||||
* alert( event.data ); // 'Example'
|
||||
* } );
|
||||
* someObject.fire( 'someEvent', 'Example' );
|
||||
*
|
||||
* @method
|
||||
* @param {String} eventName The event name to fire.
|
||||
* @param {Object} [data] Data to be sent as the
|
||||
* {@link CKEDITOR.eventInfo#data} when calling the listeners.
|
||||
* @param {CKEDITOR.editor} [editor] The editor instance to send as the
|
||||
* {@link CKEDITOR.eventInfo#editor} when calling the listener.
|
||||
* @returns {Boolean/Object} A boolean indicating that the event is to be
|
||||
* canceled, or data returned by one of the listeners.
|
||||
*/
|
||||
fire: ( function() {
|
||||
// Create the function that marks the event as stopped.
|
||||
var stopped = 0;
|
||||
var stopEvent = function() {
|
||||
stopped = 1;
|
||||
};
|
||||
|
||||
// Create the function that marks the event as canceled.
|
||||
var canceled = 0;
|
||||
var cancelEvent = function() {
|
||||
canceled = 1;
|
||||
};
|
||||
|
||||
return function( eventName, data, editor ) {
|
||||
// Get the event entry.
|
||||
var event = getPrivate( this )[ eventName ];
|
||||
|
||||
// Save the previous stopped and cancelled states. We may
|
||||
// be nesting fire() calls.
|
||||
var previousStopped = stopped,
|
||||
previousCancelled = canceled;
|
||||
|
||||
// Reset the stopped and canceled flags.
|
||||
stopped = canceled = 0;
|
||||
|
||||
if ( event ) {
|
||||
var listeners = event.listeners;
|
||||
|
||||
if ( listeners.length ) {
|
||||
// As some listeners may remove themselves from the
|
||||
// event, the original array length is dinamic. So,
|
||||
// let's make a copy of all listeners, so we are
|
||||
// sure we'll call all of them.
|
||||
listeners = listeners.slice( 0 );
|
||||
|
||||
var retData;
|
||||
// Loop through all listeners.
|
||||
for ( var i = 0; i < listeners.length; i++ ) {
|
||||
// Call the listener, passing the event data.
|
||||
if ( event.errorProof ) {
|
||||
try {
|
||||
retData = listeners[ i ].call( this, editor, data, stopEvent, cancelEvent );
|
||||
} catch ( er ) {}
|
||||
} else
|
||||
retData = listeners[ i ].call( this, editor, data, stopEvent, cancelEvent );
|
||||
|
||||
if ( retData === false )
|
||||
canceled = 1;
|
||||
else if ( typeof retData != 'undefined' )
|
||||
data = retData;
|
||||
|
||||
// No further calls is stopped or canceled.
|
||||
if ( stopped || canceled )
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var ret = canceled ? false : ( typeof data == 'undefined' ? true : data );
|
||||
|
||||
// Restore the previous stopped and canceled states.
|
||||
stopped = previousStopped;
|
||||
canceled = previousCancelled;
|
||||
|
||||
return ret;
|
||||
};
|
||||
} )(),
|
||||
|
||||
/**
|
||||
* Fires an specific event in the object, releasing all listeners
|
||||
* registered to that event. The same listeners are not called again on
|
||||
* successive calls of it or of {@link #fire}.
|
||||
*
|
||||
* someObject.on( 'someEvent', function() { ... } );
|
||||
* someObject.fire( 'someEvent' ); // Above listener called.
|
||||
* someObject.fireOnce( 'someEvent' ); // Above listener called.
|
||||
* someObject.fire( 'someEvent' ); // No listeners called.
|
||||
*
|
||||
* @param {String} eventName The event name to fire.
|
||||
* @param {Object} [data] Data to be sent as the
|
||||
* {@link CKEDITOR.eventInfo#data} when calling the listeners.
|
||||
* @param {CKEDITOR.editor} [editor] The editor instance to send as the
|
||||
* {@link CKEDITOR.eventInfo#editor} when calling the listener.
|
||||
* @returns {Boolean/Object} A booloan indicating that the event is to be
|
||||
* canceled, or data returned by one of the listeners.
|
||||
*/
|
||||
fireOnce: function( eventName, data, editor ) {
|
||||
var ret = this.fire( eventName, data, editor );
|
||||
delete getPrivate( this )[ eventName ];
|
||||
return ret;
|
||||
},
|
||||
|
||||
/**
|
||||
* Unregisters a listener function from being called at the specified
|
||||
* event. No errors are thrown if the listener has not been registered previously.
|
||||
*
|
||||
* var myListener = function() { ... };
|
||||
* someObject.on( 'someEvent', myListener );
|
||||
* someObject.fire( 'someEvent' ); // myListener called.
|
||||
* someObject.removeListener( 'someEvent', myListener );
|
||||
* someObject.fire( 'someEvent' ); // myListener not called.
|
||||
*
|
||||
* @param {String} eventName The event name.
|
||||
* @param {Function} listenerFunction The listener function to unregister.
|
||||
*/
|
||||
removeListener: function( eventName, listenerFunction ) {
|
||||
// Get the event entry.
|
||||
var event = getPrivate( this )[ eventName ];
|
||||
|
||||
if ( event ) {
|
||||
var index = event.getListenerIndex( listenerFunction );
|
||||
if ( index >= 0 )
|
||||
event.listeners.splice( index, 1 );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove all existing listeners on this object, for cleanup purpose.
|
||||
*/
|
||||
removeAllListeners: function() {
|
||||
var events = getPrivate( this );
|
||||
for ( var i in events )
|
||||
delete events[ i ];
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if there is any listener registered to a given event.
|
||||
*
|
||||
* var myListener = function() { ... };
|
||||
* someObject.on( 'someEvent', myListener );
|
||||
* alert( someObject.hasListeners( 'someEvent' ) ); // true
|
||||
* alert( someObject.hasListeners( 'noEvent' ) ); // false
|
||||
*
|
||||
* @param {String} eventName The event name.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
hasListeners: function( eventName ) {
|
||||
var event = getPrivate( this )[ eventName ];
|
||||
return ( event && event.listeners.length > 0 );
|
||||
}
|
||||
};
|
||||
} )();
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileOverview Defines the "virtual" {@link CKEDITOR.eventInfo} class, which
|
||||
* contains the defintions of the event object passed to event listeners.
|
||||
* This file is for documentation purposes only.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Virtual class that illustrates the features of the event object to be
|
||||
* passed to event listeners by a {@link CKEDITOR.event} based object.
|
||||
*
|
||||
* This class is not really part of the API.
|
||||
*
|
||||
* @class CKEDITOR.eventInfo
|
||||
* @abstract
|
||||
*/
|
||||
|
||||
/**
|
||||
* The event name.
|
||||
*
|
||||
* someObject.on( 'someEvent', function( event ) {
|
||||
* alert( event.name ); // 'someEvent'
|
||||
* } );
|
||||
* someObject.fire( 'someEvent' );
|
||||
*
|
||||
* @property {String} name
|
||||
*/
|
||||
|
||||
/**
|
||||
* The object that publishes (sends) the event.
|
||||
*
|
||||
* someObject.on( 'someEvent', function( event ) {
|
||||
* alert( event.sender == someObject ); // true
|
||||
* } );
|
||||
* someObject.fire( 'someEvent' );
|
||||
*
|
||||
* @property sender
|
||||
*/
|
||||
|
||||
/**
|
||||
* The editor instance that holds the sender. May be the same as sender. May be
|
||||
* null if the sender is not part of an editor instance, like a component
|
||||
* running in standalone mode.
|
||||
*
|
||||
* myButton.on( 'someEvent', function( event ) {
|
||||
* alert( event.editor == myEditor ); // true
|
||||
* } );
|
||||
* myButton.fire( 'someEvent', null, myEditor );
|
||||
*
|
||||
* @property {CKEDITOR.editor} editor
|
||||
*/
|
||||
|
||||
/**
|
||||
* Any kind of additional data. Its format and usage is event dependent.
|
||||
*
|
||||
* someObject.on( 'someEvent', function( event ) {
|
||||
* alert( event.data ); // 'Example'
|
||||
* } );
|
||||
* someObject.fire( 'someEvent', 'Example' );
|
||||
*
|
||||
* @property data
|
||||
*/
|
||||
|
||||
/**
|
||||
* Any extra data appended during the listener registration.
|
||||
*
|
||||
* someObject.on( 'someEvent', function( event ) {
|
||||
* alert( event.listenerData ); // 'Example'
|
||||
* }, null, 'Example' );
|
||||
*
|
||||
* @property listenerData
|
||||
*/
|
||||
|
||||
/**
|
||||
* Indicates that no further listeners are to be called.
|
||||
*
|
||||
* someObject.on( 'someEvent', function( event ) {
|
||||
* event.stop();
|
||||
* } );
|
||||
* someObject.on( 'someEvent', function( event ) {
|
||||
* // This one will not be called.
|
||||
* } );
|
||||
* alert( someObject.fire( 'someEvent' ) ); // false
|
||||
*
|
||||
* @method stop
|
||||
*/
|
||||
|
||||
/**
|
||||
* Indicates that the event is to be cancelled (if cancelable).
|
||||
*
|
||||
* someObject.on( 'someEvent', function( event ) {
|
||||
* event.cancel();
|
||||
* } );
|
||||
* someObject.on( 'someEvent', function( event ) {
|
||||
* // This one will not be called.
|
||||
* } );
|
||||
* alert( someObject.fire( 'someEvent' ) ); // true
|
||||
*
|
||||
* @method cancel
|
||||
*/
|
||||
|
||||
/**
|
||||
* Removes the current listener.
|
||||
*
|
||||
* someObject.on( 'someEvent', function( event ) {
|
||||
* event.removeListener();
|
||||
* // Now this function won't be called again by 'someEvent'.
|
||||
* } );
|
||||
*
|
||||
* @method removeListener
|
||||
*/
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,271 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileOverview Defines the {@link CKEDITOR.focusManager} class, which is used
|
||||
* to handle the focus on editor instances..
|
||||
*/
|
||||
|
||||
( function() {
|
||||
/**
|
||||
* Manages the focus activity in an editor instance. This class is to be
|
||||
* used mainly by UI elements coders when adding interface elements that need
|
||||
* to set the focus state of the editor.
|
||||
*
|
||||
* var focusManager = new CKEDITOR.focusManager( editor );
|
||||
* focusManager.focus();
|
||||
*
|
||||
* @class
|
||||
* @constructor Creates a focusManager class instance.
|
||||
* @param {CKEDITOR.editor} editor The editor instance.
|
||||
*/
|
||||
CKEDITOR.focusManager = function( editor ) {
|
||||
if ( editor.focusManager )
|
||||
return editor.focusManager;
|
||||
|
||||
/**
|
||||
* Indicates that the editor instance has focus.
|
||||
*
|
||||
* alert( CKEDITOR.instances.editor1.focusManager.hasFocus ); // e.g. true
|
||||
*/
|
||||
this.hasFocus = false;
|
||||
|
||||
/**
|
||||
* Indicate the currently focused DOM element that makes the editor activated.
|
||||
*
|
||||
* @property {CKEDITOR.dom.domObject}
|
||||
*/
|
||||
this.currentActive = null;
|
||||
|
||||
/**
|
||||
* Object used to hold private stuff.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
this._ = {
|
||||
editor: editor
|
||||
};
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
var SLOT_NAME = 'focusmanager',
|
||||
SLOT_NAME_LISTENERS = 'focusmanager_handlers';
|
||||
|
||||
/**
|
||||
* Object used to hold private stuff.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @singleton
|
||||
*/
|
||||
CKEDITOR.focusManager._ = {
|
||||
/**
|
||||
* The delay (in milliseconds) to deactivate the editor when UI dom element has lost focus.
|
||||
*
|
||||
* @private
|
||||
* @property {Number} [blurDelay=200]
|
||||
* @member CKEDITOR.focusManager._
|
||||
*/
|
||||
blurDelay: 200
|
||||
};
|
||||
|
||||
CKEDITOR.focusManager.prototype = {
|
||||
|
||||
/**
|
||||
* Indicate this editor instance is activated (due to DOM focus change),
|
||||
* the `activated` state is a symbolic indicator of an active user
|
||||
* interaction session.
|
||||
*
|
||||
* **Note:** This method will not introduce UI focus
|
||||
* impact on DOM, it's here to record editor UI focus state internally.
|
||||
* If you want to make the cursor blink inside of the editable, use
|
||||
* {@link CKEDITOR.editor#method-focus} instead.
|
||||
*
|
||||
* var editor = CKEDITOR.instances.editor1;
|
||||
* editor.focusManage.focus( editor.editable() );
|
||||
*
|
||||
* @param {CKEDITOR.dom.element} [currentActive] The new value of {@link #currentActive} property.
|
||||
* @member CKEDITOR.focusManager
|
||||
*/
|
||||
focus: function( currentActive ) {
|
||||
if ( this._.timer )
|
||||
clearTimeout( this._.timer );
|
||||
|
||||
if ( currentActive )
|
||||
this.currentActive = currentActive;
|
||||
|
||||
if ( !( this.hasFocus || this._.locked ) ) {
|
||||
// If another editor has the current focus, we first "blur" it. In
|
||||
// this way the events happen in a more logical sequence, like:
|
||||
// "focus 1" > "blur 1" > "focus 2"
|
||||
// ... instead of:
|
||||
// "focus 1" > "focus 2" > "blur 1"
|
||||
var current = CKEDITOR.currentInstance;
|
||||
current && current.focusManager.blur( 1 );
|
||||
|
||||
this.hasFocus = true;
|
||||
|
||||
var ct = this._.editor.container;
|
||||
ct && ct.addClass( 'cke_focus' );
|
||||
this._.editor.fire( 'focus' );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Prevent from changing the focus manager state until next {@link #unlock} is called.
|
||||
*
|
||||
* @member CKEDITOR.focusManager
|
||||
*/
|
||||
lock: function() {
|
||||
this._.locked = 1;
|
||||
},
|
||||
|
||||
/**
|
||||
* Restore the automatic focus management, if {@link #lock} is called.
|
||||
*
|
||||
* @member CKEDITOR.focusManager
|
||||
*/
|
||||
unlock: function() {
|
||||
delete this._.locked;
|
||||
},
|
||||
|
||||
/**
|
||||
* Used to indicate that the editor instance has been deactivated by the specified
|
||||
* element which has just lost focus.
|
||||
*
|
||||
* **Note:** that this functions acts asynchronously with a delay of 100ms to
|
||||
* avoid temporary deactivation. Use instead the `noDelay` parameter
|
||||
* to deactivate immediately.
|
||||
*
|
||||
* var editor = CKEDITOR.instances.editor1;
|
||||
* editor.focusManager.blur();
|
||||
*
|
||||
* @param {Boolean} [noDelay=false] Deactivate immediately the editor instance synchronously.
|
||||
* @member CKEDITOR.focusManager
|
||||
*/
|
||||
blur: function( noDelay ) {
|
||||
if ( this._.locked )
|
||||
return;
|
||||
|
||||
function doBlur() {
|
||||
if ( this.hasFocus ) {
|
||||
this.hasFocus = false;
|
||||
|
||||
var ct = this._.editor.container;
|
||||
ct && ct.removeClass( 'cke_focus' );
|
||||
this._.editor.fire( 'blur' );
|
||||
}
|
||||
}
|
||||
|
||||
if ( this._.timer )
|
||||
clearTimeout( this._.timer );
|
||||
|
||||
var delay = CKEDITOR.focusManager._.blurDelay;
|
||||
if ( noDelay || !delay )
|
||||
doBlur.call( this );
|
||||
else {
|
||||
this._.timer = CKEDITOR.tools.setTimeout( function() {
|
||||
delete this._.timer;
|
||||
doBlur.call( this );
|
||||
}, delay, this );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Register an UI DOM element to the focus manager, which will make the focus manager "hasFocus"
|
||||
* once input focus is relieved on the element, it's to be used by plugins to expand the jurisdiction of the editor focus.
|
||||
*
|
||||
* @param {CKEDITOR.dom.element} element The container (top most) element of one UI part.
|
||||
* @param {Boolean} isCapture If specified {@link CKEDITOR.event#useCapture} will be used when listening to the focus event.
|
||||
* @member CKEDITOR.focusManager
|
||||
*/
|
||||
add: function( element, isCapture ) {
|
||||
var fm = element.getCustomData( SLOT_NAME );
|
||||
if ( !fm || fm != this ) {
|
||||
// If this element is already taken by another instance, dismiss it first.
|
||||
fm && fm.remove( element );
|
||||
|
||||
var focusEvent = 'focus',
|
||||
blurEvent = 'blur';
|
||||
|
||||
// Bypass the element's internal DOM focus change.
|
||||
if ( isCapture ) {
|
||||
|
||||
// Use "focusin/focusout" events instead of capture phase in IEs,
|
||||
// which fires synchronously.
|
||||
if ( CKEDITOR.env.ie ) {
|
||||
focusEvent = 'focusin';
|
||||
blurEvent = 'focusout';
|
||||
} else
|
||||
CKEDITOR.event.useCapture = 1;
|
||||
}
|
||||
|
||||
var listeners = {
|
||||
blur: function() {
|
||||
if ( element.equals( this.currentActive ) )
|
||||
this.blur();
|
||||
},
|
||||
focus: function() {
|
||||
this.focus( element );
|
||||
}
|
||||
};
|
||||
|
||||
element.on( focusEvent, listeners.focus, this );
|
||||
element.on( blurEvent, listeners.blur, this );
|
||||
|
||||
if ( isCapture )
|
||||
CKEDITOR.event.useCapture = 0;
|
||||
|
||||
element.setCustomData( SLOT_NAME, this );
|
||||
element.setCustomData( SLOT_NAME_LISTENERS, listeners );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Dismiss an element from the the focus manager delegations added by {@link #add}.
|
||||
*
|
||||
* @param {CKEDITOR.dom.element} element The element to be removed from the focusmanager.
|
||||
* @member CKEDITOR.focusManager
|
||||
*/
|
||||
remove: function( element ) {
|
||||
element.removeCustomData( SLOT_NAME );
|
||||
var listeners = element.removeCustomData( SLOT_NAME_LISTENERS );
|
||||
element.removeListener( 'blur', listeners.blur );
|
||||
element.removeListener( 'focus', listeners.focus );
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} )();
|
||||
|
||||
/**
|
||||
* Fired when the editor instance receives the input focus.
|
||||
*
|
||||
* editor.on( 'focus', function( e ) {
|
||||
* alert( 'The editor named ' + e.editor.name + ' is now focused' );
|
||||
* } );
|
||||
*
|
||||
* @event focus
|
||||
* @member CKEDITOR.editor
|
||||
* @param {CKEDITOR.editor} editor The editor instance.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fired when the editor instance loses the input focus.
|
||||
*
|
||||
* **Note:** This event will **NOT** be triggered when focus is moved internally, e.g. from
|
||||
* the editable to other part of the editor UI like dialog.
|
||||
* If you're interested on only the editable focus state listen to the {@link CKEDITOR.editable#event-focus}
|
||||
* and {@link CKEDITOR.editable#blur} events instead.
|
||||
*
|
||||
* editor.on( 'blur', function( e ) {
|
||||
* alert( 'The editor named ' + e.editor.name + ' lost the focus' );
|
||||
* } );
|
||||
*
|
||||
* @event blur
|
||||
* @member CKEDITOR.editor
|
||||
* @param {CKEDITOR.editor} editor The editor instance.
|
||||
*/
|
||||
@@ -1,989 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
( function() {
|
||||
/**
|
||||
* Represents an HTML data processor, which is responsible for translating and
|
||||
* transforming the editor data on input and output.
|
||||
*
|
||||
* @class
|
||||
* @extends CKEDITOR.dataProcessor
|
||||
* @constructor Creates an htmlDataProcessor class instance.
|
||||
* @param {CKEDITOR.editor} editor
|
||||
*/
|
||||
CKEDITOR.htmlDataProcessor = function( editor ) {
|
||||
var dataFilter, htmlFilter,
|
||||
that = this;
|
||||
|
||||
this.editor = editor;
|
||||
|
||||
/**
|
||||
* Data filter used when processing input by {@link #toHtml}.
|
||||
*
|
||||
* @property {CKEDITOR.htmlParser.filter}
|
||||
*/
|
||||
this.dataFilter = dataFilter = new CKEDITOR.htmlParser.filter();
|
||||
|
||||
/**
|
||||
* HTML filter used when processing output by {@link #toDataFormat}.
|
||||
*
|
||||
* @property {CKEDITOR.htmlParser.filter}
|
||||
*/
|
||||
this.htmlFilter = htmlFilter = new CKEDITOR.htmlParser.filter();
|
||||
|
||||
/**
|
||||
* The HTML writer used by this data processor to format the output.
|
||||
*
|
||||
* @property {CKEDITOR.htmlParser.basicWriter}
|
||||
*/
|
||||
this.writer = new CKEDITOR.htmlParser.basicWriter();
|
||||
|
||||
dataFilter.addRules( defaultDataFilterRulesEditableOnly );
|
||||
dataFilter.addRules( defaultDataFilterRulesForAll, { applyToAll: true } );
|
||||
dataFilter.addRules( createBogusAndFillerRules( editor, 'data' ), { applyToAll: true } );
|
||||
htmlFilter.addRules( defaultHtmlFilterRulesEditableOnly );
|
||||
htmlFilter.addRules( defaultHtmlFilterRulesForAll, { applyToAll: true } );
|
||||
htmlFilter.addRules( createBogusAndFillerRules( editor, 'html' ), { applyToAll: true } );
|
||||
|
||||
editor.on( 'toHtml', function( evt ) {
|
||||
var evtData = evt.data,
|
||||
data = evtData.dataValue;
|
||||
|
||||
// The source data is already HTML, but we need to clean
|
||||
// it up and apply the filter.
|
||||
data = protectSource( data, editor );
|
||||
|
||||
// Protect content of textareas. (#9995)
|
||||
// Do this before protecting attributes to avoid breaking:
|
||||
// <textarea><img src="..." /></textarea>
|
||||
data = protectElements( data, protectTextareaRegex );
|
||||
|
||||
// Before anything, we must protect the URL attributes as the
|
||||
// browser may changing them when setting the innerHTML later in
|
||||
// the code.
|
||||
data = protectAttributes( data );
|
||||
|
||||
// Protect elements than can't be set inside a DIV. E.g. IE removes
|
||||
// style tags from innerHTML. (#3710)
|
||||
data = protectElements( data, protectElementsRegex );
|
||||
|
||||
// Certain elements has problem to go through DOM operation, protect
|
||||
// them by prefixing 'cke' namespace. (#3591)
|
||||
data = protectElementsNames( data );
|
||||
|
||||
// All none-IE browsers ignore self-closed custom elements,
|
||||
// protecting them into open-close. (#3591)
|
||||
data = protectSelfClosingElements( data );
|
||||
|
||||
// Compensate one leading line break after <pre> open as browsers
|
||||
// eat it up. (#5789)
|
||||
data = protectPreFormatted( data );
|
||||
|
||||
var fixBin = evtData.context || editor.editable().getName(),
|
||||
isPre;
|
||||
|
||||
// Old IEs loose formats when load html into <pre>.
|
||||
if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 && fixBin == 'pre' ) {
|
||||
fixBin = 'div';
|
||||
data = '<pre>' + data + '</pre>';
|
||||
isPre = 1;
|
||||
}
|
||||
|
||||
// Call the browser to help us fixing a possibly invalid HTML
|
||||
// structure.
|
||||
var el = editor.document.createElement( fixBin );
|
||||
// Add fake character to workaround IE comments bug. (#3801)
|
||||
el.setHtml( 'a' + data );
|
||||
data = el.getHtml().substr( 1 );
|
||||
|
||||
// Restore shortly protected attribute names.
|
||||
data = data.replace( new RegExp( ' data-cke-' + CKEDITOR.rnd + '-', 'ig' ), ' ' );
|
||||
|
||||
isPre && ( data = data.replace( /^<pre>|<\/pre>$/gi, '' ) );
|
||||
|
||||
// Unprotect "some" of the protected elements at this point.
|
||||
data = unprotectElementNames( data );
|
||||
|
||||
data = unprotectElements( data );
|
||||
|
||||
// Restore the comments that have been protected, in this way they
|
||||
// can be properly filtered.
|
||||
data = unprotectRealComments( data );
|
||||
|
||||
// Now use our parser to make further fixes to the structure, as
|
||||
// well as apply the filter.
|
||||
evtData.dataValue = CKEDITOR.htmlParser.fragment.fromHtml(
|
||||
data, evtData.context, evtData.fixForBody === false ? false : getFixBodyTag( evtData.enterMode, editor.config.autoParagraph ) );
|
||||
}, null, null, 5 );
|
||||
|
||||
// Filter incoming "data".
|
||||
// Add element filter before htmlDataProcessor.dataFilter when purifying input data to correct html.
|
||||
editor.on( 'toHtml', function( evt ) {
|
||||
if ( evt.data.filter.applyTo( evt.data.dataValue, true, evt.data.dontFilter, evt.data.enterMode ) )
|
||||
editor.fire( 'dataFiltered' );
|
||||
}, null, null, 6 );
|
||||
|
||||
editor.on( 'toHtml', function( evt ) {
|
||||
evt.data.dataValue.filterChildren( that.dataFilter, true );
|
||||
}, null, null, 10 );
|
||||
|
||||
editor.on( 'toHtml', function( evt ) {
|
||||
var evtData = evt.data,
|
||||
data = evtData.dataValue,
|
||||
writer = new CKEDITOR.htmlParser.basicWriter();
|
||||
|
||||
data.writeChildrenHtml( writer );
|
||||
data = writer.getHtml( true );
|
||||
|
||||
// Protect the real comments again.
|
||||
evtData.dataValue = protectRealComments( data );
|
||||
}, null, null, 15 );
|
||||
|
||||
|
||||
editor.on( 'toDataFormat', function( evt ) {
|
||||
var data = evt.data.dataValue;
|
||||
|
||||
// #10854 - we need to strip leading blockless <br> which FF adds
|
||||
// automatically when editable contains only non-editable content.
|
||||
// We do that for every browser (so it's a constant behavior) and
|
||||
// not in BR mode, in which chance of valid leading blockless <br> is higher.
|
||||
if ( evt.data.enterMode != CKEDITOR.ENTER_BR )
|
||||
data = data.replace( /^<br *\/?>/i, '' );
|
||||
|
||||
evt.data.dataValue = CKEDITOR.htmlParser.fragment.fromHtml(
|
||||
data, evt.data.context, getFixBodyTag( evt.data.enterMode, editor.config.autoParagraph ) );
|
||||
}, null, null, 5 );
|
||||
|
||||
editor.on( 'toDataFormat', function( evt ) {
|
||||
evt.data.dataValue.filterChildren( that.htmlFilter, true );
|
||||
}, null, null, 10 );
|
||||
|
||||
// Transform outcoming "data".
|
||||
// Add element filter after htmlDataProcessor.htmlFilter when preparing output data HTML.
|
||||
editor.on( 'toDataFormat', function( evt ) {
|
||||
evt.data.filter.applyTo( evt.data.dataValue, false, true );
|
||||
}, null, null, 11 );
|
||||
|
||||
editor.on( 'toDataFormat', function( evt ) {
|
||||
var data = evt.data.dataValue,
|
||||
writer = that.writer;
|
||||
|
||||
writer.reset();
|
||||
data.writeChildrenHtml( writer );
|
||||
data = writer.getHtml( true );
|
||||
|
||||
// Restore those non-HTML protected source. (#4475,#4880)
|
||||
data = unprotectRealComments( data );
|
||||
data = unprotectSource( data, editor );
|
||||
|
||||
evt.data.dataValue = data;
|
||||
}, null, null, 15 );
|
||||
};
|
||||
|
||||
CKEDITOR.htmlDataProcessor.prototype = {
|
||||
/**
|
||||
* Processes the input (potentially malformed) HTML to a purified form which
|
||||
* is suitable for using in the WYSIWYG editable.
|
||||
*
|
||||
* This method fires the {@link CKEDITOR.editor#toHtml} event which makes it possible
|
||||
* to hook into the process at various stages.
|
||||
*
|
||||
* **Note:** Since CKEditor 4.3 the signature of this method changed and all options
|
||||
* are now grouped in one `options` object. Previously `context`, `fixForBody` and `dontFilter`
|
||||
* were passed separately.
|
||||
*
|
||||
* @param {String} data The raw data.
|
||||
* @param {Object} [options] The options object.
|
||||
* @param {String} [options.context] The tag name of a context element within which
|
||||
* the input is to be processed, default to be the editable element.
|
||||
* If `null` is passed, then data will be parsed without context (as children of {@link CKEDITOR.htmlParser.fragment}).
|
||||
* See {@link CKEDITOR.htmlParser.fragment#fromHtml} for more details.
|
||||
* @param {Boolean} [options.fixForBody=true] Whether to trigger the auto paragraph for non-block contents.
|
||||
* @param {CKEDITOR.filter} [options.filter] When specified, instead of using the {@link CKEDITOR.editor#filter main filter},
|
||||
* passed instance will be used to filter the content.
|
||||
* @param {Boolean} [options.dontFilter] Do not filter data with {@link CKEDITOR.filter} (note: transformations
|
||||
* will be still applied).
|
||||
* @param {Number} [options.enterMode] When specified it will be used instead of the {@link CKEDITOR.editor#enterMode main enterMode}.
|
||||
* @returns {String}
|
||||
*/
|
||||
toHtml: function( data, options, fixForBody, dontFilter ) {
|
||||
var editor = this.editor,
|
||||
context, filter, enterMode;
|
||||
|
||||
// Typeof null == 'object', so check truthiness of options too.
|
||||
if ( options && typeof options == 'object' ) {
|
||||
context = options.context;
|
||||
fixForBody = options.fixForBody;
|
||||
dontFilter = options.dontFilter;
|
||||
filter = options.filter;
|
||||
enterMode = options.enterMode;
|
||||
}
|
||||
// Backward compatibility. Since CKEDITOR 4.3 every option was a separate argument.
|
||||
else
|
||||
context = options;
|
||||
|
||||
// Fall back to the editable as context if not specified.
|
||||
if ( !context && context !== null )
|
||||
context = editor.editable().getName();
|
||||
|
||||
return editor.fire( 'toHtml', {
|
||||
dataValue: data,
|
||||
context: context,
|
||||
fixForBody: fixForBody,
|
||||
dontFilter: dontFilter,
|
||||
filter: filter || editor.filter,
|
||||
enterMode: enterMode || editor.enterMode
|
||||
} ).dataValue;
|
||||
},
|
||||
|
||||
/**
|
||||
* See {@link CKEDITOR.dataProcessor#toDataFormat}.
|
||||
*
|
||||
* This method fires the {@link CKEDITOR.editor#toDataFormat} event which makes it possible
|
||||
* to hook into the process at various steps.
|
||||
*
|
||||
* @param {String} html
|
||||
* @param {Object} [options] The options object.
|
||||
* @param {String} [options.context] The tag name of a context element within which
|
||||
* the input is to be processed, default to be the editable element.
|
||||
* @param {CKEDITOR.filter} [options.filter] When specified, instead of using the {@link CKEDITOR.editor#filter main filter},
|
||||
* passed instance will be used to apply content transformations to the content.
|
||||
* @param {Number} [options.enterMode] When specified it will be used instead of the {@link CKEDITOR.editor#enterMode main enterMode}.
|
||||
* @returns {String}
|
||||
*/
|
||||
toDataFormat: function( html, options ) {
|
||||
var context, filter, enterMode;
|
||||
|
||||
// Do not shorten this to `options && options.xxx`, because
|
||||
// falsy `options` will be passed instead of undefined.
|
||||
if ( options ) {
|
||||
context = options.context;
|
||||
filter = options.filter;
|
||||
enterMode = options.enterMode;
|
||||
}
|
||||
|
||||
// Fall back to the editable as context if not specified.
|
||||
if ( !context && context !== null )
|
||||
context = this.editor.editable().getName();
|
||||
|
||||
return this.editor.fire( 'toDataFormat', {
|
||||
dataValue: html,
|
||||
filter: filter || this.editor.filter,
|
||||
context: context,
|
||||
enterMode: enterMode || this.editor.enterMode
|
||||
} ).dataValue;
|
||||
}
|
||||
};
|
||||
|
||||
// Produce a set of filtering rules that handles bogus and filler node at the
|
||||
// end of block/pseudo block, in the following consequence:
|
||||
// 1. elements:<block> - this filter removes any bogus node, then check
|
||||
// if it's an empty block that requires a filler.
|
||||
// 2. elements:<br> - After cleaned with bogus, this filter checks the real
|
||||
// line-break BR to compensate a filler after it.
|
||||
//
|
||||
// Terms definitions:
|
||||
// filler: An element that's either <BR> or &NBSP; at the end of block that established line height.
|
||||
// bogus: Whenever a filler is proceeded with inline content, it becomes a bogus which is subjected to be removed.
|
||||
//
|
||||
// Various forms of the filler:
|
||||
// In output HTML: Filler should be consistently &NBSP; <BR> at the end of block is always considered as bogus.
|
||||
// In Wysiwyg HTML: Browser dependent - see env.needsBrFiller. Either BR for when needsBrFiller is true, or &NBSP; otherwise.
|
||||
// <BR> is NEVER considered as bogus when needsBrFiller is true.
|
||||
function createBogusAndFillerRules( editor, type ) {
|
||||
function createFiller( isOutput ) {
|
||||
return isOutput || CKEDITOR.env.needsNbspFiller ?
|
||||
new CKEDITOR.htmlParser.text( '\xa0' ) :
|
||||
new CKEDITOR.htmlParser.element( 'br', { 'data-cke-bogus': 1 } );
|
||||
}
|
||||
|
||||
// This text block filter, remove any bogus and create the filler on demand.
|
||||
function blockFilter( isOutput, fillEmptyBlock ) {
|
||||
|
||||
return function( block ) {
|
||||
|
||||
// DO NOT apply the filer if it's a fragment node.
|
||||
if ( block.type == CKEDITOR.NODE_DOCUMENT_FRAGMENT )
|
||||
return;
|
||||
|
||||
cleanBogus( block );
|
||||
|
||||
// [Opera] it's mandatory for the filler to present inside of empty block when in WYSIWYG.
|
||||
if ( ( ( CKEDITOR.env.opera && !isOutput ) ||
|
||||
( typeof fillEmptyBlock == 'function' ? fillEmptyBlock( block ) !== false : fillEmptyBlock ) ) &&
|
||||
isEmptyBlockNeedFiller( block ) )
|
||||
{
|
||||
block.add( createFiller( isOutput ) );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Append a filler right after the last line-break BR, found at the end of block.
|
||||
function brFilter( isOutput ) {
|
||||
return function( br ) {
|
||||
|
||||
// DO NOT apply the filer if parent's a fragment node.
|
||||
if ( br.parent.type == CKEDITOR.NODE_DOCUMENT_FRAGMENT )
|
||||
return;
|
||||
|
||||
var attrs = br.attributes;
|
||||
// Dismiss BRs that are either bogus or eol marker.
|
||||
if ( 'data-cke-bogus' in attrs ||
|
||||
'data-cke-eol' in attrs ) {
|
||||
delete attrs [ 'data-cke-bogus' ];
|
||||
return;
|
||||
}
|
||||
|
||||
// Judge the tail line-break BR, and to insert bogus after it.
|
||||
var next = getNext( br ), previous = getPrevious( br );
|
||||
|
||||
if ( !next && isBlockBoundary( br.parent ) )
|
||||
append( br.parent, createFiller( isOutput ) );
|
||||
else if ( isBlockBoundary( next ) && previous && !isBlockBoundary( previous ) )
|
||||
createFiller( isOutput ).insertBefore( next );
|
||||
};
|
||||
}
|
||||
|
||||
// Determinate whether this node is potentially a bogus node.
|
||||
function maybeBogus( node, atBlockEnd ) {
|
||||
|
||||
// BR that's not from IE<11 DOM, except for a EOL marker.
|
||||
if ( !( isOutput && !CKEDITOR.env.needsBrFiller ) &&
|
||||
node.type == CKEDITOR.NODE_ELEMENT && node.name == 'br' &&
|
||||
!node.attributes[ 'data-cke-eol' ] )
|
||||
return true;
|
||||
|
||||
var match;
|
||||
// NBSP, possibly.
|
||||
if ( node.type == CKEDITOR.NODE_TEXT &&
|
||||
( match = node.value.match( tailNbspRegex ) ) )
|
||||
{
|
||||
// We need to separate tail NBSP out of a text node, for later removal.
|
||||
if ( match.index ) {
|
||||
( new CKEDITOR.htmlParser.text( node.value.substring( 0, match.index ) ) ).insertBefore( node );
|
||||
node.value = match[ 0 ];
|
||||
}
|
||||
|
||||
// From IE<11 DOM, at the end of a text block, or before block boundary.
|
||||
if ( !CKEDITOR.env.needsBrFiller && isOutput && ( !atBlockEnd || node.parent.name in textBlockTags ) )
|
||||
return true;
|
||||
|
||||
// From the output.
|
||||
if ( !isOutput ) {
|
||||
var previous = node.previous;
|
||||
|
||||
// Following a line-break at the end of block.
|
||||
if ( previous && previous.name == 'br' )
|
||||
return true;
|
||||
|
||||
// Or a single NBSP between two blocks.
|
||||
if ( !previous || isBlockBoundary( previous ) )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Removes all bogus inside of this block, and to convert fillers into the proper form.
|
||||
function cleanBogus( block ) {
|
||||
var bogus = [];
|
||||
var last = getLast( block ), node, previous;
|
||||
if ( last ) {
|
||||
|
||||
// Check for bogus at the end of this block.
|
||||
// e.g. <p>foo<br /></p>
|
||||
maybeBogus( last, 1 ) && bogus.push( last );
|
||||
|
||||
while ( last ) {
|
||||
|
||||
// Check for bogus at the end of any pseudo block contained.
|
||||
if ( isBlockBoundary( last ) &&
|
||||
( node = getPrevious( last ) ) &&
|
||||
maybeBogus( node ) )
|
||||
{
|
||||
// Bogus must have inline proceeding, instead single BR between two blocks,
|
||||
// is considered as filler, e.g. <hr /><br /><hr />
|
||||
if ( ( previous = getPrevious( node ) ) && !isBlockBoundary( previous ) )
|
||||
bogus.push( node );
|
||||
// Convert the filler into appropriate form.
|
||||
else {
|
||||
createFiller( isOutput ).insertAfter( node );
|
||||
node.remove();
|
||||
}
|
||||
}
|
||||
|
||||
last = last.previous;
|
||||
}
|
||||
}
|
||||
|
||||
// Now remove all bogus collected from above.
|
||||
for ( var i = 0 ; i < bogus.length ; i++ )
|
||||
bogus[ i ].remove();
|
||||
}
|
||||
|
||||
// Judge whether it's an empty block that requires a filler node.
|
||||
function isEmptyBlockNeedFiller( block ) {
|
||||
|
||||
// DO NOT fill empty editable in IE<11.
|
||||
if ( !isOutput && !CKEDITOR.env.needsBrFiller && block.type == CKEDITOR.NODE_DOCUMENT_FRAGMENT )
|
||||
return false;
|
||||
|
||||
// 1. For IE version >=8, empty blocks are displayed correctly themself in wysiwiyg;
|
||||
// 2. For the rest, at least table cell and list item need no filler space. (#6248)
|
||||
if ( !isOutput && !CKEDITOR.env.needsBrFiller &&
|
||||
( document.documentMode > 7 ||
|
||||
block.name in CKEDITOR.dtd.tr ||
|
||||
block.name in CKEDITOR.dtd.$listItem ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var last = getLast( block );
|
||||
return !last || block.name == 'form' && last.name == 'input' ;
|
||||
}
|
||||
|
||||
var rules = { elements: {} };
|
||||
var isOutput = type == 'html';
|
||||
|
||||
// Build the list of text blocks.
|
||||
var textBlockTags = CKEDITOR.tools.extend( {}, blockLikeTags );
|
||||
for ( var i in textBlockTags ) {
|
||||
if ( !( '#' in dtd[ i ] ) )
|
||||
delete textBlockTags[ i ];
|
||||
}
|
||||
|
||||
for ( i in textBlockTags )
|
||||
rules.elements[ i ] = blockFilter( isOutput, editor.config.fillEmptyBlocks !== false );
|
||||
|
||||
// Editable element is to be checked separately.
|
||||
rules.root = blockFilter( isOutput );
|
||||
rules.elements.br = brFilter( isOutput );
|
||||
return rules;
|
||||
}
|
||||
|
||||
function getFixBodyTag( enterMode, autoParagraph ) {
|
||||
return ( enterMode != CKEDITOR.ENTER_BR && autoParagraph !== false ) ? enterMode == CKEDITOR.ENTER_DIV ? 'div' : 'p' : false;
|
||||
}
|
||||
|
||||
// Regex to scan for at the end of blocks, which are actually placeholders.
|
||||
// Safari transforms the to \xa0. (#4172)
|
||||
var tailNbspRegex = /(?: |\xa0)$/;
|
||||
|
||||
var protectedSourceMarker = '{cke_protected}';
|
||||
|
||||
function getLast( node ) {
|
||||
var last = node.children[ node.children.length - 1 ];
|
||||
while ( last && isEmpty( last ) )
|
||||
last = last.previous;
|
||||
return last;
|
||||
}
|
||||
|
||||
function getNext( node ) {
|
||||
var next = node.next;
|
||||
while ( next && isEmpty( next ) )
|
||||
next = next.next;
|
||||
return next;
|
||||
}
|
||||
|
||||
function getPrevious( node ) {
|
||||
var previous = node.previous;
|
||||
while ( previous && isEmpty( previous ) )
|
||||
previous = previous.previous;
|
||||
return previous;
|
||||
}
|
||||
|
||||
// Judge whether the node is an ghost node to be ignored, when traversing.
|
||||
function isEmpty( node ) {
|
||||
return node.type == CKEDITOR.NODE_TEXT &&
|
||||
!CKEDITOR.tools.trim( node.value ) ||
|
||||
node.type == CKEDITOR.NODE_ELEMENT &&
|
||||
node.attributes[ 'data-cke-bookmark' ];
|
||||
}
|
||||
|
||||
// Judge whether the node is a block-like element.
|
||||
function isBlockBoundary( node ) {
|
||||
return node &&
|
||||
( node.type == CKEDITOR.NODE_ELEMENT && node.name in blockLikeTags ||
|
||||
node.type == CKEDITOR.NODE_DOCUMENT_FRAGMENT );
|
||||
}
|
||||
|
||||
function append( parent, node ) {
|
||||
var last = parent.children[ parent.children.length -1 ];
|
||||
parent.children.push( node );
|
||||
node.parent = parent;
|
||||
if ( last ) {
|
||||
last.next = node;
|
||||
node.previous = last;
|
||||
}
|
||||
}
|
||||
|
||||
function getNodeIndex( node ) {
|
||||
return node.parent ? node.getIndex() : -1;
|
||||
}
|
||||
|
||||
var dtd = CKEDITOR.dtd,
|
||||
// Define orders of table elements.
|
||||
tableOrder = [ 'caption', 'colgroup', 'col', 'thead', 'tfoot', 'tbody' ],
|
||||
// List of all block elements.
|
||||
blockLikeTags = CKEDITOR.tools.extend( {}, dtd.$blockLimit, dtd.$block );
|
||||
|
||||
//
|
||||
// DATA filter rules ------------------------------------------------------
|
||||
//
|
||||
|
||||
var defaultDataFilterRulesEditableOnly = {
|
||||
elements: {
|
||||
input: protectReadOnly,
|
||||
textarea: protectReadOnly
|
||||
}
|
||||
};
|
||||
|
||||
// These rules will also be applied to non-editable content.
|
||||
var defaultDataFilterRulesForAll = {
|
||||
attributeNames: [
|
||||
// Event attributes (onXYZ) must not be directly set. They can become
|
||||
// active in the editing area (IE|WebKit).
|
||||
[ ( /^on/ ), 'data-cke-pa-on' ],
|
||||
|
||||
// Don't let some old expando enter editor. Concerns only IE8,
|
||||
// but for consistency remove on all browsers.
|
||||
[ ( /^data-cke-expando$/ ), '' ]
|
||||
]
|
||||
};
|
||||
|
||||
// Disable form elements editing mode provided by some browsers. (#5746)
|
||||
function protectReadOnly( element ) {
|
||||
var attrs = element.attributes;
|
||||
|
||||
// We should flag that the element was locked by our code so
|
||||
// it'll be editable by the editor functions (#6046).
|
||||
if ( attrs.contenteditable != 'false' )
|
||||
attrs[ 'data-cke-editable' ] = attrs.contenteditable ? 'true' : 1;
|
||||
|
||||
attrs.contenteditable = 'false';
|
||||
}
|
||||
|
||||
//
|
||||
// HTML filter rules ------------------------------------------------------
|
||||
//
|
||||
|
||||
var defaultHtmlFilterRulesEditableOnly = {
|
||||
elements: {
|
||||
embed: function( element ) {
|
||||
var parent = element.parent;
|
||||
|
||||
// If the <embed> is child of a <object>, copy the width
|
||||
// and height attributes from it.
|
||||
if ( parent && parent.name == 'object' ) {
|
||||
var parentWidth = parent.attributes.width,
|
||||
parentHeight = parent.attributes.height;
|
||||
if ( parentWidth )
|
||||
element.attributes.width = parentWidth;
|
||||
if ( parentHeight )
|
||||
element.attributes.height = parentHeight;
|
||||
}
|
||||
},
|
||||
|
||||
// Remove empty link but not empty anchor. (#3829)
|
||||
a: function( element ) {
|
||||
if ( !( element.children.length || element.attributes.name || element.attributes[ 'data-cke-saved-name' ] ) )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// These rules will also be applied to non-editable content.
|
||||
var defaultHtmlFilterRulesForAll = {
|
||||
elementNames: [
|
||||
// Remove the "cke:" namespace prefix.
|
||||
[ ( /^cke:/ ), '' ],
|
||||
|
||||
// Ignore <?xml:namespace> tags.
|
||||
[ ( /^\?xml:namespace$/ ), '' ]
|
||||
],
|
||||
|
||||
attributeNames: [
|
||||
// Attributes saved for changes and protected attributes.
|
||||
[ ( /^data-cke-(saved|pa)-/ ), '' ],
|
||||
|
||||
// All "data-cke-" attributes are to be ignored.
|
||||
[ ( /^data-cke-.*/ ), '' ],
|
||||
|
||||
[ 'hidefocus', '' ]
|
||||
],
|
||||
|
||||
elements: {
|
||||
$: function( element ) {
|
||||
var attribs = element.attributes;
|
||||
|
||||
if ( attribs ) {
|
||||
// Elements marked as temporary are to be ignored.
|
||||
if ( attribs[ 'data-cke-temp' ] )
|
||||
return false;
|
||||
|
||||
// Remove duplicated attributes - #3789.
|
||||
var attributeNames = [ 'name', 'href', 'src' ],
|
||||
savedAttributeName;
|
||||
for ( var i = 0; i < attributeNames.length; i++ ) {
|
||||
savedAttributeName = 'data-cke-saved-' + attributeNames[ i ];
|
||||
savedAttributeName in attribs && ( delete attribs[ attributeNames[ i ] ] );
|
||||
}
|
||||
}
|
||||
|
||||
return element;
|
||||
},
|
||||
|
||||
// The contents of table should be in correct order (#4809).
|
||||
table: function( element ) {
|
||||
// Clone the array as it would become empty during the sort call.
|
||||
var children = element.children.slice( 0 );
|
||||
children.sort( function( node1, node2 ) {
|
||||
var index1, index2;
|
||||
|
||||
// Compare in the predefined order.
|
||||
if ( node1.type == CKEDITOR.NODE_ELEMENT &&
|
||||
node2.type == node1.type ) {
|
||||
index1 = CKEDITOR.tools.indexOf( tableOrder, node1.name );
|
||||
index2 = CKEDITOR.tools.indexOf( tableOrder, node2.name );
|
||||
}
|
||||
|
||||
// Make sure the sort is stable, if no order can be established above.
|
||||
if ( !( index1 > -1 && index2 > -1 && index1 != index2 ) ) {
|
||||
index1 = getNodeIndex( node1 );
|
||||
index2 = getNodeIndex( node2 );
|
||||
}
|
||||
|
||||
return index1 > index2 ? 1 : -1;
|
||||
} );
|
||||
},
|
||||
|
||||
// Restore param elements into self-closing.
|
||||
param: function( param ) {
|
||||
param.children = [];
|
||||
param.isEmpty = true;
|
||||
return param;
|
||||
},
|
||||
|
||||
// Remove dummy span in webkit.
|
||||
span: function( element ) {
|
||||
if ( element.attributes[ 'class' ] == 'Apple-style-span' )
|
||||
delete element.name;
|
||||
},
|
||||
|
||||
html: function( element ) {
|
||||
delete element.attributes.contenteditable;
|
||||
delete element.attributes[ 'class' ];
|
||||
},
|
||||
|
||||
body: function( element ) {
|
||||
delete element.attributes.spellcheck;
|
||||
delete element.attributes.contenteditable;
|
||||
},
|
||||
|
||||
style: function( element ) {
|
||||
var child = element.children[ 0 ];
|
||||
if ( child && child.value )
|
||||
child.value = CKEDITOR.tools.trim( child.value );
|
||||
|
||||
if ( !element.attributes.type )
|
||||
element.attributes.type = 'text/css';
|
||||
},
|
||||
|
||||
title: function( element ) {
|
||||
var titleText = element.children[ 0 ];
|
||||
|
||||
// Append text-node to title tag if not present (i.e. non-IEs) (#9882).
|
||||
!titleText && append( element, titleText = new CKEDITOR.htmlParser.text() );
|
||||
|
||||
// Transfer data-saved title to title tag.
|
||||
titleText.value = element.attributes[ 'data-cke-title' ] || '';
|
||||
},
|
||||
|
||||
input: unprotectReadyOnly,
|
||||
textarea: unprotectReadyOnly
|
||||
},
|
||||
|
||||
attributes: {
|
||||
'class': function( value, element ) {
|
||||
// Remove all class names starting with "cke_".
|
||||
return CKEDITOR.tools.ltrim( value.replace( /(?:^|\s+)cke_[^\s]*/g, '' ) ) || false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if ( CKEDITOR.env.ie ) {
|
||||
// IE outputs style attribute in capital letters. We should convert
|
||||
// them back to lower case, while not hurting the values (#5930)
|
||||
defaultHtmlFilterRulesForAll.attributes.style = function( value, element ) {
|
||||
return value.replace( /(^|;)([^\:]+)/g, function( match ) {
|
||||
return match.toLowerCase();
|
||||
} );
|
||||
};
|
||||
}
|
||||
|
||||
// Disable form elements editing mode provided by some browsers. (#5746)
|
||||
function unprotectReadyOnly( element ) {
|
||||
var attrs = element.attributes;
|
||||
switch ( attrs[ 'data-cke-editable' ] ) {
|
||||
case 'true':
|
||||
attrs.contenteditable = 'true';
|
||||
break;
|
||||
case '1':
|
||||
delete attrs.contenteditable;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Preprocessor filters ---------------------------------------------------
|
||||
//
|
||||
|
||||
var protectElementRegex = /<(a|area|img|input|source)\b([^>]*)>/gi,
|
||||
// Be greedy while looking for protected attributes. This will let us avoid an unfortunate
|
||||
// situation when "nested attributes", which may appear valid, are also protected.
|
||||
// I.e. if we consider the following HTML:
|
||||
//
|
||||
// <img data-x="<a href="X"" />
|
||||
//
|
||||
// then the "non-greedy match" returns:
|
||||
//
|
||||
// 'href' => '"X"' // It's wrong! Href is not an attribute of <img>.
|
||||
//
|
||||
// while greedy match returns:
|
||||
//
|
||||
// 'data-x' => '<a href="X"'
|
||||
//
|
||||
// which, can be easily filtered out (#11508).
|
||||
protectAttributeRegex = /([\w-]+)\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|(?:[^ "'>]+))/gi,
|
||||
protectAttributeNameRegex = /^(href|src|name)$/i;
|
||||
|
||||
// Note: we use lazy star '*?' to prevent eating everything up to the last occurrence of </style> or </textarea>.
|
||||
var protectElementsRegex = /(?:<style(?=[ >])[^>]*>[\s\S]*?<\/style>)|(?:<(:?link|meta|base)[^>]*>)/gi,
|
||||
protectTextareaRegex = /(<textarea(?=[ >])[^>]*>)([\s\S]*?)(?:<\/textarea>)/gi,
|
||||
encodedElementsRegex = /<cke:encoded>([^<]*)<\/cke:encoded>/gi;
|
||||
|
||||
var protectElementNamesRegex = /(<\/?)((?:object|embed|param|html|body|head|title)[^>]*>)/gi,
|
||||
unprotectElementNamesRegex = /(<\/?)cke:((?:html|body|head|title)[^>]*>)/gi;
|
||||
|
||||
var protectSelfClosingRegex = /<cke:(param|embed)([^>]*?)\/?>(?!\s*<\/cke:\1)/gi;
|
||||
|
||||
function protectAttributes( html ) {
|
||||
return html.replace( protectElementRegex, function( element, tag, attributes ) {
|
||||
return '<' + tag + attributes.replace( protectAttributeRegex, function( fullAttr, attrName ) {
|
||||
// Avoid corrupting the inline event attributes (#7243).
|
||||
// We should not rewrite the existed protected attributes, e.g. clipboard content from editor. (#5218)
|
||||
if ( protectAttributeNameRegex.test( attrName ) && attributes.indexOf( 'data-cke-saved-' + attrName ) == -1 )
|
||||
return ' data-cke-saved-' + fullAttr + ' data-cke-' + CKEDITOR.rnd + '-' + fullAttr;
|
||||
|
||||
return fullAttr;
|
||||
} ) + '>';
|
||||
} );
|
||||
}
|
||||
|
||||
function protectElements( html, regex ) {
|
||||
return html.replace( regex, function( match, tag, content ) {
|
||||
// Encode < and > in textarea because this won't be done by a browser, since
|
||||
// textarea will be protected during passing data through fix bin.
|
||||
if ( match.indexOf( '<textarea' ) === 0 )
|
||||
match = tag + unprotectRealComments( content ).replace( /</g, '<' ).replace( />/g, '>' ) + '</textarea>';
|
||||
|
||||
return '<cke:encoded>' + encodeURIComponent( match ) + '</cke:encoded>';
|
||||
} );
|
||||
}
|
||||
|
||||
function unprotectElements( html ) {
|
||||
return html.replace( encodedElementsRegex, function( match, encoded ) {
|
||||
return decodeURIComponent( encoded );
|
||||
} );
|
||||
}
|
||||
|
||||
function protectElementsNames( html ) {
|
||||
return html.replace( protectElementNamesRegex, '$1cke:$2' );
|
||||
}
|
||||
|
||||
function unprotectElementNames( html ) {
|
||||
return html.replace( unprotectElementNamesRegex, '$1$2' );
|
||||
}
|
||||
|
||||
function protectSelfClosingElements( html ) {
|
||||
return html.replace( protectSelfClosingRegex, '<cke:$1$2></cke:$1>' );
|
||||
}
|
||||
|
||||
function protectPreFormatted( html ) {
|
||||
return CKEDITOR.env.opera ? html : html.replace( /(<pre\b[^>]*>)(\r\n|\n)/g, '$1$2$2' );
|
||||
}
|
||||
|
||||
function protectRealComments( html ) {
|
||||
return html.replace( /<!--(?!{cke_protected})[\s\S]+?-->/g, function( match ) {
|
||||
return '<!--' + protectedSourceMarker +
|
||||
'{C}' +
|
||||
encodeURIComponent( match ).replace( /--/g, '%2D%2D' ) +
|
||||
'-->';
|
||||
} );
|
||||
}
|
||||
|
||||
function unprotectRealComments( html ) {
|
||||
return html.replace( /<!--\{cke_protected\}\{C\}([\s\S]+?)-->/g, function( match, data ) {
|
||||
return decodeURIComponent( data );
|
||||
} );
|
||||
}
|
||||
|
||||
function unprotectSource( html, editor ) {
|
||||
var store = editor._.dataStore;
|
||||
|
||||
return html.replace( /<!--\{cke_protected\}([\s\S]+?)-->/g, function( match, data ) {
|
||||
return decodeURIComponent( data );
|
||||
} ).replace( /\{cke_protected_(\d+)\}/g, function( match, id ) {
|
||||
return store && store[ id ] || '';
|
||||
} );
|
||||
}
|
||||
|
||||
function protectSource( data, editor ) {
|
||||
var protectedHtml = [],
|
||||
protectRegexes = editor.config.protectedSource,
|
||||
store = editor._.dataStore || ( editor._.dataStore = { id: 1 } ),
|
||||
tempRegex = /<\!--\{cke_temp(comment)?\}(\d*?)-->/g;
|
||||
|
||||
var regexes = [
|
||||
// Script tags will also be forced to be protected, otherwise
|
||||
// IE will execute them.
|
||||
( /<script[\s\S]*?<\/script>/gi ),
|
||||
|
||||
// <noscript> tags (get lost in IE and messed up in FF).
|
||||
/<noscript[\s\S]*?<\/noscript>/gi
|
||||
].concat( protectRegexes );
|
||||
|
||||
// First of any other protection, we must protect all comments
|
||||
// to avoid loosing them (of course, IE related).
|
||||
// Note that we use a different tag for comments, as we need to
|
||||
// transform them when applying filters.
|
||||
data = data.replace( ( /<!--[\s\S]*?-->/g ), function( match ) {
|
||||
return '<!--{cke_tempcomment}' + ( protectedHtml.push( match ) - 1 ) + '-->';
|
||||
} );
|
||||
|
||||
for ( var i = 0; i < regexes.length; i++ ) {
|
||||
data = data.replace( regexes[ i ], function( match ) {
|
||||
match = match.replace( tempRegex, // There could be protected source inside another one. (#3869).
|
||||
function( $, isComment, id ) {
|
||||
return protectedHtml[ id ];
|
||||
} );
|
||||
|
||||
// Avoid protecting over protected, e.g. /\{.*?\}/
|
||||
return ( /cke_temp(comment)?/ ).test( match ) ? match : '<!--{cke_temp}' + ( protectedHtml.push( match ) - 1 ) + '-->';
|
||||
} );
|
||||
}
|
||||
data = data.replace( tempRegex, function( $, isComment, id ) {
|
||||
return '<!--' + protectedSourceMarker +
|
||||
( isComment ? '{C}' : '' ) +
|
||||
encodeURIComponent( protectedHtml[ id ] ).replace( /--/g, '%2D%2D' ) +
|
||||
'-->';
|
||||
} );
|
||||
|
||||
// Different protection pattern is used for those that
|
||||
// live in attributes to avoid from being HTML encoded.
|
||||
return data.replace( /(['"]).*?\1/g, function( match ) {
|
||||
return match.replace( /<!--\{cke_protected\}([\s\S]+?)-->/g, function( match, data ) {
|
||||
store[ store.id ] = decodeURIComponent( data );
|
||||
return '{cke_protected_' + ( store.id++ ) + '}';
|
||||
} );
|
||||
} );
|
||||
}
|
||||
} )();
|
||||
|
||||
/**
|
||||
* Whether a filler text (non-breaking space entity — ` `) will be
|
||||
* inserted into empty block elements in HTML output.
|
||||
* This is used to render block elements properly with `line-height`.
|
||||
* When a function is specified instead, it will be passed a {@link CKEDITOR.htmlParser.element}
|
||||
* to decide whether adding the filler text by expecting a Boolean return value.
|
||||
*
|
||||
* config.fillEmptyBlocks = false; // Prevent filler nodes in all empty blocks.
|
||||
*
|
||||
* // Prevent filler node only in float cleaners.
|
||||
* config.fillEmptyBlocks = function( element ) {
|
||||
* if ( element.attributes[ 'class' ].indexOf( 'clear-both' ) != -1 )
|
||||
* return false;
|
||||
* };
|
||||
*
|
||||
* @since 3.5
|
||||
* @cfg {Boolean} [fillEmptyBlocks=true]
|
||||
* @member CKEDITOR.config
|
||||
*/
|
||||
|
||||
/**
|
||||
* This event is fired by the {@link CKEDITOR.htmlDataProcessor} when input HTML
|
||||
* is to be purified by the {@link CKEDITOR.htmlDataProcessor#toHtml} method.
|
||||
*
|
||||
* By adding listeners with different priorities it is possible
|
||||
* to process input HTML on different stages:
|
||||
*
|
||||
* * 1-4: Data is available in the original string format.
|
||||
* * 5: Data is initially filtered with regexp patterns and parsed to
|
||||
* {@link CKEDITOR.htmlParser.fragment} {@link CKEDITOR.htmlParser.element}.
|
||||
* * 5-9: Data is available in the parsed format, but {@link CKEDITOR.htmlDataProcessor#dataFilter}
|
||||
* is not applied yet.
|
||||
* * 6: Data is filtered with the {CKEDITOR.filter content filter}.
|
||||
* * 10: Data is processed with {@link CKEDITOR.htmlDataProcessor#dataFilter}.
|
||||
* * 10-14: Data is available in the parsed format and {@link CKEDITOR.htmlDataProcessor#dataFilter}
|
||||
* has already been applied.
|
||||
* * 15: Data is written back to an HTML string.
|
||||
* * 15-*: Data is available in an HTML string.
|
||||
*
|
||||
* For example to be able to process parsed, but not yet filtered data add listener this way:
|
||||
*
|
||||
* editor.on( 'toHtml', function( evt) {
|
||||
* evt.data.dataValue; // -> CKEDITOR.htmlParser.fragment instance
|
||||
* }, null, null, 7 );
|
||||
*
|
||||
* @since 4.1
|
||||
* @event toHtml
|
||||
* @member CKEDITOR.editor
|
||||
* @param {CKEDITOR.editor} editor This editor instance.
|
||||
* @param data
|
||||
* @param {String/CKEDITOR.htmlParser.fragment/CKEDITOR.htmlParser.element} data.dataValue Input data to be purified.
|
||||
* @param {String} data.context See {@link CKEDITOR.htmlDataProcessor#toHtml} The `context` argument.
|
||||
* @param {Boolean} data.fixForBody See {@link CKEDITOR.htmlDataProcessor#toHtml} The `fixForBody` argument.
|
||||
* @param {Boolean} data.dontFilter See {@link CKEDITOR.htmlDataProcessor#toHtml} The `dontFilter` argument.
|
||||
* @param {Boolean} data.filter See {@link CKEDITOR.htmlDataProcessor#toHtml} The `filter` argument.
|
||||
* @param {Boolean} data.enterMode See {@link CKEDITOR.htmlDataProcessor#toHtml} The `enterMode` argument.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This event is fired when {@link CKEDITOR.htmlDataProcessor} is converting
|
||||
* internal HTML to output data HTML.
|
||||
*
|
||||
* By adding listeners with different priorities it is possible
|
||||
* to process input HTML on different stages:
|
||||
*
|
||||
* * 1-4: Data is available in the original string format.
|
||||
* * 5: Data is initially filtered with regexp patterns and parsed to
|
||||
* {@link CKEDITOR.htmlParser.fragment} {@link CKEDITOR.htmlParser.element}.
|
||||
* * 5-9: Data is available in the parsed format, but {@link CKEDITOR.htmlDataProcessor#htmlFilter}
|
||||
* is not applied yet.
|
||||
* * 10: Data is filtered with {@link CKEDITOR.htmlDataProcessor#htmlFilter}.
|
||||
* * 11: Data is filtered with the {CKEDITOR.filter content filter} (on output the content filter makes
|
||||
* only transformations, without filtering).
|
||||
* * 10-14: Data is available in the parsed format and {@link CKEDITOR.htmlDataProcessor#htmlFilter}
|
||||
* has already been applied.
|
||||
* * 15: Data is written back to an HTML string.
|
||||
* * 15-*: Data is available in an HTML string.
|
||||
*
|
||||
* For example to be able to process parsed and already processed data add listener this way:
|
||||
*
|
||||
* editor.on( 'toDataFormat', function( evt) {
|
||||
* evt.data.dataValue; // -> CKEDITOR.htmlParser.fragment instance
|
||||
* }, null, null, 12 );
|
||||
*
|
||||
* @since 4.1
|
||||
* @event toDataFormat
|
||||
* @member CKEDITOR.editor
|
||||
* @param {CKEDITOR.editor} editor This editor instance.
|
||||
* @param data
|
||||
* @param {String/CKEDITOR.htmlParser.fragment/CKEDITOR.htmlParser.element} data.dataValue Output data to be prepared.
|
||||
* @param {String} data.context See {@link CKEDITOR.htmlDataProcessor#toDataFormat} The `context` argument.
|
||||
* @param {Boolean} data.filter See {@link CKEDITOR.htmlDataProcessor#toDataFormat} The `filter` argument.
|
||||
* @param {Boolean} data.enterMode See {@link CKEDITOR.htmlDataProcessor#toDataFormat} The `enterMode` argument.
|
||||
*/
|
||||
|
||||
@@ -1,207 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides an "event like" system to parse strings of HTML data.
|
||||
*
|
||||
* var parser = new CKEDITOR.htmlParser();
|
||||
* parser.onTagOpen = function( tagName, attributes, selfClosing ) {
|
||||
* alert( tagName );
|
||||
* };
|
||||
* parser.parse( '<p>Some <b>text</b>.</p>' ); // Alerts 'p', 'b'.
|
||||
*
|
||||
* @class
|
||||
* @constructor Creates a htmlParser class instance.
|
||||
*/
|
||||
CKEDITOR.htmlParser = function() {
|
||||
this._ = {
|
||||
htmlPartsRegex: new RegExp( '<(?:(?:\\/([^>]+)>)|(?:!--([\\S|\\s]*?)-->)|(?:([^\\s>]+)\\s*((?:(?:"[^"]*")|(?:\'[^\']*\')|[^"\'>])*)\\/?>))', 'g' )
|
||||
};
|
||||
};
|
||||
|
||||
( function() {
|
||||
var attribsRegex = /([\w\-:.]+)(?:(?:\s*=\s*(?:(?:"([^"]*)")|(?:'([^']*)')|([^\s>]+)))|(?=\s|$))/g,
|
||||
emptyAttribs = { checked: 1, compact: 1, declare: 1, defer: 1, disabled: 1, ismap: 1, multiple: 1, nohref: 1, noresize: 1, noshade: 1, nowrap: 1, readonly: 1, selected: 1 };
|
||||
|
||||
CKEDITOR.htmlParser.prototype = {
|
||||
/**
|
||||
* Function to be fired when a tag opener is found. This function
|
||||
* should be overriden when using this class.
|
||||
*
|
||||
* var parser = new CKEDITOR.htmlParser();
|
||||
* parser.onTagOpen = function( tagName, attributes, selfClosing ) {
|
||||
* alert( tagName ); // e.g. 'b'
|
||||
* } );
|
||||
* parser.parse( '<!-- Example --><b>Hello</b>' );
|
||||
*
|
||||
* @param {String} tagName The tag name. The name is guarantted to be lowercased.
|
||||
* @param {Object} attributes An object containing all tag attributes. Each
|
||||
* property in this object represent and attribute name and its value is the attribute value.
|
||||
* @param {Boolean} selfClosing `true` if the tag closes itself, false if the tag doesn't.
|
||||
*/
|
||||
onTagOpen: function() {},
|
||||
|
||||
/**
|
||||
* Function to be fired when a tag closer is found. This function
|
||||
* should be overriden when using this class.
|
||||
*
|
||||
* var parser = new CKEDITOR.htmlParser();
|
||||
* parser.onTagClose = function( tagName ) {
|
||||
* alert( tagName ); // 'b'
|
||||
* } );
|
||||
* parser.parse( '<!-- Example --><b>Hello</b>' );
|
||||
*
|
||||
* @param {String} tagName The tag name. The name is guarantted to be lowercased.
|
||||
*/
|
||||
onTagClose: function() {},
|
||||
|
||||
/**
|
||||
* Function to be fired when text is found. This function
|
||||
* should be overriden when using this class.
|
||||
*
|
||||
* var parser = new CKEDITOR.htmlParser();
|
||||
* parser.onText = function( text ) {
|
||||
* alert( text ); // 'Hello'
|
||||
* } );
|
||||
* parser.parse( '<!-- Example --><b>Hello</b>' );
|
||||
*
|
||||
* @param {String} text The text found.
|
||||
*/
|
||||
onText: function() {},
|
||||
|
||||
/**
|
||||
* Function to be fired when CDATA section is found. This function
|
||||
* should be overriden when using this class.
|
||||
*
|
||||
* var parser = new CKEDITOR.htmlParser();
|
||||
* parser.onCDATA = function( cdata ) {
|
||||
* alert( cdata ); // 'var hello;'
|
||||
* } );
|
||||
* parser.parse( '<script>var hello;</script>' );
|
||||
*
|
||||
* @param {String} cdata The CDATA been found.
|
||||
*/
|
||||
onCDATA: function() {},
|
||||
|
||||
/**
|
||||
* Function to be fired when a commend is found. This function
|
||||
* should be overriden when using this class.
|
||||
*
|
||||
* var parser = new CKEDITOR.htmlParser();
|
||||
* parser.onComment = function( comment ) {
|
||||
* alert( comment ); // ' Example '
|
||||
* } );
|
||||
* parser.parse( '<!-- Example --><b>Hello</b>' );
|
||||
*
|
||||
* @param {String} comment The comment text.
|
||||
*/
|
||||
onComment: function() {},
|
||||
|
||||
/**
|
||||
* Parses text, looking for HTML tokens, like tag openers or closers,
|
||||
* or comments. This function fires the onTagOpen, onTagClose, onText
|
||||
* and onComment function during its execution.
|
||||
*
|
||||
* var parser = new CKEDITOR.htmlParser();
|
||||
* // The onTagOpen, onTagClose, onText and onComment should be overriden
|
||||
* // at this point.
|
||||
* parser.parse( '<!-- Example --><b>Hello</b>' );
|
||||
*
|
||||
* @param {String} html The HTML to be parsed.
|
||||
*/
|
||||
parse: function( html ) {
|
||||
var parts, tagName,
|
||||
nextIndex = 0,
|
||||
cdata; // The collected data inside a CDATA section.
|
||||
|
||||
while ( ( parts = this._.htmlPartsRegex.exec( html ) ) ) {
|
||||
var tagIndex = parts.index;
|
||||
if ( tagIndex > nextIndex ) {
|
||||
var text = html.substring( nextIndex, tagIndex );
|
||||
|
||||
if ( cdata )
|
||||
cdata.push( text );
|
||||
else
|
||||
this.onText( text );
|
||||
}
|
||||
|
||||
nextIndex = this._.htmlPartsRegex.lastIndex;
|
||||
|
||||
/*
|
||||
"parts" is an array with the following items:
|
||||
0 : The entire match for opening/closing tags and comments.
|
||||
1 : Group filled with the tag name for closing tags.
|
||||
2 : Group filled with the comment text.
|
||||
3 : Group filled with the tag name for opening tags.
|
||||
4 : Group filled with the attributes part of opening tags.
|
||||
*/
|
||||
|
||||
// Closing tag
|
||||
if ( ( tagName = parts[ 1 ] ) ) {
|
||||
tagName = tagName.toLowerCase();
|
||||
|
||||
if ( cdata && CKEDITOR.dtd.$cdata[ tagName ] ) {
|
||||
// Send the CDATA data.
|
||||
this.onCDATA( cdata.join( '' ) );
|
||||
cdata = null;
|
||||
}
|
||||
|
||||
if ( !cdata ) {
|
||||
this.onTagClose( tagName );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// If CDATA is enabled, just save the raw match.
|
||||
if ( cdata ) {
|
||||
cdata.push( parts[ 0 ] );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Opening tag
|
||||
if ( ( tagName = parts[ 3 ] ) ) {
|
||||
tagName = tagName.toLowerCase();
|
||||
|
||||
// There are some tag names that can break things, so let's
|
||||
// simply ignore them when parsing. (#5224)
|
||||
if ( /="/.test( tagName ) )
|
||||
continue;
|
||||
|
||||
var attribs = {},
|
||||
attribMatch,
|
||||
attribsPart = parts[ 4 ],
|
||||
selfClosing = !!( attribsPart && attribsPart.charAt( attribsPart.length - 1 ) == '/' );
|
||||
|
||||
if ( attribsPart ) {
|
||||
while ( ( attribMatch = attribsRegex.exec( attribsPart ) ) ) {
|
||||
var attName = attribMatch[ 1 ].toLowerCase(),
|
||||
attValue = attribMatch[ 2 ] || attribMatch[ 3 ] || attribMatch[ 4 ] || '';
|
||||
|
||||
if ( !attValue && emptyAttribs[ attName ] )
|
||||
attribs[ attName ] = attName;
|
||||
else
|
||||
attribs[ attName ] = CKEDITOR.tools.htmlDecodeAttr( attValue );
|
||||
}
|
||||
}
|
||||
|
||||
this.onTagOpen( tagName, attribs, selfClosing );
|
||||
|
||||
// Open CDATA mode when finding the appropriate tags.
|
||||
if ( !cdata && CKEDITOR.dtd.$cdata[ tagName ] )
|
||||
cdata = [];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Comment
|
||||
if ( ( tagName = parts[ 2 ] ) )
|
||||
this.onComment( tagName );
|
||||
}
|
||||
|
||||
if ( html.length > nextIndex )
|
||||
this.onText( html.substring( nextIndex, html.length ) );
|
||||
}
|
||||
};
|
||||
} )();
|
||||
@@ -1,152 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @class
|
||||
* @todo
|
||||
*/
|
||||
CKEDITOR.htmlParser.basicWriter = CKEDITOR.tools.createClass( {
|
||||
/**
|
||||
* Creates a basicWriter class instance.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
$: function() {
|
||||
this._ = {
|
||||
output: []
|
||||
};
|
||||
},
|
||||
|
||||
proto: {
|
||||
/**
|
||||
* Writes the tag opening part for a opener tag.
|
||||
*
|
||||
* // Writes '<p'.
|
||||
* writer.openTag( 'p', { class : 'MyClass', id : 'MyId' } );
|
||||
*
|
||||
* @param {String} tagName The element name for this tag.
|
||||
* @param {Object} attributes The attributes defined for this tag. The
|
||||
* attributes could be used to inspect the tag.
|
||||
*/
|
||||
openTag: function( tagName, attributes ) {
|
||||
this._.output.push( '<', tagName );
|
||||
},
|
||||
|
||||
/**
|
||||
* Writes the tag closing part for a opener tag.
|
||||
*
|
||||
* // Writes '>'.
|
||||
* writer.openTagClose( 'p', false );
|
||||
*
|
||||
* // Writes ' />'.
|
||||
* writer.openTagClose( 'br', true );
|
||||
*
|
||||
* @param {String} tagName The element name for this tag.
|
||||
* @param {Boolean} isSelfClose Indicates that this is a self-closing tag,
|
||||
* like `<br>` or `<img>`.
|
||||
*/
|
||||
openTagClose: function( tagName, isSelfClose ) {
|
||||
if ( isSelfClose )
|
||||
this._.output.push( ' />' );
|
||||
else
|
||||
this._.output.push( '>' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Writes an attribute. This function should be called after opening the
|
||||
* tag with {@link #openTagClose}.
|
||||
*
|
||||
* // Writes ' class="MyClass"'.
|
||||
* writer.attribute( 'class', 'MyClass' );
|
||||
*
|
||||
* @param {String} attName The attribute name.
|
||||
* @param {String} attValue The attribute value.
|
||||
*/
|
||||
attribute: function( attName, attValue ) {
|
||||
// Browsers don't always escape special character in attribute values. (#4683, #4719).
|
||||
if ( typeof attValue == 'string' )
|
||||
attValue = CKEDITOR.tools.htmlEncodeAttr( attValue );
|
||||
|
||||
this._.output.push( ' ', attName, '="', attValue, '"' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Writes a closer tag.
|
||||
*
|
||||
* // Writes '</p>'.
|
||||
* writer.closeTag( 'p' );
|
||||
*
|
||||
* @param {String} tagName The element name for this tag.
|
||||
*/
|
||||
closeTag: function( tagName ) {
|
||||
this._.output.push( '</', tagName, '>' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Writes text.
|
||||
*
|
||||
* // Writes 'Hello Word'.
|
||||
* writer.text( 'Hello Word' );
|
||||
*
|
||||
* @param {String} text The text value.
|
||||
*/
|
||||
text: function( text ) {
|
||||
this._.output.push( text );
|
||||
},
|
||||
|
||||
/**
|
||||
* Writes a comment.
|
||||
*
|
||||
* // Writes '<!-- My comment -->'.
|
||||
* writer.comment( ' My comment ' );
|
||||
*
|
||||
* @param {String} comment The comment text.
|
||||
*/
|
||||
comment: function( comment ) {
|
||||
this._.output.push( '<!--', comment, '-->' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Writes any kind of data to the ouput.
|
||||
*
|
||||
* writer.write( 'This is an <b>example</b>.' );
|
||||
*
|
||||
* @param {String} data
|
||||
*/
|
||||
write: function( data ) {
|
||||
this._.output.push( data );
|
||||
},
|
||||
|
||||
/**
|
||||
* Empties the current output buffer.
|
||||
*
|
||||
* writer.reset();
|
||||
*/
|
||||
reset: function() {
|
||||
this._.output = [];
|
||||
this._.indent = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Empties the current output buffer.
|
||||
*
|
||||
* var html = writer.getHtml();
|
||||
*
|
||||
* @param {Boolean} reset Indicates that the {@link #reset} method is to
|
||||
* be automatically called after retrieving the HTML.
|
||||
* @returns {String} The HTML written to the writer so far.
|
||||
*/
|
||||
getHtml: function( reset ) {
|
||||
var html = this._.output.join( '' );
|
||||
|
||||
if ( reset )
|
||||
this.reset();
|
||||
|
||||
return html;
|
||||
}
|
||||
}
|
||||
} );
|
||||
@@ -1,48 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
( function() {
|
||||
|
||||
/**
|
||||
* A lightweight representation of HTML CDATA.
|
||||
*
|
||||
* @class
|
||||
* @extends CKEDITOR.htmlParser.node
|
||||
* @constructor Creates a cdata class instance.
|
||||
* @param {String} value The CDATA section value.
|
||||
*/
|
||||
CKEDITOR.htmlParser.cdata = function( value ) {
|
||||
/**
|
||||
* The CDATA value.
|
||||
*
|
||||
* @property {String}
|
||||
*/
|
||||
this.value = value;
|
||||
};
|
||||
|
||||
CKEDITOR.htmlParser.cdata.prototype = CKEDITOR.tools.extend( new CKEDITOR.htmlParser.node(), {
|
||||
/**
|
||||
* CDATA has the same type as {@link CKEDITOR.htmlParser.text} This is
|
||||
* a constant value set to {@link CKEDITOR#NODE_TEXT}.
|
||||
*
|
||||
* @readonly
|
||||
* @property {Number} [=CKEDITOR.NODE_TEXT]
|
||||
*/
|
||||
type: CKEDITOR.NODE_TEXT,
|
||||
|
||||
filter: function() {},
|
||||
|
||||
/**
|
||||
* Writes the CDATA with no special manipulations.
|
||||
*
|
||||
* @param {CKEDITOR.htmlParser.basicWriter} writer The writer to which write the HTML.
|
||||
*/
|
||||
writeHtml: function( writer ) {
|
||||
writer.write( this.value );
|
||||
}
|
||||
} );
|
||||
} )();
|
||||
@@ -1,80 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* A lightweight representation of an HTML comment.
|
||||
*
|
||||
* @class
|
||||
* @extends CKEDITOR.htmlParser.node
|
||||
* @constructor Creates a comment class instance.
|
||||
* @param {String} value The comment text value.
|
||||
*/
|
||||
CKEDITOR.htmlParser.comment = function( value ) {
|
||||
/**
|
||||
* The comment text.
|
||||
*
|
||||
* @property {String}
|
||||
*/
|
||||
this.value = value;
|
||||
|
||||
/** @private */
|
||||
this._ = {
|
||||
isBlockLike: false
|
||||
};
|
||||
};
|
||||
|
||||
CKEDITOR.htmlParser.comment.prototype = CKEDITOR.tools.extend( new CKEDITOR.htmlParser.node(), {
|
||||
/**
|
||||
* The node type. This is a constant value set to {@link CKEDITOR#NODE_COMMENT}.
|
||||
*
|
||||
* @readonly
|
||||
* @property {Number} [=CKEDITOR.NODE_COMMENT]
|
||||
*/
|
||||
type: CKEDITOR.NODE_COMMENT,
|
||||
|
||||
/**
|
||||
* Filter this comment with given filter.
|
||||
*
|
||||
* @since 4.1
|
||||
* @param {CKEDITOR.htmlParser.filter} filter
|
||||
* @returns {Boolean} Method returns `false` when this comment has
|
||||
* been removed or replaced with other node. This is an information for
|
||||
* {@link CKEDITOR.htmlParser.element#filterChildren} that it has
|
||||
* to repeat filter on current position in parent's children array.
|
||||
*/
|
||||
filter: function( filter, context ) {
|
||||
var comment = this.value;
|
||||
|
||||
if ( !( comment = filter.onComment( context, comment, this ) ) ) {
|
||||
this.remove();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( typeof comment != 'string' ) {
|
||||
this.replaceWith( comment );
|
||||
return false;
|
||||
}
|
||||
|
||||
this.value = comment;
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Writes the HTML representation of this comment to a CKEDITOR.htmlWriter.
|
||||
*
|
||||
* @param {CKEDITOR.htmlParser.basicWriter} writer The writer to which write the HTML.
|
||||
* @param {CKEDITOR.htmlParser.filter} [filter] The filter to be applied to this node.
|
||||
* **Note:** it's unsafe to filter offline (not appended) node.
|
||||
*/
|
||||
writeHtml: function( writer, filter ) {
|
||||
if ( filter )
|
||||
this.filter( filter );
|
||||
|
||||
writer.comment( this.value );
|
||||
}
|
||||
} );
|
||||
@@ -1,519 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* A lightweight representation of an HTML element.
|
||||
*
|
||||
* @class
|
||||
* @extends CKEDITOR.htmlParser.node
|
||||
* @constructor Creates an element class instance.
|
||||
* @param {String} name The element name.
|
||||
* @param {Object} attributes And object holding all attributes defined for
|
||||
* this element.
|
||||
*/
|
||||
CKEDITOR.htmlParser.element = function( name, attributes ) {
|
||||
/**
|
||||
* The element name.
|
||||
*
|
||||
* @property {String}
|
||||
*/
|
||||
this.name = name;
|
||||
|
||||
/**
|
||||
* Holds the attributes defined for this element.
|
||||
*
|
||||
* @property {Object}
|
||||
*/
|
||||
this.attributes = attributes || {};
|
||||
|
||||
/**
|
||||
* The nodes that are direct children of this element.
|
||||
*/
|
||||
this.children = [];
|
||||
|
||||
// Reveal the real semantic of our internal custom tag name (#6639),
|
||||
// when resolving whether it's block like.
|
||||
var realName = name || '',
|
||||
prefixed = realName.match( /^cke:(.*)/ );
|
||||
prefixed && ( realName = prefixed[ 1 ] );
|
||||
|
||||
var isBlockLike = !!( CKEDITOR.dtd.$nonBodyContent[ realName ] || CKEDITOR.dtd.$block[ realName ] || CKEDITOR.dtd.$listItem[ realName ] || CKEDITOR.dtd.$tableContent[ realName ] || CKEDITOR.dtd.$nonEditable[ realName ] || realName == 'br' );
|
||||
|
||||
this.isEmpty = !!CKEDITOR.dtd.$empty[ name ];
|
||||
this.isUnknown = !CKEDITOR.dtd[ name ];
|
||||
|
||||
/** @private */
|
||||
this._ = {
|
||||
isBlockLike: isBlockLike,
|
||||
hasInlineStarted: this.isEmpty || !isBlockLike
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Object presentation of CSS style declaration text.
|
||||
*
|
||||
* @class
|
||||
* @constructor Creates a cssStyle class instance.
|
||||
* @param {CKEDITOR.htmlParser.element/String} elementOrStyleText
|
||||
* A html parser element or the inline style text.
|
||||
*/
|
||||
CKEDITOR.htmlParser.cssStyle = function() {
|
||||
var styleText,
|
||||
arg = arguments[ 0 ],
|
||||
rules = {};
|
||||
|
||||
styleText = arg instanceof CKEDITOR.htmlParser.element ? arg.attributes.style : arg;
|
||||
|
||||
// html-encoded quote might be introduced by 'font-family'
|
||||
// from MS-Word which confused the following regexp. e.g.
|
||||
//'font-family: "Lucida, Console"'
|
||||
// TODO reuse CSS methods from tools.
|
||||
( styleText || '' ).replace( /"/g, '"' ).replace( /\s*([^ :;]+)\s*:\s*([^;]+)\s*(?=;|$)/g, function( match, name, value ) {
|
||||
name == 'font-family' && ( value = value.replace( /["']/g, '' ) );
|
||||
rules[ name.toLowerCase() ] = value;
|
||||
} );
|
||||
|
||||
return {
|
||||
|
||||
rules: rules,
|
||||
|
||||
/**
|
||||
* Apply the styles onto the specified element or object.
|
||||
*
|
||||
* @param {CKEDITOR.htmlParser.element/CKEDITOR.dom.element/Object} obj
|
||||
*/
|
||||
populate: function( obj ) {
|
||||
var style = this.toString();
|
||||
if ( style )
|
||||
obj instanceof CKEDITOR.dom.element ? obj.setAttribute( 'style', style ) : obj instanceof CKEDITOR.htmlParser.element ? obj.attributes.style = style : obj.style = style;
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Serialize CSS style declaration to string.
|
||||
*
|
||||
* @returns {String}
|
||||
*/
|
||||
toString: function() {
|
||||
var output = [];
|
||||
for ( var i in rules )
|
||||
rules[ i ] && output.push( i, ':', rules[ i ], ';' );
|
||||
return output.join( '' );
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/** @class CKEDITOR.htmlParser.element */
|
||||
( function() {
|
||||
// Used to sort attribute entries in an array, where the first element of
|
||||
// each object is the attribute name.
|
||||
var sortAttribs = function( a, b ) {
|
||||
a = a[ 0 ];
|
||||
b = b[ 0 ];
|
||||
return a < b ? -1 : a > b ? 1 : 0;
|
||||
},
|
||||
fragProto = CKEDITOR.htmlParser.fragment.prototype;
|
||||
|
||||
CKEDITOR.htmlParser.element.prototype = CKEDITOR.tools.extend( new CKEDITOR.htmlParser.node(), {
|
||||
/**
|
||||
* The node type. This is a constant value set to {@link CKEDITOR#NODE_ELEMENT}.
|
||||
*
|
||||
* @readonly
|
||||
* @property {Number} [=CKEDITOR.NODE_ELEMENT]
|
||||
*/
|
||||
type: CKEDITOR.NODE_ELEMENT,
|
||||
|
||||
/**
|
||||
* Adds a node to the element children list.
|
||||
*
|
||||
* @method
|
||||
* @param {CKEDITOR.htmlParser.node} node The node to be added.
|
||||
* @param {Number} [index] From where the insertion happens.
|
||||
*/
|
||||
add: fragProto.add,
|
||||
|
||||
/**
|
||||
* Clone this element.
|
||||
*
|
||||
* @returns {CKEDITOR.htmlParser.element} The element clone.
|
||||
*/
|
||||
clone: function() {
|
||||
return new CKEDITOR.htmlParser.element( this.name, this.attributes );
|
||||
},
|
||||
|
||||
/**
|
||||
* Filter this element and its children with given filter.
|
||||
*
|
||||
* @since 4.1
|
||||
* @param {CKEDITOR.htmlParser.filter} filter
|
||||
* @returns {Boolean} Method returns `false` when this element has
|
||||
* been removed or replaced with other. This is an information for
|
||||
* {@link #filterChildren} that it has to repeat filter on current
|
||||
* position in parent's children array.
|
||||
*/
|
||||
filter: function( filter, context ) {
|
||||
var element = this,
|
||||
originalName, name;
|
||||
|
||||
context = element.getFilterContext( context );
|
||||
|
||||
// Do not process elements with data-cke-processor attribute set to off.
|
||||
if ( context.off )
|
||||
return true;
|
||||
|
||||
// Filtering if it's the root node.
|
||||
if ( !element.parent )
|
||||
filter.onRoot( context, element );
|
||||
|
||||
while ( true ) {
|
||||
originalName = element.name;
|
||||
|
||||
if ( !( name = filter.onElementName( context, originalName ) ) ) {
|
||||
this.remove();
|
||||
return false;
|
||||
}
|
||||
|
||||
element.name = name;
|
||||
|
||||
if ( !( element = filter.onElement( context, element ) ) ) {
|
||||
this.remove();
|
||||
return false;
|
||||
}
|
||||
|
||||
// New element has been returned - replace current one
|
||||
// and process it (stop processing this and return false, what
|
||||
// means that element has been removed).
|
||||
if ( element !== this ) {
|
||||
this.replaceWith( element );
|
||||
return false;
|
||||
}
|
||||
|
||||
// If name has been changed - continue loop, so in next iteration
|
||||
// filters for new name will be applied to this element.
|
||||
// If name hasn't been changed - stop.
|
||||
if ( element.name == originalName )
|
||||
break;
|
||||
|
||||
// If element has been replaced with something of a
|
||||
// different type, then make the replacement filter itself.
|
||||
if ( element.type != CKEDITOR.NODE_ELEMENT ) {
|
||||
this.replaceWith( element );
|
||||
return false;
|
||||
}
|
||||
|
||||
// This indicate that the element has been dropped by
|
||||
// filter but not the children.
|
||||
if ( !element.name ) {
|
||||
this.replaceWithChildren();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var attributes = element.attributes,
|
||||
a, value, newAttrName;
|
||||
|
||||
for ( a in attributes ) {
|
||||
newAttrName = a;
|
||||
value = attributes[ a ];
|
||||
|
||||
// Loop until name isn't modified.
|
||||
// A little bit senseless, but IE would do that anyway
|
||||
// because it iterates with for-in loop even over properties
|
||||
// created during its run.
|
||||
while ( true ) {
|
||||
if ( !( newAttrName = filter.onAttributeName( context, a ) ) ) {
|
||||
delete attributes[ a ];
|
||||
break;
|
||||
} else if ( newAttrName != a ) {
|
||||
delete attributes[ a ];
|
||||
a = newAttrName;
|
||||
continue;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
if ( newAttrName ) {
|
||||
if ( ( value = filter.onAttribute( context, element, newAttrName, value ) ) === false )
|
||||
delete attributes[ newAttrName ];
|
||||
else
|
||||
attributes[ newAttrName ] = value;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !element.isEmpty )
|
||||
this.filterChildren( filter, false, context );
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Filter this element's children with given filter.
|
||||
*
|
||||
* Element's children may only be filtered once by one
|
||||
* instance of filter.
|
||||
*
|
||||
* @method filterChildren
|
||||
* @param {CKEDITOR.htmlParser.filter} filter
|
||||
*/
|
||||
filterChildren: fragProto.filterChildren,
|
||||
|
||||
/**
|
||||
* Writes the element HTML to a CKEDITOR.htmlWriter.
|
||||
*
|
||||
* @param {CKEDITOR.htmlParser.basicWriter} writer The writer to which write the HTML.
|
||||
* @param {CKEDITOR.htmlParser.filter} [filter] The filter to be applied to this node.
|
||||
* **Note:** it's unsafe to filter offline (not appended) node.
|
||||
*/
|
||||
writeHtml: function( writer, filter ) {
|
||||
if ( filter )
|
||||
this.filter( filter );
|
||||
|
||||
var name = this.name,
|
||||
attribsArray = [],
|
||||
attributes = this.attributes,
|
||||
attrName,
|
||||
attr, i, l;
|
||||
|
||||
// Open element tag.
|
||||
writer.openTag( name, attributes );
|
||||
|
||||
// Copy all attributes to an array.
|
||||
for ( attrName in attributes )
|
||||
attribsArray.push( [ attrName, attributes[ attrName ] ] );
|
||||
|
||||
// Sort the attributes by name.
|
||||
if ( writer.sortAttributes )
|
||||
attribsArray.sort( sortAttribs );
|
||||
|
||||
// Send the attributes.
|
||||
for ( i = 0, l = attribsArray.length; i < l; i++ ) {
|
||||
attr = attribsArray[ i ];
|
||||
writer.attribute( attr[ 0 ], attr[ 1 ] );
|
||||
}
|
||||
|
||||
// Close the tag.
|
||||
writer.openTagClose( name, this.isEmpty );
|
||||
|
||||
this.writeChildrenHtml( writer );
|
||||
|
||||
// Close the element.
|
||||
if ( !this.isEmpty )
|
||||
writer.closeTag( name );
|
||||
},
|
||||
|
||||
/**
|
||||
* Send children of this element to the writer.
|
||||
*
|
||||
* @param {CKEDITOR.htmlParser.basicWriter} writer The writer to which write the HTML.
|
||||
* @param {CKEDITOR.htmlParser.filter} [filter]
|
||||
*/
|
||||
writeChildrenHtml: fragProto.writeChildrenHtml,
|
||||
|
||||
/**
|
||||
* Replace this element with its children.
|
||||
*
|
||||
* @since 4.1
|
||||
*/
|
||||
replaceWithChildren: function() {
|
||||
var children = this.children;
|
||||
|
||||
for ( var i = children.length; i; )
|
||||
children[ --i ].insertAfter( this );
|
||||
|
||||
this.remove();
|
||||
},
|
||||
|
||||
/**
|
||||
* Execute callback on each node (of given type) in this element.
|
||||
*
|
||||
* // Create <p> element with foo<b>bar</b>bom as its content.
|
||||
* var elP = CKEDITOR.htmlParser.fragment.fromHtml( 'foo<b>bar</b>bom', 'p' );
|
||||
* elP.forEach( function( node ) {
|
||||
* console.log( node );
|
||||
* } );
|
||||
* // Will log:
|
||||
* // 1. document fragment,
|
||||
* // 2. <p> element,
|
||||
* // 3. "foo" text node,
|
||||
* // 4. <b> element,
|
||||
* // 5. "bar" text node,
|
||||
* // 6. "bom" text node.
|
||||
*
|
||||
* @since 4.1
|
||||
* @param {Function} callback Function to be executed on every node.
|
||||
* **Since 4.3** if `callback` returned `false` descendants of current node will be ignored.
|
||||
* @param {CKEDITOR.htmlParser.node} callback.node Node passed as argument.
|
||||
* @param {Number} [type] If specified `callback` will be executed only on nodes of this type.
|
||||
* @param {Boolean} [skipRoot] Don't execute `callback` on this element.
|
||||
*/
|
||||
forEach: fragProto.forEach,
|
||||
|
||||
/**
|
||||
* Gets this element's first child. If `condition` is given returns
|
||||
* first child which satisfies that condition.
|
||||
*
|
||||
* @since 4.3
|
||||
* @param {String/Object/Function} condition Name of a child, hash of names or validator function.
|
||||
* @returns {CKEDITOR.htmlParser.node}
|
||||
*/
|
||||
getFirst: function( condition ) {
|
||||
if ( !condition )
|
||||
return this.children.length ? this.children[ 0 ] : null;
|
||||
|
||||
if ( typeof condition != 'function' )
|
||||
condition = nameCondition( condition );
|
||||
|
||||
for ( var i = 0, l = this.children.length; i < l; ++i ) {
|
||||
if ( condition( this.children[ i ] ) )
|
||||
return this.children[ i ];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets this element's inner HTML.
|
||||
*
|
||||
* @since 4.3
|
||||
* @returns {String}
|
||||
*/
|
||||
getHtml: function() {
|
||||
var writer = new CKEDITOR.htmlParser.basicWriter();
|
||||
this.writeChildrenHtml( writer );
|
||||
return writer.getHtml();
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets this element's inner HTML.
|
||||
*
|
||||
* @since 4.3
|
||||
* @param {String} html
|
||||
*/
|
||||
setHtml: function( html ) {
|
||||
var children = this.children = CKEDITOR.htmlParser.fragment.fromHtml( html ).children;
|
||||
|
||||
for ( var i = 0, l = children.length; i < l; ++i )
|
||||
children[ i ].parent = this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets this element's outer HTML.
|
||||
*
|
||||
* @since 4.3
|
||||
* @returns {String}
|
||||
*/
|
||||
getOuterHtml: function() {
|
||||
var writer = new CKEDITOR.htmlParser.basicWriter();
|
||||
this.writeHtml( writer );
|
||||
return writer.getHtml();
|
||||
},
|
||||
|
||||
/**
|
||||
* Splits this element at given index.
|
||||
*
|
||||
* @since 4.3
|
||||
* @param {Number} index Index at which element will be split – `0` means beginning,
|
||||
* `1` after first child node, etc.
|
||||
* @returns {CKEDITOR.htmlParser.element} New element, following this one.
|
||||
*/
|
||||
split: function( index ) {
|
||||
var cloneChildren = this.children.splice( index, this.children.length - index ),
|
||||
clone = this.clone();
|
||||
|
||||
for ( var i = 0; i < cloneChildren.length; ++i )
|
||||
cloneChildren[ i ].parent = clone;
|
||||
|
||||
clone.children = cloneChildren;
|
||||
|
||||
if ( cloneChildren[ 0 ] )
|
||||
cloneChildren[ 0 ].previous = null;
|
||||
|
||||
if ( index > 0 )
|
||||
this.children[ index - 1 ].next = null;
|
||||
|
||||
this.parent.add( clone, this.getIndex() + 1 );
|
||||
|
||||
return clone;
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes class name from classes list.
|
||||
*
|
||||
* @since 4.3
|
||||
* @param {String} className The class name to be removed.
|
||||
*/
|
||||
removeClass: function( className ) {
|
||||
var classes = this.attributes[ 'class' ],
|
||||
index;
|
||||
|
||||
if ( !classes )
|
||||
return;
|
||||
|
||||
// We can safely assume that className won't break regexp.
|
||||
// http://stackoverflow.com/questions/448981/what-characters-are-valid-in-css-class-names
|
||||
classes = CKEDITOR.tools.trim( classes.replace( new RegExp( '(?:\\s+|^)' + className + '(?:\\s+|$)' ), ' ' ) );
|
||||
|
||||
if ( classes )
|
||||
this.attributes[ 'class' ] = classes;
|
||||
else
|
||||
delete this.attributes[ 'class' ];
|
||||
},
|
||||
|
||||
/**
|
||||
* Checkes whether this element has a class name.
|
||||
*
|
||||
* @since 4.3
|
||||
* @param {String} className The class name to be checked.
|
||||
* @returns {Boolean} Whether this element has a `className`.
|
||||
*/
|
||||
hasClass: function( className ) {
|
||||
var classes = this.attributes[ 'class' ];
|
||||
|
||||
if ( !classes )
|
||||
return false;
|
||||
|
||||
return ( new RegExp( '(?:^|\\s)' + className + '(?=\\s|$)' ) ).test( classes );
|
||||
},
|
||||
|
||||
getFilterContext: function( ctx ) {
|
||||
var changes = [];
|
||||
|
||||
if ( !ctx ) {
|
||||
ctx = {
|
||||
off: false,
|
||||
nonEditable: false,
|
||||
nestedEditable: false
|
||||
};
|
||||
}
|
||||
|
||||
if ( !ctx.off && this.attributes[ 'data-cke-processor' ] == 'off' )
|
||||
changes.push( 'off', true );
|
||||
|
||||
if ( !ctx.nonEditable && this.attributes.contenteditable == 'false' )
|
||||
changes.push( 'nonEditable', true );
|
||||
// A context to be given nestedEditable must be nonEditable first (by inheritance) (#11372).
|
||||
// Never set "nestedEditable" context for a body. If body is processed then it indicates
|
||||
// a fullPage editor and there's no slightest change of nesting such editable (#11504).
|
||||
else if ( this.name != 'body' && !ctx.nestedEditable && this.attributes.contenteditable == 'true' )
|
||||
changes.push( 'nestedEditable', true );
|
||||
|
||||
if ( changes.length ) {
|
||||
ctx = CKEDITOR.tools.copy( ctx );
|
||||
for ( var i = 0; i < changes.length; i += 2 )
|
||||
ctx[ changes[ i ] ] = changes[ i + 1 ];
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
}, true );
|
||||
|
||||
function nameCondition( condition ) {
|
||||
return function( el ) {
|
||||
return el.type == CKEDITOR.NODE_ELEMENT &&
|
||||
( typeof condition == 'string' ? el.name == condition : el.name in condition );
|
||||
};
|
||||
}
|
||||
} )();
|
||||
@@ -1,407 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
( function() {
|
||||
/**
|
||||
* Filter is a configurable tool for transforming and filtering {@link CKEDITOR.htmlParser.node nodes}.
|
||||
* It is mainly used during data processing phase which is done not on real DOM nodes,
|
||||
* but on their simplified form represented by {@link CKEDITOR.htmlParser.node} class and its subclasses.
|
||||
*
|
||||
* var filter = new CKEDITOR.htmlParser.filter( {
|
||||
* text: function( value ) {
|
||||
* return '@' + value + '@';
|
||||
* },
|
||||
* elements: {
|
||||
* p: function( element ) {
|
||||
* element.attributes.foo = '1';
|
||||
* }
|
||||
* }
|
||||
* } );
|
||||
*
|
||||
* var fragment = CKEDITOR.htmlParser.fragment.fromHtml( '<p>Foo<b>bar!</b></p>' ),
|
||||
* writer = new CKEDITOR.htmlParser.basicWriter();
|
||||
* filter.applyTo( fragment );
|
||||
* fragment.writeHtml( writer );
|
||||
* writer.getHtml(); // '<p foo="1">@Foo@<b>@bar!@</b></p>'
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
CKEDITOR.htmlParser.filter = CKEDITOR.tools.createClass( {
|
||||
/**
|
||||
* @constructor Creates a filter class instance.
|
||||
* @param {CKEDITOR.htmlParser.filterRulesDefinition} [rules]
|
||||
*/
|
||||
$: function( rules ) {
|
||||
/**
|
||||
* ID of filter instance, which is used to mark elements
|
||||
* to which this filter has been already applied.
|
||||
*
|
||||
* @property {Number} id
|
||||
* @readonly
|
||||
*/
|
||||
this.id = CKEDITOR.tools.getNextNumber();
|
||||
|
||||
/**
|
||||
* Rules for element names.
|
||||
*
|
||||
* @property {CKEDITOR.htmlParser.filterRulesGroup}
|
||||
* @readonly
|
||||
*/
|
||||
this.elementNameRules = new filterRulesGroup();
|
||||
|
||||
/**
|
||||
* Rules for attribute names.
|
||||
*
|
||||
* @property {CKEDITOR.htmlParser.filterRulesGroup}
|
||||
* @readonly
|
||||
*/
|
||||
this.attributeNameRules = new filterRulesGroup();
|
||||
|
||||
/**
|
||||
* Hash of elementName => {@link CKEDITOR.htmlParser.filterRulesGroup rules for elements}.
|
||||
*
|
||||
* @readonly
|
||||
*/
|
||||
this.elementsRules = {};
|
||||
|
||||
/**
|
||||
* Hash of attributeName => {@link CKEDITOR.htmlParser.filterRulesGroup rules for attributes}.
|
||||
*
|
||||
* @readonly
|
||||
*/
|
||||
this.attributesRules = {};
|
||||
|
||||
/**
|
||||
* Rules for text nodes.
|
||||
*
|
||||
* @property {CKEDITOR.htmlParser.filterRulesGroup}
|
||||
* @readonly
|
||||
*/
|
||||
this.textRules = new filterRulesGroup();
|
||||
|
||||
/**
|
||||
* Rules for comment nodes.
|
||||
*
|
||||
* @property {CKEDITOR.htmlParser.filterRulesGroup}
|
||||
* @readonly
|
||||
*/
|
||||
this.commentRules = new filterRulesGroup();
|
||||
|
||||
/**
|
||||
* Rules for a root node.
|
||||
*
|
||||
* @property {CKEDITOR.htmlParser.filterRulesGroup}
|
||||
* @readonly
|
||||
*/
|
||||
this.rootRules = new filterRulesGroup();
|
||||
|
||||
if ( rules )
|
||||
this.addRules( rules, 10 );
|
||||
},
|
||||
|
||||
proto: {
|
||||
/**
|
||||
* Add rules to this filter.
|
||||
*
|
||||
* @param {CKEDITOR.htmlParser.filterRulesDefinition} rules Object containing filter rules.
|
||||
* @param {Object/Number} [options] Object containing rules' options or a priority
|
||||
* (for a backward compatibility with CKEditor versions up to 4.2.x).
|
||||
* @param {Number} [options.priority=10] The priority of a rule.
|
||||
* @param {Boolean} [options.applyToAll=false] Whether to apply rule to non-editable
|
||||
* elements and their descendants too.
|
||||
*/
|
||||
addRules: function( rules, options ) {
|
||||
var priority;
|
||||
|
||||
// Backward compatibility.
|
||||
if ( typeof options == 'number' )
|
||||
priority = options;
|
||||
// New version - try reading from options.
|
||||
else if ( options && ( 'priority' in options ) )
|
||||
priority = options.priority;
|
||||
|
||||
// Defaults.
|
||||
if ( typeof priority != 'number' )
|
||||
priority = 10;
|
||||
if ( typeof options != 'object' )
|
||||
options = {};
|
||||
|
||||
// Add the elementNames.
|
||||
if ( rules.elementNames )
|
||||
this.elementNameRules.addMany( rules.elementNames, priority, options );
|
||||
|
||||
// Add the attributeNames.
|
||||
if ( rules.attributeNames )
|
||||
this.attributeNameRules.addMany( rules.attributeNames, priority, options );
|
||||
|
||||
// Add the elements.
|
||||
if ( rules.elements )
|
||||
addNamedRules( this.elementsRules, rules.elements, priority, options );
|
||||
|
||||
// Add the attributes.
|
||||
if ( rules.attributes )
|
||||
addNamedRules( this.attributesRules, rules.attributes, priority, options );
|
||||
|
||||
// Add the text.
|
||||
if ( rules.text )
|
||||
this.textRules.add( rules.text, priority, options );
|
||||
|
||||
// Add the comment.
|
||||
if ( rules.comment )
|
||||
this.commentRules.add( rules.comment, priority, options );
|
||||
|
||||
// Add root node rules.
|
||||
if ( rules.root )
|
||||
this.rootRules.add( rules.root, priority, options );
|
||||
},
|
||||
|
||||
/**
|
||||
* Apply this filter to given node.
|
||||
*
|
||||
* @param {CKEDITOR.htmlParser.node} node The node to be filtered.
|
||||
*/
|
||||
applyTo: function( node ) {
|
||||
node.filter( this );
|
||||
},
|
||||
|
||||
onElementName: function( context, name ) {
|
||||
return this.elementNameRules.execOnName( context, name );
|
||||
},
|
||||
|
||||
onAttributeName: function( context, name ) {
|
||||
return this.attributeNameRules.execOnName( context, name );
|
||||
},
|
||||
|
||||
onText: function( context, text ) {
|
||||
return this.textRules.exec( context, text );
|
||||
},
|
||||
|
||||
onComment: function( context, commentText, comment ) {
|
||||
return this.commentRules.exec( context, commentText, comment );
|
||||
},
|
||||
|
||||
onRoot: function( context, element ) {
|
||||
return this.rootRules.exec( context, element );
|
||||
},
|
||||
|
||||
onElement: function( context, element ) {
|
||||
// We must apply filters set to the specific element name as
|
||||
// well as those set to the generic ^/$ name. So, add both to an
|
||||
// array and process them in a small loop.
|
||||
var rulesGroups = [ this.elementsRules[ '^' ], this.elementsRules[ element.name ], this.elementsRules.$ ],
|
||||
rulesGroup, ret;
|
||||
|
||||
for ( var i = 0; i < 3; i++ ) {
|
||||
rulesGroup = rulesGroups[ i ];
|
||||
if ( rulesGroup ) {
|
||||
ret = rulesGroup.exec( context, element, this );
|
||||
|
||||
if ( ret === false )
|
||||
return null;
|
||||
|
||||
if ( ret && ret != element )
|
||||
return this.onNode( context, ret );
|
||||
|
||||
// The non-root element has been dismissed by one of the filters.
|
||||
if ( element.parent && !element.name )
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return element;
|
||||
},
|
||||
|
||||
onNode: function( context, node ) {
|
||||
var type = node.type;
|
||||
|
||||
return type == CKEDITOR.NODE_ELEMENT ? this.onElement( context, node ) :
|
||||
type == CKEDITOR.NODE_TEXT ? new CKEDITOR.htmlParser.text( this.onText( context, node.value ) ) :
|
||||
type == CKEDITOR.NODE_COMMENT ? new CKEDITOR.htmlParser.comment( this.onComment( context, node.value ) ) : null;
|
||||
},
|
||||
|
||||
onAttribute: function( context, element, name, value ) {
|
||||
var rulesGroup = this.attributesRules[ name ];
|
||||
|
||||
if ( rulesGroup )
|
||||
return rulesGroup.exec( context, value, element, this );
|
||||
return value;
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
/**
|
||||
* Class grouping filter rules for one subject (like element or attribute names).
|
||||
*
|
||||
* @class CKEDITOR.htmlParser.filterRulesGroup
|
||||
*/
|
||||
function filterRulesGroup() {
|
||||
/**
|
||||
* Array of objects containing rule, priority and options.
|
||||
*
|
||||
* @property {Object[]}
|
||||
* @readonly
|
||||
*/
|
||||
this.rules = [];
|
||||
}
|
||||
|
||||
CKEDITOR.htmlParser.filterRulesGroup = filterRulesGroup;
|
||||
|
||||
filterRulesGroup.prototype = {
|
||||
/**
|
||||
* Adds specified rule to this group.
|
||||
*
|
||||
* @param {Function/Array} rule Function for function based rule or [ pattern, replacement ] array for
|
||||
* rule applicable to names.
|
||||
* @param {Number} priority
|
||||
* @param options
|
||||
*/
|
||||
add: function( rule, priority, options ) {
|
||||
this.rules.splice( this.findIndex( priority ), 0, {
|
||||
value: rule,
|
||||
priority: priority,
|
||||
options: options
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds specified rules to this group.
|
||||
*
|
||||
* @param {Array} rules Array of rules - see {@link #add}.
|
||||
* @param {Number} priority
|
||||
* @param options
|
||||
*/
|
||||
addMany: function( rules, priority, options ) {
|
||||
var args = [ this.findIndex( priority ), 0 ];
|
||||
|
||||
for ( var i = 0, len = rules.length; i < len; i++ ) {
|
||||
args.push( {
|
||||
value: rules[ i ],
|
||||
priority: priority,
|
||||
options: options
|
||||
} );
|
||||
}
|
||||
|
||||
this.rules.splice.apply( this.rules, args );
|
||||
},
|
||||
|
||||
/**
|
||||
* Finds an index at which rule with given priority should be inserted.
|
||||
*
|
||||
* @param {Number} priority
|
||||
* @returns {Number} Index.
|
||||
*/
|
||||
findIndex: function( priority ) {
|
||||
var rules = this.rules,
|
||||
len = rules.length,
|
||||
i = len - 1;
|
||||
|
||||
// Search from the end, because usually rules will be added with default priority, so
|
||||
// we will be able to stop loop quickly.
|
||||
while ( i >= 0 && priority < rules[ i ].priority )
|
||||
i--;
|
||||
|
||||
return i + 1;
|
||||
},
|
||||
|
||||
/**
|
||||
* Executes this rules group on given value. Applicable only if function based rules were added.
|
||||
*
|
||||
* All arguments passed to this function will be forwarded to rules' functions.
|
||||
*
|
||||
* @param {CKEDITOR.htmlParser.node/CKEDITOR.htmlParser.fragment/String} currentValue The value to be filtered.
|
||||
* @returns {CKEDITOR.htmlParser.node/CKEDITOR.htmlParser.fragment/String} Filtered value.
|
||||
*/
|
||||
exec: function( context, currentValue ) {
|
||||
var isNode = currentValue instanceof CKEDITOR.htmlParser.node || currentValue instanceof CKEDITOR.htmlParser.fragment,
|
||||
// Splice '1' to remove context, which we don't want to pass to filter rules.
|
||||
args = Array.prototype.slice.call( arguments, 1 ),
|
||||
rules = this.rules,
|
||||
len = rules.length,
|
||||
orgType, orgName, ret, i, rule;
|
||||
|
||||
for ( i = 0; i < len; i++ ) {
|
||||
// Backup the node info before filtering.
|
||||
if ( isNode ) {
|
||||
orgType = currentValue.type;
|
||||
orgName = currentValue.name;
|
||||
}
|
||||
|
||||
rule = rules[ i ];
|
||||
if ( isRuleApplicable( context, rule ) ) {
|
||||
ret = rule.value.apply( null, args );
|
||||
|
||||
if ( ret === false )
|
||||
return ret;
|
||||
|
||||
// We're filtering node (element/fragment).
|
||||
// No further filtering if it's not anymore fitable for the subsequent filters.
|
||||
if ( isNode && ret && ( ret.name != orgName || ret.type != orgType ) )
|
||||
return ret;
|
||||
|
||||
// Update currentValue and corresponding argument in args array.
|
||||
// Updated values will be used in next for-loop step.
|
||||
if ( ret != undefined )
|
||||
args[ 0 ] = currentValue = ret;
|
||||
|
||||
// ret == undefined will continue loop as nothing has happened.
|
||||
}
|
||||
}
|
||||
|
||||
return currentValue;
|
||||
},
|
||||
|
||||
/**
|
||||
* Executes this rules group on name. Applicable only if filter rules for names were added.
|
||||
*
|
||||
* @param {String} currentName The name to be filtered.
|
||||
* @returns {String} Filtered name.
|
||||
*/
|
||||
execOnName: function( context, currentName ) {
|
||||
var i = 0,
|
||||
rules = this.rules,
|
||||
len = rules.length,
|
||||
rule;
|
||||
|
||||
for ( ; currentName && i < len; i++ ) {
|
||||
rule = rules[ i ];
|
||||
if ( isRuleApplicable( context, rule ) )
|
||||
currentName = currentName.replace( rule.value[ 0 ], rule.value[ 1 ] );
|
||||
}
|
||||
|
||||
return currentName;
|
||||
}
|
||||
};
|
||||
|
||||
function addNamedRules( rulesGroups, newRules, priority, options ) {
|
||||
var ruleName, rulesGroup;
|
||||
|
||||
for ( ruleName in newRules ) {
|
||||
rulesGroup = rulesGroups[ ruleName ];
|
||||
|
||||
if ( !rulesGroup )
|
||||
rulesGroup = rulesGroups[ ruleName ] = new filterRulesGroup();
|
||||
|
||||
rulesGroup.add( newRules[ ruleName ], priority, options );
|
||||
}
|
||||
}
|
||||
|
||||
function isRuleApplicable( context, rule ) {
|
||||
if ( context.nonEditable && !rule.options.applyToAll )
|
||||
return false;
|
||||
|
||||
if ( context.nestedEditable && rule.options.excludeNestedEditable )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} )();
|
||||
|
||||
/**
|
||||
* @class CKEDITOR.htmlParser.filterRulesDefinition
|
||||
* @abstract
|
||||
*/
|
||||
@@ -1,631 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* A lightweight representation of an HTML DOM structure.
|
||||
*
|
||||
* @class
|
||||
* @constructor Creates a fragment class instance.
|
||||
*/
|
||||
CKEDITOR.htmlParser.fragment = function() {
|
||||
/**
|
||||
* The nodes contained in the root of this fragment.
|
||||
*
|
||||
* var fragment = CKEDITOR.htmlParser.fragment.fromHtml( '<b>Sample</b> Text' );
|
||||
* alert( fragment.children.length ); // 2
|
||||
*/
|
||||
this.children = [];
|
||||
|
||||
/**
|
||||
* Get the fragment parent. Should always be null.
|
||||
*
|
||||
* @property {Object} [=null]
|
||||
*/
|
||||
this.parent = null;
|
||||
|
||||
/** @private */
|
||||
this._ = {
|
||||
isBlockLike: true,
|
||||
hasInlineStarted: false
|
||||
};
|
||||
};
|
||||
|
||||
( function() {
|
||||
// Block-level elements whose internal structure should be respected during
|
||||
// parser fixing.
|
||||
var nonBreakingBlocks = CKEDITOR.tools.extend( { table: 1, ul: 1, ol: 1, dl: 1 }, CKEDITOR.dtd.table, CKEDITOR.dtd.ul, CKEDITOR.dtd.ol, CKEDITOR.dtd.dl );
|
||||
|
||||
var listBlocks = { ol: 1, ul: 1 };
|
||||
|
||||
// Dtd of the fragment element, basically it accept anything except for intermediate structure, e.g. orphan <li>.
|
||||
var rootDtd = CKEDITOR.tools.extend( {}, { html: 1 }, CKEDITOR.dtd.html, CKEDITOR.dtd.body, CKEDITOR.dtd.head, { style: 1, script: 1 } );
|
||||
|
||||
function isRemoveEmpty( node ) {
|
||||
// Keep marked element event if it is empty.
|
||||
if ( node.attributes[ 'data-cke-survive' ] )
|
||||
return false;
|
||||
|
||||
// Empty link is to be removed when empty but not anchor. (#7894)
|
||||
return node.name == 'a' && node.attributes.href || CKEDITOR.dtd.$removeEmpty[ node.name ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link CKEDITOR.htmlParser.fragment} from an HTML string.
|
||||
*
|
||||
* var fragment = CKEDITOR.htmlParser.fragment.fromHtml( '<b>Sample</b> Text' );
|
||||
* alert( fragment.children[ 0 ].name ); // 'b'
|
||||
* alert( fragment.children[ 1 ].value ); // ' Text'
|
||||
*
|
||||
* @static
|
||||
* @param {String} fragmentHtml The HTML to be parsed, filling the fragment.
|
||||
* @param {CKEDITOR.htmlParser.element/String} [parent] Optional contextual
|
||||
* element which makes the content been parsed as the content of this element and fix
|
||||
* to match it.
|
||||
* If not provided, then {@link CKEDITOR.htmlParser.fragment} will be used
|
||||
* as the parent and it will be returned.
|
||||
* @param {String/Boolean} [fixingBlock] When `parent` is a block limit element,
|
||||
* and the param is a string value other than `false`, it is to
|
||||
* avoid having block-less content as the direct children of parent by wrapping
|
||||
* the content with a block element of the specified tag, e.g.
|
||||
* when `fixingBlock` specified as `p`, the content `<body><i>foo</i></body>`
|
||||
* will be fixed into `<body><p><i>foo</i></p></body>`.
|
||||
* @returns {CKEDITOR.htmlParser.fragment/CKEDITOR.htmlParser.element} The created fragment or passed `parent`.
|
||||
*/
|
||||
CKEDITOR.htmlParser.fragment.fromHtml = function( fragmentHtml, parent, fixingBlock ) {
|
||||
var parser = new CKEDITOR.htmlParser();
|
||||
|
||||
var root = parent instanceof CKEDITOR.htmlParser.element ? parent : typeof parent == 'string' ? new CKEDITOR.htmlParser.element( parent ) : new CKEDITOR.htmlParser.fragment();
|
||||
|
||||
var pendingInline = [],
|
||||
pendingBRs = [],
|
||||
currentNode = root,
|
||||
// Indicate we're inside a <textarea> element, spaces should be touched differently.
|
||||
inTextarea = root.name == 'textarea',
|
||||
// Indicate we're inside a <pre> element, spaces should be touched differently.
|
||||
inPre = root.name == 'pre';
|
||||
|
||||
function checkPending( newTagName ) {
|
||||
var pendingBRsSent;
|
||||
|
||||
if ( pendingInline.length > 0 ) {
|
||||
for ( var i = 0; i < pendingInline.length; i++ ) {
|
||||
var pendingElement = pendingInline[ i ],
|
||||
pendingName = pendingElement.name,
|
||||
pendingDtd = CKEDITOR.dtd[ pendingName ],
|
||||
currentDtd = currentNode.name && CKEDITOR.dtd[ currentNode.name ];
|
||||
|
||||
if ( ( !currentDtd || currentDtd[ pendingName ] ) && ( !newTagName || !pendingDtd || pendingDtd[ newTagName ] || !CKEDITOR.dtd[ newTagName ] ) ) {
|
||||
if ( !pendingBRsSent ) {
|
||||
sendPendingBRs();
|
||||
pendingBRsSent = 1;
|
||||
}
|
||||
|
||||
// Get a clone for the pending element.
|
||||
pendingElement = pendingElement.clone();
|
||||
|
||||
// Add it to the current node and make it the current,
|
||||
// so the new element will be added inside of it.
|
||||
pendingElement.parent = currentNode;
|
||||
currentNode = pendingElement;
|
||||
|
||||
// Remove the pending element (back the index by one
|
||||
// to properly process the next entry).
|
||||
pendingInline.splice( i, 1 );
|
||||
i--;
|
||||
} else {
|
||||
// Some element of the same type cannot be nested, flat them,
|
||||
// e.g. <a href="#">foo<a href="#">bar</a></a>. (#7894)
|
||||
if ( pendingName == currentNode.name )
|
||||
addElement( currentNode, currentNode.parent, 1 ), i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function sendPendingBRs() {
|
||||
while ( pendingBRs.length )
|
||||
addElement( pendingBRs.shift(), currentNode );
|
||||
}
|
||||
|
||||
// Rtrim empty spaces on block end boundary. (#3585)
|
||||
function removeTailWhitespace( element ) {
|
||||
if ( element._.isBlockLike && element.name != 'pre' && element.name != 'textarea' ) {
|
||||
|
||||
var length = element.children.length,
|
||||
lastChild = element.children[ length - 1 ],
|
||||
text;
|
||||
if ( lastChild && lastChild.type == CKEDITOR.NODE_TEXT ) {
|
||||
if ( !( text = CKEDITOR.tools.rtrim( lastChild.value ) ) )
|
||||
element.children.length = length - 1;
|
||||
else
|
||||
lastChild.value = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Beside of simply append specified element to target, this function also takes
|
||||
// care of other dirty lifts like forcing block in body, trimming spaces at
|
||||
// the block boundaries etc.
|
||||
//
|
||||
// @param {Element} element The element to be added as the last child of {@link target}.
|
||||
// @param {Element} target The parent element to relieve the new node.
|
||||
// @param {Boolean} [moveCurrent=false] Don't change the "currentNode" global unless
|
||||
// there's a return point node specified on the element, otherwise move current onto {@link target} node.
|
||||
//
|
||||
function addElement( element, target, moveCurrent ) {
|
||||
target = target || currentNode || root;
|
||||
|
||||
// Current element might be mangled by fix body below,
|
||||
// save it for restore later.
|
||||
var savedCurrent = currentNode;
|
||||
|
||||
// Ignore any element that has already been added.
|
||||
if ( element.previous === undefined ) {
|
||||
if ( checkAutoParagraphing( target, element ) ) {
|
||||
// Create a <p> in the fragment.
|
||||
currentNode = target;
|
||||
parser.onTagOpen( fixingBlock, {} );
|
||||
|
||||
// The new target now is the <p>.
|
||||
element.returnPoint = target = currentNode;
|
||||
}
|
||||
|
||||
removeTailWhitespace( element );
|
||||
|
||||
// Avoid adding empty inline.
|
||||
if ( !( isRemoveEmpty( element ) && !element.children.length ) )
|
||||
target.add( element );
|
||||
|
||||
if ( element.name == 'pre' )
|
||||
inPre = false;
|
||||
|
||||
if ( element.name == 'textarea' )
|
||||
inTextarea = false;
|
||||
}
|
||||
|
||||
if ( element.returnPoint ) {
|
||||
currentNode = element.returnPoint;
|
||||
delete element.returnPoint;
|
||||
} else
|
||||
currentNode = moveCurrent ? target : savedCurrent;
|
||||
}
|
||||
|
||||
// Auto paragraphing should happen when inline content enters the root element.
|
||||
function checkAutoParagraphing( parent, node ) {
|
||||
|
||||
// Check for parent that can contain block.
|
||||
if ( ( parent == root || parent.name == 'body' ) && fixingBlock &&
|
||||
( !parent.name || CKEDITOR.dtd[ parent.name ][ fixingBlock ] ) )
|
||||
{
|
||||
var name, realName;
|
||||
if ( node.attributes && ( realName = node.attributes[ 'data-cke-real-element-type' ] ) )
|
||||
name = realName;
|
||||
else
|
||||
name = node.name;
|
||||
|
||||
// Text node, inline elements are subjected, except for <script>/<style>.
|
||||
return name && name in CKEDITOR.dtd.$inline &&
|
||||
!( name in CKEDITOR.dtd.head ) &&
|
||||
!node.isOrphan ||
|
||||
node.type == CKEDITOR.NODE_TEXT;
|
||||
}
|
||||
}
|
||||
|
||||
// Judge whether two element tag names are likely the siblings from the same
|
||||
// structural element.
|
||||
function possiblySibling( tag1, tag2 ) {
|
||||
|
||||
if ( tag1 in CKEDITOR.dtd.$listItem || tag1 in CKEDITOR.dtd.$tableContent )
|
||||
return tag1 == tag2 || tag1 == 'dt' && tag2 == 'dd' || tag1 == 'dd' && tag2 == 'dt';
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
parser.onTagOpen = function( tagName, attributes, selfClosing, optionalClose ) {
|
||||
var element = new CKEDITOR.htmlParser.element( tagName, attributes );
|
||||
|
||||
// "isEmpty" will be always "false" for unknown elements, so we
|
||||
// must force it if the parser has identified it as a selfClosing tag.
|
||||
if ( element.isUnknown && selfClosing )
|
||||
element.isEmpty = true;
|
||||
|
||||
// Check for optional closed elements, including browser quirks and manually opened blocks.
|
||||
element.isOptionalClose = optionalClose;
|
||||
|
||||
// This is a tag to be removed if empty, so do not add it immediately.
|
||||
if ( isRemoveEmpty( element ) ) {
|
||||
pendingInline.push( element );
|
||||
return;
|
||||
} else if ( tagName == 'pre' )
|
||||
inPre = true;
|
||||
else if ( tagName == 'br' && inPre ) {
|
||||
currentNode.add( new CKEDITOR.htmlParser.text( '\n' ) );
|
||||
return;
|
||||
} else if ( tagName == 'textarea' )
|
||||
inTextarea = true;
|
||||
|
||||
if ( tagName == 'br' ) {
|
||||
pendingBRs.push( element );
|
||||
return;
|
||||
}
|
||||
|
||||
while ( 1 ) {
|
||||
var currentName = currentNode.name;
|
||||
|
||||
var currentDtd = currentName ? ( CKEDITOR.dtd[ currentName ] || ( currentNode._.isBlockLike ? CKEDITOR.dtd.div : CKEDITOR.dtd.span ) ) : rootDtd;
|
||||
|
||||
// If the element cannot be child of the current element.
|
||||
if ( !element.isUnknown && !currentNode.isUnknown && !currentDtd[ tagName ] ) {
|
||||
// Current node doesn't have a close tag, time for a close
|
||||
// as this element isn't fit in. (#7497)
|
||||
if ( currentNode.isOptionalClose )
|
||||
parser.onTagClose( currentName );
|
||||
// Fixing malformed nested lists by moving it into a previous list item. (#3828)
|
||||
else if ( tagName in listBlocks && currentName in listBlocks ) {
|
||||
var children = currentNode.children,
|
||||
lastChild = children[ children.length - 1 ];
|
||||
|
||||
// Establish the list item if it's not existed.
|
||||
if ( !( lastChild && lastChild.name == 'li' ) )
|
||||
addElement( ( lastChild = new CKEDITOR.htmlParser.element( 'li' ) ), currentNode );
|
||||
|
||||
!element.returnPoint && ( element.returnPoint = currentNode );
|
||||
currentNode = lastChild;
|
||||
}
|
||||
// Establish new list root for orphan list items, but NOT to create
|
||||
// new list for the following ones, fix them instead. (#6975)
|
||||
// <dl><dt>foo<dd>bar</dl>
|
||||
// <ul><li>foo<li>bar</ul>
|
||||
else if ( tagName in CKEDITOR.dtd.$listItem &&
|
||||
!possiblySibling( tagName, currentName ) ) {
|
||||
parser.onTagOpen( tagName == 'li' ? 'ul' : 'dl', {}, 0, 1 );
|
||||
}
|
||||
// We're inside a structural block like table and list, AND the incoming element
|
||||
// is not of the same type (e.g. <td>td1<td>td2</td>), we simply add this new one before it,
|
||||
// and most importantly, return back to here once this element is added,
|
||||
// e.g. <table><tr><td>td1</td><p>p1</p><td>td2</td></tr></table>
|
||||
else if ( currentName in nonBreakingBlocks &&
|
||||
!possiblySibling( tagName, currentName ) ) {
|
||||
!element.returnPoint && ( element.returnPoint = currentNode );
|
||||
currentNode = currentNode.parent;
|
||||
} else {
|
||||
// The current element is an inline element, which
|
||||
// need to be continued even after the close, so put
|
||||
// it in the pending list.
|
||||
if ( currentName in CKEDITOR.dtd.$inline )
|
||||
pendingInline.unshift( currentNode );
|
||||
|
||||
// The most common case where we just need to close the
|
||||
// current one and append the new one to the parent.
|
||||
if ( currentNode.parent )
|
||||
addElement( currentNode, currentNode.parent, 1 );
|
||||
// We've tried our best to fix the embarrassment here, while
|
||||
// this element still doesn't find it's parent, mark it as
|
||||
// orphan and show our tolerance to it.
|
||||
else {
|
||||
element.isOrphan = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
checkPending( tagName );
|
||||
sendPendingBRs();
|
||||
|
||||
element.parent = currentNode;
|
||||
|
||||
if ( element.isEmpty )
|
||||
addElement( element );
|
||||
else
|
||||
currentNode = element;
|
||||
};
|
||||
|
||||
parser.onTagClose = function( tagName ) {
|
||||
// Check if there is any pending tag to be closed.
|
||||
for ( var i = pendingInline.length - 1; i >= 0; i-- ) {
|
||||
// If found, just remove it from the list.
|
||||
if ( tagName == pendingInline[ i ].name ) {
|
||||
pendingInline.splice( i, 1 );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var pendingAdd = [],
|
||||
newPendingInline = [],
|
||||
candidate = currentNode;
|
||||
|
||||
while ( candidate != root && candidate.name != tagName ) {
|
||||
// If this is an inline element, add it to the pending list, if we're
|
||||
// really closing one of the parents element later, they will continue
|
||||
// after it.
|
||||
if ( !candidate._.isBlockLike )
|
||||
newPendingInline.unshift( candidate );
|
||||
|
||||
// This node should be added to it's parent at this point. But,
|
||||
// it should happen only if the closing tag is really closing
|
||||
// one of the nodes. So, for now, we just cache it.
|
||||
pendingAdd.push( candidate );
|
||||
|
||||
// Make sure return point is properly restored.
|
||||
candidate = candidate.returnPoint || candidate.parent;
|
||||
}
|
||||
|
||||
if ( candidate != root ) {
|
||||
// Add all elements that have been found in the above loop.
|
||||
for ( i = 0; i < pendingAdd.length; i++ ) {
|
||||
var node = pendingAdd[ i ];
|
||||
addElement( node, node.parent );
|
||||
}
|
||||
|
||||
currentNode = candidate;
|
||||
|
||||
if ( candidate._.isBlockLike )
|
||||
sendPendingBRs();
|
||||
|
||||
addElement( candidate, candidate.parent );
|
||||
|
||||
// The parent should start receiving new nodes now, except if
|
||||
// addElement changed the currentNode.
|
||||
if ( candidate == currentNode )
|
||||
currentNode = currentNode.parent;
|
||||
|
||||
pendingInline = pendingInline.concat( newPendingInline );
|
||||
}
|
||||
|
||||
if ( tagName == 'body' )
|
||||
fixingBlock = false;
|
||||
};
|
||||
|
||||
parser.onText = function( text ) {
|
||||
// Trim empty spaces at beginning of text contents except <pre> and <textarea>.
|
||||
if ( ( !currentNode._.hasInlineStarted || pendingBRs.length ) && !inPre && !inTextarea ) {
|
||||
text = CKEDITOR.tools.ltrim( text );
|
||||
|
||||
if ( text.length === 0 )
|
||||
return;
|
||||
}
|
||||
|
||||
var currentName = currentNode.name,
|
||||
currentDtd = currentName ? ( CKEDITOR.dtd[ currentName ] || ( currentNode._.isBlockLike ? CKEDITOR.dtd.div : CKEDITOR.dtd.span ) ) : rootDtd;
|
||||
|
||||
// Fix orphan text in list/table. (#8540) (#8870)
|
||||
if ( !inTextarea && !currentDtd[ '#' ] && currentName in nonBreakingBlocks ) {
|
||||
parser.onTagOpen( currentName in listBlocks ? 'li' : currentName == 'dl' ? 'dd' : currentName == 'table' ? 'tr' : currentName == 'tr' ? 'td' : '' );
|
||||
parser.onText( text );
|
||||
return;
|
||||
}
|
||||
|
||||
sendPendingBRs();
|
||||
checkPending();
|
||||
|
||||
// Shrinking consequential spaces into one single for all elements
|
||||
// text contents.
|
||||
if ( !inPre && !inTextarea )
|
||||
text = text.replace( /[\t\r\n ]{2,}|[\t\r\n]/g, ' ' );
|
||||
|
||||
text = new CKEDITOR.htmlParser.text( text );
|
||||
|
||||
|
||||
if ( checkAutoParagraphing( currentNode, text ) )
|
||||
this.onTagOpen( fixingBlock, {}, 0, 1 );
|
||||
|
||||
currentNode.add( text );
|
||||
};
|
||||
|
||||
parser.onCDATA = function( cdata ) {
|
||||
currentNode.add( new CKEDITOR.htmlParser.cdata( cdata ) );
|
||||
};
|
||||
|
||||
parser.onComment = function( comment ) {
|
||||
sendPendingBRs();
|
||||
checkPending();
|
||||
currentNode.add( new CKEDITOR.htmlParser.comment( comment ) );
|
||||
};
|
||||
|
||||
// Parse it.
|
||||
parser.parse( fragmentHtml );
|
||||
|
||||
sendPendingBRs();
|
||||
|
||||
// Close all pending nodes, make sure return point is properly restored.
|
||||
while ( currentNode != root )
|
||||
addElement( currentNode, currentNode.parent, 1 );
|
||||
|
||||
removeTailWhitespace( root );
|
||||
|
||||
return root;
|
||||
};
|
||||
|
||||
CKEDITOR.htmlParser.fragment.prototype = {
|
||||
|
||||
/**
|
||||
* The node type. This is a constant value set to {@link CKEDITOR#NODE_DOCUMENT_FRAGMENT}.
|
||||
*
|
||||
* @readonly
|
||||
* @property {Number} [=CKEDITOR.NODE_DOCUMENT_FRAGMENT]
|
||||
*/
|
||||
type: CKEDITOR.NODE_DOCUMENT_FRAGMENT,
|
||||
|
||||
/**
|
||||
* Adds a node to this fragment.
|
||||
*
|
||||
* @param {CKEDITOR.htmlParser.node} node The node to be added.
|
||||
* @param {Number} [index] From where the insertion happens.
|
||||
*/
|
||||
add: function( node, index ) {
|
||||
isNaN( index ) && ( index = this.children.length );
|
||||
|
||||
var previous = index > 0 ? this.children[ index - 1 ] : null;
|
||||
if ( previous ) {
|
||||
// If the block to be appended is following text, trim spaces at
|
||||
// the right of it.
|
||||
if ( node._.isBlockLike && previous.type == CKEDITOR.NODE_TEXT ) {
|
||||
previous.value = CKEDITOR.tools.rtrim( previous.value );
|
||||
|
||||
// If we have completely cleared the previous node.
|
||||
if ( previous.value.length === 0 ) {
|
||||
// Remove it from the list and add the node again.
|
||||
this.children.pop();
|
||||
this.add( node );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
previous.next = node;
|
||||
}
|
||||
|
||||
node.previous = previous;
|
||||
node.parent = this;
|
||||
|
||||
this.children.splice( index, 0, node );
|
||||
|
||||
if ( !this._.hasInlineStarted )
|
||||
this._.hasInlineStarted = node.type == CKEDITOR.NODE_TEXT || ( node.type == CKEDITOR.NODE_ELEMENT && !node._.isBlockLike );
|
||||
},
|
||||
|
||||
/**
|
||||
* Filter this fragment's content with given filter.
|
||||
*
|
||||
* @since 4.1
|
||||
* @param {CKEDITOR.htmlParser.filter} filter
|
||||
*/
|
||||
filter: function( filter, context ) {
|
||||
context = this.getFilterContext( context );
|
||||
|
||||
// Apply the root filter.
|
||||
filter.onRoot( context, this );
|
||||
|
||||
this.filterChildren( filter, false, context );
|
||||
},
|
||||
|
||||
/**
|
||||
* Filter this fragment's children with given filter.
|
||||
*
|
||||
* Element's children may only be filtered once by one
|
||||
* instance of filter.
|
||||
*
|
||||
* @since 4.1
|
||||
* @param {CKEDITOR.htmlParser.filter} filter
|
||||
* @param {Boolean} [filterRoot] Whether to apply the "root" filter rule specified in the `filter`.
|
||||
*/
|
||||
filterChildren: function( filter, filterRoot, context ) {
|
||||
// If this element's children were already filtered
|
||||
// by current filter, don't filter them 2nd time.
|
||||
// This situation may occur when filtering bottom-up
|
||||
// (filterChildren() called manually in element's filter),
|
||||
// or in unpredictable edge cases when filter
|
||||
// is manipulating DOM structure.
|
||||
if ( this.childrenFilteredBy == filter.id )
|
||||
return;
|
||||
|
||||
context = this.getFilterContext( context );
|
||||
|
||||
// Filtering root if enforced.
|
||||
if ( filterRoot && !this.parent )
|
||||
filter.onRoot( context, this );
|
||||
|
||||
this.childrenFilteredBy = filter.id;
|
||||
|
||||
// Don't cache anything, children array may be modified by filter rule.
|
||||
for ( var i = 0; i < this.children.length; i++ ) {
|
||||
// Stay in place if filter returned false, what means
|
||||
// that node has been removed.
|
||||
if ( this.children[ i ].filter( filter, context ) === false )
|
||||
i--;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Writes the fragment HTML to a {@link CKEDITOR.htmlParser.basicWriter}.
|
||||
*
|
||||
* var writer = new CKEDITOR.htmlWriter();
|
||||
* var fragment = CKEDITOR.htmlParser.fragment.fromHtml( '<P><B>Example' );
|
||||
* fragment.writeHtml( writer );
|
||||
* alert( writer.getHtml() ); // '<p><b>Example</b></p>'
|
||||
*
|
||||
* @param {CKEDITOR.htmlParser.basicWriter} writer The writer to which write the HTML.
|
||||
* @param {CKEDITOR.htmlParser.filter} [filter] The filter to use when writing the HTML.
|
||||
*/
|
||||
writeHtml: function( writer, filter ) {
|
||||
if ( filter )
|
||||
this.filter( filter );
|
||||
|
||||
this.writeChildrenHtml( writer );
|
||||
},
|
||||
|
||||
/**
|
||||
* Write and filtering the child nodes of this fragment.
|
||||
*
|
||||
* @param {CKEDITOR.htmlParser.basicWriter} writer The writer to which write the HTML.
|
||||
* @param {CKEDITOR.htmlParser.filter} [filter] The filter to use when writing the HTML.
|
||||
* @param {Boolean} [filterRoot] Whether to apply the "root" filter rule specified in the `filter`.
|
||||
*/
|
||||
writeChildrenHtml: function( writer, filter, filterRoot ) {
|
||||
var context = this.getFilterContext();
|
||||
|
||||
// Filtering root if enforced.
|
||||
if ( filterRoot && !this.parent && filter )
|
||||
filter.onRoot( context, this );
|
||||
|
||||
if ( filter )
|
||||
this.filterChildren( filter, false, context );
|
||||
|
||||
for ( var i = 0, children = this.children, l = children.length; i < l; i++ )
|
||||
children[ i ].writeHtml( writer );
|
||||
},
|
||||
|
||||
/**
|
||||
* Execute callback on each node (of given type) in this document fragment.
|
||||
*
|
||||
* var fragment = CKEDITOR.htmlParser.fragment.fromHtml( '<p>foo<b>bar</b>bom</p>' );
|
||||
* fragment.forEach( function( node ) {
|
||||
* console.log( node );
|
||||
* } );
|
||||
* // Will log:
|
||||
* // 1. document fragment,
|
||||
* // 2. <p> element,
|
||||
* // 3. "foo" text node,
|
||||
* // 4. <b> element,
|
||||
* // 5. "bar" text node,
|
||||
* // 6. "bom" text node.
|
||||
*
|
||||
* @since 4.1
|
||||
* @param {Function} callback Function to be executed on every node.
|
||||
* **Since 4.3** if `callback` returned `false` descendants of current node will be ignored.
|
||||
* @param {CKEDITOR.htmlParser.node} callback.node Node passed as argument.
|
||||
* @param {Number} [type] If specified `callback` will be executed only on nodes of this type.
|
||||
* @param {Boolean} [skipRoot] Don't execute `callback` on this fragment.
|
||||
*/
|
||||
forEach: function( callback, type, skipRoot ) {
|
||||
if ( !skipRoot && ( !type || this.type == type ) )
|
||||
var ret = callback( this );
|
||||
|
||||
// Do not filter children if callback returned false.
|
||||
if ( ret === false )
|
||||
return;
|
||||
|
||||
var children = this.children,
|
||||
node,
|
||||
i = 0;
|
||||
|
||||
// We do not cache the size, because the list of nodes may be changed by the callback.
|
||||
for ( ; i < children.length; i++ ) {
|
||||
node = children[ i ];
|
||||
if ( node.type == CKEDITOR.NODE_ELEMENT )
|
||||
node.forEach( callback, type );
|
||||
else if ( !type || node.type == type )
|
||||
callback( node );
|
||||
}
|
||||
},
|
||||
|
||||
getFilterContext: function( context ) {
|
||||
return context || {};
|
||||
}
|
||||
};
|
||||
} )();
|
||||
@@ -1,150 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
( function() {
|
||||
/**
|
||||
* A lightweight representation of HTML node.
|
||||
*
|
||||
* @since 4.1
|
||||
* @class
|
||||
* @constructor Creates a node class instance.
|
||||
*/
|
||||
CKEDITOR.htmlParser.node = function() {};
|
||||
|
||||
CKEDITOR.htmlParser.node.prototype = {
|
||||
/**
|
||||
* Remove this node from a tree.
|
||||
*
|
||||
* @since 4.1
|
||||
*/
|
||||
remove: function() {
|
||||
var children = this.parent.children,
|
||||
index = CKEDITOR.tools.indexOf( children, this ),
|
||||
previous = this.previous,
|
||||
next = this.next;
|
||||
|
||||
previous && ( previous.next = next );
|
||||
next && ( next.previous = previous );
|
||||
children.splice( index, 1 );
|
||||
this.parent = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Replace this node with given one.
|
||||
*
|
||||
* @since 4.1
|
||||
* @param {CKEDITOR.htmlParser.node} node The node that will replace this one.
|
||||
*/
|
||||
replaceWith: function( node ) {
|
||||
var children = this.parent.children,
|
||||
index = CKEDITOR.tools.indexOf( children, this ),
|
||||
previous = node.previous = this.previous,
|
||||
next = node.next = this.next;
|
||||
|
||||
previous && ( previous.next = node );
|
||||
next && ( next.previous = node );
|
||||
|
||||
children[ index ] = node;
|
||||
|
||||
node.parent = this.parent;
|
||||
this.parent = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Insert this node after given one.
|
||||
*
|
||||
* @since 4.1
|
||||
* @param {CKEDITOR.htmlParser.node} node The node that will precede this element.
|
||||
*/
|
||||
insertAfter: function( node ) {
|
||||
var children = node.parent.children,
|
||||
index = CKEDITOR.tools.indexOf( children, node ),
|
||||
next = node.next;
|
||||
|
||||
children.splice( index + 1, 0, this );
|
||||
|
||||
this.next = node.next;
|
||||
this.previous = node;
|
||||
node.next = this;
|
||||
next && ( next.previous = this );
|
||||
|
||||
this.parent = node.parent;
|
||||
},
|
||||
|
||||
/**
|
||||
* Insert this node before given one.
|
||||
*
|
||||
* @since 4.1
|
||||
* @param {CKEDITOR.htmlParser.node} node The node that will follow this element.
|
||||
*/
|
||||
insertBefore: function( node ) {
|
||||
var children = node.parent.children,
|
||||
index = CKEDITOR.tools.indexOf( children, node );
|
||||
|
||||
children.splice( index, 0, this );
|
||||
|
||||
this.next = node;
|
||||
this.previous = node.previous;
|
||||
node.previous && ( node.previous.next = this );
|
||||
node.previous = this;
|
||||
|
||||
this.parent = node.parent;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the closest ancestor element of this element which satisfies given condition
|
||||
*
|
||||
* @since 4.3
|
||||
* @param {String/Object/Function} condition Name of an ancestor, hash of names or validator function.
|
||||
* @returns {CKEDITOR.htmlParser.element} The closest ancestor which satisfies given condition or `null`.
|
||||
*/
|
||||
getAscendant: function( condition ) {
|
||||
var checkFn =
|
||||
typeof condition == 'function' ? condition :
|
||||
typeof condition == 'string' ? function( el ) { return el.name == condition; } :
|
||||
function( el ) { return el.name in condition; };
|
||||
|
||||
var parent = this.parent;
|
||||
|
||||
// Parent has to be an element - don't check doc fragment.
|
||||
while ( parent && parent.type == CKEDITOR.NODE_ELEMENT ) {
|
||||
if ( checkFn( parent ) )
|
||||
return parent;
|
||||
parent = parent.parent;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Wraps this element with given `wrapper`.
|
||||
*
|
||||
* @since 4.3
|
||||
* @param {CKEDITOR.htmlParser.element} wrapper The element which will be this element's new parent.
|
||||
* @returns {CKEDITOR.htmlParser.element} Wrapper.
|
||||
*/
|
||||
wrapWith: function( wrapper ) {
|
||||
this.replaceWith( wrapper );
|
||||
wrapper.add( this );
|
||||
return wrapper;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets this node's index in its parent's children array.
|
||||
*
|
||||
* @since 4.3
|
||||
* @returns {Number}
|
||||
*/
|
||||
getIndex: function() {
|
||||
return CKEDITOR.tools.indexOf( this.parent.children, this );
|
||||
},
|
||||
|
||||
getFilterContext: function( context ) {
|
||||
return context || {};
|
||||
}
|
||||
};
|
||||
} )();
|
||||
@@ -1,70 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
( function() {
|
||||
/**
|
||||
* A lightweight representation of HTML text.
|
||||
*
|
||||
* @class
|
||||
* @extends CKEDITOR.htmlParser.node
|
||||
* @constructor Creates a text class instance.
|
||||
* @param {String} value The text node value.
|
||||
*/
|
||||
CKEDITOR.htmlParser.text = function( value ) {
|
||||
/**
|
||||
* The text value.
|
||||
*
|
||||
* @property {String}
|
||||
*/
|
||||
this.value = value;
|
||||
|
||||
/** @private */
|
||||
this._ = {
|
||||
isBlockLike: false
|
||||
};
|
||||
};
|
||||
|
||||
CKEDITOR.htmlParser.text.prototype = CKEDITOR.tools.extend( new CKEDITOR.htmlParser.node(), {
|
||||
/**
|
||||
* The node type. This is a constant value set to {@link CKEDITOR#NODE_TEXT}.
|
||||
*
|
||||
* @readonly
|
||||
* @property {Number} [=CKEDITOR.NODE_TEXT]
|
||||
*/
|
||||
type: CKEDITOR.NODE_TEXT,
|
||||
|
||||
/**
|
||||
* Filter this text node with given filter.
|
||||
*
|
||||
* @since 4.1
|
||||
* @param {CKEDITOR.htmlParser.filter} filter
|
||||
* @returns {Boolean} Method returns `false` when this text node has
|
||||
* been removed. This is an information for {@link CKEDITOR.htmlParser.element#filterChildren}
|
||||
* that it has to repeat filter on current position in parent's children array.
|
||||
*/
|
||||
filter: function( filter, context ) {
|
||||
if ( !( this.value = filter.onText( context, this.value, this ) ) ) {
|
||||
this.remove();
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Writes the HTML representation of this text to a {CKEDITOR.htmlParser.basicWriter}.
|
||||
*
|
||||
* @param {CKEDITOR.htmlParser.basicWriter} writer The writer to which write the HTML.
|
||||
* @param {CKEDITOR.htmlParser.filter} [filter] The filter to be applied to this node.
|
||||
* **Note:** it's unsafe to filter offline (not appended) node.
|
||||
*/
|
||||
writeHtml: function( writer, filter ) {
|
||||
if ( filter )
|
||||
this.filter( filter );
|
||||
|
||||
writer.text( this.value );
|
||||
}
|
||||
} );
|
||||
} )();
|
||||
@@ -1,153 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* Controls keystrokes typing in an editor instance.
|
||||
*
|
||||
* @class
|
||||
* @constructor Creates a keystrokeHandler class instance.
|
||||
* @param {CKEDITOR.editor} editor The editor instance.
|
||||
*/
|
||||
CKEDITOR.keystrokeHandler = function( editor ) {
|
||||
if ( editor.keystrokeHandler )
|
||||
return editor.keystrokeHandler;
|
||||
|
||||
/**
|
||||
* List of keystrokes associated to commands. Each entry points to the
|
||||
* command to be executed.
|
||||
*
|
||||
* Since CKEditor 4 there's no need to modify this property directly during the runtime.
|
||||
* Use {@link CKEDITOR.editor#setKeystroke} instead.
|
||||
*/
|
||||
this.keystrokes = {};
|
||||
|
||||
/**
|
||||
* List of keystrokes that should be blocked if not defined at
|
||||
* {@link #keystrokes}. In this way it is possible to block the default
|
||||
* browser behavior for those keystrokes.
|
||||
*/
|
||||
this.blockedKeystrokes = {};
|
||||
|
||||
this._ = {
|
||||
editor: editor
|
||||
};
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
( function() {
|
||||
var cancel;
|
||||
|
||||
var onKeyDown = function( event ) {
|
||||
// The DOM event object is passed by the "data" property.
|
||||
event = event.data;
|
||||
|
||||
var keyCombination = event.getKeystroke();
|
||||
var command = this.keystrokes[ keyCombination ];
|
||||
var editor = this._.editor;
|
||||
|
||||
cancel = ( editor.fire( 'key', { keyCode: keyCombination } ) === false );
|
||||
|
||||
if ( !cancel ) {
|
||||
if ( command ) {
|
||||
var data = { from: 'keystrokeHandler' };
|
||||
cancel = ( editor.execCommand( command, data ) !== false );
|
||||
}
|
||||
|
||||
if ( !cancel )
|
||||
cancel = !!this.blockedKeystrokes[ keyCombination ];
|
||||
}
|
||||
|
||||
if ( cancel )
|
||||
event.preventDefault( true );
|
||||
|
||||
return !cancel;
|
||||
};
|
||||
|
||||
var onKeyPress = function( event ) {
|
||||
if ( cancel ) {
|
||||
cancel = false;
|
||||
event.data.preventDefault( true );
|
||||
}
|
||||
};
|
||||
|
||||
CKEDITOR.keystrokeHandler.prototype = {
|
||||
/**
|
||||
* Attaches this keystroke handle to a DOM object. Keystrokes typed
|
||||
* over this object will get handled by this keystrokeHandler.
|
||||
*
|
||||
* @param {CKEDITOR.dom.domObject} domObject The DOM object to attach to.
|
||||
*/
|
||||
attach: function( domObject ) {
|
||||
// For most browsers, it is enough to listen to the keydown event
|
||||
// only.
|
||||
domObject.on( 'keydown', onKeyDown, this );
|
||||
|
||||
// Some browsers instead, don't cancel key events in the keydown, but in the
|
||||
// keypress. So we must do a longer trip in those cases.
|
||||
if ( CKEDITOR.env.opera || ( CKEDITOR.env.gecko && CKEDITOR.env.mac ) )
|
||||
domObject.on( 'keypress', onKeyPress, this );
|
||||
}
|
||||
};
|
||||
} )();
|
||||
|
||||
/**
|
||||
* A list associating keystrokes to editor commands. Each element in the list
|
||||
* is an array where the first item is the keystroke, and the second is the
|
||||
* name of the command to be executed.
|
||||
*
|
||||
* This setting should be used to define (as well as to overwrite or remove) keystrokes
|
||||
* set by plugins (like `link` and `basicstyles`). If you want to set a keystroke
|
||||
* for your plugin or during the runtime, use {@link CKEDITOR.editor#setKeystroke} instead.
|
||||
*
|
||||
* Since default keystrokes are set by {@link CKEDITOR.editor#setKeystroke}
|
||||
* method, by default `config.keystrokes` is an empty array.
|
||||
*
|
||||
* See {@link CKEDITOR.editor#setKeystroke} documentation for more details
|
||||
* regarding the start up order.
|
||||
*
|
||||
* // Change default CTRL + L keystroke for 'link' command to CTRL + SHIFT + L.
|
||||
* config.keystrokes = [
|
||||
* ...
|
||||
* [ CKEDITOR.CTRL + CKEDITOR.SHIFT + 76, 'link' ], // CTRL + SHIFT + L
|
||||
* ...
|
||||
* ];
|
||||
*
|
||||
* To reset a particular keystroke, the following approach can be used:
|
||||
*
|
||||
* // Disable default CTRL + L keystroke which executes link command by default.
|
||||
* config.keystrokes = [
|
||||
* ...
|
||||
* [ CKEDITOR.CTRL + 76, null ], // CTRL + L
|
||||
* ...
|
||||
* ];
|
||||
*
|
||||
* To reset all default keystrokes an {@link CKEDITOR#instanceReady} callback should be
|
||||
* used. This is since editor defaults are merged rather than overwritten by
|
||||
* user keystrokes.
|
||||
*
|
||||
* **Note**: This can be potentially harmful for an editor. Avoid this unless you're
|
||||
* aware of the consequences.
|
||||
*
|
||||
* // Reset all default keystrokes.
|
||||
* config.on.instanceReady = function() {
|
||||
* this.keystrokeHandler.keystrokes = [];
|
||||
* };
|
||||
*
|
||||
* @cfg {Array} [keystrokes=[]]
|
||||
* @member CKEDITOR.config
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fired when any keyboard key (or combination) is pressed into the editing area.
|
||||
*
|
||||
* @event key
|
||||
* @member CKEDITOR.editor
|
||||
* @param data
|
||||
* @param {Number} data.keyCode A number representing the key code (or combination).
|
||||
* It is the sum of the current key code and the {@link CKEDITOR#CTRL}, {@link CKEDITOR#SHIFT}
|
||||
* and {@link CKEDITOR#ALT} constants, if those are pressed.
|
||||
* @param {CKEDITOR.editor} editor This editor instance.
|
||||
*/
|
||||
@@ -1,100 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
( function() {
|
||||
var loadedLangs = {};
|
||||
|
||||
/**
|
||||
* Stores language-related functions.
|
||||
*
|
||||
* @class
|
||||
* @singleton
|
||||
*/
|
||||
CKEDITOR.lang = {
|
||||
/**
|
||||
* The list of languages available in the editor core.
|
||||
*
|
||||
* alert( CKEDITOR.lang.en ); // 1
|
||||
*/
|
||||
languages: { af: 1, ar: 1, bg: 1, bn: 1, bs: 1, ca: 1, cs: 1, cy: 1, da: 1, de: 1, el: 1,
|
||||
'en-au': 1, 'en-ca': 1, 'en-gb': 1, en: 1, eo: 1, es: 1, et: 1, eu: 1, fa: 1, fi: 1, fo: 1,
|
||||
'fr-ca': 1, fr: 1, gl: 1, gu: 1, he: 1, hi: 1, hr: 1, hu: 1, id: 1, is: 1, it: 1, ja: 1, ka: 1,
|
||||
km: 1, ko: 1, ku: 1, lt: 1, lv: 1, mk: 1, mn: 1, ms: 1, nb: 1, nl: 1, no: 1, pl: 1, 'pt-br': 1,
|
||||
pt: 1, ro: 1, ru: 1, si: 1, sk: 1, sl: 1, sq: 1, 'sr-latn': 1, sr: 1, sv: 1, th: 1, tr: 1, ug: 1,
|
||||
uk: 1, vi: 1, 'zh-cn': 1, zh: 1 },
|
||||
|
||||
/**
|
||||
* The list of languages that are written Right-To-Left (RTL) and are supported by the editor.
|
||||
*/
|
||||
rtl: { ar: 1, fa: 1, he: 1, ku: 1, ug: 1 },
|
||||
|
||||
/**
|
||||
* Loads a specific language file, or auto detects it. A callback is
|
||||
* then called when the file gets loaded.
|
||||
*
|
||||
* @param {String} languageCode The code of the language file to be
|
||||
* loaded. If null or empty, autodetection will be performed. The
|
||||
* same happens if the language is not supported.
|
||||
* @param {String} defaultLanguage The language to be used if
|
||||
* `languageCode` is not supported or if the autodetection fails.
|
||||
* @param {Function} callback A function to be called once the
|
||||
* language file is loaded. Two parameters are passed to this
|
||||
* function: the language code and the loaded language entries.
|
||||
*/
|
||||
load: function( languageCode, defaultLanguage, callback ) {
|
||||
// If no languageCode - fallback to browser or default.
|
||||
// If languageCode - fallback to no-localized version or default.
|
||||
if ( !languageCode || !CKEDITOR.lang.languages[ languageCode ] )
|
||||
languageCode = this.detect( defaultLanguage, languageCode );
|
||||
|
||||
if ( !this[ languageCode ] ) {
|
||||
CKEDITOR.scriptLoader.load( CKEDITOR.getUrl( 'lang/' + languageCode + '.js' ), function() {
|
||||
this[ languageCode ].dir = this.rtl[ languageCode ] ? 'rtl' : 'ltr';
|
||||
callback( languageCode, this[ languageCode ] );
|
||||
}, this );
|
||||
} else
|
||||
callback( languageCode, this[ languageCode ] );
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the language that best fits the user language. For example,
|
||||
* suppose that the user language is "pt-br". If this language is
|
||||
* supported by the editor, it is returned. Otherwise, if only "pt" is
|
||||
* supported, it is returned instead. If none of the previous are
|
||||
* supported, a default language is then returned.
|
||||
*
|
||||
* alert( CKEDITOR.lang.detect( 'en' ) ); // e.g., in a German browser: 'de'
|
||||
*
|
||||
* @param {String} defaultLanguage The default language to be returned
|
||||
* if the user language is not supported.
|
||||
* @param {String} [probeLanguage] A language code to try to use,
|
||||
* instead of the browser-based autodetection.
|
||||
* @returns {String} The detected language code.
|
||||
*/
|
||||
detect: function( defaultLanguage, probeLanguage ) {
|
||||
var languages = this.languages;
|
||||
probeLanguage = probeLanguage || navigator.userLanguage || navigator.language || defaultLanguage;
|
||||
|
||||
var parts = probeLanguage.toLowerCase().match( /([a-z]+)(?:-([a-z]+))?/ ),
|
||||
lang = parts[ 1 ],
|
||||
locale = parts[ 2 ];
|
||||
|
||||
if ( languages[ lang + '-' + locale ] )
|
||||
lang = lang + '-' + locale;
|
||||
else if ( !languages[ lang ] )
|
||||
lang = null;
|
||||
|
||||
CKEDITOR.lang.detect = lang ?
|
||||
function() {
|
||||
return lang;
|
||||
} : function( defaultLanguage ) {
|
||||
return defaultLanguage;
|
||||
};
|
||||
|
||||
return lang || defaultLanguage;
|
||||
}
|
||||
};
|
||||
|
||||
} )();
|
||||
@@ -1,247 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileOverview Defines the {@link CKEDITOR.loader} objects, which is used to
|
||||
* load core scripts and their dependencies from _source.
|
||||
*/
|
||||
|
||||
if ( typeof CKEDITOR == 'undefined' )
|
||||
CKEDITOR = {};
|
||||
|
||||
if ( !CKEDITOR.loader ) {
|
||||
/**
|
||||
* Load core scripts and their dependencies from _source.
|
||||
*
|
||||
* @class
|
||||
* @singleton
|
||||
*/
|
||||
CKEDITOR.loader = ( function() {
|
||||
// Table of script names and their dependencies.
|
||||
var scripts = {
|
||||
'_bootstrap': [ 'config', 'creators/inline', 'creators/themedui', 'editable', 'ckeditor', 'plugins', 'scriptloader', 'style', 'tools', /* The following are entries that we want to force loading at the end to avoid dependence recursion */ 'dom/comment', 'dom/elementpath', 'dom/text', 'dom/rangelist', 'skin' ],
|
||||
'ckeditor': [ 'ckeditor_basic', 'dom', 'dtd', 'dom/document', 'dom/element', 'dom/iterator', 'editor', 'event', 'htmldataprocessor', 'htmlparser', 'htmlparser/element', 'htmlparser/fragment', 'htmlparser/filter', 'htmlparser/basicwriter', 'template', 'tools' ],
|
||||
'ckeditor_base': [],
|
||||
'ckeditor_basic': [ 'editor_basic', 'env', 'event' ],
|
||||
'command': [],
|
||||
'config': [ 'ckeditor_base' ],
|
||||
'dom': [],
|
||||
'dom/comment': [ 'dom/node' ],
|
||||
'dom/document': [ 'dom/node', 'dom/window' ],
|
||||
'dom/documentfragment': [ 'dom/element' ],
|
||||
'dom/element': [ 'dom', 'dom/document', 'dom/domobject', 'dom/node', 'dom/nodelist', 'tools' ],
|
||||
'dom/elementpath': [ 'dom/element' ],
|
||||
'dom/event': [],
|
||||
'dom/iterator': [ 'dom/range' ],
|
||||
'dom/node': [ 'dom/domobject', 'tools' ],
|
||||
'dom/nodelist': [ 'dom/node' ],
|
||||
'dom/domobject': [ 'dom/event' ],
|
||||
'dom/range': [ 'dom/document', 'dom/documentfragment', 'dom/element', 'dom/walker' ],
|
||||
'dom/rangelist': [ 'dom/range' ],
|
||||
'dom/text': [ 'dom/node', 'dom/domobject' ],
|
||||
'dom/walker': [ 'dom/node' ],
|
||||
'dom/window': [ 'dom/domobject' ],
|
||||
'dtd': [ 'tools' ],
|
||||
'editable': [ 'editor', 'tools' ],
|
||||
'editor': [ 'command', 'config', 'editor_basic', 'filter', 'focusmanager', 'keystrokehandler', 'lang', 'plugins', 'tools', 'ui' ],
|
||||
'editor_basic': [ 'event' ],
|
||||
'env': [],
|
||||
'event': [],
|
||||
'filter': [ 'dtd', 'tools' ],
|
||||
'focusmanager': [],
|
||||
'htmldataprocessor': [ 'htmlparser', 'htmlparser/basicwriter', 'htmlparser/fragment', 'htmlparser/filter' ],
|
||||
'htmlparser': [],
|
||||
'htmlparser/comment': [ 'htmlparser', 'htmlparser/node' ],
|
||||
'htmlparser/element': [ 'htmlparser', 'htmlparser/fragment', 'htmlparser/node' ],
|
||||
'htmlparser/fragment': [ 'htmlparser', 'htmlparser/comment', 'htmlparser/text', 'htmlparser/cdata' ],
|
||||
'htmlparser/text': [ 'htmlparser', 'htmlparser/node' ],
|
||||
'htmlparser/cdata': [ 'htmlparser', 'htmlparser/node' ],
|
||||
'htmlparser/filter': [ 'htmlparser' ],
|
||||
'htmlparser/basicwriter': [ 'htmlparser' ],
|
||||
'htmlparser/node': [ 'htmlparser' ],
|
||||
'keystrokehandler': [ 'event' ],
|
||||
'lang': [],
|
||||
'plugins': [ 'resourcemanager' ],
|
||||
'resourcemanager': [ 'scriptloader', 'tools' ],
|
||||
'scriptloader': [ 'dom/element', 'env' ],
|
||||
'selection': [ 'dom/range', 'dom/walker' ],
|
||||
'skin': [],
|
||||
'style': [ 'selection' ],
|
||||
'template': [],
|
||||
'tools': [ 'env' ],
|
||||
'ui': [],
|
||||
'creators/themedui': [],
|
||||
'creators/inline': []
|
||||
};
|
||||
|
||||
var basePath = ( function() {
|
||||
// This is a copy of CKEDITOR.basePath, but requires the script having
|
||||
// "_source/loader.js".
|
||||
if ( CKEDITOR && CKEDITOR.basePath )
|
||||
return CKEDITOR.basePath;
|
||||
|
||||
// Find out the editor directory path, based on its <script> tag.
|
||||
var path = '';
|
||||
var scripts = document.getElementsByTagName( 'script' );
|
||||
|
||||
for ( var i = 0; i < scripts.length; i++ ) {
|
||||
var match = scripts[ i ].src.match( /(^|.*?[\\\/])(?:_source\/)?core\/loader.js(?:\?.*)?$/i );
|
||||
|
||||
if ( match ) {
|
||||
path = match[ 1 ];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// In IE (only) the script.src string is the raw valued entered in the
|
||||
// HTML. Other browsers return the full resolved URL instead.
|
||||
if ( path.indexOf( '://' ) == -1 ) {
|
||||
// Absolute path.
|
||||
if ( path.indexOf( '/' ) === 0 )
|
||||
path = location.href.match( /^.*?:\/\/[^\/]*/ )[ 0 ] + path;
|
||||
// Relative path.
|
||||
else
|
||||
path = location.href.match( /^[^\?]*\// )[ 0 ] + path;
|
||||
}
|
||||
|
||||
return path;
|
||||
} )();
|
||||
|
||||
var timestamp = ( CKEDITOR && CKEDITOR.timestamp ) || ( new Date() ).valueOf(); // %REMOVE_LINE%
|
||||
/* // %REMOVE_LINE%
|
||||
* The production implementation contains a fixed timestamp // %REMOVE_LINE%
|
||||
* generated by the releaser // %REMOVE_LINE%
|
||||
var timestamp = '%TIMESTAMP%';
|
||||
*/ // %REMOVE_LINE%
|
||||
|
||||
var getUrl = function( resource ) {
|
||||
if ( CKEDITOR && CKEDITOR.getUrl )
|
||||
return CKEDITOR.getUrl( resource );
|
||||
|
||||
return basePath + resource + ( resource.indexOf( '?' ) >= 0 ? '&' : '?' ) + 't=' + timestamp;
|
||||
};
|
||||
|
||||
var pendingLoad = [];
|
||||
|
||||
return {
|
||||
/**
|
||||
* The list of loaded scripts in their loading order.
|
||||
*
|
||||
* // Alert the loaded script names.
|
||||
* alert( CKEDITOR.loader.loadedScripts );
|
||||
*/
|
||||
loadedScripts: [],
|
||||
/**
|
||||
* Table of script names and their dependencies.
|
||||
*
|
||||
* @property {Array}
|
||||
*/
|
||||
scripts: scripts,
|
||||
|
||||
/**
|
||||
* @todo
|
||||
*/
|
||||
loadPending: function() {
|
||||
var scriptName = pendingLoad.shift();
|
||||
|
||||
if ( !scriptName )
|
||||
return;
|
||||
|
||||
var scriptSrc = getUrl( 'core/' + scriptName + '.js' );
|
||||
|
||||
var script = document.createElement( 'script' );
|
||||
script.type = 'text/javascript';
|
||||
script.src = scriptSrc;
|
||||
|
||||
function onScriptLoaded() {
|
||||
// Append this script to the list of loaded scripts.
|
||||
CKEDITOR.loader.loadedScripts.push( scriptName );
|
||||
|
||||
// Load the next.
|
||||
CKEDITOR.loader.loadPending();
|
||||
}
|
||||
|
||||
// We must guarantee the execution order of the scripts, so we
|
||||
// need to load them one by one. (#4145)
|
||||
// The following if/else block has been taken from the scriptloader core code.
|
||||
if ( typeof( script.onreadystatechange ) !== "undefined" ) {
|
||||
/** @ignore */
|
||||
script.onreadystatechange = function() {
|
||||
if ( script.readyState == 'loaded' || script.readyState == 'complete' ) {
|
||||
script.onreadystatechange = null;
|
||||
onScriptLoaded();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
/** @ignore */
|
||||
script.onload = function() {
|
||||
// Some browsers, such as Safari, may call the onLoad function
|
||||
// immediately. Which will break the loading sequence. (#3661)
|
||||
setTimeout( function() {
|
||||
onScriptLoaded( scriptName );
|
||||
}, 0 );
|
||||
};
|
||||
}
|
||||
|
||||
document.body.appendChild( script );
|
||||
},
|
||||
|
||||
/**
|
||||
* Loads a specific script, including its dependencies. This is not a
|
||||
* synchronous loading, which means that the code to be loaded will
|
||||
* not necessarily be available after this call.
|
||||
*
|
||||
* CKEDITOR.loader.load( 'dom/element' );
|
||||
*
|
||||
* @param {String} scriptName
|
||||
* @param {Boolean} [defer=false]
|
||||
* @todo params
|
||||
*/
|
||||
load: function( scriptName, defer ) {
|
||||
// Check if the script has already been loaded.
|
||||
if ( ( 's:' + scriptName ) in this.loadedScripts )
|
||||
return;
|
||||
|
||||
// Get the script dependencies list.
|
||||
var dependencies = scripts[ scriptName ];
|
||||
if ( !dependencies )
|
||||
throw 'The script name"' + scriptName + '" is not defined.';
|
||||
|
||||
// Mark the script as loaded, even before really loading it, to
|
||||
// avoid cross references recursion.
|
||||
// Prepend script name with 's:' to avoid conflict with Array's methods.
|
||||
this.loadedScripts[ 's:' + scriptName ] = true;
|
||||
|
||||
// Load all dependencies first.
|
||||
for ( var i = 0; i < dependencies.length; i++ )
|
||||
this.load( dependencies[ i ], true );
|
||||
|
||||
var scriptSrc = getUrl( 'core/' + scriptName + '.js' );
|
||||
|
||||
// Append the <script> element to the DOM.
|
||||
// If the page is fully loaded, we can't use document.write
|
||||
// but if the script is run while the body is loading then it's safe to use it
|
||||
// Unfortunately, Firefox <3.6 doesn't support document.readyState, so it won't get this improvement
|
||||
if ( document.body && ( !document.readyState || document.readyState == 'complete' ) ) {
|
||||
pendingLoad.push( scriptName );
|
||||
|
||||
if ( !defer )
|
||||
this.loadPending();
|
||||
} else {
|
||||
// Append this script to the list of loaded scripts.
|
||||
this.loadedScripts.push( scriptName );
|
||||
|
||||
document.write( '<script src="' + scriptSrc + '" type="text/javascript"><\/script>' );
|
||||
}
|
||||
}
|
||||
};
|
||||
} )();
|
||||
}
|
||||
|
||||
// Check if any script has been defined for autoload.
|
||||
if ( CKEDITOR._autoLoad ) {
|
||||
CKEDITOR.loader.load( CKEDITOR._autoLoad );
|
||||
delete CKEDITOR._autoLoad;
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileOverview Defines the "virtual" {@link CKEDITOR.pluginDefinition} class which
|
||||
* contains the defintion of a plugin. This file is for documentation
|
||||
* purposes only.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Virtual class which just illustrates the features of plugin objects to be
|
||||
* passed to the {@link CKEDITOR.plugins#add} method.
|
||||
*
|
||||
* This class is not really part of the API, so its constructor should not be called.
|
||||
*
|
||||
* @class CKEDITOR.pluginDefinition
|
||||
* @abstract
|
||||
*/
|
||||
|
||||
/**
|
||||
* A list of plugins that are required by this plugin. Note that this property
|
||||
* does not determine the loading order of the plugins.
|
||||
*
|
||||
* CKEDITOR.plugins.add( 'sample', {
|
||||
* requires: [ 'button', 'selection' ]
|
||||
* } );
|
||||
*
|
||||
* @property {Array} requires
|
||||
*/
|
||||
|
||||
/**
|
||||
* A list of language files available for this plugin. These files are stored inside
|
||||
* the `lang` directory inside the plugin directory, follow the name
|
||||
* pattern of `langCode.js`, and contain the language definition created with
|
||||
* {@link CKEDITOR.plugins#setLang}.
|
||||
*
|
||||
* When the plugin is being loaded, the editor checks this list to see if
|
||||
* a language file of the current editor language ({@link CKEDITOR.editor#langCode})
|
||||
* is available, and if so, loads it. Otherwise, the file represented by the first item
|
||||
* in the list is loaded.
|
||||
*
|
||||
* CKEDITOR.plugins.add( 'sample', {
|
||||
* lang: [ 'en', 'fr' ]
|
||||
* } );
|
||||
*
|
||||
* @property {Array} lang
|
||||
*/
|
||||
|
||||
/**
|
||||
* A function called on initialization of every editor instance created in the
|
||||
* page before the {@link #init} call task. The `beforeInit` function will be called for
|
||||
* all plugins, after that the `init` function is called for all of them. This
|
||||
* feature makes it possible to initialize things that could be used in the
|
||||
* `init` function of other plugins.
|
||||
*
|
||||
* CKEDITOR.plugins.add( 'sample', {
|
||||
* beforeInit: function( editor ) {
|
||||
* alert( 'Editor "' + editor.name + '" is to be initialized!' );
|
||||
* }
|
||||
* } );
|
||||
*
|
||||
* @method beforeInit
|
||||
* @param {CKEDITOR.editor} editor The editor instance being initialized.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Function called on initialization of every editor instance created in the page.
|
||||
*
|
||||
* CKEDITOR.plugins.add( 'sample', {
|
||||
* init: function( editor ) {
|
||||
* alert( 'Editor "' + editor.name + '" is being initialized!' );
|
||||
* }
|
||||
* } );
|
||||
*
|
||||
* @method init
|
||||
* @param {CKEDITOR.editor} editor The editor instance being initialized.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Announces the plugin as HiDPI-ready (optimized for high pixel density screens, e.g. *Retina*)
|
||||
* by providing high-resolution icons and images. HiDPI icons must be twice as big
|
||||
* (defaults are `16px x 16px`) and stored under `plugin_name/icons/hidpi/` directory.
|
||||
*
|
||||
* The common place for additional HiDPI images used by the plugin (**but not icons**)
|
||||
* is `plugin_name/images/hidpi/` directory.
|
||||
*
|
||||
* This property is optional and only makes sense if `32px x 32px` icons
|
||||
* and high-resolution images actually exist. If this flag is set `true`, the editor
|
||||
* will automatically detect the HiDPI environment and attempt to load the
|
||||
* high-resolution resources.
|
||||
*
|
||||
* @since 4.2
|
||||
* @property {Boolean} hidpi
|
||||
*/
|
||||
@@ -1,119 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileOverview Defines the {@link CKEDITOR.plugins} object, which is used to
|
||||
* manage plugins registration and loading.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Manages plugins registration and loading.
|
||||
*
|
||||
* @class
|
||||
* @extends CKEDITOR.resourceManager
|
||||
* @singleton
|
||||
*/
|
||||
CKEDITOR.plugins = new CKEDITOR.resourceManager( 'plugins/', 'plugin' );
|
||||
|
||||
// PACKAGER_RENAME( CKEDITOR.plugins )
|
||||
|
||||
CKEDITOR.plugins.load = CKEDITOR.tools.override( CKEDITOR.plugins.load, function( originalLoad ) {
|
||||
var initialized = {};
|
||||
|
||||
return function( name, callback, scope ) {
|
||||
var allPlugins = {};
|
||||
|
||||
var loadPlugins = function( names ) {
|
||||
originalLoad.call( this, names, function( plugins ) {
|
||||
CKEDITOR.tools.extend( allPlugins, plugins );
|
||||
|
||||
var requiredPlugins = [];
|
||||
for ( var pluginName in plugins ) {
|
||||
var plugin = plugins[ pluginName ],
|
||||
requires = plugin && plugin.requires;
|
||||
|
||||
if ( !initialized[ pluginName ] ) {
|
||||
// Register all icons eventually defined by this plugin.
|
||||
if ( plugin.icons ) {
|
||||
var icons = plugin.icons.split( ',' );
|
||||
for ( var ic = icons.length; ic--; ) {
|
||||
CKEDITOR.skin.addIcon( icons[ ic ],
|
||||
plugin.path +
|
||||
'icons/' +
|
||||
( CKEDITOR.env.hidpi && plugin.hidpi ? 'hidpi/' : '' ) +
|
||||
icons[ ic ] +
|
||||
'.png' );
|
||||
}
|
||||
}
|
||||
initialized[ pluginName ] = 1;
|
||||
}
|
||||
|
||||
if ( requires ) {
|
||||
// Trasnform it into an array, if it's not one.
|
||||
if ( requires.split )
|
||||
requires = requires.split( ',' );
|
||||
|
||||
for ( var i = 0; i < requires.length; i++ ) {
|
||||
if ( !allPlugins[ requires[ i ] ] )
|
||||
requiredPlugins.push( requires[ i ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( requiredPlugins.length )
|
||||
loadPlugins.call( this, requiredPlugins );
|
||||
else {
|
||||
// Call the "onLoad" function for all plugins.
|
||||
for ( pluginName in allPlugins ) {
|
||||
plugin = allPlugins[ pluginName ];
|
||||
if ( plugin.onLoad && !plugin.onLoad._called ) {
|
||||
// Make it possible to return false from plugin::onLoad to disable it.
|
||||
if ( plugin.onLoad() === false )
|
||||
delete allPlugins[ pluginName ];
|
||||
|
||||
plugin.onLoad._called = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Call the callback.
|
||||
if ( callback )
|
||||
callback.call( scope || window, allPlugins );
|
||||
}
|
||||
}, this );
|
||||
|
||||
};
|
||||
|
||||
loadPlugins.call( this, name );
|
||||
};
|
||||
} );
|
||||
|
||||
/**
|
||||
* Loads a specific language file, or auto detect it. A callback is
|
||||
* then called when the file gets loaded.
|
||||
*
|
||||
* CKEDITOR.plugins.setLang( 'myPlugin', 'en', {
|
||||
* title: 'My plugin',
|
||||
* selectOption: 'Please select an option'
|
||||
* } );
|
||||
*
|
||||
* @param {String} pluginName The name of the plugin to which the provided translation
|
||||
* should be attached.
|
||||
* @param {String} languageCode The code of the language translation provided.
|
||||
* @param {Object} languageEntries An object that contains pairs of label and
|
||||
* the respective translation.
|
||||
*/
|
||||
CKEDITOR.plugins.setLang = function( pluginName, languageCode, languageEntries ) {
|
||||
var plugin = this.get( pluginName ),
|
||||
pluginLangEntries = plugin.langEntries || ( plugin.langEntries = {} ),
|
||||
pluginLang = plugin.lang || ( plugin.lang = [] );
|
||||
|
||||
if ( pluginLang.split )
|
||||
pluginLang = pluginLang.split( ',' );
|
||||
|
||||
if ( CKEDITOR.tools.indexOf( pluginLang, languageCode ) == -1 )
|
||||
pluginLang.push( languageCode );
|
||||
|
||||
pluginLangEntries[ languageCode ] = languageEntries;
|
||||
};
|
||||
@@ -1,227 +0,0 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileOverview Defines the {@link CKEDITOR.resourceManager} class, which is
|
||||
* the base for resource managers, like plugins.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Base class for resource managers, like plugins. This class is not
|
||||
* intended to be used out of the CKEditor core code.
|
||||
*
|
||||
* @class
|
||||
* @constructor Creates a resourceManager class instance.
|
||||
* @param {String} basePath The path for the resources folder.
|
||||
* @param {String} fileName The name used for resource files.
|
||||
*/
|
||||
CKEDITOR.resourceManager = function( basePath, fileName ) {
|
||||
/**
|
||||
* The base directory containing all resources.
|
||||
*
|
||||
* @property {String}
|
||||
*/
|
||||
this.basePath = basePath;
|
||||
|
||||
/**
|
||||
* The name used for resource files.
|
||||
*
|
||||
* @property {String}
|
||||
*/
|
||||
this.fileName = fileName;
|
||||
|
||||
/**
|
||||
* Contains references to all resources that have already been registered
|
||||
* with {@link #add}.
|
||||
*/
|
||||
this.registered = {};
|
||||
|
||||
/**
|
||||
* Contains references to all resources that have already been loaded
|
||||
* with {@link #load}.
|
||||
*/
|
||||
this.loaded = {};
|
||||
|
||||
/**
|
||||
* Contains references to all resources that have already been registered
|
||||
* with {@link #addExternal}.
|
||||
*/
|
||||
this.externals = {};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
this._ = {
|
||||
// List of callbacks waiting for plugins to be loaded.
|
||||
waitingList: {}
|
||||
};
|
||||
};
|
||||
|
||||
CKEDITOR.resourceManager.prototype = {
|
||||
/**
|
||||
* Registers a resource.
|
||||
*
|
||||
* CKEDITOR.plugins.add( 'sample', { ... plugin definition ... } );
|
||||
*
|
||||
* @param {String} name The resource name.
|
||||
* @param {Object} [definition] The resource definition.
|
||||
* @see CKEDITOR.pluginDefinition
|
||||
*/
|
||||
add: function( name, definition ) {
|
||||
if ( this.registered[ name ] )
|
||||
throw '[CKEDITOR.resourceManager.add] The resource name "' + name + '" is already registered.';
|
||||
|
||||
var resource = this.registered[ name ] = definition || {};
|
||||
resource.name = name;
|
||||
resource.path = this.getPath( name );
|
||||
|
||||
CKEDITOR.fire( name + CKEDITOR.tools.capitalize( this.fileName ) + 'Ready', resource );
|
||||
|
||||
return this.get( name );
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the definition of a specific resource.
|
||||
*
|
||||
* var definition = CKEDITOR.plugins.get( 'sample' );
|
||||
*
|
||||
* @param {String} name The resource name.
|
||||
* @returns {Object} The registered object.
|
||||
*/
|
||||
get: function( name ) {
|
||||
return this.registered[ name ] || null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the folder path for a specific loaded resource.
|
||||
*
|
||||
* alert( CKEDITOR.plugins.getPath( 'sample' ) ); // '<editor path>/plugins/sample/'
|
||||
*
|
||||
* @param {String} name The resource name.
|
||||
* @returns {String}
|
||||
*/
|
||||
getPath: function( name ) {
|
||||
var external = this.externals[ name ];
|
||||
return CKEDITOR.getUrl( ( external && external.dir ) || this.basePath + name + '/' );
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the file path for a specific loaded resource.
|
||||
*
|
||||
* alert( CKEDITOR.plugins.getFilePath( 'sample' ) ); // '<editor path>/plugins/sample/plugin.js'
|
||||
*
|
||||
* @param {String} name The resource name.
|
||||
* @returns {String}
|
||||
*/
|
||||
getFilePath: function( name ) {
|
||||
var external = this.externals[ name ];
|
||||
return CKEDITOR.getUrl( this.getPath( name ) + ( external ? external.file : this.fileName + '.js' ) );
|
||||
},
|
||||
|
||||
/**
|
||||
* Registers one or more resources to be loaded from an external path
|
||||
* instead of the core base path.
|
||||
*
|
||||
* // Loads a plugin from '/myplugin/samples/plugin.js'.
|
||||
* CKEDITOR.plugins.addExternal( 'sample', '/myplugins/sample/' );
|
||||
*
|
||||
* // Loads a plugin from '/myplugin/samples/my_plugin.js'.
|
||||
* CKEDITOR.plugins.addExternal( 'sample', '/myplugins/sample/', 'my_plugin.js' );
|
||||
*
|
||||
* // Loads a plugin from '/myplugin/samples/my_plugin.js'.
|
||||
* CKEDITOR.plugins.addExternal( 'sample', '/myplugins/sample/my_plugin.js', '' );
|
||||
*
|
||||
* @param {String} names The resource names, separated by commas.
|
||||
* @param {String} path The path of the folder containing the resource.
|
||||
* @param {String} [fileName] The resource file name. If not provided, the
|
||||
* default name is used. If provided with a empty string, will implicitly indicates that `path` argument
|
||||
* is already the full path.
|
||||
*/
|
||||
addExternal: function( names, path, fileName ) {
|
||||
names = names.split( ',' );
|
||||
for ( var i = 0; i < names.length; i++ ) {
|
||||
var name = names[ i ];
|
||||
|
||||
// If "fileName" is not provided, we assume that it may be available
|
||||
// in "path". Try to extract it in this case.
|
||||
if ( !fileName ) {
|
||||
path = path.replace( /[^\/]+$/, function( match ) {
|
||||
fileName = match;
|
||||
return '';
|
||||
} );
|
||||
}
|
||||
|
||||
this.externals[ name ] = {
|
||||
dir: path,
|
||||
|
||||
// Use the default file name if there is no "fileName" and it
|
||||
// was not found in "path".
|
||||
file: fileName || ( this.fileName + '.js' )
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Loads one or more resources.
|
||||
*
|
||||
* CKEDITOR.plugins.load( 'myplugin', function( plugins ) {
|
||||
* alert( plugins[ 'myplugin' ] ); // object
|
||||
* } );
|
||||
*
|
||||
* @param {String/Array} name The name of the resource to load. It may be a
|
||||
* string with a single resource name, or an array with several names.
|
||||
* @param {Function} callback A function to be called when all resources
|
||||
* are loaded. The callback will receive an array containing all loaded names.
|
||||
* @param {Object} [scope] The scope object to be used for the callback call.
|
||||
*/
|
||||
load: function( names, callback, scope ) {
|
||||
// Ensure that we have an array of names.
|
||||
if ( !CKEDITOR.tools.isArray( names ) )
|
||||
names = names ? [ names ] : [];
|
||||
|
||||
var loaded = this.loaded,
|
||||
registered = this.registered,
|
||||
urls = [],
|
||||
urlsNames = {},
|
||||
resources = {};
|
||||
|
||||
// Loop through all names.
|
||||
for ( var i = 0; i < names.length; i++ ) {
|
||||
var name = names[ i ];
|
||||
|
||||
if ( !name )
|
||||
continue;
|
||||
|
||||
// If not available yet.
|
||||
if ( !loaded[ name ] && !registered[ name ] ) {
|
||||
var url = this.getFilePath( name );
|
||||
urls.push( url );
|
||||
if ( !( url in urlsNames ) )
|
||||
urlsNames[ url ] = [];
|
||||
urlsNames[ url ].push( name );
|
||||
} else
|
||||
resources[ name ] = this.get( name );
|
||||
}
|
||||
|
||||
CKEDITOR.scriptLoader.load( urls, function( completed, failed ) {
|
||||
if ( failed.length ) {
|
||||
throw '[CKEDITOR.resourceManager.load] Resource name "' + urlsNames[ failed[ 0 ] ].join( ',' )
|
||||
+ '" was not found at "' + failed[ 0 ] + '".';
|
||||
}
|
||||
|
||||
for ( var i = 0; i < completed.length; i++ ) {
|
||||
var nameList = urlsNames[ completed[ i ] ];
|
||||
for ( var j = 0; j < nameList.length; j++ ) {
|
||||
var name = nameList[ j ];
|
||||
resources[ name ] = this.get( name );
|
||||
|
||||
loaded[ name ] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
callback.call( scope, resources );
|
||||
}, this );
|
||||
}
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user