Index: 3rdParty_sources/aspirin/org/apache/james/core/MailHeaders.java
===================================================================
RCS file: /usr/local/cvsroot/3rdParty_sources/aspirin/org/apache/james/core/MailHeaders.java,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ 3rdParty_sources/aspirin/org/apache/james/core/MailHeaders.java 17 Aug 2012 14:15:44 -0000 1.1
@@ -0,0 +1,148 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache", "Jakarta", "JAMES" and "Apache Software Foundation"
+ * must not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ *
name: value. + * + * @author Federico Barbieri
+ * Return the size of the message including its headers. + * MimeMessage.getSize() method only returns the size of the message body. + *
+ * + *+ * Note: this size is not guaranteed to be accurate - see Sun's + * documentation of MimeMessage.getSize(). + *
+ * + * @return approximate size of full message including headers. + * + * @throws MessagingException + * if a problem occurs while computing the message size + */ + public long getMessageSize() throws MessagingException { + //If we have a MimeMessageWrapper, then we can ask it for just the + // message size and skip calculating it + if (message instanceof MimeMessageWrapper) { + MimeMessageWrapper wrapper = (MimeMessageWrapper) message; + return wrapper.getMessageSize(); + } + //SK: Should probably eventually store this as a locally + // maintained value (so we don't have to load and reparse + // messages each time). + long size = message.getSize(); + Enumeration e = message.getAllHeaderLines(); + while (e.hasMoreElements()) { + size += ((String) e.nextElement()).length(); + } + return size; + } + + /** + * Set the error message associated with this MailImpl. + * + * @param msg + * the new error message associated with this MailImpl + */ + public void setErrorMessage(String msg) { + this.errorMessage = msg; + } + + /** + * Set the MimeMessage associated with this MailImpl. + * + * @param message + * the new MimeMessage associated with this MailImpl + */ + public void setMessage(MimeMessage message) { + this.message = message; + } + + /** + * Set the recipients for this MailImpl. + * + * @param recipients + * the recipients for this MailImpl + */ + public void setRecipients(CollectionObjectInputStream
.
+ *
+ * @param in
+ * the ObjectInputStream from which the object is read
+ *
+ * @throws IOException
+ * if an error occurs while reading from the stream
+ * @throws ClassNotFoundException ?
+ * @throws ClassCastException
+ * if the serialized objects are not of the appropriate type
+ */
+ private void readObject(java.io.ObjectInputStream in) throws IOException,
+ ClassNotFoundException {
+ try {
+ Object obj = in.readObject();
+ if (obj == null) {
+ sender = null;
+ } else if (obj instanceof String) {
+ sender = new MailAddress((String) obj);
+ } else if (obj instanceof MailAddress) {
+ sender = (MailAddress) obj;
+ }
+ } catch (ParseException pe) {
+ throw new IOException("Error parsing sender address: "
+ + pe.getMessage());
+ }
+ recipients = (CollectionObjectOutputStream
.
+ *
+ * @param in
+ * the ObjectOutputStream to which the object is written
+ *
+ * @throws IOException
+ * if an error occurs while writing to the stream
+ */
+ private void writeObject(java.io.ObjectOutputStream out) throws IOException {
+ lastUpdated = new Date();
+ out.writeObject(sender);
+ out.writeObject(recipients);
+ out.writeObject(state);
+ out.writeObject(errorMessage);
+ out.writeObject(name);
+ out.writeObject(remoteHost);
+ out.writeObject(remoteAddr);
+ out.writeObject(lastUpdated);
+ }
+
+ /**
+ * @see org.apache.avalon.framework.activity.Disposable#dispose()
+ */
+ public void dispose() {
+ /*
+ * try { MimeMessage wrapper = getMessage(); if (wrapper instanceof
+ * Disposable) { ((Disposable)wrapper).dispose(); } } catch
+ * (MessagingException me) { // Ignored }
+ */
+ }
+
+}
\ No newline at end of file
Index: 3rdParty_sources/aspirin/org/apache/james/core/MimeMessageInputStreamSource.java
===================================================================
RCS file: /usr/local/cvsroot/3rdParty_sources/aspirin/org/apache/james/core/MimeMessageInputStreamSource.java,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ 3rdParty_sources/aspirin/org/apache/james/core/MimeMessageInputStreamSource.java 17 Aug 2012 14:15:44 -0000 1.1
@@ -0,0 +1,198 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache", "Jakarta", "JAMES" and "Apache Software Foundation"
+ * must not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * InputStream
that contains the bytes of a
+ * MimeMessage.
+ *
+ * @param key the prefix for the name of the temp file
+ * @param in the stream containing the MimeMessage
+ *
+ * @throws MessagingException if an error occurs while trying to store
+ * the stream
+ */
+ public MimeMessageInputStreamSource(String key, InputStream in)
+ throws MessagingException {
+ //We want to immediately read this into a temporary file
+ //Create a temp file and channel the input stream into it
+ OutputStream fout = null;
+ try {
+ file = File.createTempFile(key, ".m64");
+ fout = new BufferedOutputStream(new FileOutputStream(file));
+ int b = -1;
+ while ((b = in.read()) != -1) {
+ fout.write(b);
+ }
+ fout.flush();
+
+ sourceId = file.getCanonicalPath();
+ } catch (IOException ioe) {
+ throw new MessagingException("Unable to retrieve the data: " + ioe.getMessage(), ioe);
+ } finally {
+ try {
+ if (fout != null) {
+ fout.close();
+ }
+ } catch (IOException ioe) {
+ // Ignored - logging unavailable to log this non-fatal error.
+ }
+
+ try {
+ if (in != null) {
+ in.close();
+ }
+ } catch (IOException ioe) {
+ // Ignored - logging unavailable to log this non-fatal error.
+ }
+ }
+ }
+
+ /**
+ * Returns the unique identifier of this input stream source
+ *
+ * @return the unique identifier for this MimeMessageInputStreamSource
+ */
+ public String getSourceId() {
+ return sourceId;
+ }
+
+ /**
+ * Get an input stream to retrieve the data stored in the temporary file
+ *
+ * @return a BufferedInputStream
containing the data
+ */
+ public synchronized InputStream getInputStream() throws IOException {
+ return new BufferedInputStream(new FileInputStream(file));
+ }
+
+ /**
+ * Get the size of the temp file
+ *
+ * @return the size of the temp file
+ *
+ * @throws IOException if an error is encoutered while computing the size of the message
+ */
+ public long getMessageSize() throws IOException {
+ return file.length();
+ }
+
+ /**
+ * @see org.apache.avalon.framework.activity.Disposable#dispose()
+ */
+ public void dispose() {
+ /*try {
+ if (file != null && file.exists()) {
+ file.delete();
+ }
+ } catch (Exception e) {
+ //ignore
+ }
+ file = null;
+ */
+ }
+
+ /**
+ * Finalizer that closes and deletes the temp file. Very bad.
+ * We're leaving this in temporarily, while also establishing a more + * formal mechanism for cleanup through use of the dispose() method. + * + */ + public void finalize() { + dispose(); + } +} Index: 3rdParty_sources/aspirin/org/apache/james/core/MimeMessageSource.java =================================================================== RCS file: /usr/local/cvsroot/3rdParty_sources/aspirin/org/apache/james/core/MimeMessageSource.java,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ 3rdParty_sources/aspirin/org/apache/james/core/MimeMessageSource.java 17 Aug 2012 14:15:44 -0000 1.1 @@ -0,0 +1,128 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2003 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, + * if any, must include the following acknowledgment: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, + * if and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Apache", "Jakarta", "JAMES" and "Apache Software Foundation" + * must not be used to endorse or promote products derived from this + * software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", + * nor may "Apache" appear in their name, without prior written + * permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + *InputStream
containing the data
+ *
+ * @throws IOException if an error occurs while generating the
+ * InputStream
+ */
+ public abstract InputStream getInputStream() throws IOException;
+
+ /**
+ * Return the size of all the data.
+ * Default implementation... others can override to do this much faster
+ *
+ * @return the size of the data represented by this source
+ * @throws IOException if an error is encountered while computing the message size
+ */
+ public long getMessageSize() throws IOException {
+ int size = 0;
+ InputStream in = null;
+ try {
+ in = getInputStream();
+ int read = 0;
+ byte[] data = new byte[1024];
+ while ((read = in.read(data)) > 0) {
+ size += read;
+ }
+ } finally {
+ try {
+ if (in != null) {
+ in.close();
+ }
+ } catch (IOException ioe) {
+ // Exception ignored because logging is
+ // unavailable
+ }
+ }
+ return size;
+ }
+
+}
Index: 3rdParty_sources/aspirin/org/apache/james/core/MimeMessageWrapper.java
===================================================================
RCS file: /usr/local/cvsroot/3rdParty_sources/aspirin/org/apache/james/core/MimeMessageWrapper.java,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ 3rdParty_sources/aspirin/org/apache/james/core/MimeMessageWrapper.java 17 Aug 2012 14:15:44 -0000 1.1
@@ -0,0 +1,1092 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache", "Jakarta", "JAMES" and "Apache Software Foundation"
+ * must not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * javax.mail.internet.MailDateFormat
class.
+ *
+ * @author Serge Knystautas This interface is designed to provide a simplified subset of the
+ * methods provided by the java.text.DateFormat
class.
This interface is necessary because of the difficulty in writing
+ * thread safe classes that inherit from java.text.DateFormat
.
+ * This difficulty leads us to approach the problem using composition
+ * rather than inheritance. In general classes that implement this
+ * interface will delegate these calls to an internal DateFormat object.
String
whose beginning should be parsed.
+ * @return A Date
parsed from the string.
+ * @throws ParseException if the beginning of the specified string
+ * cannot be parsed.
+ */
+ public Date parse(String source) throws ParseException;
+
+ /**
+ * Sets the time zone of this SimplifiedDateFormat object.
+ * @param zone the given new time zone.
+ */
+ public void setTimeZone(TimeZone zone);
+
+ /**
+ * Gets the time zone.
+ * @return the time zone associated with this SimplifiedDateFormat.
+ */
+ public TimeZone getTimeZone();
+
+ /**
+ * Specify whether or not date/time parsing is to be lenient. With
+ * lenient parsing, the parser may use heuristics to interpret inputs that
+ * do not precisely match this object's format. With strict parsing,
+ * inputs must match this object's format.
+ * @param lenient when true, parsing is lenient
+ * @see java.util.Calendar#setLenient
+ */
+ public void setLenient(boolean lenient);
+
+ /**
+ * Tell whether date/time parsing is to be lenient.
+ * @return whether this SimplifiedDateFormat is lenient.
+ */
+ public boolean isLenient();
+}
+
Index: 3rdParty_sources/aspirin/org/apache/james/util/SynchronizedDateFormat.java
===================================================================
RCS file: /usr/local/cvsroot/3rdParty_sources/aspirin/org/apache/james/util/SynchronizedDateFormat.java,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ 3rdParty_sources/aspirin/org/apache/james/util/SynchronizedDateFormat.java 17 Aug 2012 14:15:41 -0000 1.1
@@ -0,0 +1,205 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache", "Jakarta", "JAMES" and "Apache Software Foundation"
+ * must not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * java.text.DateFormat
subclass. In general,
+ * these subclasses (most notably the java.text.SimpleDateFormat
+ * classes are not thread safe, so we need to synchronize on the
+ * internal DateFormat for all delegated calls.
+ *
+ * @author Peter M. Goldstein Wrapper method to allow child classes to synchronize a preexisting + * DateFormat.
+ * + *TODO: Investigate replacing this with a factory method.
+ * + * @param the DateFormat to synchronize + */ + protected SynchronizedDateFormat(DateFormat theDateFormat) { + internalDateFormat = theDateFormat; + } + + /** + * SimpleDateFormat will handle most of this for us, but we + * want to ensure thread safety, so we wrap the call in a + * synchronized block. + * + * @return java.lang.String + * @param d Date + */ + public String format(Date d) { + synchronized (internalDateFormat) { + return internalDateFormat.format(d); + } + } + + /** + * Parses text from the beginning of the given string to produce a date. + * The method may not use the entire text of the given string. + *
+ * This method is designed to be thread safe, so we wrap our delegated
+ * parse method in an appropriate synchronized block.
+ *
+ * @param source A This class encapsulates functionalities to access to different
+ * parts of an email address without dealing with its parsing. A MailAddress is an address specified in the MAIL FROM and
+ * RCPT TO commands in SMTP sessions. These are either passed by
+ * an external server to the mailet-compliant SMTP server, or they
+ * are created programmatically by the mailet-compliant server to
+ * send to another (external) SMTP server. Mailets and matchers
+ * use the MailAddress for the purpose of evaluating the sender
+ * and recipient(s) of a message. MailAddress parses an email address as defined in RFC 821
+ * (SMTP) p. 30 and 31 where addresses are defined in BNF convention.
+ * As the mailet API does not support the aged "SMTP-relayed mail"
+ * addressing protocol, this leaves all addresses to be a This class is a good way to validate email addresses as there are
+ * some valid addresses which would fail with a simpler approach
+ * to parsing address. It also removes parsing burden from
+ * mailets and matchers that might not realize the flexibility of an
+ * SMTP address. For instance, "serge@home"@lokitech.com is a valid
+ * SMTP address (the quoted text serge@home is the user and
+ * lokitech.com is the host). This means all current parsing to date
+ * is incorrect as we just find the first @ and use that to separate
+ * user from host. This parses an address as per the BNF specification for Construct a MailAddress parsing the provided The This class represents the configuration of Aspirin. You can configure this
+ * software two ways: There is a way to change behavior of Aspirin dinamically. You can use
+ * JMX to change configuration parameters. In the parameters list we marked the
+ * parameters which are applied immediately. For more informations view
+ * {@link ConfigurationMBean}. This interface is part of configuration subsystem. If a configuration
+ * parameter was changed, the classes which implements this interface could be
+ * notified about these changes. It is requried to allow immediately dynamic
+ * configuration. This is the JMX bean of Aspirin configuration. Some configuration
+ * parameter could be applied immediately. This object handles the RemoteDelivery thread objects in the ObjectPool.
+ * Initialization of this Factory. Prerequisite of right working. This class represents the mailing queue of the Aspirin. This is a listener interface. This defines the mail delivery listeners,
+ * which could get messages if an email is delivered to a recipient (with the
+ * delivery result) and if an email is delivered to all recipients. This object is the manager, the main class of mail delivering. This thread You can set the next QuedItem to deliver with this method. It wakes
+ * up this delivery thread which try to deliver the QuedItem set. This method sets the parent pool, which this thread is given back
+ * into after finishing delivery. This method gives back the host name(s) where we can send the email.
+ * First time we ask DNS to find MX record(s) of a domain name. If no MX
+ * records are found, we check the upper level domains (if exists). At last
+ * we try to get the domain A record, because the MX server could be same as
+ * the normal domain handler server. If only upper level domain has MX
+ * record then we append the A record of original hostname (if exists) as
+ * first element of record collection. If none of these tries are
+ * successful, we give back an empty collection. ... [text...), by comparing the document position
+ // with 'enlargeable' node.
+ this.setStartAt(
+ blockBoundary,
+ !blockBoundary.is( 'br' ) &&
+ ( !enlargeable && this.checkStartOfBlock()
+ || enlargeable && blockBoundary.contains( enlargeable ) ) ?
+ CKEDITOR.POSITION_AFTER_START :
+ CKEDITOR.POSITION_AFTER_END );
+
+ // Avoid enlarging the range further when end boundary spans right after the BR. (#7490)
+ if ( unit == CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS )
+ {
+ var theRange = this.clone();
+ walker = new CKEDITOR.dom.walker( theRange );
+
+ var whitespaces = CKEDITOR.dom.walker.whitespaces(),
+ bookmark = CKEDITOR.dom.walker.bookmark();
+
+ walker.evaluator = function( node ) { return !whitespaces( node ) && !bookmark( node ); };
+ var previous = walker.previous();
+ if ( previous && previous.type == CKEDITOR.NODE_ELEMENT && previous.is( 'br' ) )
+ return;
+ }
+
+
+ // Enlarging the end boundary.
+ walkerRange = this.clone();
+ walkerRange.collapse();
+ walkerRange.setEndAt( body, CKEDITOR.POSITION_BEFORE_END );
+ walker = new CKEDITOR.dom.walker( walkerRange );
+
+ // tailBrGuard only used for on range end.
+ walker.guard = ( unit == CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS ) ?
+ tailBrGuard : boundaryGuard;
+ blockBoundary = null;
+ // End the range right before the block boundary node.
+
+ enlargeable = walker.lastForward();
+
+ // It's the body which stop the enlarging if no block boundary found.
+ blockBoundary = blockBoundary || body;
+
+ // Close the range either before the found block start (text] ...String
whose beginning should be parsed.
+ * @return A Date
parsed from the string.
+ * @throws ParseException if the beginning of the specified string
+ * cannot be parsed.
+ */
+ public Date parse(String source) throws ParseException {
+ synchronized (internalDateFormat) {
+ return internalDateFormat.parse(source);
+ }
+ }
+
+ /**
+ * Sets the time zone of this SynchronizedDateFormat object.
+ * @param zone the given new time zone.
+ */
+ public void setTimeZone(TimeZone zone) {
+ synchronized(internalDateFormat) {
+ internalDateFormat.setTimeZone(zone);
+ }
+ }
+
+ /**
+ * Gets the time zone.
+ * @return the time zone associated with this SynchronizedDateFormat.
+ */
+ public TimeZone getTimeZone() {
+ synchronized(internalDateFormat) {
+ return internalDateFormat.getTimeZone();
+ }
+ }
+
+ /**
+ * Specify whether or not date/time parsing is to be lenient. With
+ * lenient parsing, the parser may use heuristics to interpret inputs that
+ * do not precisely match this object's format. With strict parsing,
+ * inputs must match this object's format.
+ * @param lenient when true, parsing is lenient
+ * @see java.util.Calendar#setLenient
+ */
+ public void setLenient(boolean lenient)
+ {
+ synchronized(internalDateFormat) {
+ internalDateFormat.setLenient(lenient);
+ }
+ }
+
+ /**
+ * Tell whether date/time parsing is to be lenient.
+ * @return whether this SynchronizedDateFormat is lenient.
+ */
+ public boolean isLenient()
+ {
+ synchronized(internalDateFormat) {
+ return internalDateFormat.isLenient();
+ }
+ }
+
+ /**
+ * Overrides hashCode
+ */
+ public int hashCode() {
+ synchronized(internalDateFormat) {
+ return internalDateFormat.hashCode();
+ }
+ }
+
+ /**
+ * Overrides equals
+ */
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ synchronized(internalDateFormat) {
+ return internalDateFormat.equals(obj);
+ }
+ }
+
+}
Index: 3rdParty_sources/aspirin/org/apache/mailet/Mail.java
===================================================================
RCS file: /usr/local/cvsroot/3rdParty_sources/aspirin/org/apache/mailet/Mail.java,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ 3rdParty_sources/aspirin/org/apache/mailet/Mail.java 17 Aug 2012 14:15:49 -0000 1.1
@@ -0,0 +1,153 @@
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache", "Jakarta", "JAMES" and "Apache Software Foundation"
+ * must not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * String
object.personal
variable is left empty.String
object representing the host part
+ * of this email address. If the host is of the dotNum form
+ * (e.g. [yyy.yyy.yyy.yyy]) then strip the braces first.
+ */
+ public String getHost() {
+ if (!(host.startsWith("[") && host.endsWith("]"))) {
+ return host;
+ } else {
+ return host.substring(1, host.length() -1);
+ }
+ }
+
+ /**
+ * Return the user part.
+ *
+ * @return a String
object representing the user part
+ * of this email address.
+ * @throws AddressException if the parse failed
+ */
+ public String getUser() {
+ return user;
+ }
+
+ public String toString() {
+ StringBuffer addressBuffer =
+ new StringBuffer(128)
+ .append(user)
+ .append("@")
+ .append(host);
+ return addressBuffer.toString();
+ }
+
+ public InternetAddress toInternetAddress() {
+ try {
+ return new InternetAddress(toString());
+ } catch (javax.mail.internet.AddressException ae) {
+ //impossible really
+ return null;
+ }
+ }
+
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ } else if (obj instanceof String) {
+ String theString = (String)obj;
+ return toString().equalsIgnoreCase(theString);
+ } else if (obj instanceof MailAddress) {
+ MailAddress addr = (MailAddress)obj;
+ return getUser().equalsIgnoreCase(addr.getUser()) && getHost().equalsIgnoreCase(addr.getHost());
+ }
+ return false;
+ }
+
+ /**
+ * Return a hashCode for this object which should be identical for addresses
+ * which are equivalent. This is implemented by obtaining the default
+ * hashcode of the String representation of the MailAddress. Without this
+ * explicit definition, the default hashCode will create different hashcodes
+ * for separate object instances.
+ *
+ * @return the hashcode.
+ */
+ public int hashCode() {
+ return toString().toLowerCase(Locale.US).hashCode();
+ }
+
+ private String parseQuotedLocalPart(String address) throws ParseException {
+ StringBuffer resultSB = new StringBuffer();
+ resultSB.append('\"');
+ pos++;
+ // |
::= any one of the 128 ASCII characters except
+ *
+ *
+ *
+ *
+ *
+ * @author Kate Rhodes masukomi at masukomi dot org
+ * @version $Id: Configuration.java,v 1.1 2012/08/17 14:15:37 marcin Exp $
+ */
+public class Configuration implements ConfigurationMBean {
+
+ private static Configuration instance;
+ private int maxAttempts = 3; // aspirin.delivery.attempt.count
+ private long retryInterval = 300000; // aspirin.delivery.attempt.delay
+ private boolean debugCommunication = false; // aspirin.delivery.debug
+ private String hostname = "localhost"; // aspirin.delivery.hostname
+ private int deliveryThreads = 3; // aspirin.delivery.threads.active.max
+ private int idleDeliveryThreads = deliveryThreads; // aspirin.delivery.threads.idle.max
+ private int connectionTimeout = 30000; // in milliseconds, aspirin.delivery.timeout
+ private String encoding = "UTF-8"; // aspirin.encoding
+ private static String loggerName = "Aspirin"; // aspirin.logger.name
+ private static Log log = LogFactory.getLog(loggerName); // inherited from aspirin.logger.name
+ private String loggerPrefix = "Aspirin "; // aspirin.logger.prefix
+ protected MailAddress postmaster = null; // inherited from aspirin.postmaster.email
+
+ private List
+ *
+ * Name
+ * Deprecated name
+ * Type
+ * Description
+ *
+ *
+ * aspirin.delivery.attempt.delay
+ * aspirinRetryInterval
+ * Integer
+ * The delay of next attempt to delivery in milliseconds. Change by
+ * JMX applied immediately.
+ *
+ *
+ * aspirin.delivery.attempt.count
+ * aspirinMaxAttempts
+ * Integer
+ * Maximal number of delivery attempts of an email. Change by JMX
+ * applied immediately.
+ *
+ *
+ * aspirin.delivery.debug
+ *
+ * Boolean
+ * If true, full SMTP communication will be logged. Change by JMX
+ * applied immediately.
+ *
+ *
+ * aspirin.delivery.threads.active.max
+ * aspirinDeliverThreads
+ * Integer
+ * Maximum number of active delivery threads in the pool. Change by
+ * JMX applied immediately.
+ *
+ *
+ * aspirin.delivery.threads.idle.max
+ * aspirinDeliverThreads
+ * Integer
+ * Maximum number of idle delivery threads in the pool (the deilvery
+ * threads over this limit will be shutdown). Change by JMX applied
+ * immediately.
+ *
+ *
+ * aspirin.delivery.timeout
+ *
+ * Integer
+ * Socket and {@link Transport} timeout in milliseconds. Change by
+ * JMX applied immediately.
+ *
+ *
+ * aspirin.encoding
+ *
+ * String
+ * The MIME encoding. Change by JMX applied immediately.
+ *
+ *
+ * aspirin.hostname
+ * aspirinHostname
+ * String
+ * The hostname. Change by JMX applied immediately.
+ *
+ *
+ * aspirin.logger.name
+ *
+ * String
+ *
+ * The name of the logger. Change by JMX applied immediately.
+ *
+ *
+ * WARNING! Changing logger name cause replacing of logger.
+ *
+ *
+ * aspirin.logger.prefix
+ *
+ * String
+ * The prefix of the logger. This will be put in the logs at the first
+ * position. Change by JMX applied immediately.
+ *
+ *
+ * aspirin.postmaster.email
+ * aspirinPostmaster
+ * String
+ * The email address of the postmaster. Change by JMX applied
+ * immediately.
+ *
+ *
+ * This configuration option is global and executed during the jQuery Adapter loading.
+ * It can't be customized across editor instances.
+ * @type Boolean
+ * @example
+ * <script>
+ * CKEDITOR.config.jqueryOverrideVal = true;
+ * </script>
+ * <!-- Important: The JQuery adapter is loaded *after* setting jqueryOverrideVal -->
+ * <script src="/ckeditor/adapters/jquery.js"></script>
+ * @example
+ * // ... then later in the code ...
+ *
+ * $( 'textarea' ).ckeditor();
+ * // ...
+ * $( 'textarea' ).val( 'New content' );
+ */
+ CKEDITOR.config.jqueryOverrideVal = typeof CKEDITOR.config.jqueryOverrideVal == 'undefined'
+ ? true : CKEDITOR.config.jqueryOverrideVal;
+
+ var jQuery = window.jQuery;
+
+ if ( typeof jQuery == 'undefined' )
+ return;
+
+ // jQuery object methods.
+ jQuery.extend( jQuery.fn,
+ /** @lends jQuery.fn */
+ {
+ /**
+ * Return existing CKEditor instance for first matched element.
+ * Allows to easily use internal API. Doesn't return jQuery object.
+ *
+ * Raised exception if editor doesn't exist or isn't ready yet.
+ *
+ * @name jQuery.ckeditorGet
+ * @return CKEDITOR.editor
+ * @see CKEDITOR.editor
+ */
+ ckeditorGet: function()
+ {
+ var instance = this.eq( 0 ).data( 'ckeditorInstance' );
+ if ( !instance )
+ throw "CKEditor not yet initialized, use ckeditor() with callback.";
+ return instance;
+ },
+ /**
+ * Triggers creation of CKEditor in all matched elements (reduced to DIV, P and TEXTAREAs).
+ * Binds callback to instanceReady event of all instances. If editor is already created, than
+ * callback is fired right away.
+ *
+ * Mixed parameter order allowed.
+ *
+ * @param callback Function to be run on editor instance. Passed parameters: [ textarea ].
+ * Callback is fiered in "this" scope being ckeditor instance and having source textarea as first param.
+ *
+ * @param config Configuration options for new instance(s) if not already created.
+ * See URL
+ *
+ * @example
+ * $( 'textarea' ).ckeditor( function( textarea ) {
+ * $( textarea ).val( this.getData() )
+ * } );
+ *
+ * @name jQuery.fn.ckeditor
+ * @return jQuery.fn
+ */
+ ckeditor: function( callback, config )
+ {
+ if ( !CKEDITOR.env.isCompatible )
+ return this;
+
+ if ( !jQuery.isFunction( callback ))
+ {
+ var tmp = config;
+ config = callback;
+ callback = tmp;
+ }
+ config = config || {};
+
+ this.filter( 'textarea, div, p' ).each( function()
+ {
+ var $element = jQuery( this ),
+ editor = $element.data( 'ckeditorInstance' ),
+ instanceLock = $element.data( '_ckeditorInstanceLock' ),
+ element = this;
+
+ if ( editor && !instanceLock )
+ {
+ if ( callback )
+ callback.apply( editor, [ this ] );
+ }
+ else if ( !instanceLock )
+ {
+ // CREATE NEW INSTANCE
+
+ // Handle config.autoUpdateElement inside this plugin if desired.
+ if ( config.autoUpdateElement
+ || ( typeof config.autoUpdateElement == 'undefined' && CKEDITOR.config.autoUpdateElement ) )
+ {
+ config.autoUpdateElementJquery = true;
+ }
+
+ // Always disable config.autoUpdateElement.
+ config.autoUpdateElement = false;
+ $element.data( '_ckeditorInstanceLock', true );
+
+ // Set instance reference in element's data.
+ editor = CKEDITOR.replace( element, config );
+ $element.data( 'ckeditorInstance', editor );
+
+ // Register callback.
+ editor.on( 'instanceReady', function( event )
+ {
+ var editor = event.editor;
+ setTimeout( function()
+ {
+ // Delay bit more if editor is still not ready.
+ if ( !editor.element )
+ {
+ setTimeout( arguments.callee, 100 );
+ return;
+ }
+
+ // Remove this listener.
+ event.removeListener( 'instanceReady', this.callee );
+
+ // Forward setData on dataReady.
+ editor.on( 'dataReady', function()
+ {
+ $element.trigger( 'setData' + '.ckeditor', [ editor ] );
+ });
+
+ // Forward getData.
+ editor.on( 'getData', function( event ) {
+ $element.trigger( 'getData' + '.ckeditor', [ editor, event.data ] );
+ }, 999 );
+
+ // Forward destroy event.
+ editor.on( 'destroy', function()
+ {
+ $element.trigger( 'destroy.ckeditor', [ editor ] );
+ });
+
+ // Integrate with form submit.
+ if ( editor.config.autoUpdateElementJquery && $element.is( 'textarea' ) && $element.parents( 'form' ).length )
+ {
+ var onSubmit = function()
+ {
+ $element.ckeditor( function()
+ {
+ editor.updateElement();
+ });
+ };
+
+ // Bind to submit event.
+ $element.parents( 'form' ).submit( onSubmit );
+
+ // Bind to form-pre-serialize from jQuery Forms plugin.
+ $element.parents( 'form' ).bind( 'form-pre-serialize', onSubmit );
+
+ // Unbind when editor destroyed.
+ $element.bind( 'destroy.ckeditor', function()
+ {
+ $element.parents( 'form' ).unbind( 'submit', onSubmit );
+ $element.parents( 'form' ).unbind( 'form-pre-serialize', onSubmit );
+ });
+ }
+
+ // Garbage collect on destroy.
+ editor.on( 'destroy', function()
+ {
+ $element.data( 'ckeditorInstance', null );
+ });
+
+ // Remove lock.
+ $element.data( '_ckeditorInstanceLock', null );
+
+ // Fire instanceReady event.
+ $element.trigger( 'instanceReady.ckeditor', [ editor ] );
+
+ // Run given (first) code.
+ if ( callback )
+ callback.apply( editor, [ element ] );
+ }, 0 );
+ }, null, null, 9999);
+ }
+ else
+ {
+ // Editor is already during creation process, bind our code to the event.
+ CKEDITOR.on( 'instanceReady', function( event )
+ {
+ var editor = event.editor;
+ setTimeout( function()
+ {
+ // Delay bit more if editor is still not ready.
+ if ( !editor.element )
+ {
+ setTimeout( arguments.callee, 100 );
+ return;
+ }
+
+ if ( editor.element.$ == element )
+ {
+ // Run given code.
+ if ( callback )
+ callback.apply( editor, [ element ] );
+ }
+ }, 0 );
+ }, null, null, 9999);
+ }
+ });
+ return this;
+ }
+ });
+
+ // New val() method for objects.
+ if ( CKEDITOR.config.jqueryOverrideVal )
+ {
+ jQuery.fn.val = CKEDITOR.tools.override( jQuery.fn.val, function( oldValMethod )
+ {
+ /**
+ * CKEditor-aware val() method.
+ *
+ * Acts same as original jQuery val(), but for textareas which have CKEditor instances binded to them, method
+ * returns editor's content. It also works for settings values.
+ *
+ * @param oldValMethod
+ * @name jQuery.fn.val
+ */
+ return function( newValue, forceNative )
+ {
+ var isSetter = typeof newValue != 'undefined',
+ result;
+
+ this.each( function()
+ {
+ var $this = jQuery( this ),
+ editor = $this.data( 'ckeditorInstance' );
+
+ if ( !forceNative && $this.is( 'textarea' ) && editor )
+ {
+ if ( isSetter )
+ editor.setData( newValue );
+ else
+ {
+ result = editor.getData();
+ // break;
+ return null;
+ }
+ }
+ else
+ {
+ if ( isSetter )
+ oldValMethod.call( $this, newValue );
+ else
+ {
+ result = oldValMethod.call( $this );
+ // break;
+ return null;
+ }
+ }
+
+ return true;
+ });
+ return isSetter ? this : result;
+ };
+ });
+ }
+})();
Index: 3rdParty_sources/ckeditor/core/_bootstrap.js
===================================================================
RCS file: /usr/local/cvsroot/3rdParty_sources/ckeditor/core/_bootstrap.js,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ 3rdParty_sources/ckeditor/core/_bootstrap.js 17 Aug 2012 14:15:40 -0000 1.1
@@ -0,0 +1,87 @@
+/*
+Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview API initialization code.
+ */
+
+(function()
+{
+ // Disable HC detaction in WebKit. (#5429)
+ if ( CKEDITOR.env.webkit )
+ {
+ CKEDITOR.env.hc = false;
+ return;
+ }
+
+ // Check whether high contrast is active by creating a colored border.
+ var hcDetect = CKEDITOR.dom.element.createFromHtml(
+ '', CKEDITOR.document );
+
+ hcDetect.appendTo( CKEDITOR.document.getHead() );
+
+ // Update CKEDITOR.env.
+ // Catch exception needed sometimes for FF. (#4230)
+ try
+ {
+ CKEDITOR.env.hc = hcDetect.getComputedStyle( 'border-top-color' ) == hcDetect.getComputedStyle( 'border-right-color' );
+ }
+ catch (e)
+ {
+ CKEDITOR.env.hc = false;
+ }
+
+ if ( CKEDITOR.env.hc )
+ CKEDITOR.env.cssClass += ' cke_hc';
+
+ hcDetect.remove();
+})();
+
+// Load core plugins.
+CKEDITOR.plugins.load( CKEDITOR.config.corePlugins.split( ',' ), function()
+ {
+ CKEDITOR.status = 'loaded';
+ CKEDITOR.fire( 'loaded' );
+
+ // Process all instances created by the "basic" implementation.
+ var pending = CKEDITOR._.pending;
+ if ( pending )
+ {
+ delete CKEDITOR._.pending;
+
+ for ( var i = 0 ; i < pending.length ; i++ )
+ CKEDITOR.add( pending[ i ] );
+ }
+ });
+
+// Needed for IE6 to not request image (HTTP 200 or 304) for every CSS background. (#6187)
+if ( CKEDITOR.env.ie )
+{
+ // Remove IE mouse flickering on IE6 because of background images.
+ try
+ {
+ document.execCommand( 'BackgroundImageCache', false, true );
+ }
+ catch (e)
+ {
+ // We have been reported about loading problems caused by the above
+ // line. For safety, let's just ignore errors.
+ }
+}
+
+/**
+ * Indicates that CKEditor is running on a High Contrast environment.
+ * @name CKEDITOR.env.hc
+ * @example
+ * if ( CKEDITOR.env.hc )
+ * alert( 'You're running on High Contrast mode. The editor interface will get adapted to provide you a better experience.' );
+ */
+
+/**
+ * Fired when a CKEDITOR core object is fully loaded and ready for interaction.
+ * @name CKEDITOR#loaded
+ * @event
+ */
Index: 3rdParty_sources/ckeditor/core/ckeditor.js
===================================================================
RCS file: /usr/local/cvsroot/3rdParty_sources/ckeditor/core/ckeditor.js,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ 3rdParty_sources/ckeditor/core/ckeditor.js 17 Aug 2012 14:15:40 -0000 1.1
@@ -0,0 +1,141 @@
+/*
+Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Contains the third and last part of the {@link CKEDITOR} object
+ * definition.
+ */
+
+// Remove the CKEDITOR.loadFullCore reference defined on ckeditor_basic.
+delete CKEDITOR.loadFullCore;
+
+/**
+ * Holds references to all editor instances created. The name of the properties
+ * in this object correspond to instance names, and their values contains the
+ * {@link CKEDITOR.editor} object representing them.
+ * @type {Object}
+ * @example
+ * alert( CKEDITOR.instances.editor1.name ); // "editor1"
+ */
+CKEDITOR.instances = {};
+
+/**
+ * The document of the window holding the CKEDITOR object.
+ * @type {CKEDITOR.dom.document}
+ * @example
+ * alert( CKEDITOR.document.getBody().getName() ); // "body"
+ */
+CKEDITOR.document = new CKEDITOR.dom.document( document );
+
+/**
+ * Adds an editor instance to the global {@link CKEDITOR} object. This function
+ * is available for internal use mainly.
+ * @param {CKEDITOR.editor} editor The editor instance to be added.
+ * @example
+ */
+CKEDITOR.add = function( editor )
+{
+ CKEDITOR.instances[ editor.name ] = editor;
+
+ editor.on( 'focus', function()
+ {
+ if ( CKEDITOR.currentInstance != editor )
+ {
+ CKEDITOR.currentInstance = editor;
+ CKEDITOR.fire( 'currentInstance' );
+ }
+ });
+
+ editor.on( 'blur', function()
+ {
+ if ( CKEDITOR.currentInstance == editor )
+ {
+ CKEDITOR.currentInstance = null;
+ CKEDITOR.fire( 'currentInstance' );
+ }
+ });
+};
+
+/**
+ * Removes an editor instance from the global {@link CKEDITOR} object. This function
+ * is available for internal use only. External code must use {@link CKEDITOR.editor.prototype.destroy}
+ * to avoid memory leaks.
+ * @param {CKEDITOR.editor} editor The editor instance to be removed.
+ * @example
+ */
+CKEDITOR.remove = function( editor )
+{
+ delete CKEDITOR.instances[ editor.name ];
+};
+
+/**
+ * Perform global clean up to free as much memory as possible
+ * when there are no instances left
+ */
+CKEDITOR.on( 'instanceDestroyed', function ()
+ {
+ if ( CKEDITOR.tools.isEmpty( this.instances ) )
+ CKEDITOR.fire( 'reset' );
+ });
+
+// Load the bootstrap script.
+CKEDITOR.loader.load( 'core/_bootstrap' ); // @Packager.RemoveLine
+
+// Tri-state constants.
+
+/**
+ * Used to indicate the ON or ACTIVE state.
+ * @constant
+ * @example
+ */
+CKEDITOR.TRISTATE_ON = 1;
+
+/**
+ * Used to indicate the OFF or NON ACTIVE state.
+ * @constant
+ * @example
+ */
+CKEDITOR.TRISTATE_OFF = 2;
+
+/**
+ * Used to indicate DISABLED state.
+ * @constant
+ * @example
+ */
+CKEDITOR.TRISTATE_DISABLED = 0;
+
+/**
+ * The editor which is currently active (have user focus).
+ * @name CKEDITOR.currentInstance
+ * @type CKEDITOR.editor
+ * @see CKEDITOR#currentInstance
+ * @example
+ * function showCurrentEditorName()
+ * {
+ * if ( CKEDITOR.currentInstance )
+ * alert( CKEDITOR.currentInstance.name );
+ * else
+ * alert( 'Please focus an editor first.' );
+ * }
+ */
+
+/**
+ * Fired when the CKEDITOR.currentInstance object reference changes. This may
+ * happen when setting the focus on different editor instances in the page.
+ * @name CKEDITOR#currentInstance
+ * @event
+ * var editor; // Variable to hold a reference to the current editor.
+ * CKEDITOR.on( 'currentInstance' , function( e )
+ * {
+ * editor = CKEDITOR.currentInstance;
+ * });
+ */
+
+/**
+ * Fired when the last instance has been destroyed. This event is used to perform
+ * global memory clean up.
+ * @name CKEDITOR#reset
+ * @event
+ */
Index: 3rdParty_sources/ckeditor/core/ckeditor_base.js
===================================================================
RCS file: /usr/local/cvsroot/3rdParty_sources/ckeditor/core/ckeditor_base.js,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ 3rdParty_sources/ckeditor/core/ckeditor_base.js 17 Aug 2012 14:15:40 -0000 1.1
@@ -0,0 +1,227 @@
+/*
+Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Contains the first and essential part of the {@link CKEDITOR}
+ * object definition.
+ */
+
+// #### Compressed Code
+// Must be updated on changes in the script as well as updated in the
+// ckeditor_source.js and ckeditor_basic_source.js files.
+
+// if(!window.CKEDITOR)window.CKEDITOR=(function(){var a={timestamp:'',version:'3.6.2',rev:'7275',_:{},status:'unloaded',basePath:(function(){var d=window.CKEDITOR_BASEPATH||'';if(!d){var e=document.getElementsByTagName('script');for(var f=0;f
+ *
+ * @type String
+ * @example
+ * if ( CKEDITOR.status == 'loaded' )
+ * {
+ * // The API can now be fully used.
+ * }
+ */
+ status : 'unloaded',
+
+ /**
+ * Contains the full URL for the CKEditor installation directory.
+ * It is possible to manually provide the base path by setting a
+ * global variable named CKEDITOR_BASEPATH. This global variable
+ * must be set before the editor script loading.
+ * @type String
+ * @example
+ * alert( CKEDITOR.basePath ); // "http://www.example.com/ckeditor/" (e.g.)
+ */
+ basePath : (function()
+ {
+ // ATTENTION: fixes to this code must be ported to
+ // var basePath in "core/loader.js".
+
+ // Find out the editor directory path, based on its ")' );
+ }
+ }
+
+ return $ && new CKEDITOR.dom.document( $.contentWindow.document );
+ },
+
+ /**
+ * Copy all the attributes from one node to the other, kinda like a clone
+ * skipAttributes is an object with the attributes that must NOT be copied.
+ * @param {CKEDITOR.dom.element} dest The destination element.
+ * @param {Object} skipAttributes A dictionary of attributes to skip.
+ * @example
+ */
+ copyAttributes : function( dest, skipAttributes )
+ {
+ var attributes = this.$.attributes;
+ skipAttributes = skipAttributes || {};
+
+ for ( var n = 0 ; n < attributes.length ; n++ )
+ {
+ var attribute = attributes[n];
+
+ // Lowercase attribute name hard rule is broken for
+ // some attribute on IE, e.g. CHECKED.
+ var attrName = attribute.nodeName.toLowerCase(),
+ attrValue;
+
+ // We can set the type only once, so do it with the proper value, not copying it.
+ if ( attrName in skipAttributes )
+ continue;
+
+ if ( attrName == 'checked' && ( attrValue = this.getAttribute( attrName ) ) )
+ dest.setAttribute( attrName, attrValue );
+ // IE BUG: value attribute is never specified even if it exists.
+ else if ( attribute.specified ||
+ ( CKEDITOR.env.ie && attribute.nodeValue && attrName == 'value' ) )
+ {
+ attrValue = this.getAttribute( attrName );
+ if ( attrValue === null )
+ attrValue = attribute.nodeValue;
+
+ dest.setAttribute( attrName, attrValue );
+ }
+ }
+
+ // The style:
+ if ( this.$.style.cssText !== '' )
+ dest.$.style.cssText = this.$.style.cssText;
+ },
+
+ /**
+ * Changes the tag name of the current element.
+ * @param {String} newTag The new tag for the element.
+ */
+ renameNode : function( newTag )
+ {
+ // If it's already correct exit here.
+ if ( this.getName() == newTag )
+ return;
+
+ var doc = this.getDocument();
+
+ // Create the new node.
+ var newNode = new CKEDITOR.dom.element( newTag, doc );
+
+ // Copy all attributes.
+ this.copyAttributes( newNode );
+
+ // Move children to the new node.
+ this.moveChildren( newNode );
+
+ // Replace the node.
+ this.getParent() && this.$.parentNode.replaceChild( newNode.$, this.$ );
+ newNode.$[ 'data-cke-expando' ] = this.$[ 'data-cke-expando' ];
+ this.$ = newNode.$;
+ },
+
+ /**
+ * Gets a DOM tree descendant under the current node.
+ * @param {Array|Number} indices The child index or array of child indices under the node.
+ * @returns {CKEDITOR.dom.node} The specified DOM child under the current node. Null if child does not exist.
+ * @example
+ * var strong = p.getChild(0);
+ */
+ getChild : function( indices )
+ {
+ var rawNode = this.$;
+
+ if ( !indices.slice )
+ rawNode = rawNode.childNodes[ indices ];
+ else
+ {
+ while ( indices.length > 0 && rawNode )
+ rawNode = rawNode.childNodes[ indices.shift() ];
+ }
+
+ return rawNode ? new CKEDITOR.dom.node( rawNode ) : null;
+ },
+
+ getChildCount : function()
+ {
+ return this.$.childNodes.length;
+ },
+
+ disableContextMenu : function()
+ {
+ this.on( 'contextmenu', function( event )
+ {
+ // Cancel the browser context menu.
+ if ( !event.data.getTarget().hasClass( 'cke_enable_context_menu' ) )
+ event.data.preventDefault();
+ } );
+ },
+
+ /**
+ * Gets element's direction. Supports both CSS 'direction' prop and 'dir' attr.
+ */
+ getDirection : function( useComputed )
+ {
+ return useComputed ?
+ this.getComputedStyle( 'direction' )
+ // Webkit: offline element returns empty direction (#8053).
+ || this.getDirection()
+ || this.getDocument().$.dir
+ || this.getDocument().getBody().getDirection( 1 )
+ : this.getStyle( 'direction' ) || this.getAttribute( 'dir' );
+ },
+
+ /**
+ * Gets, sets and removes custom data to be stored as HTML5 data-* attributes.
+ * @param {String} name The name of the attribute, excluding the 'data-' part.
+ * @param {String} [value] The value to set. If set to false, the attribute will be removed.
+ * @example
+ * element.data( 'extra-info', 'test' ); // appended the attribute data-extra-info="test" to the element
+ * alert( element.data( 'extra-info' ) ); // "test"
+ * element.data( 'extra-info', false ); // remove the data-extra-info attribute from the element
+ */
+ data : function ( name, value )
+ {
+ name = 'data-' + name;
+ if ( value === undefined )
+ return this.getAttribute( name );
+ else if ( value === false )
+ this.removeAttribute( name );
+ else
+ this.setAttribute( name, value );
+
+ return null;
+ }
+ });
+
+( function()
+{
+ var sides = {
+ width : [ "border-left-width", "border-right-width","padding-left", "padding-right" ],
+ height : [ "border-top-width", "border-bottom-width", "padding-top", "padding-bottom" ]
+ };
+
+ function marginAndPaddingSize( type )
+ {
+ var adjustment = 0;
+ for ( var i = 0, len = sides[ type ].length; i < len; i++ )
+ adjustment += parseInt( this.getComputedStyle( sides [ type ][ i ] ) || 0, 10 ) || 0;
+ return adjustment;
+ }
+
+ /**
+ * Sets the element size considering the box model.
+ * @name CKEDITOR.dom.element.prototype.setSize
+ * @function
+ * @param {String} type The dimension to set. It accepts "width" and "height".
+ * @param {Number} size The length unit in px.
+ * @param {Boolean} isBorderBox Apply the size based on the border box model.
+ */
+ CKEDITOR.dom.element.prototype.setSize = function( type, size, isBorderBox )
+ {
+ if ( typeof size == 'number' )
+ {
+ if ( isBorderBox && !( CKEDITOR.env.ie && CKEDITOR.env.quirks ) )
+ size -= marginAndPaddingSize.call( this, type );
+
+ this.setStyle( type, size + 'px' );
+ }
+ };
+
+ /**
+ * Gets the element size, possibly considering the box model.
+ * @name CKEDITOR.dom.element.prototype.getSize
+ * @function
+ * @param {String} type The dimension to get. It accepts "width" and "height".
+ * @param {Boolean} isBorderBox Get the size based on the border box model.
+ */
+ CKEDITOR.dom.element.prototype.getSize = function( type, isBorderBox )
+ {
+ var size = Math.max( this.$[ 'offset' + CKEDITOR.tools.capitalize( type ) ],
+ this.$[ 'client' + CKEDITOR.tools.capitalize( type ) ] ) || 0;
+
+ if ( isBorderBox )
+ size -= marginAndPaddingSize.call( this, type );
+
+ return size;
+ };
+})();
Index: 3rdParty_sources/ckeditor/core/dom/elementpath.js
===================================================================
RCS file: /usr/local/cvsroot/3rdParty_sources/ckeditor/core/dom/elementpath.js,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ 3rdParty_sources/ckeditor/core/dom/elementpath.js 17 Aug 2012 14:15:42 -0000 1.1
@@ -0,0 +1,119 @@
+/*
+Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+(function()
+{
+ // Elements that may be considered the "Block boundary" in an element path.
+ var pathBlockElements = { address:1,blockquote:1,dl:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,p:1,pre:1,li:1,dt:1,dd:1, legend:1,caption:1 };
+
+ // Elements that may be considered the "Block limit" in an element path.
+ var pathBlockLimitElements = { body:1,div:1,table:1,tbody:1,tr:1,td:1,th:1,form:1,fieldset:1 };
+
+ // Check if an element contains any block element.
+ var checkHasBlock = function( element )
+ {
+ var childNodes = element.getChildren();
+
+ for ( var i = 0, count = childNodes.count() ; i < count ; i++ )
+ {
+ var child = childNodes.getItem( i );
+
+ if ( child.type == CKEDITOR.NODE_ELEMENT && CKEDITOR.dtd.$block[ child.getName() ] )
+ return true;
+ }
+
+ return false;
+ };
+
+ /**
+ * @class
+ */
+ CKEDITOR.dom.elementPath = function( lastNode )
+ {
+ var block = null;
+ var blockLimit = null;
+ var elements = [];
+
+ var e = lastNode;
+
+ while ( e )
+ {
+ if ( e.type == CKEDITOR.NODE_ELEMENT )
+ {
+ if ( !this.lastElement )
+ this.lastElement = e;
+
+ var elementName = e.getName();
+ if ( CKEDITOR.env.ie && e.$.scopeName != 'HTML' )
+ elementName = e.$.scopeName.toLowerCase() + ':' + elementName;
+
+ if ( !blockLimit )
+ {
+ if ( !block && pathBlockElements[ elementName ] )
+ block = e;
+
+ if ( pathBlockLimitElements[ elementName ] )
+ {
+ // DIV is considered the Block, if no block is available (#525)
+ // and if it doesn't contain other blocks.
+ if ( !block && elementName == 'div' && !checkHasBlock( e ) )
+ block = e;
+ else
+ blockLimit = e;
+ }
+ }
+
+ elements.push( e );
+
+ if ( elementName == 'body' )
+ break;
+ }
+ e = e.getParent();
+ }
+
+ this.block = block;
+ this.blockLimit = blockLimit;
+ this.elements = elements;
+ };
+})();
+
+CKEDITOR.dom.elementPath.prototype =
+{
+ /**
+ * Compares this element path with another one.
+ * @param {CKEDITOR.dom.elementPath} otherPath The elementPath object to be
+ * compared with this one.
+ * @returns {Boolean} "true" if the paths are equal, containing the same
+ * number of elements and the same elements in the same order.
+ */
+ compare : function( otherPath )
+ {
+ var thisElements = this.elements;
+ var otherElements = otherPath && otherPath.elements;
+
+ if ( !otherElements || thisElements.length != otherElements.length )
+ return false;
+
+ for ( var i = 0 ; i < thisElements.length ; i++ )
+ {
+ if ( !thisElements[ i ].equals( otherElements[ i ] ) )
+ return false;
+ }
+
+ return true;
+ },
+
+ contains : function( tagNames )
+ {
+ var elements = this.elements;
+ for ( var i = 0 ; i < elements.length ; i++ )
+ {
+ if ( elements[ i ].getName() in tagNames )
+ return elements[ i ];
+ }
+
+ return null;
+ }
+};
Index: 3rdParty_sources/ckeditor/core/dom/event.js
===================================================================
RCS file: /usr/local/cvsroot/3rdParty_sources/ckeditor/core/dom/event.js,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ 3rdParty_sources/ckeditor/core/dom/event.js 17 Aug 2012 14:15:42 -0000 1.1
@@ -0,0 +1,145 @@
+/*
+Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the {@link CKEDITOR.dom.event} class, which
+ * represents the a native DOM event object.
+ */
+
+/**
+ * Represents a native DOM event object.
+ * @constructor
+ * @param {Object} domEvent A native DOM event object.
+ * @example
+ */
+CKEDITOR.dom.event = function( domEvent )
+{
+ /**
+ * The native DOM event object represented by this class instance.
+ * @type Object
+ * @example
+ */
+ this.$ = domEvent;
+};
+
+CKEDITOR.dom.event.prototype =
+{
+ /**
+ * Gets the key code associated to the event.
+ * @returns {Number} The key code.
+ * @example
+ * alert( event.getKey() ); "65" is "a" has been pressed
+ */
+ getKey : function()
+ {
+ return this.$.keyCode || this.$.which;
+ },
+
+ /**
+ * Gets a number represeting the combination of the keys pressed during the
+ * event. It is the sum with the current key code and the {@link CKEDITOR.CTRL},
+ * {@link CKEDITOR.SHIFT} and {@link CKEDITOR.ALT} constants.
+ * @returns {Number} The number representing the keys combination.
+ * @example
+ * alert( event.getKeystroke() == 65 ); // "a" key
+ * alert( event.getKeystroke() == CKEDITOR.CTRL + 65 ); // CTRL + "a" key
+ * alert( event.getKeystroke() == CKEDITOR.CTRL + CKEDITOR.SHIFT + 65 ); // CTRL + SHIFT + "a" key
+ */
+ getKeystroke : function()
+ {
+ var keystroke = this.getKey();
+
+ if ( this.$.ctrlKey || this.$.metaKey )
+ keystroke += CKEDITOR.CTRL;
+
+ if ( this.$.shiftKey )
+ keystroke += CKEDITOR.SHIFT;
+
+ if ( this.$.altKey )
+ keystroke += CKEDITOR.ALT;
+
+ return keystroke;
+ },
+
+ /**
+ * Prevents the original behavior of the event to happen. It can optionally
+ * stop propagating the event in the event chain.
+ * @param {Boolean} [stopPropagation] Stop propagating this event in the
+ * event chain.
+ * @example
+ * var element = CKEDITOR.document.getById( 'myElement' );
+ * element.on( 'click', function( ev )
+ * {
+ * // The DOM event object is passed by the "data" property.
+ * var domEvent = ev.data;
+ * // Prevent the click to chave any effect in the element.
+ * domEvent.preventDefault();
+ * });
+ */
+ preventDefault : function( stopPropagation )
+ {
+ var $ = this.$;
+ if ( $.preventDefault )
+ $.preventDefault();
+ else
+ $.returnValue = false;
+
+ if ( stopPropagation )
+ this.stopPropagation();
+ },
+
+ stopPropagation : function()
+ {
+ var $ = this.$;
+ if ( $.stopPropagation )
+ $.stopPropagation();
+ else
+ $.cancelBubble = true;
+ },
+
+ /**
+ * Returns the DOM node where the event was targeted to.
+ * @returns {CKEDITOR.dom.node} The target DOM node.
+ * @example
+ * var element = CKEDITOR.document.getById( 'myElement' );
+ * element.on( 'click', function( ev )
+ * {
+ * // The DOM event object is passed by the "data" property.
+ * var domEvent = ev.data;
+ * // Add a CSS class to the event target.
+ * domEvent.getTarget().addClass( 'clicked' );
+ * });
+ */
+
+ getTarget : function()
+ {
+ var rawNode = this.$.target || this.$.srcElement;
+ return rawNode ? new CKEDITOR.dom.node( rawNode ) : null;
+ }
+};
+
+// For the followind constants, we need to go over the Unicode boundaries
+// (0x10FFFF) to avoid collision.
+
+/**
+ * CTRL key (0x110000).
+ * @constant
+ * @example
+ */
+CKEDITOR.CTRL = 0x110000;
+
+/**
+ * SHIFT key (0x220000).
+ * @constant
+ * @example
+ */
+CKEDITOR.SHIFT = 0x220000;
+
+/**
+ * ALT key (0x440000).
+ * @constant
+ * @example
+ */
+CKEDITOR.ALT = 0x440000;
Index: 3rdParty_sources/ckeditor/core/dom/node.js
===================================================================
RCS file: /usr/local/cvsroot/3rdParty_sources/ckeditor/core/dom/node.js,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ 3rdParty_sources/ckeditor/core/dom/node.js 17 Aug 2012 14:15:42 -0000 1.1
@@ -0,0 +1,696 @@
+/*
+Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @fileOverview Defines the {@link CKEDITOR.dom.node} class which is the base
+ * class for classes that represent DOM nodes.
+ */
+
+/**
+ * Base class for classes representing DOM nodes. This constructor may return
+ * an instance of a class that inherits from this class, like
+ * {@link CKEDITOR.dom.element} or {@link CKEDITOR.dom.text}.
+ * @augments CKEDITOR.dom.domObject
+ * @param {Object} domNode A native DOM node.
+ * @constructor
+ * @see CKEDITOR.dom.element
+ * @see CKEDITOR.dom.text
+ * @example
+ */
+CKEDITOR.dom.node = function( domNode )
+{
+ if ( domNode )
+ {
+ switch ( domNode.nodeType )
+ {
+ // Safari don't consider document as element node type. (#3389)
+ case CKEDITOR.NODE_DOCUMENT :
+ return new CKEDITOR.dom.document( domNode );
+
+ case CKEDITOR.NODE_ELEMENT :
+ return new CKEDITOR.dom.element( domNode );
+
+ case CKEDITOR.NODE_TEXT :
+ return new CKEDITOR.dom.text( domNode );
+ }
+
+ // Call the base constructor.
+ CKEDITOR.dom.domObject.call( this, domNode );
+ }
+
+ return this;
+};
+
+CKEDITOR.dom.node.prototype = new CKEDITOR.dom.domObject();
+
+/**
+ * Element node type.
+ * @constant
+ * @example
+ */
+CKEDITOR.NODE_ELEMENT = 1;
+
+/**
+ * Document node type.
+ * @constant
+ * @example
+ */
+CKEDITOR.NODE_DOCUMENT = 9;
+
+/**
+ * Text node type.
+ * @constant
+ * @example
+ */
+CKEDITOR.NODE_TEXT = 3;
+
+/**
+ * Comment node type.
+ * @constant
+ * @example
+ */
+CKEDITOR.NODE_COMMENT = 8;
+
+CKEDITOR.NODE_DOCUMENT_FRAGMENT = 11;
+
+CKEDITOR.POSITION_IDENTICAL = 0;
+CKEDITOR.POSITION_DISCONNECTED = 1;
+CKEDITOR.POSITION_FOLLOWING = 2;
+CKEDITOR.POSITION_PRECEDING = 4;
+CKEDITOR.POSITION_IS_CONTAINED = 8;
+CKEDITOR.POSITION_CONTAINS = 16;
+
+CKEDITOR.tools.extend( CKEDITOR.dom.node.prototype,
+ /** @lends CKEDITOR.dom.node.prototype */
+ {
+ /**
+ * Makes this node a child of another element.
+ * @param {CKEDITOR.dom.element} element The target element to which
+ * this node will be appended.
+ * @returns {CKEDITOR.dom.element} The target element.
+ * @example
+ * var p = new CKEDITOR.dom.element( 'p' );
+ * var strong = new CKEDITOR.dom.element( 'strong' );
+ * strong.appendTo( p );
+ *
+ * // result: "<p><strong></strong></p>"
+ */
+ appendTo : function( element, toStart )
+ {
+ element.append( this, toStart );
+ return element;
+ },
+
+ clone : function( includeChildren, cloneId )
+ {
+ var $clone = this.$.cloneNode( includeChildren );
+
+ var removeIds = function( node )
+ {
+ if ( node.nodeType != CKEDITOR.NODE_ELEMENT )
+ return;
+
+ if ( !cloneId )
+ node.removeAttribute( 'id', false );
+ node.removeAttribute( 'data-cke-expando', false );
+
+ if ( includeChildren )
+ {
+ var childs = node.childNodes;
+ for ( var i=0; i < childs.length; i++ )
+ removeIds( childs[ i ] );
+ }
+ };
+
+ // The "id" attribute should never be cloned to avoid duplication.
+ removeIds( $clone );
+
+ return new CKEDITOR.dom.node( $clone );
+ },
+
+ hasPrevious : function()
+ {
+ return !!this.$.previousSibling;
+ },
+
+ hasNext : function()
+ {
+ return !!this.$.nextSibling;
+ },
+
+ /**
+ * Inserts this element after a node.
+ * @param {CKEDITOR.dom.node} node The node that will precede this element.
+ * @returns {CKEDITOR.dom.node} The node preceding this one after
+ * insertion.
+ * @example
+ * var em = new CKEDITOR.dom.element( 'em' );
+ * var strong = new CKEDITOR.dom.element( 'strong' );
+ * strong.insertAfter( em );
+ *
+ * // result: "<em></em><strong></strong>"
+ */
+ insertAfter : function( node )
+ {
+ node.$.parentNode.insertBefore( this.$, node.$.nextSibling );
+ return node;
+ },
+
+ /**
+ * Inserts this element before a node.
+ * @param {CKEDITOR.dom.node} node The node that will succeed this element.
+ * @returns {CKEDITOR.dom.node} The node being inserted.
+ * @example
+ * var em = new CKEDITOR.dom.element( 'em' );
+ * var strong = new CKEDITOR.dom.element( 'strong' );
+ * strong.insertBefore( em );
+ *
+ * // result: "<strong></strong><em></em>"
+ */
+ insertBefore : function( node )
+ {
+ node.$.parentNode.insertBefore( this.$, node.$ );
+ return node;
+ },
+
+ insertBeforeMe : function( node )
+ {
+ this.$.parentNode.insertBefore( node.$, this.$ );
+ return node;
+ },
+
+ /**
+ * Retrieves a uniquely identifiable tree address for this node.
+ * The tree address returned is an array of integers, with each integer
+ * indicating a child index of a DOM node, starting from
+ * document.documentElement
.
+ *
+ * For example, assuming <body>
is the second child
+ * of <html>
(<head>
being the first),
+ * and we would like to address the third child under the
+ * fourth child of <body>
, the tree address returned would be:
+ * [1, 3, 2]
+ *
+ * The tree address cannot be used for finding back the DOM tree node once
+ * the DOM tree structure has been modified.
+ */
+ getAddress : function( normalized )
+ {
+ var address = [];
+ var $documentElement = this.getDocument().$.documentElement;
+ var node = this.$;
+
+ while ( node && node != $documentElement )
+ {
+ var parentNode = node.parentNode;
+
+ if ( parentNode )
+ {
+ // Get the node index. For performance, call getIndex
+ // directly, instead of creating a new node object.
+ address.unshift( this.getIndex.call( { $ : node }, normalized ) );
+ }
+
+ node = parentNode;
+ }
+
+ return address;
+ },
+
+ /**
+ * Gets the document containing this element.
+ * @returns {CKEDITOR.dom.document} The document.
+ * @example
+ * var element = CKEDITOR.document.getById( 'example' );
+ * alert( element.getDocument().equals( CKEDITOR.document ) ); // "true"
+ */
+ getDocument : function()
+ {
+ return new CKEDITOR.dom.document( this.$.ownerDocument || this.$.parentNode.ownerDocument );
+ },
+
+ getIndex : function( normalized )
+ {
+ // Attention: getAddress depends on this.$
+
+ var current = this.$,
+ index = 0;
+
+ while ( ( current = current.previousSibling ) )
+ {
+ // When normalizing, do not count it if this is an
+ // empty text node or if it's a text node following another one.
+ if ( normalized && current.nodeType == 3 &&
+ ( !current.nodeValue.length ||
+ ( current.previousSibling && current.previousSibling.nodeType == 3 ) ) )
+ {
+ continue;
+ }
+
+ index++;
+ }
+
+ return index;
+ },
+
+ getNextSourceNode : function( startFromSibling, nodeType, guard )
+ {
+ // If "guard" is a node, transform it in a function.
+ if ( guard && !guard.call )
+ {
+ var guardNode = guard;
+ guard = function( node )
+ {
+ return !node.equals( guardNode );
+ };
+ }
+
+ var node = ( !startFromSibling && this.getFirst && this.getFirst() ),
+ parent;
+
+ // Guarding when we're skipping the current element( no children or 'startFromSibling' ).
+ // send the 'moving out' signal even we don't actually dive into.
+ if ( !node )
+ {
+ if ( this.type == CKEDITOR.NODE_ELEMENT && guard && guard( this, true ) === false )
+ return null;
+ node = this.getNext();
+ }
+
+ while ( !node && ( parent = ( parent || this ).getParent() ) )
+ {
+ // The guard check sends the "true" paramenter to indicate that
+ // we are moving "out" of the element.
+ if ( guard && guard( parent, true ) === false )
+ return null;
+
+ node = parent.getNext();
+ }
+
+ if ( !node )
+ return null;
+
+ if ( guard && guard( node ) === false )
+ return null;
+
+ if ( nodeType && nodeType != node.type )
+ return node.getNextSourceNode( false, nodeType, guard );
+
+ return node;
+ },
+
+ getPreviousSourceNode : function( startFromSibling, nodeType, guard )
+ {
+ if ( guard && !guard.call )
+ {
+ var guardNode = guard;
+ guard = function( node )
+ {
+ return !node.equals( guardNode );
+ };
+ }
+
+ var node = ( !startFromSibling && this.getLast && this.getLast() ),
+ parent;
+
+ // Guarding when we're skipping the current element( no children or 'startFromSibling' ).
+ // send the 'moving out' signal even we don't actually dive into.
+ if ( !node )
+ {
+ if ( this.type == CKEDITOR.NODE_ELEMENT && guard && guard( this, true ) === false )
+ return null;
+ node = this.getPrevious();
+ }
+
+ while ( !node && ( parent = ( parent || this ).getParent() ) )
+ {
+ // The guard check sends the "true" paramenter to indicate that
+ // we are moving "out" of the element.
+ if ( guard && guard( parent, true ) === false )
+ return null;
+
+ node = parent.getPrevious();
+ }
+
+ if ( !node )
+ return null;
+
+ if ( guard && guard( node ) === false )
+ return null;
+
+ if ( nodeType && node.type != nodeType )
+ return node.getPreviousSourceNode( false, nodeType, guard );
+
+ return node;
+ },
+
+ getPrevious : function( evaluator )
+ {
+ var previous = this.$, retval;
+ do
+ {
+ previous = previous.previousSibling;
+ retval = previous && new CKEDITOR.dom.node( previous );
+ }
+ while ( retval && evaluator && !evaluator( retval ) )
+ return retval;
+ },
+
+ /**
+ * Gets the node that follows this element in its parent's child list.
+ * @param {Function} evaluator Filtering the result node.
+ * @returns {CKEDITOR.dom.node} The next node or null if not available.
+ * @example
+ * var element = CKEDITOR.dom.element.createFromHtml( '<div><b>Example</b> <i>next</i></div>' );
+ * var first = element.getFirst().getNext();
+ * alert( first.getName() ); // "i"
+ */
+ getNext : function( evaluator )
+ {
+ var next = this.$, retval;
+ do
+ {
+ next = next.nextSibling;
+ retval = next && new CKEDITOR.dom.node( next );
+ }
+ while ( retval && evaluator && !evaluator( retval ) )
+ return retval;
+ },
+
+ /**
+ * Gets the parent element for this node.
+ * @returns {CKEDITOR.dom.element} The parent element.
+ * @example
+ * var node = editor.document.getBody().getFirst();
+ * var parent = node.getParent();
+ * alert( node.getName() ); // "body"
+ */
+ getParent : function()
+ {
+ var parent = this.$.parentNode;
+ return ( parent && parent.nodeType == 1 ) ? new CKEDITOR.dom.node( parent ) : null;
+ },
+
+ getParents : function( closerFirst )
+ {
+ var node = this;
+ var parents = [];
+
+ do
+ {
+ parents[ closerFirst ? 'push' : 'unshift' ]( node );
+ }
+ while ( ( node = node.getParent() ) )
+
+ return parents;
+ },
+
+ getCommonAncestor : function( node )
+ {
+ if ( node.equals( this ) )
+ return this;
+
+ if ( node.contains && node.contains( this ) )
+ return node;
+
+ var start = this.contains ? this : this.getParent();
+
+ do
+ {
+ if ( start.contains( node ) )
+ return start;
+ }
+ while ( ( start = start.getParent() ) );
+
+ return null;
+ },
+
+ getPosition : function( otherNode )
+ {
+ var $ = this.$;
+ var $other = otherNode.$;
+
+ if ( $.compareDocumentPosition )
+ return $.compareDocumentPosition( $other );
+
+ // IE and Safari have no support for compareDocumentPosition.
+
+ if ( $ == $other )
+ return CKEDITOR.POSITION_IDENTICAL;
+
+ // Only element nodes support contains and sourceIndex.
+ if ( this.type == CKEDITOR.NODE_ELEMENT && otherNode.type == CKEDITOR.NODE_ELEMENT )
+ {
+ if ( $.contains )
+ {
+ if ( $.contains( $other ) )
+ return CKEDITOR.POSITION_CONTAINS + CKEDITOR.POSITION_PRECEDING;
+
+ if ( $other.contains( $ ) )
+ return CKEDITOR.POSITION_IS_CONTAINED + CKEDITOR.POSITION_FOLLOWING;
+ }
+
+ if ( 'sourceIndex' in $ )
+ {
+ return ( $.sourceIndex < 0 || $other.sourceIndex < 0 ) ? CKEDITOR.POSITION_DISCONNECTED :
+ ( $.sourceIndex < $other.sourceIndex ) ? CKEDITOR.POSITION_PRECEDING :
+ CKEDITOR.POSITION_FOLLOWING;
+ }
+ }
+
+ // For nodes that don't support compareDocumentPosition, contains
+ // or sourceIndex, their "address" is compared.
+
+ var addressOfThis = this.getAddress(),
+ addressOfOther = otherNode.getAddress(),
+ minLevel = Math.min( addressOfThis.length, addressOfOther.length );
+
+ // Determinate preceed/follow relationship.
+ for ( var i = 0 ; i <= minLevel - 1 ; i++ )
+ {
+ if ( addressOfThis[ i ] != addressOfOther[ i ] )
+ {
+ if ( i < minLevel )
+ {
+ return addressOfThis[ i ] < addressOfOther[ i ] ?
+ CKEDITOR.POSITION_PRECEDING : CKEDITOR.POSITION_FOLLOWING;
+ }
+ break;
+ }
+ }
+
+ // Determinate contains/contained relationship.
+ return ( addressOfThis.length < addressOfOther.length ) ?
+ CKEDITOR.POSITION_CONTAINS + CKEDITOR.POSITION_PRECEDING :
+ CKEDITOR.POSITION_IS_CONTAINED + CKEDITOR.POSITION_FOLLOWING;
+ },
+
+ /**
+ * Gets the closest ancestor node of this node, specified by its name.
+ * @param {String} reference The name of the ancestor node to search or
+ * an object with the node names to search for.
+ * @param {Boolean} [includeSelf] Whether to include the current
+ * node in the search.
+ * @returns {CKEDITOR.dom.node} The located ancestor node or null if not found.
+ * @since 3.6.1
+ * @example
+ * // Suppose we have the following HTML structure:
+ * // <div id="outer"><div id="inner"><p><b>Some text</b></p></div></div>
+ * // If node == <b>
+ * ascendant = node.getAscendant( 'div' ); // ascendant == <div id="inner">
+ * ascendant = node.getAscendant( 'b' ); // ascendant == null
+ * ascendant = node.getAscendant( 'b', true ); // ascendant == <b>
+ * ascendant = node.getAscendant( { div: 1, p: 1} ); // Searches for the first 'div' or 'p': ascendant == <div id="inner">
+ */
+ getAscendant : function( reference, includeSelf )
+ {
+ var $ = this.$,
+ name;
+
+ if ( !includeSelf )
+ $ = $.parentNode;
+
+ while ( $ )
+ {
+ if ( $.nodeName && ( name = $.nodeName.toLowerCase(), ( typeof reference == 'string' ? name == reference : name in reference ) ) )
+ return new CKEDITOR.dom.node( $ );
+
+ $ = $.parentNode;
+ }
+ return null;
+ },
+
+ hasAscendant : function( name, includeSelf )
+ {
+ var $ = this.$;
+
+ if ( !includeSelf )
+ $ = $.parentNode;
+
+ while ( $ )
+ {
+ if ( $.nodeName && $.nodeName.toLowerCase() == name )
+ return true;
+
+ $ = $.parentNode;
+ }
+ return false;
+ },
+
+ move : function( target, toStart )
+ {
+ target.append( this.remove(), toStart );
+ },
+
+ /**
+ * Removes this node from the document DOM.
+ * @param {Boolean} [preserveChildren] Indicates that the children
+ * elements must remain in the document, removing only the outer
+ * tags.
+ * @example
+ * var element = CKEDITOR.dom.element.getById( 'MyElement' );
+ * element.remove();
+ */
+ remove : function( preserveChildren )
+ {
+ var $ = this.$;
+ var parent = $.parentNode;
+
+ if ( parent )
+ {
+ if ( preserveChildren )
+ {
+ // Move all children before the node.
+ for ( var child ; ( child = $.firstChild ) ; )
+ {
+ parent.insertBefore( $.removeChild( child ), $ );
+ }
+ }
+
+ parent.removeChild( $ );
+ }
+
+ return this;
+ },
+
+ replace : function( nodeToReplace )
+ {
+ this.insertBefore( nodeToReplace );
+ nodeToReplace.remove();
+ },
+
+ trim : function()
+ {
+ this.ltrim();
+ this.rtrim();
+ },
+
+ ltrim : function()
+ {
+ var child;
+ while ( this.getFirst && ( child = this.getFirst() ) )
+ {
+ if ( child.type == CKEDITOR.NODE_TEXT )
+ {
+ var trimmed = CKEDITOR.tools.ltrim( child.getText() ),
+ originalLength = child.getLength();
+
+ if ( !trimmed )
+ {
+ child.remove();
+ continue;
+ }
+ else if ( trimmed.length < originalLength )
+ {
+ child.split( originalLength - trimmed.length );
+
+ // IE BUG: child.remove() may raise JavaScript errors here. (#81)
+ this.$.removeChild( this.$.firstChild );
+ }
+ }
+ break;
+ }
+ },
+
+ rtrim : function()
+ {
+ var child;
+ while ( this.getLast && ( child = this.getLast() ) )
+ {
+ if ( child.type == CKEDITOR.NODE_TEXT )
+ {
+ var trimmed = CKEDITOR.tools.rtrim( child.getText() ),
+ originalLength = child.getLength();
+
+ if ( !trimmed )
+ {
+ child.remove();
+ continue;
+ }
+ else if ( trimmed.length < originalLength )
+ {
+ child.split( trimmed.length );
+
+ // IE BUG: child.getNext().remove() may raise JavaScript errors here.
+ // (#81)
+ this.$.lastChild.parentNode.removeChild( this.$.lastChild );
+ }
+ }
+ break;
+ }
+
+ if ( !CKEDITOR.env.ie && !CKEDITOR.env.opera )
+ {
+ child = this.$.lastChild;
+
+ if ( child && child.type == 1 && child.nodeName.toLowerCase() == 'br' )
+ {
+ // Use "eChildNode.parentNode" instead of "node" to avoid IE bug (#324).
+ child.parentNode.removeChild( child ) ;
+ }
+ }
+ },
+
+ /**
+ * Checks if this node is read-only (should not be changed).
+ * @returns {Boolean}
+ * @since 3.5
+ * @example
+ * // For the following HTML:
+ * // <div contenteditable="false">Some <b>text</b></div>
+ *
+ * // If "ele" is the above <div>
+ * ele.isReadOnly(); // true
+ */
+ isReadOnly : function()
+ {
+ var element = this;
+ if ( this.type != CKEDITOR.NODE_ELEMENT )
+ element = this.getParent();
+
+ if ( element && typeof element.$.isContentEditable != 'undefined' )
+ return ! ( element.$.isContentEditable || element.data( 'cke-editable' ) );
+ else
+ {
+ // Degrade for old browsers which don't support "isContentEditable", e.g. FF3
+ var current = element;
+ while( current )
+ {
+ if ( current.is( 'body' ) || !!current.data( 'cke-editable' ) )
+ break;
+
+ if ( current.getAttribute( 'contentEditable' ) == 'false' )
+ return true;
+ else if ( current.getAttribute( 'contentEditable' ) == 'true' )
+ break;
+
+ current = current.getParent();
+ }
+
+ return false;
+ }
+ }
+ }
+);
Index: 3rdParty_sources/ckeditor/core/dom/nodelist.js
===================================================================
RCS file: /usr/local/cvsroot/3rdParty_sources/ckeditor/core/dom/nodelist.js,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ 3rdParty_sources/ckeditor/core/dom/nodelist.js 17 Aug 2012 14:15:43 -0000 1.1
@@ -0,0 +1,26 @@
+/*
+Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * @class
+ */
+CKEDITOR.dom.nodeList = function( nativeList )
+{
+ this.$ = nativeList;
+};
+
+CKEDITOR.dom.nodeList.prototype =
+{
+ count : function()
+ {
+ return this.$.length;
+ },
+
+ getItem : function( index )
+ {
+ var $node = this.$[ index ];
+ return $node ? new CKEDITOR.dom.node( $node ) : null;
+ }
+};
Index: 3rdParty_sources/ckeditor/core/dom/range.js
===================================================================
RCS file: /usr/local/cvsroot/3rdParty_sources/ckeditor/core/dom/range.js,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ 3rdParty_sources/ckeditor/core/dom/range.js 17 Aug 2012 14:15:43 -0000 1.1
@@ -0,0 +1,2054 @@
+/*
+Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
+For licensing, see LICENSE.html or http://ckeditor.com/license
+*/
+
+/**
+ * Creates a CKEDITOR.dom.range instance that can be used inside a specific
+ * DOM Document.
+ * @class Represents a delimited piece of content in a DOM Document.
+ * It is contiguous in the sense that it can be characterized as selecting all
+ * of the content between a pair of boundary-points.
+ *
+ * This class shares much of the W3C
+ * Document Object Model Range
+ * ideas and features, adding several range manipulation tools to it, but it's
+ * not intended to be compatible with it.
+ * @param {CKEDITOR.dom.document} document The document into which the range
+ * features will be available.
+ * @example
+ * // Create a range for the entire contents of the editor document body.
+ * var range = new CKEDITOR.dom.range( editor.document );
+ * range.selectNodeContents( editor.document.getBody() );
+ * // Delete the contents.
+ * range.deleteContents();
+ */
+CKEDITOR.dom.range = function( document )
+{
+ /**
+ * Node within which the range begins.
+ * @type {CKEDITOR.NODE_ELEMENT|CKEDITOR.NODE_TEXT}
+ * @example
+ * var range = new CKEDITOR.dom.range( editor.document );
+ * range.selectNodeContents( editor.document.getBody() );
+ * alert( range.startContainer.getName() ); // "body"
+ */
+ this.startContainer = null;
+
+ /**
+ * Offset within the starting node of the range.
+ * @type {Number}
+ * @example
+ * var range = new CKEDITOR.dom.range( editor.document );
+ * range.selectNodeContents( editor.document.getBody() );
+ * alert( range.startOffset ); // "0"
+ */
+ this.startOffset = null;
+
+ /**
+ * Node within which the range ends.
+ * @type {CKEDITOR.NODE_ELEMENT|CKEDITOR.NODE_TEXT}
+ * @example
+ * var range = new CKEDITOR.dom.range( editor.document );
+ * range.selectNodeContents( editor.document.getBody() );
+ * alert( range.endContainer.getName() ); // "body"
+ */
+ this.endContainer = null;
+
+ /**
+ * Offset within the ending node of the range.
+ * @type {Number}
+ * @example
+ * var range = new CKEDITOR.dom.range( editor.document );
+ * range.selectNodeContents( editor.document.getBody() );
+ * alert( range.endOffset ); // == editor.document.getBody().getChildCount()
+ */
+ this.endOffset = null;
+
+ /**
+ * Indicates that this is a collapsed range. A collapsed range has it's
+ * start and end boudaries at the very same point so nothing is contained
+ * in it.
+ * @example
+ * var range = new CKEDITOR.dom.range( editor.document );
+ * range.selectNodeContents( editor.document.getBody() );
+ * alert( range.collapsed ); // "false"
+ * range.collapse();
+ * alert( range.collapsed ); // "true"
+ */
+ this.collapsed = true;
+
+ /**
+ * The document within which the range can be used.
+ * @type {CKEDITOR.dom.document}
+ * @example
+ * // Selects the body contents of the range document.
+ * range.selectNodeContents( range.document.getBody() );
+ */
+ this.document = document;
+};
+
+(function()
+{
+ // Updates the "collapsed" property for the given range object.
+ var updateCollapsed = function( range )
+ {
+ range.collapsed = (
+ range.startContainer &&
+ range.endContainer &&
+ range.startContainer.equals( range.endContainer ) &&
+ range.startOffset == range.endOffset );
+ };
+
+ // This is a shared function used to delete, extract and clone the range
+ // contents.
+ // V2
+ var execContentsAction = function( range, action, docFrag, mergeThen )
+ {
+ range.optimizeBookmark();
+
+ var startNode = range.startContainer;
+ var endNode = range.endContainer;
+
+ var startOffset = range.startOffset;
+ var endOffset = range.endOffset;
+
+ var removeStartNode;
+ var removeEndNode;
+
+ // For text containers, we must simply split the node and point to the
+ // second part. The removal will be handled by the rest of the code .
+ if ( endNode.type == CKEDITOR.NODE_TEXT )
+ endNode = endNode.split( endOffset );
+ else
+ {
+ // If the end container has children and the offset is pointing
+ // to a child, then we should start from it.
+ if ( endNode.getChildCount() > 0 )
+ {
+ // If the offset points after the last node.
+ if ( endOffset >= endNode.getChildCount() )
+ {
+ // Let's create a temporary node and mark it for removal.
+ endNode = endNode.append( range.document.createText( '' ) );
+ removeEndNode = true;
+ }
+ else
+ endNode = endNode.getChild( endOffset );
+ }
+ }
+
+ // For text containers, we must simply split the node. The removal will
+ // be handled by the rest of the code .
+ if ( startNode.type == CKEDITOR.NODE_TEXT )
+ {
+ startNode.split( startOffset );
+
+ // In cases the end node is the same as the start node, the above
+ // splitting will also split the end, so me must move the end to
+ // the second part of the split.
+ if ( startNode.equals( endNode ) )
+ endNode = startNode.getNext();
+ }
+ else
+ {
+ // If the start container has children and the offset is pointing
+ // to a child, then we should start from its previous sibling.
+
+ // If the offset points to the first node, we don't have a
+ // sibling, so let's use the first one, but mark it for removal.
+ if ( !startOffset )
+ {
+ // Let's create a temporary node and mark it for removal.
+ startNode = startNode.getFirst().insertBeforeMe( range.document.createText( '' ) );
+ removeStartNode = true;
+ }
+ else if ( startOffset >= startNode.getChildCount() )
+ {
+ // Let's create a temporary node and mark it for removal.
+ startNode = startNode.append( range.document.createText( '' ) );
+ removeStartNode = true;
+ }
+ else
+ startNode = startNode.getChild( startOffset ).getPrevious();
+ }
+
+ // Get the parent nodes tree for the start and end boundaries.
+ var startParents = startNode.getParents();
+ var endParents = endNode.getParents();
+
+ // Compare them, to find the top most siblings.
+ var i, topStart, topEnd;
+
+ for ( i = 0 ; i < startParents.length ; i++ )
+ {
+ topStart = startParents[ i ];
+ topEnd = endParents[ i ];
+
+ // The compared nodes will match until we find the top most
+ // siblings (different nodes that have the same parent).
+ // "i" will hold the index in the parents array for the top
+ // most element.
+ if ( !topStart.equals( topEnd ) )
+ break;
+ }
+
+ var clone = docFrag, levelStartNode, levelClone, currentNode, currentSibling;
+
+ // Remove all successive sibling nodes for every node in the
+ // startParents tree.
+ for ( var j = i ; j < startParents.length ; j++ )
+ {
+ levelStartNode = startParents[j];
+
+ // For Extract and Clone, we must clone this level.
+ if ( clone && !levelStartNode.equals( startNode ) ) // action = 0 = Delete
+ levelClone = clone.append( levelStartNode.clone() );
+
+ currentNode = levelStartNode.getNext();
+
+ while ( currentNode )
+ {
+ // Stop processing when the current node matches a node in the
+ // endParents tree or if it is the endNode.
+ if ( currentNode.equals( endParents[ j ] ) || currentNode.equals( endNode ) )
+ break;
+
+ // Cache the next sibling.
+ currentSibling = currentNode.getNext();
+
+ // If cloning, just clone it.
+ if ( action == 2 ) // 2 = Clone
+ clone.append( currentNode.clone( true ) );
+ else
+ {
+ // Both Delete and Extract will remove the node.
+ currentNode.remove();
+
+ // When Extracting, move the removed node to the docFrag.
+ if ( action == 1 ) // 1 = Extract
+ clone.append( currentNode );
+ }
+
+ currentNode = currentSibling;
+ }
+
+ if ( clone )
+ clone = levelClone;
+ }
+
+ clone = docFrag;
+
+ // Remove all previous sibling nodes for every node in the
+ // endParents tree.
+ for ( var k = i ; k < endParents.length ; k++ )
+ {
+ levelStartNode = endParents[ k ];
+
+ // For Extract and Clone, we must clone this level.
+ if ( action > 0 && !levelStartNode.equals( endNode ) ) // action = 0 = Delete
+ levelClone = clone.append( levelStartNode.clone() );
+
+ // The processing of siblings may have already been done by the parent.
+ if ( !startParents[ k ] || levelStartNode.$.parentNode != startParents[ k ].$.parentNode )
+ {
+ currentNode = levelStartNode.getPrevious();
+
+ while ( currentNode )
+ {
+ // Stop processing when the current node matches a node in the
+ // startParents tree or if it is the startNode.
+ if ( currentNode.equals( startParents[ k ] ) || currentNode.equals( startNode ) )
+ break;
+
+ // Cache the next sibling.
+ currentSibling = currentNode.getPrevious();
+
+ // If cloning, just clone it.
+ if ( action == 2 ) // 2 = Clone
+ clone.$.insertBefore( currentNode.$.cloneNode( true ), clone.$.firstChild ) ;
+ else
+ {
+ // Both Delete and Extract will remove the node.
+ currentNode.remove();
+
+ // When Extracting, mode the removed node to the docFrag.
+ if ( action == 1 ) // 1 = Extract
+ clone.$.insertBefore( currentNode.$, clone.$.firstChild );
+ }
+
+ currentNode = currentSibling;
+ }
+ }
+
+ if ( clone )
+ clone = levelClone;
+ }
+
+ if ( action == 2 ) // 2 = Clone.
+ {
+ // No changes in the DOM should be done, so fix the split text (if any).
+
+ var startTextNode = range.startContainer;
+ if ( startTextNode.type == CKEDITOR.NODE_TEXT )
+ {
+ startTextNode.$.data += startTextNode.$.nextSibling.data;
+ startTextNode.$.parentNode.removeChild( startTextNode.$.nextSibling );
+ }
+
+ var endTextNode = range.endContainer;
+ if ( endTextNode.type == CKEDITOR.NODE_TEXT && endTextNode.$.nextSibling )
+ {
+ endTextNode.$.data += endTextNode.$.nextSibling.data;
+ endTextNode.$.parentNode.removeChild( endTextNode.$.nextSibling );
+ }
+ }
+ else
+ {
+ // Collapse the range.
+
+ // If a node has been partially selected, collapse the range between
+ // topStart and topEnd. Otherwise, simply collapse it to the start. (W3C specs).
+ if ( topStart && topEnd && ( startNode.$.parentNode != topStart.$.parentNode || endNode.$.parentNode != topEnd.$.parentNode ) )
+ {
+ var endIndex = topEnd.getIndex();
+
+ // If the start node is to be removed, we must correct the
+ // index to reflect the removal.
+ if ( removeStartNode && topEnd.$.parentNode == startNode.$.parentNode )
+ endIndex--;
+
+ // Merge splitted parents.
+ if ( mergeThen && topStart.type == CKEDITOR.NODE_ELEMENT )
+ {
+ var span = CKEDITOR.dom.element.createFromHtml( ' ', range.document );
+ span.insertAfter( topStart );
+ topStart.mergeSiblings( false );
+ range.moveToBookmark( { startNode : span } );
+ }
+ else
+ range.setStart( topEnd.getParent(), endIndex );
+ }
+
+ // Collapse it to the start.
+ range.collapse( true );
+ }
+
+ // Cleanup any marked node.
+ if ( removeStartNode )
+ startNode.remove();
+
+ if ( removeEndNode && endNode.$.parentNode )
+ endNode.remove();
+ };
+
+ var inlineChildReqElements = { abbr:1,acronym:1,b:1,bdo:1,big:1,cite:1,code:1,del:1,dfn:1,em:1,font:1,i:1,ins:1,label:1,kbd:1,q:1,samp:1,small:1,span:1,strike:1,strong:1,sub:1,sup:1,tt:1,u:1,'var':1 };
+
+ // Creates the appropriate node evaluator for the dom walker used inside
+ // check(Start|End)OfBlock.
+ function getCheckStartEndBlockEvalFunction( isStart )
+ {
+ var hadBr = false, bookmarkEvaluator = CKEDITOR.dom.walker.bookmark( true );
+ return function( node )
+ {
+ // First ignore bookmark nodes.
+ if ( bookmarkEvaluator( node ) )
+ return true;
+
+ if ( node.type == CKEDITOR.NODE_TEXT )
+ {
+ // If there's any visible text, then we're not at the start.
+ if ( node.hasAscendant( 'pre' ) || CKEDITOR.tools.trim( node.getText() ).length )
+ return false;
+ }
+ else if ( node.type == CKEDITOR.NODE_ELEMENT )
+ {
+ // If there are non-empty inline elements (e.g. ), then we're not
+ // at the start.
+ if ( !inlineChildReqElements[ node.getName() ] )
+ {
+ // If we're working at the end-of-block, forgive the first
in non-IE
+ // browsers.
+ if ( !isStart && !CKEDITOR.env.ie && node.getName() == 'br' && !hadBr )
+ hadBr = true;
+ else
+ return false;
+ }
+ }
+ return true;
+ };
+ }
+
+ // Evaluator for CKEDITOR.dom.element::checkBoundaryOfElement, reject any
+ // text node and non-empty elements unless it's being bookmark text.
+ function elementBoundaryEval( node )
+ {
+ // Reject any text node unless it's being bookmark
+ // OR it's spaces. (#3883)
+ return node.type != CKEDITOR.NODE_TEXT
+ && node.getName() in CKEDITOR.dtd.$removeEmpty
+ || !CKEDITOR.tools.trim( node.getText() )
+ || !!node.getParent().data( 'cke-bookmark' );
+ }
+
+ var whitespaceEval = new CKEDITOR.dom.walker.whitespaces(),
+ bookmarkEval = new CKEDITOR.dom.walker.bookmark();
+
+ function nonWhitespaceOrBookmarkEval( node )
+ {
+ // Whitespaces and bookmark nodes are to be ignored.
+ return !whitespaceEval( node ) && !bookmarkEval( node );
+ }
+
+ CKEDITOR.dom.range.prototype =
+ {
+ clone : function()
+ {
+ var clone = new CKEDITOR.dom.range( this.document );
+
+ clone.startContainer = this.startContainer;
+ clone.startOffset = this.startOffset;
+ clone.endContainer = this.endContainer;
+ clone.endOffset = this.endOffset;
+ clone.collapsed = this.collapsed;
+
+ return clone;
+ },
+
+ collapse : function( toStart )
+ {
+ if ( toStart )
+ {
+ this.endContainer = this.startContainer;
+ this.endOffset = this.startOffset;
+ }
+ else
+ {
+ this.startContainer = this.endContainer;
+ this.startOffset = this.endOffset;
+ }
+
+ this.collapsed = true;
+ },
+
+ /**
+ * The content nodes of the range are cloned and added to a document fragment, which is returned.
+ * Note: Text selection may lost after invoking this method. (caused by text node splitting).
+ */
+ cloneContents : function()
+ {
+ var docFrag = new CKEDITOR.dom.documentFragment( this.document );
+
+ if ( !this.collapsed )
+ execContentsAction( this, 2, docFrag );
+
+ return docFrag;
+ },
+
+ /**
+ * Deletes the content nodes of the range permanently from the DOM tree.
+ * @param {Boolean} [mergeThen] Merge any splitted elements result in DOM true due to partial selection.
+ */
+ deleteContents : function( mergeThen )
+ {
+ if ( this.collapsed )
+ return;
+
+ execContentsAction( this, 0, null, mergeThen );
+ },
+
+ /**
+ * The content nodes of the range are cloned and added to a document fragment,
+ * meanwhile they're removed permanently from the DOM tree.
+ * @param {Boolean} [mergeThen] Merge any splitted elements result in DOM true due to partial selection.
+ */
+ extractContents : function( mergeThen )
+ {
+ var docFrag = new CKEDITOR.dom.documentFragment( this.document );
+
+ if ( !this.collapsed )
+ execContentsAction( this, 1, docFrag, mergeThen );
+
+ return docFrag;
+ },
+
+ /**
+ * Creates a bookmark object, which can be later used to restore the
+ * range by using the moveToBookmark function.
+ * This is an "intrusive" way to create a bookmark. It includes tags
+ * in the range boundaries. The advantage of it is that it is possible to
+ * handle DOM mutations when moving back to the bookmark.
+ * Attention: the inclusion of nodes in the DOM is a design choice and
+ * should not be changed as there are other points in the code that may be
+ * using those nodes to perform operations. See GetBookmarkNode.
+ * @param {Boolean} [serializable] Indicates that the bookmark nodes
+ * must contain ids, which can be used to restore the range even
+ * when these nodes suffer mutations (like a clonation or innerHTML
+ * change).
+ * @returns {Object} And object representing a bookmark.
+ */
+ createBookmark : function( serializable )
+ {
+ var startNode, endNode;
+ var baseId;
+ var clone;
+ var collapsed = this.collapsed;
+
+ startNode = this.document.createElement( 'span' );
+ startNode.data( 'cke-bookmark', 1 );
+ startNode.setStyle( 'display', 'none' );
+
+ // For IE, it must have something inside, otherwise it may be
+ // removed during DOM operations.
+ startNode.setHtml( ' ' );
+
+ if ( serializable )
+ {
+ baseId = 'cke_bm_' + CKEDITOR.tools.getNextNumber();
+ startNode.setAttribute( 'id', baseId + 'S' );
+ }
+
+ // If collapsed, the endNode will not be created.
+ if ( !collapsed )
+ {
+ endNode = startNode.clone();
+ endNode.setHtml( ' ' );
+
+ if ( serializable )
+ endNode.setAttribute( 'id', baseId + 'E' );
+
+ clone = this.clone();
+ clone.collapse();
+ clone.insertNode( endNode );
+ }
+
+ clone = this.clone();
+ clone.collapse( true );
+ clone.insertNode( startNode );
+
+ // Update the range position.
+ if ( endNode )
+ {
+ this.setStartAfter( startNode );
+ this.setEndBefore( endNode );
+ }
+ else
+ this.moveToPosition( startNode, CKEDITOR.POSITION_AFTER_END );
+
+ return {
+ startNode : serializable ? baseId + 'S' : startNode,
+ endNode : serializable ? baseId + 'E' : endNode,
+ serializable : serializable,
+ collapsed : collapsed
+ };
+ },
+
+ /**
+ * Creates a "non intrusive" and "mutation sensible" bookmark. This
+ * kind of bookmark should be used only when the DOM is supposed to
+ * remain stable after its creation.
+ * @param {Boolean} [normalized] Indicates that the bookmark must
+ * normalized. When normalized, the successive text nodes are
+ * considered a single node. To sucessful load a normalized
+ * bookmark, the DOM tree must be also normalized before calling
+ * moveToBookmark.
+ * @returns {Object} An object representing the bookmark.
+ */
+ createBookmark2 : function( normalized )
+ {
+ var startContainer = this.startContainer,
+ endContainer = this.endContainer;
+
+ var startOffset = this.startOffset,
+ endOffset = this.endOffset;
+
+ var collapsed = this.collapsed;
+
+ var child, previous;
+
+ // If there is no range then get out of here.
+ // It happens on initial load in Safari #962 and if the editor it's
+ // hidden also in Firefox
+ if ( !startContainer || !endContainer )
+ return { start : 0, end : 0 };
+
+ if ( normalized )
+ {
+ // Find out if the start is pointing to a text node that will
+ // be normalized.
+ if ( startContainer.type == CKEDITOR.NODE_ELEMENT )
+ {
+ child = startContainer.getChild( startOffset );
+
+ // In this case, move the start information to that text
+ // node.
+ if ( child && child.type == CKEDITOR.NODE_TEXT
+ && startOffset > 0 && child.getPrevious().type == CKEDITOR.NODE_TEXT )
+ {
+ startContainer = child;
+ startOffset = 0;
+ }
+
+ // Get the normalized offset.
+ if ( child && child.type == CKEDITOR.NODE_ELEMENT )
+ startOffset = child.getIndex( 1 );
+ }
+
+ // Normalize the start.
+ while ( startContainer.type == CKEDITOR.NODE_TEXT
+ && ( previous = startContainer.getPrevious() )
+ && previous.type == CKEDITOR.NODE_TEXT )
+ {
+ startContainer = previous;
+ startOffset += previous.getLength();
+ }
+
+ // Process the end only if not normalized.
+ if ( !collapsed )
+ {
+ // Find out if the start is pointing to a text node that
+ // will be normalized.
+ if ( endContainer.type == CKEDITOR.NODE_ELEMENT )
+ {
+ child = endContainer.getChild( endOffset );
+
+ // In this case, move the start information to that
+ // text node.
+ if ( child && child.type == CKEDITOR.NODE_TEXT
+ && endOffset > 0 && child.getPrevious().type == CKEDITOR.NODE_TEXT )
+ {
+ endContainer = child;
+ endOffset = 0;
+ }
+
+ // Get the normalized offset.
+ if ( child && child.type == CKEDITOR.NODE_ELEMENT )
+ endOffset = child.getIndex( 1 );
+ }
+
+ // Normalize the end.
+ while ( endContainer.type == CKEDITOR.NODE_TEXT
+ && ( previous = endContainer.getPrevious() )
+ && previous.type == CKEDITOR.NODE_TEXT )
+ {
+ endContainer = previous;
+ endOffset += previous.getLength();
+ }
+ }
+ }
+
+ return {
+ start : startContainer.getAddress( normalized ),
+ end : collapsed ? null : endContainer.getAddress( normalized ),
+ startOffset : startOffset,
+ endOffset : endOffset,
+ normalized : normalized,
+ collapsed : collapsed,
+ is2 : true // It's a createBookmark2 bookmark.
+ };
+ },
+
+ moveToBookmark : function( bookmark )
+ {
+ if ( bookmark.is2 ) // Created with createBookmark2().
+ {
+ // Get the start information.
+ var startContainer = this.document.getByAddress( bookmark.start, bookmark.normalized ),
+ startOffset = bookmark.startOffset;
+
+ // Get the end information.
+ var endContainer = bookmark.end && this.document.getByAddress( bookmark.end, bookmark.normalized ),
+ endOffset = bookmark.endOffset;
+
+ // Set the start boundary.
+ this.setStart( startContainer, startOffset );
+
+ // Set the end boundary. If not available, collapse it.
+ if ( endContainer )
+ this.setEnd( endContainer, endOffset );
+ else
+ this.collapse( true );
+ }
+ else // Created with createBookmark().
+ {
+ var serializable = bookmark.serializable,
+ startNode = serializable ? this.document.getById( bookmark.startNode ) : bookmark.startNode,
+ endNode = serializable ? this.document.getById( bookmark.endNode ) : bookmark.endNode;
+
+ // Set the range start at the bookmark start node position.
+ this.setStartBefore( startNode );
+
+ // Remove it, because it may interfere in the setEndBefore call.
+ startNode.remove();
+
+ // Set the range end at the bookmark end node position, or simply
+ // collapse it if it is not available.
+ if ( endNode )
+ {
+ this.setEndBefore( endNode );
+ endNode.remove();
+ }
+ else
+ this.collapse( true );
+ }
+ },
+
+ getBoundaryNodes : function()
+ {
+ var startNode = this.startContainer,
+ endNode = this.endContainer,
+ startOffset = this.startOffset,
+ endOffset = this.endOffset,
+ childCount;
+
+ if ( startNode.type == CKEDITOR.NODE_ELEMENT )
+ {
+ childCount = startNode.getChildCount();
+ if ( childCount > startOffset )
+ startNode = startNode.getChild( startOffset );
+ else if ( childCount < 1 )
+ startNode = startNode.getPreviousSourceNode();
+ else // startOffset > childCount but childCount is not 0
+ {
+ // Try to take the node just after the current position.
+ startNode = startNode.$;
+ while ( startNode.lastChild )
+ startNode = startNode.lastChild;
+ startNode = new CKEDITOR.dom.node( startNode );
+
+ // Normally we should take the next node in DFS order. But it
+ // is also possible that we've already reached the end of
+ // document.
+ startNode = startNode.getNextSourceNode() || startNode;
+ }
+ }
+ if ( endNode.type == CKEDITOR.NODE_ELEMENT )
+ {
+ childCount = endNode.getChildCount();
+ if ( childCount > endOffset )
+ endNode = endNode.getChild( endOffset ).getPreviousSourceNode( true );
+ else if ( childCount < 1 )
+ endNode = endNode.getPreviousSourceNode();
+ else // endOffset > childCount but childCount is not 0
+ {
+ // Try to take the node just before the current position.
+ endNode = endNode.$;
+ while ( endNode.lastChild )
+ endNode = endNode.lastChild;
+ endNode = new CKEDITOR.dom.node( endNode );
+ }
+ }
+
+ // Sometimes the endNode will come right before startNode for collapsed
+ // ranges. Fix it. (#3780)
+ if ( startNode.getPosition( endNode ) & CKEDITOR.POSITION_FOLLOWING )
+ startNode = endNode;
+
+ return { startNode : startNode, endNode : endNode };
+ },
+
+ /**
+ * Find the node which fully contains the range.
+ * @param includeSelf
+ * @param {Boolean} ignoreTextNode Whether ignore CKEDITOR.NODE_TEXT type.
+ */
+ getCommonAncestor : function( includeSelf , ignoreTextNode )
+ {
+ var start = this.startContainer,
+ end = this.endContainer,
+ ancestor;
+
+ if ( start.equals( end ) )
+ {
+ if ( includeSelf
+ && start.type == CKEDITOR.NODE_ELEMENT
+ && this.startOffset == this.endOffset - 1 )
+ ancestor = start.getChild( this.startOffset );
+ else
+ ancestor = start;
+ }
+ else
+ ancestor = start.getCommonAncestor( end );
+
+ return ignoreTextNode && !ancestor.is ? ancestor.getParent() : ancestor;
+ },
+
+ /**
+ * Transforms the startContainer and endContainer properties from text
+ * nodes to element nodes, whenever possible. This is actually possible
+ * if either of the boundary containers point to a text node, and its
+ * offset is set to zero, or after the last char in the node.
+ */
+ optimize : function()
+ {
+ var container = this.startContainer;
+ var offset = this.startOffset;
+
+ if ( container.type != CKEDITOR.NODE_ELEMENT )
+ {
+ if ( !offset )
+ this.setStartBefore( container );
+ else if ( offset >= container.getLength() )
+ this.setStartAfter( container );
+ }
+
+ container = this.endContainer;
+ offset = this.endOffset;
+
+ if ( container.type != CKEDITOR.NODE_ELEMENT )
+ {
+ if ( !offset )
+ this.setEndBefore( container );
+ else if ( offset >= container.getLength() )
+ this.setEndAfter( container );
+ }
+ },
+
+ /**
+ * Move the range out of bookmark nodes if they'd been the container.
+ */
+ optimizeBookmark: function()
+ {
+ var startNode = this.startContainer,
+ endNode = this.endContainer;
+
+ if ( startNode.is && startNode.is( 'span' )
+ && startNode.data( 'cke-bookmark' ) )
+ this.setStartAt( startNode, CKEDITOR.POSITION_BEFORE_START );
+ if ( endNode && endNode.is && endNode.is( 'span' )
+ && endNode.data( 'cke-bookmark' ) )
+ this.setEndAt( endNode, CKEDITOR.POSITION_AFTER_END );
+ },
+
+ trim : function( ignoreStart, ignoreEnd )
+ {
+ var startContainer = this.startContainer,
+ startOffset = this.startOffset,
+ collapsed = this.collapsed;
+ if ( ( !ignoreStart || collapsed )
+ && startContainer && startContainer.type == CKEDITOR.NODE_TEXT )
+ {
+ // If the offset is zero, we just insert the new node before
+ // the start.
+ if ( !startOffset )
+ {
+ startOffset = startContainer.getIndex();
+ startContainer = startContainer.getParent();
+ }
+ // If the offset is at the end, we'll insert it after the text
+ // node.
+ else if ( startOffset >= startContainer.getLength() )
+ {
+ startOffset = startContainer.getIndex() + 1;
+ startContainer = startContainer.getParent();
+ }
+ // In other case, we split the text node and insert the new
+ // node at the split point.
+ else
+ {
+ var nextText = startContainer.split( startOffset );
+
+ startOffset = startContainer.getIndex() + 1;
+ startContainer = startContainer.getParent();
+
+ // Check all necessity of updating the end boundary.
+ if ( this.startContainer.equals( this.endContainer ) )
+ this.setEnd( nextText, this.endOffset - this.startOffset );
+ else if ( startContainer.equals( this.endContainer ) )
+ this.endOffset += 1;
+ }
+
+ this.setStart( startContainer, startOffset );
+
+ if ( collapsed )
+ {
+ this.collapse( true );
+ return;
+ }
+ }
+
+ var endContainer = this.endContainer;
+ var endOffset = this.endOffset;
+
+ if ( !( ignoreEnd || collapsed )
+ && endContainer && endContainer.type == CKEDITOR.NODE_TEXT )
+ {
+ // If the offset is zero, we just insert the new node before
+ // the start.
+ if ( !endOffset )
+ {
+ endOffset = endContainer.getIndex();
+ endContainer = endContainer.getParent();
+ }
+ // If the offset is at the end, we'll insert it after the text
+ // node.
+ else if ( endOffset >= endContainer.getLength() )
+ {
+ endOffset = endContainer.getIndex() + 1;
+ endContainer = endContainer.getParent();
+ }
+ // In other case, we split the text node and insert the new
+ // node at the split point.
+ else
+ {
+ endContainer.split( endOffset );
+
+ endOffset = endContainer.getIndex() + 1;
+ endContainer = endContainer.getParent();
+ }
+
+ this.setEnd( endContainer, endOffset );
+ }
+ },
+
+ /**
+ * Expands the range so that partial units are completely contained.
+ * @param unit {Number} The unit type to expand with.
+ * @param {Boolean} [excludeBrs=false] Whether include line-breaks when expanding.
+ */
+ enlarge : function( unit, excludeBrs )
+ {
+ switch ( unit )
+ {
+ case CKEDITOR.ENLARGE_ELEMENT :
+
+ if ( this.collapsed )
+ return;
+
+ // Get the common ancestor.
+ var commonAncestor = this.getCommonAncestor();
+
+ var body = this.document.getBody();
+
+ // For each boundary
+ // a. Depending on its position, find out the first node to be checked (a sibling) or, if not available, to be enlarge.
+ // b. Go ahead checking siblings and enlarging the boundary as much as possible until the common ancestor is not reached. After reaching the common ancestor, just save the enlargeable node to be used later.
+
+ var startTop, endTop;
+
+ var enlargeable, sibling, commonReached;
+
+ // Indicates that the node can be added only if whitespace
+ // is available before it.
+ var needsWhiteSpace = false;
+ var isWhiteSpace;
+ var siblingText;
+
+ // Process the start boundary.
+
+ var container = this.startContainer;
+ var offset = this.startOffset;
+
+ if ( container.type == CKEDITOR.NODE_TEXT )
+ {
+ if ( offset )
+ {
+ // Check if there is any non-space text before the
+ // offset. Otherwise, container is null.
+ container = !CKEDITOR.tools.trim( container.substring( 0, offset ) ).length && container;
+
+ // If we found only whitespace in the node, it
+ // means that we'll need more whitespace to be able
+ // to expand. For example, can be expanded in
+ // "A [B]", but not in "A [B]".
+ needsWhiteSpace = !!container;
+ }
+
+ if ( container )
+ {
+ if ( !( sibling = container.getPrevious() ) )
+ enlargeable = container.getParent();
+ }
+ }
+ else
+ {
+ // If we have offset, get the node preceeding it as the
+ // first sibling to be checked.
+ if ( offset )
+ sibling = container.getChild( offset - 1 ) || container.getLast();
+
+ // If there is no sibling, mark the container to be
+ // enlarged.
+ if ( !sibling )
+ enlargeable = container;
+ }
+
+ while ( enlargeable || sibling )
+ {
+ if ( enlargeable && !sibling )
+ {
+ // If we reached the common ancestor, mark the flag
+ // for it.
+ if ( !commonReached && enlargeable.equals( commonAncestor ) )
+ commonReached = true;
+
+ if ( !body.contains( enlargeable ) )
+ break;
+
+ // If we don't need space or this element breaks
+ // the line, then enlarge it.
+ if ( !needsWhiteSpace || enlargeable.getComputedStyle( 'display' ) != 'inline' )
+ {
+ needsWhiteSpace = false;
+
+ // If the common ancestor has been reached,
+ // we'll not enlarge it immediately, but just
+ // mark it to be enlarged later if the end
+ // boundary also enlarges it.
+ if ( commonReached )
+ startTop = enlargeable;
+ else
+ this.setStartBefore( enlargeable );
+ }
+
+ sibling = enlargeable.getPrevious();
+ }
+
+ // Check all sibling nodes preceeding the enlargeable
+ // node. The node wil lbe enlarged only if none of them
+ // blocks it.
+ while ( sibling )
+ {
+ // This flag indicates that this node has
+ // whitespaces at the end.
+ isWhiteSpace = false;
+
+ if ( sibling.type == CKEDITOR.NODE_TEXT )
+ {
+ siblingText = sibling.getText();
+
+ if ( /[^\s\ufeff]/.test( siblingText ) )
+ sibling = null;
+
+ isWhiteSpace = /[\s\ufeff]$/.test( siblingText );
+ }
+ else
+ {
+ // If this is a visible element.
+ // We need to check for the bookmark attribute because IE insists on
+ // rendering the display:none nodes we use for bookmarks. (#3363)
+ // Line-breaks (br) are rendered with zero width, which we don't want to include. (#7041)
+ if ( ( sibling.$.offsetWidth > 0 || excludeBrs && sibling.is( 'br' ) ) && !sibling.data( 'cke-bookmark' ) )
+ {
+ // We'll accept it only if we need
+ // whitespace, and this is an inline
+ // element with whitespace only.
+ if ( needsWhiteSpace && CKEDITOR.dtd.$removeEmpty[ sibling.getName() ] )
+ {
+ // It must contains spaces and inline elements only.
+
+ siblingText = sibling.getText();
+
+ if ( (/[^\s\ufeff]/).test( siblingText ) ) // Spaces + Zero Width No-Break Space (U+FEFF)
+ sibling = null;
+ else
+ {
+ var allChildren = sibling.$.all || sibling.$.getElementsByTagName( '*' );
+ for ( var i = 0, child ; child = allChildren[ i++ ] ; )
+ {
+ if ( !CKEDITOR.dtd.$removeEmpty[ child.nodeName.toLowerCase() ] )
+ {
+ sibling = null;
+ break;
+ }
+ }
+ }
+
+ if ( sibling )
+ isWhiteSpace = !!siblingText.length;
+ }
+ else
+ sibling = null;
+ }
+ }
+
+ // A node with whitespaces has been found.
+ if ( isWhiteSpace )
+ {
+ // Enlarge the last enlargeable node, if we
+ // were waiting for spaces.
+ if ( needsWhiteSpace )
+ {
+ if ( commonReached )
+ startTop = enlargeable;
+ else if ( enlargeable )
+ this.setStartBefore( enlargeable );
+ }
+ else
+ needsWhiteSpace = true;
+ }
+
+ if ( sibling )
+ {
+ var next = sibling.getPrevious();
+
+ if ( !enlargeable && !next )
+ {
+ // Set the sibling as enlargeable, so it's
+ // parent will be get later outside this while.
+ enlargeable = sibling;
+ sibling = null;
+ break;
+ }
+
+ sibling = next;
+ }
+ else
+ {
+ // If sibling has been set to null, then we
+ // need to stop enlarging.
+ enlargeable = null;
+ }
+ }
+
+ if ( enlargeable )
+ enlargeable = enlargeable.getParent();
+ }
+
+ // Process the end boundary. This is basically the same
+ // code used for the start boundary, with small changes to
+ // make it work in the oposite side (to the right). This
+ // makes it difficult to reuse the code here. So, fixes to
+ // the above code are likely to be replicated here.
+
+ container = this.endContainer;
+ offset = this.endOffset;
+
+ // Reset the common variables.
+ enlargeable = sibling = null;
+ commonReached = needsWhiteSpace = false;
+
+ if ( container.type == CKEDITOR.NODE_TEXT )
+ {
+ // Check if there is any non-space text after the
+ // offset. Otherwise, container is null.
+ container = !CKEDITOR.tools.trim( container.substring( offset ) ).length && container;
+
+ // If we found only whitespace in the node, it
+ // means that we'll need more whitespace to be able
+ // to expand. For example, can be expanded in
+ // "A [B]", but not in "A [B]".
+ needsWhiteSpace = !( container && container.getLength() );
+
+ if ( container )
+ {
+ if ( !( sibling = container.getNext() ) )
+ enlargeable = container.getParent();
+ }
+ }
+ else
+ {
+ // Get the node right after the boudary to be checked
+ // first.
+ sibling = container.getChild( offset );
+
+ if ( !sibling )
+ enlargeable = container;
+ }
+
+ while ( enlargeable || sibling )
+ {
+ if ( enlargeable && !sibling )
+ {
+ if ( !commonReached && enlargeable.equals( commonAncestor ) )
+ commonReached = true;
+
+ if ( !body.contains( enlargeable ) )
+ break;
+
+ if ( !needsWhiteSpace || enlargeable.getComputedStyle( 'display' ) != 'inline' )
+ {
+ needsWhiteSpace = false;
+
+ if ( commonReached )
+ endTop = enlargeable;
+ else if ( enlargeable )
+ this.setEndAfter( enlargeable );
+ }
+
+ sibling = enlargeable.getNext();
+ }
+
+ while ( sibling )
+ {
+ isWhiteSpace = false;
+
+ if ( sibling.type == CKEDITOR.NODE_TEXT )
+ {
+ siblingText = sibling.getText();
+
+ if ( /[^\s\ufeff]/.test( siblingText ) )
+ sibling = null;
+
+ isWhiteSpace = /^[\s\ufeff]/.test( siblingText );
+ }
+ else
+ {
+ // If this is a visible element.
+ // We need to check for the bookmark attribute because IE insists on
+ // rendering the display:none nodes we use for bookmarks. (#3363)
+ // Line-breaks (br) are rendered with zero width, which we don't want to include. (#7041)
+ if ( ( sibling.$.offsetWidth > 0 || excludeBrs && sibling.is( 'br' ) ) && !sibling.data( 'cke-bookmark' ) )
+ {
+ // We'll accept it only if we need
+ // whitespace, and this is an inline
+ // element with whitespace only.
+ if ( needsWhiteSpace && CKEDITOR.dtd.$removeEmpty[ sibling.getName() ] )
+ {
+ // It must contains spaces and inline elements only.
+
+ siblingText = sibling.getText();
+
+ if ( (/[^\s\ufeff]/).test( siblingText ) )
+ sibling = null;
+ else
+ {
+ allChildren = sibling.$.all || sibling.$.getElementsByTagName( '*' );
+ for ( i = 0 ; child = allChildren[ i++ ] ; )
+ {
+ if ( !CKEDITOR.dtd.$removeEmpty[ child.nodeName.toLowerCase() ] )
+ {
+ sibling = null;
+ break;
+ }
+ }
+ }
+
+ if ( sibling )
+ isWhiteSpace = !!siblingText.length;
+ }
+ else
+ sibling = null;
+ }
+ }
+
+ if ( isWhiteSpace )
+ {
+ if ( needsWhiteSpace )
+ {
+ if ( commonReached )
+ endTop = enlargeable;
+ else
+ this.setEndAfter( enlargeable );
+ }
+ }
+
+ if ( sibling )
+ {
+ next = sibling.getNext();
+
+ if ( !enlargeable && !next )
+ {
+ enlargeable = sibling;
+ sibling = null;
+ break;
+ }
+
+ sibling = next;
+ }
+ else
+ {
+ // If sibling has been set to null, then we
+ // need to stop enlarging.
+ enlargeable = null;
+ }
+ }
+
+ if ( enlargeable )
+ enlargeable = enlargeable.getParent();
+ }
+
+ // If the common ancestor can be enlarged by both boundaries, then include it also.
+ if ( startTop && endTop )
+ {
+ commonAncestor = startTop.contains( endTop ) ? endTop : startTop;
+
+ this.setStartBefore( commonAncestor );
+ this.setEndAfter( commonAncestor );
+ }
+ break;
+
+ case CKEDITOR.ENLARGE_BLOCK_CONTENTS:
+ case CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS:
+
+ // Enlarging the start boundary.
+ var walkerRange = new CKEDITOR.dom.range( this.document );
+
+ body = this.document.getBody();
+
+ walkerRange.setStartAt( body, CKEDITOR.POSITION_AFTER_START );
+ walkerRange.setEnd( this.startContainer, this.startOffset );
+
+ var walker = new CKEDITOR.dom.walker( walkerRange ),
+ blockBoundary, // The node on which the enlarging should stop.
+ tailBr, // In case BR as block boundary.
+ notBlockBoundary = CKEDITOR.dom.walker.blockBoundary(
+ ( unit == CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS ) ? { br : 1 } : null ),
+ // Record the encountered 'blockBoundary' for later use.
+ boundaryGuard = function( node )
+ {
+ var retval = notBlockBoundary( node );
+ if ( !retval )
+ blockBoundary = node;
+ return retval;
+ },
+ // Record the encounted 'tailBr' for later use.
+ tailBrGuard = function( node )
+ {
+ var retval = boundaryGuard( node );
+ if ( !retval && node.is && node.is( 'br' ) )
+ tailBr = node;
+ return retval;
+ };
+
+ walker.guard = boundaryGuard;
+
+ enlargeable = walker.lastBackward();
+
+ // It's the body which stop the enlarging if no block boundary found.
+ blockBoundary = blockBoundary || body;
+
+ // Start the range either after the end of found block (