From d3c5850981896061f008fe89fdfb451cfe76c97b Mon Sep 17 00:00:00 2001
From: Louis St-Amour
+ New features:
+ Fixed issues:
+ New features:
+ Fixed issues:
+ New features:
+ Fixed issues:
+ New features:
+ Fixed issues:
+ Changelog starts at this release.
+ 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 web sites.
+ Installing CKEditor is an easy task. Just follow these simple steps:
+ Note: CKEditor is by default installed in the "ckeditor"
+ folder. You can place the files in whichever you want though.
+ 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 web site:
+ The full editor documentation is available online at the following address:
+ CKEditor™ - The text editor for Internet™ -
+ http://ckeditor.com
+ Licensed under the terms of any of the following licenses at your choice:
+
+ You are not required to, but if you want to explicitly declare the license you have
+ chosen to be bound to when using, reproducing, modifying and distributing this software,
+ just include a text file titled "LEGAL" in your version of this software, indicating
+ your license choice. In any case, your choice will not restrict any recipient of
+ your version of this software to use, reproduce, modify and distribute this software
+ under any of the above licenses.
+
+ Where not otherwise indicated, all CKEditor content is authored by CKSource engineers
+ and consists of CKSource-owned intellectual property. In some specific instances,
+ CKEditor will incorporate work done by developers outside of CKSource with their
+ express permission.
+
+ YUI Test: At _source/tests/yuitest.js
+ can be found part of the source code of YUI, which is licensed under the terms of
+ the BSD License. YUI is
+ Copyright © 2008, Yahoo! Inc.
+
+ CKEditor is a trademark of CKSource - Frederico Knabben. All other brand and product
+ names are trademarks, registered trademarks or service marks of their respective
+ holders.
+
+
+
+
+ This sample shows how to use the dialog API to customize dialogs whithout changing
+ the original editor code. The following customizations are being done::
+ Double-click on any of the following DIVs to transform them into editor instances.
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Cras et ipsum quis mi
+ semper accumsan. Integer pretium dui id massa. Suspendisse in nisl sit amet urna
+ rutrum imperdiet. Nulla eu tellus. Donec ante nisi, ullamcorper quis, fringilla
+ nec, sagittis eleifend, pede. Nulla commodo interdum massa. Donec id metus. Fusce
+ eu ipsum. Suspendisse auctor. Phasellus fermentum porttitor risus.
+
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Cras et ipsum quis mi
+ semper accumsan. Integer pretium dui id massa. Suspendisse in nisl sit amet urna
+ rutrum imperdiet. Nulla eu tellus. Donec ante nisi, ullamcorper quis, fringilla
+ nec, sagittis eleifend, pede. Nulla commodo interdum massa. Donec id metus. Fusce
+ eu ipsum. Suspendisse auctor. Phasellus fermentum porttitor risus.
+
+ Donec velit. Mauris massa. Vestibulum non nulla. Nam suscipit arcu nec elit. Phasellus
+ sollicitudin iaculis ante. Ut non mauris et sapien tincidunt adipiscing. Vestibulum
+ vitae leo. Suspendisse nec mi tristique nulla laoreet vulputate.
+
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Cras et ipsum quis mi
+ semper accumsan. Integer pretium dui id massa. Suspendisse in nisl sit amet urna
+ rutrum imperdiet. Nulla eu tellus. Donec ante nisi, ullamcorper quis, fringilla
+ nec, sagittis eleifend, pede. Nulla commodo interdum massa. Donec id metus. Fusce
+ eu ipsum. Suspendisse auctor. Phasellus fermentum porttitor risus.
+
+ CKEditor Changelog
+
+
+ CKEditor 3.1
+
+
+
+
+
+ CKEditor 3.0.2
+
+
+
+
+
+ CKEditor 3.0.1
+
+
+
+
+
+ CKEditor 3.0
+
+
+
+
+
+
+ CKEditor 3.0 RC
+
+ CKEditor Installation Guide
+
+ What's CKEditor?
+
+ Installation
+
+
+
+ Checking Your Installation
+
+
+http://<your site>/<CKEditor installation path>/_samples/index.html
+
+For example:
+http://www.example.com/ckeditor/_samples/index.html
+
+ Documentation
+
+ http://docs.cksource.com/ckeditor
+ Software License Agreement
+
+
+ Copyright © 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+
+
+
+ Sources of Intellectual Property Included in CKEditor
+
+
+ Trademarks
+
+
+ CKEditor Sample
+
+
+
+ CKEditor Sample
+
+
+
+ CKEditor Sample
+
+
+
+
+
+
+
+
+
diff --git a/plugins/CKEditor/ckeditor/_samples/api_dialog/my_dialog.js b/plugins/CKEditor/ckeditor/_samples/api_dialog/my_dialog.js
new file mode 100644
index 00000000000..02f412fc6c8
--- /dev/null
+++ b/plugins/CKEditor/ckeditor/_samples/api_dialog/my_dialog.js
@@ -0,0 +1,28 @@
+/*
+Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+CKEDITOR.dialog.add( 'myDialog', function( editor )
+{
+ return {
+ title : 'My Dialog',
+ minWidth : 400,
+ minHeight : 200,
+ contents : [
+ {
+ id : 'tab1',
+ label : 'First Tab',
+ title : 'First Tab',
+ elements :
+ [
+ {
+ id : 'input1',
+ type : 'text',
+ label : 'Input 1'
+ }
+ ]
+ }
+ ]
+ };
+} );
diff --git a/plugins/CKEditor/ckeditor/_samples/divreplace.html b/plugins/CKEditor/ckeditor/_samples/divreplace.html
new file mode 100644
index 00000000000..bee679772e9
--- /dev/null
+++ b/plugins/CKEditor/ckeditor/_samples/divreplace.html
@@ -0,0 +1,137 @@
+
+
+
+
+
+ CKEditor Sample
+
+
+
+ Part 1
+
+ Part 2
+
+ Part 3
+
+ CKEditor Sample
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/CKEditor/ckeditor/_samples/fullpage.html b/plugins/CKEditor/ckeditor/_samples/fullpage.html
new file mode 100644
index 00000000000..bdd301e5cb4
--- /dev/null
+++ b/plugins/CKEditor/ckeditor/_samples/fullpage.html
@@ -0,0 +1,62 @@
+
+
+
+
+
+ CKEditor Sample
+
+
+
+ CKEditor Samples List
+
+
+ Basic Samples
+
+
+
+
+ Basic Customization
+
+
+
+ Advanced Samples
+
+
+
+
+
+
diff --git a/plugins/CKEditor/ckeditor/_samples/jqueryadapter.html b/plugins/CKEditor/ckeditor/_samples/jqueryadapter.html
new file mode 100644
index 00000000000..66bf976402d
--- /dev/null
+++ b/plugins/CKEditor/ckeditor/_samples/jqueryadapter.html
@@ -0,0 +1,73 @@
+
+
+
+
+
+ CKEditor Sample
+
+
+
+ CKEditor Sample
+
+
+
Your browser is not compatible with CKEditor.'; + + var browsers = + { + gecko : 'Firefox 2.0', + ie : 'Internet Explorer 6.0', + opera : 'Opera 9.5', + webkit : 'Safari 3.0' + }; + + var alsoBrowsers = ''; + + for ( var key in env ) + { + if ( browsers[ key ] ) + { + if ( env[key] ) + html += ' CKEditor is compatible with ' + browsers[ key ] + ' or higher.'; + else + alsoBrowsers += browsers[ key ] + '+, '; + } + } + + alsoBrowsers = alsoBrowsers.replace( /\+,([^,]+), $/, '+ and $1' ); + + html += ' It is also compatible with ' + alsoBrowsers + '.'; + + html += '
With non compatible browsers, you should still be able to see and edit the contents (HTML) in a plain text field.
'; + + var alertsEl = document.getElementById( 'alerts' ); + alertsEl && ( alertsEl.innerHTML = html ); + }; + + var onload = function() + { + // Show a friendly compatibility message as soon as the page is loaded, + // for those browsers that are not compatible with CKEditor. + if ( !CKEDITOR.env.isCompatible ) + showCompatibilityMsg(); + }; + + // Register the onload listener. + if ( window.addEventListener ) + window.addEventListener( 'load', onload, false ); + else if ( window.attachEvent ) + window.attachEvent( 'onload', onload ); + })(); +} diff --git a/plugins/CKEditor/ckeditor/_samples/sample_posteddata.php b/plugins/CKEditor/ckeditor/_samples/sample_posteddata.php new file mode 100644 index 00000000000..af20e89ae1f --- /dev/null +++ b/plugins/CKEditor/ckeditor/_samples/sample_posteddata.php @@ -0,0 +1,59 @@ + + + + +Field Name | +Value | +
---|---|
+ | + |
+ Click the UI Color Picker button to test your color preferences at runtime.
+ + + + diff --git a/plugins/CKEditor/ckeditor/_samples/ui_languages.html b/plugins/CKEditor/ckeditor/_samples/ui_languages.html new file mode 100644 index 00000000000..c6105886cc2 --- /dev/null +++ b/plugins/CKEditor/ckeditor/_samples/ui_languages.html @@ -0,0 +1,106 @@ + + + + +element, spaces should be touched differently. + inPre = false, + returnPoint; + + function checkPending( newTagName ) + { + if ( pendingInline.length > 0 ) + { + for ( var i = 0 ; i < pendingInline.length ; i++ ) + { + var pendingElement = pendingInline[ i ], + pendingName = pendingElement.name, + pendingDtd = CKEDITOR.dtd[ pendingName ], + currentDtd = currentNode.name && CKEDITOR.dtd[ currentNode.name ]; + + if ( ( !currentDtd || currentDtd[ pendingName ] ) && ( !newTagName || !pendingDtd || pendingDtd[ newTagName ] || !CKEDITOR.dtd[ newTagName ] ) ) + { + // Get a clone for the pending element. + pendingElement = pendingElement.clone(); + + // Add it to the current node and make it the current, + // so the new element will be added inside of it. + pendingElement.parent = currentNode; + currentNode = pendingElement; + + // Remove the pending element (back the index by one + // to properly process the next entry). + pendingInline.splice( i, 1 ); + i--; + } + } + } + } + + function addElement( element, target, enforceCurrent ) + { + target = target || currentNode || fragment; + + // If the target is the fragment and this element can't go inside + // body (if fixForBody). + if ( fixForBody && !target.type ) + { + var elementName, realElementName; + if ( element.attributes + && ( realElementName = + element.attributes[ '_cke_real_element_type' ] ) ) + elementName = realElementName; + else + elementName = element.name; + if ( elementName + && !( elementName in CKEDITOR.dtd.$body ) + && !( elementName in CKEDITOR.dtd.$nonBodyContent ) ) + { + var savedCurrent = currentNode; + + // Create ain the fragment. + currentNode = target; + parser.onTagOpen( fixForBody, {} ); + + // The new target now is the
. + target = currentNode; + + if ( enforceCurrent ) + currentNode = savedCurrent; + } + } + + // Rtrim empty spaces on block end boundary. (#3585) + if ( element._.isBlockLike + && element.name != 'pre' ) + { + + 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; + } + } + + target.add( element ); + + if ( element.returnPoint ) + { + currentNode = element.returnPoint; + delete element.returnPoint; + } + } + + parser.onTagOpen = function( tagName, attributes, selfClosing ) + { + 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; + + // This is a tag to be removed if empty, so do not add it immediately. + if ( CKEDITOR.dtd.$removeEmpty[ tagName ] ) + { + pendingInline.push( element ); + return; + } + else if ( tagName == 'pre' ) + inPre = true; + else if ( tagName == 'br' && inPre ) + { + currentNode.add( new CKEDITOR.htmlParser.text( '\n' ) ); + return; + } + + var currentName = currentNode.name; + + var currentDtd = currentName + && ( CKEDITOR.dtd[ currentName ] + || ( currentNode._.isBlockLike ? CKEDITOR.dtd.div : CKEDITOR.dtd.span ) ); + + // If the element cannot be child of the current element. + if ( currentDtd // Fragment could receive any elements. + && !element.isUnknown && !currentNode.isUnknown && !currentDtd[ tagName ] ) + { + + var reApply = false, + addPoint; // New position to start adding nodes. + + // Fixing malformed nested lists by moving it into a previous list item. (#3828) + 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 in listItems ) ) + addElement( ( lastChild = new CKEDITOR.htmlParser.element( 'li' ) ), currentNode ); + + returnPoint = currentNode, addPoint = lastChild; + } + // If the element name is the same as the current element name, + // then just close the current one and append the new one to the + // parent. This situation usually happens with
,
. + if ( !currentNode._.hasInlineStarted && !inPre ) + { + text = CKEDITOR.tools.ltrim( text ); + + if ( text.length === 0 ) + return; + } + + checkPending(); + + if ( fixForBody + && ( !currentNode.type || currentNode.name == 'body' ) + && CKEDITOR.tools.trim( text ) ) + { + this.onTagOpen( fixForBody, {} ); + } + + // Shrinking consequential spaces into one single for all elements + // text contents. + if ( !inPre ) + text = text.replace( /[\t\r\n ]{2,}|[\t\r\n]/g, ' ' ); + + currentNode.add( new CKEDITOR.htmlParser.text( text ) ); + }; + + parser.onCDATA = function( cdata ) + { + currentNode.add( new CKEDITOR.htmlParser.cdata( cdata ) ); + }; + + parser.onComment = function( comment ) + { + currentNode.add( new CKEDITOR.htmlParser.comment( comment ) ); + }; + + // Parse it. + parser.parse( fragmentHtml ); + + // Close all pending nodes. + while ( currentNode.type ) + { + var parent = currentNode.parent, + node = currentNode; + + if ( fixForBody + && ( !parent.type || parent.name == 'body' ) + && !CKEDITOR.dtd.$body[ node.name ] ) + { + currentNode = parent; + parser.onTagOpen( fixForBody, {} ); + parent = currentNode; + } + + parent.add( node ); + currentNode = parent; + } + + return fragment; + }; + + CKEDITOR.htmlParser.fragment.prototype = + { + /** + * Adds a node to this fragment. + * @param {Object} node The node to be added. It can be any of of the + * following types: {@link CKEDITOR.htmlParser.element}, + * {@link CKEDITOR.htmlParser.text} and + * {@link CKEDITOR.htmlParser.comment}. + * @example + */ + add : function( node ) + { + var len = this.children.length, + previous = len > 0 && this.children[ len - 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.push( node ); + + this._.hasInlineStarted = node.type == CKEDITOR.NODE_TEXT || ( node.type == CKEDITOR.NODE_ELEMENT && !node._.isBlockLike ); + }, + + /** + * Writes the fragment HTML to a CKEDITOR.htmlWriter. + * @param {CKEDITOR.htmlWriter} writer The writer to which write the HTML. + * @example + * 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>" + */ + writeHtml : function( writer, filter ) + { + var isChildrenFiltered; + this.filterChildren = function() + { + var writer = new CKEDITOR.htmlParser.basicWriter(); + this.writeChildrenHtml.call( this, writer, filter, true ); + var html = writer.getHtml(); + this.children = new CKEDITOR.htmlParser.fragment.fromHtml( html ).children; + isChildrenFiltered = 1; + }; + + // Filtering the root fragment before anything else. + !this.name && filter && filter.onFragment( this ); + + this.writeChildrenHtml( writer, isChildrenFiltered ? null : filter ); + }, + + writeChildrenHtml : function( writer, filter ) + { + for ( var i = 0 ; i < this.children.length ; i++ ) + this.children[i].writeHtml( writer, filter ); + } + }; +})(); diff --git a/plugins/CKEditor/ckeditor/_source/core/htmlparser/text.js b/plugins/CKEditor/ckeditor/_source/core/htmlparser/text.js new file mode 100644 index 00000000000..0d63ac9dd4d --- /dev/null +++ b/plugins/CKEditor/ckeditor/_source/core/htmlparser/text.js @@ -0,0 +1,55 @@ +/* +Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +(function() +{ + var spacesRegex = /[\t\r\n ]{2,}|[\t\r\n]/g; + + /** + * A lightweight representation of HTML text. + * @constructor + * @example + */ + CKEDITOR.htmlParser.text = function( value ) + { + /** + * The text value. + * @type String + * @example + */ + this.value = value; + + /** @private */ + this._ = + { + isBlockLike : false + }; + }; + + CKEDITOR.htmlParser.text.prototype = + { + /** + * The node type. This is a constant value set to {@link CKEDITOR.NODE_TEXT}. + * @type Number + * @example + */ + type : CKEDITOR.NODE_TEXT, + + /** + * Writes the HTML representation of this text to a CKEDITOR.htmlWriter. + * @param {CKEDITOR.htmlWriter} writer The writer to which write the HTML. + * @example + */ + writeHtml : function( writer, filter ) + { + var text = this.value; + + if ( filter && !( text = filter.onText( text, this ) ) ) + return; + + writer.text( text ); + } + }; +})(); diff --git a/plugins/CKEditor/ckeditor/_source/core/imagecacher.js b/plugins/CKEditor/ckeditor/_source/core/imagecacher.js new file mode 100644 index 00000000000..3a635bb3982 --- /dev/null +++ b/plugins/CKEditor/ckeditor/_source/core/imagecacher.js @@ -0,0 +1,58 @@ +/* +Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +(function() +{ + var loaded = {}; + + var loadImage = function( image, callback ) + { + var doCallback = function() + { + loaded[ image ] = 1; + callback(); + }; + + var img = new CKEDITOR.dom.element( 'img' ); + img.on( 'load', doCallback ); + img.on( 'error', doCallback ); + img.setAttribute( 'src', image ); + }; + + /** + * Load images into the browser cache. + * @namespace + * @example + */ + CKEDITOR.imageCacher = + { + /** + * Loads one or more images. + * @param {Array} images The URLs for the images to be loaded. + * @param {Function} callback The function to be called once all images + * are loaded. + */ + load : function( images, callback ) + { + var pendingCount = images.length; + + var checkPending = function() + { + if ( --pendingCount === 0 ) + callback(); + }; + + for ( var i = 0 ; i < images.length ; i++ ) + { + var image = images[ i ]; + + if ( loaded[ image ] ) + checkPending(); + else + loadImage( image, checkPending ); + } + } + }; +})(); diff --git a/plugins/CKEditor/ckeditor/_source/core/lang.js b/plugins/CKEditor/ckeditor/_source/core/lang.js new file mode 100644 index 00000000000..d24fa1a57c4 --- /dev/null +++ b/plugins/CKEditor/ckeditor/_source/core/lang.js @@ -0,0 +1,151 @@ +/* +Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +(function() +{ + var loadedLangs = {}; + + CKEDITOR.lang = + { + /** + * The list of languages available in the editor core. + * @type Object + * @example + * alert( CKEDITOR.lang.en ); // "true" + */ + languages : + { + 'af' : 1, + 'ar' : 1, + 'bg' : 1, + 'bn' : 1, + 'bs' : 1, + 'ca' : 1, + 'cs' : 1, + 'da' : 1, + 'de' : 1, + 'el' : 1, + 'en-au' : 1, + 'en-ca' : 1, + 'en-uk' : 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, + 'is' : 1, + 'it' : 1, + 'ja' : 1, + 'km' : 1, + 'ko' : 1, + 'lt' : 1, + 'lv' : 1, + 'mn' : 1, + 'ms' : 1, + 'nb' : 1, + 'nl' : 1, + 'no' : 1, + 'pl' : 1, + 'pt-br' : 1, + 'pt' : 1, + 'ro' : 1, + 'ru' : 1, + 'sk' : 1, + 'sl' : 1, + 'sr-latn' : 1, + 'sr' : 1, + 'sv' : 1, + 'th' : 1, + 'tr' : 1, + 'uk' : 1, + 'vi' : 1, + 'zh-cn' : 1, + 'zh' : 1 + }, + + /** + * Loads a specific language file, or auto detect it. A callback is + * then called when the file gets loaded. + * @param {String} languageCode The code of the language file to be + * loaded. If "autoDetect" is set to true, this language will be + * used as the default one, if the detect language is not + * available in the core. + * @param {Boolean} autoDetect Indicates that the function must try to + * detect the user language and load it instead. + * @param {Function} callback The 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. + * @example + */ + 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( + '_source/' + // @Packager.RemoveLine + 'lang/' + languageCode + '.js' ), + function() + { + callback( languageCode, this[ languageCode ] ); + } + , this ); + } + else + callback( languageCode, this[ languageCode ] ); + }, + + /** + * Returns the language that best fit 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. + * @param {String} defaultLanguage The default language to be returned + * if the user language is not supported. + * @returns {String} The detected language code. + * @example + * alert( CKEDITOR.lang.detect( 'en' ) ); // e.g., in a German browser: "de" + */ + detect : function( defaultLanguage, probeLanguage ) + { + var languages = this.languages; + probeLanguage = probeLanguage || navigator.userLanguage || navigator.language; + + 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; + } + }; + +})(); diff --git a/plugins/CKEditor/ckeditor/_source/core/loader.js b/plugins/CKEditor/ckeditor/_source/core/loader.js new file mode 100644 index 00000000000..92648a027dc --- /dev/null +++ b/plugins/CKEditor/ckeditor/_source/core/loader.js @@ -0,0 +1,239 @@ +/* +Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html 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. + * @namespace + * @example + */ + CKEDITOR.loader = (function() + { + // Table of script names and their dependencies. + var scripts = + { + 'core/_bootstrap' : [ 'core/config', 'core/ckeditor', 'core/plugins', 'core/scriptloader', 'core/tools', /* The following are entries that we want to force loading at the end to avoid dependence recursion */ 'core/dom/comment', 'core/dom/elementpath', 'core/dom/text', 'core/dom/range' ], + 'core/ajax' : [ 'core/xml' ], + 'core/ckeditor' : [ 'core/ckeditor_basic', 'core/dom', 'core/dtd', 'core/dom/document', 'core/dom/element', 'core/editor', 'core/event', 'core/htmlparser', 'core/htmlparser/element', 'core/htmlparser/fragment', 'core/htmlparser/filter', 'core/htmlparser/basicwriter', 'core/tools' ], + 'core/ckeditor_base' : [], + 'core/ckeditor_basic' : [ 'core/editor_basic', 'core/env', 'core/event' ], + 'core/command' : [], + 'core/config' : [ 'core/ckeditor_base' ], + 'core/dom' : [], + 'core/dom/comment' : [ 'core/dom/node' ], + 'core/dom/document' : [ 'core/dom', 'core/dom/domobject', 'core/dom/window' ], + 'core/dom/documentfragment' : [ 'core/dom/element' ], + 'core/dom/element' : [ 'core/dom', 'core/dom/document', 'core/dom/domobject', 'core/dom/node', 'core/dom/nodelist', 'core/tools' ], + 'core/dom/elementpath' : [ 'core/dom/element' ], + 'core/dom/event' : [], + 'core/dom/node' : [ 'core/dom/domobject', 'core/tools' ], + 'core/dom/nodelist' : [ 'core/dom/node' ], + 'core/dom/domobject' : [ 'core/dom/event' ], + 'core/dom/range' : [ 'core/dom/document', 'core/dom/documentfragment', 'core/dom/element', 'core/dom/walker' ], + 'core/dom/text' : [ 'core/dom/node', 'core/dom/domobject' ], + 'core/dom/walker' : [ 'core/dom/node' ], + 'core/dom/window' : [ 'core/dom/domobject' ], + 'core/dtd' : [ 'core/tools' ], + 'core/editor' : [ 'core/command', 'core/config', 'core/editor_basic', 'core/focusmanager', 'core/lang', 'core/plugins', 'core/skins', 'core/themes', 'core/tools', 'core/ui' ], + 'core/editor_basic' : [ 'core/event' ], + 'core/env' : [], + 'core/event' : [], + 'core/focusmanager' : [], + 'core/htmlparser' : [], + 'core/htmlparser/comment' : [ 'core/htmlparser' ], + 'core/htmlparser/element' : [ 'core/htmlparser', 'core/htmlparser/fragment' ], + 'core/htmlparser/fragment' : [ 'core/htmlparser', 'core/htmlparser/comment', 'core/htmlparser/text', 'core/htmlparser/cdata' ], + 'core/htmlparser/text' : [ 'core/htmlparser' ], + 'core/htmlparser/cdata' : [ 'core/htmlparser' ], + 'core/htmlparser/filter' : [ 'core/htmlparser' ], + 'core/htmlparser/basicwriter': [ 'core/htmlparser' ], + 'core/imagecacher' : [ 'core/dom/element' ], + 'core/lang' : [], + 'core/plugins' : [ 'core/resourcemanager' ], + 'core/resourcemanager' : [ 'core/scriptloader', 'core/tools' ], + 'core/scriptloader' : [ 'core/dom/element', 'core/env' ], + 'core/skins' : [ 'core/imagecacher', 'core/scriptloader' ], + 'core/themes' : [ 'core/resourcemanager' ], + 'core/tools' : [ 'core/env' ], + 'core/ui' : [], + 'core/xml' : [ 'core/env' ] + }; + + var basePath = (function() + { + // This is a copy of CKEDITOR.basePath, but requires the script having + // "_source/core/loader.js". + if ( CKEDITOR && CKEDITOR.basePath ) + return CKEDITOR.basePath; + + // Find out the editor directory path, based on its ', + + onShow : function() + { + if ( CKEDITOR.env.ie ) + this.getParentEditor().document.getBody().$.contentEditable = 'false'; + + // FIREFOX BUG: Force the browser to render the dialog to make the to-be- + // inserted iframe editable. (#3366) + this.parts.dialog.$.offsetHeight; + + var container = this.getContentElement( 'general', 'editing_area' ).getElement(), + iframe = CKEDITOR.dom.element.createFromHtml( '' ); + + var lang = this.getParentEditor().lang; + + iframe.setStyles( + { + width : '346px', + height : '130px', + 'background-color' : 'white', + border : '1px solid black' + } ); + iframe.setCustomData( 'dialog', this ); + + var accTitle = lang.editorTitle.replace( '%1', lang.clipboard.title ); + + if ( CKEDITOR.env.ie ) + container.setHtml( '' ); + else + { + container.setHtml( '' ); + container.setAttributes( + { + role : 'region', + title : accTitle + } ); + iframe.setAttributes( + { + role : 'region', + title : ' ' + } ); + } + container.append( iframe ); + if ( CKEDITOR.env.ie ) + container.setStyle( 'height', ( iframe.$.offsetHeight + 2 ) + 'px' ); + + if ( isCustomDomain ) + { + CKEDITOR._cke_htmlToLoad = this.definition.htmlToLoad; + iframe.setAttribute( 'src', + 'javascript:void( (function(){' + + 'document.open();' + + 'document.domain="' + document.domain + '";' + + 'document.write( window.parent.CKEDITOR._cke_htmlToLoad );' + + 'delete window.parent.CKEDITOR._cke_htmlToLoad;' + + 'document.close();' + + '})() )' ); + } + else + { + var doc = iframe.$.contentWindow.document; + doc.open(); + doc.write( this.definition.htmlToLoad ); + doc.close(); + } + }, + + onHide : function() + { + if ( CKEDITOR.env.ie ) + this.getParentEditor().document.getBody().$.contentEditable = 'true'; + }, + + onLoad : function() + { + if ( ( CKEDITOR.env.ie7Compat || CKEDITOR.env.ie6Compat ) && editor.lang.dir == 'rtl' ) + this.parts.contents.setStyle( 'overflow', 'hidden' ); + }, + + onOk : function() + { + var container = this.getContentElement( 'general', 'editing_area' ).getElement(), + iframe = container.getElementsByTag( 'iframe' ).getItem( 0 ), + editor = this.getParentEditor(), + html = iframe.$.contentWindow.document.body.innerHTML; + + setTimeout( function(){ + editor.fire( 'paste', { 'html' : html } ); + }, 0 ); + + }, + + contents : [ + { + id : 'general', + label : editor.lang.common.generalTab, + elements : [ + { + type : 'html', + id : 'securityMsg', + html : '', + focus : function() + { + var div = this.getElement(); + var iframe = div.getElementsByTag( 'iframe' ); + if ( iframe.count() < 1 ) + return; + iframe = iframe.getItem( 0 ); + + // #3291 : JAWS needs the 500ms delay to detect that the editor iframe + // iframe is no longer editable. So that it will put the focus into the + // Paste from Word dialog's editable area instead. + setTimeout( function() + { + iframe.$.contentWindow.focus(); + }, 500 ); + } + } + ] + } + ] + }; +}); diff --git a/plugins/CKEditor/ckeditor/_source/plugins/clipboard/plugin.js b/plugins/CKEditor/ckeditor/_source/plugins/clipboard/plugin.js new file mode 100644 index 00000000000..d215221c73f --- /dev/null +++ b/plugins/CKEditor/ckeditor/_source/plugins/clipboard/plugin.js @@ -0,0 +1,358 @@ +/* +Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +/** + * @file Clipboard support + */ + +(function() +{ + // Tries to execute any of the paste, cut or copy commands in IE. Returns a + // boolean indicating that the operation succeeded. + var execIECommand = function( editor, command ) + { + var doc = editor.document, + body = doc.getBody(); + + var enabled = false; + var onExec = function() + { + enabled = true; + }; + + // The following seems to be the only reliable way to detect that + // clipboard commands are enabled in IE. It will fire the + // onpaste/oncut/oncopy events only if the security settings allowed + // the command to execute. + body.on( command, onExec ); + + doc.$.execCommand( command ); + + body.removeListener( command, onExec ); + + return enabled; + }; + + // Attempts to execute the Cut and Copy operations. + var tryToCutCopy = + CKEDITOR.env.ie ? + function( editor, type ) + { + return execIECommand( editor, type ); + } + : // !IE. + function( editor, type ) + { + try + { + // Other browsers throw an error if the command is disabled. + return editor.document.$.execCommand( type ); + } + catch( e ) + { + return false; + } + }; + + // A class that represents one of the cut or copy commands. + var cutCopyCmd = function( type ) + { + this.type = type; + this.canUndo = ( this.type == 'cut' ); // We can't undo copy to clipboard. + }; + + cutCopyCmd.prototype = + { + exec : function( editor, data ) + { + var success = tryToCutCopy( editor, this.type ); + + if ( !success ) + alert( editor.lang.clipboard[ this.type + 'Error' ] ); // Show cutError or copyError. + + return success; + } + }; + + // Paste command. + var pasteCmd = + { + canUndo : false, + + exec : + CKEDITOR.env.ie ? + function( editor ) + { + // Prevent IE from pasting at the begining of the document. + editor.focus(); + + if ( !editor.document.getBody().fire( 'beforepaste' ) + && !execIECommand( editor, 'paste' ) ) + { + editor.fire( 'pasteDialog' ); + return false; + } + } + : + function( editor ) + { + try + { + if ( !editor.document.getBody().fire( 'beforepaste' ) + && !editor.document.$.execCommand( 'Paste', false, null ) ) + { + throw 0; + } + } + catch ( e ) + { + setTimeout( function() + { + editor.fire( 'pasteDialog' ); + }, 0 ); + return false; + } + } + }; + + // Listens for some clipboard related keystrokes, so they get customized. + var onKey = function( event ) + { + if ( this.mode != 'wysiwyg' ) + return; + + switch ( event.data.keyCode ) + { + // Paste + case CKEDITOR.CTRL + 86 : // CTRL+V + case CKEDITOR.SHIFT + 45 : // SHIFT+INS + + var body = this.document.getBody(); + + // Simulate 'beforepaste' event for all none-IEs. + if ( !CKEDITOR.env.ie && body.fire( 'beforepaste' ) ) + event.cancel(); + // Simulate 'paste' event for Opera/Firefox2. + else if ( CKEDITOR.env.opera + || CKEDITOR.env.gecko && CKEDITOR.env.version < 10900 ) + body.fire( 'paste' ); + return; + + // Cut + case CKEDITOR.CTRL + 88 : // CTRL+X + case CKEDITOR.SHIFT + 46 : // SHIFT+DEL + + // Save Undo snapshot. + var editor = this; + this.fire( 'saveSnapshot' ); // Save before paste + setTimeout( function() + { + editor.fire( 'saveSnapshot' ); // Save after paste + }, 0 ); + } + }; + + // Allow to peek clipboard content by redirecting the + // pasting content into a temporary bin and grab the content of it. + function getClipboardData( evt, mode, callback ) + { + var doc = this.document; + + // Avoid recursions on 'paste' event for IE. + if ( CKEDITOR.env.ie && doc.getById( 'cke_pastebin' ) ) + return; + + var sel = this.getSelection(), + range = new CKEDITOR.dom.range( doc ); + + // Create container to paste into + var pastebin = new CKEDITOR.dom.element( mode == 'text' ? 'textarea' : 'div', doc ); + pastebin.setAttribute( 'id', 'cke_pastebin' ); + // Safari requires a filler node inside the div to have the content pasted into it. (#4882) + CKEDITOR.env.webkit && pastebin.append( doc.createText( '\xa0' ) ); + doc.getBody().append( pastebin ); + + // It's definitely a better user experience if we make the paste-bin pretty unnoticed + // by pulling it off the screen, while this hack will make the paste-bin a control type element + // and that become a selection plain later. + if ( !CKEDITOR.env.ie && mode != 'html' ) + { + pastebin.setStyles( + { + position : 'absolute', + left : '-1000px', + // Position the bin exactly at the position of the selected element + // to avoid any subsequent document scroll. + top : sel.getStartElement().getDocumentPosition().y + 'px', + width : '1px', + height : '1px', + overflow : 'hidden' + }); + } + + var bms = sel.createBookmarks(); + + // Turn off design mode temporarily before give focus to the paste bin. + if ( mode == 'text' ) + { + if ( CKEDITOR.env.ie ) + { + var ieRange = doc.getBody().$.createTextRange(); + ieRange.moveToElementText( pastebin.$ ); + ieRange.execCommand( 'Paste' ); + evt.data.preventDefault(); + } + else + { + doc.$.designMode = 'off'; + pastebin.$.focus(); + } + } + else + { + range.setStartAt( pastebin, CKEDITOR.POSITION_AFTER_START ); + range.setEndAt( pastebin, CKEDITOR.POSITION_BEFORE_END ); + range.select( true ); + } + + // Wait a while and grab the pasted contents + window.setTimeout( function() + { + mode == 'text' && !CKEDITOR.env.ie && ( doc.$.designMode = 'on' ); + pastebin.remove(); + + // Grab the HTML contents. + // We need to look for a apple style wrapper on webkit it also adds + // a div wrapper if you copy/paste the body of the editor. + // Remove hidden div and restore selection. + var bogusSpan; + pastebin = ( CKEDITOR.env.webkit + && ( bogusSpan = pastebin.getFirst() ) + && ( bogusSpan.is && bogusSpan.hasClass( 'Apple-style-span' ) ) ? + bogusSpan : pastebin ); + + sel.selectBookmarks( bms ); + callback( pastebin[ 'get' + ( mode == 'text' ? 'Value' : 'Html' ) ]() ); + }, 0 ); + } + + // Register the plugin. + CKEDITOR.plugins.add( 'clipboard', + { + requires : [ 'htmldataprocessor' ], + init : function( editor ) + { + // Inserts processed data into the editor at the end of the + // events chain. + editor.on( 'paste', function( evt ) + { + var data = evt.data; + if ( data[ 'html' ] ) + editor.insertHtml( data[ 'html' ] ); + else if ( data[ 'text' ] ) + editor.insertText( data[ 'text' ] ); + + }, null, null, 1000 ); + + editor.on( 'pasteDialog', function( evt ) + { + setTimeout( function() + { + // Open default paste dialog. + editor.openDialog( 'paste' ); + }, 0 ); + }); + + function addButtonCommand( buttonName, commandName, command, ctxMenuOrder ) + { + var lang = editor.lang[ commandName ]; + + editor.addCommand( commandName, command ); + editor.ui.addButton( buttonName, + { + label : lang, + command : commandName + }); + + // If the "menu" plugin is loaded, register the menu item. + if ( editor.addMenuItems ) + { + editor.addMenuItem( commandName, + { + label : lang, + command : commandName, + group : 'clipboard', + order : ctxMenuOrder + }); + } + } + + addButtonCommand( 'Cut', 'cut', new cutCopyCmd( 'cut' ), 1 ); + addButtonCommand( 'Copy', 'copy', new cutCopyCmd( 'copy' ), 4 ); + addButtonCommand( 'Paste', 'paste', pasteCmd, 8 ); + + CKEDITOR.dialog.add( 'paste', CKEDITOR.getUrl( this.path + 'dialogs/paste.js' ) ); + + editor.on( 'key', onKey, editor ); + + var mode = editor.config.forcePasteAsPlainText ? 'text' : 'html'; + + // We'll be catching all pasted content in one line, regardless of whether the + // it's introduced by a document command execution (e.g. toolbar buttons) or + // user paste behaviors. (e.g. Ctrl-V) + editor.on( 'contentDom', function() + { + var body = editor.document.getBody(); + body.on( ( mode == 'text' && CKEDITOR.env.ie ) ? 'paste' : 'beforepaste', + function( evt ) + { + if( depressBeforePasteEvent ) + return; + + getClipboardData.call( editor, evt, mode, function ( data ) + { + // The very last guard to make sure the + // paste has successfully happened. + if ( !data ) + return; + + var dataTransfer = {}; + dataTransfer[ mode ] = data; + editor.fire( 'paste', dataTransfer ); + } ); + }); + + }); + + // If the "contextmenu" plugin is loaded, register the listeners. + if ( editor.contextMenu ) + { + var depressBeforePasteEvent; + function stateFromNamedCommand( command ) + { + // IE Bug: queryCommandEnabled('paste') fires also 'beforepaste', + // guard to distinguish from the ordinary sources( either + // keyboard paste or execCommand ) (#4874). + CKEDITOR.env.ie && command == 'Paste'&& ( depressBeforePasteEvent = 1 ); + + var retval = editor.document.$.queryCommandEnabled( command ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED; + depressBeforePasteEvent = 0; + return retval; + } + + editor.contextMenu.addListener( function() + { + return { + cut : stateFromNamedCommand( 'Cut' ), + + // Browser bug: 'Cut' has the correct states for both Copy and Cut. + copy : stateFromNamedCommand( 'Cut' ), + paste : CKEDITOR.env.webkit ? CKEDITOR.TRISTATE_OFF : stateFromNamedCommand( 'Paste' ) + }; + }); + } + } + }); +})(); diff --git a/plugins/CKEditor/ckeditor/_source/plugins/colorbutton/plugin.js b/plugins/CKEditor/ckeditor/_source/plugins/colorbutton/plugin.js new file mode 100644 index 00000000000..b49e5f84da2 --- /dev/null +++ b/plugins/CKEditor/ckeditor/_source/plugins/colorbutton/plugin.js @@ -0,0 +1,215 @@ +/* +Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +CKEDITOR.plugins.add( 'colorbutton', +{ + requires : [ 'panelbutton', 'floatpanel', 'styles' ], + + init : function( editor ) + { + var config = editor.config, + lang = editor.lang.colorButton; + + var clickFn; + + if ( !CKEDITOR.env.hc ) + { + addButton( 'TextColor', 'fore', lang.textColorTitle ); + addButton( 'BGColor', 'back', lang.bgColorTitle ); + } + + function addButton( name, type, title ) + { + editor.ui.add( name, CKEDITOR.UI_PANELBUTTON, + { + label : title, + title : title, + className : 'cke_button_' + name.toLowerCase(), + modes : { wysiwyg : 1 }, + + panel : + { + css : editor.skin.editor.css + }, + + onBlock : function( panel, blockName ) + { + var block = panel.addBlock( blockName ); + block.autoSize = true; + block.element.addClass( 'cke_colorblock' ); + block.element.setHtml( renderColors( panel, type ) ); + + var keys = block.keys; + keys[ 39 ] = 'next'; // ARROW-RIGHT + keys[ 9 ] = 'next'; // TAB + keys[ 37 ] = 'prev'; // ARROW-LEFT + keys[ CKEDITOR.SHIFT + 9 ] = 'prev'; // SHIFT + TAB + keys[ 32 ] = 'click'; // SPACE + } + }); + } + + + function renderColors( panel, type ) + { + var output = [], + colors = config.colorButton_colors.split( ',' ); + + var clickFn = CKEDITOR.tools.addFunction( function( color, type ) + { + if ( color == '?' ) + { + var applyColorStyle = arguments.callee; + function onColorDialogClose( evt ) + { + this.removeListener( 'ok', onColorDialogClose ); + this.removeListener( 'cancel', onColorDialogClose ); + + evt.name == 'ok' && applyColorStyle( this.getContentElement( 'picker', 'selectedColor' ).getValue(), type ); + } + + editor.openDialog( 'colordialog', function() + { + this.on( 'ok', onColorDialogClose ); + this.on( 'cancel', onColorDialogClose ); + } ); + + return; + } + + editor.focus(); + + panel.hide(); + + var style = new CKEDITOR.style( config['colorButton_' + type + 'Style'], color && { color : color } ); + + editor.fire( 'saveSnapshot' ); + if ( color ) + style.apply( editor.document ); + else + style.remove( editor.document ); + editor.fire( 'saveSnapshot' ); + }); + + // Render the "Automatic" button. + output.push( + '' + + '' + editor.lang.clipboard.securityMsg + '' + }, + { + type : 'html', + id : 'pasteMsg', + html : ''+editor.lang.clipboard.pasteMsg +'' + }, + { + type : 'html', + id : 'editing_area', + style : 'width: 100%; height: 100%;', + html : '
' + + '' + + ' | ' + + '', + lang.auto, + ' | ' + + '
' + + '' + + '' + + '' + + ' | ' ); + } + + // Render the "More Colors" button. + if ( config.colorButton_enableMore ) + { + output.push( + '|||||||
' + + '', + lang.more, + '' + + ' | ' ); // It is later in the code. + } + + output.push( '
0 ) + html.push( 'style="', styles.join( '; ' ), '" ' ); + html.push( ' class="cke_dialog_ui_vbox_child">', childHtmlList[i], ' |
+ * { + * type : 'button', + * id : 'browse', + * filebrowser : 'tabId:elementId', + * label : editor.lang.common.browseServer + * } + *+ * + * If you set the 'filebrowser' attribute on any element other than + * 'fileButton', the 'Browse' action will be triggered. + * + * Example 2: (Quick Upload) + * + *
+ * { + * type : 'fileButton', + * id : 'uploadButton', + * filebrowser : 'tabId:elementId', + * label : editor.lang.common.uploadSubmit, + * 'for' : [ 'upload', 'upload' ] + * } + *+ * + * If you set the 'filebrowser' attribute on a fileButton element, the + * 'QuickUpload' action will be executed. + * + * Filebrowser plugin also supports more advanced configuration (through + * javascript object). + * + * The following settings are supported: + * + *
+ * [action] - Browse or QuickUpload + * [target] - field to update, tabId:elementId + * [params] - additional arguments to be passed to the server connector (optional) + * [onSelect] - function to execute when file is selected/uploaded (optional) + * [url] - the URL to be called (optional) + *+ * + * Example 3: (Quick Upload) + * + *
+ * { + * type : 'fileButton', + * label : editor.lang.common.uploadSubmit, + * id : 'buttonId', + * filebrowser : + * { + * action : 'QuickUpload', //required + * target : 'tab1:elementId', //required + * params : //optional + * { + * type : 'Files', + * currentFolder : '/folder/' + * }, + * onSelect : function( fileUrl, errorMessage ) //optional + * { + * // Do not call the built-in selectFuntion + * // return false; + * } + * }, + * 'for' : [ 'tab1', 'myFile' ] + * } + *+ * + * Suppose we have a file element with id 'myFile', text field with id + * 'elementId' and a fileButton. If filebowser.url is not specified explicitly, + * form action will be set to 'filebrowser[DialogName]UploadUrl' or, if not + * specified, to 'filebrowserUploadUrl'. Additional parameters from 'params' + * object will be added to the query string. It is possible to create your own + * uploadHandler and cancel the built-in updateTargetElement command. + * + * Example 4: (Browse) + * + *
+ * { + * type : 'button', + * id : 'buttonId', + * label : editor.lang.common.browseServer, + * filebrowser : + * { + * action : 'Browse', + * url : '/ckfinder/ckfinder.html&type=Images', + * target : 'tab1:elementId' + * } + * } + *+ * + * In this example, after pressing a button, file browser will be opened in a + * popup. If we don't specify filebrowser.url attribute, + * 'filebrowser[DialogName]BrowseUrl' or 'filebrowserBrowseUrl' will be used. + * After selecting a file in a file browser, an element with id 'elementId' will + * be updated. Just like in the third example, a custom 'onSelect' function may be + * defined. + */ +( function() +{ + /** + * Adds (additional) arguments to given url. + * + * @param {String} + * url The url. + * @param {Object} + * params Additional parameters. + */ + function addQueryString( url, params ) + { + var queryString = []; + + if ( !params ) + return url; + else + { + for ( var i in params ) + queryString.push( i + "=" + encodeURIComponent( params[ i ] ) ); + } + + return url + ( ( url.indexOf( "?" ) != -1 ) ? "&" : "?" ) + queryString.join( "&" ); + } + + /** + * Make a string's first character uppercase. + * + * @param {String} + * str String. + */ + function ucFirst( str ) + { + str += ''; + var f = str.charAt( 0 ).toUpperCase(); + return f + str.substr( 1 ); + } + + /** + * The onlick function assigned to the 'Browse Server' button. Opens the + * file browser and updates target field when file is selected. + * + * @param {CKEDITOR.event} + * evt The event object. + */ + function browseServer( evt ) + { + var dialog = this.getDialog(); + var editor = dialog.getParentEditor(); + + editor._.filebrowserSe = this; + + var width = editor.config[ 'filebrowser' + ucFirst( dialog.getName() ) + 'WindowWidth' ] + || editor.config.filebrowserWindowWidth || '80%'; + var height = editor.config[ 'filebrowser' + ucFirst( dialog.getName() ) + 'WindowHeight' ] + || editor.config.filebrowserWindowHeight || '70%'; + + var params = this.filebrowser.params || {}; + params.CKEditor = editor.name; + params.CKEditorFuncNum = editor._.filebrowserFn; + if ( !params.langCode ) + params.langCode = editor.langCode; + + var url = addQueryString( this.filebrowser.url, params ); + editor.popup( url, width, height ); + } + + /** + * The onlick function assigned to the 'Upload' button. Makes the final + * decision whether form is really submitted and updates target field when + * file is uploaded. + * + * @param {CKEDITOR.event} + * evt The event object. + */ + function uploadFile( evt ) + { + var dialog = this.getDialog(); + var editor = dialog.getParentEditor(); + + editor._.filebrowserSe = this; + + // If user didn't select the file, stop the upload. + if ( !dialog.getContentElement( this[ 'for' ][ 0 ], this[ 'for' ][ 1 ] ).getInputElement().$.value ) + return false; + + if ( !dialog.getContentElement( this[ 'for' ][ 0 ], this[ 'for' ][ 1 ] ).getAction() ) + return false; + + return true; + } + + /** + * Setups the file element. + * + * @param {CKEDITOR.ui.dialog.file} + * fileInput The file element used during file upload. + * @param {Object} + * filebrowser Object containing filebrowser settings assigned to + * the fileButton associated with this file element. + */ + function setupFileElement( editor, fileInput, filebrowser ) + { + var params = filebrowser.params || {}; + params.CKEditor = editor.name; + params.CKEditorFuncNum = editor._.filebrowserFn; + if ( !params.langCode ) + params.langCode = editor.langCode; + + fileInput.action = addQueryString( filebrowser.url, params ); + fileInput.filebrowser = filebrowser; + } + + /** + * Traverse through the content definition and attach filebrowser to + * elements with 'filebrowser' attribute. + * + * @param String + * dialogName Dialog name. + * @param {CKEDITOR.dialog.dialogDefinitionObject} + * definition Dialog definition. + * @param {Array} + * elements Array of {@link CKEDITOR.dialog.contentDefinition} + * objects. + */ + function attachFileBrowser( editor, dialogName, definition, elements ) + { + var element, fileInput; + + for ( var i in elements ) + { + element = elements[ i ]; + + if ( element.type == 'hbox' || element.type == 'vbox' ) + attachFileBrowser( editor, dialogName, definition, element.children ); + + if ( !element.filebrowser ) + continue; + + if ( typeof element.filebrowser == 'string' ) + { + var fb = + { + action : ( element.type == 'fileButton' ) ? 'QuickUpload' : 'Browse', + target : element.filebrowser + }; + element.filebrowser = fb; + } + + if ( element.filebrowser.action == 'Browse' ) + { + var url = element.filebrowser.url || editor.config[ 'filebrowser' + ucFirst( dialogName ) + 'BrowseUrl' ] + || editor.config.filebrowserBrowseUrl; + + if ( url ) + { + element.onClick = browseServer; + element.filebrowser.url = url; + element.hidden = false; + } + } + else if ( element.filebrowser.action == 'QuickUpload' && element[ 'for' ] ) + { + url = element.filebrowser.url || editor.config[ 'filebrowser' + ucFirst( dialogName ) + 'UploadUrl' ] + || editor.config.filebrowserUploadUrl; + + if ( url ) + { + element.onClick = uploadFile; + element.filebrowser.url = url; + element.hidden = false; + setupFileElement( editor, definition.getContents( element[ 'for' ][ 0 ] ).get( element[ 'for' ][ 1 ] ), element.filebrowser ); + } + } + } + } + + /** + * Updates the target element with the url of uploaded/selected file. + * + * @param {String} + * url The url of a file. + */ + function updateTargetElement( url, sourceElement ) + { + var dialog = sourceElement.getDialog(); + var targetElement = sourceElement.filebrowser.target || null; + url = url.replace( /#/g, '%23' ); + + // If there is a reference to targetElement, update it. + if ( targetElement ) + { + var target = targetElement.split( ':' ); + var element = dialog.getContentElement( target[ 0 ], target[ 1 ] ); + if ( element ) + { + element.setValue( url ); + dialog.selectPage( target[ 0 ] ); + } + } + } + + /** + * Returns true if filebrowser is configured in one of the elements. + * + * @param {CKEDITOR.dialog.dialogDefinitionObject} + * definition Dialog definition. + * @param String + * tabId The tab id where element(s) can be found. + * @param String + * elementId The element id (or ids, separated with a semicolon) to check. + */ + function isConfigured( definition, tabId, elementId ) + { + if ( elementId.indexOf( ";" ) !== -1 ) + { + var ids = elementId.split( ";" ); + for ( var i = 0 ; i < ids.length ; i++ ) + { + if ( isConfigured( definition, tabId, ids[i]) ) + return true; + } + return false; + } + + return ( definition.getContents( tabId ).get( elementId ).filebrowser && definition.getContents( tabId ).get( elementId ).filebrowser.url ); + } + + function setUrl( fileUrl, data ) + { + var dialog = this._.filebrowserSe.getDialog(), + targetInput = this._.filebrowserSe[ 'for' ], + onSelect = this._.filebrowserSe.filebrowser.onSelect; + + if ( targetInput ) + dialog.getContentElement( targetInput[ 0 ], targetInput[ 1 ] ).reset(); + + if ( onSelect && onSelect.call( this._.filebrowserSe, fileUrl, data ) === false ) + return; + + // The "data" argument may be used to pass the error message to the editor. + if ( typeof data == 'string' && data ) + alert( data ); + + if ( fileUrl ) + updateTargetElement( fileUrl, this._.filebrowserSe ); + } + + CKEDITOR.plugins.add( 'filebrowser', + { + init : function( editor, pluginPath ) + { + editor._.filebrowserFn = CKEDITOR.tools.addFunction( setUrl, editor ); + + CKEDITOR.on( 'dialogDefinition', function( evt ) + { + // Associate filebrowser to elements with 'filebrowser' attribute. + for ( var i in evt.data.definition.contents ) + { + attachFileBrowser( evt.editor, evt.data.name, evt.data.definition, evt.data.definition.contents[ i ].elements ); + if ( evt.data.definition.contents[ i ].hidden && evt.data.definition.contents[ i ].filebrowser ) + { + evt.data.definition.contents[ i ].hidden = + !isConfigured( evt.data.definition, evt.data.definition.contents[ i ][ 'id' ], evt.data.definition.contents[ i ].filebrowser ); + } + } + } ); + } + } ); + +} )(); diff --git a/plugins/CKEditor/ckeditor/_source/plugins/find/dialogs/find.js b/plugins/CKEditor/ckeditor/_source/plugins/find/dialogs/find.js new file mode 100644 index 00000000000..600fcf844d1 --- /dev/null +++ b/plugins/CKEditor/ckeditor/_source/plugins/find/dialogs/find.js @@ -0,0 +1,846 @@ +/* +Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +(function() +{ + function guardDomWalkerNonEmptyTextNode( node ) + { + return ( node.type == CKEDITOR.NODE_TEXT && node.getLength() > 0 ); + } + + /** + * Elements which break characters been considered as sequence. + */ + function checkCharactersBoundary ( node ) + { + var dtd = CKEDITOR.dtd; + return node.isBlockBoundary( + CKEDITOR.tools.extend( {}, dtd.$empty, dtd.$nonEditable ) ); + } + + /** + * Get the cursor object which represent both current character and it's dom + * position thing. + */ + var cursorStep = function() + { + return { + textNode : this.textNode, + offset : this.offset, + character : this.textNode ? + this.textNode.getText().charAt( this.offset ) : null, + hitMatchBoundary : this._.matchBoundary + }; + }; + + var pages = [ 'find', 'replace' ], + fieldsMapping = [ + [ 'txtFindFind', 'txtFindReplace' ], + [ 'txtFindCaseChk', 'txtReplaceCaseChk' ], + [ 'txtFindWordChk', 'txtReplaceWordChk' ], + [ 'txtFindCyclic', 'txtReplaceCyclic' ] ]; + + /** + * Synchronize corresponding filed values between 'replace' and 'find' pages. + * @param {String} currentPageId The page id which receive values. + */ + function syncFieldsBetweenTabs( currentPageId ) + { + var sourceIndex, targetIndex, + sourceField, targetField; + + sourceIndex = currentPageId === 'find' ? 1 : 0; + targetIndex = 1 - sourceIndex; + var i, l = fieldsMapping.length; + for ( i = 0 ; i < l ; i++ ) + { + sourceField = this.getContentElement( pages[ sourceIndex ], + fieldsMapping[ i ][ sourceIndex ] ); + targetField = this.getContentElement( pages[ targetIndex ], + fieldsMapping[ i ][ targetIndex ] ); + + targetField.setValue( sourceField.getValue() ); + } + } + + var findDialog = function( editor, startupPage ) + { + // Style object for highlights. + var highlightStyle = new CKEDITOR.style( editor.config.find_highlight ); + + /** + * Iterator which walk through the specified range char by char. By + * default the walking will not stop at the character boundaries, until + * the end of the range is encountered. + * @param { CKEDITOR.dom.range } range + * @param {Boolean} matchWord Whether the walking will stop at character boundary. + */ + var characterWalker = function( range , matchWord ) + { + var walker = + new CKEDITOR.dom.walker( range ); + walker[ matchWord ? 'guard' : 'evaluator' ] = + guardDomWalkerNonEmptyTextNode; + walker.breakOnFalse = true; + + this._ = { + matchWord : matchWord, + walker : walker, + matchBoundary : false + }; + }; + + characterWalker.prototype = { + next : function() + { + return this.move(); + }, + + back : function() + { + return this.move( true ); + }, + + move : function( rtl ) + { + var currentTextNode = this.textNode; + // Already at the end of document, no more character available. + if( currentTextNode === null ) + return cursorStep.call( this ); + + this._.matchBoundary = false; + + // There are more characters in the text node, step forward. + if( currentTextNode + && rtl + && this.offset > 0 ) + { + this.offset--; + return cursorStep.call( this ); + } + else if( currentTextNode + && this.offset < currentTextNode.getLength() - 1 ) + { + this.offset++; + return cursorStep.call( this ); + } + else + { + currentTextNode = null; + // At the end of the text node, walking foward for the next. + while ( !currentTextNode ) + { + currentTextNode = + this._.walker[ rtl ? 'previous' : 'next' ].call( this._.walker ); + + // Stop searching if we're need full word match OR + // already reach document end. + if ( this._.matchWord && !currentTextNode + ||this._.walker._.end ) + break; + + // Marking as match character boundaries. + if( !currentTextNode + && checkCharactersBoundary( this._.walker.current ) ) + this._.matchBoundary = true; + + } + // Found a fresh text node. + this.textNode = currentTextNode; + if ( currentTextNode ) + this.offset = rtl ? currentTextNode.getLength() - 1 : 0; + else + this.offset = 0; + } + + return cursorStep.call( this ); + } + + }; + + /** + * A range of cursors which represent a trunk of characters which try to + * match, it has the same length as the pattern string. + */ + var characterRange = function( characterWalker, rangeLength ) + { + this._ = { + walker : characterWalker, + cursors : [], + rangeLength : rangeLength, + highlightRange : null, + isMatched : false + }; + }; + + characterRange.prototype = { + /** + * Translate this range to {@link CKEDITOR.dom.range} + */ + toDomRange : function() + { + var cursors = this._.cursors; + if ( cursors.length < 1 ) + return null; + + var first = cursors[0], + last = cursors[ cursors.length - 1 ], + range = new CKEDITOR.dom.range( editor.document ); + + range.setStart( first.textNode, first.offset ); + range.setEnd( last.textNode, last.offset + 1 ); + return range; + }, + /** + * Reflect the latest changes from dom range. + */ + updateFromDomRange : function( domRange ) + { + var cursor, + walker = new characterWalker( domRange ); + this._.cursors = []; + do + { + cursor = walker.next(); + if ( cursor.character ) + this._.cursors.push( cursor ); + } + while ( cursor.character ); + this._.rangeLength = this._.cursors.length; + }, + + setMatched : function() + { + this._.isMatched = true; + }, + + clearMatched : function() + { + this._.isMatched = false; + }, + + isMatched : function() + { + return this._.isMatched; + }, + + /** + * Hightlight the current matched chunk of text. + */ + highlight : function() + { + // Do not apply if nothing is found. + if ( this._.cursors.length < 1 ) + return; + + // Remove the previous highlight if there's one. + if ( this._.highlightRange ) + this.removeHighlight(); + + // Apply the highlight. + var range = this.toDomRange(); + highlightStyle.applyToRange( range ); + this._.highlightRange = range; + + // Scroll the editor to the highlighted area. + var element = range.startContainer; + if ( element.type != CKEDITOR.NODE_ELEMENT ) + element = element.getParent(); + element.scrollIntoView(); + + // Update the character cursors. + this.updateFromDomRange( range ); + }, + + /** + * Remove highlighted find result. + */ + removeHighlight : function() + { + if ( !this._.highlightRange ) + return; + + highlightStyle.removeFromRange( this._.highlightRange ); + this.updateFromDomRange( this._.highlightRange ); + this._.highlightRange = null; + }, + + moveBack : function() + { + var retval = this._.walker.back(), + cursors = this._.cursors; + + if ( retval.hitMatchBoundary ) + this._.cursors = cursors = []; + + cursors.unshift( retval ); + if ( cursors.length > this._.rangeLength ) + cursors.pop(); + + return retval; + }, + + moveNext : function() + { + var retval = this._.walker.next(), + cursors = this._.cursors; + + // Clear the cursors queue if we've crossed a match boundary. + if ( retval.hitMatchBoundary ) + this._.cursors = cursors = []; + + cursors.push( retval ); + if ( cursors.length > this._.rangeLength ) + cursors.shift(); + + return retval; + }, + + getEndCharacter : function() + { + var cursors = this._.cursors; + if ( cursors.length < 1 ) + return null; + + return cursors[ cursors.length - 1 ].character; + }, + + getNextCharacterRange : function( maxLength ) + { + var lastCursor, + cursors = this._.cursors; + if ( !( lastCursor = cursors[ cursors.length - 1 ] ) ) + return null; + return new characterRange( + new characterWalker( + getRangeAfterCursor( lastCursor ) ), + maxLength ); + }, + + getCursors : function() + { + return this._.cursors; + } + }; + + + // The remaining document range after the character cursor. + function getRangeAfterCursor( cursor , inclusive ) + { + var range = new CKEDITOR.dom.range(); + range.setStart( cursor.textNode, + ( inclusive ? cursor.offset : cursor.offset + 1 ) ); + range.setEndAt( editor.document.getBody(), + CKEDITOR.POSITION_BEFORE_END ); + return range; + } + + // The document range before the character cursor. + function getRangeBeforeCursor( cursor ) + { + var range = new CKEDITOR.dom.range(); + range.setStartAt( editor.document.getBody(), + CKEDITOR.POSITION_AFTER_START ); + range.setEnd( cursor.textNode, cursor.offset ); + return range; + } + + var KMP_NOMATCH = 0, + KMP_ADVANCED = 1, + KMP_MATCHED = 2; + /** + * Examination the occurrence of a word which implement KMP algorithm. + */ + var kmpMatcher = function( pattern, ignoreCase ) + { + var overlap = [ -1 ]; + if ( ignoreCase ) + pattern = pattern.toLowerCase(); + for ( var i = 0 ; i < pattern.length ; i++ ) + { + overlap.push( overlap[i] + 1 ); + while ( overlap[ i + 1 ] > 0 + && pattern.charAt( i ) != pattern + .charAt( overlap[ i + 1 ] - 1 ) ) + overlap[ i + 1 ] = overlap[ overlap[ i + 1 ] - 1 ] + 1; + } + + this._ = { + overlap : overlap, + state : 0, + ignoreCase : !!ignoreCase, + pattern : pattern + }; + }; + + kmpMatcher.prototype = + { + feedCharacter : function( c ) + { + if ( this._.ignoreCase ) + c = c.toLowerCase(); + + while ( true ) + { + if ( c == this._.pattern.charAt( this._.state ) ) + { + this._.state++; + if ( this._.state == this._.pattern.length ) + { + this._.state = 0; + return KMP_MATCHED; + } + return KMP_ADVANCED; + } + else if ( !this._.state ) + return KMP_NOMATCH; + else + this._.state = this._.overlap[ this._.state ]; + } + + return null; + }, + + reset : function() + { + this._.state = 0; + } + }; + + var wordSeparatorRegex = + /[.,"'?!;: \u0085\u00a0\u1680\u280e\u2028\u2029\u202f\u205f\u3000]/; + + var isWordSeparator = function( c ) + { + if ( !c ) + return true; + var code = c.charCodeAt( 0 ); + return ( code >= 9 && code <= 0xd ) + || ( code >= 0x2000 && code <= 0x200a ) + || wordSeparatorRegex.test( c ); + }; + + var finder = { + searchRange : null, + matchRange : null, + find : function( pattern, matchCase, matchWord, matchCyclic, highlightMatched, cyclicRerun ) + { + if( !this.matchRange ) + this.matchRange = + new characterRange( + new characterWalker( this.searchRange ), + pattern.length ); + else + { + this.matchRange.removeHighlight(); + this.matchRange = this.matchRange.getNextCharacterRange( pattern.length ); + } + + var matcher = new kmpMatcher( pattern, !matchCase ), + matchState = KMP_NOMATCH, + character = '%'; + + while ( character !== null ) + { + this.matchRange.moveNext(); + while ( ( character = this.matchRange.getEndCharacter() ) ) + { + matchState = matcher.feedCharacter( character ); + if ( matchState == KMP_MATCHED ) + break; + if ( this.matchRange.moveNext().hitMatchBoundary ) + matcher.reset(); + } + + if ( matchState == KMP_MATCHED ) + { + if ( matchWord ) + { + var cursors = this.matchRange.getCursors(), + tail = cursors[ cursors.length - 1 ], + head = cursors[ 0 ]; + + var headWalker = new characterWalker( getRangeBeforeCursor( head ), true ), + tailWalker = new characterWalker( getRangeAfterCursor( tail ), true ); + + if ( ! ( isWordSeparator( headWalker.back().character ) + && isWordSeparator( tailWalker.next().character ) ) ) + continue; + } + this.matchRange.setMatched(); + if ( highlightMatched !== false ) + this.matchRange.highlight(); + return true; + } + } + + this.matchRange.clearMatched(); + this.matchRange.removeHighlight(); + // Clear current session and restart with the default search + // range. + // Re-run the finding once for cyclic.(#3517) + if ( matchCyclic && !cyclicRerun ) + { + this.searchRange = getSearchRange( true ); + this.matchRange = null; + return arguments.callee.apply( this, + Array.prototype.slice.call( arguments ).concat( [ true ] ) ); + } + + return false; + }, + + /** + * Record how much replacement occurred toward one replacing. + */ + replaceCounter : 0, + + replace : function( dialog, pattern, newString, matchCase, matchWord, + matchCyclic , isReplaceAll ) + { + // Successiveness of current replace/find. + var result = false; + + // 1. Perform the replace when there's already a match here. + // 2. Otherwise perform the find but don't replace it immediately. + if ( this.matchRange && this.matchRange.isMatched() + && !this.matchRange._.isReplaced ) + { + // Turn off highlight for a while when saving snapshots. + this.matchRange.removeHighlight(); + var domRange = this.matchRange.toDomRange(); + var text = editor.document.createText( newString ); + if ( !isReplaceAll ) + { + // Save undo snaps before and after the replacement. + var selection = editor.getSelection(); + selection.selectRanges( [ domRange ] ); + editor.fire( 'saveSnapshot' ); + } + domRange.deleteContents(); + domRange.insertNode( text ); + if ( !isReplaceAll ) + { + selection.selectRanges( [ domRange ] ); + editor.fire( 'saveSnapshot' ); + } + this.matchRange.updateFromDomRange( domRange ); + if ( !isReplaceAll ) + this.matchRange.highlight(); + this.matchRange._.isReplaced = true; + this.replaceCounter++; + result = true; + } + else + result = this.find( pattern, matchCase, matchWord, matchCyclic, !isReplaceAll ); + + return result; + } + }; + + /** + * The range in which find/replace happened, receive from user + * selection prior. + */ + function getSearchRange( isDefault ) + { + var searchRange, + sel = editor.getSelection(), + body = editor.document.getBody(); + if ( sel && !isDefault ) + { + searchRange = sel.getRanges()[ 0 ].clone(); + searchRange.collapse( true ); + } + else + { + searchRange = new CKEDITOR.dom.range(); + searchRange.setStartAt( body, CKEDITOR.POSITION_AFTER_START ); + } + searchRange.setEndAt( body, CKEDITOR.POSITION_BEFORE_END ); + return searchRange; + } + + return { + title : editor.lang.findAndReplace.title, + resizable : CKEDITOR.DIALOG_RESIZE_NONE, + minWidth : 350, + minHeight : 165, + buttons : [ CKEDITOR.dialog.cancelButton ], //Cancel button only. + contents : [ + { + id : 'find', + label : editor.lang.findAndReplace.find, + title : editor.lang.findAndReplace.find, + accessKey : '', + elements : [ + { + type : 'hbox', + widths : [ '230px', '90px' ], + children : + [ + { + type : 'text', + id : 'txtFindFind', + label : editor.lang.findAndReplace.findWhat, + isChanged : false, + labelLayout : 'horizontal', + accessKey : 'F' + }, + { + type : 'button', + align : 'left', + style : 'width:100%', + label : editor.lang.findAndReplace.find, + onClick : function() + { + var dialog = this.getDialog(); + if ( !finder.find( dialog.getValueOf( 'find', 'txtFindFind' ), + dialog.getValueOf( 'find', 'txtFindCaseChk' ), + dialog.getValueOf( 'find', 'txtFindWordChk' ), + dialog.getValueOf( 'find', 'txtFindCyclic' ) ) ) + alert( editor.lang.findAndReplace + .notFoundMsg ); + } + } + ] + }, + { + type : 'vbox', + padding : 0, + children : + [ + { + type : 'checkbox', + id : 'txtFindCaseChk', + isChanged : false, + style : 'margin-top:28px', + label : editor.lang.findAndReplace.matchCase + }, + { + type : 'checkbox', + id : 'txtFindWordChk', + isChanged : false, + label : editor.lang.findAndReplace.matchWord + }, + { + type : 'checkbox', + id : 'txtFindCyclic', + isChanged : false, + 'default' : true, + label : editor.lang.findAndReplace.matchCyclic + } + ] + } + ] + }, + { + id : 'replace', + label : editor.lang.findAndReplace.replace, + accessKey : 'M', + elements : [ + { + type : 'hbox', + widths : [ '230px', '90px' ], + children : + [ + { + type : 'text', + id : 'txtFindReplace', + label : editor.lang.findAndReplace.findWhat, + isChanged : false, + labelLayout : 'horizontal', + accessKey : 'F' + }, + { + type : 'button', + align : 'left', + style : 'width:100%', + label : editor.lang.findAndReplace.replace, + onClick : function() + { + var dialog = this.getDialog(); + if ( !finder.replace( dialog, + dialog.getValueOf( 'replace', 'txtFindReplace' ), + dialog.getValueOf( 'replace', 'txtReplace' ), + dialog.getValueOf( 'replace', 'txtReplaceCaseChk' ), + dialog.getValueOf( 'replace', 'txtReplaceWordChk' ), + dialog.getValueOf( 'replace', 'txtReplaceCyclic' ) ) ) + alert( editor.lang.findAndReplace + .notFoundMsg ); + } + } + ] + }, + { + type : 'hbox', + widths : [ '230px', '90px' ], + children : + [ + { + type : 'text', + id : 'txtReplace', + label : editor.lang.findAndReplace.replaceWith, + isChanged : false, + labelLayout : 'horizontal', + accessKey : 'R' + }, + { + type : 'button', + align : 'left', + style : 'width:100%', + label : editor.lang.findAndReplace.replaceAll, + isChanged : false, + onClick : function() + { + var dialog = this.getDialog(); + var replaceNums; + + finder.replaceCounter = 0; + + // Scope to full document. + finder.searchRange = getSearchRange( true ); + if ( finder.matchRange ) + { + finder.matchRange.removeHighlight(); + finder.matchRange = null; + } + editor.fire( 'saveSnapshot' ); + while( finder.replace( dialog, + dialog.getValueOf( 'replace', 'txtFindReplace' ), + dialog.getValueOf( 'replace', 'txtReplace' ), + dialog.getValueOf( 'replace', 'txtReplaceCaseChk' ), + dialog.getValueOf( 'replace', 'txtReplaceWordChk' ), + false, true ) ) + { /*jsl:pass*/ } + + if ( finder.replaceCounter ) + { + alert( editor.lang.findAndReplace.replaceSuccessMsg.replace( /%1/, finder.replaceCounter ) ); + editor.fire( 'saveSnapshot' ); + } + else + alert( editor.lang.findAndReplace.notFoundMsg ); + } + } + ] + }, + { + type : 'vbox', + padding : 0, + children : + [ + { + type : 'checkbox', + id : 'txtReplaceCaseChk', + isChanged : false, + label : editor.lang.findAndReplace + .matchCase + }, + { + type : 'checkbox', + id : 'txtReplaceWordChk', + isChanged : false, + label : editor.lang.findAndReplace + .matchWord + }, + { + type : 'checkbox', + id : 'txtReplaceCyclic', + isChanged : false, + 'default' : true, + label : editor.lang.findAndReplace + .matchCyclic + } + ] + } + ] + } + ], + onLoad : function() + { + var dialog = this; + + //keep track of the current pattern field in use. + var patternField, wholeWordChkField; + + //Ignore initial page select on dialog show + var isUserSelect = false; + this.on('hide', function() + { + isUserSelect = false; + } ); + this.on('show', function() + { + isUserSelect = true; + } ); + + this.selectPage = CKEDITOR.tools.override( this.selectPage, function( originalFunc ) + { + return function( pageId ) + { + originalFunc.call( dialog, pageId ); + + var currPage = dialog._.tabs[ pageId ]; + var patternFieldInput, patternFieldId, wholeWordChkFieldId; + patternFieldId = pageId === 'find' ? 'txtFindFind' : 'txtFindReplace'; + wholeWordChkFieldId = pageId === 'find' ? 'txtFindWordChk' : 'txtReplaceWordChk'; + + patternField = dialog.getContentElement( pageId, + patternFieldId ); + wholeWordChkField = dialog.getContentElement( pageId, + wholeWordChkFieldId ); + + // prepare for check pattern text filed 'keyup' event + if ( !currPage.initialized ) + { + patternFieldInput = CKEDITOR.document + .getById( patternField._.inputId ); + currPage.initialized = true; + } + + if( isUserSelect ) + // synchronize fields on tab switch. + syncFieldsBetweenTabs.call( this, pageId ); + }; + } ); + + }, + onShow : function() + { + // Establish initial searching start position. + finder.searchRange = getSearchRange(); + + if ( startupPage == 'replace' ) + this.getContentElement( 'replace', 'txtFindReplace' ).focus(); + else + this.getContentElement( 'find', 'txtFindFind' ).focus(); + }, + onHide : function() + { + if ( finder.matchRange && finder.matchRange.isMatched() ) + { + finder.matchRange.removeHighlight(); + editor.focus(); + editor.getSelection().selectRanges( + [ finder.matchRange.toDomRange() ] ); + } + + // Clear current session before dialog close + delete finder.matchRange; + } + }; + }; + + CKEDITOR.dialog.add( 'find', function( editor ) + { + return findDialog( editor, 'find' ); + }); + + CKEDITOR.dialog.add( 'replace', function( editor ) + { + return findDialog( editor, 'replace' ); + }); +})(); diff --git a/plugins/CKEditor/ckeditor/_source/plugins/find/plugin.js b/plugins/CKEditor/ckeditor/_source/plugins/find/plugin.js new file mode 100644 index 00000000000..7b4cecaea3e --- /dev/null +++ b/plugins/CKEditor/ckeditor/_source/plugins/find/plugin.js @@ -0,0 +1,46 @@ +/* +Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +CKEDITOR.plugins.add( 'find', +{ + init : function( editor ) + { + var forms = CKEDITOR.plugins.find; + editor.ui.addButton( 'Find', + { + label : editor.lang.findAndReplace.find, + command : 'find' + }); + var findCommand = editor.addCommand( 'find', new CKEDITOR.dialogCommand( 'find' ) ); + findCommand.canUndo = false; + + editor.ui.addButton( 'Replace', + { + label : editor.lang.findAndReplace.replace, + command : 'replace' + }); + var replaceCommand = editor.addCommand( 'replace', new CKEDITOR.dialogCommand( 'replace' ) ); + replaceCommand.canUndo = false; + + CKEDITOR.dialog.add( 'find', this.path + 'dialogs/find.js' ); + CKEDITOR.dialog.add( 'replace', this.path + 'dialogs/find.js' ); + }, + + requires : [ 'styles' ] +} ); + +/** + * Defines the style to be used to highlight results with the find dialog. + * @type Object + * @default { element : 'span', styles : { 'background-color' : '#004', 'color' : '#fff' } } + * @example + * // Highlight search results with blue on yellow. + * config.find_highlight = + * { + * element : 'span', + * styles : { 'background-color' : '#ff0', 'color' : '#00f' } + * }; + */ +CKEDITOR.config.find_highlight = { element : 'span', styles : { 'background-color' : '#004', 'color' : '#fff' } }; diff --git a/plugins/CKEditor/ckeditor/_source/plugins/flash/dialogs/flash.js b/plugins/CKEditor/ckeditor/_source/plugins/flash/dialogs/flash.js new file mode 100644 index 00000000000..0e74e01e8f0 --- /dev/null +++ b/plugins/CKEditor/ckeditor/_source/plugins/flash/dialogs/flash.js @@ -0,0 +1,688 @@ +/* +Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. +For licensing, see LICENSE.html or http://ckeditor.com/license +*/ + +(function() +{ + /* + * It is possible to set things in three different places. + * 1. As attributes in the object tag. + * 2. As param tags under the object tag. + * 3. As attributes in the embed tag. + * It is possible for a single attribute to be present in more than one place. + * So let's define a mapping between a sementic attribute and its syntactic + * equivalents. + * Then we'll set and retrieve attribute values according to the mapping, + * instead of having to check and set each syntactic attribute every time. + * + * Reference: http://kb.adobe.com/selfservice/viewContent.do?externalId=tn_12701 + */ + var ATTRTYPE_OBJECT = 1, + ATTRTYPE_PARAM = 2, + ATTRTYPE_EMBED = 4; + + var attributesMap = + { + id : [ { type : ATTRTYPE_OBJECT, name : 'id' } ], + classid : [ { type : ATTRTYPE_OBJECT, name : 'classid' } ], + codebase : [ { type : ATTRTYPE_OBJECT, name : 'codebase'} ], + pluginspage : [ { type : ATTRTYPE_EMBED, name : 'pluginspage' } ], + src : [ { type : ATTRTYPE_PARAM, name : 'movie' }, { type : ATTRTYPE_EMBED, name : 'src' } ], + name : [ { type : ATTRTYPE_EMBED, name : 'name' } ], + align : [ { type : ATTRTYPE_OBJECT, name : 'align' } ], + title : [ { type : ATTRTYPE_OBJECT, name : 'title' }, { type : ATTRTYPE_EMBED, name : 'title' } ], + 'class' : [ { type : ATTRTYPE_OBJECT, name : 'class' }, { type : ATTRTYPE_EMBED, name : 'class'} ], + width : [ { type : ATTRTYPE_OBJECT, name : 'width' }, { type : ATTRTYPE_EMBED, name : 'width' } ], + height : [ { type : ATTRTYPE_OBJECT, name : 'height' }, { type : ATTRTYPE_EMBED, name : 'height' } ], + hSpace : [ { type : ATTRTYPE_OBJECT, name : 'hSpace' }, { type : ATTRTYPE_EMBED, name : 'hSpace' } ], + vSpace : [ { type : ATTRTYPE_OBJECT, name : 'vSpace' }, { type : ATTRTYPE_EMBED, name : 'vSpace' } ], + style : [ { type : ATTRTYPE_OBJECT, name : 'style' }, { type : ATTRTYPE_EMBED, name : 'style' } ], + type : [ { type : ATTRTYPE_EMBED, name : 'type' } ] + }; + + var names = [ 'play', 'loop', 'menu', 'quality', 'scale', 'salign', 'wmode', 'bgcolor', 'base', 'flashvars', 'allowScriptAccess', + 'allowFullScreen' ]; + for ( var i = 0 ; i < names.length ; i++ ) + attributesMap[ names[i] ] = [ { type : ATTRTYPE_EMBED, name : names[i] }, { type : ATTRTYPE_PARAM, name : names[i] } ]; + names = [ 'allowFullScreen', 'play', 'loop', 'menu' ]; + for ( i = 0 ; i < names.length ; i++ ) + attributesMap[ names[i] ][0]['default'] = attributesMap[ names[i] ][1]['default'] = true; + + function loadValue( objectNode, embedNode, paramMap ) + { + var attributes = attributesMap[ this.id ]; + if ( !attributes ) + return; + + var isCheckbox = ( this instanceof CKEDITOR.ui.dialog.checkbox ); + for ( var i = 0 ; i < attributes.length ; i++ ) + { + var attrDef = attributes[ i ]; + switch ( attrDef.type ) + { + case ATTRTYPE_OBJECT: + if ( !objectNode ) + continue; + if ( objectNode.getAttribute( attrDef.name ) !== null ) + { + var value = objectNode.getAttribute( attrDef.name ); + if ( isCheckbox ) + this.setValue( value.toLowerCase() == 'true' ); + else + this.setValue( value ); + return; + } + else if ( isCheckbox ) + this.setValue( !!attrDef[ 'default' ] ); + break; + case ATTRTYPE_PARAM: + if ( !objectNode ) + continue; + if ( attrDef.name in paramMap ) + { + value = paramMap[ attrDef.name ]; + if ( isCheckbox ) + this.setValue( value.toLowerCase() == 'true' ); + else + this.setValue( value ); + return; + } + else if ( isCheckbox ) + this.setValue( !!attrDef[ 'default' ] ); + break; + case ATTRTYPE_EMBED: + if ( !embedNode ) + continue; + if ( embedNode.getAttribute( attrDef.name ) ) + { + value = embedNode.getAttribute( attrDef.name ); + if ( isCheckbox ) + this.setValue( value.toLowerCase() == 'true' ); + else + this.setValue( value ); + return; + } + else if ( isCheckbox ) + this.setValue( !!attrDef[ 'default' ] ); + } + } + } + + function commitValue( objectNode, embedNode, paramMap ) + { + var attributes = attributesMap[ this.id ]; + if ( !attributes ) + return; + + var isRemove = ( this.getValue() === '' ), + isCheckbox = ( this instanceof CKEDITOR.ui.dialog.checkbox ); + + for ( var i = 0 ; i < attributes.length ; i++ ) + { + var attrDef = attributes[i]; + switch ( attrDef.type ) + { + case ATTRTYPE_OBJECT: + if ( !objectNode ) + continue; + var value = this.getValue(); + if ( isRemove || isCheckbox && value === attrDef[ 'default' ] ) + objectNode.removeAttribute( attrDef.name ); + else + objectNode.setAttribute( attrDef.name, value ); + break; + case ATTRTYPE_PARAM: + if ( !objectNode ) + continue; + value = this.getValue(); + if ( isRemove || isCheckbox && value === attrDef[ 'default' ] ) + { + if ( attrDef.name in paramMap ) + paramMap[ attrDef.name ].remove(); + } + else + { + if ( attrDef.name in paramMap ) + paramMap[ attrDef.name ].setAttribute( 'value', value ); + else + { + var param = CKEDITOR.dom.element.createFromHtml( '
right now. + // TODO: Support filler for, line break is also occupy line height. + delete blockLikeTags.pre; + var defaultDataFilterRules = + { + attributeNames : + [ + // Event attributes (onXYZ) must not be directly set. They can become + // active in the editing area (IE|WebKit). + [ ( /^on/ ), '_cke_pa_on' ] + ] + }; + + var defaultDataBlockFilterRules = { elements : {} }; + + for ( i in blockLikeTags ) + defaultDataBlockFilterRules.elements[ i ] = extendBlockForDisplay; + + var defaultHtmlFilterRules = + { + elementNames : + [ + // Remove the "cke:" namespace prefix. + [ ( /^cke:/ ), '' ], + + // Ignore tags. + [ ( /^\?xml:namespace$/ ), '' ] + ], + + attributeNames : + [ + // Attributes saved for changes and protected attributes. + [ ( /^_cke_(saved|pa)_/ ), '' ], + + // All "_cke" attributes are to be ignored. + [ ( /^_cke.*/ ), '' ], + + [ 'hidefocus', '' ] + ], + + elements : + { + $ : function( element ) + { + var attribs = element.attributes; + + if ( attribs ) + { + // Elements marked as temporary are to be ignored. + if ( attribs.cke_temp ) + return false; + + // Remove duplicated attributes - #3789. + var attributeNames = [ 'name', 'href', 'src' ], + savedAttributeName; + for ( var i = 0 ; i < attributeNames.length ; i++ ) + { + savedAttributeName = '_cke_saved_' + attributeNames[ i ]; + savedAttributeName in attribs && ( delete attribs[ attributeNames[ i ] ] ); + } + } + + return element; + }, + + embed : function( element ) + { + var parent = element.parent; + + // If the