diff --git a/LICENSE.html b/LICENSE.html
index 3767273..f7ba067 100644
--- a/LICENSE.html
+++ b/LICENSE.html
@@ -7,7 +7,7 @@ Software License Agreement
==========================
CKEditor - The text editor for Internet - http://ckeditor.com
-Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
Licensed under the terms of any of the following licenses at your
choice:
@@ -1286,7 +1286,7 @@ EXHIBIT A -Mozilla Public License.
Licensed under the terms of any of the following licenses at your choice:
diff --git a/_samples/ajax.html b/_samples/ajax.html
index 32b1e8f..f9ff42d 100644
--- a/_samples/ajax.html
+++ b/_samples/ajax.html
@@ -1,16 +1,16 @@
@@ -25,56 +25,55 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
-
+ //]]>
+
+
+
+
+
+
diff --git a/_samples/sample.css b/_samples/sample.css
index ede21ca..34fecf9 100644
--- a/_samples/sample.css
+++ b/_samples/sample.css
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
diff --git a/_samples/sample.js b/_samples/sample.js
index acd809e..dda5209 100644
--- a/_samples/sample.js
+++ b/_samples/sample.js
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
@@ -7,13 +7,6 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
// It is just a helper file that displays a red message about browser compatibility
// at the top of the samples (if incompatible browser is detected).
-// Firebug has been presented some bugs with console. It must be "initialized"
-// before the page load to work.
-// FIXME: Remove the following in the future, if Firebug gets fixed.
-if ( typeof console != 'undefined' )
- console.log();
-
-
if ( window.CKEDITOR )
{
(function()
@@ -51,7 +44,8 @@ if ( window.CKEDITOR )
html += '
With non compatible browsers, you should still be able to see and edit the contents (HTML) in a plain text field.
';
- document.getElementById( 'alerts' ).innerHTML = html;
+ var alertsEl = document.getElementById( 'alerts' );
+ alertsEl && ( alertsEl.innerHTML = html );
};
var onload = function()
diff --git a/_samples/sample_posteddata.php b/_samples/sample_posteddata.php
index 12cbec3..c50001a 100644
--- a/_samples/sample_posteddata.php
+++ b/_samples/sample_posteddata.php
@@ -1,7 +1,7 @@
@@ -52,7 +52,7 @@ foreach ( $postArray as $sForm => $value )
CKEditor - The text editor for Internet - http://ckeditor.com
');},commit:function(){var l=this;l._.close();l.element.appendHtml(l._.pendingHtml.join(''));l._.pendingHtml=[];},toggle:function(l){var m=this.isMarked(l);if(m)this.unmark(l);else this.mark(l);return!m;},hideGroup:function(l){var m=this.element.getDocument().getById(this._.groups[l]),n=m&&m.getNext();if(m){m.setStyle('display','none');if(n&&n.getName()=='ul')n.setStyle('display','none');}},hideItem:function(l){this.element.getDocument().getById(this._.items[l]).setStyle('display','none');},showAll:function(){var l=this._.items,m=this._.groups,n=this.element.getDocument();for(var o in l)n.getById(l[o]).setStyle('display','');for(var p in m){var q=n.getById(m[p]),r=q.getNext();q.setStyle('display','');if(r&&r.getName()=='ul')r.setStyle('display','');}},mark:function(l){var m=this;if(!m.multiSelect)m.unmarkAll();m.element.getDocument().getById(m._.items[l]).addClass('cke_selected');},unmark:function(l){this.element.getDocument().getById(this._.items[l]).removeClass('cke_selected');},unmarkAll:function(){var l=this._.items,m=this.element.getDocument();for(var n in l)m.getById(l[n]).removeClass('cke_selected');},isMarked:function(l){return this.element.getDocument().getById(this._.items[l]).hasClass('cke_selected');},focus:function(l){this._.focusIndex=-1;if(l){var m=this.element.getDocument().getById(this._.items[l]).getFirst(),n=this.element.getElementsByTag('a'),o,p=-1;while(o=n.getItem(++p)){if(o.equals(m)){this._.focusIndex=p;break;}}setTimeout(function(){m.focus();},0);}}}});}});j.add('dialogui');(function(){var l=function(s){var v=this;v._||(v._={});v._['default']=v._.initValue=s['default']||'';var t=[v._];for(var u=1;u',t.label,'','
");
+ * ?>
+ * @endcode
*/
if ( !function_exists('version_compare') || version_compare( phpversion(), '5', '<' ) )
diff --git a/ckeditor_basic.js b/ckeditor_basic.js
index 94ed840..9e8362f 100644
--- a/ckeditor_basic.js
+++ b/ckeditor_basic.js
@@ -1,8 +1,8 @@
/*
-Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
-(function(){if(!window.CKEDITOR)window.CKEDITOR=(function(){var a={timestamp:'99GE',version:'3.0.1',revision:'4391',_:{},status:'unloaded',basePath:(function(){var d=window.CKEDITOR_BASEPATH||'';if(!d){var e=document.getElementsByTagName('script');for(var f=0;f=0?'&':'?')+('t=')+this.timestamp;return d;}},b=window.CKEDITOR_GETURL;if(b){var c=a.getUrl;a.getUrl=function(d){return b.call(a,d)||c.call(a,d);};}return a;})();var a=CKEDITOR;if(!a.event){a.event=function(){};a.event.implementOn=function(b,c){var d=a.event.prototype;for(var e in d)if(b[e]==undefined)b[e]=d[e];};a.event.prototype=(function(){var b=function(d){var e=d.getPrivate&&d.getPrivate()||d._||(d._={});return e.events||(e.events={});},c=function(d){this.name=d;this.listeners=[];};c.prototype={getListenerIndex:function(d){for(var e=0,f=this.listeners;e=0;n--)if(k[n].priority<=h){k.splice(n+1,0,m);return;}k.unshift(m);}},fire:(function(){var d=false,e=function(){d=true;},f=false,g=function(){f=true;};return function(h,i,j){var k=b(this)[h],l=d,m=f;d=f=false;if(k){var n=k.listeners;if(n.length){n=n.slice(0);for(var o=0;o=0)f.listeners.splice(g,1);}},hasListeners:function(d){var e=b(this)[d];return e&&e.listeners.length>0;}};})();}if(!a.editor){a.ELEMENT_MODE_NONE=0;a.ELEMENT_MODE_REPLACE=1;a.ELEMENT_MODE_APPENDTO=2;a.editor=function(b,c,d){var e=this;e._={instanceConfig:b,element:c};
-e.elementMode=d||0;a.event.call(e);e._init();};a.editor.replace=function(b,c){var d=b;if(typeof d!='object'){d=document.getElementById(b);if(!d){var e=0,f=document.getElementsByName(b);while((d=f[e++])&&(d.tagName.toLowerCase()!='textarea')){}}if(!d)throw '[CKEDITOR.editor.replace] The element with id or name "'+b+'" was not found.';}d.style.visibility='hidden';return new a.editor(c,d,1);};a.editor.appendTo=function(b,c){if(typeof b!='object'){b=document.getElementById(b);if(!b)throw '[CKEDITOR.editor.appendTo] The element with id "'+b+'" was not found.';}return new a.editor(c,b,2);};a.editor.prototype={_init:function(){var b=a.editor._pending||(a.editor._pending=[]);b.push(this);},fire:function(b,c){return a.event.prototype.fire.call(this,b,c,this);},fireOnce:function(b,c){return a.event.prototype.fireOnce.call(this,b,c,this);}};a.event.implementOn(a.editor.prototype,true);}if(!a.env)a.env=(function(){var b=navigator.userAgent.toLowerCase(),c=window.opera,d={ie:/*@cc_on!@*/false,opera:!!c&&c.version,webkit:b.indexOf(' applewebkit/')>-1,air:b.indexOf(' adobeair/')>-1,mac:b.indexOf('macintosh')>-1,quirks:document.compatMode=='BackCompat',isCustomDomain:function(){return this.ie&&document.domain!=window.location.hostname;}};d.gecko=navigator.product=='Gecko'&&!d.webkit&&!d.opera;var e=0;if(d.ie){e=parseFloat(b.match(/msie (\d+)/)[1]);d.ie8=!!document.documentMode;d.ie8Compat=document.documentMode==8;d.ie7Compat=e==7&&!document.documentMode||document.documentMode==7;d.ie6Compat=e<7||d.quirks;}if(d.gecko){var f=b.match(/rv:([\d\.]+)/);if(f){f=f[1].split('.');e=f[0]*10000+(f[1]||0)*(100)+ +(f[2]||0);}}if(d.opera)e=parseFloat(c.version());if(d.air)e=parseFloat(b.match(/ adobeair\/(\d+)/)[1]);if(d.webkit)e=parseFloat(b.match(/ applewebkit\/(\d+)/)[1]);d.version=e;d.isCompatible=d.ie&&e>=6||d.gecko&&e>=10801||d.opera&&e>=9.5||d.air&&e>=1||d.webkit&&e>=522||false;d.cssClass='cke_browser_'+(d.ie?'ie':d.gecko?'gecko':d.opera?'opera':d.air?'air':d.webkit?'webkit':'unknown');if(d.quirks)d.cssClass+=' cke_browser_quirks';if(d.ie){d.cssClass+=' cke_browser_ie'+(d.version<7?'6':d.version>=8?'8':'7');if(d.quirks)d.cssClass+=' cke_browser_iequirks';}if(d.gecko&&e<10900)d.cssClass+=' cke_browser_gecko18';return d;})();var b=a.env;var c=b.ie;if(a.status=='unloaded')(function(){a.event.implementOn(a);a.loadFullCore=function(){if(a.status!='basic_ready'){a.loadFullCore._load=true;return;}delete a.loadFullCore;var e=document.createElement('script');e.type='text/javascript';
+(function(){if(!window.CKEDITOR)window.CKEDITOR=(function(){var a={timestamp:'A06B',version:'3.1',revision:'4891',_:{},status:'unloaded',basePath:(function(){var d=window.CKEDITOR_BASEPATH||'';if(!d){var e=document.getElementsByTagName('script');for(var f=0;f=0?'&':'?')+'t='+this.timestamp;return d;}},b=window.CKEDITOR_GETURL;if(b){var c=a.getUrl;a.getUrl=function(d){return b.call(a,d)||c.call(a,d);};}return a;})();var a=CKEDITOR;if(!a.event){a.event=function(){};a.event.implementOn=function(b,c){var d=a.event.prototype;for(var e in d){if(b[e]==undefined)b[e]=d[e];}};a.event.prototype=(function(){var b=function(d){var e=d.getPrivate&&d.getPrivate()||d._||(d._={});return e.events||(e.events={});},c=function(d){this.name=d;this.listeners=[];};c.prototype={getListenerIndex:function(d){for(var e=0,f=this.listeners;e=0;n--){if(k[n].priority<=h){k.splice(n+1,0,m);return;}}k.unshift(m);}},fire:(function(){var d=false,e=function(){d=true;},f=false,g=function(){f=true;};return function(h,i,j){var k=b(this)[h],l=d,m=f;d=f=false;if(k){var n=k.listeners;if(n.length){n=n.slice(0);for(var o=0;o=0)f.listeners.splice(g,1);}},hasListeners:function(d){var e=b(this)[d];return e&&e.listeners.length>0;}};})();}if(!a.editor){a.ELEMENT_MODE_NONE=0;a.ELEMENT_MODE_REPLACE=1;a.ELEMENT_MODE_APPENDTO=2;a.editor=function(b,c,d){var e=this;e._={instanceConfig:b,element:c};
+e.elementMode=d||0;a.event.call(e);e._init();};a.editor.replace=function(b,c){var d=b;if(typeof d!='object'){d=document.getElementById(b);if(!d){var e=0,f=document.getElementsByName(b);while((d=f[e++])&&d.tagName.toLowerCase()!='textarea'){}}if(!d)throw '[CKEDITOR.editor.replace] The element with id or name "'+b+'" was not found.';}d.style.visibility='hidden';return new a.editor(c,d,1);};a.editor.appendTo=function(b,c){var d=b;if(typeof d!='object'){d=document.getElementById(b);if(!d)throw '[CKEDITOR.editor.appendTo] The element with id "'+b+'" was not found.';}return new a.editor(c,d,2);};a.editor.prototype={_init:function(){var b=a.editor._pending||(a.editor._pending=[]);b.push(this);},fire:function(b,c){return a.event.prototype.fire.call(this,b,c,this);},fireOnce:function(b,c){return a.event.prototype.fireOnce.call(this,b,c,this);}};a.event.implementOn(a.editor.prototype,true);}if(!a.env)a.env=(function(){var b=navigator.userAgent.toLowerCase(),c=window.opera,d={ie:/*@cc_on!@*/false,opera:!!c&&c.version,webkit:b.indexOf(' applewebkit/')>-1,air:b.indexOf(' adobeair/')>-1,mac:b.indexOf('macintosh')>-1,quirks:document.compatMode=='BackCompat',isCustomDomain:function(){return this.ie&&document.domain!=window.location.hostname;}};d.gecko=navigator.product=='Gecko'&&!d.webkit&&!d.opera;var e=0;if(d.ie){e=parseFloat(b.match(/msie (\d+)/)[1]);d.ie8=!!document.documentMode;d.ie8Compat=document.documentMode==8;d.ie7Compat=e==7&&!document.documentMode||document.documentMode==7;d.ie6Compat=e<7||d.quirks;}if(d.gecko){var f=b.match(/rv:([\d\.]+)/);if(f){f=f[1].split('.');e=f[0]*10000+(f[1]||0)*100+ +(f[2]||0);}}if(d.opera)e=parseFloat(c.version());if(d.air)e=parseFloat(b.match(/ adobeair\/(\d+)/)[1]);if(d.webkit)e=parseFloat(b.match(/ applewebkit\/(\d+)/)[1]);d.version=e;d.isCompatible=d.ie&&e>=6||d.gecko&&e>=10801||d.opera&&e>=9.5||d.air&&e>=1||d.webkit&&e>=522||false;d.cssClass='cke_browser_'+(d.ie?'ie':d.gecko?'gecko':d.opera?'opera':d.air?'air':d.webkit?'webkit':'unknown');if(d.quirks)d.cssClass+=' cke_browser_quirks';if(d.ie){d.cssClass+=' cke_browser_ie'+(d.version<7?'6':d.version>=8?'8':'7');if(d.quirks)d.cssClass+=' cke_browser_iequirks';}if(d.gecko&&e<10900)d.cssClass+=' cke_browser_gecko18';return d;})();var b=a.env;var c=b.ie;if(a.status=='unloaded')(function(){a.event.implementOn(a);a.loadFullCore=function(){if(a.status!='basic_ready'){a.loadFullCore._load=true;return;}delete a.loadFullCore;var e=document.createElement('script');e.type='text/javascript';
e.src=a.basePath+'ckeditor.js';document.getElementsByTagName('head')[0].appendChild(e);};a.loadFullCoreTimeout=0;a.replaceClass='ckeditor';a.replaceByClassEnabled=true;var d=function(e,f,g){if(b.isCompatible){if(a.loadFullCore)a.loadFullCore();var h=g(e,f);a.add(h);return h;}return null;};a.replace=function(e,f){return d(e,f,a.editor.replace);};a.appendTo=function(e,f){return d(e,f,a.editor.appendTo);};a.add=function(e){var f=this._.pending||(this._.pending=[]);f.push(e);};a.replaceAll=function(){var e=document.getElementsByTagName('textarea');for(var f=0;f=0?'&':'?')+('t=')+this.timestamp;return d;}},b=window.CKEDITOR_GETURL;if(b){var c=a.getUrl;a.getUrl=function(d){return b.call(a,d)||c.call(a,d);};}return a;})();
+if(!window.CKEDITOR)window.CKEDITOR=(function(){var a={timestamp:'',version:'3.1',revision:'4891',_:{},status:'unloaded',basePath:(function(){var d=window.CKEDITOR_BASEPATH||'';if(!d){var e=document.getElementsByTagName('script');for(var f=0;f=0?'&':'?')+('t=')+this.timestamp;return d;}},b=window.CKEDITOR_GETURL;if(b){var c=a.getUrl;a.getUrl=function(d){return b.call(a,d)||c.call(a,d);};}return a;})();
/*jsl:end*/
// Uncomment the following line to have a new timestamp generated for each
diff --git a/ckeditor_php4.php b/ckeditor_php4.php
index 0663d7c..385b839 100644
--- a/ckeditor_php4.php
+++ b/ckeditor_php4.php
@@ -1,262 +1,593 @@
editor("editor1", "
Initial value.
");
+ * @endcode
*/
-function FCKeditor_IsCompatibleBrowser()
+class CKEditor
{
- if ( isset( $_SERVER ) ) {
- $sAgent = $_SERVER['HTTP_USER_AGENT'] ;
- }
- else {
- global $HTTP_SERVER_VARS ;
- if ( isset( $HTTP_SERVER_VARS ) ) {
- $sAgent = $HTTP_SERVER_VARS['HTTP_USER_AGENT'] ;
- }
- else {
- global $HTTP_USER_AGENT ;
- $sAgent = $HTTP_USER_AGENT ;
- }
- }
-
- if ( strpos($sAgent, 'MSIE') !== false && strpos($sAgent, 'mac') === false && strpos($sAgent, 'Opera') === false )
- {
- $iVersion = (float)substr($sAgent, strpos($sAgent, 'MSIE') + 5, 3) ;
- return ($iVersion >= 5.5) ;
- }
- else if ( strpos($sAgent, 'Gecko/') !== false )
- {
- $iVersion = (int)substr($sAgent, strpos($sAgent, 'Gecko/') + 6, 8) ;
- return ($iVersion >= 20030210) ;
- }
- else if ( strpos($sAgent, 'Opera/') !== false )
- {
- $fVersion = (float)substr($sAgent, strpos($sAgent, 'Opera/') + 6, 4) ;
- return ($fVersion >= 9.5) ;
- }
- else if ( preg_match( "|AppleWebKit/(\d+)|i", $sAgent, $matches ) )
- {
- $iVersion = $matches[1] ;
- return ( $matches[1] >= 522 ) ;
- }
- else
- return false ;
-}
+ /**
+ * The version of %CKEditor.
+ * \private
+ */
+ var $version = '3.1';
+ /**
+ * A constant string unique for each release of %CKEditor.
+ * \private
+ */
+ var $_timestamp = 'A06B';
-class FCKeditor
-{
/**
- * Name of the FCKeditor instance.
+ * URL to the %CKEditor installation directory (absolute or relative to document root).
+ * If not set, CKEditor will try to guess it's path.
*
- * @access protected
- * @var string
+ * Example usage:
+ * @code
+ * $CKEditor->basePath = '/ckeditor/';
+ * @endcode
*/
- var $InstanceName ;
+ var $basePath;
/**
- * Path to FCKeditor relative to the document root.
+ * An array that holds the global %CKEditor configuration.
+ * For the list of available options, see http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.config.html
*
- * @var string
+ * Example usage:
+ * @code
+ * $CKEditor->config['height'] = 400;
+ * // Use @@ at the beggining of a string to ouput it without surrounding quotes.
+ * $CKEditor->config['width'] = '@@screen.width * 0.8';
+ * @endcode
*/
- var $BasePath ;
+ var $config = array();
/**
- * Width of the FCKeditor.
- * Examples: 100%, 600
+ * A boolean variable indicating whether CKEditor has been initialized.
+ * Set it to true only if you have already included
+ * <script> tag loading ckeditor.js in your website.
+ */
+ var $initialized = false;
+ /**
+ * Boolean variable indicating whether created code should be printed out or returned by a function.
*
- * @var mixed
+ * Example 1: get the code creating %CKEditor instance and print it on a page with the "echo" function.
+ * @code
+ * $CKEditor = new CKEditor();
+ * $CKEditor->returnOutput = true;
+ * $code = $CKEditor->editor("editor1", "
Initial value.
");
+ * echo "
Editor 1:
";
+ * echo $code;
+ * @endcode
*/
- var $Width ;
+ var $returnOutput = false;
/**
- * Height of the FCKeditor.
- * Examples: 400, 50%
+ * An array with textarea attributes.
*
- * @var mixed
+ * When %CKEditor is created with the editor() method, a HTML <textarea> element is created,
+ * it will be displayed to anyone with JavaScript disabled or with incompatible browser.
+ */
+ var $textareaAttributes = array( "rows" => 8, "cols" => 60 );
+ /**
+ * A string indicating the creation date of %CKEditor.
+ * Do not change it unless you want to force browsers to not use previously cached version of %CKEditor.
+ */
+ var $timestamp = "A06B";
+ /**
+ * An array that holds event listeners.
+ * \private
*/
- var $Height ;
+ var $_events = array();
/**
- * Name of the toolbar to load.
+ * An array that holds global event listeners.
+ * \private
+ */
+ var $_globalEvents = array();
+
+ /**
+ * Main Constructor.
*
- * @var string
+ * @param $basePath (string) URL to the %CKEditor installation directory (optional).
*/
- var $ToolbarSet ;
+ function CKEditor($basePath = null) {
+ if (!empty($basePath)) {
+ $this->basePath = $basePath;
+ }
+ }
+
/**
- * Initial value.
+ * Creates a %CKEditor instance.
+ * In incompatible browsers %CKEditor will downgrade to plain HTML <textarea> element.
+ *
+ * @param $name (string) Name of the %CKEditor instance (this will be also the "name" attribute of textarea element).
+ * @param $value (string) Initial value (optional).
+ * @param $config (array) The specific configurations to apply to this editor instance (optional).
+ * @param $events (array) Event listeners for this editor instance (optional).
*
- * @var string
+ * Example usage:
+ * @code
+ * $CKEditor = new CKEditor();
+ * $CKEditor->editor("field1", "
", $config, $events);
+ * @endcode
*/
- var $Value ;
+ function editor($name, $value = "", $config = array(), $events = array())
+ {
+ $attr = "";
+ foreach ($this->textareaAttributes as $key => $val) {
+ $attr.= " " . $key . '="' . str_replace('"', '"', $val) . '"';
+ }
+ $out = "\n";
+ if (!$this->initialized) {
+ $out .= $this->init();
+ }
+
+ $_config = $this->configSettings($config, $events);
+
+ $js = $this->returnGlobalEvents();
+ if (!empty($_config))
+ $js .= "CKEDITOR.replace('".$name."', ".$this->jsEncode($_config).");";
+ else
+ $js .= "CKEDITOR.replace('".$name."');";
+
+ $out .= $this->script($js);
+
+ if (!$this->returnOutput) {
+ print $out;
+ $out = "";
+ }
+
+ return $out;
+ }
+
/**
- * This is where additional configuration can be passed.
- * Example:
- * $oFCKeditor->Config['EnterMode'] = 'br';
+ * Replaces a <textarea> with a %CKEditor instance.
*
- * @var array
+ * @param $id (string) The id or name of textarea element.
+ * @param $config (array) The specific configurations to apply to this editor instance (optional).
+ * @param $events (array) Event listeners for this editor instance (optional).
+ *
+ * Example 1: adding %CKEditor to <textarea name="article"></textarea> element:
+ * @code
+ * $CKEditor = new CKEditor();
+ * $CKEditor->replace("article");
+ * @endcode
*/
- var $Config ;
+ function replace($id, $config = array(), $events = array())
+ {
+ $out = "";
+ if (!$this->initialized) {
+ $out .= $this->init();
+ }
+
+ $_config = $this->configSettings($config, $events);
+
+ $js = $this->returnGlobalEvents();
+ if (!empty($_config)) {
+ $js .= "CKEDITOR.replace('".$id."', ".$this->jsEncode($_config).");";
+ }
+ else {
+ $js .= "CKEDITOR.replace('".$id."');";
+ }
+ $out .= $this->script($js);
+
+ if (!$this->returnOutput) {
+ print $out;
+ $out = "";
+ }
+
+ return $out;
+ }
/**
- * Main Constructor.
- * Refer to the _samples/php directory for examples.
+ * Replace all <textarea> elements available in the document with editor instances.
+ *
+ * @param $className (string) If set, replace all textareas with class className in the page.
*
- * @param string $instanceName
+ * Example 1: replace all <textarea> elements in the page.
+ * @code
+ * $CKEditor = new CKEditor();
+ * $CKEditor->replaceAll();
+ * @endcode
+ *
+ * Example 2: replace all <textarea class="myClassName"> elements in the page.
+ * @code
+ * $CKEditor = new CKEditor();
+ * $CKEditor->replaceAll( 'myClassName' );
+ * @endcode
*/
- function FCKeditor( $instanceName )
+ function replaceAll($className = null)
{
- $this->InstanceName = $instanceName ;
- $this->BasePath = '/fckeditor/' ;
- $this->Width = '100%' ;
- $this->Height = '200' ;
- $this->ToolbarSet = 'Default' ;
- $this->Value = '' ;
-
- $this->Config = array() ;
+ $out = "";
+ if (!$this->initialized) {
+ $out .= $this->init();
+ }
+
+ $_config = $this->configSettings();
+
+ $js = $this->returnGlobalEvents();
+ if (empty($_config)) {
+ if (empty($className)) {
+ $js .= "CKEDITOR.replaceAll();";
+ }
+ else {
+ $js .= "CKEDITOR.replaceAll('".$className."');";
+ }
+ }
+ else {
+ $classDetection = "";
+ $js .= "CKEDITOR.replaceAll( function(textarea, config) {\n";
+ if (!empty($className)) {
+ $js .= " var classRegex = new RegExp('(?:^| )' + '". $className ."' + '(?:$| )');\n";
+ $js .= " if (!classRegex.test(textarea.className))\n";
+ $js .= " return false;\n";
+ }
+ $js .= " CKEDITOR.tools.extend(config, ". $this->jsEncode($_config) .", true);";
+ $js .= "} );";
+
+ }
+
+ $out .= $this->script($js);
+
+ if (!$this->returnOutput) {
+ print $out;
+ $out = "";
+ }
+
+ return $out;
}
/**
- * Display FCKeditor.
+ * Adds event listener.
+ * Events are fired by %CKEditor in various situations.
*
+ * @param $event (string) Event name.
+ * @param $javascriptCode (string) Javascript anonymous function or function name.
+ *
+ * Example usage:
+ * @code
+ * $CKEditor->addEventHandler('instanceReady', 'function (ev) {
+ * alert("Loaded: " + ev.editor.name);
+ * }');
+ * @endcode
*/
- function Create()
+ function addEventHandler($event, $javascriptCode)
{
- echo $this->CreateHtml() ;
+ if (!isset($this->_events[$event])) {
+ $this->_events[$event] = array();
+ }
+ // Avoid duplicates.
+ if (!in_array($javascriptCode, $this->_events[$event])) {
+ $this->_events[$event][] = $javascriptCode;
+ }
}
/**
- * Return the HTML code required to run FCKeditor.
+ * Clear registered event handlers.
+ * Note: this function will have no effect on already created editor instances.
*
- * @return string
+ * @param $event (string) Event name, if not set all event handlers will be removed (optional).
*/
- function CreateHtml()
+ function clearEventHandlers($event = null)
{
- $HtmlValue = htmlspecialchars( $this->Value ) ;
+ if (!empty($event)) {
+ $this->_events[$event] = array();
+ }
+ else {
+ $this->_events = array();
+ }
+ }
- $Html = '' ;
+ /**
+ * Adds global event listener.
+ *
+ * @param $event (string) Event name.
+ * @param $javascriptCode (string) Javascript anonymous function or function name.
+ *
+ * Example usage:
+ * @code
+ * $CKEditor->addGlobalEventHandler('dialogDefinition', 'function (ev) {
+ * alert("Loading dialog: " + ev.data.name);
+ * }');
+ * @endcode
+ */
+ function addGlobalEventHandler($event, $javascriptCode)
+ {
+ if (!isset($this->_globalEvents[$event])) {
+ $this->_globalEvents[$event] = array();
+ }
+ // Avoid duplicates.
+ if (!in_array($javascriptCode, $this->_globalEvents[$event])) {
+ $this->_globalEvents[$event][] = $javascriptCode;
+ }
+ }
- if ( !isset( $_GET ) ) {
- global $HTTP_GET_VARS ;
- $_GET = $HTTP_GET_VARS ;
+ /**
+ * Clear registered global event handlers.
+ * Note: this function will have no effect if the event handler has been already printed/returned.
+ *
+ * @param $event (string) Event name, if not set all event handlers will be removed (optional).
+ */
+ function clearGlobalEventHandlers($event = null)
+ {
+ if (!empty($event)) {
+ $this->_globalEvents[$event] = array();
+ }
+ else {
+ $this->_globalEvents = array();
}
+ }
- if ( $this->IsCompatible() )
- {
- if ( isset( $_GET['fcksource'] ) && $_GET['fcksource'] == "true" )
- $File = 'fckeditor.original.html' ;
- else
- $File = 'fckeditor.html' ;
+ /**
+ * Prints javascript code.
+ * \private
+ *
+ * @param string $js
+ */
+ function script($js)
+ {
+ $out = "\n";
- $Link = "{$this->BasePath}editor/{$File}?InstanceName={$this->InstanceName}" ;
+ return $out;
+ }
- if ( $this->ToolbarSet != '' )
- $Link .= "&Toolbar={$this->ToolbarSet}" ;
+ /**
+ * Returns the configuration array (global and instance specific settings are merged into one array).
+ * \private
+ *
+ * @param $config (array) The specific configurations to apply to editor instance.
+ * @param $events (array) Event listeners for editor instance.
+ */
+ function configSettings($config = array(), $events = array())
+ {
+ $_config = $this->config;
+ $_events = $this->_events;
- // Render the linked hidden field.
- $Html .= "InstanceName}\" name=\"{$this->InstanceName}\" value=\"{$HtmlValue}\" style=\"display:none\" />" ;
+ if (is_array($config) && !empty($config)) {
+ $_config = array_merge($_config, $config);
+ }
- // Render the configurations hidden field.
- $Html .= "InstanceName}___Config\" value=\"" . $this->GetConfigFieldString() . "\" style=\"display:none\" />" ;
+ if (is_array($events) && !empty($events)) {
+ foreach ($events as $eventName => $code) {
+ if (!isset($_events[$eventName])) {
+ $_events[$eventName] = array();
+ }
+ if (!in_array($code, $_events[$eventName])) {
+ $_events[$eventName][] = $code;
+ }
+ }
+ }
- // Render the editor IFRAME.
- $Html .= "InstanceName}___Frame\" src=\"{$Link}\" width=\"{$this->Width}\" height=\"{$this->Height}\" frameborder=\"0\" scrolling=\"no\">" ;
+ if (!empty($_events)) {
+ foreach($_events as $eventName => $handlers) {
+ if (empty($handlers)) {
+ continue;
+ }
+ else if (count($handlers) == 1) {
+ $_config['on'][$eventName] = '@@'.$handlers[0];
+ }
+ else {
+ $_config['on'][$eventName] = '@@function (ev){';
+ foreach ($handlers as $handler => $code) {
+ $_config['on'][$eventName] .= '('.$code.')(ev);';
+ }
+ $_config['on'][$eventName] .= '}';
+ }
+ }
}
- else
- {
- if ( strpos( $this->Width, '%' ) === false )
- $WidthCSS = $this->Width . 'px' ;
- else
- $WidthCSS = $this->Width ;
- if ( strpos( $this->Height, '%' ) === false )
- $HeightCSS = $this->Height . 'px' ;
- else
- $HeightCSS = $this->Height ;
+ return $_config;
+ }
+
+ /**
+ * Return global event handlers.
+ * \private
+ */
+ function returnGlobalEvents()
+ {
+ static $returnedEvents;
+ $out = "";
- $Html .= "" ;
+ if (!isset($returnedEvents)) {
+ $returnedEvents = array();
}
- return $Html ;
+ if (!empty($this->_globalEvents)) {
+ foreach ($this->_globalEvents as $eventName => $handlers) {
+ foreach ($handlers as $handler => $code) {
+ if (!isset($returnedEvents[$eventName])) {
+ $returnedEvents[$eventName] = array();
+ }
+ // Return only new events
+ if (!in_array($code, $returnedEvents[$eventName])) {
+ $out .= ($code ? "\n" : "") . "CKEDITOR.on('". $eventName ."', $code);";
+ $returnedEvents[$eventName][] = $code;
+ }
+ }
+ }
+ }
+
+ return $out;
}
/**
- * Returns true if browser is compatible with FCKeditor.
- *
- * @return boolean
+ * Initializes CKEditor (executed only once).
+ * \private
*/
- function IsCompatible()
+ function init()
{
- return FCKeditor_IsCompatibleBrowser() ;
+ static $initComplete;
+ $out = "";
+
+ if (!empty($initComplete)) {
+ return "";
+ }
+
+ if ($this->initialized) {
+ $initComplete = true;
+ return "";
+ }
+
+ $args = "";
+ $ckeditorPath = $this->ckeditorPath();
+
+ if (!empty($this->timestamp) && $this->timestamp != "%"."TIMESTAMP%") {
+ $args = '?t=' . $this->timestamp;
+ }
+
+ // Skip relative paths...
+ if (strpos($ckeditorPath, '..') !== 0) {
+ $out .= $this->script("window.CKEDITOR_BASEPATH='". $ckeditorPath ."';");
+ }
+
+ $out .= "\n";
+
+ $extraCode = "";
+ if ($this->timestamp != $this->_timestamp) {
+ $extraCode .= ($extraCode ? "\n" : "") . "CKEDITOR.timestamp = '". $this->timestamp ."';";
+ }
+ if ($extraCode) {
+ $out .= $this->script($extraCode);
+ }
+
+ $initComplete = $this->initialized = true;
+
+ return $out;
}
/**
- * Get settings from Config array as a single string.
- *
- * @access protected
- * @return string
+ * Return path to ckeditor.js.
+ * \private
*/
- function GetConfigFieldString()
+ function ckeditorPath()
{
- $sParams = '' ;
- $bFirst = true ;
+ if (!empty($this->basePath)) {
+ return $this->basePath;
+ }
- foreach ( $this->Config as $sKey => $sValue )
- {
- if ( $bFirst == false )
- $sParams .= '&' ;
- else
- $bFirst = false ;
+ /**
+ * The absolute pathname of the currently executing script.
+ * Note: If a script is executed with the CLI, as a relative path, such as file.php or ../file.php,
+ * $_SERVER['SCRIPT_FILENAME'] will contain the relative path specified by the user.
+ */
+ if (isset($_SERVER['SCRIPT_FILENAME'])) {
+ $realPath = dirname($_SERVER['SCRIPT_FILENAME']);
+ }
+ else {
+ /**
+ * realpath — Returns canonicalized absolute pathname
+ */
+ $realPath = realpath( './' ) ;
+ }
+
+ /**
+ * The filename of the currently executing script, relative to the document root.
+ * For instance, $_SERVER['PHP_SELF'] in a script at the address http://example.com/test.php/foo.bar
+ * would be /test.php/foo.bar.
+ */
+ $selfPath = dirname($_SERVER['PHP_SELF']);
+ $file = str_replace("\\", "/", __FILE__);
- if ( $sValue === true )
- $sParams .= $this->EncodeConfig( $sKey ) . '=true' ;
- else if ( $sValue === false )
- $sParams .= $this->EncodeConfig( $sKey ) . '=false' ;
- else
- $sParams .= $this->EncodeConfig( $sKey ) . '=' . $this->EncodeConfig( $sValue ) ;
+ if (!$selfPath || !$realPath || !$file) {
+ return "/ckeditor/";
}
- return $sParams ;
+ $documentRoot = substr($realPath, 0, strlen($realPath) - strlen($selfPath));
+ $fileUrl = substr($file, strlen($documentRoot));
+ $ckeditorUrl = str_replace("ckeditor_php5.php", "", $fileUrl);
+
+ return $ckeditorUrl;
}
/**
- * Encode characters that may break the configuration string
- * generated by GetConfigFieldString().
+ * This little function provides a basic JSON support.
+ * http://php.net/manual/en/function.json-encode.php
+ * \private
*
- * @access protected
- * @param string $valueToEncode
+ * @param mixed $val
* @return string
*/
- function EncodeConfig( $valueToEncode )
+ function jsEncode($val)
{
- $chars = array(
- '&' => '%26',
- '=' => '%3D',
- '"' => '%22' ) ;
+ if (is_null($val)) {
+ return 'null';
+ }
+ if ($val === false) {
+ return 'false';
+ }
+ if ($val === true) {
+ return 'true';
+ }
+ if (is_scalar($val))
+ {
+ if (is_float($val))
+ {
+ // Always use "." for floats.
+ $val = str_replace(",", ".", strval($val));
+ }
- return strtr( $valueToEncode, $chars ) ;
+ // Use @@ to not use quotes when outputting string value
+ if (strpos($val, '@@') === 0) {
+ return substr($val, 2);
+ }
+ else {
+ // All scalars are converted to strings to avoid indeterminism.
+ // PHP's "1" and 1 are equal for all PHP operators, but
+ // JS's "1" and 1 are not. So if we pass "1" or 1 from the PHP backend,
+ // we should get the same result in the JS frontend (string).
+ // Character replacements for JSON.
+ static $jsonReplaces = array(array("\\", "/", "\n", "\t", "\r", "\b", "\f", '"'),
+ array('\\\\', '\\/', '\\n', '\\t', '\\r', '\\b', '\\f', '\"'));
+
+ $val = str_replace($jsonReplaces[0], $jsonReplaces[1], $val);
+
+ return '"' . $val . '"';
+ }
+ }
+ $isList = true;
+ for ($i = 0, reset($val); $i < count($val); $i++, next($val))
+ {
+ if (key($val) !== $i)
+ {
+ $isList = false;
+ break;
+ }
+ }
+ $result = array();
+ if ($isList)
+ {
+ foreach ($val as $v) $result[] = $this->jsEncode($v);
+ return '[ ' . join(', ', $result) . ' ]';
+ }
+ else
+ {
+ foreach ($val as $k => $v) $result[] = $this->jsEncode($k).': '.$this->jsEncode($v);
+ return '{ ' . join(', ', $result) . ' }';
+ }
}
}
diff --git a/ckeditor_php5.php b/ckeditor_php5.php
index 86375f5..bf408da 100644
--- a/ckeditor_php5.php
+++ b/ckeditor_php5.php
@@ -1,257 +1,583 @@
editor("editor1", "
Initial value.
");
+ * @endcode
*/
-function FCKeditor_IsCompatibleBrowser()
+class CKEditor
{
- if ( isset( $_SERVER ) ) {
- $sAgent = $_SERVER['HTTP_USER_AGENT'] ;
- }
- else {
- global $HTTP_SERVER_VARS ;
- if ( isset( $HTTP_SERVER_VARS ) ) {
- $sAgent = $HTTP_SERVER_VARS['HTTP_USER_AGENT'] ;
- }
- else {
- global $HTTP_USER_AGENT ;
- $sAgent = $HTTP_USER_AGENT ;
- }
- }
-
- if ( strpos($sAgent, 'MSIE') !== false && strpos($sAgent, 'mac') === false && strpos($sAgent, 'Opera') === false )
- {
- $iVersion = (float)substr($sAgent, strpos($sAgent, 'MSIE') + 5, 3) ;
- return ($iVersion >= 5.5) ;
- }
- else if ( strpos($sAgent, 'Gecko/') !== false )
- {
- $iVersion = (int)substr($sAgent, strpos($sAgent, 'Gecko/') + 6, 8) ;
- return ($iVersion >= 20030210) ;
- }
- else if ( strpos($sAgent, 'Opera/') !== false )
- {
- $fVersion = (float)substr($sAgent, strpos($sAgent, 'Opera/') + 6, 4) ;
- return ($fVersion >= 9.5) ;
- }
- else if ( preg_match( "|AppleWebKit/(\d+)|i", $sAgent, $matches ) )
- {
- $iVersion = $matches[1] ;
- return ( $matches[1] >= 522 ) ;
- }
- else
- return false ;
-}
+ /**
+ * The version of %CKEditor.
+ */
+ const version = '3.1';
+ /**
+ * A constant string unique for each release of %CKEditor.
+ */
+ const timestamp = 'A06B';
-class FCKeditor
-{
/**
- * Name of the FCKeditor instance.
+ * URL to the %CKEditor installation directory (absolute or relative to document root).
+ * If not set, CKEditor will try to guess it's path.
*
- * @access protected
- * @var string
+ * Example usage:
+ * @code
+ * $CKEditor->basePath = '/ckeditor/';
+ * @endcode
*/
- public $InstanceName ;
+ public $basePath;
/**
- * Path to FCKeditor relative to the document root.
+ * An array that holds the global %CKEditor configuration.
+ * For the list of available options, see http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.config.html
*
- * @var string
+ * Example usage:
+ * @code
+ * $CKEditor->config['height'] = 400;
+ * // Use @@ at the beggining of a string to ouput it without surrounding quotes.
+ * $CKEditor->config['width'] = '@@screen.width * 0.8';
+ * @endcode
*/
- public $BasePath ;
+ public $config = array();
/**
- * Width of the FCKeditor.
- * Examples: 100%, 600
- *
- * @var mixed
+ * A boolean variable indicating whether CKEditor has been initialized.
+ * Set it to true only if you have already included
+ * <script> tag loading ckeditor.js in your website.
*/
- public $Width ;
+ public $initialized = false;
/**
- * Height of the FCKeditor.
- * Examples: 400, 50%
+ * Boolean variable indicating whether created code should be printed out or returned by a function.
*
- * @var mixed
+ * Example 1: get the code creating %CKEditor instance and print it on a page with the "echo" function.
+ * @code
+ * $CKEditor = new CKEditor();
+ * $CKEditor->returnOutput = true;
+ * $code = $CKEditor->editor("editor1", "
Initial value.
");
+ * echo "
Editor 1:
";
+ * echo $code;
+ * @endcode
*/
- public $Height ;
+ public $returnOutput = false;
/**
- * Name of the toolbar to load.
+ * An array with textarea attributes.
*
- * @var string
+ * When %CKEditor is created with the editor() method, a HTML <textarea> element is created,
+ * it will be displayed to anyone with JavaScript disabled or with incompatible browser.
*/
- public $ToolbarSet ;
+ public $textareaAttributes = array( "rows" => 8, "cols" => 60 );
/**
- * Initial value.
- *
- * @var string
+ * A string indicating the creation date of %CKEditor.
+ * Do not change it unless you want to force browsers to not use previously cached version of %CKEditor.
*/
- public $Value ;
+ public $timestamp = "A06B";
/**
- * This is where additional configuration can be passed.
- * Example:
- * $oFCKeditor->Config['EnterMode'] = 'br';
- *
- * @var array
+ * An array that holds event listeners.
+ */
+ private $events = array();
+ /**
+ * An array that holds global event listeners.
*/
- public $Config ;
+ private $globalEvents = array();
/**
* Main Constructor.
- * Refer to the _samples/php directory for examples.
*
- * @param string $instanceName
+ * @param $basePath (string) URL to the %CKEditor installation directory (optional).
*/
- public function __construct( $instanceName )
- {
- $this->InstanceName = $instanceName ;
- $this->BasePath = '/fckeditor/' ;
- $this->Width = '100%' ;
- $this->Height = '200' ;
- $this->ToolbarSet = 'Default' ;
- $this->Value = '' ;
-
- $this->Config = array() ;
+ function __construct($basePath = null) {
+ if (!empty($basePath)) {
+ $this->basePath = $basePath;
+ }
}
/**
- * Display FCKeditor.
+ * Creates a %CKEditor instance.
+ * In incompatible browsers %CKEditor will downgrade to plain HTML <textarea> element.
+ *
+ * @param $name (string) Name of the %CKEditor instance (this will be also the "name" attribute of textarea element).
+ * @param $value (string) Initial value (optional).
+ * @param $config (array) The specific configurations to apply to this editor instance (optional).
+ * @param $events (array) Event listeners for this editor instance (optional).
*
+ * Example usage:
+ * @code
+ * $CKEditor = new CKEditor();
+ * $CKEditor->editor("field1", "
Brug i stedet tastaturet til at klippe teksten (Ctrl+X).',copyError:'Din browsers sikkerhedsindstillinger tillader ikke editoren at få automatisk adgang til udklipsholderen.
Brug i stedet tastaturet til at kopiere teksten (Ctrl+C).',pasteMsg:'Indsæt i feltet herunder (Ctrl+V) og klik på OK.',securityMsg:'Din browsers sikkerhedsindstillinger tillader ikke editoren at få automatisk adgang til udklipsholderen.
Brug i stedet tastaturet til at klippe teksten (Ctrl+X).',copyError:'Din browsers sikkerhedsindstillinger tillader ikke editoren at få automatisk adgang til udklipsholderen.
Brug i stedet tastaturet til at kopiere teksten (Ctrl+C).',pasteMsg:'Indsæt i feltet herunder (Ctrl+V) og klik på OK.',securityMsg:'Din browsers sikkerhedsindstillinger tillader ikke editoren at få automatisk adgang til udklipsholderen.