Conflicts:
	static/favicon.ico
	static/index.html
	static/pad.html
This commit is contained in:
Eric Martindale 2012-03-01 05:39:08 -05:00
commit 326de3ad26
92 changed files with 4501 additions and 9505 deletions

4
.gitignore vendored
View file

@ -1,6 +1,7 @@
node_modules node_modules
settings.json settings.json
static/js/jquery.min.js static/js/jquery.js
static/js/prefixfree.js
APIKEY.txt APIKEY.txt
bin/abiword.exe bin/abiword.exe
bin/node.exe bin/node.exe
@ -9,3 +10,4 @@ var/dirty.db
bin/convertSettings.json bin/convertSettings.json
*~ *~
*.patch *.patch
*.DS_Store

202
LICENSE Normal file
View file

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2012 THE ETHERPAD FOUNDATION
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -58,7 +58,7 @@ Here is the **[FAQ](https://github.com/Pita/etherpad-lite/wiki/FAQ)**
<ol> <ol>
<li>Install the dependencies. We need gzip, git, curl, libssl develop libraries, python and gcc. <br><i>For Debian/Ubuntu</i> <code>apt-get install gzip git-core curl python libssl-dev build-essential</code><br> <li>Install the dependencies. We need gzip, git, curl, libssl develop libraries, python and gcc. <br><i>For Debian/Ubuntu</i> <code>apt-get install gzip git-core curl python libssl-dev build-essential</code><br>
<i>For Fedora/CentOS</i> <code>yum install gzip git-core curl python openssl-dev && yum groupinstall "Development Tools"</code> <i>For Fedora/CentOS</i> <code>yum install gzip git-core curl python openssl-devel && yum groupinstall "Development Tools"</code>
</li><br> </li><br>
<li>Install node.js <li>Install node.js
<ol type="a"> <ol type="a">
@ -81,6 +81,8 @@ Here is the **[FAQ](https://github.com/Pita/etherpad-lite/wiki/FAQ)**
## Next Steps ## Next Steps
You can modify the settings in the file `settings.json` You can modify the settings in the file `settings.json`
If you have multiple settings files, you may pass one to `bin/run.sh` using the `-s|--settings` option. This allows you to run multiple Etherpad Lite instances from the same installation.
You should use a dedicated database such as "mysql" if you are planning on using etherpad-lite in a production environment, the "dirty" database driver is only for testing and/or development purposes. You should use a dedicated database such as "mysql" if you are planning on using etherpad-lite in a production environment, the "dirty" database driver is only for testing and/or development purposes.
You can update to the latest version with `git pull origin`. The next start with bin/run.sh will update the dependencies You can update to the latest version with `git pull origin`. The next start with bin/run.sh will update the dependencies
@ -98,7 +100,7 @@ Look at this wiki pages:
You can find more information in the [wiki](https://github.com/Pita/etherpad-lite/wiki). Feel free to improve these wiki pages You can find more information in the [wiki](https://github.com/Pita/etherpad-lite/wiki). Feel free to improve these wiki pages
# Develop # Develop
If you're new to git and github, start here <http://learn.github.com/p/intro.html>. If you're new to git and github, start by watching [this video](http://youtu.be/67-Q26YH97E) then read this [git guide](http://learn.github.com/p/intro.html).
If you're new to node.js, start with this video <http://youtu.be/jo_B4LTHi3I>. If you're new to node.js, start with this video <http://youtu.be/jo_B4LTHi3I>.
@ -111,11 +113,18 @@ You can join the [mailinglist](http://groups.google.com/group/etherpad-lite-dev)
You also help the project, if you only host a Etherpad Lite instance and share your experience with us. You also help the project, if you only host a Etherpad Lite instance and share your experience with us.
Please consider using [jshint](http://www.jshint.com/about/) if you plan to
contribute to Etherpad Lite.
# Modules created for this project # Modules created for this project
* [ueberDB](https://github.com/Pita/ueberDB) "transforms every database into a object key value store" - manages all database access * [ueberDB](https://github.com/Pita/ueberDB) "transforms every database into a object key value store" - manages all database access
* [channels](https://github.com/Pita/channels) "Event channels in node.js" - ensures that ueberDB operations are atomic and in series for each key * [channels](https://github.com/Pita/channels) "Event channels in node.js" - ensures that ueberDB operations are atomic and in series for each key
* [async-stacktrace](https://github.com/Pita/async-stacktrace) "Improves node.js stacktraces and makes it easier to handle errors" * [async-stacktrace](https://github.com/Pita/async-stacktrace) "Improves node.js stacktraces and makes it easier to handle errors"
# Donations
* [Etherpad Foundation Flattr] (http://flattr.com/thing/71378/Etherpad-Foundation)
* [Paypal] (http://etherpad.org) <-- Click the donate button
# License # License
[Apache License v2](http://www.apache.org/licenses/LICENSE-2.0.html) [Apache License v2](http://www.apache.org/licenses/LICENSE-2.0.html)

View file

@ -15,7 +15,8 @@ var log4js = require("log4js");
log4js.setGlobalLogLevel("INFO"); log4js.setGlobalLogLevel("INFO");
var async = require("async"); var async = require("async");
var db = require('../node/db/DB'); var db = require('../node/db/DB');
var Changeset = require('../node/utils/Changeset'); var CommonCode = require('../node/utils/common_code');
var Changeset = CommonCode.require("/Changeset");
var padManager; var padManager;
async.series([ async.series([

View file

@ -1,10 +1,12 @@
var CommonCode = require('../node/utils/common_code');
var startTime = new Date().getTime(); var startTime = new Date().getTime();
var fs = require("fs"); var fs = require("fs");
var ueberDB = require("ueberDB"); var ueberDB = require("ueberDB");
var mysql = require("mysql"); var mysql = require("mysql");
var async = require("async"); var async = require("async");
var Changeset = require("../node/utils/Changeset"); var Changeset = CommonCode.require("/Changeset");
var AttributePoolFactory = require("../node/utils/AttributePoolFactory"); var randomString = CommonCode.require('/pad_utils').randomString;
var AttributePoolFactory = CommonCode.require("/AttributePoolFactory");
var settingsFile = process.argv[2]; var settingsFile = process.argv[2];
var sqlOutputFile = process.argv[3]; var sqlOutputFile = process.argv[3];
@ -450,18 +452,3 @@ function parsePage(array, pageStart, offsets, data, json)
start+=unitLength; start+=unitLength;
} }
} }
/**
* Generates a random String with the given length. Is needed to generate the Author Ids
*/
function randomString(len)
{
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var randomstring = '';
for (var i = 0; i < len; i++)
{
var rnum = Math.floor(Math.random() * chars.length);
randomstring += chars.substring(rnum, rnum + 1);
}
return randomstring;
}

View file

@ -16,7 +16,7 @@ hash curl > /dev/null 2>&1 || {
#Is node installed? #Is node installed?
hash node > /dev/null 2>&1 || { hash node > /dev/null 2>&1 || {
echo "Please install node.js ( http://nodesjs.org )" >&2 echo "Please install node.js ( http://nodejs.org )" >&2
exit 1 exit 1
} }
@ -33,10 +33,25 @@ if [ ! $(echo $NPM_VERSION | cut -d "." -f 1) = "1" ]; then
exit 1 exit 1
fi fi
#Does a settings.json exist? if no copy the template #check node version
if [ ! -f "settings.json" ]; then NODE_VERSION=$(node --version)
echo "Copy the settings template to settings.json..." if [ ! $(echo $NODE_VERSION | cut -d "." -f 1-2) = "v0.6" ]; then
cp -v settings.json.template settings.json || exit 1 echo "You're running a wrong version of node, you're using $NODE_VERSION, we need v0.6.x" >&2
exit 1
fi
#Get the name of the settings file
settings="settings.json"
a='';
for arg in $*; do
if [ "$a" = "--settings" ] || [ "$a" = "-s" ]; then settings=$arg; fi
a=$arg
done
#Does a $settings exist? if no copy the template
if [ ! -f $settings ]; then
echo "Copy the settings template to $settings..."
cp -v settings.json.template $settings || exit 1
fi fi
echo "Ensure that all dependencies are up to date..." echo "Ensure that all dependencies are up to date..."
@ -47,9 +62,9 @@ npm install || {
echo "Ensure jQuery is downloaded and up to date..." echo "Ensure jQuery is downloaded and up to date..."
DOWNLOAD_JQUERY="true" DOWNLOAD_JQUERY="true"
NEEDED_VERSION="1.7" NEEDED_VERSION="1.7.1"
if [ -f "static/js/jquery.min.js" ]; then if [ -f "static/js/jquery.js" ]; then
VERSION=$(cat static/js/jquery.min.js | head -n 3 | grep -o "v[0-9].[0-9]"); VERSION=$(cat static/js/jquery.js | head -n 3 | grep -o "v[0-9]\.[0-9]\(\.[0-9]\)\?");
if [ ${VERSION#v} = $NEEDED_VERSION ]; then if [ ${VERSION#v} = $NEEDED_VERSION ]; then
DOWNLOAD_JQUERY="false" DOWNLOAD_JQUERY="false"
@ -57,7 +72,22 @@ if [ -f "static/js/jquery.min.js" ]; then
fi fi
if [ $DOWNLOAD_JQUERY = "true" ]; then if [ $DOWNLOAD_JQUERY = "true" ]; then
curl -lo static/js/jquery.min.js http://code.jquery.com/jquery-$NEEDED_VERSION.min.js || exit 1 curl -lo static/js/jquery.js http://code.jquery.com/jquery-$NEEDED_VERSION.js || exit 1
fi
echo "Ensure prefixfree is downloaded and up to date..."
DOWNLOAD_PREFIXFREE="true"
NEEDED_VERSION="1.0.4"
if [ -f "static/js/prefixfree.js" ]; then
VERSION=$(cat static/js/prefixfree.js | grep "PrefixFree" | grep -o "[0-9].[0-9].[0-9]");
if [ $VERSION = $NEEDED_VERSION ]; then
DOWNLOAD_PREFIXFREE="false"
fi
fi
if [ $DOWNLOAD_PREFIXFREE = "true" ]; then
curl -lo static/js/prefixfree.js -k https://raw.github.com/LeaVerou/prefixfree/master/prefixfree.js || exit 1
fi fi
#Remove all minified data to force node creating it new #Remove all minified data to force node creating it new

9
bin/jshint.sh Executable file
View file

@ -0,0 +1,9 @@
#!/bin/sh
if [ -d "../bin" ]; then
cd "../"
fi
JSHINT=./node_modules/jshint/bin/hint
$JSHINT ./node/

75
bin/loadTesting/README Normal file
View file

@ -0,0 +1,75 @@
This load tester is extremely useful for testing how many dormant clients can connect to etherpad lite.
TODO:
Emulate characters being typed into a pad
HOW TO USE (from @mjd75) proper formatting at: https://github.com/Pita/etherpad-lite/issues/360
Server 1:
Installed Node.js (etc), EtherPad Lite and MySQL
Server 2:
Installed Xvfb and PhantomJS
I installed Xvfb following (roughly) this guide: http://blog.martin-lyness.com/archives/installing-xvfb-on-ubuntu-9-10-karmic-koala
#sudo apt-get install xvfb
#sudo apt-get install xfonts-100dpi xfonts-75dpi xfonts-scalable xfonts-cyrillic
Launched two instances of Xvfb directly from the terminal:
#Xvfb :0 -ac
#Xvfb :1 -ac
I installed PhantomJS following this guide: http://code.google.com/p/phantomjs/wiki/Installation
#sudo add-apt-repository ppa:jerome-etienne/neoip
#sudo apt-get update
#sudo apt-get install phantomjs
I created a small JavaScript file for PhatomJS to use to control the browser instances:
### BEGIN JAVASCRIPT ###
var page = new WebPage(),
t, address;
if (phantom.args.length === 0) {
console.log('Usage: loader.js <some URL>');
phantom.exit();
} else {
t = Date.now();
address = phantom.args[0];
var page = new WebPage();
page.onResourceRequested = function (request) {
console.log('Request ' + JSON.stringify(request, undefined, 4));
};
page.onResourceReceived = function (response) {
console.log('Receive ' + JSON.stringify(response, undefined, 4));
};
page.open(address);
}
### END JAVASCRIPT ###
And finally a launcher script that uses screen to run 400 instances of PhantomJS with the above script:
### BEGIN SHELL SCRIPT ###
#!/bin/bash
# connect 200 instances to display :0
for i in {1..200}
do
DISPLAY=:0 screen -d -m phantomjs loader.js http://ec2-50-17-168-xx.compute-1.amazonaws.com:9001/p/pad2 && sleep 2
done
# connect 200 instances to display :1
for i in {1..200}
do
DISPLAY=:1 screen -d -m phantomjs loader.js http://ec2-50-17-168-xx.compute-1.amazonaws.com:9001/p/pad2 && sleep 2
done
### END SHELL SCRIPT ###

16
bin/loadTesting/launcher.sh Executable file
View file

@ -0,0 +1,16 @@
#!/bin/bash
# connect 500 instances to display :0
for i in {1..500}
do
echo $i
echo "Displaying Some shit"
DISPLAY=:0 screen -d -m /home/phantomjs/bin/phantomjs loader.js http://10.0.0.55:9001/p/pad2 && sleep 2
done
# connect 500 instances to display :1
for i in {1..500}
do
echo $i
DISPLAY=:1 screen -d -m /home/phantomjs/bin/phantomjs loader.js http://10.0.0.55:9001/p/pad2 && sleep 2
done

20
bin/loadTesting/loader.js Normal file
View file

@ -0,0 +1,20 @@
var page = new WebPage(),
t, address;
if (phantom.args.length === 0) {
console.log('Usage: loader.js <some URL>');
phantom.exit();
} else {
t = Date.now();
address = phantom.args[0];
var page = new WebPage();
page.onResourceRequested = function (request) {
console.log('Request ' + JSON.stringify(request, undefined, 4));
};
page.onResourceReceived = function (response) {
console.log('Receive ' + JSON.stringify(response, undefined, 4));
};
page.open(address);
}

View file

@ -21,9 +21,9 @@ if [ "$(id -u)" -eq 0 ]; then
fi fi
#prepare the enviroment #prepare the enviroment
bin/installDeps.sh || exit 1 bin/installDeps.sh $* || exit 1
#Move to the node folder and start #Move to the node folder and start
echo "start..." echo "start..."
cd "node" cd "node"
node server.js node server.js $*

View file

@ -6,7 +6,7 @@
# Module name conventions # Module name conventions
Module file names starts with a capital letter and uses camelCase Module file names start with a capital letter and uses camelCase
# Where does it start? # Where does it start?

View file

@ -293,7 +293,7 @@ Example returns:
exports.createPad = function(padID, text, callback) exports.createPad = function(padID, text, callback)
{ {
//ensure there is no $ in the padID //ensure there is no $ in the padID
if(padID.indexOf("$") != -1) if(padID && padID.indexOf("$") != -1)
{ {
callback(new customError("createPad can't create group pads","apierror")); callback(new customError("createPad can't create group pads","apierror"));
return; return;
@ -360,7 +360,7 @@ Example returns:
exports.setPublicStatus = function(padID, publicStatus, callback) exports.setPublicStatus = function(padID, publicStatus, callback)
{ {
//ensure this is a group pad //ensure this is a group pad
if(padID.indexOf("$") == -1) if(padID && padID.indexOf("$") == -1)
{ {
callback(new customError("You can only get/set the publicStatus of pads that belong to a group","apierror")); callback(new customError("You can only get/set the publicStatus of pads that belong to a group","apierror"));
return; return;
@ -393,7 +393,7 @@ Example returns:
exports.getPublicStatus = function(padID, callback) exports.getPublicStatus = function(padID, callback)
{ {
//ensure this is a group pad //ensure this is a group pad
if(padID.indexOf("$") == -1) if(padID && padID.indexOf("$") == -1)
{ {
callback(new customError("You can only get/set the publicStatus of pads that belong to a group","apierror")); callback(new customError("You can only get/set the publicStatus of pads that belong to a group","apierror"));
return; return;
@ -419,7 +419,7 @@ Example returns:
exports.setPassword = function(padID, password, callback) exports.setPassword = function(padID, password, callback)
{ {
//ensure this is a group pad //ensure this is a group pad
if(padID.indexOf("$") == -1) if(padID && padID.indexOf("$") == -1)
{ {
callback(new customError("You can only get/set the password of pads that belong to a group","apierror")); callback(new customError("You can only get/set the password of pads that belong to a group","apierror"));
return; return;
@ -448,7 +448,7 @@ Example returns:
exports.isPasswordProtected = function(padID, callback) exports.isPasswordProtected = function(padID, callback)
{ {
//ensure this is a group pad //ensure this is a group pad
if(padID.indexOf("$") == -1) if(padID && padID.indexOf("$") == -1)
{ {
callback(new customError("You can only get/set the password of pads that belong to a group","apierror")); callback(new customError("You can only get/set the password of pads that belong to a group","apierror"));
return; return;

View file

@ -18,9 +18,11 @@
* limitations under the License. * limitations under the License.
*/ */
var CommonCode = require('../utils/common_code');
var ERR = require("async-stacktrace"); var ERR = require("async-stacktrace");
var db = require("./DB").db; var db = require("./DB").db;
var async = require("async"); var async = require("async");
var randomString = CommonCode.require('/pad_utils').randomString;
/** /**
* Checks if the author exists * Checks if the author exists
@ -177,18 +179,3 @@ exports.setAuthorName = function (author, name, callback)
{ {
db.setSub("globalAuthor:" + author, ["name"], name, callback); db.setSub("globalAuthor:" + author, ["name"], name, callback);
} }
/**
* Generates a random String with the given length. Is needed to generate the Author Ids
*/
function randomString(len)
{
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var randomstring = '';
for (var i = 0; i < len; i++)
{
var rnum = Math.floor(Math.random() * chars.length);
randomstring += chars.substring(rnum, rnum + 1);
}
return randomstring;
}

View file

@ -18,8 +18,10 @@
* limitations under the License. * limitations under the License.
*/ */
var CommonCode = require('../utils/common_code');
var ERR = require("async-stacktrace"); var ERR = require("async-stacktrace");
var customError = require("../utils/customError"); var customError = require("../utils/customError");
var randomString = CommonCode.require('/pad_utils').randomString;
var db = require("./DB").db; var db = require("./DB").db;
var async = require("async"); var async = require("async");
var padManager = require("./PadManager"); var padManager = require("./PadManager");
@ -247,26 +249,15 @@ exports.listPads = function(groupID, callback)
//group exists, let's get the pads //group exists, let's get the pads
else else
{ {
db.getSub("group:" + groupID, ["pads"], function(err, pads) db.getSub("group:" + groupID, ["pads"], function(err, result)
{ {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
var pads = [];
for ( var padId in result ) {
pads.push(padId);
}
callback(null, {padIDs: pads}); callback(null, {padIDs: pads});
}); });
} }
}); });
} }
/**
* Generates a random String with the given length. Is needed to generate the Author Ids
*/
function randomString(len)
{
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var randomstring = '';
for (var i = 0; i < len; i++)
{
var rnum = Math.floor(Math.random() * chars.length);
randomstring += chars.substring(rnum, rnum + 1);
}
return randomstring;
}

View file

@ -2,11 +2,11 @@
* The pad object, defined with joose * The pad object, defined with joose
*/ */
require('joose'); var CommonCode = require('../utils/common_code');
var ERR = require("async-stacktrace"); var ERR = require("async-stacktrace");
var Changeset = require("../utils/Changeset"); var Changeset = CommonCode.require("/Changeset");
var AttributePoolFactory = require("../utils/AttributePoolFactory"); var AttributePoolFactory = CommonCode.require("/AttributePoolFactory");
var randomString = CommonCode.require('/pad_utils').randomString;
var db = require("./DB").db; var db = require("./DB").db;
var async = require("async"); var async = require("async");
var settings = require('../utils/Settings'); var settings = require('../utils/Settings');
@ -22,60 +22,36 @@ var crypto = require("crypto");
*/ */
exports.cleanText = function (txt) { exports.cleanText = function (txt) {
return txt.replace(/\r\n/g,'\n').replace(/\r/g,'\n').replace(/\t/g, ' ').replace(/\xa0/g, ' '); return txt.replace(/\r\n/g,'\n').replace(/\r/g,'\n').replace(/\t/g, ' ').replace(/\xa0/g, ' ');
} };
Class('Pad', {
// these are the properties var Pad = function Pad(id) {
has : {
atext : { this.atext = Changeset.makeAText("\n");
is : 'rw', // readwrite this.pool = AttributePoolFactory.createAttributePool();
init : function() { return Changeset.makeAText("\n"); } // first value this.head = -1;
}, // atext this.chatHead = -1;
this.publicStatus = false;
this.passwordHash = null;
this.id = id;
pool : { };
is: 'rw',
init : function() { return AttributePoolFactory.createAttributePool(); },
getterName : 'apool' // legacy
}, // pool
head : { exports.Pad = Pad;
is : 'rw',
init : -1,
getterName : 'getHeadRevisionNumber'
}, // head
chatHead : { Pad.prototype.apool = function apool() {
is: 'rw', return this.pool;
init: -1 };
}, // chatHead
publicStatus : { Pad.prototype.getHeadRevisionNumber = function getHeadRevisionNumber() {
is: 'rw', return this.head;
init: false, };
getterName : 'getPublicStatus'
}, //publicStatus
passwordHash : { Pad.prototype.getPublicStatus = function getPublicStatus() {
is: 'rw', return this.publicStatus;
init: null };
}, // passwordHash
id : { is : 'r' } Pad.prototype.appendRevision = function appendRevision(aChangeset, author) {
},
methods : {
BUILD : function (id)
{
return {
'id' : id,
}
},
appendRevision : function(aChangeset, author)
{
if(!author) if(!author)
author = ''; author = '';
@ -106,25 +82,21 @@ Class('Pad', {
chatHead: this.chatHead, chatHead: this.chatHead,
publicStatus: this.publicStatus, publicStatus: this.publicStatus,
passwordHash: this.passwordHash}); passwordHash: this.passwordHash});
}, //appendRevision };
getRevisionChangeset : function(revNum, callback) Pad.prototype.getRevisionChangeset = function getRevisionChangeset(revNum, callback) {
{
db.getSub("pad:"+this.id+":revs:"+revNum, ["changeset"], callback); db.getSub("pad:"+this.id+":revs:"+revNum, ["changeset"], callback);
}, // getRevisionChangeset };
getRevisionAuthor : function(revNum, callback) Pad.prototype.getRevisionAuthor = function getRevisionAuthor(revNum, callback) {
{
db.getSub("pad:"+this.id+":revs:"+revNum, ["meta", "author"], callback); db.getSub("pad:"+this.id+":revs:"+revNum, ["meta", "author"], callback);
}, // getRevisionAuthor };
getRevisionDate : function(revNum, callback) Pad.prototype.getRevisionDate = function getRevisionDate(revNum, callback) {
{
db.getSub("pad:"+this.id+":revs:"+revNum, ["meta", "timestamp"], callback); db.getSub("pad:"+this.id+":revs:"+revNum, ["meta", "timestamp"], callback);
}, // getRevisionAuthor };
getAllAuthors : function() Pad.prototype.getAllAuthors = function getAllAuthors() {
{
var authors = []; var authors = [];
for(key in this.pool.numToAttrib) for(key in this.pool.numToAttrib)
@ -136,10 +108,9 @@ Class('Pad', {
} }
return authors; return authors;
}, };
getInternalRevisionAText : function(targetRev, callback) Pad.prototype.getInternalRevisionAText = function getInternalRevisionAText(targetRev, callback) {
{
var _this = this; var _this = this;
var keyRev = this.getKeyRevisionNumber(targetRev); var keyRev = this.getKeyRevisionNumber(targetRev);
@ -205,20 +176,17 @@ Class('Pad', {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
callback(null, atext); callback(null, atext);
}); });
}, };
getKeyRevisionNumber : function(revNum) Pad.prototype.getKeyRevisionNumber = function getKeyRevisionNumber(revNum) {
{
return Math.floor(revNum / 100) * 100; return Math.floor(revNum / 100) * 100;
}, };
text : function() Pad.prototype.text = function text() {
{
return this.atext.text; return this.atext.text;
}, };
setText : function(newText) Pad.prototype.setText = function setText(newText) {
{
//clean the new text //clean the new text
newText = exports.cleanText(newText); newText = exports.cleanText(newText);
@ -229,19 +197,17 @@ Class('Pad', {
//append the changeset //append the changeset
this.appendRevision(changeset); this.appendRevision(changeset);
}, };
appendChatMessage: function(text, userId, time) Pad.prototype.appendChatMessage = function appendChatMessage(text, userId, time) {
{
this.chatHead++; this.chatHead++;
//save the chat entry in the database //save the chat entry in the database
db.set("pad:"+this.id+":chat:"+this.chatHead, {"text": text, "userId": userId, "time": time}); db.set("pad:"+this.id+":chat:"+this.chatHead, {"text": text, "userId": userId, "time": time});
//save the new chat head //save the new chat head
db.setSub("pad:"+this.id, ["chatHead"], this.chatHead); db.setSub("pad:"+this.id, ["chatHead"], this.chatHead);
}, };
getChatMessage: function(entryNum, callback) Pad.prototype.getChatMessage = function getChatMessage(entryNum, callback) {
{
var _this = this; var _this = this;
var entry; var entry;
@ -279,10 +245,9 @@ Class('Pad', {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
callback(null, entry); callback(null, entry);
}); });
}, };
getLastChatMessages: function(count, callback) Pad.prototype.getLastChatMessages = function getLastChatMessages(count, callback) {
{
//return an empty array if there are no chat messages //return an empty array if there are no chat messages
if(this.chatHead == -1) if(this.chatHead == -1)
{ {
@ -340,10 +305,9 @@ Class('Pad', {
callback(null, cleanedEntries); callback(null, cleanedEntries);
}); });
}, };
init : function (text, callback) Pad.prototype.init = function init(text, callback) {
{
var _this = this; var _this = this;
//replace text with default text if text isn't set //replace text with default text if text isn't set
@ -392,9 +356,9 @@ Class('Pad', {
callback(null); callback(null);
}); });
}, };
remove: function(callback)
{ Pad.prototype.remove = function remove(callback) {
var padID = this.id; var padID = this.id;
var _this = this; var _this = this;
@ -483,29 +447,26 @@ Class('Pad', {
{ {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
callback(); callback();
}) });
}, };
//set in db //set in db
setPublicStatus: function(publicStatus) Pad.prototype.setPublicStatus = function setPublicStatus(publicStatus) {
{
this.publicStatus = publicStatus; this.publicStatus = publicStatus;
db.setSub("pad:"+this.id, ["publicStatus"], this.publicStatus); db.setSub("pad:"+this.id, ["publicStatus"], this.publicStatus);
}, };
setPassword: function(password)
{ Pad.prototype.setPassword = function setPassword(password) {
this.passwordHash = password == null ? null : hash(password, generateSalt()); this.passwordHash = password == null ? null : hash(password, generateSalt());
db.setSub("pad:"+this.id, ["passwordHash"], this.passwordHash); db.setSub("pad:"+this.id, ["passwordHash"], this.passwordHash);
}, };
isCorrectPassword: function(password)
{ Pad.prototype.isCorrectPassword = function isCorrectPassword(password) {
return compare(this.passwordHash, password) return compare(this.passwordHash, password);
}, };
isPasswordProtected: function()
{ Pad.prototype.isPasswordProtected = function isPasswordProtected() {
return this.passwordHash != null; return this.passwordHash != null;
} };
}, // methods
});
/* Crypto helper methods */ /* Crypto helper methods */
@ -518,15 +479,7 @@ function hash(password, salt)
function generateSalt() function generateSalt()
{ {
var len = 86; return randomString(86);
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz./";
var randomstring = '';
for (var i = 0; i < len; i++)
{
var rnum = Math.floor(Math.random() * chars.length);
randomstring += chars.substring(rnum, rnum + 1);
}
return randomstring;
} }
function compare(hashStr, password) function compare(hashStr, password)

View file

@ -20,7 +20,7 @@
var ERR = require("async-stacktrace"); var ERR = require("async-stacktrace");
var customError = require("../utils/customError"); var customError = require("../utils/customError");
require("../db/Pad"); var Pad = require("../db/Pad").Pad;
var db = require("./DB").db; var db = require("./DB").db;
/** /**
@ -38,6 +38,15 @@ var globalPads = {
remove: function (name) { delete this[':'+name]; } remove: function (name) { delete this[':'+name]; }
}; };
/**
* An array of padId transformations. These represent changes in pad name policy over
* time, and allow us to "play back" these changes so legacy padIds can be found.
*/
var padIdTransforms = [
[/\s+/g, '_'],
[/:+/g, '_']
];
/** /**
* Returns a Pad Object with the callback * Returns a Pad Object with the callback
* @param id A String with the id of the pad * @param id A String with the id of the pad
@ -106,10 +115,43 @@ exports.doesPadExists = function(padId, callback)
db.get("pad:"+padId, function(err, value) db.get("pad:"+padId, function(err, value)
{ {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
callback(null, value != null); callback(null, value != null && value.atext);
}); });
} }
//returns a sanitized padId, respecting legacy pad id formats
exports.sanitizePadId = function(padId, callback) {
var transform_index = arguments[2] || 0;
//we're out of possible transformations, so just return it
if(transform_index >= padIdTransforms.length)
{
callback(padId);
}
//check if padId exists
else
{
exports.doesPadExists(padId, function(junk, exists)
{
if(exists)
{
callback(padId);
}
else
{
//get the next transformation *that's different*
var transformedPadId = padId;
while(transformedPadId == padId && transform_index < padIdTransforms.length)
{
transformedPadId = padId.replace(padIdTransforms[transform_index][0], padIdTransforms[transform_index][1]);
transform_index += 1;
}
//check the next transform
exports.sanitizePadId(transformedPadId, callback, transform_index);
}
});
}
}
exports.isValidPadId = function(padId) exports.isValidPadId = function(padId)
{ {
return /^(g.[a-zA-Z0-9]{16}\$)?[^$]{1,50}$/.test(padId); return /^(g.[a-zA-Z0-9]{16}\$)?[^$]{1,50}$/.test(padId);

View file

@ -18,9 +18,11 @@
* limitations under the License. * limitations under the License.
*/ */
var CommonCode = require('../utils/common_code');
var ERR = require("async-stacktrace"); var ERR = require("async-stacktrace");
var db = require("./DB").db; var db = require("./DB").db;
var async = require("async"); var async = require("async");
var randomString = CommonCode.require('/pad_utils').randomString;
/** /**
* returns a read only id for a pad * returns a read only id for a pad
@ -70,18 +72,3 @@ exports.getPadId = function(readOnlyId, callback)
{ {
db.get("readonly2pad:" + readOnlyId, callback); db.get("readonly2pad:" + readOnlyId, callback);
} }
/**
* Generates a random String with the given length. Is needed to generate the read only ids
*/
function randomString(len)
{
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var randomstring = '';
for (var i = 0; i < len; i++)
{
var rnum = Math.floor(Math.random() * chars.length);
randomstring += chars.substring(rnum, rnum + 1);
}
return randomstring;
}

View file

@ -18,6 +18,7 @@
* limitations under the License. * limitations under the License.
*/ */
var CommonCode = require('../utils/common_code');
var ERR = require("async-stacktrace"); var ERR = require("async-stacktrace");
var db = require("./DB").db; var db = require("./DB").db;
var async = require("async"); var async = require("async");
@ -25,6 +26,7 @@ var authorManager = require("./AuthorManager");
var padManager = require("./PadManager"); var padManager = require("./PadManager");
var sessionManager = require("./SessionManager"); var sessionManager = require("./SessionManager");
var settings = require("../utils/Settings") var settings = require("../utils/Settings")
var randomString = CommonCode.require('/pad_utils').randomString;
/** /**
* This function controlls the access to a pad, it checks if the user can access a pad. * This function controlls the access to a pad, it checks if the user can access a pad.

View file

@ -18,8 +18,10 @@
* limitations under the License. * limitations under the License.
*/ */
var CommonCode = require('../utils/common_code');
var ERR = require("async-stacktrace"); var ERR = require("async-stacktrace");
var customError = require("../utils/customError"); var customError = require("../utils/customError");
var randomString = CommonCode.require('/pad_utils').randomString;
var db = require("./DB").db; var db = require("./DB").db;
var async = require("async"); var async = require("async");
var groupMangager = require("./GroupManager"); var groupMangager = require("./GroupManager");
@ -358,21 +360,6 @@ function listSessionsWithDBKey (dbkey, callback)
}); });
} }
/**
* Generates a random String with the given length. Is needed to generate the SessionIDs
*/
function randomString(len)
{
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var randomstring = '';
for (var i = 0; i < len; i++)
{
var rnum = Math.floor(Math.random() * chars.length);
randomstring += chars.substring(rnum, rnum + 1);
}
return randomstring;
}
//checks if a number is an int //checks if a number is an int
function is_int(value) function is_int(value)
{ {

View file

@ -20,8 +20,9 @@
* limitations under the License. * limitations under the License.
*/ */
var Changeset = require('./utils/Changeset'); var CommonCode = require('./utils/common_code');
var AttributePoolFactory = require("./utils/AttributePoolFactory"); var Changeset = CommonCode.require("/Changeset");
var AttributePoolFactory = CommonCode.require("/AttributePoolFactory");
function random() { function random() {
this.nextInt = function (maxValue) { this.nextInt = function (maxValue) {

View file

@ -18,9 +18,12 @@
* limitations under the License. * limitations under the License.
*/ */
var CommonCode = require('../utils/common_code');
var ERR = require("async-stacktrace"); var ERR = require("async-stacktrace");
var fs = require("fs"); var fs = require("fs");
var api = require("../db/API"); var api = require("../db/API");
var padManager = require("../db/PadManager");
var randomString = CommonCode.require('/pad_utils').randomString;
//ensure we have an apikey //ensure we have an apikey
var apikey = null; var apikey = null;
@ -96,6 +99,32 @@ exports.handle = function(functionName, fields, req, res)
return; return;
} }
//sanitize any pad id's before continuing
if(fields["padID"])
{
padManager.sanitizePadId(fields["padID"], function(padId)
{
fields["padID"] = padId;
callAPI(functionName, fields, req, res);
});
}
else if(fields["padName"])
{
padManager.sanitizePadId(fields["padName"], function(padId)
{
fields["padName"] = padId;
callAPI(functionName, fields, req, res);
});
}
else
{
callAPI(functionName, fields, req, res);
}
}
//calls the api function
function callAPI(functionName, fields, req, res)
{
//put the function parameters in an array //put the function parameters in an array
var functionParams = []; var functionParams = [];
for(var i=0;i<functions[functionName].length;i++) for(var i=0;i<functions[functionName].length;i++)
@ -130,18 +159,3 @@ exports.handle = function(functionName, fields, req, res)
//call the api function //call the api function
api[functionName](functionParams[0],functionParams[1],functionParams[2],functionParams[3],functionParams[4]); api[functionName](functionParams[0],functionParams[1],functionParams[2],functionParams[3],functionParams[4]);
} }
/**
* Generates a random String with the given length. Is needed to generate the Author Ids
*/
function randomString(len)
{
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var randomstring = '';
for (var i = 0; i < len; i++)
{
var rnum = Math.floor(Math.random() * chars.length);
randomstring += chars.substring(rnum, rnum + 1);
}
return randomstring;
}

View file

@ -53,8 +53,16 @@ exports.doExport = function(req, res, padId, type)
padManager.getPad(padId, function(err, pad) padManager.getPad(padId, function(err, pad)
{ {
ERR(err); ERR(err);
if(req.params.rev){
pad.getInternalRevisionAText(req.params.rev, function(junk, text)
{
res.send(text.text ? text.text : null);
});
}
else
{
res.send(pad.text()); res.send(pad.text());
}
}); });
} }
else if(type == 'dokuwiki') else if(type == 'dokuwiki')
@ -66,7 +74,7 @@ exports.doExport = function(req, res, padId, type)
//render the dokuwiki document //render the dokuwiki document
function(callback) function(callback)
{ {
exportdokuwiki.getPadDokuWikiDocument(padId, null, function(err, dokuwiki) exportdokuwiki.getPadDokuWikiDocument(padId, req.params.rev, function(err, dokuwiki)
{ {
res.send(dokuwiki); res.send(dokuwiki);
callback("stop"); callback("stop");
@ -87,7 +95,7 @@ exports.doExport = function(req, res, padId, type)
//render the html document //render the html document
function(callback) function(callback)
{ {
exporthtml.getPadHTMLDocument(padId, null, false, function(err, _html) exporthtml.getPadHTMLDocument(padId, req.params.rev, false, function(err, _html)
{ {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
html = _html; html = _html;

View file

@ -82,7 +82,7 @@ exports.doImport = function(req, res, padId)
//this allows us to accept source code files like .c or .java //this allows us to accept source code files like .c or .java
function(callback) function(callback)
{ {
var fileEnding = srcFile.split(".")[1].toLowerCase(); var fileEnding = (srcFile.split(".")[1] || "").toLowerCase();
var knownFileEndings = ["txt", "doc", "docx", "pdf", "odt", "html", "htm"]; var knownFileEndings = ["txt", "doc", "docx", "pdf", "odt", "html", "htm"];
//find out if this is a known file ending //find out if this is a known file ending
@ -115,7 +115,15 @@ exports.doImport = function(req, res, padId)
{ {
var randNum = Math.floor(Math.random()*0xFFFFFFFF); var randNum = Math.floor(Math.random()*0xFFFFFFFF);
destFile = tempDirectory + "eplite_import_" + randNum + ".txt"; destFile = tempDirectory + "eplite_import_" + randNum + ".txt";
abiword.convertFile(srcFile, destFile, "txt", callback); abiword.convertFile(srcFile, destFile, "txt", function(err){
//catch convert errors
if(err){
console.warn("Converting Error:", err);
return callback("convertFailed");
} else {
callback();
}
});
}, },
//get the pad object //get the pad object
@ -176,16 +184,18 @@ exports.doImport = function(req, res, padId)
} }
], function(err) ], function(err)
{ {
//the upload failed, there is nothing we can do, send a 500 var status = "ok";
if(err == "uploadFailed")
//check for known errors and replace the status
if(err == "uploadFailed" || err == "convertFailed")
{ {
res.send(500); status = err;
return; err = null;
} }
ERR(err); ERR(err);
//close the connection //close the connection
res.send("ok"); res.send("<script>document.domain = document.domain; var impexp = window.top.require('/pad_impexp').padimpexp.handleFrameCall('" + status + "'); </script>", 200);
}); });
} }

View file

@ -18,17 +18,17 @@
* limitations under the License. * limitations under the License.
*/ */
var CommonCode = require('../utils/common_code');
var ERR = require("async-stacktrace"); var ERR = require("async-stacktrace");
var async = require("async"); var async = require("async");
var padManager = require("../db/PadManager"); var padManager = require("../db/PadManager");
var Changeset = require("../utils/Changeset"); var Changeset = CommonCode.require("/Changeset");
var AttributePoolFactory = require("../utils/AttributePoolFactory"); var AttributePoolFactory = CommonCode.require("/AttributePoolFactory");
var authorManager = require("../db/AuthorManager"); var authorManager = require("../db/AuthorManager");
var readOnlyManager = require("../db/ReadOnlyManager"); var readOnlyManager = require("../db/ReadOnlyManager");
var settings = require('../utils/Settings'); var settings = require('../utils/Settings');
var securityManager = require("../db/SecurityManager"); var securityManager = require("../db/SecurityManager");
var log4js = require('log4js'); var log4js = require('log4js');
var os = require("os");
var messageLogger = log4js.getLogger("message"); var messageLogger = log4js.getLogger("message");
/** /**
@ -517,7 +517,12 @@ exports.updatePadClients = function(pad, callback)
], function(err) ], function(err)
{ {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
// next if session has not been deleted
if(sessioninfos[session] == null)
{
callback(null);
return;
}
if(author == sessioninfos[session].author) if(author == sessioninfos[session].author)
{ {
socketio.sockets.sockets[session].json.send({"type":"COLLABROOM","data":{type:"ACCEPT_COMMIT", newRev:r}}); socketio.sockets.sockets[session].json.send({"type":"COLLABROOM","data":{type:"ACCEPT_COMMIT", newRev:r}});
@ -539,7 +544,10 @@ exports.updatePadClients = function(pad, callback)
callback callback
); );
if(sessioninfos[session] != null)
{
sessioninfos[session].rev = pad.getHeadRevisionNumber(); sessioninfos[session].rev = pad.getHeadRevisionNumber();
}
},callback); },callback);
} }
@ -755,13 +763,6 @@ function handleClientReady(client, message)
var apool = attribsForWire.pool.toJsonable(); var apool = attribsForWire.pool.toJsonable();
atext.attribs = attribsForWire.translated; atext.attribs = attribsForWire.translated;
//check if abiword is avaiable
var abiwordAvailable = settings.abiword != null ? "yes" : "no";
if(settings.abiword != null && os.type().indexOf("Windows") != -1)
{
abiwordAvailable = "withoutPDF";
}
var clientVars = { var clientVars = {
"accountPrivs": { "accountPrivs": {
"maxRevisions": 100 "maxRevisions": 100
@ -798,7 +799,7 @@ function handleClientReady(client, message)
"fullWidth": false, "fullWidth": false,
"hideSidebar": false "hideSidebar": false
}, },
"abiwordAvailable": abiwordAvailable, "abiwordAvailable": settings.abiwordAvailable(),
"hooks": {} "hooks": {}
} }
@ -852,8 +853,19 @@ function handleClientReady(client, message)
//Run trough all sessions of this pad //Run trough all sessions of this pad
async.forEach(pad2sessions[message.padId], function(sessionID, callback) async.forEach(pad2sessions[message.padId], function(sessionID, callback)
{ {
var sessionAuthorName, sessionAuthorColorId; var author, socket, sessionAuthorName, sessionAuthorColorId;
//Since sessioninfos might change while being enumerated, check if the
//sessionID is still assigned to a valid session
if(sessioninfos[sessionID] !== undefined &&
socketio.sockets.sockets[sessionID] !== undefined){
author = sessioninfos[sessionID].author;
socket = socketio.sockets.sockets[sessionID];
}else {
// If the sessionID is not valid, callback();
callback();
return;
}
async.series([ async.series([
//get the authorname & colorId //get the authorname & colorId
function(callback) function(callback)
@ -861,7 +873,7 @@ function handleClientReady(client, message)
async.parallel([ async.parallel([
function(callback) function(callback)
{ {
authorManager.getAuthorColorId(sessioninfos[sessionID].author, function(err, value) authorManager.getAuthorColorId(author, function(err, value)
{ {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
sessionAuthorColorId = value; sessionAuthorColorId = value;
@ -870,7 +882,7 @@ function handleClientReady(client, message)
}, },
function(callback) function(callback)
{ {
authorManager.getAuthorName(sessioninfos[sessionID].author, function(err, value) authorManager.getAuthorName(author, function(err, value)
{ {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
sessionAuthorName = value; sessionAuthorName = value;
@ -885,7 +897,7 @@ function handleClientReady(client, message)
if(sessionID != client.id) if(sessionID != client.id)
{ {
//Send this Session the Notification about the new user //Send this Session the Notification about the new user
socketio.sockets.sockets[sessionID].json.send(messageToTheOtherUsers); socket.json.send(messageToTheOtherUsers);
//Send the new User a Notification about this other user //Send the new User a Notification about this other user
var messageToNotifyTheClientAboutTheOthers = { var messageToNotifyTheClientAboutTheOthers = {
@ -897,7 +909,7 @@ function handleClientReady(client, message)
"colorId": sessionAuthorColorId, "colorId": sessionAuthorColorId,
"name": sessionAuthorName, "name": sessionAuthorName,
"userAgent": "Anonymous", "userAgent": "Anonymous",
"userId": sessioninfos[sessionID].author "userId": author
} }
} }
}; };

View file

@ -18,11 +18,13 @@
* limitations under the License. * limitations under the License.
*/ */
var CommonCode = require('../utils/common_code');
var ERR = require("async-stacktrace"); var ERR = require("async-stacktrace");
var async = require("async"); var async = require("async");
var padManager = require("../db/PadManager"); var padManager = require("../db/PadManager");
var Changeset = require("../utils/Changeset"); var Changeset = CommonCode.require("/Changeset");
var AttributePoolFactory = require("../utils/AttributePoolFactory"); var AttributePoolFactory = CommonCode.require("/AttributePoolFactory");
var settings = require('../utils/Settings');
var authorManager = require("../db/AuthorManager"); var authorManager = require("../db/AuthorManager");
var log4js = require('log4js'); var log4js = require('log4js');
var messageLogger = log4js.getLogger("message"); var messageLogger = log4js.getLogger("message");
@ -160,6 +162,7 @@ function createTimesliderClientVars (padId, callback)
fullWidth: false, fullWidth: false,
disableRightBar: false, disableRightBar: false,
initialChangesets: [], initialChangesets: [],
abiwordAvailable: settings.abiwordAvailable(),
hooks: [], hooks: [],
initialStyledContents: {} initialStyledContents: {}
}; };

View file

@ -31,6 +31,8 @@ var async = require('async');
var express = require('express'); var express = require('express');
var path = require('path'); var path = require('path');
var minify = require('./utils/Minify'); var minify = require('./utils/Minify');
var CachingMiddleware = require('./utils/caching_middleware');
var Yajsml = require('yajsml');
var formidable = require('formidable'); var formidable = require('formidable');
var apiHandler; var apiHandler;
var exportHandler; var exportHandler;
@ -45,8 +47,9 @@ var socketIORouter;
var version = ""; var version = "";
try try
{ {
var ref = fs.readFileSync("../.git/HEAD", "utf-8"); var rootPath = path.normalize(__dirname + "/../")
var refPath = "../.git/" + ref.substring(5, ref.indexOf("\n")); var ref = fs.readFileSync(rootPath + ".git/HEAD", "utf-8");
var refPath = rootPath + ".git/" + ref.substring(5, ref.indexOf("\n"));
version = fs.readFileSync(refPath, "utf-8"); version = fs.readFileSync(refPath, "utf-8");
version = version.substring(0, 7); version = version.substring(0, 7);
console.log("Your Etherpad Lite git version is " + version); console.log("Your Etherpad Lite git version is " + version);
@ -60,8 +63,7 @@ console.log("Report bugs at https://github.com/Pita/etherpad-lite/issues")
var serverName = "Etherpad-Lite " + version + " (http://j.mp/ep-lite)"; var serverName = "Etherpad-Lite " + version + " (http://j.mp/ep-lite)";
//cache 6 hours exports.maxAge = settings.maxAge;
exports.maxAge = 1000*60*60*6;
//set loglevel //set loglevel
log4js.setGlobalLogLevel(settings.loglevel); log4js.setGlobalLogLevel(settings.loglevel);
@ -78,6 +80,38 @@ async.waterfall([
//create server //create server
var app = express.createServer(); var app = express.createServer();
app.use(function (req, res, next) {
res.header("Server", serverName);
next();
});
//redirects browser to the pad's sanitized url if needed. otherwise, renders the html
app.param('pad', function (req, res, next, padId) {
//ensure the padname is valid and the url doesn't end with a /
if(!padManager.isValidPadId(padId) || /\/$/.test(req.url))
{
res.send('Such a padname is forbidden', 404);
}
else
{
padManager.sanitizePadId(padId, function(sanitizedPadId) {
//the pad id was sanitized, so we redirect to the sanitized version
if(sanitizedPadId != padId)
{
var real_path = req.path.replace(/^\/p\/[^\/]+/, './' + sanitizedPadId);
res.header('Location', real_path);
res.send('You should be redirected to <a href="' + real_path + '">' + real_path + '</a>', 302);
}
//the pad id was fine, so just render it
else
{
next();
}
});
}
});
//load modules that needs a initalized db //load modules that needs a initalized db
readOnlyManager = require("./db/ReadOnlyManager"); readOnlyManager = require("./db/ReadOnlyManager");
exporthtml = require("./utils/ExportHtml"); exporthtml = require("./utils/ExportHtml");
@ -108,31 +142,26 @@ async.waterfall([
gracefulShutdown(); gracefulShutdown();
}); });
//serve static files // Cache both minified and static.
app.get('/static/*', function(req, res) var assetCache = new CachingMiddleware;
{ app.all('/(minified|static)/*', assetCache.handle);
res.header("Server", serverName);
var filePath = path.normalize(__dirname + "/.." + // Minify will serve static files compressed (minify enabled). It also has
req.url.replace(/\.\./g, '').split("?")[0]); // file-specific hacks for ace/require-kernel/etc.
res.sendfile(filePath, { maxAge: exports.maxAge }); app.all('/static/:filename(*)', minify.minify);
});
// Setup middleware that will package JavaScript files served by minify for
//serve minified files // CommonJS loader on the client-side.
app.get('/minified/:id', function(req, res, next) var jsServer = new (Yajsml.Server)({
{ rootPath: 'minified/'
res.header("Server", serverName); , rootURI: 'http://localhost:' + settings.port + '/static/js/'
var id = req.params.id;
if(id == "pad.js" || id == "timeslider.js")
{
minify.minifyJS(req,res,id);
}
else
{
next();
}
}); });
var StaticAssociator = Yajsml.associators.StaticAssociator;
var associations =
Yajsml.associators.associationsForSimpleMapping(minify.tar);
var associator = new StaticAssociator(associations);
jsServer.setAssociator(associator);
app.use(jsServer);
//checks for padAccess //checks for padAccess
function hasPadAccess(req, res, callback) function hasPadAccess(req, res, callback)
@ -177,8 +206,6 @@ async.waterfall([
//serve read only pad //serve read only pad
app.get('/ro/:id', function(req, res) app.get('/ro/:id', function(req, res)
{ {
res.header("Server", serverName);
var html; var html;
var padId; var padId;
var pad; var pad;
@ -236,14 +263,6 @@ async.waterfall([
//serve pad.html under /p //serve pad.html under /p
app.get('/p/:pad', function(req, res, next) app.get('/p/:pad', function(req, res, next)
{ {
//ensure the padname is valid and the url doesn't end with a /
if(!padManager.isValidPadId(req.params.pad) || /\/$/.test(req.url))
{
res.send('Such a padname is forbidden', 404);
return;
}
res.header("Server", serverName);
var filePath = path.normalize(__dirname + "/../static/pad.html"); var filePath = path.normalize(__dirname + "/../static/pad.html");
res.sendfile(filePath, { maxAge: exports.maxAge }); res.sendfile(filePath, { maxAge: exports.maxAge });
}); });
@ -251,28 +270,13 @@ async.waterfall([
//serve timeslider.html under /p/$padname/timeslider //serve timeslider.html under /p/$padname/timeslider
app.get('/p/:pad/timeslider', function(req, res, next) app.get('/p/:pad/timeslider', function(req, res, next)
{ {
//ensure the padname is valid and the url doesn't end with a /
if(!padManager.isValidPadId(req.params.pad) || /\/$/.test(req.url))
{
res.send('Such a padname is forbidden', 404);
return;
}
res.header("Server", serverName);
var filePath = path.normalize(__dirname + "/../static/timeslider.html"); var filePath = path.normalize(__dirname + "/../static/timeslider.html");
res.sendfile(filePath, { maxAge: exports.maxAge }); res.sendfile(filePath, { maxAge: exports.maxAge });
}); });
//serve timeslider.html under /p/$padname/timeslider //serve timeslider.html under /p/$padname/timeslider
app.get('/p/:pad/export/:type', function(req, res, next) app.get('/p/:pad/:rev?/export/:type', function(req, res, next)
{ {
//ensure the padname is valid and the url doesn't end with a /
if(!padManager.isValidPadId(req.params.pad) || /\/$/.test(req.url))
{
res.send('Such a padname is forbidden', 404);
return;
}
var types = ["pdf", "doc", "txt", "html", "odt", "dokuwiki"]; var types = ["pdf", "doc", "txt", "html", "odt", "dokuwiki"];
//send a 404 if we don't support this filetype //send a 404 if we don't support this filetype
if(types.indexOf(req.params.type) == -1) if(types.indexOf(req.params.type) == -1)
@ -290,7 +294,6 @@ async.waterfall([
} }
res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Origin", "*");
res.header("Server", serverName);
hasPadAccess(req, res, function() hasPadAccess(req, res, function()
{ {
@ -301,13 +304,6 @@ async.waterfall([
//handle import requests //handle import requests
app.post('/p/:pad/import', function(req, res, next) app.post('/p/:pad/import', function(req, res, next)
{ {
//ensure the padname is valid and the url doesn't end with a /
if(!padManager.isValidPadId(req.params.pad) || /\/$/.test(req.url))
{
res.send('Such a padname is forbidden', 404);
return;
}
//if abiword is disabled, skip handling this request //if abiword is disabled, skip handling this request
if(settings.abiword == null) if(settings.abiword == null)
{ {
@ -315,8 +311,6 @@ async.waterfall([
return; return;
} }
res.header("Server", serverName);
hasPadAccess(req, res, function() hasPadAccess(req, res, function()
{ {
importHandler.doImport(req, res, req.params.pad); importHandler.doImport(req, res, req.params.pad);
@ -326,8 +320,8 @@ async.waterfall([
var apiLogger = log4js.getLogger("API"); var apiLogger = log4js.getLogger("API");
//This is for making an api call, collecting all post information and passing it to the apiHandler //This is for making an api call, collecting all post information and passing it to the apiHandler
var apiCaller = function(req, res, fields) { var apiCaller = function(req, res, fields)
res.header("Server", serverName); {
res.header("Content-Type", "application/json; charset=utf-8"); res.header("Content-Type", "application/json; charset=utf-8");
apiLogger.info("REQUEST, " + req.params.func + ", " + JSON.stringify(fields)); apiLogger.info("REQUEST, " + req.params.func + ", " + JSON.stringify(fields));
@ -388,7 +382,6 @@ async.waterfall([
//serve index.html under / //serve index.html under /
app.get('/', function(req, res) app.get('/', function(req, res)
{ {
res.header("Server", serverName);
var filePath = path.normalize(__dirname + "/../static/index.html"); var filePath = path.normalize(__dirname + "/../static/index.html");
res.sendfile(filePath, { maxAge: exports.maxAge }); res.sendfile(filePath, { maxAge: exports.maxAge });
}); });
@ -396,7 +389,6 @@ async.waterfall([
//serve robots.txt //serve robots.txt
app.get('/robots.txt', function(req, res) app.get('/robots.txt', function(req, res)
{ {
res.header("Server", serverName);
var filePath = path.normalize(__dirname + "/../static/robots.txt"); var filePath = path.normalize(__dirname + "/../static/robots.txt");
res.sendfile(filePath, { maxAge: exports.maxAge }); res.sendfile(filePath, { maxAge: exports.maxAge });
}); });
@ -404,7 +396,6 @@ async.waterfall([
//serve favicon.ico //serve favicon.ico
app.get('/favicon.ico', function(req, res) app.get('/favicon.ico', function(req, res)
{ {
res.header("Server", serverName);
var filePath = path.normalize(__dirname + "/../static/custom/favicon.ico"); var filePath = path.normalize(__dirname + "/../static/custom/favicon.ico");
res.sendfile(filePath, { maxAge: exports.maxAge }, function(err) res.sendfile(filePath, { maxAge: exports.maxAge }, function(err)
{ {

View file

@ -53,7 +53,7 @@ if(os.type().indexOf("Windows") > -1)
abiword.on('exit', function (code) abiword.on('exit', function (code)
{ {
if(code != 0) { if(code != 0) {
throw "Abiword died with exit code " + code; return callback("Abiword died with exit code " + code);
} }
if(stdoutBuffer != "") if(stdoutBuffer != "")
@ -75,7 +75,12 @@ if(os.type().indexOf("Windows") > -1)
else else
{ {
//spawn the abiword process //spawn the abiword process
var abiword = spawn(settings.abiword, ["--plugin", "AbiCommand"]); var abiword;
var stdoutCallback = null;
var spawnAbiword = function (){
abiword = spawn(settings.abiword, ["--plugin", "AbiCommand"]);
var stdoutBuffer = "";
var firstPrompt = true;
//append error messages to the buffer //append error messages to the buffer
abiword.stderr.on('data', function (data) abiword.stderr.on('data', function (data)
@ -83,20 +88,15 @@ else
stdoutBuffer += data.toString(); stdoutBuffer += data.toString();
}); });
//throw exceptions if abiword is dieing //abiword died, let's restart abiword and return an error with the callback
abiword.on('exit', function (code) abiword.on('exit', function (code)
{ {
throw "Abiword died with exit code " + code; spawnAbiword();
stdoutCallback("Abiword died with exit code " + code);
}); });
//delegate the processing of stdout to a other function //delegate the processing of stdout to a other function
abiword.stdout.on('data',onAbiwordStdout); abiword.stdout.on('data',function (data)
var stdoutCallback = null;
var stdoutBuffer = "";
var firstPrompt = true;
function onAbiwordStdout(data)
{ {
//add data to buffer //add data to buffer
stdoutBuffer+=data.toString(); stdoutBuffer+=data.toString();
@ -120,7 +120,9 @@ else
firstPrompt = false; firstPrompt = false;
} }
});
} }
spawnAbiword();
doConvertTask = function(task, callback) doConvertTask = function(task, callback)
{ {
@ -130,6 +132,7 @@ else
stdoutCallback = function (err) stdoutCallback = function (err)
{ {
callback(); callback();
console.log("queue continue");
task.callback(err); task.callback(err);
}; };
} }

38
node/utils/Cli.js Normal file
View file

@ -0,0 +1,38 @@
/**
* The CLI module handles command line parameters
*/
/*
* 2012 Jordan Hollinger
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an
"AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// An object containing the parsed command-line options
exports.argv = {};
var argv = process.argv.slice(2);
var arg, prevArg;
// Loop through args
for ( var i = 0; i < argv.length; i++ ) {
arg = argv[i];
// Override location of settings.json file
if ( prevArg == '--settings' || prevArg == '-s' ) {
exports.argv.settings = arg;
}
prevArg = arg;
}

View file

@ -15,7 +15,8 @@
*/ */
var async = require("async"); var async = require("async");
var Changeset = require("./Changeset"); var CommonCode = require('./common_code');
var Changeset = CommonCode.require("/Changeset");
var padManager = require("../db/PadManager"); var padManager = require("../db/PadManager");
function getPadDokuWiki(pad, revNum, callback) function getPadDokuWiki(pad, revNum, callback)

View file

@ -14,10 +14,12 @@
* limitations under the License. * limitations under the License.
*/ */
var CommonCode = require('./common_code');
var async = require("async"); var async = require("async");
var Changeset = require("./Changeset"); var Changeset = CommonCode.require("/Changeset");
var padManager = require("../db/PadManager"); var padManager = require("../db/PadManager");
var ERR = require("async-stacktrace"); var ERR = require("async-stacktrace");
var Security = CommonCode.require('/security');
function getPadPlainText(pad, revNum) function getPadPlainText(pad, revNum)
{ {
@ -269,7 +271,7 @@ function getHTMLFromAtext(pad, atext)
//from but they break the abiword parser and are completly useless //from but they break the abiword parser and are completly useless
s = s.replace(String.fromCharCode(12), ""); s = s.replace(String.fromCharCode(12), "");
assem.append(_escapeHTML(s)); assem.append(_encodeWhitespace(Security.escapeHTML(s)));
} // end iteration over spans in line } // end iteration over spans in line
var tags2close = []; var tags2close = [];
@ -292,7 +294,7 @@ function getHTMLFromAtext(pad, atext)
var url = urlData[1]; var url = urlData[1];
var urlLength = url.length; var urlLength = url.length;
processNextChars(startIndex - idx); processNextChars(startIndex - idx);
assem.append('<a href="' + url.replace(/\"/g, '&quot;') + '">'); assem.append('<a href="' + Security.escapeHTMLAttribute(url) + '">');
processNextChars(urlLength); processNextChars(urlLength);
assem.append('</a>'); assem.append('</a>');
}); });
@ -309,13 +311,14 @@ function getHTMLFromAtext(pad, atext)
// People might use weird indenting, e.g. skip a level, // People might use weird indenting, e.g. skip a level,
// so we want to do something reasonable there. We also // so we want to do something reasonable there. We also
// want to deal gracefully with blank lines. // want to deal gracefully with blank lines.
// => keeps track of the parents level of indentation
var lists = []; // e.g. [[1,'bullet'], [3,'bullet'], ...] var lists = []; // e.g. [[1,'bullet'], [3,'bullet'], ...]
for (var i = 0; i < textLines.length; i++) for (var i = 0; i < textLines.length; i++)
{ {
var line = _analyzeLine(textLines[i], attribLines[i], apool); var line = _analyzeLine(textLines[i], attribLines[i], apool);
var lineContent = getLineHTML(line.text, line.aline); var lineContent = getLineHTML(line.text, line.aline);
if (line.listLevel || lists.length > 0) if (line.listLevel)//If we are inside a list
{ {
// do list stuff // do list stuff
var whichList = -1; // index into lists or -1 var whichList = -1; // index into lists or -1
@ -331,41 +334,89 @@ function getHTMLFromAtext(pad, atext)
} }
} }
if (whichList >= lists.length) if (whichList >= lists.length)//means we are on a deeper level of indentation than the previous line
{ {
lists.push([line.listLevel, line.listTypeName]); lists.push([line.listLevel, line.listTypeName]);
pieces.push('<ul><li>', lineContent || '<br>'); if(line.listTypeName == "number")
{
pieces.push('<ol class="'+line.listTypeName+'"><li>', lineContent || '<br>');
} }
else if (whichList == -1) else
{
pieces.push('<ul class="'+line.listTypeName+'"><li>', lineContent || '<br>');
}
}
//the following code *seems* dead after my patch.
//I keep it just in case I'm wrong...
/*else if (whichList == -1)//means we are not inside a list
{ {
if (line.text) if (line.text)
{ {
console.log('trace 1');
// non-blank line, end all lists // non-blank line, end all lists
pieces.push(new Array(lists.length + 1).join('</li></ul\n>')); if(line.listTypeName == "number")
{
pieces.push(new Array(lists.length + 1).join('</li></ol>'));
}
else
{
pieces.push(new Array(lists.length + 1).join('</li></ul>'));
}
lists.length = 0; lists.length = 0;
pieces.push(lineContent, '<br>'); pieces.push(lineContent, '<br>');
} }
else else
{ {
console.log('trace 2');
pieces.push('<br><br>'); pieces.push('<br><br>');
} }
} }*/
else else//means we are getting closer to the lowest level of indentation
{ {
while (whichList < lists.length - 1) while (whichList < lists.length - 1)
{
if(lists[lists.length - 1][1] == "number")
{
pieces.push('</li></ol>');
}
else
{ {
pieces.push('</li></ul>'); pieces.push('</li></ul>');
}
lists.length--; lists.length--;
} }
pieces.push('</li><li>', lineContent || '<br>'); pieces.push('</li><li>', lineContent || '<br>');
} }
} }
else//outside any list
{
while (lists.length > 0)//if was in a list: close it before
{
if(lists[lists.length - 1][1] == "number")
{
pieces.push('</li></ol>');
}
else else
{ {
pieces.push('</li></ul>');
}
lists.length--;
}
pieces.push(lineContent, '<br>'); pieces.push(lineContent, '<br>');
} }
} }
pieces.push(new Array(lists.length + 1).join('</li></ul>'));
for (var k = lists.length - 1; k >= 0; k--)
{
if(lists[k][1] == "number")
{
pieces.push('</li></ol>');
}
else
{
pieces.push('</li></ul>');
}
}
return pieces.join(''); return pieces.join('');
} }
@ -415,7 +466,24 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback)
{ {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
var head = (noDocType ? '' : '<!doctype html>\n') + '<html lang="en">\n' + (noDocType ? '' : '<head>\n' + '<meta charset="utf-8">\n' + '<style> * { font-family: arial, sans-serif;\n' + 'font-size: 13px;\n' + 'line-height: 17px; }</style>\n' + '</head>\n') + '<body>'; var head =
(noDocType ? '' : '<!doctype html>\n') +
'<html lang="en">\n' + (noDocType ? '' : '<head>\n' +
'<meta charset="utf-8">\n' +
'<style> * { font-family: arial, sans-serif;\n' +
'font-size: 13px;\n' +
'line-height: 17px; }' +
'ul.indent { list-style-type: none; }' +
'ol { list-style-type: decimal; }' +
'ol ol { list-style-type: lower-latin; }' +
'ol ol ol { list-style-type: lower-roman; }' +
'ol ol ol ol { list-style-type: decimal; }' +
'ol ol ol ol ol { list-style-type: lower-latin; }' +
'ol ol ol ol ol ol{ list-style-type: lower-roman; }' +
'ol ol ol ol ol ol ol { list-style-type: decimal; }' +
'ol ol ol ol ol ol ol ol{ list-style-type: lower-latin; }' +
'</style>\n' + '</head>\n') +
'<body>';
var foot = '</body>\n</html>\n'; var foot = '</body>\n</html>\n';
@ -427,24 +495,7 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback)
}); });
} }
function _escapeHTML(s) function _encodeWhitespace(s) {
{
var re = /[&<>]/g;
if (!re.MAP)
{
// persisted across function calls!
re.MAP = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
};
}
s = s.replace(re, function (c)
{
return re.MAP[c];
});
return s.replace(/[^\x21-\x7E\s\t\n\r]/g, function(c) return s.replace(/[^\x21-\x7E\s\t\n\r]/g, function(c)
{ {
return "&#" +c.charCodeAt(0) + ";" return "&#" +c.charCodeAt(0) + ";"

View file

@ -17,9 +17,10 @@
var jsdom = require('jsdom-nocontextifiy').jsdom; var jsdom = require('jsdom-nocontextifiy').jsdom;
var log4js = require('log4js'); var log4js = require('log4js');
var Changeset = require("./Changeset"); var CommonCode = require('../utils/common_code');
var contentcollector = require("./contentcollector"); var Changeset = CommonCode.require("/Changeset");
var map = require("../../static/js/ace2_common.js").map; var contentcollector = CommonCode.require("/contentcollector");
var map = CommonCode.require("/ace2_common").map;
function setPadHTML(pad, html, callback) function setPadHTML(pad, html, callback)
{ {

View file

@ -27,51 +27,182 @@ var cleanCSS = require('clean-css');
var jsp = require("uglify-js").parser; var jsp = require("uglify-js").parser;
var pro = require("uglify-js").uglify; var pro = require("uglify-js").uglify;
var path = require('path'); var path = require('path');
var Buffer = require('buffer').Buffer; var RequireKernel = require('require-kernel');
var gzip = require('gzip');
var server = require('../server'); var server = require('../server');
var os = require('os');
var padJS = ["jquery.min.js", "pad_utils.js", "plugins.js", "undo-xpopup.js", "json2.js", "pad_cookie.js", "pad_editor.js", "pad_editbar.js", "pad_docbar.js", "pad_modals.js", "ace.js", "collab_client.js", "pad_userlist.js", "pad_impexp.js", "pad_savedrevs.js", "pad_connectionstatus.js", "pad2.js", "jquery-ui.js", "chat.js", "excanvas.js", "farbtastic.js"]; var ROOT_DIR = path.normalize(__dirname + "/../../static/");
var TAR_PATH = path.join(__dirname, 'tar.json');
var tar = JSON.parse(fs.readFileSync(TAR_PATH, 'utf8'));
var timesliderJS = ["jquery.min.js", "plugins.js", "undo-xpopup.js", "json2.js", "colorutils.js", "draggable.js", "pad_utils.js", "pad_cookie.js", "pad_editor.js", "pad_editbar.js", "pad_docbar.js", "pad_modals.js", "easysync2_client.js", "domline_client.js", "linestylefilter_client.js", "cssmanager_client.js", "broadcast.js", "broadcast_slider.js", "broadcast_revisions.js"]; // Rewrite tar to include modules with no extensions and proper rooted paths.
exports.tar = {};
for (var key in tar) {
exports.tar['/' + key] =
tar[key].map(function (p) {return '/' + p}).concat(
tar[key].map(function (p) {return '/' + p.replace(/\.js$/, '')})
);
}
/** /**
* creates the minifed javascript for the given minified name * creates the minifed javascript for the given minified name
* @param req the Express request * @param req the Express request
* @param res the Express response * @param res the Express response
*/ */
exports.minifyJS = function(req, res, jsFilename) exports.minify = function(req, res, next)
{ {
res.header("Content-Type","text/javascript"); var filename = req.params['filename'];
//choose the js files we need // No relative paths, especially if they may go up the file hierarchy.
if(jsFilename == "pad.js") filename = path.normalize(path.join(ROOT_DIR, filename));
{ if (filename.indexOf(ROOT_DIR) == 0) {
jsFiles = padJS; filename = filename.slice(ROOT_DIR.length);
} filename = filename.replace(/\\/g, '/'); // Windows (safe generally?)
else if(jsFilename == "timeslider.js") } else {
{ res.writeHead(404, {});
jsFiles = timesliderJS; res.end();
} return;
else
{
throw new Error("there is no profile for creating " + name);
} }
//minifying is enabled // What content type should this be?
if(settings.minify) // TODO: This should use a MIME module.
{ var contentType;
var fileValues = {}; if (filename.match(/\.js$/)) {
var embeds = {}; contentType = "text/javascript";
} else if (filename.match(/\.css$/)) {
contentType = "text/css";
} else if (filename.match(/\.html$/)) {
contentType = "text/html";
} else if (filename.match(/\.txt$/)) {
contentType = "text/plain";
} else if (filename.match(/\.png$/)) {
contentType = "image/png";
} else if (filename.match(/\.gif$/)) {
contentType = "image/gif";
} else if (filename.match(/\.ico$/)) {
contentType = "image/x-icon";
} else {
contentType = "application/octet-stream";
}
statFile(filename, function (error, date, exists) {
if (date) {
date = new Date(date);
res.setHeader('last-modified', date.toUTCString());
res.setHeader('date', (new Date()).toUTCString());
if (server.maxAge) {
var expiresDate = new Date((new Date()).getTime()+server.maxAge*1000);
res.setHeader('expires', expiresDate.toUTCString());
res.setHeader('cache-control', 'max-age=' + server.maxAge);
}
}
if (error) {
res.writeHead(500, {});
res.end();
} else if (!exists) {
res.writeHead(404, {});
res.end();
} else if (new Date(req.headers['if-modified-since']) >= date) {
res.writeHead(304, {});
res.end();
} else {
if (req.method == 'HEAD') {
res.header("Content-Type", contentType);
res.writeHead(200, {});
res.end();
} else if (req.method == 'GET') {
getFileCompressed(filename, contentType, function (error, content) {
if(ERR(error)) return;
res.header("Content-Type", contentType);
res.writeHead(200, {});
res.write(content);
res.end();
});
} else {
res.writeHead(405, {'allow': 'HEAD, GET'});
res.end();
}
}
});
}
// find all includes in ace.js and embed them.
function getAceFile(callback) {
fs.readFile(ROOT_DIR + 'js/ace.js', "utf8", function(err, data) {
if(ERR(err, callback)) return;
// Find all includes in ace.js and embed them
var founds = data.match(/\$\$INCLUDE_[a-zA-Z_]+\("[^"]*"\)/gi);
if (!settings.minify) {
founds = [];
}
// Always include the require kernel.
founds.push('$$INCLUDE_JS("../static/js/require-kernel.js")');
data += ';\n';
data += 'Ace2Editor.EMBEDED = Ace2Editor.EMBEDED || {};\n';
// Request the contents of the included file on the server-side and write
// them into the file.
async.forEach(founds, function (item, callback) {
var filename = item.match(/"([^"]*)"/)[1];
var request = require('request');
var baseURI = 'http://localhost:' + settings.port
request(baseURI + path.normalize(path.join('/static/', filename)), function (error, response, body) {
if (!error && response.statusCode == 200) {
data += 'Ace2Editor.EMBEDED[' + JSON.stringify(filename) + '] = '
+ JSON.stringify(body || '') + ';\n';
} else {
// Silence?
}
callback();
});
}, function(error) {
callback(error, data);
});
});
}
// Check for the existance of the file and get the last modification date.
function statFile(filename, callback) {
if (filename == 'js/ace.js') {
// Sometimes static assets are inlined into this file, so we have to stat
// everything.
lastModifiedDateOfEverything(function (error, date) {
callback(error, date, !error);
});
} else if (filename == 'js/require-kernel.js') {
callback(null, requireLastModified(), true);
} else {
fs.stat(ROOT_DIR + filename, function (error, stats) {
if (error) {
if (error.code == "ENOENT") {
// Stat the directory instead.
fs.stat(path.dirname(ROOT_DIR + filename), function (error, stats) {
if (error) {
if (error.code == "ENOENT") {
callback(null, null, false);
} else {
callback(error);
}
} else {
callback(null, stats.mtime.getTime(), false);
}
});
} else {
callback(error);
}
} else {
callback(null, stats.mtime.getTime(), true);
}
});
}
}
function lastModifiedDateOfEverything(callback) {
var folders2check = [ROOT_DIR + 'js/', ROOT_DIR + 'css/'];
var latestModification = 0; var latestModification = 0;
async.series([
//find out the highest modification date
function(callback)
{
var folders2check = ["../static/css","../static/js"];
//go trough this two folders //go trough this two folders
async.forEach(folders2check, function(path, callback) async.forEach(folders2check, function(path, callback)
{ {
@ -104,208 +235,49 @@ exports.minifyJS = function(req, res, jsFilename)
}); });
}, callback); }, callback);
}); });
}, callback); }, function () {
}, callback(null, latestModification);
function(callback)
{
//check the modification time of the minified js
fs.stat("../var/minified_" + jsFilename, function(err, stats)
{
if(err && err.code != "ENOENT")
{
ERR(err, callback);
return;
}
//there is no minfied file or there new changes since this file was generated, so continue generating this file
if((err && err.code == "ENOENT") || stats.mtime.getTime() < latestModification)
{
callback();
}
//the minified file is still up to date, stop minifying
else
{
callback("stop");
}
});
},
//load all js files
function (callback)
{
async.forEach(jsFiles, function (item, callback)
{
fs.readFile("../static/js/" + item, "utf-8", function(err, data)
{
if(ERR(err, callback)) return;
fileValues[item] = data;
callback();
});
}, callback);
},
//find all includes in ace.js and embed them
function(callback)
{
//if this is not the creation of pad.js, skip this part
if(jsFilename != "pad.js")
{
callback();
return;
}
var founds = fileValues["ace.js"].match(/\$\$INCLUDE_[a-zA-Z_]+\([a-zA-Z0-9.\/_"]+\)/gi);
//go trough all includes
async.forEach(founds, function (item, callback)
{
var filename = item.match(/"[^"]*"/g)[0].substr(1);
filename = filename.substr(0,filename.length-1);
var type = item.match(/INCLUDE_[A-Z]+/g)[0].substr("INCLUDE_".length);
var quote = item.search("_Q") != -1;
//read the included file
fs.readFile(filename, "utf-8", function(err, data)
{
if(ERR(err, callback)) return;
//compress the file
if(type == "JS")
{
embeds[item] = "<script>\n" + compressJS([data])+ "\n\\x3c/script>";
}
else
{
embeds[item] = "<style>" + compressCSS([data])+ "</style>";
}
//do the first escape
embeds[item] = JSON.stringify(embeds[item]).replace(/'/g, "\\'").replace(/\\"/g, "\"");
embeds[item] = embeds[item].substr(1);
embeds[item] = embeds[item].substr(0, embeds[item].length-1);
//add quotes, if wished
if(quote)
{
embeds[item] = "'" + embeds[item] + "'";
}
//do the second escape
embeds[item] = JSON.stringify(embeds[item]).replace(/'/g, "\\'").replace(/\"/g, "\"");
embeds[item] = embeds[item].substr(1);
embeds[item] = embeds[item].substr(0, embeds[item].length-1);
embeds[item] = "'" + embeds[item] + "'";
callback();
});
}, function(err)
{
if(ERR(err, callback)) return;
//replace the include command with the include
for(var i in embeds)
{
fileValues["ace.js"]=fileValues["ace.js"].replace(i, embeds[i]);
}
callback();
});
},
//put all together and write it into a file
function(callback)
{
//put all javascript files in an array
var values = [];
for(var i in jsFiles)
{
values.push(fileValues[jsFiles[i]]);
}
//minify all javascript files to one
var result = compressJS(values);
async.parallel([
//write the results plain in a file
function(callback)
{
fs.writeFile("../var/minified_" + jsFilename, result, "utf8", callback);
},
//write the results compressed in a file
function(callback)
{
//spawn a gzip process if we're on a unix system
if(os.type().indexOf("Windows") == -1)
{
gzip(result, 9, function(err, compressedResult){
//weird gzip bug that returns 0 instead of null if everything is ok
err = err === 0 ? null : err;
if(ERR(err, callback)) return;
fs.writeFile("../var/minified_" + jsFilename + ".gz", compressedResult, callback);
}); });
} }
//skip this step on windows
else // This should be provided by the module, but until then, just use startup
{ // time.
callback(); var _requireLastModified = new Date();
function requireLastModified() {
return _requireLastModified.toUTCString();
} }
} function requireDefinition() {
],callback); return 'var require = ' + RequireKernel.kernelSource + ';\n';
}
], function(err)
{
if(err && err != "stop")
{
if(ERR(err)) return;
} }
//check if gzip is supported by this browser function getFileCompressed(filename, contentType, callback) {
var gzipSupport = req.header('Accept-Encoding', '').indexOf('gzip') != -1; getFile(filename, function (error, content) {
if (error || !content) {
var pathStr; callback(error, content);
if(gzipSupport && os.type().indexOf("Windows") == -1) } else {
{ if (settings.minify) {
pathStr = path.normalize(__dirname + "/../../var/minified_" + jsFilename + ".gz"); if (contentType == 'text/javascript') {
res.header('Content-Encoding', 'gzip'); try {
content = compressJS([content]);
} catch (error) {
// silence
} }
else } else if (contentType == 'text/css') {
{ content = compressCSS([content]);
pathStr = path.normalize(__dirname + "/../../var/minified_" + jsFilename );
} }
res.sendfile(pathStr, { maxAge: server.maxAge });
})
} }
//minifying is disabled, so put the files together in one file callback(null, content);
else }
{
var fileValues = {};
//read all js files
async.forEach(jsFiles, function (item, callback)
{
fs.readFile("../static/js/" + item, "utf-8", function(err, data)
{
if(ERR(err, callback)) return;
fileValues[item] = data;
callback();
}); });
},
//send all files together
function(err)
{
if(ERR(err)) return;
for(var i=0;i<jsFiles.length;i++)
{
var fileName = jsFiles[i];
res.write("\n\n\n/*** File: static/js/" + fileName + " ***/\n\n\n");
res.write(fileValues[fileName]);
} }
res.end(); function getFile(filename, callback) {
}); if (filename == 'js/ace.js') {
getAceFile(callback);
} else if (filename == 'js/require-kernel.js') {
callback(undefined, requireDefinition());
} else {
fs.readFile(ROOT_DIR + filename, callback);
} }
} }

View file

@ -20,6 +20,9 @@
*/ */
var fs = require("fs"); var fs = require("fs");
var os = require("os");
var path = require('path');
var argv = require('./Cli').argv;
/** /**
* The IP ep-lite should listen to * The IP ep-lite should listen to
@ -53,6 +56,11 @@ exports.requireSession = false;
*/ */
exports.editOnly = false; exports.editOnly = false;
/**
* Max age that responses will have (affects caching layer).
*/
exports.maxAge = 1000*60*60*6; // 6 hours
/** /**
* A flag that shows if minification is enabled or not * A flag that shows if minification is enabled or not
*/ */
@ -73,8 +81,25 @@ exports.loglevel = "INFO";
*/ */
exports.httpAuth = null; exports.httpAuth = null;
//checks if abiword is avaiable
exports.abiwordAvailable = function()
{
if(exports.abiword != null)
{
return os.type().indexOf("Windows") != -1 ? "withoutPDF" : "yes";
}
else
{
return "no";
}
}
// Discover where the settings file lives
var settingsFilename = argv.settings || "settings.json";
var settingsPath = settingsFilename.charAt(0) == '/' ? '' : path.normalize(__dirname + "/../../");
//read the settings sync //read the settings sync
var settingsStr = fs.readFileSync("../settings.json").toString(); var settingsStr = fs.readFileSync(settingsPath + settingsFilename).toString();
//remove all comments //remove all comments
settingsStr = settingsStr.replace(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/gm,"").replace(/#.*/g,"").replace(/\/\/.*/g,""); settingsStr = settingsStr.replace(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/gm,"").replace(/#.*/g,"").replace(/\/\/.*/g,"");

View file

@ -0,0 +1,177 @@
/*
* 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var async = require('async');
var Buffer = require('buffer').Buffer;
var fs = require('fs');
var path = require('path');
var server = require('../server');
var zlib = require('zlib');
var util = require('util');
var ROOT_DIR = path.normalize(__dirname + "/../");
var CACHE_DIR = ROOT_DIR + '../var/';
var responseCache = {};
/*
This caches and compresses 200 and 404 responses to GET and HEAD requests.
TODO: Caching and compressing are solved problems, a middleware configuration
should replace this.
*/
function CachingMiddleware() {
}
CachingMiddleware.prototype = new function () {
function handle(req, res, next) {
if (!(req.method == "GET" || req.method == "HEAD")) {
return next(undefined, req, res);
}
var old_req = {};
var old_res = {};
var supportsGzip =
req.header('Accept-Encoding', '').indexOf('gzip') != -1;
var path = require('url').parse(req.url).path;
var cacheKey = (new Buffer(path)).toString('base64').replace(/[\/\+=]/g, '');
fs.stat(CACHE_DIR + 'minified_' + cacheKey, function (error, stats) {
var modifiedSince = (req.headers['if-modified-since']
&& new Date(req.headers['if-modified-since']));
var lastModifiedCache = !error && stats.mtime;
if (lastModifiedCache) {
req.headers['if-modified-since'] = lastModifiedCache.toUTCString();
} else {
delete req.headers['if-modified-since'];
}
// Always issue get to downstream.
old_req.method = req.method;
req.method = 'GET';
var expirationDate = new Date(((responseCache[cacheKey] || {}).headers || {})['expires']);
if (expirationDate > new Date()) {
// Our cached version is still valid.
return respond();
}
var _headers = {};
old_res.setHeader = res.setHeader;
res.setHeader = function (key, value) {
_headers[key.toLowerCase()] = value;
old_res.setHeader.call(res, key, value);
};
old_res.writeHead = res.writeHead;
res.writeHead = function (status, headers) {
var lastModified = (res.getHeader('last-modified')
&& new Date(res.getHeader('last-modified')));
res.writeHead = old_res.writeHead;
if (status == 200 || status == 404) {
// Update cache
var buffer = '';
Object.keys(headers || {}).forEach(function (key) {
res.setHeader(key, headers[key]);
});
headers = _headers;
old_res.write = res.write;
old_res.end = res.end;
res.write = function(data, encoding) {
buffer += data.toString(encoding);
};
res.end = function(data, encoding) {
async.parallel([
function (callback) {
var path = CACHE_DIR + 'minified_' + cacheKey;
fs.writeFile(path, buffer, function (error, stats) {
callback();
});
}
, function (callback) {
var path = CACHE_DIR + 'minified_' + cacheKey + '.gz';
zlib.gzip(buffer, function(error, content) {
if (error) {
callback();
} else {
fs.writeFile(path, content, function (error, stats) {
callback();
});
}
});
}
], function () {
responseCache[cacheKey] = {statusCode: status, headers: headers};
respond();
});
};
} else if (status == 304) {
// Nothing new changed from the cached version.
old_res.write = res.write;
old_res.end = res.end;
res.write = function(data, encoding) {};
res.end = function(data, encoding) { respond() };
} else {
res.writeHead(status, headers);
}
};
next(undefined, req, res);
// This handles read/write synchronization as well as its predecessor,
// which is to say, not at all.
// TODO: Implement locking on write or ditch caching of gzip and use
// existing middlewares.
function respond() {
req.method = old_req.method || req.method;
res.write = old_res.write || res.write;
res.end = old_res.end || res.end;
var headers = responseCache[cacheKey].headers;
var statusCode = responseCache[cacheKey].statusCode;
var pathStr = CACHE_DIR + 'minified_' + cacheKey;
if (supportsGzip && (headers['content-type'] || '').match(/^text\//)) {
pathStr = pathStr + '.gz';
headers['content-encoding'] = 'gzip';
}
var lastModified = (headers['last-modified']
&& new Date(headers['last-modified']));
if (statusCode == 200 && lastModified <= modifiedSince) {
res.writeHead(304, headers);
res.end();
} else if (req.method == 'GET') {
var readStream = fs.createReadStream(pathStr);
res.writeHead(statusCode, headers);
util.pump(readStream, res);
} else {
res.writeHead(statusCode, headers);
res.end();
}
}
});
}
this.handle = handle;
}();
module.exports = CachingMiddleware;

24
node/utils/common_code.js Normal file
View file

@ -0,0 +1,24 @@
/**
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var RequireKernel = require('require-kernel/');
var path = require('path');
var CLIENT_JS_SRC = path.normalize(__dirname + '/../../static/js/');
var client_require = RequireKernel.requireForPaths(
'file://' + (CLIENT_JS_SRC.charAt(0) == '/' ? '' : '/') + encodeURI(CLIENT_JS_SRC));
exports.require = client_require;

View file

@ -1,692 +0,0 @@
// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.contentcollector
// %APPJET%: import("etherpad.collab.ace.easysync2.Changeset");
// %APPJET%: import("etherpad.admin.plugins");
/**
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var Changeset = require("../utils/Changeset");
var _MAX_LIST_LEVEL = 8;
function sanitizeUnicode(s)
{
return s.replace(/[\uffff\ufffe\ufeff\ufdd0-\ufdef\ud800-\udfff]/g, '?');
}
function makeContentCollector(collectStyles, browser, apool, domInterface, className2Author)
{
browser = browser || {};
var plugins_;
if (typeof(plugins) != 'undefined')
{
plugins_ = plugins;
}
else
{
plugins_ = {callHook: function () {}};
}
var dom = domInterface || {
isNodeText: function(n)
{
return (n.nodeType == 3);
},
nodeTagName: function(n)
{
return n.tagName;
},
nodeValue: function(n)
{
return n.nodeValue;
},
nodeNumChildren: function(n)
{
return n.childNodes.length;
},
nodeChild: function(n, i)
{
return n.childNodes.item(i);
},
nodeProp: function(n, p)
{
return n[p];
},
nodeAttr: function(n, a)
{
return n.getAttribute(a);
},
optNodeInnerHTML: function(n)
{
return n.innerHTML;
}
};
var _blockElems = {
"div": 1,
"p": 1,
"pre": 1,
"li": 1
};
function isBlockElement(n)
{
return !!_blockElems[(dom.nodeTagName(n) || "").toLowerCase()];
}
function textify(str)
{
return sanitizeUnicode(
str.replace(/[\n\r ]/g, ' ').replace(/\xa0/g, ' ').replace(/\t/g, ' '));
}
function getAssoc(node, name)
{
return dom.nodeProp(node, "_magicdom_" + name);
}
var lines = (function()
{
var textArray = [];
var attribsArray = [];
var attribsBuilder = null;
var op = Changeset.newOp('+');
var self = {
length: function()
{
return textArray.length;
},
atColumnZero: function()
{
return textArray[textArray.length - 1] === "";
},
startNew: function()
{
textArray.push("");
self.flush(true);
attribsBuilder = Changeset.smartOpAssembler();
},
textOfLine: function(i)
{
return textArray[i];
},
appendText: function(txt, attrString)
{
textArray[textArray.length - 1] += txt;
//dmesg(txt+" / "+attrString);
op.attribs = attrString;
op.chars = txt.length;
attribsBuilder.append(op);
},
textLines: function()
{
return textArray.slice();
},
attribLines: function()
{
return attribsArray;
},
// call flush only when you're done
flush: function(withNewline)
{
if (attribsBuilder)
{
attribsArray.push(attribsBuilder.toString());
attribsBuilder = null;
}
}
};
self.startNew();
return self;
}());
var cc = {};
function _ensureColumnZero(state)
{
if (!lines.atColumnZero())
{
cc.startNewLine(state);
}
}
var selection, startPoint, endPoint;
var selStart = [-1, -1],
selEnd = [-1, -1];
var blockElems = {
"div": 1,
"p": 1,
"pre": 1
};
function _isEmpty(node, state)
{
// consider clean blank lines pasted in IE to be empty
if (dom.nodeNumChildren(node) == 0) return true;
if (dom.nodeNumChildren(node) == 1 && getAssoc(node, "shouldBeEmpty") && dom.optNodeInnerHTML(node) == "&nbsp;" && !getAssoc(node, "unpasted"))
{
if (state)
{
var child = dom.nodeChild(node, 0);
_reachPoint(child, 0, state);
_reachPoint(child, 1, state);
}
return true;
}
return false;
}
function _pointHere(charsAfter, state)
{
var ln = lines.length() - 1;
var chr = lines.textOfLine(ln).length;
if (chr == 0 && state.listType && state.listType != 'none')
{
chr += 1; // listMarker
}
chr += charsAfter;
return [ln, chr];
}
function _reachBlockPoint(nd, idx, state)
{
if (!dom.isNodeText(nd)) _reachPoint(nd, idx, state);
}
function _reachPoint(nd, idx, state)
{
if (startPoint && nd == startPoint.node && startPoint.index == idx)
{
selStart = _pointHere(0, state);
}
if (endPoint && nd == endPoint.node && endPoint.index == idx)
{
selEnd = _pointHere(0, state);
}
}
cc.incrementFlag = function(state, flagName)
{
state.flags[flagName] = (state.flags[flagName] || 0) + 1;
}
cc.decrementFlag = function(state, flagName)
{
state.flags[flagName]--;
}
cc.incrementAttrib = function(state, attribName)
{
if (!state.attribs[attribName])
{
state.attribs[attribName] = 1;
}
else
{
state.attribs[attribName]++;
}
_recalcAttribString(state);
}
cc.decrementAttrib = function(state, attribName)
{
state.attribs[attribName]--;
_recalcAttribString(state);
}
function _enterList(state, listType)
{
var oldListType = state.listType;
state.listLevel = (state.listLevel || 0) + 1;
if (listType != 'none')
{
state.listNesting = (state.listNesting || 0) + 1;
}
state.listType = listType;
_recalcAttribString(state);
return oldListType;
}
function _exitList(state, oldListType)
{
state.listLevel--;
if (state.listType != 'none')
{
state.listNesting--;
}
state.listType = oldListType;
_recalcAttribString(state);
}
function _enterAuthor(state, author)
{
var oldAuthor = state.author;
state.authorLevel = (state.authorLevel || 0) + 1;
state.author = author;
_recalcAttribString(state);
return oldAuthor;
}
function _exitAuthor(state, oldAuthor)
{
state.authorLevel--;
state.author = oldAuthor;
_recalcAttribString(state);
}
function _recalcAttribString(state)
{
var lst = [];
for (var a in state.attribs)
{
if (state.attribs[a])
{
lst.push([a, 'true']);
}
}
if (state.authorLevel > 0)
{
var authorAttrib = ['author', state.author];
if (apool.putAttrib(authorAttrib, true) >= 0)
{
// require that author already be in pool
// (don't add authors from other documents, etc.)
lst.push(authorAttrib);
}
}
state.attribString = Changeset.makeAttribsString('+', lst, apool);
}
function _produceListMarker(state)
{
lines.appendText('*', Changeset.makeAttribsString('+', [
['list', state.listType],
['insertorder', 'first']
], apool));
}
cc.startNewLine = function(state)
{
if (state)
{
var atBeginningOfLine = lines.textOfLine(lines.length() - 1).length == 0;
if (atBeginningOfLine && state.listType && state.listType != 'none')
{
_produceListMarker(state);
}
}
lines.startNew();
}
cc.notifySelection = function(sel)
{
if (sel)
{
selection = sel;
startPoint = selection.startPoint;
endPoint = selection.endPoint;
}
};
cc.doAttrib = function(state, na)
{
state.localAttribs = (state.localAttribs || []);
state.localAttribs.push(na);
cc.incrementAttrib(state, na);
};
cc.collectContent = function(node, state)
{
if (!state)
{
state = {
flags: { /*name -> nesting counter*/
},
localAttribs: null,
attribs: { /*name -> nesting counter*/
},
attribString: ''
};
}
var localAttribs = state.localAttribs;
state.localAttribs = null;
var isBlock = isBlockElement(node);
var isEmpty = _isEmpty(node, state);
if (isBlock) _ensureColumnZero(state);
var startLine = lines.length() - 1;
_reachBlockPoint(node, 0, state);
if (dom.isNodeText(node))
{
var txt = dom.nodeValue(node);
var rest = '';
var x = 0; // offset into original text
if (txt.length == 0)
{
if (startPoint && node == startPoint.node)
{
selStart = _pointHere(0, state);
}
if (endPoint && node == endPoint.node)
{
selEnd = _pointHere(0, state);
}
}
while (txt.length > 0)
{
var consumed = 0;
if (state.flags.preMode)
{
var firstLine = txt.split('\n', 1)[0];
consumed = firstLine.length + 1;
rest = txt.substring(consumed);
txt = firstLine;
}
else
{ /* will only run this loop body once */
}
if (startPoint && node == startPoint.node && startPoint.index - x <= txt.length)
{
selStart = _pointHere(startPoint.index - x, state);
}
if (endPoint && node == endPoint.node && endPoint.index - x <= txt.length)
{
selEnd = _pointHere(endPoint.index - x, state);
}
var txt2 = txt;
if ((!state.flags.preMode) && /^[\r\n]*$/.exec(txt))
{
// prevents textnodes containing just "\n" from being significant
// in safari when pasting text, now that we convert them to
// spaces instead of removing them, because in other cases
// removing "\n" from pasted HTML will collapse words together.
txt2 = "";
}
var atBeginningOfLine = lines.textOfLine(lines.length() - 1).length == 0;
if (atBeginningOfLine)
{
// newlines in the source mustn't become spaces at beginning of line box
txt2 = txt2.replace(/^\n*/, '');
}
if (atBeginningOfLine && state.listType && state.listType != 'none')
{
_produceListMarker(state);
}
lines.appendText(textify(txt2), state.attribString);
x += consumed;
txt = rest;
if (txt.length > 0)
{
cc.startNewLine(state);
}
}
}
else
{
var tname = (dom.nodeTagName(node) || "").toLowerCase();
if (tname == "br")
{
cc.startNewLine(state);
}
else if (tname == "script" || tname == "style")
{
// ignore
}
else if (!isEmpty)
{
var styl = dom.nodeAttr(node, "style");
var cls = dom.nodeProp(node, "className");
var isPre = (tname == "pre");
if ((!isPre) && browser.safari)
{
isPre = (styl && /\bwhite-space:\s*pre\b/i.exec(styl));
}
if (isPre) cc.incrementFlag(state, 'preMode');
var oldListTypeOrNull = null;
var oldAuthorOrNull = null;
if (collectStyles)
{
plugins_.callHook('collectContentPre', {
cc: cc,
state: state,
tname: tname,
styl: styl,
cls: cls
});
if (tname == "b" || (styl && /\bfont-weight:\s*bold\b/i.exec(styl)) || tname == "strong")
{
cc.doAttrib(state, "bold");
}
if (tname == "i" || (styl && /\bfont-style:\s*italic\b/i.exec(styl)) || tname == "em")
{
cc.doAttrib(state, "italic");
}
if (tname == "u" || (styl && /\btext-decoration:\s*underline\b/i.exec(styl)) || tname == "ins")
{
cc.doAttrib(state, "underline");
}
if (tname == "s" || (styl && /\btext-decoration:\s*line-through\b/i.exec(styl)) || tname == "del")
{
cc.doAttrib(state, "strikethrough");
}
if (tname == "ul")
{
var type;
var rr = cls && /(?:^| )list-(bullet[12345678])\b/.exec(cls);
type = rr && rr[1] || "bullet" + String(Math.min(_MAX_LIST_LEVEL, (state.listNesting || 0) + 1));
oldListTypeOrNull = (_enterList(state, type) || 'none');
}
else if ((tname == "div" || tname == "p") && cls && cls.match(/(?:^| )ace-line\b/))
{
oldListTypeOrNull = (_enterList(state, type) || 'none');
}
if (className2Author && cls)
{
var classes = cls.match(/\S+/g);
if (classes && classes.length > 0)
{
for (var i = 0; i < classes.length; i++)
{
var c = classes[i];
var a = className2Author(c);
if (a)
{
oldAuthorOrNull = (_enterAuthor(state, a) || 'none');
break;
}
}
}
}
}
var nc = dom.nodeNumChildren(node);
for (var i = 0; i < nc; i++)
{
var c = dom.nodeChild(node, i);
cc.collectContent(c, state);
}
if (collectStyles)
{
plugins_.callHook('collectContentPost', {
cc: cc,
state: state,
tname: tname,
styl: styl,
cls: cls
});
}
if (isPre) cc.decrementFlag(state, 'preMode');
if (state.localAttribs)
{
for (var i = 0; i < state.localAttribs.length; i++)
{
cc.decrementAttrib(state, state.localAttribs[i]);
}
}
if (oldListTypeOrNull)
{
_exitList(state, oldListTypeOrNull);
}
if (oldAuthorOrNull)
{
_exitAuthor(state, oldAuthorOrNull);
}
}
}
if (!browser.msie)
{
_reachBlockPoint(node, 1, state);
}
if (isBlock)
{
if (lines.length() - 1 == startLine)
{
cc.startNewLine(state);
}
else
{
_ensureColumnZero(state);
}
}
if (browser.msie)
{
// in IE, a point immediately after a DIV appears on the next line
_reachBlockPoint(node, 1, state);
}
state.localAttribs = localAttribs;
};
// can pass a falsy value for end of doc
cc.notifyNextNode = function(node)
{
// an "empty block" won't end a line; this addresses an issue in IE with
// typing into a blank line at the end of the document. typed text
// goes into the body, and the empty line div still looks clean.
// it is incorporated as dirty by the rule that a dirty region has
// to end a line.
if ((!node) || (isBlockElement(node) && !_isEmpty(node)))
{
_ensureColumnZero(null);
}
};
// each returns [line, char] or [-1,-1]
var getSelectionStart = function()
{
return selStart;
};
var getSelectionEnd = function()
{
return selEnd;
};
// returns array of strings for lines found, last entry will be "" if
// last line is complete (i.e. if a following span should be on a new line).
// can be called at any point
cc.getLines = function()
{
return lines.textLines();
};
cc.finish = function()
{
lines.flush();
var lineAttribs = lines.attribLines();
var lineStrings = cc.getLines();
lineStrings.length--;
lineAttribs.length--;
var ss = getSelectionStart();
var se = getSelectionEnd();
function fixLongLines()
{
// design mode does not deal with with really long lines!
var lineLimit = 2000; // chars
var buffer = 10; // chars allowed over before wrapping
var linesWrapped = 0;
var numLinesAfter = 0;
for (var i = lineStrings.length - 1; i >= 0; i--)
{
var oldString = lineStrings[i];
var oldAttribString = lineAttribs[i];
if (oldString.length > lineLimit + buffer)
{
var newStrings = [];
var newAttribStrings = [];
while (oldString.length > lineLimit)
{
//var semiloc = oldString.lastIndexOf(';', lineLimit-1);
//var lengthToTake = (semiloc >= 0 ? (semiloc+1) : lineLimit);
lengthToTake = lineLimit;
newStrings.push(oldString.substring(0, lengthToTake));
oldString = oldString.substring(lengthToTake);
newAttribStrings.push(Changeset.subattribution(oldAttribString, 0, lengthToTake));
oldAttribString = Changeset.subattribution(oldAttribString, lengthToTake);
}
if (oldString.length > 0)
{
newStrings.push(oldString);
newAttribStrings.push(oldAttribString);
}
function fixLineNumber(lineChar)
{
if (lineChar[0] < 0) return;
var n = lineChar[0];
var c = lineChar[1];
if (n > i)
{
n += (newStrings.length - 1);
}
else if (n == i)
{
var a = 0;
while (c > newStrings[a].length)
{
c -= newStrings[a].length;
a++;
}
n += a;
}
lineChar[0] = n;
lineChar[1] = c;
}
fixLineNumber(ss);
fixLineNumber(se);
linesWrapped++;
numLinesAfter += newStrings.length;
newStrings.unshift(i, 1);
lineStrings.splice.apply(lineStrings, newStrings);
newAttribStrings.unshift(i, 1);
lineAttribs.splice.apply(lineAttribs, newAttribStrings);
}
}
return {
linesWrapped: linesWrapped,
numLinesAfter: numLinesAfter
};
}
var wrapData = fixLongLines();
return {
selStart: ss,
selEnd: se,
linesWrapped: wrapData.linesWrapped,
numLinesAfter: wrapData.numLinesAfter,
lines: lineStrings,
lineAttribs: lineAttribs
};
}
return cc;
}
exports.makeContentCollector = makeContentCollector;

70
node/utils/tar.json Normal file
View file

@ -0,0 +1,70 @@
{
"pad.js": [
"jquery.js"
, "security.js"
, "pad.js"
, "ace2_common.js"
, "pad_utils.js"
, "plugins.js"
, "undo-xpopup.js"
, "json2.js"
, "pad_cookie.js"
, "pad_editor.js"
, "pad_editbar.js"
, "pad_docbar.js"
, "pad_modals.js"
, "ace.js"
, "collab_client.js"
, "pad_userlist.js"
, "pad_impexp.js"
, "pad_savedrevs.js"
, "pad_connectionstatus.js"
, "chat.js"
, "excanvas.js"
, "farbtastic.js"
, "prefixfree.js"
]
, "timeslider.js": [
"jquery.js"
, "security.js"
, "plugins.js"
, "undo-xpopup.js"
, "json2.js"
, "colorutils.js"
, "draggable.js"
, "ace2_common.js"
, "pad_utils.js"
, "pad_cookie.js"
, "pad_editor.js"
, "pad_editbar.js"
, "pad_docbar.js"
, "pad_modals.js"
, "pad_savedrevs.js"
, "pad_impexp.js"
, "AttributePoolFactory.js"
, "Changeset.js"
, "domline.js"
, "linestylefilter.js"
, "cssmanager.js"
, "broadcast.js"
, "broadcast_slider.js"
, "broadcast_revisions.js"
, "timeslider.js"
]
, "ace2_inner.js": [
"ace2_common.js"
, "AttributePoolFactory.js"
, "Changeset.js"
, "security.js"
, "skiplist.js"
, "virtual_lines.js"
, "cssmanager.js"
, "colorutils.js"
, "undomodule.js"
, "contentcollector.js"
, "changesettracker.js"
, "linestylefilter.js"
, "domline.js"
, "ace2_inner.js"
]
}

View file

@ -10,18 +10,25 @@
"name": "Robin Buse" } "name": "Robin Buse" }
], ],
"dependencies" : { "dependencies" : {
"yajsml" : "1.1.2",
"request" : "2.9.100",
"require-kernel" : "1.0.3",
"socket.io" : "0.8.7", "socket.io" : "0.8.7",
"ueberDB" : "0.1.3", "ueberDB" : "0.1.7",
"async" : "0.1.15", "async" : "0.1.18",
"joose" : "3.50.0", "express" : "2.5.8",
"express" : "2.5.0", "clean-css" : "0.3.2",
"clean-css" : "0.2.4", "uglify-js" : "1.2.5",
"uglify-js" : "1.1.1", "formidable" : "1.0.9",
"gzip" : "0.1.0", "log4js" : "0.4.1",
"formidable" : "1.0.7",
"log4js" : "0.3.9",
"jsdom-nocontextifiy" : "0.2.10", "jsdom-nocontextifiy" : "0.2.10",
"async-stacktrace" : "0.0.2" "async-stacktrace" : "0.0.2"
}, },
"devDependencies": {
"jshint" : "*"
},
"engines" : { "node" : ">=0.6.0",
"npm" : ">=1.0"
},
"version" : "1.0.0" "version" : "1.0.0"
} }

View file

@ -39,6 +39,10 @@
but makes it impossible to debug the javascript/css */ but makes it impossible to debug the javascript/css */
"minify" : true, "minify" : true,
/* How long may clients use served javascript code? Without versioning this
is may cause problems during deployment. */
"maxAge" : 21600000, // 6 hours
/* This is the path to the Abiword executable. Setting it to null, disables abiword. /* This is the path to the Abiword executable. Setting it to null, disables abiword.
Abiword is needed to enable the import/export of pads*/ Abiword is needed to enable the import/export of pads*/
"abiword" : null, "abiword" : null,

View file

@ -32,6 +32,25 @@ ul.list-bullet6 { list-style-type: square; }
ul.list-bullet7 { list-style-type: disc; } ul.list-bullet7 { list-style-type: disc; }
ul.list-bullet8 { list-style-type: circle; } ul.list-bullet8 { list-style-type: circle; }
ol.list-number1 { margin-left: 1.5em; }
ol.list-number2 { margin-left: 3em; }
ol.list-number3 { margin-left: 4.5em; }
ol.list-number4 { margin-left: 6em; }
ol.list-number5 { margin-left: 7.5em; }
ol.list-number6 { margin-left: 9em; }
ol.list-number7 { margin-left: 10.5em; }
ol.list-number8 { margin-left: 12em; }
ol { list-style-type: decimal; }
ol.list-number1 { list-style-type: decimal; }
ol.list-number2 { list-style-type: lower-latin; }
ol.list-number3 { list-style-type: lower-roman; }
ol.list-number4 { list-style-type: decimal; }
ol.list-number5 { list-style-type: lower-latin; }
ol.list-number6 { list-style-type: lower-roman; }
ol.list-number7 { list-style-type: decimal; }
ol.list-number8 { list-style-type: lower-latin; }
ul.list-indent1 { margin-left: 1.5em; } ul.list-indent1 { margin-left: 1.5em; }
ul.list-indent2 { margin-left: 3em; } ul.list-indent2 { margin-left: 3em; }
ul.list-indent3 { margin-left: 4.5em; } ul.list-indent3 { margin-left: 4.5em; }
@ -74,7 +93,7 @@ body.doesWrap {
padding-top: 1px; /* important for some reason? */ padding-top: 1px; /* important for some reason? */
padding-right: 10px; padding-right: 10px;
padding-bottom: 8px; padding-bottom: 8px;
padding-left: 1px /* prevents characters from looking chopped off in FF3 */; padding-left: 1px /* prevents characters from looking chopped off in FF3 -- Removed because it added too much whitespace */;
overflow: hidden; overflow: hidden;
/* blank 1x1 gif, so that IE8 doesn't consider the body transparent */ /* blank 1x1 gif, so that IE8 doesn't consider the body transparent */
background-image: url(data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==); background-image: url(data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==);
@ -151,34 +170,3 @@ p {
} }
#overlaysdiv { position: absolute; left: -1000px; top: -1000px; } #overlaysdiv { position: absolute; left: -1000px; top: -1000px; }
/* ---------- Used by JavaScript Lexer ---------- */
.syntax .c { color: #bd3f00; font-style: italic } /* Comment */
.syntax .o { font-weight: bold; } /* Operator */
.syntax .p { font-weight: bold; } /* Punctuation */
.syntax .k { color: blue; } /* Keyword */
.syntax .kc { color: purple } /* Keyword.Constant */
.syntax .nx { } /* Name.Other */
.syntax .mf { color: purple } /* Literal.Number.Float */
.syntax .mh { color: purple } /* Literal.Number.Hex */
.syntax .mi { color: purple } /* Literal.Number.Integer */
.syntax .sr { color: purple } /* Literal.String.Regex */
.syntax .s2 { color: purple } /* Literal.String.Double */
.syntax .s1 { color: purple } /* Literal.String.Single */
.syntax .sd { color: purple } /* Literal.String.Doc */
.syntax .cs { color: #00aa33; font-weight: bold; font-style: italic } /* Comment.Special */
.syntax .err { color: #cc0000; font-weight: bold; text-decoration: underline; } /* Error */
/* css */
.syntax .nt { font-weight: bold; } /* tag */
.syntax .nc { color: #336; } /* class */
.syntax .nf { color: #336; } /* id */
.syntax .nd { color: #999; } /* :foo */
.syntax .m { color: purple } /* number */
.syntax .nb { color: purple } /* built-in */
.syntax .cp { color: #bd3f00; } /* !important */
.syntax .flash { background-color: #adf !important; }
.syntax .flashbad { background-color: #f55 !important; }

View file

@ -1,13 +1,13 @@
*,html,body,p{ margin: 0; padding: 0; } *,html,body,p{ margin: 0; padding: 0; }
.clear { clear: both; } .clear { clear: both; }
html { font-size: 62.5%; } html { font-size: 62.5%; width: 100%; }
body, textarea { font-family: Helvetica, Arial, sans-serif; } body, textarea { font-family: Helvetica, Arial, sans-serif; }
iframe {position:absolute;} iframe {position:absolute;}
#users #users
{ {
position: absolute; position: absolute;
z-index: 10; z-index:500;
background-color: #000; background-color: #000;
background-color: rgba(0,0,0,0.7); background-color: rgba(0,0,0,0.7);
width: 160px; width: 160px;
@ -15,7 +15,6 @@ iframe {position:absolute;}
top: 40px; top: 40px;
color: #fff; color: #fff;
padding: 5px; padding: 5px;
-moz-border-radius: 6px;
border-radius: 6px; border-radius: 6px;
} }
@ -39,10 +38,7 @@ a img
#editbar #editbar
{ {
background: #f7f7f7; background: #f7f7f7;
background: -moz-linear-gradient(#f7f7f7, #f1f1f1 80%); background: linear-gradient(#f7f7f7, #f1f1f1 80%);
background: -ms-linear-gradient(#f7f7f7, #f1f1f1 80%);
background: -o-linear-gradient(#f7f7f7, #f1f1f1 80%);
background: -webkit-linear-gradient(#f7f7f7, #f1f1f1 80%);
border-bottom: 1px solid #ccc; border-bottom: 1px solid #ccc;
height: 32px; height: 32px;
overflow: hidden; overflow: hidden;
@ -53,10 +49,7 @@ a img
#editbar ul li #editbar ul li
{ {
background: #fff; background: #fff;
background: -moz-linear-gradient(#fff, #f0f0f0); background: linear-gradient(#fff, #f0f0f0);
background: -ms-linear-gradient(#fff, #f0f0f0);
background: -o-linear-gradient(#fff, #f0f0f0);
background: -webkit-linear-gradient(#fff, #f0f0f0);
border: 1px solid #ccc; border: 1px solid #ccc;
border-radius: 4px; border-radius: 4px;
cursor: pointer; cursor: pointer;
@ -72,6 +65,7 @@ a img
{ {
text-decoration: none; text-decoration: none;
color: #ccc; color: #ccc;
position: absolute;
} }
#editbar ul li a span #editbar ul li a span
@ -82,18 +76,13 @@ a img
#editbar ul li:hover { #editbar ul li:hover {
background: #fff; background: #fff;
background: linear-gradient(#f4f4f4, #e4e4e4);
} }
#editbar ul li:active { #editbar ul li:active {
background: #eee; background: #eee;
background: -moz-linear-gradient(#ddd, #fff); background: linear-gradient(#ddd, #fff);
background: -ms-linear-gradient(#ddd, #fff); box-shadow: 0 0 8px rgba(0,0,0,.1) inset;
background: -o-linear-gradient(#ddd, #fff);
background: -webkit-linear-gradient(#ddd, #fff);
}
input[type="file"] {
color: #000;
} }
#editbar ul li.separator #editbar ul li.separator
@ -194,7 +183,6 @@ a#backtoprosite { padding-left: 20px; left: 6px;
#alertbar { #alertbar {
margin-top: 6px; margin-top: 6px;
opacity: 0; opacity: 0;
filter: alpha(opacity = 0); /* IE */
display: none; display: none;
position:absolute; position:absolute;
left:0; left:0;
@ -388,10 +376,7 @@ a#hidetopmsg { position: absolute; right: 5px; bottom: 5px; }
#mycolorpickersave, #mycolorpickercancel { #mycolorpickersave, #mycolorpickercancel {
background: #fff; background: #fff;
background: -moz-linear-gradient(#fff, #ccc); background: linear-gradient(#fff, #ccc);
background: -ms-linear-gradient(#fff, #ccc);
background: -o-linear-gradient(#fff, #ccc);
background: -webkit-linear-gradient(#fff, #ccc);
border: 1px solid #ccc; border: 1px solid #ccc;
border-radius: 4px; border-radius: 4px;
font-size:12px; font-size:12px;
@ -564,28 +549,6 @@ table#otheruserstable { display: none; }
display: none; z-index: 55; } display: none; z-index: 55; }
#revision-notifier .label { color: #777; font-weight: bold; } #revision-notifier .label { color: #777; font-weight: bold; }
/* We don't ever actually hide the wrapper, even when the panel is
cloased, so that its contents can always be manipulated accurately. */
#padoptions { position: absolute; top: 0; left: 0; font-size: 1.2em;
color: #444; height: 100%; width: 100%; line-height: 15px; }
#options-viewhead { font-weight: bold; position: absolute; top: 10px; left: 15px;
width: auto; height: auto; }
#padoptions label { display: block; }
#padoptions input { padding: 0; margin: 0; }
#options-colorscheck { position: absolute; left: 15px; top: 34px; width: 15px; height: 15px; }
#options-colorslabel { position: absolute; left: 35px; top: 34px; }
#options-linenoscheck { position: absolute; left: 15px; top: 57px; width: 15px; height: 15px; }
#options-linenoslabel { position: absolute; left: 35px; top: 57px; }
#options-fontlabel { position: absolute; left: 15px; top: 82px; }
#viewfontmenu { position: absolute; top: 80px; left: 90px; width: 110px; }
#options-viewexplain { position: absolute; left: 215px; top: 15px; width: 100px; height: 70px; font-size: .7em;
padding-left: 10px; padding-top: 10px; border-left: 1px solid #ccc;
line-height: 20px; font-weight: bold; color: #999; }
#options-close { display: block; position: absolute; right: 7px; bottom: 8px;
width: auto; height: auto; font-size: 85%; color: #444; }
#mainmodals { z-index: 600; /* higher than the modals themselves #mainmodals { z-index: 600; /* higher than the modals themselves
so that modals are on top in IE */ } so that modals are on top in IE */ }
.modalfield { font-size: 1.2em; padding: 1px; border: 1px solid #bbb;} .modalfield { font-size: 1.2em; padding: 1px; border: 1px solid #bbb;}
@ -751,14 +714,7 @@ a#topbarmaximize {
text-decoration: none; text-decoration: none;
padding: 50pt; padding: 50pt;
font-size: 20pt; font-size: 20pt;
-moz-border-radius-topleft: 3pt; border-radius: 3pt;
-moz-border-radius-topright: 3pt;
-moz-border-radius-bottomleft: 3pt;
-moz-border-radius-bottomright: 3pt;
-webkit-border-top-left-radius: 3pt;
-webkit-border-top-right-radius: 3pt;
-webkit-border-bottom-left-radius: 3pt;
-webkit-border-bottom-right-radius: 3pt;
} }
.modaldialog .bigbutton { .modaldialog .bigbutton {
@ -767,38 +723,8 @@ a#topbarmaximize {
width: 100%; width: 100%;
} }
#embed, #readonly {
display:none;
position:absolute;
top:40px;
font-size:14px;
width:400px;
right: 20px;
z-index: 500;
background-color: #000;
color: white;
background-color: rgb(0,0,0);
background-color: rgba(0,0,0,0.7);
padding: 10px;
-moz-border-radius: 6px;
border-radius: 6px;
} }
#embedreadonly {
float:right;
}
#embedcode, #readonlyUrl, #linkcode {
margin-left:10px;
}
#embedinput, #readonlyInput, #linkinput {
width:375px;
height:24px;
display:inline;
margin-top: 10px;
padding-left:4px;
}
ul#colorpickerswatches ul#colorpickerswatches
{ {
@ -854,6 +780,7 @@ ul#colorpickerswatches li:hover
left:0px; left:0px;
top:25px; top:25px;
bottom:25px; bottom:25px;
z-index:1002;
} }
#chattext p #chattext p
@ -874,12 +801,11 @@ ul#colorpickerswatches li:hover
#chatlabel #chatlabel
{ {
font-size:13px; font-size:13px;
line-height:16px;
font-weight:bold; font-weight:bold;
color:#555; color:#555;
text-decoration: none; text-decoration: none;
position: relative; margin-right: 3px;
bottom: 3px; vertical-align: middle;
} }
#chatinput #chatinput
@ -892,7 +818,7 @@ ul#colorpickerswatches li:hover
#chaticon #chaticon
{ {
z-index: 400; z-index: 400;
position:absolute; position: fixed;
bottom: 0px; bottom: 0px;
right: 20px; right: 20px;
padding: 5px; padding: 5px;
@ -914,8 +840,7 @@ ul#colorpickerswatches li:hover
{ {
color:#555; color:#555;
font-size:9px; font-size:9px;
position:relative; vertical-align: middle;
bottom: 2px;
} }
#titlebar #titlebar
@ -930,8 +855,7 @@ ul#colorpickerswatches li:hover
#titlelabel #titlelabel
{ {
font-size:13px; font-size:13px;
margin-left:20px; margin:4px 0 0 4px;
padding-top:3px;
position:absolute; position:absolute;
} }
@ -956,83 +880,18 @@ ul#colorpickerswatches li:hover
margin-top:2px; margin-top:2px;
} }
/* resizable stuff for chat */
.ui-resizable {
position: relative;
}
.ui-resizable-handle {
position: absolute;
font-size: 0.1px;
z-index: 99999;
display: block;
}
.ui-resizable-nw {
background-image: url("../img/nw-resize.png");
background-repeat: no-repeat;
background-size: 100% auto;
cursor: nw-resize;
height: 22px;
left: 0;
top: 0;
width: 22px;
}
.ui-resizable-ne
{
cursor: ne-resize;
width: 9px;
height: 9px;
right: -5px;
top: -5px;
}
#importexport{
position:absolute;
top:40px;
font-size:14px;
width:450px;
right: 20px;
z-index: 500;
background-color: #000;
color: white;
background-color: rgb(0,0,0);
background-color: rgba(0,0,0,0.7);
padding: 10px;
-moz-border-radius: 6px;
border-radius: 6px;
height:190px;
display:none;
}
#import{
position:absolute;
width:250px;
left:10px;
line-height:20px;
}
#export{
position:absolute;
width:180px;
right:10px;
line-height:20px;
}
.exporttype{ .exporttype{
line-height:25px; margin-top: 2px;
background-repeat:no-repeat; background-repeat:no-repeat;
padding-left:25px; padding-left:25px;
background-image: url("../img/fileicons.gif"); background-image: url("../../static/img/etherpad_lite_icons.png");
color:#fff; color:#fff;
text-decoration:none; text-decoration:none;
} }
#importexportline{ #importexportline{
border: dotted 1px; border-left: 1px solid #fff;
height: 185px; height: 190px;
position:absolute; position:absolute;
width:0px; width:0px;
left:260px; left:260px;
@ -1040,40 +899,36 @@ position: relative;
} }
.impexpbutton{ .impexpbutton{
background-image: -moz-linear-gradient(center top , #EEEEEE, white 20%, white 20%); background-image: linear-gradient(center top , #EEEEEE, white 20%, white 20%);
padding:3px; padding:3px;
} }
#exporthtml{ #exporthtml{
background-position: 2px -25px; background-position: 0px -299px;
} }
#exportplain{ #exportplain{
background-position: 2px -121px; background-position: 0px -395px;
} }
#exportword{ #exportword{
background-position: 2px -0px; background-position: 0px -275px;
} }
#exportpdf{ #exportpdf{
background-position: 2px -97px; background-position: 0px -371px;
} }
#exportopen{ #exportopen{
background-position: 2px -74px; background-position: 0px -347px;
} }
#exportwordle{ #exportwordle{
background-position: 2px -49px; background-position: 0px -323px;
} }
#exportdokuwiki{ #exportdokuwiki{
background-position: 2px -144px; background-position: 0px -459px;
}
#export a{
text-decoration: none;
} }
#importstatusball{ #importstatusball{
@ -1112,7 +967,6 @@ color: white;
background-color: rgb(0,0,0); background-color: rgb(0,0,0);
background-color: rgba(0,0,0,0.7); background-color: rgba(0,0,0,0.7);
padding: 10px; padding: 10px;
-moz-border-radius: 6px;
border-radius: 6px; border-radius: 6px;
opacity:.8; opacity:.8;
} }
@ -1125,6 +979,61 @@ background-repeat: no-repeat;
margin-left: 1px; margin-left: 1px;
margin-top: 1px; margin-top: 1px;
} }
.buttonicon-bold {
background-position: 0px -116px;
}
.buttonicon-italic {
background-position: 0px 0px;
}
.buttonicon-underline {
background-position: 0px -236px;
}
.buttonicon-strikethrough {
background-position: 0px -200px;
}
.buttonicon-insertorderedlist {
background-position: 0px -477px
}
.buttonicon-insertunorderedlist {
background-position: 0px -34px;
}
.buttonicon-indent {
background-position: 0px -52px;
}
.buttonicon-outdent {
background-position: 0px -134px;
}
.buttonicon-undo {
background-position: 0px -255px;
}
.buttonicon-redo {
background-position :0px -166px;
}
.buttonicon-clearauthorship {
background-position: 0px -86px;
}
.buttonicon-settings {
background-position: 0px -436px;
}
.buttonicon-import_export {
background-position: 0px -68px;
}
.buttonicon-embed {
background-position: 0px -18px;
}
.buttonicon-history {
background-position: 0px -218px;
}
.buttonicon-chat {
background-position: 0px -102px;
display: inline-block;
vertical-align: middle;
margin: 0 !important;
}
.buttonicon-showusers {
background-position: 0px -183px;
display: inline-block;
}
#usericon #usericon
{ {
@ -1145,21 +1054,112 @@ width:33px !important;
} }
#online_count{ #online_count{
color: #999; color: #888;
font-size: 11px;
line-height: 18px;
position: fixed;
} }
label[for=readonlyinput] {
margin: 0 10px 0 2px;
}
#qr_center { #qr_center {
margin: 10px 10px auto 0; margin: 10px 10px auto 0;
text-align: center; text-align: center;
} }
#qrcode{ #embedreadonlyqr {
margin-left:10px; box-shadow: 0 0 10px #000;
border-radius: 3px;
transition: all .2s ease-in-out;
}
#embedreadonlyqr:hover {
cursor: none;
transform: scale(1.5);
}
.rtl{
direction:RTL;
}
#chattext p {
word-wrap: break-word;
}
/* fix for misaligned checkboxes */
input[type=checkbox] {
vertical-align: -1px;
}
.right {
float:right;
}
.popup {
font-size: 14px;
width: 450px;
z-index: 500;
padding: 10px;
border-radius: 6px;
background: #222;
background: rgba(0,0,0,.7);
background: linear-gradient(rgba(0,0,0,.6), rgba(0,0,0,.7) 35px, rgba(0,0,0,.6));
box-shadow: 0 0 8px #888;
color: #fff;
}
.popup input[type=text] {
width: 100%;
padding: 5px;
box-sizing: border-box;
display:block;
margin-top: 10px;
}
.popup a {
text-decoration: none;
}
.popup h1 {
font-size: 18px;
}
.popup h2 {
font-size: 15px;
}
.popup p {
margin: 5px 0;
}
.column {
float: left;
width: 50%;
}
#settingsmenu, #importexport, #embed {
position: absolute;
top: 55px;
right: 20px;
display: none;
}
.note {
color: #ddd;
font-size: 11px;
font-weight: bold;
}
.selected {
background: #eee !important;
background: linear-gradient(#EEE, #F0F0F0) !important;
}
.stickyChat {
background-color: #f1f1f1 !important;
right: 0px !important;
top: 36px;
border-radius: 0px !important;
height: auto !important;
border: none !important;
border-left: 1px solid #ccc !important;
width: 185px !important;
} }
@media screen and (max-width: 960px) { @media screen and (max-width: 960px) {
@ -1178,34 +1178,25 @@ label[for=readonlyinput] {
} }
} }
@media only screen and (min-device-width: 320px) and (max-device-width: 600px) { @media only screen and (min-device-width: 320px) and (max-device-width: 720px) {
#editbar ul li { #editbar ul li {
padding: 4px 3px; padding: 4px 3px;
} }
#editbar ul#menu_right > li {
padding: 4px 8px;
margin-top: 2px;
}
#chaticon {
opacity: .8;
}
#users { #users {
right: none; right: 0;
left: 30px; top: 36px;
bottom: 33px;
border-radius: none;
} }
#mycolorpicker { #mycolorpicker {
right: 0; left: -72px; /* #mycolorpicker:width - #users:width */
left: 0 !important;
} }
#editorcontainer { #editorcontainer {
margin-bottom: 33px; margin-bottom: 33px;
} }
#editbar ul#menu_right { #editbar ul#menu_right {
background: #f7f7f7; background: #f7f7f7;
background: -moz-linear-gradient(#f7f7f7, #f1f1f1 80%); background: linear-gradient(#f7f7f7, #f1f1f1 80%);
background: -ms-linear-gradient(#f7f7f7, #f1f1f1 80%);
background: -o-linear-gradient(#f7f7f7, #f1f1f1 80%);
background: -webkit-linear-gradient(#f7f7f7, #f1f1f1 80%);
width: 100%; width: 100%;
overflow: hidden; overflow: hidden;
height: 32px; height: 32px;
@ -1213,9 +1204,6 @@ label[for=readonlyinput] {
bottom: 0; bottom: 0;
border-top: 1px solid #ccc; border-top: 1px solid #ccc;
} }
#editbar ul#menu_right li:not(:last-child) {
display: none;
}
#editbar ul#menu_right li:last-child { #editbar ul#menu_right li:last-child {
height: 24px; height: 24px;
border-radius: 0; border-radius: 0;
@ -1224,21 +1212,19 @@ label[for=readonlyinput] {
float: right; float: right;
} }
#chaticon { #chaticon {
bottom: 0; bottom: 3px;
right: 55px; right: 55px;
border-right: none; border-right: none;
border-radius: 0; border-radius: 0;
background: #f7f7f7; background: #f7f7f7;
background: -moz-linear-gradient(#f7f7f7, #f1f1f1 80%); background: linear-gradient(#f7f7f7, #f1f1f1 80%);
background: -ms-linear-gradient(#f7f7f7, #f1f1f1 80%);
background: -o-linear-gradient(#f7f7f7, #f1f1f1 80%);
background: -webkit-linear-gradient(#f7f7f7, #f1f1f1 80%);
border: 0; border: 0;
} }
#chatbox { #chatbox {
bottom: 32px; bottom: 32px;
right: 0; right: 0;
border-top-right-radius: 0; border-top-right-radius: 0;
border-right: none;
} }
#editbar ul li a span { #editbar ul li a span {
top: -3px; top: -3px;
@ -1246,10 +1232,39 @@ label[for=readonlyinput] {
#usericonback { #usericonback {
margin-top: 4px; margin-top: 4px;
} }
#sidediv { #qrcode {
display: none; display: none;
} }
#editbar ul#menu_right li:not(:last-child) {
display: block;
}
#editbar ul#menu_right > li {
background: none;
border: none;
margin-top: 4px;
padding: 4px 8px;
}
.selected {
background: none !important;
}
#timesliderlink {
display: none !important;
}
.popup {
border-radius: 0;
box-sizing: border-box;
width: 100%;
}
#settingsmenu, #importexport, #embed {
left: 0;
top: 0;
bottom: 33px;
right: 0;
}
.separator {
display: none;
}
#online_count {
line-height: 24px;
} }
.rtl{
direction:RTL;
} }

View file

@ -1,218 +1,106 @@
#editorcontainerbox { #editorcontainerbox {overflow:auto; top:40px;}
overflow: auto;
top:40px;
}
#padcontent { #padcontent {font-size:12px; padding:10px;}
padding: 10px;
font-size: 12px;
}
#timeslider-wrapper { #timeslider-wrapper {left:0; position:relative; right:0; top:0;}
position: relative; #timeslider-left {background-image:url(../../static/img/timeslider_left.png); height:63px; left:0; position:absolute; width:134px;}
left: 0px; #timeslider-right {background-image:url(../../static/img/timeslider_right.png); height:63px; position:absolute; right:0; top:0; width:155px;}
right: 0px; #timeslider {background-image:url(../../static/img/timeslider_background.png); height:63px; margin:0 9px;}
top: 0px; #timeslider #timeslider-slider {height:61px; left:0; position:absolute; top:1px; width:100%;}
} #ui-slider-handle {
-khtml-user-select:none;
#timeslider-left { -moz-user-select:none;
position: absolute; -ms-user-select:none;
left:0px; -webkit-user-select:none;
background-image: url(../../static/img/timeslider_left.png);
width: 134px;
height: 63px;
}
#timeslider-right {
position: absolute;
top:0px;
right:0px;
background-image: url(../../static/img/timeslider_right.png);
width: 155px;
height: 63px;
}
#timeslider {
/* margin:7px;*/
margin-bottom: 0px;
height: 63px;
margin-left: 9px;
margin-right: 9px;
background-image: url(../../static/img/timeslider_background.png);
}
div#timeslider #timeslider-slider {
position: absolute;
left: 0px;
top: 1px;
height: 61px;
width: 100%;
}
div#ui-slider-handle {
width: 13px;
height: 61px;
background-image:url(../../static/img/crushed_current_location.png); background-image:url(../../static/img/crushed_current_location.png);
cursor:pointer; cursor:pointer;
-moz-user-select: none; height:61px;
-khtml-user-select: none; left:0;
user-select: none;
position:absolute; position:absolute;
top:0; top:0;
left: 0; user-select:none;
width:13px;
} }
* html div#ui-slider-handle { /* IE 6/7 */ #ui-slider-bar {
background-image: url(../../static/img/current_location.gif); -khtml-user-select:none;
} -moz-user-select:none;
-ms-user-select:none;
div#ui-slider-bar { -webkit-user-select:none;
position: relative; cursor:pointer;
margin-right: 148px;
height:35px; height:35px;
margin-left:5px; margin-left:5px;
margin-right:148px;
position:relative;
top:20px; top:20px;
cursor: pointer;
-moz-user-select: none;
-khtml-user-select: none;
user-select:none; user-select:none;
} }
div#timeslider div#playpause_button { #playpause_button, #playpause_button_icon {height:47px; position:absolute; width:47px;}
background-image: url(../../static/img/crushed_button_undepressed.png); #playpause_button {background-image:url(../../static/img/crushed_button_undepressed.png); right:77px; top:9px;}
width: 47px; #playpause_button_icon {background-image:url(../../static/img/play.png); left:0; top:0;}
height: 47px; .pause#playpause_button_icon {background-image:url(../../static/img/pause.png);}
position: absolute;
right: 77px;
top: 9px;
}
div#timeslider div#playpause_button div#playpause_button_icon { #leftstar, #rightstar, #leftstep, #rightstep
background-image: url(../../static/img/play.png); {background:url(../../static/img/stepper_buttons.png) 0 0 no-repeat; height:21px; overflow:hidden; position:absolute;}
width: 47px; #leftstar {background-position:0 44px; right:34px; top:8px; width:30px;}
height: 47px; #rightstar {background-position:29px 44px; right:5px; top:8px; width:29px;}
position: absolute; #leftstep {background-position:0 22px; right:34px; top:20px; width:30px;}
top :0px; #rightstep {background-position:29px 22px; right:5px; top:20px; width:29px;}
left:0px;
}
* html div#timeslider div#playpause_button div#playpause_button_icon {
background-image: url(../../static/img/play.gif); /* IE 6/7 */
}
div#timeslider div#playpause_button div.pause#playpause_button_icon { #timeslider .star {
background-image: url(../../static/img/pause.png); background-image:url(../../static/img/star.png);
} cursor:pointer;
* html div#timeslider div#playpause_button div.pause#playpause_button_icon { height:16px;
background-image: url(../../static/img/pause.gif); /* IE 6/7 */
}
div #timeslider div#steppers div#leftstar {
position: absolute;
right: 34px;
top: 8px;
width:30px;
height:21px;
background: url(../../static/img/stepper_buttons.png) 0px 44px;
overflow:hidden;
}
div #timeslider div#steppers div#rightstar {
position: absolute;
right: 5px;
top: 8px;
width:29px;
height:21px;
background: url(../../static/img/stepper_buttons.png) 29px 44px;
overflow:hidden;
}
div #timeslider div#steppers div#leftstep {
position: absolute;
right: 34px;
top: 20px;
width:30px;
height:21px;
background: url(../../static/img/stepper_buttons.png) 0px 22px;
overflow:hidden;
}
div #timeslider div#steppers div#rightstep {
position: absolute;
right: 5px;
top: 20px;
width:29px;
height:21px;
background: url(../../static/img/stepper_buttons.png) 29px 22px;
overflow:hidden;
}
#timeslider div.star {
position:absolute; position:absolute;
top:40px; top:40px;
background-image: url(../../static/img/star.png);
width:15px; width:15px;
height: 16px;
cursor: pointer;
}
* html #timeslider div.star {
background-image: url(../../static/img/star.gif); /* IE 6/7 */
} }
#timeslider div#timer { #timeslider #timer {
position: absolute; color:#fff;
font-family:Arial, sans-serif; font-family:Arial, sans-serif;
font-size:11px;
left:7px; left:7px;
position:absolute;
text-align:center;
top:9px; top:9px;
width:122px; width:122px;
text-align: center;
color: white;
font-size: 11px;
} }
.topbarcenter{ .topbarcenter, #docbar {display:none;}
display:none; #padmain {top:30px;}
} #editbarright {float:right;}
#returnbutton {color:#222; font-size:16px; line-height:29px; margin-top:0; padding-right:6px;}
#importexport {top:118px;}
#importexport .popup {width:185px;}
#docbar{ /* lists */
display:none; .list-bullet2, .list-indent2, .list-number2 {margin-left:3em;}
} .list-bullet3, .list-indent3, .list-number3 {margin-left:4.5em;}
.list-bullet4, .list-indent4, .list-number4 {margin-left:6em;}
.list-bullet5, .list-indent5, .list-number5 {margin-left:7.5em;}
.list-bullet6, .list-indent6, .list-number6 {margin-left:9em;}
.list-bullet7, .list-indent7, .list-number7 {margin-left:10.5em;}
.list-bullet8, .list-indent8, .list-number8 {margin-left:12em;}
#padmain{ /* unordered lists */
top:30px; UL {list-style-type:disc; margin-left:1.5em;}
} UL UL {margin-left:0 !important;}
#editbarright{ .list-bullet2, .list-bullet5, .list-bullet8 {list-style-type:circle;}
float:right; .list-bullet3, .list-bullet6 {list-style-type:square;}
}
#returnbutton .list-indent1, .list-indent2, .list-indent3, .list-indent5, .list-indent5, .list-indent6, .list-indent7, .list-indent8 {list-style-type:none;}
{
font-size: 16px;
line-height: 29px;
margin-top: 0;
padding-right: 6pt;
color: #222;
}
ul { margin-left: 1.5em; } /* ordered lists */
ul ul { margin-left: 0 !important; } OL {list-style-type:decimal; margin-left:1.5em;}
ul.list-bullet1 { margin-left: 1.5em; } .list-number2, .list-number5, .list-number8 {list-style-type:lower-latin;}
ul.list-bullet2 { margin-left: 3em; } .list-number3, .list-number6 {list-style-type:lower-roman;}
ul.list-bullet3 { margin-left: 4.5em; }
ul.list-bullet4 { margin-left: 6em; }
ul.list-bullet5 { margin-left: 7.5em; }
ul.list-bullet6 { margin-left: 9em; }
ul.list-bullet7 { margin-left: 10.5em; }
ul.list-bullet8 { margin-left: 12em; }
ul { list-style-type: disc; }
ul.list-bullet1 { list-style-type: disc; }
ul.list-bullet2 { list-style-type: circle; }
ul.list-bullet3 { list-style-type: square; }
ul.list-bullet4 { list-style-type: disc; }
ul.list-bullet5 { list-style-type: circle; }
ul.list-bullet6 { list-style-type: square; }
ul.list-bullet7 { list-style-type: disc; }
ul.list-bullet8 { list-style-type: circle; }
/* IE 6/7 fixes ################################################################ */
* HTML #ui-slider-handle {background-image:url(../../static/img/current_location.gif);}
* HTML #timeslider .star {background-image:url(../../static/img/star.gif);}
* HTML #playpause_button_icon {background-image:url(../../static/img/play.gif);}
* HTML .pause#playpause_button_icon {background-image:url(../../static/img/pause.gif);}

View file

@ -1,5 +1,8 @@
/* /*
You may have to use !important to override css attributs, for example: custom css files are loaded after core css files. Simply use the same selector to override a style.
Example:
* {color: blue !important;} #editbar LI {border:1px solid #000;}
overrides
#editbar LI {border:1px solid #d5d5d5;}
from pad.css
*/ */

View file

@ -1,6 +1,6 @@
function costumStart() function customStart()
{ {
//define your javascript here //define your javascript here
//jquery is avaiable - except index.js //jquery is available - except index.js
//you can load extra scripts with $.getScript http://api.jquery.com/jQuery.getScript/ //you can load extra scripts with $.getScript http://api.jquery.com/jQuery.getScript/
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 452 B

View file

@ -1,27 +1,25 @@
<!doctype html> <!doctype html>
<html> <html>
<title>RPG Write</title> <title>RPG Write</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
<style> <style>
*{ margin:0;padding:0; }
body { body {
background: rgba(0,0,0,.05); margin: 0;
height: 100%;
color: #333; color: #333;
font: 14px helvetica, sans-serif; font: 14px helvetica, sans-serif;
background: #ccc; background: #ddd;
background: -webkit-radial-gradient(circle,#aaa,#eee 60%) center fixed; background: -webkit-radial-gradient(circle,#aaa,#eee 60%) center fixed;
background: -moz-radial-gradient(circle,#aaa,#eee 60%) center fixed; background: -moz-radial-gradient(circle,#aaa,#eee 60%) center fixed;
background: -ms-radial-gradient(circle,#aaa,#eee 60%) center fixed; background: -ms-radial-gradient(circle,#aaa,#eee 60%) center fixed;
background: -o-radial-gradient(circle,#aaa,#eee 60%) center fixed; background: -o-radial-gradient(circle,#aaa,#eee 60%) center fixed;
overflow-x: hidden;
border-top: 8px solid rgba(51,51,51,.8); border-top: 8px solid rgba(51,51,51,.8);
} }
#container { #wrapper {
text-shadow: 0 1px 1px #fff;
border-top: 1px solid #999; border-top: 1px solid #999;
margin-top: 160px; margin-top: 160px;
text-align: center;
padding: 15px; padding: 15px;
background: #eee; background: #eee;
background: -webkit-linear-gradient(#fff,#ccc); background: -webkit-linear-gradient(#fff,#ccc);
@ -31,6 +29,10 @@
opacity: .9; opacity: .9;
box-shadow: 0px 1px 8px rgba(0,0,0,0.3); box-shadow: 0px 1px 8px rgba(0,0,0,0.3);
} }
#inner {
width: 300px;
margin: 0 auto;
}
#button { #button {
margin: 0 auto; margin: 0 auto;
border-radius: 3px; border-radius: 3px;
@ -40,7 +42,6 @@
text-shadow: 0 -1px 0 rgba(0,0,0,.8); text-shadow: 0 -1px 0 rgba(0,0,0,.8);
height: 70px; height: 70px;
line-height: 70px; line-height: 70px;
width: 300px;
background: #555; background: #555;
background: -webkit-linear-gradient(#5F5F5F,#565656 50%,#4C4C4C 51%,#373737); background: -webkit-linear-gradient(#5F5F5F,#565656 50%,#4C4C4C 51%,#373737);
background: -moz-linear-gradient(#5F5F5F,#565656 50%,#4C4C4C 51%,#373737); background: -moz-linear-gradient(#5F5F5F,#565656 50%,#4C4C4C 51%,#373737);
@ -62,61 +63,70 @@
} }
#label { #label {
text-align: left; text-align: left;
margin: 0 auto; text-shadow: 0 1px 1px #fff;
width: 300px; margin: 16px auto 0;
} }
input { form {
vertical-align: middle; height: 38px;
background: #fff;
border: 1px solid #bbb;
border-radius: 3px;
position: relative;
}
button, input {
font-weight: bold; font-weight: bold;
font-size: 15px; font-size: 15px;
} }
input[type="text"] { input[type="text"] {
width: 243px;
padding: 10px 47px 10px 10px;
background: #fff;
border: 1px solid #bbb;
outline: none;
border-radius: 3px; border-radius: 3px;
text-shadow: 0 0 1px #fff; box-sizing: border-box;
-moz-box-sizing: border-box;
padding: 0 45px 0 10px;
*padding: 0; /* IE7 hack */
width: 100%;
height: 100%;
outline: none;
border: none;
position: absolute;
} }
input[type="submit"] { button[type="submit"] {
position: absolute;
right: 0;
width: 45px; width: 45px;
margin-left: -50px; height: 38px;
padding: 8px;
} }
input[type="submit"]::-moz-focus-inner { border: 0 } @media only screen and (min-device-width: 320px) and (max-device-width: 720px) {
@-moz-document url-prefix() { input[type="submit"] { padding: 7px } }
@media only screen and (min-device-width: 320px) and (max-device-width: 600px) {
body { body {
background: #bbb; background: #bbb;
background: -webkit-linear-gradient(#aaa,#eee 60%) center fixed; background: -webkit-linear-gradient(#aaa,#eee 60%) center fixed;
height: 100%; background: -moz-linear-gradient(#aaa,#eee 60%) center fixed;
background: -ms-linear-gradient(#aaa,#eee 60%) center fixed;
} }
#container { #wrapper {
margin-top: 0; margin-top: 0;
text-align: left;
} }
#button, #label { #inner {
text-align: center;
width: 95%; width: 95%;
} }
form { #label {
text-align: center; text-align: center;
} }
input[type=text] {
width: 75%;
}
} }
</style> </style>
<link href="static/custom/index.css" rel="stylesheet"> <link href="static/custom/index.css" rel="stylesheet">
<script src="static/custom/index.js"></script>
<div id="container"> <div id="wrapper">
<div id="button" onclick="go2Random()" class="translate">New Pad</div><br><div id="label" class="translate">or create/open a Pad with the name</div> <div id="inner">
<div id="button" onclick="go2Random()" class="translate">New Pad</div>
<div id="label" class="translate">or create/open a Pad with the name</div>
<form action="#" onsubmit="go2Name();return false;"> <form action="#" onsubmit="go2Name();return false;">
<input type="text" id="padname" autofocus> <input type="text" id="padname" autofocus x-webkit-speech>
<input type="submit" value="OK"> <button type="submit">OK</button>
</form> </form>
</div> </div>
</div>
<script src="static/custom/index.js"></script>
<script> <script>
function go2Name() function go2Name()
{ {
@ -142,9 +152,16 @@
return randomstring; return randomstring;
} }
<<<<<<< HEAD
//start the costum js //start the costum js
if(typeof costumStart == "function") costumStart(); if(typeof costumStart == "function") costumStart();
</script> </script>
</body> </body>
</html> </html>
=======
// start the custom js
if (typeof customStart == "function") customStart();
</script>
>>>>>>> 6fd73ecfda27fd6639c30a4c05c6149d66f669ee
</html> </html>

View file

@ -25,16 +25,30 @@
* limitations under the License. * limitations under the License.
*/ */
var AttributePoolFactory = require("./AttributePoolFactory"); var AttributePoolFactory = require("/AttributePoolFactory");
var _opt = null; var _opt = null;
//var exports = {}; /**
* ==================== General Util Functions =======================
*/
/**
* This method is called whenever there is an error in the sync process
* @param msg {string} Just some message
*/
exports.error = function error(msg) { exports.error = function error(msg) {
var e = new Error(msg); var e = new Error(msg);
e.easysync = true; e.easysync = true;
throw e; throw e;
}; };
/**
* This method is user for assertions with Messages
* if assert fails, the error function called.
* @param b {boolean} assertion condition
* @param msgParts {string} error to be passed if it fails
*/
exports.assert = function assert(b, msgParts) { exports.assert = function assert(b, msgParts) {
if (!b) { if (!b) {
var msg = Array.prototype.slice.call(arguments, 1).join(''); var msg = Array.prototype.slice.call(arguments, 1).join('');
@ -42,12 +56,30 @@ exports.assert = function assert(b, msgParts) {
} }
}; };
/**
* Parses a number from string base 36
* @param str {string} string of the number in base 36
* @returns {int} number
*/
exports.parseNum = function (str) { exports.parseNum = function (str) {
return parseInt(str, 36); return parseInt(str, 36);
}; };
/**
* Writes a number in base 36 and puts it in a string
* @param num {int} number
* @returns {string} string
*/
exports.numToString = function (num) { exports.numToString = function (num) {
return num.toString(36).toLowerCase(); return num.toString(36).toLowerCase();
}; };
/**
* Converts stuff before $ to base 10
* @obsolete not really used anywhere??
* @param cs {string} the string
* @return integer
*/
exports.toBaseTen = function (cs) { exports.toBaseTen = function (cs) {
var dollarIndex = cs.indexOf('$'); var dollarIndex = cs.indexOf('$');
var beforeDollar = cs.substring(0, dollarIndex); var beforeDollar = cs.substring(0, dollarIndex);
@ -57,13 +89,34 @@ exports.toBaseTen = function (cs) {
}) + fromDollar; }) + fromDollar;
}; };
/**
* ==================== Changeset Functions =======================
*/
/**
* returns the required length of the text before changeset
* can be applied
* @param cs {string} String representation of the Changeset
*/
exports.oldLen = function (cs) { exports.oldLen = function (cs) {
return exports.unpack(cs).oldLen; return exports.unpack(cs).oldLen;
}; };
/**
* returns the length of the text after changeset is applied
* @param cs {string} String representation of the Changeset
*/
exports.newLen = function (cs) { exports.newLen = function (cs) {
return exports.unpack(cs).newLen; return exports.unpack(cs).newLen;
}; };
/**
* this function creates an iterator which decodes string changeset operations
* @param opsStr {string} String encoding of the change operations to be performed
* @param optStartIndex {int} from where in the string should the iterator start
* @return {Op} type object iterator
*/
exports.opIterator = function (opsStr, optStartIndex) { exports.opIterator = function (opsStr, optStartIndex) {
//print(opsStr); //print(opsStr);
var regex = /((?:\*[0-9a-z]+)*)(?:\|([0-9a-z]+))?([-+=])([0-9a-z]+)|\?|/g; var regex = /((?:\*[0-9a-z]+)*)(?:\|([0-9a-z]+))?([-+=])([0-9a-z]+)|\?|/g;
@ -129,12 +182,21 @@ exports.opIterator = function (opsStr, optStartIndex) {
}; };
}; };
/**
* Cleans an Op object
* @param {Op} object to be cleared
*/
exports.clearOp = function (op) { exports.clearOp = function (op) {
op.opcode = ''; op.opcode = '';
op.chars = 0; op.chars = 0;
op.lines = 0; op.lines = 0;
op.attribs = ''; op.attribs = '';
}; };
/**
* Creates a new Op object
* @param optOpcode the type operation of the Op object
*/
exports.newOp = function (optOpcode) { exports.newOp = function (optOpcode) {
return { return {
opcode: (optOpcode || ''), opcode: (optOpcode || ''),
@ -143,6 +205,11 @@ exports.newOp = function (optOpcode) {
attribs: '' attribs: ''
}; };
}; };
/**
* Clones an Op
* @param op Op to be cloned
*/
exports.cloneOp = function (op) { exports.cloneOp = function (op) {
return { return {
opcode: op.opcode, opcode: op.opcode,
@ -151,12 +218,22 @@ exports.cloneOp = function (op) {
attribs: op.attribs attribs: op.attribs
}; };
}; };
/**
* Copies op1 to op2
* @param op1 src Op
* @param op2 dest Op
*/
exports.copyOp = function (op1, op2) { exports.copyOp = function (op1, op2) {
op2.opcode = op1.opcode; op2.opcode = op1.opcode;
op2.chars = op1.chars; op2.chars = op1.chars;
op2.lines = op1.lines; op2.lines = op1.lines;
op2.attribs = op1.attribs; op2.attribs = op1.attribs;
}; };
/**
* Writes the Op in a string the way that changesets need it
*/
exports.opString = function (op) { exports.opString = function (op) {
// just for debugging // just for debugging
if (!op.opcode) return 'null'; if (!op.opcode) return 'null';
@ -164,11 +241,19 @@ exports.opString = function (op) {
assem.append(op); assem.append(op);
return assem.toString(); return assem.toString();
}; };
/**
* Used just for debugging
*/
exports.stringOp = function (str) { exports.stringOp = function (str) {
// just for debugging // just for debugging
return exports.opIterator(str).next(); return exports.opIterator(str).next();
}; };
/**
* Used to check if a Changeset if valid
* @param cs {Changeset} Changeset to be checked
*/
exports.checkRep = function (cs) { exports.checkRep = function (cs) {
// doesn't check things that require access to attrib pool (e.g. attribute order) // doesn't check things that require access to attrib pool (e.g. attribute order)
// or original string (e.g. newline positions) // or original string (e.g. newline positions)
@ -218,6 +303,15 @@ exports.checkRep = function (cs) {
return cs; return cs;
} }
/**
* ==================== Util Functions =======================
*/
/**
* creates an object that allows you to append operations (type Op) and also
* compresses them if possible
*/
exports.smartOpAssembler = function () { exports.smartOpAssembler = function () {
// Like opAssembler but able to produce conforming exportss // Like opAssembler but able to produce conforming exportss
// from slightly looser input, at the cost of speed. // from slightly looser input, at the cost of speed.
@ -474,6 +568,10 @@ if (_opt) {
}; };
} }
/**
* A custom made String Iterator
* @param str {string} String to be iterated over
*/
exports.stringIterator = function (str) { exports.stringIterator = function (str) {
var curIndex = 0; var curIndex = 0;
@ -510,6 +608,9 @@ exports.stringIterator = function (str) {
}; };
}; };
/**
* A custom made StringBuffer
*/
exports.stringAssembler = function () { exports.stringAssembler = function () {
var pieces = []; var pieces = [];
@ -526,7 +627,11 @@ exports.stringAssembler = function () {
}; };
}; };
// "lines" need not be an array as long as it supports certain calls (lines_foo inside). /**
* This class allows to iterate and modify texts which have several lines
* It is used for applying Changesets on arrays of lines
* Note from prev docs: "lines" need not be an array as long as it supports certain calls (lines_foo inside).
*/
exports.textLinesMutator = function (lines) { exports.textLinesMutator = function (lines) {
// Mutates lines, an array of strings, in place. // Mutates lines, an array of strings, in place.
// Mutation operations have the same constraints as exports operations // Mutation operations have the same constraints as exports operations
@ -781,6 +886,21 @@ exports.textLinesMutator = function (lines) {
return self; return self;
}; };
/**
* Function allowing iterating over two Op strings.
* @params in1 {string} first Op string
* @params idx1 {int} integer where 1st iterator should start
* @params in2 {string} second Op string
* @params idx2 {int} integer where 2nd iterator should start
* @params func {function} which decides how 1st or 2nd iterator
* advances. When opX.opcode = 0, iterator X advances to
* next element
* func has signature f(op1, op2, opOut)
* op1 - current operation of the first iterator
* op2 - current operation of the second iterator
* opOut - result operator to be put into Changeset
* @return {string} the integrated changeset
*/
exports.applyZip = function (in1, idx1, in2, idx2, func) { exports.applyZip = function (in1, idx1, in2, idx2, func) {
var iter1 = exports.opIterator(in1, idx1); var iter1 = exports.opIterator(in1, idx1);
var iter2 = exports.opIterator(in2, idx2); var iter2 = exports.opIterator(in2, idx2);
@ -802,6 +922,11 @@ exports.applyZip = function (in1, idx1, in2, idx2, func) {
return assem.toString(); return assem.toString();
}; };
/**
* Unpacks a string encoded Changeset into a proper Changeset object
* @params cs {string} String encoded Changeset
* @returns {Changeset} a Changeset class
*/
exports.unpack = function (cs) { exports.unpack = function (cs) {
var headerRegex = /Z:([0-9a-z]+)([><])([0-9a-z]+)|/; var headerRegex = /Z:([0-9a-z]+)([><])([0-9a-z]+)|/;
var headerMatch = headerRegex.exec(cs); var headerMatch = headerRegex.exec(cs);
@ -823,6 +948,14 @@ exports.unpack = function (cs) {
}; };
}; };
/**
* Packs Changeset object into a string
* @params oldLen {int} Old length of the Changeset
* @params newLen {int] New length of the Changeset
* @params opsStr {string} String encoding of the changes to be made
* @params bank {string} Charbank of the Changeset
* @returns {Changeset} a Changeset class
*/
exports.pack = function (oldLen, newLen, opsStr, bank) { exports.pack = function (oldLen, newLen, opsStr, bank) {
var lenDiff = newLen - oldLen; var lenDiff = newLen - oldLen;
var lenDiffStr = (lenDiff >= 0 ? '>' + exports.numToString(lenDiff) : '<' + exports.numToString(-lenDiff)); var lenDiffStr = (lenDiff >= 0 ? '>' + exports.numToString(lenDiff) : '<' + exports.numToString(-lenDiff));
@ -831,6 +964,11 @@ exports.pack = function (oldLen, newLen, opsStr, bank) {
return a.join(''); return a.join('');
}; };
/**
* Applies a Changeset to a string
* @params cs {string} String encoded Changeset
* @params str {string} String to which a Changeset should be applied
*/
exports.applyToText = function (cs, str) { exports.applyToText = function (cs, str) {
var unpacked = exports.unpack(cs); var unpacked = exports.unpack(cs);
exports.assert(str.length == unpacked.oldLen, "mismatched apply: ", str.length, " / ", unpacked.oldLen); exports.assert(str.length == unpacked.oldLen, "mismatched apply: ", str.length, " / ", unpacked.oldLen);
@ -856,6 +994,11 @@ exports.applyToText = function (cs, str) {
return assem.toString(); return assem.toString();
}; };
/**
* applies a changeset on an array of lines
* @param CS {Changeset} the changeset to be applied
* @param lines The lines to which the changeset needs to be applied
*/
exports.mutateTextLines = function (cs, lines) { exports.mutateTextLines = function (cs, lines) {
var unpacked = exports.unpack(cs); var unpacked = exports.unpack(cs);
var csIter = exports.opIterator(unpacked.ops); var csIter = exports.opIterator(unpacked.ops);
@ -878,6 +1021,13 @@ exports.mutateTextLines = function (cs, lines) {
mut.close(); mut.close();
}; };
/**
* Composes two attribute strings (see below) into one.
* @param att1 {string} first attribute string
* @param att2 {string} second attribue string
* @param resultIsMutaton {boolean}
* @param pool {AttribPool} attribute pool
*/
exports.composeAttributes = function (att1, att2, resultIsMutation, pool) { exports.composeAttributes = function (att1, att2, resultIsMutation, pool) {
// att1 and att2 are strings like "*3*f*1c", asMutation is a boolean. // att1 and att2 are strings like "*3*f*1c", asMutation is a boolean.
// Sometimes attribute (key,value) pairs are treated as attribute presence // Sometimes attribute (key,value) pairs are treated as attribute presence
@ -935,6 +1085,10 @@ exports.composeAttributes = function (att1, att2, resultIsMutation, pool) {
return buf.toString(); return buf.toString();
}; };
/**
* Function used as parameter for applyZip to apply a Changeset to an
* attribute
*/
exports._slicerZipperFunc = function (attOp, csOp, opOut, pool) { exports._slicerZipperFunc = function (attOp, csOp, opOut, pool) {
// attOp is the op from the sequence that is being operated on, either an // attOp is the op from the sequence that is being operated on, either an
// attribution string or the earlier of two exportss being composed. // attribution string or the earlier of two exportss being composed.
@ -1021,6 +1175,12 @@ exports._slicerZipperFunc = function (attOp, csOp, opOut, pool) {
} }
}; };
/**
* Applies a Changeset to the attribs string of a AText.
* @param cs {string} Changeset
* @param astr {string} the attribs string of a AText
* @param pool {AttribsPool} the attibutes pool
*/
exports.applyToAttribution = function (cs, astr, pool) { exports.applyToAttribution = function (cs, astr, pool) {
var unpacked = exports.unpack(cs); var unpacked = exports.unpack(cs);
@ -1129,6 +1289,11 @@ exports.mutateAttributionLines = function (cs, lines, pool) {
//dmesg("-> "+lines.toSource()); //dmesg("-> "+lines.toSource());
}; };
/**
* joins several Attribution lines
* @param theAlines collection of Attribution lines
* @returns {string} joined Attribution lines
*/
exports.joinAttributionLines = function (theAlines) { exports.joinAttributionLines = function (theAlines) {
var assem = exports.mergingOpAssembler(); var assem = exports.mergingOpAssembler();
for (var i = 0; i < theAlines.length; i++) { for (var i = 0; i < theAlines.length; i++) {
@ -1179,10 +1344,20 @@ exports.splitAttributionLines = function (attrOps, text) {
return lines; return lines;
}; };
/**
* splits text into lines
* @param {string} text to be splitted
*/
exports.splitTextLines = function (text) { exports.splitTextLines = function (text) {
return text.match(/[^\n]*(?:\n|[^\n]$)/g); return text.match(/[^\n]*(?:\n|[^\n]$)/g);
}; };
/**
* compose two Changesets
* @param cs1 {Changeset} first Changeset
* @param cs2 {Changeset} second Changeset
* @param pool {AtribsPool} Attribs pool
*/
exports.compose = function (cs1, cs2, pool) { exports.compose = function (cs1, cs2, pool) {
var unpacked1 = exports.unpack(cs1); var unpacked1 = exports.unpack(cs1);
var unpacked2 = exports.unpack(cs2); var unpacked2 = exports.unpack(cs2);
@ -1225,10 +1400,14 @@ exports.compose = function (cs1, cs2, pool) {
return exports.pack(len1, len3, newOps, bankAssem.toString()); return exports.pack(len1, len3, newOps, bankAssem.toString());
}; };
/**
* returns a function that tests if a string of attributes
* (e.g. *3*4) contains a given attribute key,value that
* is already present in the pool.
* @param attribPair array [key,value] of the attribute
* @param pool {AttribPool} Attribute pool
*/
exports.attributeTester = function (attribPair, pool) { exports.attributeTester = function (attribPair, pool) {
// returns a function that tests if a string of attributes
// (e.g. *3*4) contains a given attribute key,value that
// is already present in the pool.
if (!pool) { if (!pool) {
return never; return never;
} }
@ -1247,10 +1426,27 @@ exports.attributeTester = function (attribPair, pool) {
} }
}; };
/**
* creates the identity Changeset of length N
* @param N {int} length of the identity changeset
*/
exports.identity = function (N) { exports.identity = function (N) {
return exports.pack(N, N, "", ""); return exports.pack(N, N, "", "");
}; };
/**
* creates a Changeset which works on oldFullText and removes text
* from spliceStart to spliceStart+numRemoved and inserts newText
* instead. Also gives possibility to add attributes optNewTextAPairs
* for the new text.
* @param oldFullText {string} old text
* @param spliecStart {int} where splicing starts
* @param numRemoved {int} number of characters to be removed
* @param newText {string} string to be inserted
* @param optNewTextAPairs {string} new pairs to be inserted
* @param pool {AttribPool} Attribution Pool
*/
exports.makeSplice = function (oldFullText, spliceStart, numRemoved, newText, optNewTextAPairs, pool) { exports.makeSplice = function (oldFullText, spliceStart, numRemoved, newText, optNewTextAPairs, pool) {
var oldLen = oldFullText.length; var oldLen = oldFullText.length;
@ -1271,8 +1467,14 @@ exports.makeSplice = function (oldFullText, spliceStart, numRemoved, newText, op
return exports.pack(oldLen, newLen, assem.toString(), newText); return exports.pack(oldLen, newLen, assem.toString(), newText);
}; };
/**
* Transforms a changeset into a list of splices in the form
* [startChar, endChar, newText] meaning replace text from
* startChar to endChar with newText
* @param cs Changeset
*/
exports.toSplices = function (cs) { exports.toSplices = function (cs) {
// get a list of splices, [startChar, endChar, newText] //
var unpacked = exports.unpack(cs); var unpacked = exports.unpack(cs);
var splices = []; var splices = [];
@ -1302,6 +1504,9 @@ exports.toSplices = function (cs) {
return splices; return splices;
}; };
/**
*
*/
exports.characterRangeFollow = function (cs, startChar, endChar, insertionsAfter) { exports.characterRangeFollow = function (cs, startChar, endChar, insertionsAfter) {
var newStartChar = startChar; var newStartChar = startChar;
var newEndChar = endChar; var newEndChar = endChar;
@ -1346,6 +1551,14 @@ exports.characterRangeFollow = function (cs, startChar, endChar, insertionsAfter
return [newStartChar, newEndChar]; return [newStartChar, newEndChar];
}; };
/**
* Iterate over attributes in a changeset and move them from
* oldPool to newPool
* @param cs {Changeset} Chageset/attribution string to be iterated over
* @param oldPool {AttribPool} old attributes pool
* @param newPool {AttribPool} new attributes pool
* @return {string} the new Changeset
*/
exports.moveOpsToNewPool = function (cs, oldPool, newPool) { exports.moveOpsToNewPool = function (cs, oldPool, newPool) {
// works on exports or attribution string // works on exports or attribution string
var dollarPos = cs.indexOf('$'); var dollarPos = cs.indexOf('$');
@ -1363,13 +1576,22 @@ exports.moveOpsToNewPool = function (cs, oldPool, newPool) {
}) + fromDollar; }) + fromDollar;
}; };
/**
* create an attribution inserting a text
* @param text {string} text to be inserted
*/
exports.makeAttribution = function (text) { exports.makeAttribution = function (text) {
var assem = exports.smartOpAssembler(); var assem = exports.smartOpAssembler();
assem.appendOpWithText('+', text); assem.appendOpWithText('+', text);
return assem.toString(); return assem.toString();
}; };
// callable on a exports, attribution string, or attribs property of an op /**
* Iterates over attributes in exports, attribution string, or attribs property of an op
* and runs function func on them
* @param cs {Changeset} changeset
* @param func {function} function to be called
*/
exports.eachAttribNumber = function (cs, func) { exports.eachAttribNumber = function (cs, func) {
var dollarPos = cs.indexOf('$'); var dollarPos = cs.indexOf('$');
if (dollarPos < 0) { if (dollarPos < 0) {
@ -1383,12 +1605,21 @@ exports.eachAttribNumber = function (cs, func) {
}); });
}; };
// callable on a exports, attribution string, or attribs property of an op, /**
// though it may easily create adjacent ops that can be merged. * Filter attributes which should remain in a Changeset
* callable on a exports, attribution string, or attribs property of an op,
* though it may easily create adjacent ops that can be merged.
* @param cs {Changeset} changeset to be filtered
* @param filter {function} fnc which returns true if an
* attribute X (int) should be kept in the Changeset
*/
exports.filterAttribNumbers = function (cs, filter) { exports.filterAttribNumbers = function (cs, filter) {
return exports.mapAttribNumbers(cs, filter); return exports.mapAttribNumbers(cs, filter);
}; };
/**
* does exactly the same as exports.filterAttribNumbers
*/
exports.mapAttribNumbers = function (cs, func) { exports.mapAttribNumbers = function (cs, func) {
var dollarPos = cs.indexOf('$'); var dollarPos = cs.indexOf('$');
if (dollarPos < 0) { if (dollarPos < 0) {
@ -1410,6 +1641,12 @@ exports.mapAttribNumbers = function (cs, func) {
return newUpToDollar + cs.substring(dollarPos); return newUpToDollar + cs.substring(dollarPos);
}; };
/**
* Create a Changeset going from Identity to a certain state
* @params text {string} text of the final change
* @attribs attribs {string} optional, operations which insert
* the text and also puts the right attributes
*/
exports.makeAText = function (text, attribs) { exports.makeAText = function (text, attribs) {
return { return {
text: text, text: text,
@ -1417,6 +1654,12 @@ exports.makeAText = function (text, attribs) {
}; };
}; };
/**
* Apply a Changeset to a AText
* @param cs {Changeset} Changeset to be applied
* @param atext {AText}
* @param pool {AttribPool} Attribute Pool to add to
*/
exports.applyToAText = function (cs, atext, pool) { exports.applyToAText = function (cs, atext, pool) {
return { return {
text: exports.applyToText(cs, atext.text), text: exports.applyToText(cs, atext.text),
@ -1424,6 +1667,10 @@ exports.applyToAText = function (cs, atext, pool) {
}; };
}; };
/**
* Clones a AText structure
* @param atext {AText}
*/
exports.cloneAText = function (atext) { exports.cloneAText = function (atext) {
return { return {
text: atext.text, text: atext.text,
@ -1431,11 +1678,20 @@ exports.cloneAText = function (atext) {
}; };
}; };
/**
* Copies a AText structure from atext1 to atext2
* @param atext {AText}
*/
exports.copyAText = function (atext1, atext2) { exports.copyAText = function (atext1, atext2) {
atext2.text = atext1.text; atext2.text = atext1.text;
atext2.attribs = atext1.attribs; atext2.attribs = atext1.attribs;
}; };
/**
* Append the set of operations from atext to an assembler
* @param atext {AText}
* @param assem Assembler like smartOpAssembler
*/
exports.appendATextToAssembler = function (atext, assem) { exports.appendATextToAssembler = function (atext, assem) {
// intentionally skips last newline char of atext // intentionally skips last newline char of atext
var iter = exports.opIterator(atext.attribs); var iter = exports.opIterator(atext.attribs);
@ -1469,6 +1725,11 @@ exports.appendATextToAssembler = function (atext, assem) {
} }
}; };
/**
* Creates a clone of a Changeset and it's APool
* @param cs {Changeset}
* @param pool {AtributePool}
*/
exports.prepareForWire = function (cs, pool) { exports.prepareForWire = function (cs, pool) {
var newPool = AttributePoolFactory.createAttributePool();; var newPool = AttributePoolFactory.createAttributePool();;
var newCs = exports.moveOpsToNewPool(cs, pool, newPool); var newCs = exports.moveOpsToNewPool(cs, pool, newPool);
@ -1478,15 +1739,32 @@ exports.prepareForWire = function (cs, pool) {
}; };
}; };
/**
* Checks if a changeset s the identity changeset
*/
exports.isIdentity = function (cs) { exports.isIdentity = function (cs) {
var unpacked = exports.unpack(cs); var unpacked = exports.unpack(cs);
return unpacked.ops == "" && unpacked.oldLen == unpacked.newLen; return unpacked.ops == "" && unpacked.oldLen == unpacked.newLen;
}; };
/**
* returns all the values of attributes with a certain key
* in an Op attribs string
* @param attribs {string} Attribute string of a Op
* @param key {string} string to be seached for
* @param pool {AttribPool} attribute pool
*/
exports.opAttributeValue = function (op, key, pool) { exports.opAttributeValue = function (op, key, pool) {
return exports.attribsAttributeValue(op.attribs, key, pool); return exports.attribsAttributeValue(op.attribs, key, pool);
}; };
/**
* returns all the values of attributes with a certain key
* in an attribs string
* @param attribs {string} Attribute string
* @param key {string} string to be seached for
* @param pool {AttribPool} attribute pool
*/
exports.attribsAttributeValue = function (attribs, key, pool) { exports.attribsAttributeValue = function (attribs, key, pool) {
var value = ''; var value = '';
if (attribs) { if (attribs) {
@ -1499,6 +1777,11 @@ exports.attribsAttributeValue = function (attribs, key, pool) {
return value; return value;
}; };
/**
* Creates a Changeset builder for a string with initial
* length oldLen. Allows to add/remove parts of it
* @param oldLen {int} Old length
*/
exports.builder = function (oldLen) { exports.builder = function (oldLen) {
var assem = exports.smartOpAssembler(); var assem = exports.smartOpAssembler();
var o = exports.newOp(); var o = exports.newOp();

View file

@ -28,9 +28,10 @@ Ace2Editor.registry = {
nextId: 1 nextId: 1
}; };
var plugins = require('/plugins').plugins;
function Ace2Editor() function Ace2Editor()
{ {
var thisFunctionsName = "Ace2Editor";
var ace2 = Ace2Editor; var ace2 = Ace2Editor;
var editor = {}; var editor = {};
@ -48,8 +49,7 @@ function Ace2Editor()
{ {
var that = this; var that = this;
var args = arguments; var args = arguments;
var action = function()
function action()
{ {
func.apply(that, args); func.apply(that, args);
} }
@ -70,78 +70,47 @@ function Ace2Editor()
function doActionsPendingInit() function doActionsPendingInit()
{ {
for (var i = 0; i < actionsPendingInit.length; i++) $.each(actionsPendingInit, function(i,fn){
{ fn()
actionsPendingInit[i](); });
}
actionsPendingInit = []; actionsPendingInit = [];
} }
ace2.registry[info.id] = info; ace2.registry[info.id] = info;
editor.importText = pendingInit(function(newCode, undoable) // The following functions (prefixed by 'ace_') are exposed by editor, but
{ // execution is delayed until init is complete
info.ace_importText(newCode, undoable); var aceFunctionsPendingInit = ['importText', 'importAText', 'focus',
'setEditable', 'getFormattedCode', 'setOnKeyPress', 'setOnKeyDown',
'setNotifyDirty', 'setProperty', 'setBaseText', 'setBaseAttributedText',
'applyChangesToBase', 'applyPreparedChangesetToBase',
'setUserChangeNotificationCallback', 'setAuthorInfo',
'setAuthorSelectionRange', 'callWithAce', 'execCommand', 'replaceRange'];
$.each(aceFunctionsPendingInit, function(i,fnName){
var prefix = 'ace_';
var name = prefix + fnName;
editor[fnName] = pendingInit(function(){
info[prefix + fnName].apply(this, arguments);
}); });
editor.importAText = pendingInit(function(newCode, apoolJsonObj, undoable)
{
info.ace_importAText(newCode, apoolJsonObj, undoable);
}); });
editor.exportText = function() editor.exportText = function()
{ {
if (!loaded) return "(awaiting init)\n"; if (!loaded) return "(awaiting init)\n";
return info.ace_exportText(); return info.ace_exportText();
}; };
editor.getFrame = function() editor.getFrame = function()
{ {
return info.frame || null; return info.frame || null;
}; };
editor.focus = pendingInit(function()
{
info.ace_focus();
});
editor.setEditable = pendingInit(function(newVal)
{
info.ace_setEditable(newVal);
});
editor.getFormattedCode = function()
{
return info.ace_getFormattedCode();
};
editor.setOnKeyPress = pendingInit(function(handler)
{
info.ace_setOnKeyPress(handler);
});
editor.setOnKeyDown = pendingInit(function(handler)
{
info.ace_setOnKeyDown(handler);
});
editor.setNotifyDirty = pendingInit(function(handler)
{
info.ace_setNotifyDirty(handler);
});
editor.setProperty = pendingInit(function(key, value)
{
info.ace_setProperty(key, value);
});
editor.getDebugProperty = function(prop) editor.getDebugProperty = function(prop)
{ {
return info.ace_getDebugProperty(prop); return info.ace_getDebugProperty(prop);
}; };
editor.setBaseText = pendingInit(function(txt)
{
info.ace_setBaseText(txt);
});
editor.setBaseAttributedText = pendingInit(function(atxt, apoolJsonObj)
{
info.ace_setBaseAttributedText(atxt, apoolJsonObj);
});
editor.applyChangesToBase = pendingInit(function(changes, optAuthor, apoolJsonObj)
{
info.ace_applyChangesToBase(changes, optAuthor, apoolJsonObj);
});
// prepareUserChangeset: // prepareUserChangeset:
// Returns null if no new changes or ACE not ready. Otherwise, bundles up all user changes // Returns null if no new changes or ACE not ready. Otherwise, bundles up all user changes
// to the latest base text into a Changeset, which is returned (as a string if encodeAsString). // to the latest base text into a Changeset, which is returned (as a string if encodeAsString).
@ -156,24 +125,6 @@ function Ace2Editor()
if (!loaded) return null; if (!loaded) return null;
return info.ace_prepareUserChangeset(); return info.ace_prepareUserChangeset();
}; };
editor.applyPreparedChangesetToBase = pendingInit(
function()
{
info.ace_applyPreparedChangesetToBase();
});
editor.setUserChangeNotificationCallback = pendingInit(function(callback)
{
info.ace_setUserChangeNotificationCallback(callback);
});
editor.setAuthorInfo = pendingInit(function(author, authorInfo)
{
info.ace_setAuthorInfo(author, authorInfo);
});
editor.setAuthorSelectionRange = pendingInit(function(author, start, end)
{
info.ace_setAuthorSelectionRange(author, start, end);
});
editor.getUnhandledErrors = function() editor.getUnhandledErrors = function()
{ {
@ -182,45 +133,71 @@ function Ace2Editor()
return info.ace_getUnhandledErrors(); return info.ace_getUnhandledErrors();
}; };
editor.callWithAce = pendingInit(function(fn, callStack, normalize)
{
return info.ace_callWithAce(fn, callStack, normalize);
});
editor.execCommand = pendingInit(function(cmd, arg1)
{
info.ace_execCommand(cmd, arg1);
});
editor.replaceRange = pendingInit(function(start, end, text)
{
info.ace_replaceRange(start, end, text);
});
// calls to these functions ($$INCLUDE_...) are replaced when this file is processed function sortFilesByEmbeded(files) {
// and compressed, putting the compressed code from the named file directly into the var embededFiles = [];
// source here. var remoteFiles = [];
var $$INCLUDE_CSS = function(fileName)
{
return '<link rel="stylesheet" type="text/css" href="' + fileName + '"/>';
};
var $$INCLUDE_JS = function(fileName)
{
return '\x3cscript type="text/javascript" src="' + fileName + '">\x3c/script>';
};
var $$INCLUDE_JS_DEV = $$INCLUDE_JS;
var $$INCLUDE_CSS_DEV = $$INCLUDE_CSS;
var $$INCLUDE_CSS_Q = function(fileName) if (Ace2Editor.EMBEDED) {
{ for (var i = 0, ii = files.length; i < ii; i++) {
return '\'<link rel="stylesheet" type="text/css" href="' + fileName + '"/>\''; var file = files[i];
}; if (Object.prototype.hasOwnProperty.call(Ace2Editor.EMBEDED, file)) {
var $$INCLUDE_JS_Q = function(fileName) embededFiles.push(file);
{ } else {
return '\'\\x3cscript type="text/javascript" src="' + fileName + '">\\x3c/script>\''; remoteFiles.push(file);
}; }
var $$INCLUDE_JS_Q_DEV = $$INCLUDE_JS_Q; }
var $$INCLUDE_CSS_Q_DEV = $$INCLUDE_CSS_Q; } else {
remoteFiles = files;
}
return {embeded: embededFiles, remote: remoteFiles};
}
function pushRequireScriptTo(buffer) {
var KERNEL_SOURCE = '../static/js/require-kernel.js';
var KERNEL_BOOT = 'require.setRootURI("../minified/");\nrequire.setGlobalKeyPath("require");'
if (Ace2Editor.EMBEDED && Ace2Editor.EMBEDED[KERNEL_SOURCE]) {
buffer.push('<script type="text/javascript">');
buffer.push(Ace2Editor.EMBEDED[KERNEL_SOURCE]);
buffer.push(KERNEL_BOOT);
buffer.push('<\/script>');
}
}
function pushScriptsTo(buffer) {
/* Folling is for packaging regular expression. */
/* $$INCLUDE_JS("../minified/ace2_inner.js?callback=require.define"); */
var ACE_SOURCE = '../minified/ace2_inner.js?callback=require.define';
if (Ace2Editor.EMBEDED && Ace2Editor.EMBEDED[ACE_SOURCE]) {
buffer.push('<script type="text/javascript">');
buffer.push(Ace2Editor.EMBEDED[ACE_SOURCE]);
buffer.push('require("/ace2_inner");');
buffer.push('<\/script>');
} else {
buffer.push('<script type="application/javascript" src="' + ACE_SOURCE + '"><\/script>');
buffer.push('<script type="text/javascript">');
buffer.push('require("/ace2_inner");');
buffer.push('<\/script>');
}
}
function pushStyleTagsFor(buffer, files) {
var sorted = sortFilesByEmbeded(files);
var embededFiles = sorted.embeded;
var remoteFiles = sorted.remote;
if (embededFiles.length > 0) {
buffer.push('<style type="text/css">');
for (var i = 0, ii = embededFiles.length; i < ii; i++) {
var file = embededFiles[i];
buffer.push(Ace2Editor.EMBEDED[file].replace(/<\//g, '<\\/'));
}
buffer.push('<\/style>');
}
for (var i = 0, ii = remoteFiles.length; i < ii; i++) {
var file = remoteFiles[i];
buffer.push('<link rel="stylesheet" type="text/css" href="' + file + '"\/>');
}
}
editor.destroy = pendingInit(function() editor.destroy = pendingInit(function()
{ {
@ -246,52 +223,69 @@ function Ace2Editor()
{ {
var doctype = "<!doctype html>"; var doctype = "<!doctype html>";
var iframeHTML = ["'" + doctype + "<html><head>'"]; var iframeHTML = [];
iframeHTML.push(doctype);
iframeHTML.push("<html><head>");
// For compatability's sake transform in and out.
for (var i = 0, ii = iframeHTML.length; i < ii; i++) {
iframeHTML[i] = JSON.stringify(iframeHTML[i]);
}
plugins.callHook("aceInitInnerdocbodyHead", { plugins.callHook("aceInitInnerdocbodyHead", {
iframeHTML: iframeHTML iframeHTML: iframeHTML
}); });
for (var i = 0, ii = iframeHTML.length; i < ii; i++) {
iframeHTML[i] = JSON.parse(iframeHTML[i]);
}
// calls to these functions ($$INCLUDE_...) are replaced when this file is processed
// and compressed, putting the compressed code from the named file directly into the
// source here.
// these lines must conform to a specific format because they are passed by the build script: // these lines must conform to a specific format because they are passed by the build script:
iframeHTML.push($$INCLUDE_CSS_Q("../static/css/iframe_editor.css")); var includedCSS = [];
iframeHTML.push($$INCLUDE_CSS_Q("../static/css/pad.css")); var $$INCLUDE_CSS = function(filename) {includedCSS.push(filename)};
iframeHTML.push($$INCLUDE_CSS_Q("../static/custom/pad.css")); $$INCLUDE_CSS("../static/css/iframe_editor.css");
iframeHTML.push($$INCLUDE_JS_Q("../static/js/ace2_common.js")); $$INCLUDE_CSS("../static/css/pad.css");
iframeHTML.push($$INCLUDE_JS_Q("../static/js/skiplist.js")); $$INCLUDE_CSS("../static/custom/pad.css");
iframeHTML.push($$INCLUDE_JS_Q("../static/js/virtual_lines.js")); pushStyleTagsFor(iframeHTML, includedCSS);
iframeHTML.push($$INCLUDE_JS_Q("../static/js/easysync2.js"));
iframeHTML.push($$INCLUDE_JS_Q("../static/js/cssmanager.js"));
iframeHTML.push($$INCLUDE_JS_Q("../static/js/colorutils.js"));
iframeHTML.push($$INCLUDE_JS_Q("../static/js/undomodule.js"));
iframeHTML.push($$INCLUDE_JS_Q("../static/js/contentcollector.js"));
iframeHTML.push($$INCLUDE_JS_Q("../static/js/changesettracker.js"));
iframeHTML.push($$INCLUDE_JS_Q("../static/js/linestylefilter.js"));
iframeHTML.push($$INCLUDE_JS_Q("../static/js/domline.js"));
iframeHTML.push($$INCLUDE_JS_Q("../static/js/ace2_inner.js"));
iframeHTML.push('\'\\n<style type="text/css" title="dynamicsyntax"></style>\\n\''); var includedJS = [];
iframeHTML.push('\'</head><body id="innerdocbody" class="syntax" spellcheck="false">&nbsp;</body></html>\''); var $$INCLUDE_JS = function(filename) {includedJS.push(filename)};
pushRequireScriptTo(iframeHTML);
// Inject my plugins into my child.
iframeHTML.push('\
<script type="text/javascript">\
require.define("/plugins", null);\n\
require.define("/plugins.js", function (require, exports, module) {\
module.exports = parent.parent.require("/plugins");\
});\
</script>\
');
pushScriptsTo(iframeHTML);
iframeHTML.push('<style type="text/css" title="dynamicsyntax"></style>');
iframeHTML.push('</head><body id="innerdocbody" class="syntax" spellcheck="false">&nbsp;</body></html>');
// Expose myself to global for my child frame.
var thisFunctionsName = "ChildAccessibleAce2Editor";
(function () {return this}())[thisFunctionsName] = Ace2Editor;
var outerScript = 'editorId = "' + info.id + '"; editorInfo = parent.' + thisFunctionsName + '.registry[editorId]; ' + 'window.onload = function() ' + '{ window.onload = null; setTimeout' + '(function() ' + '{ var iframe = document.createElement("IFRAME"); ' + 'iframe.scrolling = "no"; var outerdocbody = document.getElementById("outerdocbody"); ' + 'iframe.frameBorder = 0; iframe.allowTransparency = true; ' + // for IE var outerScript = 'editorId = "' + info.id + '"; editorInfo = parent.' + thisFunctionsName + '.registry[editorId]; ' + 'window.onload = function() ' + '{ window.onload = null; setTimeout' + '(function() ' + '{ var iframe = document.createElement("IFRAME"); ' + 'iframe.scrolling = "no"; var outerdocbody = document.getElementById("outerdocbody"); ' + 'iframe.frameBorder = 0; iframe.allowTransparency = true; ' + // for IE
'outerdocbody.insertBefore(iframe, outerdocbody.firstChild); ' + 'iframe.ace_outerWin = window; ' + 'readyFunc = function() { editorInfo.onEditorReady(); readyFunc = null; editorInfo = null; }; ' + 'var doc = iframe.contentWindow.document; doc.open(); var text = (' + iframeHTML.join('+') + ').replace(/\\\\x3c/g, \'<\');doc.write(text); doc.close(); ' + '}, 0); }'; 'outerdocbody.insertBefore(iframe, outerdocbody.firstChild); ' + 'iframe.ace_outerWin = window; ' + 'readyFunc = function() { editorInfo.onEditorReady(); readyFunc = null; editorInfo = null; }; ' + 'var doc = iframe.contentWindow.document; doc.open(); var text = (' + JSON.stringify(iframeHTML.join('\n')) + ');doc.write(text); doc.close(); ' + '}, 0); }';
var outerHTML = [doctype, '<html><head>']
var includedCSS = [];
var $$INCLUDE_CSS = function(filename) {includedCSS.push(filename)};
$$INCLUDE_CSS("../static/css/iframe_editor.css");
$$INCLUDE_CSS("../static/css/pad.css");
$$INCLUDE_CSS("../static/custom/pad.css");
pushStyleTagsFor(outerHTML, includedCSS);
var outerHTML = [doctype, '<html><head>', $$INCLUDE_CSS("../static/css/iframe_editor.css"), $$INCLUDE_CSS("../static/css/pad.css"), $$INCLUDE_CSS("../static/custom/pad.css"),
// bizarrely, in FF2, a file with no "external" dependencies won't finish loading properly // bizarrely, in FF2, a file with no "external" dependencies won't finish loading properly
// (throbs busy while typing) // (throbs busy while typing)
'<link rel="stylesheet" type="text/css" href="data:text/css,"/>', '\x3cscript>\n', outerScript, '\n\x3c/script>', '</head><body id="outerdocbody"><div id="sidediv"><!-- --></div><div id="linemetricsdiv">x</div><div id="overlaysdiv"><!-- --></div></body></html>']; outerHTML.push('<link rel="stylesheet" type="text/css" href="data:text/css,"/>', '\x3cscript>\n', outerScript.replace(/<\//g, '<\\/'), '\n\x3c/script>', '</head><body id="outerdocbody"><div id="sidediv"><!-- --></div><div id="linemetricsdiv">x</div><div id="overlaysdiv"><!-- --></div></body></html>');
if (!Array.prototype.map) Array.prototype.map = function(fun)
{ //needed for IE
if (typeof fun != "function") throw new TypeError();
var len = this.length;
var res = new Array(len);
var thisp = arguments[1];
for (var i = 0; i < len; i++)
{
if (i in this) res[i] = fun.call(thisp, this[i], i, this);
}
return res;
};
var outerFrame = document.createElement("IFRAME"); var outerFrame = document.createElement("IFRAME");
outerFrame.frameBorder = 0; // for IE outerFrame.frameBorder = 0; // for IE
@ -308,3 +302,5 @@ function Ace2Editor()
return editor; return editor;
} }
exports.Ace2Editor = Ace2Editor;

View file

@ -20,6 +20,7 @@
* limitations under the License. * limitations under the License.
*/ */
var Security = require('/security');
function isNodeText(node) function isNodeText(node)
{ {
@ -80,14 +81,8 @@ function isArray(testObject)
return testObject && typeof testObject === 'object' && !(testObject.propertyIsEnumerable('length')) && typeof testObject.length === 'number'; return testObject && typeof testObject === 'object' && !(testObject.propertyIsEnumerable('length')) && typeof testObject.length === 'number';
} }
if (typeof exports !== "undefined") var userAgent = (((function () {return this;})().navigator || {}).userAgent || 'node-js').toLowerCase();
{
userAgent = "node-js";
}
else
{
userAgent = navigator.userAgent.toLowerCase();
}
// Figure out what browser is being used (stolen from jquery 1.2.1) // Figure out what browser is being used (stolen from jquery 1.2.1)
var browser = { var browser = {
version: (userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/) || [])[1], version: (userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/) || [])[1],
@ -95,7 +90,8 @@ var browser = {
opera: /opera/.test(userAgent), opera: /opera/.test(userAgent),
msie: /msie/.test(userAgent) && !/opera/.test(userAgent), msie: /msie/.test(userAgent) && !/opera/.test(userAgent),
mozilla: /mozilla/.test(userAgent) && !/(compatible|webkit)/.test(userAgent), mozilla: /mozilla/.test(userAgent) && !/(compatible|webkit)/.test(userAgent),
windows: /windows/.test(userAgent) // dgreensp windows: /windows/.test(userAgent),
mobile: /mobile/.test(userAgent) || /android/.test(userAgent)
}; };
@ -142,10 +138,25 @@ function binarySearchInfinite(expectedLength, func)
function htmlPrettyEscape(str) function htmlPrettyEscape(str)
{ {
return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\r?\n/g, '\\n'); return Security.escapeHTML(str).replace(/\r?\n/g, '\\n');
} }
if (typeof exports !== "undefined") var noop = function(){};
{ var identity = function(x){return x};
exports.isNodeText = isNodeText;
exports.object = object;
exports.extend = extend;
exports.forEach = forEach;
exports.map = map; exports.map = map;
} exports.filter = filter;
exports.isArray = isArray;
exports.browser = browser;
exports.getAssoc = getAssoc;
exports.setAssoc = setAssoc;
exports.binarySearch = binarySearch;
exports.binarySearchInfinite = binarySearchInfinite;
exports.htmlPrettyEscape = htmlPrettyEscape;
exports.map = map;
exports.noop = noop;
exports.identity = identity;

File diff suppressed because it is too large Load diff

View file

@ -20,45 +20,22 @@
* limitations under the License. * limitations under the License.
*/ */
var global = this; var makeCSSManager = require('/cssmanager').makeCSSManager;
var domline = require('/domline').domline;
var AttribPool = require('/AttributePoolFactory').createAttributePool;
var Changeset = require('/Changeset');
var linestylefilter = require('/linestylefilter').linestylefilter;
var colorutils = require('/colorutils').colorutils;
var Ace2Common = require('./ace2_common');
function loadBroadcastJS() var map = Ace2Common.map;
{ var forEach = Ace2Common.forEach;
// just in case... (todo: this must be somewhere else in the client code.)
// Below Array#map code was direct pasted by AppJet/Etherpad, licence unknown. Possible source: http://www.tutorialspoint.com/javascript/array_map.htm
if (!Array.prototype.map)
{
Array.prototype.map = function(fun /*, thisp*/ )
{
var len = this.length >>> 0;
if (typeof fun != "function") throw new TypeError();
var res = new Array(len); // These parameters were global, now they are injected. A reference to the
var thisp = arguments[1]; // Timeslider controller would probably be more appropriate.
for (var i = 0; i < len; i++) function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, BroadcastSlider)
{ {
if (i in this) res[i] = fun.call(thisp, this[i], i, this); var changesetLoader = undefined;
}
return res;
};
}
// Below Array#forEach code was direct pasted by AppJet/Etherpad, licence unknown. Possible source: http://www.tutorialspoint.com/javascript/array_foreach.htm
if (!Array.prototype.forEach)
{
Array.prototype.forEach = function(fun /*, thisp*/ )
{
var len = this.length >>> 0;
if (typeof fun != "function") throw new TypeError();
var thisp = arguments[1];
for (var i = 0; i < len; i++)
{
if (i in this) fun.call(thisp, this[i], i, this);
}
};
}
// Below Array#indexOf code was direct pasted by AppJet/Etherpad, licence unknown. Possible source: http://www.tutorialspoint.com/javascript/array_indexof.htm // Below Array#indexOf code was direct pasted by AppJet/Etherpad, licence unknown. Possible source: http://www.tutorialspoint.com/javascript/array_indexof.htm
if (!Array.prototype.indexOf) if (!Array.prototype.indexOf)
@ -91,11 +68,6 @@ function loadBroadcastJS()
} }
} }
function randomString()
{
return "_" + Math.floor(Math.random() * 1000000);
}
// for IE // for IE
if ($.browser.msie) if ($.browser.msie)
{ {
@ -107,7 +79,7 @@ function loadBroadcastJS()
{} {}
} }
var userId = "hiddenUser" + randomString();
var socketId; var socketId;
//var socket; //var socket;
var channelState = "DISCONNECTED"; var channelState = "DISCONNECTED";
@ -183,10 +155,7 @@ function loadBroadcastJS()
// splice the lines // splice the lines
splice: function(start, numRemoved, newLinesVA) splice: function(start, numRemoved, newLinesVA)
{ {
var newLines = Array.prototype.slice.call(arguments, 2).map( var newLines = map(Array.prototype.slice.call(arguments, 2), function(s) {
function(s)
{
return s; return s;
}); });
@ -308,10 +277,13 @@ function loadBroadcastJS()
padContents.currentTime += timeDelta * 1000; padContents.currentTime += timeDelta * 1000;
debugLog('Time Delta: ', timeDelta) debugLog('Time Delta: ', timeDelta)
updateTimer(); updateTimer();
BroadcastSlider.setAuthors(padContents.getActiveAuthors().map(function(name)
var authors = map(padContents.getActiveAuthors(), function(name)
{ {
return authorData[name]; return authorData[name];
})); });
BroadcastSlider.setAuthors(authors);
} }
function updateTimer() function updateTimer()
@ -411,13 +383,14 @@ function loadBroadcastJS()
changesetLoader.queueUp(start, 1, update); changesetLoader.queueUp(start, 1, update);
} }
BroadcastSlider.setAuthors(padContents.getActiveAuthors().map(function(name)
{ var authors = map(padContents.getActiveAuthors(), function(name){
return authorData[name]; return authorData[name];
})); });
BroadcastSlider.setAuthors(authors);
} }
global.changesetLoader = { changesetLoader = {
running: false, running: false,
resolved: [], resolved: [],
requestQueue1: [], requestQueue1: [],
@ -553,10 +526,12 @@ function loadBroadcastJS()
var authorMap = {}; var authorMap = {};
authorMap[obj.author] = obj.data; authorMap[obj.author] = obj.data;
receiveAuthorData(authorMap); receiveAuthorData(authorMap);
BroadcastSlider.setAuthors(padContents.getActiveAuthors().map(function(name)
{ var authors = map(padContents.getActiveAuthors(),function(name) {
return authorData[name]; return authorData[name];
})); });
BroadcastSlider.setAuthors(authors);
} }
else if (obj['type'] == "NEW_SAVEDREV") else if (obj['type'] == "NEW_SAVEDREV")
{ {
@ -608,53 +583,6 @@ function loadBroadcastJS()
})); }));
} }
/*function setUpSocket()
{
// required for Comet
if ((!$.browser.msie) && (!($.browser.mozilla && $.browser.version.indexOf("1.8.") == 0)))
{
document.domain = document.domain; // for comet
}
var success = false;
callCatchingErrors("setUpSocket", function ()
{
appLevelDisconnectReason = null;
socketId = String(Math.floor(Math.random() * 1e12));
socket = new WebSocket(socketId);
socket.onmessage = wrapRecordingErrors("socket.onmessage", handleMessageFromServer);
socket.onclosed = wrapRecordingErrors("socket.onclosed", handleSocketClosed);
socket.onopen = wrapRecordingErrors("socket.onopen", function ()
{
setChannelState("CONNECTED");
var msg = {
type: "CLIENT_READY",
roomType: 'padview',
roomName: 'padview/' + clientVars.viewId,
data: {
lastRev: clientVars.revNum,
userInfo: {
userId: userId
}
}
};
sendMessage(msg);
});
// socket.onhiccup = wrapRecordingErrors("socket.onhiccup", handleCometHiccup);
// socket.onlogmessage = function(x) {debugLog(x); };
socket.connect();
success = true;
});
if (success)
{
//initialStartConnectTime = +new Date();
}
else
{
abandonConnection("initsocketfail");
}
}*/
function setChannelState(newChannelState, moreInfo) function setChannelState(newChannelState, moreInfo)
{ {
@ -683,7 +611,7 @@ function loadBroadcastJS()
window.onload = function () window.onload = function ()
{ {
window['isloaded'] = true; window['isloaded'] = true;
window['onloadFuncts'].forEach(function (funct) forEach(window['onloadFuncts'],function (funct)
{ {
funct(); funct();
}); });
@ -750,11 +678,17 @@ function loadBroadcastJS()
var bgcolor = typeof data.colorId == "number" ? clientVars.colorPalette[data.colorId] : data.colorId; var bgcolor = typeof data.colorId == "number" ? clientVars.colorPalette[data.colorId] : data.colorId;
if (bgcolor && dynamicCSS) if (bgcolor && dynamicCSS)
{ {
dynamicCSS.selectorStyle('.' + linestylefilter.getAuthorClassName(author)).backgroundColor = bgcolor; var selector = dynamicCSS.selectorStyle('.' + linestylefilter.getAuthorClassName(author));
selector.backgroundColor = bgcolor
selector.color = (colorutils.luminosity(colorutils.css2triple(bgcolor)) < 0.5) ? '#ffffff' : '#000000'; //see ace2_inner.js for the other part
} }
authorData[author] = data; authorData[author] = data;
} }
} }
receiveAuthorData(clientVars.historicalAuthorData); receiveAuthorData(clientVars.historicalAuthorData);
return changesetLoader;
} }
exports.loadBroadcastJS = loadBroadcastJS;

View file

@ -22,7 +22,6 @@
// revision info is a skip list whos entries represent a particular revision // revision info is a skip list whos entries represent a particular revision
// of the document. These revisions are connected together by various // of the document. These revisions are connected together by various
// changesets, or deltas, between any two revisions. // changesets, or deltas, between any two revisions.
var global = this;
function loadBroadcastRevisionsJS() function loadBroadcastRevisionsJS()
{ {
@ -125,3 +124,5 @@ function loadBroadcastRevisionsJS()
}; };
} }
} }
exports.loadBroadcastRevisionsJS = loadBroadcastRevisionsJS;

View file

@ -19,10 +19,12 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
var global = this;
function loadBroadcastSliderJS() // These parameters were global, now they are injected. A reference to the
// Timeslider controller would probably be more appropriate.
function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded)
{ {
var BroadcastSlider;
(function() (function()
{ // wrap this code in its own namespace { // wrap this code in its own namespace
@ -203,7 +205,7 @@ function loadBroadcastSliderJS()
} }
} }
global.BroadcastSlider = { BroadcastSlider = {
onSlider: onSlider, onSlider: onSlider,
getSliderPosition: getSliderPosition, getSliderPosition: getSliderPosition,
setSliderPosition: setSliderPosition, setSliderPosition: setSliderPosition,
@ -495,4 +497,8 @@ function loadBroadcastSliderJS()
{ {
$("#viewlatest").html(loc == BroadcastSlider.getSliderLength() ? "Viewing latest content" : "View latest content"); $("#viewlatest").html(loc == BroadcastSlider.getSliderLength() ? "Viewing latest content" : "View latest content");
}) })
return BroadcastSlider;
} }
exports.loadBroadcastSliderJS = loadBroadcastSliderJS;

View file

@ -20,6 +20,8 @@
* limitations under the License. * limitations under the License.
*/ */
var AttribPool = require('/AttributePoolFactory').createAttributePool;
var Changeset = require('/Changeset');
function makeChangesetTracker(scheduler, apool, aceCallbacksProvider) function makeChangesetTracker(scheduler, apool, aceCallbacksProvider)
{ {
@ -207,3 +209,5 @@ function makeChangesetTracker(scheduler, apool, aceCallbacksProvider)
}; };
} }
exports.makeChangesetTracker = makeChangesetTracker;

View file

@ -20,62 +20,45 @@
* limitations under the License. * limitations under the License.
*/ */
var padutils = require('/pad_utils').padutils;
var padcookie = require('/pad_cookie').padcookie;
var chat = (function() var chat = (function()
{ {
var ua = navigator.userAgent.toLowerCase(); var isStuck = false;
var isAndroid = ua.indexOf("android") > -1;
var isMobileSafari = ua.indexOf("mobile") > -1;
var bottomMargin = "0px";
var sDuration = 500;
var hDuration = 750;
var chatMentions = 0; var chatMentions = 0;
var title = document.title; var title = document.title;
if (isAndroid || isMobileSafari){
sDuration = 0;
hDuration = 0;
}
var self = { var self = {
show: function () show: function ()
{ {
$("#chaticon").hide("slide", { $("#chaticon").hide();
direction: "down" $("#chatbox").show();
}, hDuration, function ()
{
$("#chatbox").show("slide", {
direction: "down"
}, sDuration, self.scrollDown);
$("#chatbox").resizable(
{
handles: 'nw',
minHeight: 40,
minWidth: 80,
start: function (event, ui)
{
$("#focusprotector").show();
},
stop: function (event, ui)
{
$("#focusprotector").hide();
if(isAndroid || isMobileSafari)
bottommargin = "32px";
$("#chatbox").css({right: "20px", bottom: bottomMargin, left: "", top: ""});
self.scrollDown(); self.scrollDown();
}
});
});
chatMentions = 0; chatMentions = 0;
document.title = title; document.title = title;
}, },
stickToScreen: function(fromInitialCall) // Make chat stick to right hand side of screen
{
chat.show();
if(!isStuck || fromInitialCall) { // Stick it to
padcookie.setPref("chatAlwaysVisible", true);
$('#chatbox').addClass("stickyChat");
$('#chattext').css({"top":"0px"});
$('#editorcontainer').css({"right":"192px", "width":"auto"});
isStuck = true;
} else { // Unstick it
padcookie.setPref("chatAlwaysVisible", false);
$('#chatbox').removeClass("stickyChat");
$('#chattext').css({"top":"25px"});
$('#editorcontainer').css({"right":"0px", "width":"100%"});
isStuck = false;
}
},
hide: function () hide: function ()
{ {
$("#chatcounter").text("0"); $("#chatcounter").text("0");
$("#chatbox").hide("slide", { direction: "down" }, sDuration, function() $("#chaticon").show();
{ $("#chatbox").hide();
$("#chaticon").show("slide", { direction: "down" }, hDuration);
});
}, },
scrollDown: function() scrollDown: function()
{ {
@ -85,13 +68,13 @@ var chat = (function()
send: function() send: function()
{ {
var text = $("#chatinput").val(); var text = $("#chatinput").val();
pad.collabClient.sendMessage({"type": "CHAT_MESSAGE", "text": text}); this._pad.collabClient.sendMessage({"type": "CHAT_MESSAGE", "text": text});
$("#chatinput").val(""); $("#chatinput").val("");
}, },
addMessage: function(msg, increment) addMessage: function(msg, increment)
{ {
//correct the time //correct the time
msg.time += pad.clientTimeOffset; msg.time += this._pad.clientTimeOffset;
//create the time string //create the time string
var minutes = "" + new Date(msg.time).getMinutes(); var minutes = "" + new Date(msg.time).getMinutes();
@ -109,7 +92,7 @@ var chat = (function()
return 'z' + c.charCodeAt(0) + 'z'; return 'z' + c.charCodeAt(0) + 'z';
}); });
var text = padutils.escapeHtmlWithClickableLinks(padutils.escapeHtml(msg.text), "_blank"); var text = padutils.escapeHtmlWithClickableLinks(msg.text, "_blank");
/* Performs an action if your name is mentioned */ /* Performs an action if your name is mentioned */
var myName = $('#myusernameedit').val(); var myName = $('#myusernameedit').val();
@ -123,7 +106,7 @@ var chat = (function()
var authorName = msg.userName == null ? "unnamed" : padutils.escapeHtml(msg.userName); var authorName = msg.userName == null ? "unnamed" : padutils.escapeHtml(msg.userName);
var html = "<p class='" + authorClass + "'><b>" + authorName + ":</b><span class='time'>" + timeStr + "</span> " + text + "</p>"; var html = "<p class='" + authorClass + "'><b>" + authorName + ":</b><span class='time " + authorClass + "'>" + timeStr + "</span> " + text + "</p>";
$("#chattext").append(html); $("#chattext").append(html);
//should we increment the counter?? //should we increment the counter??
@ -138,23 +121,22 @@ var chat = (function()
if (chatMentions == 0){ if (chatMentions == 0){
title = document.title; title = document.title;
} }
$('#chatthrob').html("<b>"+authorName+"</b>" + ": " + text); $('#chatthrob').html("<b>"+authorName+"</b>" + ": " + text).show().delay(4000).hide(400);
$('#chatthrob').effect("pulsate", {times:1,mode:"hide"},4000);
chatMentions++; chatMentions++;
document.title = "("+chatMentions+") " + title; document.title = "("+chatMentions+") " + title;
} }
else else
{ {
$('#chatthrob').html("<b>"+authorName+"</b>" + ": " + text); $('#chatthrob').html("<b>"+authorName+"</b>" + ": " + text).show().delay(2000).hide(400);
$('#chatthrob').effect("pulsate", {times:1,mode:"hide"},2000);
} }
} }
self.scrollDown(); self.scrollDown();
}, },
init: function() init: function(pad)
{ {
this._pad = pad;
$("#chatinput").keypress(function(evt) $("#chatinput").keypress(function(evt)
{ {
//if the user typed enter, fire the send //if the user typed enter, fire the send
@ -175,3 +157,6 @@ var chat = (function()
return self; return self;
}()); }());
exports.chat = chat;

View file

@ -20,17 +20,22 @@
* limitations under the License. * limitations under the License.
*/ */
$(window).bind("load", function() var chat = require('/chat').chat;
{
getCollabClient.windowLoaded = true; // Dependency fill on init. This exists for `pad.socket` only.
}); // TODO: bind directly to the socket.
var pad = undefined;
function getSocket() {
return pad && pad.socket;
}
/** Call this when the document is ready, and a new Ace2Editor() has been created and inited. /** Call this when the document is ready, and a new Ace2Editor() has been created and inited.
ACE's ready callback does not need to have fired yet. ACE's ready callback does not need to have fired yet.
"serverVars" are from calling doc.getCollabClientVars() on the server. */ "serverVars" are from calling doc.getCollabClientVars() on the server. */
function getCollabClient(ace2editor, serverVars, initialUserInfo, options) function getCollabClient(ace2editor, serverVars, initialUserInfo, options, _pad)
{ {
var editor = ace2editor; var editor = ace2editor;
pad = _pad; // Inject pad to avoid a circular dependency.
var rev = serverVars.rev; var rev = serverVars.rev;
var padId = serverVars.padId; var padId = serverVars.padId;
@ -79,13 +84,6 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options)
{} {}
}; };
$(window).bind("unload", function()
{
if (socket)
{
setChannelState("DISCONNECTED", "unload");
}
});
if ($.browser.mozilla) if ($.browser.mozilla)
{ {
// Prevent "escape" from taking effect and canceling a comet connection; // Prevent "escape" from taking effect and canceling a comet connection;
@ -111,7 +109,7 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options)
function handleUserChanges() function handleUserChanges()
{ {
if ((!socket) || channelState == "CONNECTING") if ((!getSocket()) || channelState == "CONNECTING")
{ {
if (channelState == "CONNECTING" && (((+new Date()) - initialStartConnectTime) > 20000)) if (channelState == "CONNECTING" && (((+new Date()) - initialStartConnectTime) > 20000))
{ {
@ -258,19 +256,6 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options)
}*/ }*/
} }
function setUpSocketWhenWindowLoaded()
{
if (getCollabClient.windowLoaded)
{
setUpSocket();
}
else
{
setTimeout(setUpSocketWhenWindowLoaded, 200);
}
}
setTimeout(setUpSocketWhenWindowLoaded, 0);
var hiccupCount = 0; var hiccupCount = 0;
function handleCometHiccup(params) function handleCometHiccup(params)
@ -295,7 +280,7 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options)
function sendMessage(msg) function sendMessage(msg)
{ {
socket.json.send( getSocket().json.send(
{ {
type: "COLLABROOM", type: "COLLABROOM",
component: "pad", component: "pad",
@ -337,7 +322,7 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options)
{ {
if (window.console) console.log(evt); if (window.console) console.log(evt);
if (!socket) return; if (!getSocket()) return;
if (!evt.data) return; if (!evt.data) return;
var wrapper = evt; var wrapper = evt;
if (wrapper.type != "COLLABROOM") return; if (wrapper.type != "COLLABROOM") return;
@ -442,7 +427,7 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options)
userInfo.userId = userId; userInfo.userId = userId;
userSet[userId] = userInfo; userSet[userId] = userInfo;
tellAceActiveAuthorInfo(userInfo); tellAceActiveAuthorInfo(userInfo);
if (!socket) return; if (!getSocket()) return;
sendMessage( sendMessage(
{ {
type: "USERINFO_UPDATE", type: "USERINFO_UPDATE",
@ -644,8 +629,7 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options)
}, 0); }, 0);
} }
var self; var self = {
return (self = {
setOnUserJoin: function(cb) setOnUserJoin: function(cb)
{ {
callbacks.onUserJoin = cb; callbacks.onUserJoin = cb;
@ -688,7 +672,10 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options)
callWhenNotCommitting: callWhenNotCommitting, callWhenNotCommitting: callWhenNotCommitting,
addHistoricalAuthors: tellAceAboutHistoricalAuthors, addHistoricalAuthors: tellAceAboutHistoricalAuthors,
setChannelState: setChannelState setChannelState: setChannelState
}); };
$(document).ready(setUpSocket);
return self;
} }
function selectElementContents(elem) function selectElementContents(elem)
@ -714,3 +701,6 @@ function selectElementContents(elem)
} }
} }
} }
exports.getCollabClient = getCollabClient;
exports.selectElementContents = selectElementContents;

View file

@ -119,3 +119,20 @@ colorutils.blend = function(c1, c2, t)
{ {
return [colorutils.scale(t, c1[0], c2[0]), colorutils.scale(t, c1[1], c2[1]), colorutils.scale(t, c1[2], c2[2])]; return [colorutils.scale(t, c1[0], c2[0]), colorutils.scale(t, c1[1], c2[1]), colorutils.scale(t, c1[2], c2[2])];
} }
colorutils.invert = function(c)
{
return [1 - c[0], 1 - c[1], 1- c[2]];
}
colorutils.complementary = function(c)
{
var inv = colorutils.invert(c);
return [
(inv[0] >= c[0]) ? Math.min(inv[0] * 1.30, 1) : (c[0] * 0.30),
(inv[1] >= c[1]) ? Math.min(inv[1] * 1.59, 1) : (c[1] * 0.59),
(inv[2] >= c[2]) ? Math.min(inv[2] * 1.11, 1) : (c[2] * 0.11)
];
}
exports.colorutils = colorutils;

View file

@ -25,6 +25,9 @@
var _MAX_LIST_LEVEL = 8; var _MAX_LIST_LEVEL = 8;
var Changeset = require('/Changeset');
var plugins = require('/plugins').plugins;
function sanitizeUnicode(s) function sanitizeUnicode(s)
{ {
return s.replace(/[\uffff\ufffe\ufeff\ufdd0-\ufdef\ud800-\udfff]/g, '?'); return s.replace(/[\uffff\ufffe\ufeff\ufdd0-\ufdef\ud800-\udfff]/g, '?');
@ -34,15 +37,7 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class
{ {
browser = browser || {}; browser = browser || {};
var plugins_; var plugins_ = plugins;
if (typeof(plugins) != 'undefined')
{
plugins_ = plugins;
}
else
{
plugins_ = parent.parent.plugins;
}
var dom = domInterface || { var dom = domInterface || {
isNodeText: function(n) isNodeText: function(n)
@ -476,7 +471,7 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class
{ {
cc.doAttrib(state, "strikethrough"); cc.doAttrib(state, "strikethrough");
} }
if (tname == "ul") if (tname == "ul" || tname == "ol")
{ {
var type; var type;
var rr = cls && /(?:^| )list-([a-z]+[12345678])\b/.exec(cls); var rr = cls && /(?:^| )list-([a-z]+[12345678])\b/.exec(cls);
@ -692,3 +687,6 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class
return cc; return cc;
} }
exports.sanitizeUnicode = sanitizeUnicode;
exports.makeContentCollector = makeContentCollector;

View file

@ -118,3 +118,5 @@ function makeCSSManager(emptyStylesheetTitle, top)
} }
}; };
} }
exports.makeCSSManager = makeCSSManager;

View file

@ -1,116 +0,0 @@
/**
* This code is mostly from the old Etherpad. Please help us to comment this code.
* This helps other people to understand this code better and helps them to improve it.
* TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
*/
// DO NOT EDIT THIS FILE, edit infrastructure/ace/www/cssmanager.js
/**
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
function makeCSSManager(emptyStylesheetTitle)
{
function getSheetByTitle(title)
{
var allSheets = document.styleSheets;
for (var i = 0; i < allSheets.length; i++)
{
var s = allSheets[i];
if (s.title == title)
{
return s;
}
}
return null;
}
/*function getSheetTagByTitle(title) {
var allStyleTags = document.getElementsByTagName("style");
for(var i=0;i<allStyleTags.length;i++) {
var t = allStyleTags[i];
if (t.title == title) {
return t;
}
}
return null;
}*/
var browserSheet = getSheetByTitle(emptyStylesheetTitle);
//var browserTag = getSheetTagByTitle(emptyStylesheetTitle);
function browserRules()
{
return (browserSheet.cssRules || browserSheet.rules);
}
function browserDeleteRule(i)
{
if (browserSheet.deleteRule) browserSheet.deleteRule(i);
else browserSheet.removeRule(i);
}
function browserInsertRule(i, selector)
{
if (browserSheet.insertRule) browserSheet.insertRule(selector + ' {}', i);
else browserSheet.addRule(selector, null, i);
}
var selectorList = [];
function indexOfSelector(selector)
{
for (var i = 0; i < selectorList.length; i++)
{
if (selectorList[i] == selector)
{
return i;
}
}
return -1;
}
function selectorStyle(selector)
{
var i = indexOfSelector(selector);
if (i < 0)
{
// add selector
browserInsertRule(0, selector);
selectorList.splice(0, 0, selector);
i = 0;
}
return browserRules().item(i).style;
}
function removeSelectorStyle(selector)
{
var i = indexOfSelector(selector);
if (i >= 0)
{
browserDeleteRule(i);
selectorList.splice(i, 1);
}
}
return {
selectorStyle: selectorStyle,
removeSelectorStyle: removeSelectorStyle,
info: function()
{
return selectorList.length + ":" + browserRules().length;
}
};
}

View file

@ -25,13 +25,15 @@
// requires: top // requires: top
// requires: plugins // requires: plugins
// requires: undefined // requires: undefined
var Security = require('/security');
var Ace2Common = require('/ace2_common');
var plugins = require('/plugins').plugins;
var map = Ace2Common.map;
var noop = Ace2Common.noop;
var identity = Ace2Common.identity;
var domline = {}; var domline = {};
domline.noop = function()
{};
domline.identity = function(x)
{
return x;
};
domline.addToLineClass = function(lineClass, cls) domline.addToLineClass = function(lineClass, cls)
{ {
@ -55,11 +57,11 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument)
{ {
var result = { var result = {
node: null, node: null,
appendSpan: domline.noop, appendSpan: noop,
prepareForAdd: domline.noop, prepareForAdd: noop,
notifyAdded: domline.noop, notifyAdded: noop,
clearSpans: domline.noop, clearSpans: noop,
finishUpdate: domline.noop, finishUpdate: noop,
lineMarker: 0 lineMarker: 0
}; };
@ -86,7 +88,7 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument)
{ {
return domline.processSpaces(s, doesWrap); return domline.processSpaces(s, doesWrap);
} }
var identity = domline.identity;
var perTextNodeProcess = (doesWrap ? identity : processSpaces); var perTextNodeProcess = (doesWrap ? identity : processSpaces);
var perHtmlLineProcess = (doesWrap ? processSpaces : identity); var perHtmlLineProcess = (doesWrap ? processSpaces : identity);
var lineClass = 'ace-line'; var lineClass = 'ace-line';
@ -95,14 +97,24 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument)
if (cls.indexOf('list') >= 0) if (cls.indexOf('list') >= 0)
{ {
var listType = /(?:^| )list:(\S+)/.exec(cls); var listType = /(?:^| )list:(\S+)/.exec(cls);
var start = /(?:^| )start:(\S+)/.exec(cls);
if (listType) if (listType)
{ {
listType = listType[1]; listType = listType[1];
start = start?'start="'+Security.escapeHTMLAttribute(start[1])+'"':'';
if (listType) if (listType)
{ {
preHtml = '<ul class="list-' + listType + '"><li>'; if(listType.indexOf("number") < 0)
{
preHtml = '<ul class="list-' + Security.escapeHTMLAttribute(listType) + '"><li>';
postHtml = '</li></ul>'; postHtml = '</li></ul>';
} }
else
{
preHtml = '<ol '+start+' class="list-' + Security.escapeHTMLAttribute(listType) + '"><li>';
postHtml = '</li></ol>';
}
}
result.lineMarker += txt.length; result.lineMarker += txt.length;
return; // don't append any text return; // don't append any text
} }
@ -130,20 +142,12 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument)
var extraOpenTags = ""; var extraOpenTags = "";
var extraCloseTags = ""; var extraCloseTags = "";
var plugins_; var plugins_ = plugins;
if (typeof(plugins) != 'undefined')
{
plugins_ = plugins;
}
else
{
plugins_ = parent.parent.plugins;
}
plugins_.callHook("aceCreateDomLine", { map(plugins_.callHook("aceCreateDomLine", {
domline: domline, domline: domline,
cls: cls cls: cls
}).map(function(modifier) }), function(modifier)
{ {
cls = modifier.cls; cls = modifier.cls;
extraOpenTags = extraOpenTags + modifier.extraOpenTags; extraOpenTags = extraOpenTags + modifier.extraOpenTags;
@ -162,7 +166,7 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument)
{ {
href = "http://"+href; href = "http://"+href;
} }
extraOpenTags = extraOpenTags + '<a href="' + href.replace(/\"/g, '&quot;') + '">'; extraOpenTags = extraOpenTags + '<a href="' + Security.escapeHTMLAttribute(href) + '">';
extraCloseTags = '</a>' + extraCloseTags; extraCloseTags = '</a>' + extraCloseTags;
} }
if (simpleTags) if (simpleTags)
@ -172,7 +176,7 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument)
simpleTags.reverse(); simpleTags.reverse();
extraCloseTags = '</' + simpleTags.join('></') + '>' + extraCloseTags; extraCloseTags = '</' + simpleTags.join('></') + '>' + extraCloseTags;
} }
html.push('<span class="', cls || '', '">', extraOpenTags, perTextNodeProcess(domline.escapeHTML(txt)), extraCloseTags, '</span>'); html.push('<span class="', Security.escapeHTMLAttribute(cls || ''), '">', extraOpenTags, perTextNodeProcess(Security.escapeHTML(txt)), extraCloseTags, '</span>');
} }
}; };
result.clearSpans = function() result.clearSpans = function()
@ -218,27 +222,6 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument)
return result; return result;
}; };
domline.escapeHTML = function(s)
{
var re = /[&<>'"]/g;
/']/; // stupid indentation thing
if (!re.MAP)
{
// persisted across function calls!
re.MAP = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&#34;',
"'": '&#39;'
};
}
return s.replace(re, function(c)
{
return re.MAP[c];
});
};
domline.processSpaces = function(s, doesWrap) domline.processSpaces = function(s, doesWrap)
{ {
if (s.indexOf("<") < 0 && !doesWrap) if (s.indexOf("<") < 0 && !doesWrap)
@ -300,3 +283,5 @@ domline.processSpaces = function(s, doesWrap)
} }
return parts.join(''); return parts.join('');
}; };
exports.domline = domline;

View file

@ -1,298 +0,0 @@
/**
* This code is mostly from the old Etherpad. Please help us to comment this code.
* This helps other people to understand this code better and helps them to improve it.
* TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
*/
// DO NOT EDIT THIS FILE, edit infrastructure/ace/www/domline.js
// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.domline
/**
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// requires: top
// requires: plugins
// requires: undefined
var domline = {};
domline.noop = function()
{};
domline.identity = function(x)
{
return x;
};
domline.addToLineClass = function(lineClass, cls)
{
// an "empty span" at any point can be used to add classes to
// the line, using line:className. otherwise, we ignore
// the span.
cls.replace(/\S+/g, function(c)
{
if (c.indexOf("line:") == 0)
{
// add class to line
lineClass = (lineClass ? lineClass + ' ' : '') + c.substring(5);
}
});
return lineClass;
}
// if "document" is falsy we don't create a DOM node, just
// an object with innerHTML and className
domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument)
{
var result = {
node: null,
appendSpan: domline.noop,
prepareForAdd: domline.noop,
notifyAdded: domline.noop,
clearSpans: domline.noop,
finishUpdate: domline.noop,
lineMarker: 0
};
var browser = (optBrowser || {});
var document = optDocument;
if (document)
{
result.node = document.createElement("div");
}
else
{
result.node = {
innerHTML: '',
className: ''
};
}
var html = [];
var preHtml, postHtml;
var curHTML = null;
function processSpaces(s)
{
return domline.processSpaces(s, doesWrap);
}
var identity = domline.identity;
var perTextNodeProcess = (doesWrap ? identity : processSpaces);
var perHtmlLineProcess = (doesWrap ? processSpaces : identity);
var lineClass = 'ace-line';
result.appendSpan = function(txt, cls)
{
if (cls.indexOf('list') >= 0)
{
var listType = /(?:^| )list:(\S+)/.exec(cls);
if (listType)
{
listType = listType[1];
if (listType)
{
preHtml = '<ul class="list-' + listType + '"><li>';
postHtml = '</li></ul>';
}
result.lineMarker += txt.length;
return; // don't append any text
}
}
var href = null;
var simpleTags = null;
if (cls.indexOf('url') >= 0)
{
cls = cls.replace(/(^| )url:(\S+)/g, function(x0, space, url)
{
href = url;
return space + "url";
});
}
if (cls.indexOf('tag') >= 0)
{
cls = cls.replace(/(^| )tag:(\S+)/g, function(x0, space, tag)
{
if (!simpleTags) simpleTags = [];
simpleTags.push(tag.toLowerCase());
return space + tag;
});
}
var extraOpenTags = "";
var extraCloseTags = "";
var plugins_;
if (typeof(plugins) != 'undefined')
{
plugins_ = plugins;
}
else
{
plugins_ = parent.parent.plugins;
}
plugins_.callHook("aceCreateDomLine", {
domline: domline,
cls: cls,
document: document
}).map(function(modifier)
{
cls = modifier.cls;
extraOpenTags = extraOpenTags + modifier.extraOpenTags;
extraCloseTags = modifier.extraCloseTags + extraCloseTags;
});
if ((!txt) && cls)
{
lineClass = domline.addToLineClass(lineClass, cls);
}
else if (txt)
{
if (href)
{
extraOpenTags = extraOpenTags + '<a href="' + href.replace(/\"/g, '&quot;') + '">';
extraCloseTags = '</a>' + extraCloseTags;
}
if (simpleTags)
{
simpleTags.sort();
extraOpenTags = extraOpenTags + '<' + simpleTags.join('><') + '>';
simpleTags.reverse();
extraCloseTags = '</' + simpleTags.join('></') + '>' + extraCloseTags;
}
html.push('<span class="', cls || '', '">', extraOpenTags, perTextNodeProcess(domline.escapeHTML(txt)), extraCloseTags, '</span>');
}
};
result.clearSpans = function()
{
html = [];
lineClass = ''; // non-null to cause update
result.lineMarker = 0;
};
function writeHTML()
{
var newHTML = perHtmlLineProcess(html.join(''));
if (!newHTML)
{
if ((!document) || (!optBrowser))
{
newHTML += '&nbsp;';
}
else if (!browser.msie)
{
newHTML += '<br/>';
}
}
if (nonEmpty)
{
newHTML = (preHtml || '') + newHTML + (postHtml || '');
}
html = preHtml = postHtml = null; // free memory
if (newHTML !== curHTML)
{
curHTML = newHTML;
result.node.innerHTML = curHTML;
}
if (lineClass !== null) result.node.className = lineClass;
}
result.prepareForAdd = writeHTML;
result.finishUpdate = writeHTML;
result.getInnerHTML = function()
{
return curHTML || '';
};
return result;
};
domline.escapeHTML = function(s)
{
var re = /[&<>'"]/g;
/']/; // stupid indentation thing
if (!re.MAP)
{
// persisted across function calls!
re.MAP = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&#34;',
"'": '&#39;'
};
}
return s.replace(re, function(c)
{
return re.MAP[c];
});
};
domline.processSpaces = function(s, doesWrap)
{
if (s.indexOf("<") < 0 && !doesWrap)
{
// short-cut
return s.replace(/ /g, '&nbsp;');
}
var parts = [];
s.replace(/<[^>]*>?| |[^ <]+/g, function(m)
{
parts.push(m);
});
if (doesWrap)
{
var endOfLine = true;
var beforeSpace = false;
// last space in a run is normal, others are nbsp,
// end of line is nbsp
for (var i = parts.length - 1; i >= 0; i--)
{
var p = parts[i];
if (p == " ")
{
if (endOfLine || beforeSpace) parts[i] = '&nbsp;';
endOfLine = false;
beforeSpace = true;
}
else if (p.charAt(0) != "<")
{
endOfLine = false;
beforeSpace = false;
}
}
// beginning of line is nbsp
for (var i = 0; i < parts.length; i++)
{
var p = parts[i];
if (p == " ")
{
parts[i] = '&nbsp;';
break;
}
else if (p.charAt(0) != "<")
{
break;
}
}
}
else
{
for (var i = 0; i < parts.length; i++)
{
var p = parts[i];
if (p == " ")
{
parts[i] = '&nbsp;';
}
}
}
return parts.join('');
};

View file

@ -193,3 +193,5 @@ function makeResizableHPane(left, sep, right, minLeft, minRight, sepWidth, sepOf
} }
}); });
} }
exports.makeDraggable = makeDraggable;

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

157
static/js/jquery-ui.js vendored
View file

@ -1,157 +0,0 @@
/*!
* jQuery UI 1.8.14
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI
*/
(function(c,j){function k(a,b){var d=a.nodeName.toLowerCase();if("area"===d){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&l(a)}return(/input|select|textarea|button|object/.test(d)?!a.disabled:"a"==d?a.href||b:b)&&l(a)}function l(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.14",
keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=this;setTimeout(function(){c(d).focus();
b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this,"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,
"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position");if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"),10);if(!isNaN(b)&&b!==0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind((c.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",
function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,m,n){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(m)g-=parseFloat(c.curCSS(f,"border"+this+"Width",true))||0;if(n)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight,outerWidth:c.fn.outerWidth,
outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c(this).css(h,d(this,f)+"px")})};c.fn["outer"+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c(this).css(h,d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){return k(a,!isNaN(c.attr(a,"tabindex")))},tabbable:function(a){var b=c.attr(a,"tabindex"),d=isNaN(b);
return(d||b>=0)&&k(a,!d)}});c(function(){var a=document.body,b=a.appendChild(b=document.createElement("div"));c.extend(b.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.offsetHeight===100;c.support.selectstart="onselectstart"in b;a.removeChild(b).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&a.element[0].parentNode)for(var e=
0;e<b.length;e++)a.options[b[e][0]]&&b[e][1].apply(a.element,d)}},contains:function(a,b){return document.compareDocumentPosition?a.compareDocumentPosition(b)&16:a!==b&&a.contains(b)},hasScroll:function(a,b){if(c(a).css("overflow")==="hidden")return false;b=b&&b==="left"?"scrollLeft":"scrollTop";var d=false;if(a[b]>0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a<b+d},isOver:function(a,b,d,e,h,i){return c.ui.isOverAxis(a,d,h)&&c.ui.isOverAxis(b,e,i)}})}})(jQuery);
;/*!
* jQuery UI Widget 1.8.14
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Widget
*/
(function(b,j){if(b.cleanData){var k=b.cleanData;b.cleanData=function(a){for(var c=0,d;(d=a[c])!=null;c++)b(d).triggerHandler("remove");k(a)}}else{var l=b.fn.remove;b.fn.remove=function(a,c){return this.each(function(){if(!c)if(!a||b.filter(a,[this]).length)b("*",this).add([this]).each(function(){b(this).triggerHandler("remove")});return l.call(b(this),a,c)})}}b.widget=function(a,c,d){var e=a.split(".")[0],f;a=a.split(".")[1];f=e+"-"+a;if(!d){d=c;c=b.Widget}b.expr[":"][f]=function(h){return!!b.data(h,
a)};b[e]=b[e]||{};b[e][a]=function(h,g){arguments.length&&this._createWidget(h,g)};c=new c;c.options=b.extend(true,{},c.options);b[e][a].prototype=b.extend(true,c,{namespace:e,widgetName:a,widgetEventPrefix:b[e][a].prototype.widgetEventPrefix||a,widgetBaseClass:f},d);b.widget.bridge(a,b[e][a])};b.widget.bridge=function(a,c){b.fn[a]=function(d){var e=typeof d==="string",f=Array.prototype.slice.call(arguments,1),h=this;d=!e&&f.length?b.extend.apply(null,[true,d].concat(f)):d;if(e&&d.charAt(0)==="_")return h;
e?this.each(function(){var g=b.data(this,a),i=g&&b.isFunction(g[d])?g[d].apply(g,f):g;if(i!==g&&i!==j){h=i;return false}}):this.each(function(){var g=b.data(this,a);g?g.option(d||{})._init():b.data(this,a,new c(d,this))});return h}};b.Widget=function(a,c){arguments.length&&this._createWidget(a,c)};b.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:false},_createWidget:function(a,c){b.data(c,this.widgetName,this);this.element=b(c);this.options=b.extend(true,{},this.options,
this._getCreateOptions(),a);var d=this;this.element.bind("remove."+this.widgetName,function(){d.destroy()});this._create();this._trigger("create");this._init()},_getCreateOptions:function(){return b.metadata&&b.metadata.get(this.element[0])[this.widgetName]},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled ui-state-disabled")},
widget:function(){return this.element},option:function(a,c){var d=a;if(arguments.length===0)return b.extend({},this.options);if(typeof a==="string"){if(c===j)return this.options[a];d={};d[a]=c}this._setOptions(d);return this},_setOptions:function(a){var c=this;b.each(a,function(d,e){c._setOption(d,e)});return this},_setOption:function(a,c){this.options[a]=c;if(a==="disabled")this.widget()[c?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",c);return this},
enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(a,c,d){var e=this.options[a];c=b.Event(c);c.type=(a===this.widgetEventPrefix?a:this.widgetEventPrefix+a).toLowerCase();d=d||{};if(c.originalEvent){a=b.event.props.length;for(var f;a;){f=b.event.props[--a];c[f]=c.originalEvent[f]}}this.element.trigger(c,d);return!(b.isFunction(e)&&e.call(this.element[0],c,d)===false||c.isDefaultPrevented())}}})(jQuery);
;/*!
* jQuery UI Mouse 1.8.14
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Mouse
*
* Depends:
* jquery.ui.widget.js
*/
(function(b){var d=false;b(document).mousedown(function(){d=false});b.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var a=this;this.element.bind("mousedown."+this.widgetName,function(c){return a._mouseDown(c)}).bind("click."+this.widgetName,function(c){if(true===b.data(c.target,a.widgetName+".preventClickEvent")){b.removeData(c.target,a.widgetName+".preventClickEvent");c.stopImmediatePropagation();return false}});this.started=false},_mouseDestroy:function(){this.element.unbind("."+
this.widgetName)},_mouseDown:function(a){if(!d){this._mouseStarted&&this._mouseUp(a);this._mouseDownEvent=a;var c=this,f=a.which==1,g=typeof this.options.cancel=="string"?b(a.target).closest(this.options.cancel).length:false;if(!f||g||!this._mouseCapture(a))return true;this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet)this._mouseDelayTimer=setTimeout(function(){c.mouseDelayMet=true},this.options.delay);if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a)){this._mouseStarted=this._mouseStart(a)!==
false;if(!this._mouseStarted){a.preventDefault();return true}}true===b.data(a.target,this.widgetName+".preventClickEvent")&&b.removeData(a.target,this.widgetName+".preventClickEvent");this._mouseMoveDelegate=function(e){return c._mouseMove(e)};this._mouseUpDelegate=function(e){return c._mouseUp(e)};b(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);a.preventDefault();return d=true}},_mouseMove:function(a){if(b.browser.msie&&
!(document.documentMode>=9)&&!a.button)return this._mouseUp(a);if(this._mouseStarted){this._mouseDrag(a);return a.preventDefault()}if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a))(this._mouseStarted=this._mouseStart(this._mouseDownEvent,a)!==false)?this._mouseDrag(a):this._mouseUp(a);return!this._mouseStarted},_mouseUp:function(a){b(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=
false;a.target==this._mouseDownEvent.target&&b.data(a.target,this.widgetName+".preventClickEvent",true);this._mouseStop(a)}return false},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery);
;/*
* jQuery UI Resizable 1.8.14
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Resizables
*
* Depends:
* jquery.ui.core.js
* jquery.ui.mouse.js
* jquery.ui.widget.js
*/
(function(e){e.widget("ui.resizable",e.ui.mouse,{widgetEventPrefix:"resize",options:{alsoResize:false,animate:false,animateDuration:"slow",animateEasing:"swing",aspectRatio:false,autoHide:false,containment:false,ghost:false,grid:false,handles:"e,s,se",helper:false,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:1E3},_create:function(){var b=this,a=this.options;this.element.addClass("ui-resizable");e.extend(this,{_aspectRatio:!!a.aspectRatio,aspectRatio:a.aspectRatio,originalElement:this.element,
_proportionallyResizeElements:[],_helper:a.helper||a.ghost||a.animate?a.helper||"ui-resizable-helper":null});if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)){/relative/.test(this.element.css("position"))&&e.browser.opera&&this.element.css({position:"relative",top:"auto",left:"auto"});this.element.wrap(e('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),
top:this.element.css("top"),left:this.element.css("left")}));this.element=this.element.parent().data("resizable",this.element.data("resizable"));this.elementIsWrapper=true;this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")});this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});this.originalResizeStyle=
this.originalElement.css("resize");this.originalElement.css("resize","none");this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"}));this.originalElement.css({margin:this.originalElement.css("margin")});this._proportionallyResize()}this.handles=a.handles||(!e(".ui-resizable-handle",this.element).length?"e,s,se":{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",
nw:".ui-resizable-nw"});if(this.handles.constructor==String){if(this.handles=="all")this.handles="n,e,s,w,se,sw,ne,nw";var c=this.handles.split(",");this.handles={};for(var d=0;d<c.length;d++){var f=e.trim(c[d]),g=e('<div class="ui-resizable-handle '+("ui-resizable-"+f)+'"></div>');/sw|se|ne|nw/.test(f)&&g.css({zIndex:++a.zIndex});"se"==f&&g.addClass("ui-icon ui-icon-gripsmall-diagonal-se");this.handles[f]=".ui-resizable-"+f;this.element.append(g)}}this._renderAxis=function(h){h=h||this.element;for(var i in this.handles){if(this.handles[i].constructor==
String)this.handles[i]=e(this.handles[i],this.element).show();if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var j=e(this.handles[i],this.element),l=0;l=/sw|ne|nw|se|n|s/.test(i)?j.outerHeight():j.outerWidth();j=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join("");h.css(j,l);this._proportionallyResize()}e(this.handles[i])}};this._renderAxis(this.element);this._handles=e(".ui-resizable-handle",this.element).disableSelection();
this._handles.mouseover(function(){if(!b.resizing){if(this.className)var h=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);b.axis=h&&h[1]?h[1]:"se"}});if(a.autoHide){this._handles.hide();e(this.element).addClass("ui-resizable-autohide").hover(function(){if(!a.disabled){e(this).removeClass("ui-resizable-autohide");b._handles.show()}},function(){if(!a.disabled)if(!b.resizing){e(this).addClass("ui-resizable-autohide");b._handles.hide()}})}this._mouseInit()},destroy:function(){this._mouseDestroy();
var b=function(c){e(c).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};if(this.elementIsWrapper){b(this.element);var a=this.element;a.after(this.originalElement.css({position:a.css("position"),width:a.outerWidth(),height:a.outerHeight(),top:a.css("top"),left:a.css("left")})).remove()}this.originalElement.css("resize",this.originalResizeStyle);b(this.originalElement);return this},_mouseCapture:function(b){var a=
false;for(var c in this.handles)if(e(this.handles[c])[0]==b.target)a=true;return!this.options.disabled&&a},_mouseStart:function(b){var a=this.options,c=this.element.position(),d=this.element;this.resizing=true;this.documentScroll={top:e(document).scrollTop(),left:e(document).scrollLeft()};if(d.is(".ui-draggable")||/absolute/.test(d.css("position")))d.css({position:"absolute",top:c.top,left:c.left});e.browser.opera&&/relative/.test(d.css("position"))&&d.css({position:"relative",top:"auto",left:"auto"});
this._renderProxy();c=m(this.helper.css("left"));var f=m(this.helper.css("top"));if(a.containment){c+=e(a.containment).scrollLeft()||0;f+=e(a.containment).scrollTop()||0}this.offset=this.helper.offset();this.position={left:c,top:f};this.size=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalSize=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalPosition={left:c,top:f};this.sizeDiff=
{width:d.outerWidth()-d.width(),height:d.outerHeight()-d.height()};this.originalMousePosition={left:b.pageX,top:b.pageY};this.aspectRatio=typeof a.aspectRatio=="number"?a.aspectRatio:this.originalSize.width/this.originalSize.height||1;a=e(".ui-resizable-"+this.axis).css("cursor");e("body").css("cursor",a=="auto"?this.axis+"-resize":a);d.addClass("ui-resizable-resizing");this._propagate("start",b);return true},_mouseDrag:function(b){var a=this.helper,c=this.originalMousePosition,d=this._change[this.axis];
if(!d)return false;c=d.apply(this,[b,b.pageX-c.left||0,b.pageY-c.top||0]);this._updateVirtualBoundaries(b.shiftKey);if(this._aspectRatio||b.shiftKey)c=this._updateRatio(c,b);c=this._respectSize(c,b);this._propagate("resize",b);a.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"});!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize();this._updateCache(c);this._trigger("resize",b,this.ui());return false},
_mouseStop:function(b){this.resizing=false;var a=this.options,c=this;if(this._helper){var d=this._proportionallyResizeElements,f=d.length&&/textarea/i.test(d[0].nodeName);d=f&&e.ui.hasScroll(d[0],"left")?0:c.sizeDiff.height;f=f?0:c.sizeDiff.width;f={width:c.helper.width()-f,height:c.helper.height()-d};d=parseInt(c.element.css("left"),10)+(c.position.left-c.originalPosition.left)||null;var g=parseInt(c.element.css("top"),10)+(c.position.top-c.originalPosition.top)||null;a.animate||this.element.css(e.extend(f,
{top:g,left:d}));c.helper.height(c.size.height);c.helper.width(c.size.width);this._helper&&!a.animate&&this._proportionallyResize()}e("body").css("cursor","auto");this.element.removeClass("ui-resizable-resizing");this._propagate("stop",b);this._helper&&this.helper.remove();return false},_updateVirtualBoundaries:function(b){var a=this.options,c,d,f;a={minWidth:k(a.minWidth)?a.minWidth:0,maxWidth:k(a.maxWidth)?a.maxWidth:Infinity,minHeight:k(a.minHeight)?a.minHeight:0,maxHeight:k(a.maxHeight)?a.maxHeight:
Infinity};if(this._aspectRatio||b){b=a.minHeight*this.aspectRatio;d=a.minWidth/this.aspectRatio;c=a.maxHeight*this.aspectRatio;f=a.maxWidth/this.aspectRatio;if(b>a.minWidth)a.minWidth=b;if(d>a.minHeight)a.minHeight=d;if(c<a.maxWidth)a.maxWidth=c;if(f<a.maxHeight)a.maxHeight=f}this._vBoundaries=a},_updateCache:function(b){this.offset=this.helper.offset();if(k(b.left))this.position.left=b.left;if(k(b.top))this.position.top=b.top;if(k(b.height))this.size.height=b.height;if(k(b.width))this.size.width=
b.width},_updateRatio:function(b){var a=this.position,c=this.size,d=this.axis;if(k(b.height))b.width=b.height*this.aspectRatio;else if(k(b.width))b.height=b.width/this.aspectRatio;if(d=="sw"){b.left=a.left+(c.width-b.width);b.top=null}if(d=="nw"){b.top=a.top+(c.height-b.height);b.left=a.left+(c.width-b.width)}return b},_respectSize:function(b){var a=this._vBoundaries,c=this.axis,d=k(b.width)&&a.maxWidth&&a.maxWidth<b.width,f=k(b.height)&&a.maxHeight&&a.maxHeight<b.height,g=k(b.width)&&a.minWidth&&
a.minWidth>b.width,h=k(b.height)&&a.minHeight&&a.minHeight>b.height;if(g)b.width=a.minWidth;if(h)b.height=a.minHeight;if(d)b.width=a.maxWidth;if(f)b.height=a.maxHeight;var i=this.originalPosition.left+this.originalSize.width,j=this.position.top+this.size.height,l=/sw|nw|w/.test(c);c=/nw|ne|n/.test(c);if(g&&l)b.left=i-a.minWidth;if(d&&l)b.left=i-a.maxWidth;if(h&&c)b.top=j-a.minHeight;if(f&&c)b.top=j-a.maxHeight;if((a=!b.width&&!b.height)&&!b.left&&b.top)b.top=null;else if(a&&!b.top&&b.left)b.left=
null;return b},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var b=this.helper||this.element,a=0;a<this._proportionallyResizeElements.length;a++){var c=this._proportionallyResizeElements[a];if(!this.borderDif){var d=[c.css("borderTopWidth"),c.css("borderRightWidth"),c.css("borderBottomWidth"),c.css("borderLeftWidth")],f=[c.css("paddingTop"),c.css("paddingRight"),c.css("paddingBottom"),c.css("paddingLeft")];this.borderDif=e.map(d,function(g,h){g=parseInt(g,10)||
0;h=parseInt(f[h],10)||0;return g+h})}e.browser.msie&&(e(b).is(":hidden")||e(b).parents(":hidden").length)||c.css({height:b.height()-this.borderDif[0]-this.borderDif[2]||0,width:b.width()-this.borderDif[1]-this.borderDif[3]||0})}},_renderProxy:function(){var b=this.options;this.elementOffset=this.element.offset();if(this._helper){this.helper=this.helper||e('<div style="overflow:hidden;"></div>');var a=e.browser.msie&&e.browser.version<7,c=a?1:0;a=a?2:-1;this.helper.addClass(this._helper).css({width:this.element.outerWidth()+
a,height:this.element.outerHeight()+a,position:"absolute",left:this.elementOffset.left-c+"px",top:this.elementOffset.top-c+"px",zIndex:++b.zIndex});this.helper.appendTo("body").disableSelection()}else this.helper=this.element},_change:{e:function(b,a){return{width:this.originalSize.width+a}},w:function(b,a){return{left:this.originalPosition.left+a,width:this.originalSize.width-a}},n:function(b,a,c){return{top:this.originalPosition.top+c,height:this.originalSize.height-c}},s:function(b,a,c){return{height:this.originalSize.height+
c}},se:function(b,a,c){return e.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[b,a,c]))},sw:function(b,a,c){return e.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[b,a,c]))},ne:function(b,a,c){return e.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[b,a,c]))},nw:function(b,a,c){return e.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[b,a,c]))}},_propagate:function(b,a){e.ui.plugin.call(this,b,[a,this.ui()]);
b!="resize"&&this._trigger(b,a,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}});e.extend(e.ui.resizable,{version:"1.8.14"});e.ui.plugin.add("resizable","alsoResize",{start:function(){var b=e(this).data("resizable").options,a=function(c){e(c).each(function(){var d=e(this);d.data("resizable-alsoresize",{width:parseInt(d.width(),
10),height:parseInt(d.height(),10),left:parseInt(d.css("left"),10),top:parseInt(d.css("top"),10),position:d.css("position")})})};if(typeof b.alsoResize=="object"&&!b.alsoResize.parentNode)if(b.alsoResize.length){b.alsoResize=b.alsoResize[0];a(b.alsoResize)}else e.each(b.alsoResize,function(c){a(c)});else a(b.alsoResize)},resize:function(b,a){var c=e(this).data("resizable");b=c.options;var d=c.originalSize,f=c.originalPosition,g={height:c.size.height-d.height||0,width:c.size.width-d.width||0,top:c.position.top-
f.top||0,left:c.position.left-f.left||0},h=function(i,j){e(i).each(function(){var l=e(this),q=e(this).data("resizable-alsoresize"),p={},r=j&&j.length?j:l.parents(a.originalElement[0]).length?["width","height"]:["width","height","top","left"];e.each(r,function(n,o){if((n=(q[o]||0)+(g[o]||0))&&n>=0)p[o]=n||null});if(e.browser.opera&&/relative/.test(l.css("position"))){c._revertToRelativePosition=true;l.css({position:"absolute",top:"auto",left:"auto"})}l.css(p)})};typeof b.alsoResize=="object"&&!b.alsoResize.nodeType?
e.each(b.alsoResize,function(i,j){h(i,j)}):h(b.alsoResize)},stop:function(){var b=e(this).data("resizable"),a=b.options,c=function(d){e(d).each(function(){var f=e(this);f.css({position:f.data("resizable-alsoresize").position})})};if(b._revertToRelativePosition){b._revertToRelativePosition=false;typeof a.alsoResize=="object"&&!a.alsoResize.nodeType?e.each(a.alsoResize,function(d){c(d)}):c(a.alsoResize)}e(this).removeData("resizable-alsoresize")}});e.ui.plugin.add("resizable","animate",{stop:function(b){var a=
e(this).data("resizable"),c=a.options,d=a._proportionallyResizeElements,f=d.length&&/textarea/i.test(d[0].nodeName),g=f&&e.ui.hasScroll(d[0],"left")?0:a.sizeDiff.height;f={width:a.size.width-(f?0:a.sizeDiff.width),height:a.size.height-g};g=parseInt(a.element.css("left"),10)+(a.position.left-a.originalPosition.left)||null;var h=parseInt(a.element.css("top"),10)+(a.position.top-a.originalPosition.top)||null;a.element.animate(e.extend(f,h&&g?{top:h,left:g}:{}),{duration:c.animateDuration,easing:c.animateEasing,
step:function(){var i={width:parseInt(a.element.css("width"),10),height:parseInt(a.element.css("height"),10),top:parseInt(a.element.css("top"),10),left:parseInt(a.element.css("left"),10)};d&&d.length&&e(d[0]).css({width:i.width,height:i.height});a._updateCache(i);a._propagate("resize",b)}})}});e.ui.plugin.add("resizable","containment",{start:function(){var b=e(this).data("resizable"),a=b.element,c=b.options.containment;if(a=c instanceof e?c.get(0):/parent/.test(c)?a.parent().get(0):c){b.containerElement=
e(a);if(/document/.test(c)||c==document){b.containerOffset={left:0,top:0};b.containerPosition={left:0,top:0};b.parentData={element:e(document),left:0,top:0,width:e(document).width(),height:e(document).height()||document.body.parentNode.scrollHeight}}else{var d=e(a),f=[];e(["Top","Right","Left","Bottom"]).each(function(i,j){f[i]=m(d.css("padding"+j))});b.containerOffset=d.offset();b.containerPosition=d.position();b.containerSize={height:d.innerHeight()-f[3],width:d.innerWidth()-f[1]};c=b.containerOffset;
var g=b.containerSize.height,h=b.containerSize.width;h=e.ui.hasScroll(a,"left")?a.scrollWidth:h;g=e.ui.hasScroll(a)?a.scrollHeight:g;b.parentData={element:a,left:c.left,top:c.top,width:h,height:g}}}},resize:function(b){var a=e(this).data("resizable"),c=a.options,d=a.containerOffset,f=a.position;b=a._aspectRatio||b.shiftKey;var g={top:0,left:0},h=a.containerElement;if(h[0]!=document&&/static/.test(h.css("position")))g=d;if(f.left<(a._helper?d.left:0)){a.size.width+=a._helper?a.position.left-d.left:
a.position.left-g.left;if(b)a.size.height=a.size.width/c.aspectRatio;a.position.left=c.helper?d.left:0}if(f.top<(a._helper?d.top:0)){a.size.height+=a._helper?a.position.top-d.top:a.position.top;if(b)a.size.width=a.size.height*c.aspectRatio;a.position.top=a._helper?d.top:0}a.offset.left=a.parentData.left+a.position.left;a.offset.top=a.parentData.top+a.position.top;c=Math.abs((a._helper?a.offset.left-g.left:a.offset.left-g.left)+a.sizeDiff.width);d=Math.abs((a._helper?a.offset.top-g.top:a.offset.top-
d.top)+a.sizeDiff.height);f=a.containerElement.get(0)==a.element.parent().get(0);g=/relative|absolute/.test(a.containerElement.css("position"));if(f&&g)c-=a.parentData.left;if(c+a.size.width>=a.parentData.width){a.size.width=a.parentData.width-c;if(b)a.size.height=a.size.width/a.aspectRatio}if(d+a.size.height>=a.parentData.height){a.size.height=a.parentData.height-d;if(b)a.size.width=a.size.height*a.aspectRatio}},stop:function(){var b=e(this).data("resizable"),a=b.options,c=b.containerOffset,d=b.containerPosition,
f=b.containerElement,g=e(b.helper),h=g.offset(),i=g.outerWidth()-b.sizeDiff.width;g=g.outerHeight()-b.sizeDiff.height;b._helper&&!a.animate&&/relative/.test(f.css("position"))&&e(this).css({left:h.left-d.left-c.left,width:i,height:g});b._helper&&!a.animate&&/static/.test(f.css("position"))&&e(this).css({left:h.left-d.left-c.left,width:i,height:g})}});e.ui.plugin.add("resizable","ghost",{start:function(){var b=e(this).data("resizable"),a=b.options,c=b.size;b.ghost=b.originalElement.clone();b.ghost.css({opacity:0.25,
display:"block",position:"relative",height:c.height,width:c.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof a.ghost=="string"?a.ghost:"");b.ghost.appendTo(b.helper)},resize:function(){var b=e(this).data("resizable");b.ghost&&b.ghost.css({position:"relative",height:b.size.height,width:b.size.width})},stop:function(){var b=e(this).data("resizable");b.ghost&&b.helper&&b.helper.get(0).removeChild(b.ghost.get(0))}});e.ui.plugin.add("resizable","grid",{resize:function(){var b=
e(this).data("resizable"),a=b.options,c=b.size,d=b.originalSize,f=b.originalPosition,g=b.axis;a.grid=typeof a.grid=="number"?[a.grid,a.grid]:a.grid;var h=Math.round((c.width-d.width)/(a.grid[0]||1))*(a.grid[0]||1);a=Math.round((c.height-d.height)/(a.grid[1]||1))*(a.grid[1]||1);if(/^(se|s|e)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a}else if(/^(ne)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}else{if(/^(sw)$/.test(g)){b.size.width=d.width+h;b.size.height=
d.height+a}else{b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}b.position.left=f.left-h}}});var m=function(b){return parseInt(b,10)||0},k=function(b){return!isNaN(parseInt(b,10))}})(jQuery);
;/*
* jQuery UI Effects 1.8.14
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Effects/
*/
jQuery.effects||function(f,j){function m(c){var a;if(c&&c.constructor==Array&&c.length==3)return c;if(a=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c))return[parseInt(a[1],10),parseInt(a[2],10),parseInt(a[3],10)];if(a=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c))return[parseFloat(a[1])*2.55,parseFloat(a[2])*2.55,parseFloat(a[3])*2.55];if(a=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c))return[parseInt(a[1],
16),parseInt(a[2],16),parseInt(a[3],16)];if(a=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c))return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16)];if(/rgba\(0, 0, 0, 0\)/.exec(c))return n.transparent;return n[f.trim(c).toLowerCase()]}function s(c,a){var b;do{b=f.curCSS(c,a);if(b!=""&&b!="transparent"||f.nodeName(c,"body"))break;a="backgroundColor"}while(c=c.parentNode);return m(b)}function o(){var c=document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle,
a={},b,d;if(c&&c.length&&c[0]&&c[c[0]])for(var e=c.length;e--;){b=c[e];if(typeof c[b]=="string"){d=b.replace(/\-(\w)/g,function(g,h){return h.toUpperCase()});a[d]=c[b]}}else for(b in c)if(typeof c[b]==="string")a[b]=c[b];return a}function p(c){var a,b;for(a in c){b=c[a];if(b==null||f.isFunction(b)||a in t||/scrollbar/.test(a)||!/color/i.test(a)&&isNaN(parseFloat(b)))delete c[a]}return c}function u(c,a){var b={_:0},d;for(d in a)if(c[d]!=a[d])b[d]=a[d];return b}function k(c,a,b,d){if(typeof c=="object"){d=
a;b=null;a=c;c=a.effect}if(f.isFunction(a)){d=a;b=null;a={}}if(typeof a=="number"||f.fx.speeds[a]){d=b;b=a;a={}}if(f.isFunction(b)){d=b;b=null}a=a||{};b=b||a.duration;b=f.fx.off?0:typeof b=="number"?b:b in f.fx.speeds?f.fx.speeds[b]:f.fx.speeds._default;d=d||a.complete;return[c,a,b,d]}function l(c){if(!c||typeof c==="number"||f.fx.speeds[c])return true;if(typeof c==="string"&&!f.effects[c])return true;return false}f.effects={};f.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor",
"borderTopColor","borderColor","color","outlineColor"],function(c,a){f.fx.step[a]=function(b){if(!b.colorInit){b.start=s(b.elem,a);b.end=m(b.end);b.colorInit=true}b.elem.style[a]="rgb("+Math.max(Math.min(parseInt(b.pos*(b.end[0]-b.start[0])+b.start[0],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[1]-b.start[1])+b.start[1],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[2]-b.start[2])+b.start[2],10),255),0)+")"}});var n={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,
0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,
211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]},q=["add","remove","toggle"],t={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};f.effects.animateClass=function(c,a,b,
d){if(f.isFunction(b)){d=b;b=null}return this.queue(function(){var e=f(this),g=e.attr("style")||" ",h=p(o.call(this)),r,v=e.attr("class");f.each(q,function(w,i){c[i]&&e[i+"Class"](c[i])});r=p(o.call(this));e.attr("class",v);e.animate(u(h,r),{queue:false,duration:a,easing:b,complete:function(){f.each(q,function(w,i){c[i]&&e[i+"Class"](c[i])});if(typeof e.attr("style")=="object"){e.attr("style").cssText="";e.attr("style").cssText=g}else e.attr("style",g);d&&d.apply(this,arguments);f.dequeue(this)}})})};
f.fn.extend({_addClass:f.fn.addClass,addClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{add:c},a,b,d]):this._addClass(c)},_removeClass:f.fn.removeClass,removeClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{remove:c},a,b,d]):this._removeClass(c)},_toggleClass:f.fn.toggleClass,toggleClass:function(c,a,b,d,e){return typeof a=="boolean"||a===j?b?f.effects.animateClass.apply(this,[a?{add:c}:{remove:c},b,d,e]):this._toggleClass(c,a):f.effects.animateClass.apply(this,
[{toggle:c},a,b,d])},switchClass:function(c,a,b,d,e){return f.effects.animateClass.apply(this,[{add:a,remove:c},b,d,e])}});f.extend(f.effects,{version:"1.8.14",save:function(c,a){for(var b=0;b<a.length;b++)a[b]!==null&&c.data("ec.storage."+a[b],c[0].style[a[b]])},restore:function(c,a){for(var b=0;b<a.length;b++)a[b]!==null&&c.css(a[b],c.data("ec.storage."+a[b]))},setMode:function(c,a){if(a=="toggle")a=c.is(":hidden")?"show":"hide";return a},getBaseline:function(c,a){var b;switch(c[0]){case "top":b=
0;break;case "middle":b=0.5;break;case "bottom":b=1;break;default:b=c[0]/a.height}switch(c[1]){case "left":c=0;break;case "center":c=0.5;break;case "right":c=1;break;default:c=c[1]/a.width}return{x:c,y:b}},createWrapper:function(c){if(c.parent().is(".ui-effects-wrapper"))return c.parent();var a={width:c.outerWidth(true),height:c.outerHeight(true),"float":c.css("float")},b=f("<div></div>").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0});
c.wrap(b);b=c.parent();if(c.css("position")=="static"){b.css({position:"relative"});c.css({position:"relative"})}else{f.extend(a,{position:c.css("position"),zIndex:c.css("z-index")});f.each(["top","left","bottom","right"],function(d,e){a[e]=c.css(e);if(isNaN(parseInt(a[e],10)))a[e]="auto"});c.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})}return b.css(a).show()},removeWrapper:function(c){if(c.parent().is(".ui-effects-wrapper"))return c.parent().replaceWith(c);return c},setTransition:function(c,
a,b,d){d=d||{};f.each(a,function(e,g){unit=c.cssUnit(g);if(unit[0]>0)d[g]=unit[0]*b+unit[1]});return d}});f.fn.extend({effect:function(c){var a=k.apply(this,arguments),b={options:a[1],duration:a[2],callback:a[3]};a=b.options.mode;var d=f.effects[c];if(f.fx.off||!d)return a?this[a](b.duration,b.callback):this.each(function(){b.callback&&b.callback.call(this)});return d.call(this,b)},_show:f.fn.show,show:function(c){if(l(c))return this._show.apply(this,arguments);else{var a=k.apply(this,arguments);
a[1].mode="show";return this.effect.apply(this,a)}},_hide:f.fn.hide,hide:function(c){if(l(c))return this._hide.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="hide";return this.effect.apply(this,a)}},__toggle:f.fn.toggle,toggle:function(c){if(l(c)||typeof c==="boolean"||f.isFunction(c))return this.__toggle.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="toggle";return this.effect.apply(this,a)}},cssUnit:function(c){var a=this.css(c),b=[];f.each(["em","px","%",
"pt"],function(d,e){if(a.indexOf(e)>0)b=[parseFloat(a),e]});return b}});f.easing.jswing=f.easing.swing;f.extend(f.easing,{def:"easeOutQuad",swing:function(c,a,b,d,e){return f.easing[f.easing.def](c,a,b,d,e)},easeInQuad:function(c,a,b,d,e){return d*(a/=e)*a+b},easeOutQuad:function(c,a,b,d,e){return-d*(a/=e)*(a-2)+b},easeInOutQuad:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a+b;return-d/2*(--a*(a-2)-1)+b},easeInCubic:function(c,a,b,d,e){return d*(a/=e)*a*a+b},easeOutCubic:function(c,a,b,d,e){return d*
((a=a/e-1)*a*a+1)+b},easeInOutCubic:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a+b;return d/2*((a-=2)*a*a+2)+b},easeInQuart:function(c,a,b,d,e){return d*(a/=e)*a*a*a+b},easeOutQuart:function(c,a,b,d,e){return-d*((a=a/e-1)*a*a*a-1)+b},easeInOutQuart:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a+b;return-d/2*((a-=2)*a*a*a-2)+b},easeInQuint:function(c,a,b,d,e){return d*(a/=e)*a*a*a*a+b},easeOutQuint:function(c,a,b,d,e){return d*((a=a/e-1)*a*a*a*a+1)+b},easeInOutQuint:function(c,a,b,d,e){if((a/=
e/2)<1)return d/2*a*a*a*a*a+b;return d/2*((a-=2)*a*a*a*a+2)+b},easeInSine:function(c,a,b,d,e){return-d*Math.cos(a/e*(Math.PI/2))+d+b},easeOutSine:function(c,a,b,d,e){return d*Math.sin(a/e*(Math.PI/2))+b},easeInOutSine:function(c,a,b,d,e){return-d/2*(Math.cos(Math.PI*a/e)-1)+b},easeInExpo:function(c,a,b,d,e){return a==0?b:d*Math.pow(2,10*(a/e-1))+b},easeOutExpo:function(c,a,b,d,e){return a==e?b+d:d*(-Math.pow(2,-10*a/e)+1)+b},easeInOutExpo:function(c,a,b,d,e){if(a==0)return b;if(a==e)return b+d;if((a/=
e/2)<1)return d/2*Math.pow(2,10*(a-1))+b;return d/2*(-Math.pow(2,-10*--a)+2)+b},easeInCirc:function(c,a,b,d,e){return-d*(Math.sqrt(1-(a/=e)*a)-1)+b},easeOutCirc:function(c,a,b,d,e){return d*Math.sqrt(1-(a=a/e-1)*a)+b},easeInOutCirc:function(c,a,b,d,e){if((a/=e/2)<1)return-d/2*(Math.sqrt(1-a*a)-1)+b;return d/2*(Math.sqrt(1-(a-=2)*a)+1)+b},easeInElastic:function(c,a,b,d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h<Math.abs(d)){h=d;c=g/4}else c=g/(2*Math.PI)*Math.asin(d/
h);return-(h*Math.pow(2,10*(a-=1))*Math.sin((a*e-c)*2*Math.PI/g))+b},easeOutElastic:function(c,a,b,d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h<Math.abs(d)){h=d;c=g/4}else c=g/(2*Math.PI)*Math.asin(d/h);return h*Math.pow(2,-10*a)*Math.sin((a*e-c)*2*Math.PI/g)+d+b},easeInOutElastic:function(c,a,b,d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e/2)==2)return b+d;g||(g=e*0.3*1.5);if(h<Math.abs(d)){h=d;c=g/4}else c=g/(2*Math.PI)*Math.asin(d/h);if(a<1)return-0.5*
h*Math.pow(2,10*(a-=1))*Math.sin((a*e-c)*2*Math.PI/g)+b;return h*Math.pow(2,-10*(a-=1))*Math.sin((a*e-c)*2*Math.PI/g)*0.5+d+b},easeInBack:function(c,a,b,d,e,g){if(g==j)g=1.70158;return d*(a/=e)*a*((g+1)*a-g)+b},easeOutBack:function(c,a,b,d,e,g){if(g==j)g=1.70158;return d*((a=a/e-1)*a*((g+1)*a+g)+1)+b},easeInOutBack:function(c,a,b,d,e,g){if(g==j)g=1.70158;if((a/=e/2)<1)return d/2*a*a*(((g*=1.525)+1)*a-g)+b;return d/2*((a-=2)*a*(((g*=1.525)+1)*a+g)+2)+b},easeInBounce:function(c,a,b,d,e){return d-f.easing.easeOutBounce(c,
e-a,0,d,e)+b},easeOutBounce:function(c,a,b,d,e){return(a/=e)<1/2.75?d*7.5625*a*a+b:a<2/2.75?d*(7.5625*(a-=1.5/2.75)*a+0.75)+b:a<2.5/2.75?d*(7.5625*(a-=2.25/2.75)*a+0.9375)+b:d*(7.5625*(a-=2.625/2.75)*a+0.984375)+b},easeInOutBounce:function(c,a,b,d,e){if(a<e/2)return f.easing.easeInBounce(c,a*2,0,d,e)*0.5+b;return f.easing.easeOutBounce(c,a*2-e,0,d,e)*0.5+d*0.5+b}})}(jQuery);
;/*
* jQuery UI Effects Pulsate 1.8.14
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Effects/Pulsate
*
* Depends:
* jquery.effects.core.js
*/
(function(d){d.effects.pulsate=function(a){return this.queue(function(){var b=d(this),c=d.effects.setMode(b,a.options.mode||"show");times=(a.options.times||5)*2-1;duration=a.duration?a.duration/2:d.fx.speeds._default/2;isVisible=b.is(":visible");animateTo=0;if(!isVisible){b.css("opacity",0).show();animateTo=1}if(c=="hide"&&isVisible||c=="show"&&!isVisible)times--;for(c=0;c<times;c++){b.animate({opacity:animateTo},duration,a.options.easing);animateTo=(animateTo+1)%2}b.animate({opacity:animateTo},duration,
a.options.easing,function(){animateTo==0&&b.hide();a.callback&&a.callback.apply(this,arguments)});b.queue("fx",function(){b.dequeue()}).dequeue()})}})(jQuery);
;/*
* jQuery UI Effects Slide 1.8.14
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Effects/Slide
*
* Depends:
* jquery.effects.core.js
*/
(function(c){c.effects.slide=function(d){return this.queue(function(){var a=c(this),h=["position","top","bottom","left","right"],f=c.effects.setMode(a,d.options.mode||"show"),b=d.options.direction||"left";c.effects.save(a,h);a.show();c.effects.createWrapper(a).css({overflow:"hidden"});var g=b=="up"||b=="down"?"top":"left";b=b=="up"||b=="left"?"pos":"neg";var e=d.options.distance||(g=="top"?a.outerHeight({margin:true}):a.outerWidth({margin:true}));if(f=="show")a.css(g,b=="pos"?isNaN(e)?"-"+e:-e:e);
var i={};i[g]=(f=="show"?b=="pos"?"+=":"-=":b=="pos"?"-=":"+=")+e;a.animate(i,{queue:false,duration:d.duration,easing:d.options.easing,complete:function(){f=="hide"&&a.hide();c.effects.restore(a,h);c.effects.removeWrapper(a);d.callback&&d.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);
;

View file

@ -469,3 +469,5 @@ if (!JSON)
}; };
} }
}()); }());
module.exports = JSON;

View file

@ -27,6 +27,11 @@
// requires: top // requires: top
// requires: plugins // requires: plugins
// requires: undefined // requires: undefined
var Changeset = require('/Changeset');
var plugins = require('/plugins').plugins;
var map = require('/ace2_common').map;
var linestylefilter = {}; var linestylefilter = {};
linestylefilter.ATTRIB_CLASSES = { linestylefilter.ATTRIB_CLASSES = {
@ -50,15 +55,7 @@ linestylefilter.getAuthorClassName = function(author)
linestylefilter.getLineStyleFilter = function(lineLength, aline, textAndClassFunc, apool) linestylefilter.getLineStyleFilter = function(lineLength, aline, textAndClassFunc, apool)
{ {
var plugins_; var plugins_ = plugins;
if (typeof(plugins) != 'undefined')
{
plugins_ = plugins;
}
else
{
plugins_ = parent.parent.plugins;
}
if (lineLength == 0) return textAndClassFunc; if (lineLength == 0) return textAndClassFunc;
@ -90,6 +87,10 @@ linestylefilter.getLineStyleFilter = function(lineLength, aline, textAndClassFun
{ {
classes += ' list:' + value; classes += ' list:' + value;
} }
else if (key == 'start')
{
classes += ' start:' + value;
}
else if (linestylefilter.ATTRIB_CLASSES[key]) else if (linestylefilter.ATTRIB_CLASSES[key])
{ {
classes += ' ' + linestylefilter.ATTRIB_CLASSES[key]; classes += ' ' + linestylefilter.ATTRIB_CLASSES[key];
@ -299,21 +300,13 @@ linestylefilter.getFilterStack = function(lineText, textAndClassFunc, browser)
{ {
var func = linestylefilter.getURLFilter(lineText, textAndClassFunc); var func = linestylefilter.getURLFilter(lineText, textAndClassFunc);
var plugins_; var plugins_ = plugins;
if (typeof(plugins) != 'undefined')
{
plugins_ = plugins;
}
else
{
plugins_ = parent.parent.plugins;
}
var hookFilters = plugins_.callHook("aceGetFilterStack", { var hookFilters = plugins_.callHook("aceGetFilterStack", {
linestylefilter: linestylefilter, linestylefilter: linestylefilter,
browser: browser browser: browser
}); });
hookFilters.map(function(hookFilter) map(hookFilters, function(hookFilter)
{ {
func = hookFilter(lineText, func); func = hookFilter(lineText, func);
}); });
@ -348,3 +341,5 @@ linestylefilter.populateDomLine = function(textLine, aline, apool, domLineObj)
func = linestylefilter.getLineStyleFilter(text.length, aline, func, apool); func = linestylefilter.getLineStyleFilter(text.length, aline, func, apool);
func(text, ''); func(text, '');
}; };
exports.linestylefilter = linestylefilter;

View file

@ -1,348 +0,0 @@
/**
* This code is mostly from the old Etherpad. Please help us to comment this code.
* This helps other people to understand this code better and helps them to improve it.
* TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
*/
// DO NOT EDIT THIS FILE, edit infrastructure/ace/www/linestylefilter.js
// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.linestylefilter
/**
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// requires: easysync2.Changeset
// requires: top
// requires: plugins
// requires: undefined
var linestylefilter = {};
linestylefilter.ATTRIB_CLASSES = {
'bold': 'tag:b',
'italic': 'tag:i',
'underline': 'tag:u',
'strikethrough': 'tag:s'
};
linestylefilter.getAuthorClassName = function(author)
{
return "author-" + author.replace(/[^a-y0-9]/g, function(c)
{
if (c == ".") return "-";
return 'z' + c.charCodeAt(0) + 'z';
});
};
// lineLength is without newline; aline includes newline,
// but may be falsy if lineLength == 0
linestylefilter.getLineStyleFilter = function(lineLength, aline, textAndClassFunc, apool)
{
var plugins_;
if (typeof(plugins) != 'undefined')
{
plugins_ = plugins;
}
else
{
plugins_ = parent.parent.plugins;
}
if (lineLength == 0) return textAndClassFunc;
var nextAfterAuthorColors = textAndClassFunc;
var authorColorFunc = (function()
{
var lineEnd = lineLength;
var curIndex = 0;
var extraClasses;
var leftInAuthor;
function attribsToClasses(attribs)
{
var classes = '';
Changeset.eachAttribNumber(attribs, function(n)
{
var key = apool.getAttribKey(n);
if (key)
{
var value = apool.getAttribValue(n);
if (value)
{
if (key == 'author')
{
classes += ' ' + linestylefilter.getAuthorClassName(value);
}
else if (key == 'list')
{
classes += ' list:' + value;
}
else if (linestylefilter.ATTRIB_CLASSES[key])
{
classes += ' ' + linestylefilter.ATTRIB_CLASSES[key];
}
else
{
classes += plugins_.callHookStr("aceAttribsToClasses", {
linestylefilter: linestylefilter,
key: key,
value: value
}, " ", " ", "");
}
}
}
});
return classes.substring(1);
}
var attributionIter = Changeset.opIterator(aline);
var nextOp, nextOpClasses;
function goNextOp()
{
nextOp = attributionIter.next();
nextOpClasses = (nextOp.opcode && attribsToClasses(nextOp.attribs));
}
goNextOp();
function nextClasses()
{
if (curIndex < lineEnd)
{
extraClasses = nextOpClasses;
leftInAuthor = nextOp.chars;
goNextOp();
while (nextOp.opcode && nextOpClasses == extraClasses)
{
leftInAuthor += nextOp.chars;
goNextOp();
}
}
}
nextClasses();
return function(txt, cls)
{
while (txt.length > 0)
{
if (leftInAuthor <= 0)
{
// prevent infinite loop if something funny's going on
return nextAfterAuthorColors(txt, cls);
}
var spanSize = txt.length;
if (spanSize > leftInAuthor)
{
spanSize = leftInAuthor;
}
var curTxt = txt.substring(0, spanSize);
txt = txt.substring(spanSize);
nextAfterAuthorColors(curTxt, (cls && cls + " ") + extraClasses);
curIndex += spanSize;
leftInAuthor -= spanSize;
if (leftInAuthor == 0)
{
nextClasses();
}
}
};
})();
return authorColorFunc;
};
linestylefilter.getAtSignSplitterFilter = function(lineText, textAndClassFunc)
{
var at = /@/g;
at.lastIndex = 0;
var splitPoints = null;
var execResult;
while ((execResult = at.exec(lineText)))
{
if (!splitPoints)
{
splitPoints = [];
}
splitPoints.push(execResult.index);
}
if (!splitPoints) return textAndClassFunc;
return linestylefilter.textAndClassFuncSplitter(textAndClassFunc, splitPoints);
};
linestylefilter.getRegexpFilter = function(regExp, tag)
{
return function(lineText, textAndClassFunc)
{
regExp.lastIndex = 0;
var regExpMatchs = null;
var splitPoints = null;
var execResult;
while ((execResult = regExp.exec(lineText)))
{
if (!regExpMatchs)
{
regExpMatchs = [];
splitPoints = [];
}
var startIndex = execResult.index;
var regExpMatch = execResult[0];
regExpMatchs.push([startIndex, regExpMatch]);
splitPoints.push(startIndex, startIndex + regExpMatch.length);
}
if (!regExpMatchs) return textAndClassFunc;
function regExpMatchForIndex(idx)
{
for (var k = 0; k < regExpMatchs.length; k++)
{
var u = regExpMatchs[k];
if (idx >= u[0] && idx < u[0] + u[1].length)
{
return u[1];
}
}
return false;
}
var handleRegExpMatchsAfterSplit = (function()
{
var curIndex = 0;
return function(txt, cls)
{
var txtlen = txt.length;
var newCls = cls;
var regExpMatch = regExpMatchForIndex(curIndex);
if (regExpMatch)
{
newCls += " " + tag + ":" + regExpMatch;
}
textAndClassFunc(txt, newCls);
curIndex += txtlen;
};
})();
return linestylefilter.textAndClassFuncSplitter(handleRegExpMatchsAfterSplit, splitPoints);
};
};
linestylefilter.REGEX_WORDCHAR = /[\u0030-\u0039\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u1FFF\u3040-\u9FFF\uF900-\uFDFF\uFE70-\uFEFE\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFDC]/;
linestylefilter.REGEX_URLCHAR = new RegExp('(' + /[-:@a-zA-Z0-9_.,~%+\/\\?=&#;()$]/.source + '|' + linestylefilter.REGEX_WORDCHAR.source + ')');
linestylefilter.REGEX_URL = new RegExp(/(?:(?:https?|s?ftp|ftps|file|smb|afp|nfs|(x-)?man|gopher|txmt):\/\/|mailto:)/.source + linestylefilter.REGEX_URLCHAR.source + '*(?![:.,;])' + linestylefilter.REGEX_URLCHAR.source, 'g');
linestylefilter.getURLFilter = linestylefilter.getRegexpFilter(
linestylefilter.REGEX_URL, 'url');
linestylefilter.textAndClassFuncSplitter = function(func, splitPointsOpt)
{
var nextPointIndex = 0;
var idx = 0;
// don't split at 0
while (splitPointsOpt && nextPointIndex < splitPointsOpt.length && splitPointsOpt[nextPointIndex] == 0)
{
nextPointIndex++;
}
function spanHandler(txt, cls)
{
if ((!splitPointsOpt) || nextPointIndex >= splitPointsOpt.length)
{
func(txt, cls);
idx += txt.length;
}
else
{
var splitPoints = splitPointsOpt;
var pointLocInSpan = splitPoints[nextPointIndex] - idx;
var txtlen = txt.length;
if (pointLocInSpan >= txtlen)
{
func(txt, cls);
idx += txt.length;
if (pointLocInSpan == txtlen)
{
nextPointIndex++;
}
}
else
{
if (pointLocInSpan > 0)
{
func(txt.substring(0, pointLocInSpan), cls);
idx += pointLocInSpan;
}
nextPointIndex++;
// recurse
spanHandler(txt.substring(pointLocInSpan), cls);
}
}
}
return spanHandler;
};
linestylefilter.getFilterStack = function(lineText, textAndClassFunc, browser)
{
var func = linestylefilter.getURLFilter(lineText, textAndClassFunc);
var plugins_;
if (typeof(plugins) != 'undefined')
{
plugins_ = plugins;
}
else
{
plugins_ = parent.parent.plugins;
}
var hookFilters = plugins_.callHook("aceGetFilterStack", {
linestylefilter: linestylefilter,
browser: browser
});
hookFilters.map(function(hookFilter)
{
func = hookFilter(lineText, func);
});
if (browser !== undefined && browser.msie)
{
// IE7+ will take an e-mail address like <foo@bar.com> and linkify it to foo@bar.com.
// We then normalize it back to text with no angle brackets. It's weird. So always
// break spans at an "at" sign.
func = linestylefilter.getAtSignSplitterFilter(
lineText, func);
}
return func;
};
// domLineObj is like that returned by domline.createDomLine
linestylefilter.populateDomLine = function(textLine, aline, apool, domLineObj)
{
// remove final newline from text if any
var text = textLine;
if (text.slice(-1) == '\n')
{
text = text.substring(0, text.length - 1);
}
function textAndClassFunc(tokenText, tokenClass)
{
domLineObj.appendSpan(tokenText, tokenClass);
}
var func = linestylefilter.getFilterStack(text, textAndClassFunc);
func = linestylefilter.getLineStyleFilter(text.length, aline, func, apool);
func(text, '');
};

View file

@ -23,67 +23,32 @@
/* global $, window */ /* global $, window */
var socket; var socket;
var LineNumbersDisabled = false;
var noColors = false;
var useMonospaceFontGlobal = false;
var globalUserName = false;
var hideQRCode = false;
var rtlIsTrue = false;
$(document).ready(function() // These jQuery things should create local references, but for now `require()`
{ // assigns to the global `$` and augments it with plugins.
//start the costum js require('/jquery');
if(typeof costumStart == "function") costumStart(); require('/farbtastic');
getParams(); require('/excanvas');
handshake(); JSON = require('/json2');
}); require('/undo-xpopup');
require('/prefixfree');
$(window).unload(function() var chat = require('/chat').chat;
{ var getCollabClient = require('/collab_client').getCollabClient;
pad.dispose(); var padconnectionstatus = require('/pad_connectionstatus').padconnectionstatus;
}); var padcookie = require('/pad_cookie').padcookie;
var paddocbar = require('/pad_docbar').paddocbar;
var padeditbar = require('/pad_editbar').padeditbar;
var padeditor = require('/pad_editor').padeditor;
var padimpexp = require('/pad_impexp').padimpexp;
var padmodals = require('/pad_modals').padmodals;
var padsavedrevs = require('/pad_savedrevs').padsavedrevs;
var paduserlist = require('/pad_userlist').paduserlist;
var padutils = require('/pad_utils').padutils;
function createCookie(name, value, days, path) var createCookie = require('/pad_utils').createCookie;
{ var readCookie = require('/pad_utils').readCookie;
if (days) var randomString = require('/pad_utils').randomString;
{
var date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
var expires = "; expires=" + date.toGMTString();
}
else var expires = "";
if(!path)
path = "/";
document.cookie = name + "=" + value + expires + "; path=" + path;
}
function readCookie(name)
{
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++)
{
var c = ca[i];
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
}
return null;
}
function randomString()
{
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var string_length = 20;
var randomstring = '';
for (var i = 0; i < string_length; i++)
{
var rnum = Math.floor(Math.random() * chars.length);
randomstring += chars.substring(rnum, rnum + 1);
}
return "t." + randomstring;
}
function getParams() function getParams()
{ {
@ -96,12 +61,13 @@ function getParams()
var IsnoColors = params["noColors"]; var IsnoColors = params["noColors"];
var hideQRCode = params["hideQRCode"]; var hideQRCode = params["hideQRCode"];
var rtl = params["rtl"]; var rtl = params["rtl"];
var alwaysShowChat = params["alwaysShowChat"];
if(IsnoColors) if(IsnoColors)
{ {
if(IsnoColors == "true") if(IsnoColors == "true")
{ {
noColors = true; settings.noColors = true;
$('#clearAuthorship').hide(); $('#clearAuthorship').hide();
} }
} }
@ -124,20 +90,20 @@ function getParams()
{ {
if(showLineNumbers == "false") if(showLineNumbers == "false")
{ {
LineNumbersDisabled = true; settings.LineNumbersDisabled = true;
} }
} }
if(useMonospaceFont) if(useMonospaceFont)
{ {
if(useMonospaceFont == "true") if(useMonospaceFont == "true")
{ {
useMonospaceFontGlobal = true; settings.useMonospaceFontGlobal = true;
} }
} }
if(userName) if(userName)
{ {
// If the username is set as a parameter we should set a global value that we can call once we have initiated the pad. // If the username is set as a parameter we should set a global value that we can call once we have initiated the pad.
globalUserName = unescape(userName); settings.globalUserName = decodeURIComponent(userName);
} }
if(hideQRCode) if(hideQRCode)
{ {
@ -147,7 +113,14 @@ function getParams()
{ {
if(rtl == "true") if(rtl == "true")
{ {
rtlIsTrue = true settings.rtlIsTrue = true
}
}
if(alwaysShowChat)
{
if(alwaysShowChat == "true")
{
chat.stickToScreen();
} }
} }
} }
@ -173,6 +146,12 @@ function savePassword()
document.location=document.location; document.location=document.location;
} }
function ieTestXMLHTTP(){
// Test for IE known XML HTTP issue
if ($.browser.msie && !window.XMLHttpRequest){
$("#editorloadingbox").html("You do not have XML HTTP enabled in your browser. <a target='_blank' href='https://github.com/Pita/etherpad-lite/wiki/How-to-enable-native-XMLHTTP-support-in-IE'>Fix this issue</a>");
}
}
function handshake() function handshake()
{ {
var loc = document.location; var loc = document.location;
@ -183,9 +162,10 @@ function handshake()
//find out in which subfolder we are //find out in which subfolder we are
var resource = loc.pathname.substr(1, loc.pathname.indexOf("/p/")) + "socket.io"; var resource = loc.pathname.substr(1, loc.pathname.indexOf("/p/")) + "socket.io";
//connect //connect
socket = io.connect(url, { socket = pad.socket = io.connect(url, {
resource: resource, resource: resource,
'max reconnection attempts': 3 'max reconnection attempts': 3,
'sync disconnect on unload' : false
}); });
function sendClientReady(isReconnect) function sendClientReady(isReconnect)
@ -194,12 +174,12 @@ function handshake()
padId = decodeURIComponent(padId); // unescape neccesary due to Safari and Opera interpretation of spaces padId = decodeURIComponent(padId); // unescape neccesary due to Safari and Opera interpretation of spaces
if(!isReconnect) if(!isReconnect)
document.title = document.title + " | " + padId; document.title = padId.replace(/_+/g, ' ') + " | " + document.title;
var token = readCookie("token"); var token = readCookie("token");
if (token == null) if (token == null)
{ {
token = randomString(); token = "t." + randomString();
createCookie("token", token, 60); createCookie("token", token, 60);
} }
@ -243,7 +223,10 @@ function handshake()
sendClientReady(true); sendClientReady(true);
}); });
socket.on('disconnect', function () { socket.on('disconnect', function (reason) {
if(reason == "booted"){
pad.collabClient.setChannelState("DISCONNECTED");
} else {
function disconnectEvent() function disconnectEvent()
{ {
pad.collabClient.setChannelState("DISCONNECTED", "reconnect_timeout"); pad.collabClient.setChannelState("DISCONNECTED", "reconnect_timeout");
@ -252,6 +235,7 @@ function handshake()
pad.collabClient.setChannelState("RECONNECTING"); pad.collabClient.setChannelState("RECONNECTING");
disconnectTimeout = setTimeout(disconnectEvent, 10000); disconnectTimeout = setTimeout(disconnectEvent, 10000);
}
}); });
var receivedClientVars = false; var receivedClientVars = false;
@ -270,13 +254,13 @@ function handshake()
{ {
$("#editorloadingbox").html("<b>You need a password to access this pad</b><br>" + $("#editorloadingbox").html("<b>You need a password to access this pad</b><br>" +
"<input id='passwordinput' type='password' name='password'>"+ "<input id='passwordinput' type='password' name='password'>"+
"<button type='button' onclick='savePassword()'>ok</button>"); "<button type='button' onclick=\"" + padutils.escapeHtml('require('+JSON.stringify(module.id)+").savePassword()") + "\">ok</button>");
} }
else if(obj.accessStatus == "wrongPassword") else if(obj.accessStatus == "wrongPassword")
{ {
$("#editorloadingbox").html("<b>You're password was wrong</b><br>" + $("#editorloadingbox").html("<b>You're password was wrong</b><br>" +
"<input id='passwordinput' type='password' name='password'>"+ "<input id='passwordinput' type='password' name='password'>"+
"<button type='button' onclick='savePassword()'>ok</button>"); "<button type='button' onclick=\"" + padutils.escapeHtml('require('+JSON.stringify(module.id)+").savePassword()") + "\">ok</button>");
} }
} }
@ -294,37 +278,37 @@ function handshake()
clientVars.collab_client_vars.clientAgent = "Anonymous"; clientVars.collab_client_vars.clientAgent = "Anonymous";
//initalize the pad //initalize the pad
pad.init(); pad._afterHandshake();
initalized = true; initalized = true;
// If the LineNumbersDisabled value is set to true then we need to hide the Line Numbers // If the LineNumbersDisabled value is set to true then we need to hide the Line Numbers
if (LineNumbersDisabled == true) if (settings.LineNumbersDisabled == true)
{ {
pad.changeViewOption('showLineNumbers', false); pad.changeViewOption('showLineNumbers', false);
} }
// If the noColors value is set to true then we need to hide the backround colors on the ace spans // If the noColors value is set to true then we need to hide the background colors on the ace spans
if (noColors == true) if (settings.noColors == true)
{ {
pad.changeViewOption('noColors', true); pad.changeViewOption('noColors', true);
} }
if (rtlIsTrue == true) if (settings.rtlIsTrue == true)
{ {
pad.changeViewOption('rtl', true); pad.changeViewOption('rtl', true);
} }
// If the Monospacefont value is set to true then change it to monospace. // If the Monospacefont value is set to true then change it to monospace.
if (useMonospaceFontGlobal == true) if (settings.useMonospaceFontGlobal == true)
{ {
pad.changeViewOption('useMonospaceFont', true); pad.changeViewOption('useMonospaceFont', true);
} }
// if the globalUserName value is set we need to tell the server and the client about the new authorname // if the globalUserName value is set we need to tell the server and the client about the new authorname
if (globalUserName !== false) if (settings.globalUserName !== false)
{ {
pad.notifyChangeName(globalUserName); // Notifies the server pad.notifyChangeName(settings.globalUserName); // Notifies the server
pad.myUserInfo.name = globalUserName; pad.myUserInfo.name = settings.globalUserName;
$('#myusernameedit').attr({"value":globalUserName}); // Updates the current users UI $('#myusernameedit').attr({"value":settings.globalUserName}); // Updates the current users UI
} }
} }
//This handles every Message after the clientVars //This handles every Message after the clientVars
@ -343,7 +327,6 @@ function handshake()
} }
} }
}); });
// Bind the colorpicker // Bind the colorpicker
var fb = $('#colorpicker').farbtastic({ callback: '#mycolorpickerpreview', width: 220}); var fb = $('#colorpicker').farbtastic({ callback: '#mycolorpickerpreview', width: 220});
} }
@ -392,7 +375,6 @@ var pad = {
{ {
return clientVars.userIsGuest; return clientVars.userIsGuest;
}, },
//
getUserId: function() getUserId: function()
{ {
return pad.myUserInfo.userId; return pad.myUserInfo.userId;
@ -407,11 +389,25 @@ var pad = {
}, },
init: function() init: function()
{
padutils.setupGlobalExceptionHandler();
$(document).ready(function()
{
// test for XML HTTP capabiites
ieTestXMLHTTP();
// start the custom js
if (typeof customStart == "function") customStart();
getParams();
handshake();
});
},
_afterHandshake: function()
{ {
pad.clientTimeOffset = new Date().getTime() - clientVars.serverTimestamp; pad.clientTimeOffset = new Date().getTime() - clientVars.serverTimestamp;
//initialize the chat //initialize the chat
chat.init(); chat.init(this);
pad.initTime = +(new Date()); pad.initTime = +(new Date());
pad.padOptions = clientVars.initialOptions; pad.padOptions = clientVars.initialOptions;
@ -432,10 +428,10 @@ var pad = {
} }
// order of inits is important here: // order of inits is important here:
padcookie.init(clientVars.cookiePrefsToSet); padcookie.init(clientVars.cookiePrefsToSet, this);
$("#widthprefcheck").click(pad.toggleWidthPref); $("#widthprefcheck").click(pad.toggleWidthPref);
$("#sidebarcheck").click(pad.toggleSidebar); // $("#sidebarcheck").click(pad.togglewSidebar);
pad.myUserInfo = { pad.myUserInfo = {
userId: clientVars.userId, userId: clientVars.userId,
@ -459,20 +455,20 @@ var pad = {
initialTitle: clientVars.initialTitle, initialTitle: clientVars.initialTitle,
initialPassword: clientVars.initialPassword, initialPassword: clientVars.initialPassword,
guestPolicy: pad.padOptions.guestPolicy guestPolicy: pad.padOptions.guestPolicy
}); }, this);
padimpexp.init(); padimpexp.init(this);
padsavedrevs.init(clientVars.initialRevisionList); padsavedrevs.init(clientVars.initialRevisionList, this);
padeditor.init(postAceInit, pad.padOptions.view || {}); padeditor.init(postAceInit, pad.padOptions.view || {}, this);
paduserlist.init(pad.myUserInfo); paduserlist.init(pad.myUserInfo, this);
// padchat.init(clientVars.chatHistory, pad.myUserInfo); // padchat.init(clientVars.chatHistory, pad.myUserInfo);
padconnectionstatus.init(); padconnectionstatus.init();
padmodals.init(); padmodals.init(this);
pad.collabClient = getCollabClient(padeditor.ace, clientVars.collab_client_vars, pad.myUserInfo, { pad.collabClient = getCollabClient(padeditor.ace, clientVars.collab_client_vars, pad.myUserInfo, {
colorPalette: pad.getColorPalette() colorPalette: pad.getColorPalette()
}); }, pad);
pad.collabClient.setOnUserJoin(pad.handleUserJoin); pad.collabClient.setOnUserJoin(pad.handleUserJoin);
pad.collabClient.setOnUpdateUserInfo(pad.handleUserUpdate); pad.collabClient.setOnUpdateUserInfo(pad.handleUserUpdate);
pad.collabClient.setOnUserLeave(pad.handleUserLeave); pad.collabClient.setOnUserLeave(pad.handleUserLeave);
@ -488,6 +484,13 @@ var pad = {
{ {
padeditor.ace.focus(); padeditor.ace.focus();
}, 0); }, 0);
if(padcookie.getPref("chatAlwaysVisible")){ // if we have a cookie for always showing chat then show it
chat.stickToScreen(true); // stick it to the screen
$('#options-stickychat').prop("checked", true); // set the checkbox to on
}
if(padcookie.getPref("showAuthorshipColors") == false){
pad.changeViewOption('showAuthorColors', false);
}
} }
}, },
dispose: function() dispose: function()
@ -543,16 +546,6 @@ var pad = {
}; };
options.view[key] = value; options.view[key] = value;
pad.handleOptionsChange(options); pad.handleOptionsChange(options);
// if the request isn't to hide line numbers then broadcast this to other users
if (key != "showLineNumbers" && key != "useMonospaceFont")
{
pad.collabClient.sendClientMessage(
{
type: 'padoptions',
options: options,
changedBy: pad.myUserInfo.name || "unnamed"
});
}
}, },
handleOptionsChange: function(opts) handleOptionsChange: function(opts)
{ {
@ -760,15 +753,18 @@ var pad = {
padsavedrevs.handleIsFullyConnected(isConnected); padsavedrevs.handleIsFullyConnected(isConnected);
pad.determineSidebarVisibility(isConnected && !isInitialConnect); // pad.determineSidebarVisibility(isConnected && !isInitialConnect);
pad.determineChatVisibility(isConnected && !isInitialConnect);
pad.determineAuthorshipColorsVisibility();
}, },
determineSidebarVisibility: function(asNowConnectedFeedback) /* determineSidebarVisibility: function(asNowConnectedFeedback)
{ {
if (pad.isFullyConnected()) if (pad.isFullyConnected())
{ {
var setSidebarVisibility = padutils.getCancellableAction("set-sidebar-visibility", function() var setSidebarVisibility = padutils.getCancellableAction("set-sidebar-visibility", function()
{ {
$("body").toggleClass('hidesidebar', !! padcookie.getPref('hideSidebar')); // $("body").toggleClass('hidesidebar', !! padcookie.getPref('hideSidebar'));
}); });
window.setTimeout(setSidebarVisibility, asNowConnectedFeedback ? 3000 : 0); window.setTimeout(setSidebarVisibility, asNowConnectedFeedback ? 3000 : 0);
} }
@ -778,6 +774,27 @@ var pad = {
$("body").removeClass('hidesidebar'); $("body").removeClass('hidesidebar');
} }
}, },
*/
determineChatVisibility: function(asNowConnectedFeedback){
var chatVisCookie = padcookie.getPref('chatAlwaysVisible');
if(chatVisCookie){ // if the cookie is set for chat always visible
chat.stickToScreen(true); // stick it to the screen
$('#options-stickychat').prop("checked", true); // set the checkbox to on
}
else{
$('#options-stickychat').prop("checked", false); // set the checkbox for off
}
},
determineAuthorshipColorsVisibility: function(){
var authColCookie = padcookie.getPref('showAuthorshipColors');
if (authColCookie){
pad.changeViewOption('showAuthorColors', true);
$('#options-colorscheck').prop("checked", true);
}
else {
$('#options-colorscheck').prop("checked", false);
}
},
handleCollabAction: function(action) handleCollabAction: function(action)
{ {
if (action == "commitPerformed") if (action == "commitPerformed")
@ -826,6 +843,7 @@ var pad = {
$("#widthprefcheck").toggleClass('widthprefchecked', !! newValue).toggleClass('widthprefunchecked', !newValue); $("#widthprefcheck").toggleClass('widthprefchecked', !! newValue).toggleClass('widthprefunchecked', !newValue);
pad.handleWidthChange(); pad.handleWidthChange();
}, },
/*
toggleSidebar: function() toggleSidebar: function()
{ {
var newValue = !padcookie.getPref('hideSidebar'); var newValue = !padcookie.getPref('hideSidebar');
@ -833,6 +851,7 @@ var pad = {
$("#sidebarcheck").toggleClass('sidebarchecked', !newValue).toggleClass('sidebarunchecked', !! newValue); $("#sidebarcheck").toggleClass('sidebarchecked', !newValue).toggleClass('sidebarunchecked', !! newValue);
pad.determineSidebarVisibility(); pad.determineSidebarVisibility();
}, },
*/
handleWidthChange: function() handleWidthChange: function()
{ {
var isFullWidth = padcookie.getPref('fullWidth'); var isFullWidth = padcookie.getPref('fullWidth');
@ -951,3 +970,31 @@ var alertBar = (function()
}; };
return self; return self;
}()); }());
function init() {
return pad.init();
}
var settings = {
LineNumbersDisabled: false
, noColors: false
, useMonospaceFontGlobal: false
, globalUserName: false
, hideQRCode: false
, rtlIsTrue: false
};
pad.settings = settings;
exports.settings = settings;
exports.createCookie = createCookie;
exports.readCookie = readCookie;
exports.randomString = randomString;
exports.getParams = getParams;
exports.getUrlVars = getUrlVars;
exports.savePassword = savePassword;
exports.handshake = handshake;
exports.pad = pad;
exports.init = init;
exports.alertBar = alertBar;

View file

@ -20,6 +20,8 @@
* limitations under the License. * limitations under the License.
*/ */
var padmodals = require('/pad_modals').padmodals;
var padconnectionstatus = (function() var padconnectionstatus = (function()
{ {
@ -85,3 +87,5 @@ var padconnectionstatus = (function()
}; };
return self; return self;
}()); }());
exports.padconnectionstatus = padconnectionstatus;

View file

@ -85,9 +85,12 @@ var padcookie = (function()
var alreadyWarnedAboutNoCookies = false; var alreadyWarnedAboutNoCookies = false;
var inited = false; var inited = false;
var pad = undefined;
var self = { var self = {
init: function(prefsToSet) init: function(prefsToSet, _pad)
{ {
pad = _pad;
var rawCookie = getRawCookie(); var rawCookie = getRawCookie();
if (rawCookie) if (rawCookie)
{ {
@ -126,3 +129,5 @@ var padcookie = (function()
}; };
return self; return self;
}()); }());
exports.padcookie = padcookie;

View file

@ -20,6 +20,7 @@
* limitations under the License. * limitations under the License.
*/ */
var padutils = require('/pad_utils').padutils;
var paddocbar = (function() var paddocbar = (function()
{ {
@ -113,11 +114,14 @@ var paddocbar = (function()
self.renderPassword(); self.renderPassword();
} }
var pad = undefined;
var self = { var self = {
title: null, title: null,
password: null, password: null,
init: function(opts) init: function(opts, _pad)
{ {
pad = _pad;
panels = { panels = {
impexp: { impexp: {
animator: getPanelOpenCloseAnimator("impexp", 160) animator: getPanelOpenCloseAnimator("impexp", 160)
@ -444,6 +448,8 @@ var paddocbar = (function()
}, },
handleResizePage: function() handleResizePage: function()
{ {
// Side-step circular reference. This should be injected.
var padsavedrevs = require('/pad_savedrevs').padsavedrevs;
padsavedrevs.handleResizePage(); padsavedrevs.handleResizePage();
}, },
hideLaterIfNoOtherInteraction: function() hideLaterIfNoOtherInteraction: function()
@ -456,3 +462,5 @@ var paddocbar = (function()
}; };
return self; return self;
}()); }());
exports.paddocbar = paddocbar;

View file

@ -20,6 +20,19 @@
* limitations under the License. * limitations under the License.
*/ */
var padutils = require('/pad_utils').padutils;
var padeditor = require('/pad_editor').padeditor;
var padsavedrevs = require('/pad_savedrevs').padsavedrevs;
function indexOf(array, value) {
for (var i = 0, ii = array.length; i < ii; i++) {
if (array[i] == value) {
return i;
}
}
return -1;
}
var padeditbar = (function() var padeditbar = (function()
{ {
@ -104,17 +117,20 @@ var padeditbar = (function()
{ {
self.toogleDropDown("users"); self.toogleDropDown("users");
} }
else if (cmd == 'settings')
{
self.toogleDropDown("settingsmenu");
}
else if (cmd == 'embed') else if (cmd == 'embed')
{ {
self.setEmbedLinks(); self.setEmbedLinks();
$('#embedinput').focus().select(); $('#linkinput').focus().select();
self.toogleDropDown("embed"); self.toogleDropDown("embed");
} }
else if (cmd == 'import_export') else if (cmd == 'import_export')
{ {
self.toogleDropDown("importexport"); self.toogleDropDown("importexport");
} }
else if (cmd == 'save') else if (cmd == 'save')
{ {
padsavedrevs.saveNow(); padsavedrevs.saveNow();
@ -126,6 +142,7 @@ var padeditbar = (function()
if (cmd == 'bold' || cmd == 'italic' || cmd == 'underline' || cmd == 'strikethrough') ace.ace_toggleAttributeOnSelection(cmd); if (cmd == 'bold' || cmd == 'italic' || cmd == 'underline' || cmd == 'strikethrough') ace.ace_toggleAttributeOnSelection(cmd);
else if (cmd == 'undo' || cmd == 'redo') ace.ace_doUndoRedo(cmd); else if (cmd == 'undo' || cmd == 'redo') ace.ace_doUndoRedo(cmd);
else if (cmd == 'insertunorderedlist') ace.ace_doInsertUnorderedList(); else if (cmd == 'insertunorderedlist') ace.ace_doInsertUnorderedList();
else if (cmd == 'insertorderedlist') ace.ace_doInsertOrderedList();
else if (cmd == 'indent') else if (cmd == 'indent')
{ {
if (!ace.ace_doIndentOutdent(false)) if (!ace.ace_doIndentOutdent(false))
@ -156,15 +173,16 @@ var padeditbar = (function()
}, cmd, true); }, cmd, true);
} }
} }
padeditor.ace.focus(); if(padeditor.ace) padeditor.ace.focus();
}, },
toogleDropDown: function(moduleName) toogleDropDown: function(moduleName)
{ {
var modules = ["embed", "users", "readonly", "importexport"]; var modules = ["settingsmenu", "importexport", "embed", "users"];
//hide all modules //hide all modules
if(moduleName == "none") if(moduleName == "none")
{ {
$("#editbar ul#menu_right > li").removeClass("selected");
for(var i=0;i<modules.length;i++) for(var i=0;i<modules.length;i++)
{ {
//skip the userlist //skip the userlist
@ -181,6 +199,11 @@ var padeditbar = (function()
} }
else else
{ {
var nth_child = indexOf(modules, moduleName) + 1;
if (nth_child > 0 && nth_child <= 3) {
$("#editbar ul#menu_right li:not(:nth-child(" + nth_child + "))").removeClass("selected");
$("#editbar ul#menu_right li:nth-child(" + nth_child + ")").toggleClass("selected");
}
//hide all modules that are not selected and show the selected one //hide all modules that are not selected and show the selected one
for(var i=0;i<modules.length;i++) for(var i=0;i<modules.length;i++)
{ {
@ -216,16 +239,18 @@ var padeditbar = (function()
var readonlyLink = basePath + "/ro/" + clientVars.readOnlyId; var readonlyLink = basePath + "/ro/" + clientVars.readOnlyId;
$('#embedinput').val("<iframe src='" + readonlyLink + "?showControls=true&showChat=true&showLineNumbers=true&useMonospaceFont=false' width=600 height=400>"); $('#embedinput').val("<iframe src='" + readonlyLink + "?showControls=true&showChat=true&showLineNumbers=true&useMonospaceFont=false' width=600 height=400>");
$('#linkinput').val(readonlyLink); $('#linkinput').val(readonlyLink);
$('#embedreadonlyqr').attr("src","https://chart.googleapis.com/chart?chs=200x200&cht=qr&chld=H|0&chl=" + readonlyLink); $('#embedreadonlyqr').attr("src","https://chart.googleapis.com/chart?chs=200x200&cht=qr&chld=|0&chl=" + readonlyLink);
} }
else else
{ {
var padurl = window.location.href.split("?")[0]; var padurl = window.location.href.split("?")[0];
$('#embedinput').val("<iframe src='" + padurl + "?showControls=true&showChat=true&showLineNumbers=true&useMonospaceFont=false' width=600 height=400>"); $('#embedinput').val("<iframe src='" + padurl + "?showControls=true&showChat=true&showLineNumbers=true&useMonospaceFont=false' width=600 height=400>");
$('#linkinput').val(padurl); $('#linkinput').val(padurl);
$('#embedreadonlyqr').attr("src","https://chart.googleapis.com/chart?chs=200x200&cht=qr&chld=H|0&chl=" + padurl); $('#embedreadonlyqr').attr("src","https://chart.googleapis.com/chart?chs=200x200&cht=qr&chld=|0&chl=" + padurl);
} }
} }
}; };
return self; return self;
}()); }());
exports.padeditbar = padeditbar;

View file

@ -20,15 +20,23 @@
* limitations under the License. * limitations under the License.
*/ */
var padcookie = require('/pad_cookie').padcookie;
var padutils = require('/pad_utils').padutils;
var padeditor = (function() var padeditor = (function()
{ {
var Ace2Editor = undefined;
var pad = undefined;
var settings = undefined;
var self = { var self = {
ace: null, ace: null,
// this is accessed directly from other files // this is accessed directly from other files
viewZoom: 100, viewZoom: 100,
init: function(readyFunc, initialViewOptions) init: function(readyFunc, initialViewOptions, _pad)
{ {
Ace2Editor = require('/ace').Ace2Editor;
pad = _pad;
settings = pad.settings;
function aceReady() function aceReady()
{ {
@ -61,14 +69,13 @@ var padeditor = (function()
}); });
padutils.bindCheckboxChange($("#options-colorscheck"), function() padutils.bindCheckboxChange($("#options-colorscheck"), function()
{ {
padcookie.setPref('showAuthorshipColors', padutils.getCheckbox("#options-colorscheck"));
pad.changeViewOption('showAuthorColors', padutils.getCheckbox("#options-colorscheck")); pad.changeViewOption('showAuthorColors', padutils.getCheckbox("#options-colorscheck"));
}); });
$("#viewfontmenu").change(function() $("#viewfontmenu").change(function()
{ {
pad.changeViewOption('useMonospaceFont', $("#viewfontmenu").val() == 'monospace'); pad.changeViewOption('useMonospaceFont', $("#viewfontmenu").val() == 'monospace');
}); });
noColors = !noColors; // Inversed so we can pass it to showauthorcolors
}, },
setViewOptions: function(newOptions) setViewOptions: function(newOptions)
{ {
@ -79,6 +86,11 @@ var padeditor = (function()
if (value == "false") return false; if (value == "false") return false;
return defaultValue; return defaultValue;
} }
self.ace.setProperty("showsauthorcolors", !settings.noColors);
self.ace.setProperty("rtlIsTrue", settings.rtlIsTrue);
var v; var v;
v = getOption('showLineNumbers', true); v = getOption('showLineNumbers', true);
@ -92,10 +104,6 @@ var padeditor = (function()
v = getOption('useMonospaceFont', false); v = getOption('useMonospaceFont', false);
self.ace.setProperty("textface", (v ? "monospace" : "Arial, sans-serif")); self.ace.setProperty("textface", (v ? "monospace" : "Arial, sans-serif"));
$("#viewfontmenu").val(v ? "monospace" : "normal"); $("#viewfontmenu").val(v ? "monospace" : "normal");
self.ace.setProperty("showsauthorcolors", noColors);
self.ace.setProperty("rtlIsTrue", rtlIsTrue);
}, },
initViewZoom: function() initViewZoom: function()
{ {
@ -132,6 +140,7 @@ var padeditor = (function()
if (self.ace) if (self.ace)
{ {
self.ace.destroy(); self.ace.destroy();
self.ace = null;
} }
}, },
disable: function() disable: function()
@ -150,3 +159,5 @@ var padeditor = (function()
}; };
return self; return self;
}()); }());
exports.padeditor = padeditor;

View file

@ -20,6 +20,7 @@
* limitations under the License. * limitations under the License.
*/ */
var paddocbar = require('/pad_docbar').paddocbar;
var padimpexp = (function() var padimpexp = (function()
{ {
@ -94,11 +95,6 @@ var padimpexp = (function()
}, 0); }, 0);
$('#importarrow').stop(true, true).hide(); $('#importarrow').stop(true, true).hide();
$('#importstatusball').show(); $('#importstatusball').show();
$("#import .importframe").load(function()
{
importDone();
});
} }
return ret; return ret;
} }
@ -106,8 +102,6 @@ var padimpexp = (function()
function importFailed(msg) function importFailed(msg)
{ {
importErrorMessage(msg); importErrorMessage(msg);
importDone();
addImportFrames();
} }
function importDone() function importDone()
@ -119,6 +113,7 @@ var padimpexp = (function()
}, 0); }, 0);
$('#importstatusball').hide(); $('#importstatusball').hide();
importClearTimeout(); importClearTimeout();
addImportFrames();
} }
function importClearTimeout() function importClearTimeout()
@ -130,11 +125,19 @@ var padimpexp = (function()
} }
} }
function importErrorMessage(msg) function importErrorMessage(status)
{ {
var msg="";
if(status === "convertFailed"){
msg = "We were not able to import this file. Please use a different document format or copy paste manually";
} else if(status === "uploadFailed"){
msg = "The upload failed, please try again";
}
function showError(fade) function showError(fade)
{ {
$('#importmessagefail').html('<strong style="color: red">Import failed:</strong> ' + (msg || 'Please try a different file.'))[(fade ? "fadeIn" : "show")](); $('#importmessagefail').html('<strong style="color: red">Import failed:</strong> ' + (msg || 'Please copy paste'))[(fade ? "fadeIn" : "show")]();
} }
if ($('#importexport .importmessage').is(':visible')) if ($('#importexport .importmessage').is(':visible'))
@ -174,39 +177,6 @@ var padimpexp = (function()
importDone(); importDone();
} }
function importApplicationSuccessful(data, textStatus)
{
if (data.substr(0, 2) == "ok")
{
if ($('#importexport .importmessage').is(':visible'))
{
$('#importexport .importmessage').hide();
}
$('#importmessagesuccess').html('<strong style="color: green">Import successful!</strong>').show();
$('#importformfilediv').hide();
window.setTimeout(function()
{
$('#importmessagesuccess').fadeOut("slow", function()
{
$('#importformfilediv').show();
});
if (hidePanelCall)
{
hidePanelCall();
}
}, 3000);
}
else if (data.substr(0, 4) == "fail")
{
importErrorMessage("Couldn't update pad contents. This can happen if your web browser has \"cookies\" disabled.");
}
else if (data.substr(0, 4) == "msg:")
{
importErrorMessage(data.substr(4));
}
importDone();
}
///// export ///// export
function cantExport() function cantExport()
@ -233,14 +203,22 @@ var padimpexp = (function()
} }
///// /////
var pad = undefined;
var self = { var self = {
init: function() init: function(_pad)
{ {
pad = _pad;
//get /p/padname
var pad_root_path = new RegExp(/.*\/p\/[^\/]+/).exec(document.location.pathname)
//get http://example.com/p/padname
var pad_root_url = document.location.href.replace(document.location.pathname, pad_root_path)
// build the export links // build the export links
$("#exporthtmla").attr("href", document.location.pathname + "/export/html"); $("#exporthtmla").attr("href", pad_root_path + "/export/html");
$("#exportplaina").attr("href", document.location.pathname + "/export/txt"); $("#exportplaina").attr("href", pad_root_path + "/export/txt");
$("#exportwordlea").attr("href", document.location.pathname + "/export/wordle"); $("#exportwordlea").attr("href", pad_root_path + "/export/wordle");
$("#exportdokuwikia").attr("href", document.location.pathname + "/export/dokuwiki"); $("#exportdokuwikia").attr("href", pad_root_path + "/export/dokuwiki");
//hide stuff thats not avaible if abiword is disabled //hide stuff thats not avaible if abiword is disabled
if(clientVars.abiwordAvailable == "no") if(clientVars.abiwordAvailable == "no")
@ -248,29 +226,28 @@ var padimpexp = (function()
$("#exportworda").remove(); $("#exportworda").remove();
$("#exportpdfa").remove(); $("#exportpdfa").remove();
$("#exportopena").remove(); $("#exportopena").remove();
$("#importexport").css({"height":"115px"}); $(".importformdiv").remove();
$("#importexportline").css({"height":"115px"}); $("#import").html("Import is not available. To enable import please install abiword");
$("#import").html("Import is not available");
} }
else if(clientVars.abiwordAvailable == "withoutPDF") else if(clientVars.abiwordAvailable == "withoutPDF")
{ {
$("#exportpdfa").remove(); $("#exportpdfa").remove();
$("#exportworda").attr("href", document.location.pathname + "/export/doc"); $("#exportworda").attr("href", pad_root_path + "/export/doc");
$("#exportopena").attr("href", document.location.pathname + "/export/odt"); $("#exportopena").attr("href", pad_root_path + "/export/odt");
$("#importexport").css({"height":"142px"}); $("#importexport").css({"height":"142px"});
$("#importexportline").css({"height":"142px"}); $("#importexportline").css({"height":"142px"});
$("#importform").get(0).setAttribute('action', document.location.href + "/import"); $("#importform").attr('action', pad_root_url + "/import");
} }
else else
{ {
$("#exportworda").attr("href", document.location.pathname + "/export/doc"); $("#exportworda").attr("href", pad_root_path + "/export/doc");
$("#exportpdfa").attr("href", document.location.pathname + "/export/pdf"); $("#exportpdfa").attr("href", pad_root_path + "/export/pdf");
$("#exportopena").attr("href", document.location.pathname + "/export/odt"); $("#exportopena").attr("href", pad_root_path + "/export/odt");
$("#importform").get(0).setAttribute('action', document.location.pathname + "/import"); $("#importform").attr('action', pad_root_path + "/import");
} }
$("#impexp-close").click(function() $("#impexp-close").click(function()
@ -283,16 +260,14 @@ var padimpexp = (function()
$('#importform').submit(fileInputSubmit); $('#importform').submit(fileInputSubmit);
$('.disabledexport').click(cantExport); $('.disabledexport').click(cantExport);
}, },
handleFrameCall: function(callName, argsArray) handleFrameCall: function(status)
{ {
if (callName == 'importFailed') if (status !== "ok")
{ {
importFailed(argsArray[0]); importFailed(status);
}
else if (callName == 'importSuccessful')
{
importSuccessful(argsArray[0]);
} }
importDone();
}, },
disable: function() disable: function()
{ {
@ -308,7 +283,7 @@ var padimpexp = (function()
}, },
export2Wordle: function() export2Wordle: function()
{ {
var padUrl = document.location.href + "/export/txt"; var padUrl = $('#exportwordlea').attr('href').replace(/\/wordle$/, '/txt')
$.get(padUrl, function(data) $.get(padUrl, function(data)
{ {
@ -320,3 +295,5 @@ var padimpexp = (function()
}; };
return self; return self;
}()); }());
exports.padimpexp = padimpexp;

View file

@ -20,6 +20,9 @@
* limitations under the License. * limitations under the License.
*/ */
var padutils = require('/pad_utils').padutils;
var paddocbar = require('/pad_docbar').paddocbar;
var padmodals = (function() var padmodals = (function()
{ {
@ -70,9 +73,12 @@ var padmodals = (function()
clearShareBoxTo(); clearShareBoxTo();
} }
var pad = undefined;
var self = { var self = {
init: function() init: function(_pad)
{ {
pad = _pad;
self.initFeedback(); self.initFeedback();
self.initShareBox(); self.initShareBox();
}, },
@ -364,3 +370,5 @@ var padmodals = (function()
}; };
return self; return self;
}()); }());
exports.padmodals = padmodals;

View file

@ -20,6 +20,8 @@
* limitations under the License. * limitations under the License.
*/ */
var padutils = require('/pad_utils').padutils;
var paddocbar = require('/pad_docbar').paddocbar;
var padsavedrevs = (function() var padsavedrevs = (function()
{ {
@ -39,7 +41,7 @@ var padsavedrevs = (function()
box.find(".srauthor").html("by " + padutils.escapeHtml(revisionInfo.savedBy)); box.find(".srauthor").html("by " + padutils.escapeHtml(revisionInfo.savedBy));
var viewLink = '/ep/pad/view/' + pad.getPadId() + '/' + revisionInfo.id; var viewLink = '/ep/pad/view/' + pad.getPadId() + '/' + revisionInfo.id;
box.find(".srview").attr('href', viewLink); box.find(".srview").attr('href', viewLink);
var restoreLink = 'javascript:void padsavedrevs.restoreRevision(' + rnum + ');'; var restoreLink = 'javascript:void(require('+JSON.stringify(module.id)+').padsavedrevs.restoreRevision(' + JSON.stringify(rnum) + ');';
box.find(".srrestore").attr('href', restoreLink); box.find(".srrestore").attr('href', restoreLink);
box.find(".srname").click(function(evt) box.find(".srname").click(function(evt)
{ {
@ -345,9 +347,11 @@ var padsavedrevs = (function()
$(document).unbind('mouseup', clearScrollRepeatTimer); $(document).unbind('mouseup', clearScrollRepeatTimer);
} }
var pad = undefined;
var self = { var self = {
init: function(initialRevisions) init: function(initialRevisions, _pad)
{ {
pad = _pad;
self.newRevisionList(initialRevisions, true); self.newRevisionList(initialRevisions, true);
$("#savedrevs-savenow").click(function() $("#savedrevs-savenow").click(function()
@ -518,3 +522,5 @@ var padsavedrevs = (function()
}; };
return self; return self;
}()); }());
exports.padsavedrevs = padsavedrevs;

View file

@ -20,6 +20,8 @@
* limitations under the License. * limitations under the License.
*/ */
var padutils = require('/pad_utils').padutils;
var myUserInfo = {}; var myUserInfo = {};
var colorPickerOpen = false; var colorPickerOpen = false;
@ -460,9 +462,12 @@ var paduserlist = (function()
return true; return true;
}, 1000); }, 1000);
var pad = undefined;
var self = { var self = {
init: function(myInitialUserInfo) init: function(myInitialUserInfo, _pad)
{ {
pad = _pad;
self.setMyUserInfo(myInitialUserInfo); self.setMyUserInfo(myInitialUserInfo);
$("#otheruserstable tr").remove(); $("#otheruserstable tr").remove();
@ -652,7 +657,7 @@ var paduserlist = (function()
if (box.length == 0) if (box.length == 0)
{ {
// make guest prompt box // make guest prompt box
box = $('<div id="guestprompt-' + encodedUserId + '" class="guestprompt"><div class="choices"><a href="javascript:void(paduserlist.answerGuestPrompt(\'' + encodedUserId + '\',false))">Deny</a> <a href="javascript:void(paduserlist.answerGuestPrompt(\'' + encodedUserId + '\',true))">Approve</a></div><div class="guestname"><strong>Guest:</strong> ' + padutils.escapeHtml(displayName) + '</div></div>'); box = $('<div id="'+padutils.escapeHtml('guestprompt-' + encodedUserId) + '" class="guestprompt"><div class="choices"><a href="' + padutils.escapeHtml('javascript:void(require('+JSON.stringify(module.id)+').paduserlist.answerGuestPrompt(' + JSON.stringify(encodedUserId) + ',false))')+'">Deny</a> <a href="' + padutils.escapeHtml('javascript:void(require('+JSON.stringify(module.id)+').paduserlist.answerGuestPrompt(' + JSON.stringify(encodedUserId) + ',true))') + '">Approve</a></div><div class="guestname"><strong>Guest:</strong> ' + padutils.escapeHtml(displayName) + '</div></div>');
$("#guestprompts").append(box); $("#guestprompts").append(box);
} }
else else
@ -805,3 +810,5 @@ function showColorPicker()
$($("#colorpickerswatches li")[myUserInfo.colorId]).addClass("picked"); //seems weird $($("#colorpickerswatches li")[myUserInfo.colorId]).addClass("picked"); //seems weird
} }
} }
exports.paduserlist = paduserlist;

View file

@ -20,13 +20,62 @@
* limitations under the License. * limitations under the License.
*/ */
var Security = require('/security');
/**
* Generates a random String with the given length. Is needed to generate the Author, Group, readonly, session Ids
*/
function randomString(len)
{
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var randomstring = '';
len = len || 20
for (var i = 0; i < len; i++)
{
var rnum = Math.floor(Math.random() * chars.length);
randomstring += chars.substring(rnum, rnum + 1);
}
return randomstring;
}
function createCookie(name, value, days, path)
{
if (days)
{
var date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
var expires = "; expires=" + date.toGMTString();
}
else var expires = "";
if(!path)
path = "/";
document.cookie = name + "=" + value + expires + "; path=" + path;
}
function readCookie(name)
{
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++)
{
var c = ca[i];
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
}
return null;
}
var padutils = { var padutils = {
escapeHtml: function(x) escapeHtml: function(x)
{ {
return String(x).replace(/\</g, '&lt;').replace(/\>/g, '&gt;'); return Security.escapeHTML(String(x));
}, },
uniqueId: function() uniqueId: function()
{ {
var pad = require('/pad').pad; // Sidestep circular dependency
function encodeNum(n, width) function encodeNum(n, width)
{ {
// returns string that is exactly 'width' chars, padding with zeros // returns string that is exactly 'width' chars, padding with zeros
@ -102,24 +151,6 @@ var padutils = {
var x = ua.split(' ')[0]; var x = ua.split(' ')[0];
return clean(x); return clean(x);
}, },
// "func" is a function over 0..(numItems-1) that is monotonically
// "increasing" with index (false, then true). Finds the boundary
// between false and true, a number between 0 and numItems inclusive.
binarySearch: function(numItems, func)
{
if (numItems < 1) return 0;
if (func(0)) return 0;
if (!func(numItems - 1)) return numItems;
var low = 0; // func(low) is always false
var high = numItems - 1; // func(high) is always true
while ((high - low) > 1)
{
var x = Math.floor((low + high) / 2); // x != low, x != high
if (func(x)) high = x;
else low = x;
}
return high;
},
// e.g. "Thu Jun 18 2009 13:09" // e.g. "Thu Jun 18 2009 13:09"
simpleDateTime: function(date) simpleDateTime: function(date)
{ {
@ -169,7 +200,7 @@ var padutils = {
{ {
if (i > idx) if (i > idx)
{ {
pieces.push(padutils.escapeHtml(text.substring(idx, i))); pieces.push(Security.escapeHTML(text.substring(idx, i)));
idx = i; idx = i;
} }
} }
@ -180,7 +211,7 @@ var padutils = {
var startIndex = urls[j][0]; var startIndex = urls[j][0];
var href = urls[j][1]; var href = urls[j][1];
advanceTo(startIndex); advanceTo(startIndex);
pieces.push('<a ', (target ? 'target="' + target + '" ' : ''), 'href="', href.replace(/\"/g, '&quot;'), '">'); pieces.push('<a ', (target ? 'target="' + Security.escapeHTMLAttribute(target) + '" ' : ''), 'href="', Security.escapeHTMLAttribute(href), '">');
advanceTo(startIndex + href.length); advanceTo(startIndex + href.length);
pieces.push('</a>'); pieces.push('</a>');
} }
@ -219,6 +250,7 @@ var padutils = {
}, },
timediff: function(d) timediff: function(d)
{ {
var pad = require('/pad').pad; // Sidestep circular dependency
function format(n, word) function format(n, word)
{ {
n = Math.round(n); n = Math.round(n);
@ -468,8 +500,11 @@ var padutils = {
} }
}; };
var globalExceptionHandler = undefined;
function setupGlobalExceptionHandler() {
//send javascript errors to the server //send javascript errors to the server
window.onerror = function test (msg, url, linenumber) if (!globalExceptionHandler) {
globalExceptionHandler = function test (msg, url, linenumber)
{ {
var errObj = {errorInfo: JSON.stringify({msg: msg, url: url, linenumber: linenumber, userAgent: navigator.userAgent})}; var errObj = {errorInfo: JSON.stringify({msg: msg, url: url, linenumber: linenumber, userAgent: navigator.userAgent})};
var loc = document.location; var loc = document.location;
@ -479,3 +514,15 @@ window.onerror = function test (msg, url, linenumber)
return false; return false;
}; };
window.onerror = globalExceptionHandler;
}
}
padutils.setupGlobalExceptionHandler = setupGlobalExceptionHandler;
padutils.binarySearch = require('/ace2_common').binarySearch;
exports.randomString = randomString;
exports.createCookie = createCookie;
exports.readCookie = readCookie;
exports.padutils = padutils;

View file

@ -4,10 +4,11 @@
* TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
*/ */
plugins = { var plugins = {
callHook: function(hookName, args) callHook: function(hookName, args)
{ {
var hook = clientVars.hooks[hookName]; var global = (function () {return this}());
var hook = ((global.clientVars || {}).hooks || {})[hookName];
if (hook === undefined) return []; if (hook === undefined) return [];
var res = []; var res = [];
for (var i = 0, N = hook.length; i < N; i++) for (var i = 0, N = hook.length; i < N; i++)
@ -24,9 +25,13 @@ plugins = {
if (sep == undefined) sep = ''; if (sep == undefined) sep = '';
if (pre == undefined) pre = ''; if (pre == undefined) pre = '';
if (post == undefined) post = ''; if (post == undefined) post = '';
return plugins.callHook(hookName, args).map(function(x) var newCallhooks = [];
{ var callhooks = plugins.callHook(hookName, args);
return pre + x + post for (var i = 0, ii = callhooks.length; i < ii; i++) {
}).join(sep || ""); newCallhooks[i] = pre + callhooks[i] + post;
}
return newCallhooks.join(sep || "");
} }
}; };
exports.plugins = plugins;

421
static/js/prefixfree.js Normal file
View file

@ -0,0 +1,421 @@
/**
* StyleFix 1.0.1
* @author Lea Verou
* MIT license
*/
(function(){
if(!window.addEventListener) {
return;
}
var self = window.StyleFix = {
link: function(link) {
try {
// Ignore stylesheets with data-noprefix attribute as well as alternate stylesheets
if(link.rel !== 'stylesheet' || !link.sheet.cssRules || link.hasAttribute('data-noprefix')) {
return;
}
}
catch(e) {
return;
}
if(link.href == "data:text/css,"){
return false;
}
var url = link.href || link.getAttribute('data-href'),
base = url.replace(/[^\/]+$/, ''),
parent = link.parentNode,
xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onreadystatechange = function() {
if(xhr.readyState === 4) {
var css = xhr.responseText;
if(css && link.parentNode) {
css = self.fix(css, true, link);
// Convert relative URLs to absolute, if needed
if(base) {
css = css.replace(/url\((?:'|")?(.+?)(?:'|")?\)/gi, function($0, url) {
if(!/^([a-z]{3,10}:|\/|#)/i.test(url)) { // If url not absolute & not a hash
// May contain sequences like /../ and /./ but those DO work
return 'url("' + base + url + '")';
}
return $0;
});
// behavior URLs shoudnt be converted (Issue #19)
css = css.replace(RegExp('\\b(behavior:\\s*?url\\(\'?"?)' + base, 'gi'), '$1');
}
var style = document.createElement('style');
style.textContent = css;
style.media = link.media;
style.disabled = link.disabled;
style.setAttribute('data-href', link.getAttribute('href'));
parent.insertBefore(style, link);
parent.removeChild(link);
}
}
};
xhr.send(null);
link.setAttribute('data-inprogress', '');
},
styleElement: function(style) {
var disabled = style.disabled;
style.textContent = self.fix(style.textContent, true, style);
style.disabled = disabled;
},
styleAttribute: function(element) {
var css = element.getAttribute('style');
css = self.fix(css, false, element);
element.setAttribute('style', css);
},
process: function() {
// Linked stylesheets
$('link[rel="stylesheet"]:not([data-inprogress])').forEach(StyleFix.link);
// Inline stylesheets
$('style').forEach(StyleFix.styleElement);
// Inline styles
$('[style]').forEach(StyleFix.styleAttribute);
},
register: function(fixer, index) {
(self.fixers = self.fixers || [])
.splice(index === undefined? self.fixers.length : index, 0, fixer);
},
fix: function(css, raw) {
for(var i=0; i<self.fixers.length; i++) {
css = self.fixers[i](css, raw) || css;
}
return css;
},
camelCase: function(str) {
return str.replace(/-([a-z])/g, function($0, $1) { return $1.toUpperCase(); }).replace('-','');
},
deCamelCase: function(str) {
return str.replace(/[A-Z]/g, function($0) { return '-' + $0.toLowerCase() });
}
};
/**************************************
* Process styles
**************************************/
(function(){
setTimeout(function(){
$('link[rel="stylesheet"]').forEach(StyleFix.link);
}, 10);
document.addEventListener('DOMContentLoaded', StyleFix.process, false);
})();
function $(expr, con) {
return [].slice.call((con || document).querySelectorAll(expr));
}
})();
/**
* PrefixFree 1.0.4
* @author Lea Verou
* MIT license
*/
(function(root, undefined){
if(!window.StyleFix || !window.getComputedStyle) {
return;
}
var self = window.PrefixFree = {
prefixCSS: function(css, raw) {
var prefix = self.prefix;
function fix(what, before, after, replacement) {
what = self[what];
if(what.length) {
var regex = RegExp(before + '(' + what.join('|') + ')' + after, 'gi');
css = css.replace(regex, replacement);
}
}
fix('functions', '(\\s|:|,)', '\\s*\\(', '$1' + prefix + '$2(');
fix('keywords', '(\\s|:)', '(\\s|;|\\}|$)', '$1' + prefix + '$2$3');
fix('properties', '(^|\\{|\\s|;)', '\\s*:', '$1' + prefix + '$2:');
// Prefix properties *inside* values (issue #8)
if (self.properties.length) {
var regex = RegExp('\\b(' + self.properties.join('|') + ')(?!:)', 'gi');
fix('valueProperties', '\\b', ':(.+?);', function($0) {
return $0.replace(regex, prefix + "$1")
});
}
if(raw) {
fix('selectors', '', '\\b', self.prefixSelector);
fix('atrules', '@', '\\b', '@' + prefix + '$1');
}
// Fix double prefixing
css = css.replace(RegExp('-' + prefix, 'g'), '-');
return css;
},
// Warning: prefixXXX functions prefix no matter what, even if the XXX is supported prefix-less
prefixSelector: function(selector) {
return selector.replace(/^:{1,2}/, function($0) { return $0 + self.prefix })
},
prefixProperty: function(property, camelCase) {
var prefixed = self.prefix + property;
return camelCase? StyleFix.camelCase(prefixed) : prefixed;
}
};
/**************************************
* Properties
**************************************/
(function() {
var prefixes = {},
properties = [],
shorthands = {},
style = getComputedStyle(document.documentElement, null),
dummy = document.createElement('div').style;
// Why are we doing this instead of iterating over properties in a .style object? Cause Webkit won't iterate over those.
var iterate = function(property) {
if(property.charAt(0) === '-') {
properties.push(property);
var parts = property.split('-'),
prefix = parts[1];
// Count prefix uses
prefixes[prefix] = ++prefixes[prefix] || 1;
// This helps determining shorthands
while(parts.length > 3) {
parts.pop();
var shorthand = parts.join('-');
if(supported(shorthand) && properties.indexOf(shorthand) === -1) {
properties.push(shorthand);
}
}
}
},
supported = function(property) {
return StyleFix.camelCase(property) in dummy;
}
// Some browsers have numerical indices for the properties, some don't
if(style.length > 0) {
for(var i=0; i<style.length; i++) {
iterate(style[i])
}
}
else {
for(var property in style) {
iterate(StyleFix.deCamelCase(property));
}
}
// Find most frequently used prefix
var highest = {uses:0};
for(var prefix in prefixes) {
var uses = prefixes[prefix];
if(highest.uses < uses) {
highest = {prefix: prefix, uses: uses};
}
}
self.prefix = '-' + highest.prefix + '-';
self.Prefix = StyleFix.camelCase(self.prefix);
self.properties = [];
// Get properties ONLY supported with a prefix
for(var i=0; i<properties.length; i++) {
var property = properties[i];
if(property.indexOf(self.prefix) === 0) { // we might have multiple prefixes, like Opera
var unprefixed = property.slice(self.prefix.length);
if(!supported(unprefixed)) {
self.properties.push(unprefixed);
}
}
}
// IE fix
if(self.Prefix == 'Ms'
&& !('transform' in dummy)
&& !('MsTransform' in dummy)
&& ('msTransform' in dummy)) {
self.properties.push('transform', 'transform-origin');
}
self.properties.sort();
})();
/**************************************
* Values
**************************************/
(function() {
// Values that might need prefixing
var functions = {
'linear-gradient': {
property: 'backgroundImage',
params: 'red, teal'
},
'calc': {
property: 'width',
params: '1px + 5%'
},
'element': {
property: 'backgroundImage',
params: '#foo'
}
};
functions['repeating-linear-gradient'] =
functions['repeating-radial-gradient'] =
functions['radial-gradient'] =
functions['linear-gradient'];
var keywords = {
'initial': 'color',
'zoom-in': 'cursor',
'zoom-out': 'cursor',
'box': 'display',
'flexbox': 'display',
'inline-flexbox': 'display'
};
self.functions = [];
self.keywords = [];
var style = document.createElement('div').style;
function supported(value, property) {
style[property] = '';
style[property] = value;
return !!style[property];
}
for (var func in functions) {
var test = functions[func],
property = test.property,
value = func + '(' + test.params + ')';
if (!supported(value, property)
&& supported(self.prefix + value, property)) {
// It's supported, but with a prefix
self.functions.push(func);
}
}
for (var keyword in keywords) {
var property = keywords[keyword];
if (!supported(keyword, property)
&& supported(self.prefix + keyword, property)) {
// It's supported, but with a prefix
self.keywords.push(keyword);
}
}
})();
/**************************************
* Selectors and @-rules
**************************************/
(function() {
var
selectors = {
':read-only': null,
':read-write': null,
':any-link': null,
'::selection': null
},
atrules = {
'keyframes': 'name',
'viewport': null,
'document': 'regexp(".")'
};
self.selectors = [];
self.atrules = [];
var style = root.appendChild(document.createElement('style'));
function supported(selector) {
style.textContent = selector + '{}'; // Safari 4 has issues with style.innerHTML
return !!style.sheet.cssRules.length;
}
for(var selector in selectors) {
var test = selector + (selectors[selector]? '(' + selectors[selector] + ')' : '');
if(!supported(test) && supported(self.prefixSelector(test))) {
self.selectors.push(selector);
}
}
for(var atrule in atrules) {
var test = atrule + ' ' + (atrules[atrule] || '');
if(!supported('@' + test) && supported('@' + self.prefix + test)) {
self.atrules.push(atrule);
}
}
root.removeChild(style);
})();
// Properties that accept properties as their value
self.valueProperties = [
'transition',
'transition-property'
]
// Add class for current prefix
root.className += ' ' + self.prefix;
StyleFix.register(self.prefixCSS);
})(document.documentElement);

54
static/js/security.js Normal file
View file

@ -0,0 +1,54 @@
/**
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var HTML_ENTITY_MAP = {
'&': '&amp;'
, '<': '&lt;'
, '>': '&gt;'
, '"': '&quot;'
, "'": '&#x27;'
, '/': '&#x2F;'
};
// OSWASP Guidlines: &, <, >, ", ' plus forward slash.
var HTML_CHARACTERS_EXPRESSION = /[&"'<>\/]/g;
function escapeHTML(text) {
return text && text.replace(HTML_CHARACTERS_EXPRESSION, function (c) {
return HTML_ENTITY_MAP[c] || c;
});
}
// OSWASP Guidlines: escape all non alphanumeric characters in ASCII space.
var HTML_ATTRIBUTE_CHARACTERS_EXPRESSION =
/[\x00-\x2F\x3A-\x40\5B-\x60\x7B-\xFF]/g;
function escapeHTMLAttribute(text) {
return text && text.replace(HTML_ATTRIBUTE_CHARACTERS_EXPRESSION, function (c) {
return "&#x" + ('00' + c.charCodeAt(0).toString(16)).slice(-2) + ";";
});
};
// OSWASP Guidlines: escape all non alphanumeric characters in ASCII space.
var JAVASCRIPT_CHARACTERS_EXPRESSION =
/[\x00-\x2F\x3A-\x40\5B-\x60\x7B-\xFF]/g;
function escapeJavaScriptData(text) {
return text && text.replace(JAVASCRIPT_CHARACTERS_EXPRESSION, function (c) {
return "\\x" + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
});
}
exports.escapeHTML = escapeHTML;
exports.escapeHTMLAttribute = escapeHTMLAttribute;
exports.escapeJavaScriptData = escapeJavaScriptData;

View file

@ -21,7 +21,7 @@
*/ */
var noop = require('./ace2_common').noop;
function newSkipList() function newSkipList()
@ -41,9 +41,6 @@ function newSkipList()
}; };
} }
function noop()
{}
// if there are N elements in the skiplist, "start" is element -1 and "end" is element N // if there are N elements in the skiplist, "start" is element -1 and "end" is element N
var start = { var start = {
key: null, key: null,
@ -488,3 +485,5 @@ that is a string.
} }
return self; return self;
} }
exports.newSkipList = newSkipList;

154
static/js/timeslider.js Normal file
View file

@ -0,0 +1,154 @@
/**
* This code is mostly from the old Etherpad. Please help us to comment this code.
* This helps other people to understand this code better and helps them to improve it.
* TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
*/
/**
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// These jQuery things should create local references, but for now `require()`
// assigns to the global `$` and augments it with plugins.
require('/jquery');
JSON = require('/json2');
require('/undo-xpopup');
var createCookie = require('/pad_utils').createCookie;
var readCookie = require('/pad_utils').readCookie;
var randomString = require('/pad_utils').randomString;
var socket, token, padId, export_links;
function init() {
$(document).ready(function ()
{
// start the custom js
if (typeof customStart == "function") customStart();
//get the padId out of the url
var urlParts= document.location.pathname.split("/");
padId = decodeURIComponent(urlParts[urlParts.length-2]);
//set the title
document.title = padId.replace(/_+/g, ' ') + " | " + document.title;
//ensure we have a token
token = readCookie("token");
if(token == null)
{
token = "t." + randomString();
createCookie("token", token, 60);
}
var loc = document.location;
//get the correct port
var port = loc.port == "" ? (loc.protocol == "https:" ? 443 : 80) : loc.port;
//create the url
var url = loc.protocol + "//" + loc.hostname + ":" + port + "/";
//find out in which subfolder we are
var resource = loc.pathname.substr(1,loc.pathname.indexOf("/p/")) + "socket.io";
//build up the socket io connection
socket = io.connect(url, {resource: resource});
//send the ready message once we're connected
socket.on('connect', function()
{
sendSocketMsg("CLIENT_READY", {});
});
//route the incoming messages
socket.on('message', function(message)
{
if(window.console) console.log(message);
if(message.type == "CLIENT_VARS")
{
handleClientVars(message);
}
else if(message.type == "CHANGESET_REQ")
{
changesetLoader.handleSocketResponse(message);
}
else if(message.accessStatus)
{
$("body").html("<h2>You have no permission to access this pad</h2>")
}
});
//get all the export links
export_links = $('#export > .exportlink')
if(document.referrer.length > 0 && document.referrer.substring(document.referrer.lastIndexOf("/")-1,document.referrer.lastIndexOf("/")) === "p") {
$("#returnbutton").attr("href", document.referrer);
} else {
$("#returnbutton").attr("href", document.location.href.substring(0,document.location.href.lastIndexOf("/")));
}
});
}
//sends a message over the socket
function sendSocketMsg(type, data)
{
var sessionID = readCookie("sessionID");
var password = readCookie("password");
var msg = { "component" : "timeslider",
"type": type,
"data": data,
"padId": padId,
"token": token,
"sessionID": sessionID,
"password": password,
"protocolVersion": 2};
socket.json.send(msg);
}
var fireWhenAllScriptsAreLoaded = [];
var BroadcastSlider, changesetLoader;
function handleClientVars(message)
{
//save the client Vars
clientVars = message.data;
//load all script that doesn't work without the clientVars
BroadcastSlider = require('/broadcast_slider').loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded);
require('/broadcast_revisions').loadBroadcastRevisionsJS();
changesetLoader = require('/broadcast').loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, BroadcastSlider);
//initialize export ui
require('/pad_impexp').padimpexp.init();
//change export urls when the slider moves
var export_rev_regex = /(\/\d+)?\/export/
BroadcastSlider.onSlider(function(revno)
{
export_links.each(function()
{
this.setAttribute('href', this.href.replace(export_rev_regex, '/' + revno + '/export'));
});
});
//fire all start functions of these scripts, formerly fired with window.load
for(var i=0;i < fireWhenAllScriptsAreLoaded.length;i++)
{
fireWhenAllScriptsAreLoaded[i]();
}
}
exports.init = init;

View file

@ -20,8 +20,10 @@
* limitations under the License. * limitations under the License.
*/ */
var Changeset = require('/Changeset');
var extend = require('/ace2_common').extend;
undoModule = (function() var undoModule = (function()
{ {
var stack = (function() var stack = (function()
{ {
@ -329,3 +331,5 @@ undoModule = (function()
apool: null apool: null
}; // apool is filled in by caller }; // apool is filled in by caller
})(); })();
exports.undoModule = undoModule;

View file

@ -384,3 +384,5 @@ function makeVirtualLineView(lineNode)
} }
} }
exports.makeVirtualLineView = makeVirtualLineView;

View file

@ -1,204 +1,158 @@
<!doctype html> <!doctype html>
<html lang="en"> <html>
<head> <title>RPG Write</title>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="robots" content="noindex, nofollow"> <meta name="robots" content="noindex, nofollow">
<title>RPG Write</title> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<!-- CSS -->
<link href="../static/css/pad.css" rel="stylesheet">
<!-- javascript -->
<script type="text/javascript">
// <![CDATA[
var clientVars = {}; // ]]>
</script>
<script src="../socket.io/socket.io.js"></script>
<script src="../minified/pad.js"></script>
<link href="../static/custom/pad.css" rel="stylesheet">
<script src="../static/custom/pad.js"></script>
<style type="text/css" title="dynamicsyntax"></style>
</head>
<body> <link href="../static/css/pad.css" rel="stylesheet">
<link href="../static/custom/pad.css" rel="stylesheet">
<style title="dynamicsyntax"></style>
<!-- head and body had been removed intentionally -->
<div id="editbar"> <div id="editbar">
<ul id="menu_left"> <ul id="menu_left">
<li onClick="window.pad&&pad.editbarClick('bold');return false" > <li id="bold" onClick="window.pad&amp;&amp;pad.editbarClick('bold');return false">
<a title="Bold (ctrl-B)"> <a class="buttonicon buttonicon-bold" title="Bold (ctrl-B)"></a>
<div class="buttonicon" style="background-position:0px -116px"></div>
</a>
</li> </li>
<li onClick="window.pad&&pad.editbarClick('italic'); return false;" > <li id="italic" onClick="window.pad&amp;&amp;pad.editbarClick('italic'); return false;">
<a title="Italics (ctrl-I)"> <a class="buttonicon buttonicon-italic" title="Italics (ctrl-I)"></a>
<div class="buttonicon" style="background-position:0px 0px"></div>
</a>
</li> </li>
<li onClick="window.pad&&pad.editbarClick('underline');return false;" > <li id="underline" onClick="window.pad&amp;&amp;pad.editbarClick('underline');return false;" >
<a title="Underline (ctrl-U)"> <a class="buttonicon buttonicon-underline" title="Underline (ctrl-U)"></a>
<div class="buttonicon" style="background-position:0px -236px;margin-top:1px;"></div>
</a>
</li> </li>
<li onClick="window.pad&&pad.editbarClick('strikethrough');return false;" > <li id="strikethrough" onClick="window.pad&amp;&amp;pad.editbarClick('strikethrough');return false;">
<a title="Strikethrough"> <a class="buttonicon buttonicon-strikethrough" title="Strikethrough"></a>
<div class="buttonicon" style="background-position:0px -200px"></div>
</a>
</li> </li>
<li class="separator"></li> <li class="separator"></li>
<li onClick="window.pad&&pad.editbarClick('insertunorderedlist');return false;" > <li id="oderedlist" onClick="window.pad&amp;&amp;pad.editbarClick('insertorderedlist');return false;">
<a title="Toggle Bullet List"> <a class="buttonicon buttonicon-insertorderedlist" title="Toggle Ordered List"></a>
<div class="buttonicon" style="background-position:0px -34px"></div>
</a>
</li> </li>
<li onClick="window.pad&&pad.editbarClick('indent');return false;" > <li id="unoderedlist" onClick="window.pad&amp;&amp;pad.editbarClick('insertunorderedlist');return false;">
<a title="Indent"> <a class="buttonicon buttonicon-insertunorderedlist" title="Toggle Bullet List"></a>
<div class="buttonicon" style="background-position:0px -52px"></div>
</a>
</li> </li>
<li onClick="window.pad&&pad.editbarClick('outdent');return false;" > <li id="indent" onClick="window.pad&amp;&amp;pad.editbarClick('indent');return false;">
<a title="Unindent"> <a class="buttonicon buttonicon-indent" title="Indent"></a>
<div class="buttonicon" style="background-position:0px -134px"></div> </li>
</a> <li id="outdent" onClick="window.pad&amp;&amp;pad.editbarClick('outdent');return false;">
<a class="buttonicon buttonicon-outdent" title="Unindent"></a>
</li> </li>
<li class="separator"></li> <li class="separator"></li>
<li onClick="window.pad&&pad.editbarClick('undo');return false;" > <li id="undo" onClick="window.pad&amp;&amp;pad.editbarClick('undo');return false;">
<a title="Undo (ctrl-Z)"> <a class="buttonicon buttonicon-undo" title="Undo (ctrl-Z)"></a>
<div class="buttonicon" style="background-position:0px -255px"></div>
</a>
</li> </li>
<li onClick="window.pad&&pad.editbarClick('redo');return false;" > <li id="redo" onClick="window.pad&amp;&amp;pad.editbarClick('redo');return false;">
<a title="Redo (ctrl-Y)"> <a class="buttonicon buttonicon-redo" title="Redo (ctrl-Y)"></a>
<div class="buttonicon" style="background-position:0px -166px"></div>
</a>
</li> </li>
<li class="separator"></li> <li class="separator"></li>
<li id="clearAuthorship" onClick="window.pad&&pad.editbarClick('clearauthorship');return false;" > <li id="clearAuthorship" onClick="window.pad&amp;&amp;pad.editbarClick('clearauthorship');return false;">
<a title="Clear Authorship Colors"> <a class="buttonicon buttonicon-clearauthorship" title="Clear Authorship Colors"></a>
<div class="buttonicon" style="background-position:0px -86px"></div>
</a>
</li> </li>
</ul> </ul>
<ul id="menu_right"> <ul id="menu_right">
<li id="settingslink" onClick="window.pad&amp;&amp;pad.editbarClick('settings');return false;">
<li onClick="window.pad&&pad.editbarClick('import_export');return false;"> <a class="buttonicon buttonicon-settings" id="settingslink" title="Settings of this pad"></a>
<a id="exportlink" title="Import/Export from/to different document formats">
<div class="buttonicon" style="background-position:0px -68px"></div>
</a>
</li> </li>
<li onClick="window.pad&&pad.editbarClick('embed');return false;" > <li id="importexportlink" onClick="window.pad&amp;&amp;pad.editbarClick('import_export');return false;">
<a id="embedlink" title="Share and Embed this pad"> <a class="buttonicon buttonicon-import_export" id="exportlink" title="Import/Export from/to different document formats"></a>
<div class="buttonicon" style="background-position:0px -18px"></div> </li>
</a> <li id="embedlink" onClick="window.pad&amp;&amp;pad.editbarClick('embed');return false;" >
<a class="buttonicon buttonicon-embed" id="embedlink" title="Share and Embed this pad"></a>
</li> </li>
<li class="separator"></li> <li class="separator"></li>
<li id="timesliderlink" onClick="document.location = document.location.pathname+ '/timeslider'"> <li id="timesliderlink" onClick="document.location = document.location.pathname+ '/timeslider'">
<a title="Show the history of this pad"> <a class="buttonicon buttonicon-history" title="Show the history of this pad"></a>
<div class="buttonicon" style="background-position:0px -218px"></div>
</a>
</li> </li>
<li id="usericon" onClick="window.pad&&pad.editbarClick('showusers');return false;" > <li id="usericon" onClick="window.pad&amp;&amp;pad.editbarClick('showusers');return false;" title="Show connected users">
<a title="Show connected users"> <span class="buttonicon buttonicon-showusers" id="usericonback"></span>
<div class="buttonicon" id="usericonback" style="background-position:0px -184px;display:inline-block;"></div>
<span id="online_count">1</span> <span id="online_count">1</span>
</a>
</li> </li>
</ul> </ul>
</div> </div>
<div id="users"> <div id="users">
<div id="connectionstatus"> <div id="connectionstatus"></div>
<!-- -->
</div>
<div id="myuser"> <div id="myuser">
<div id="mycolorpicker"> <div id="mycolorpicker">
<div id="colorpicker"></div> <div id="colorpicker"></div>
<!-- <button id="mycolorpickersave">Save</button>
<ul id="colorpickerswatches"> <button id="mycolorpickercancel">Cancel</button>
</ul>
-->
<span id="mycolorpickersave">
<a onclick="closeColorPicker()">Save</a>
</span>
<span id="mycolorpickercancel">
<a onclick="closeColorPicker()">Cancel</a>
</span>
<span id="mycolorpickerpreview" class="myswatchboxhoverable"></span> <span id="mycolorpickerpreview" class="myswatchboxhoverable"></span>
</div> </div>
<div id="myswatchbox"><div id="myswatch"></div></div>
<div id="myswatchbox"><div id="myswatch"><!-- --></div></div> <div id="myusernameform"><input type="text" id="myusernameedit" disabled="disabled"></div>
<div id="myusernameform"><input type="text" id="myusernameedit" disabled="disabled" /></div> <div id="mystatusform"><input type="text" id="mystatusedit" disabled="disabled"></div>
<div id="mystatusform"><input type="text" id="mystatusedit" disabled="disabled" /></div>
</div> </div>
<div id="otherusers"> <div id="otherusers">
<div id="guestprompts"><!-- --></div> <div id="guestprompts"></div>
<table id="otheruserstable" cellspacing="0" cellpadding="0" border="0"> <table id="otheruserstable" cellspacing="0" cellpadding="0" border="0">
<tr> <tr><td></td></tr>
<td>
</td>
</tr>
</table> </table>
<div id="nootherusers"></div>
<div id="nootherusers">
</div> </div>
<div id="userlistbuttonarea"></div>
</div> </div>
<div id="userlistbuttonarea">
<!--<a href="javascript:void(0)" id="sharebutton">Share</a>-->
</div>
</div>
<!-- /padusers -->
<!--<div id="users">
<!-- some example code so I can make the css --*>
</div>-->
<div id="editorcontainerbox"> <div id="editorcontainerbox">
<div id="editorcontainer"></div>
<div id="editorcontainer"> <div id="editorloadingbox">Loading...</div>
<!-- -->
</div>
<div id="editorloadingbox">
Loading...
</div> </div>
<div id="settingsmenu" class="popup">
<h1>Pad settings</h1>
<div class="column">
<h2>My view</h2>
<p>
<input type="checkbox" id="options-stickychat" onClick="chat.stickToScreen();">
<label for="options-stickychat">Chat always on screen</label>
</p>
<p>
<input type="checkbox" id="options-colorscheck">
<label for="options-colorscheck">Authorship colors</label>
</p>
<p>
<input type="checkbox" id="options-linenoscheck" checked>
<label for="options-linenoscheck">Line numbers</label>
</p>
<p>
Font type:
<select id="viewfontmenu">
<option value="normal">Normal</option>
<option value="monospace">Monospaced</option>
</select>
</p>
</div>
<div class="column">
<h2>Global view</h2>
<p>Currently nothing.</p>
<p class="note">These options affect everyone viewing this pad.</p>
</div>
</div> </div>
<!-- import export code --> <div id="importexport" class="popup">
<div id="importexport"> <div class="column">
<h2>Import from text file, HTML, PDF, Word, ODT or RTF</h2><br>
<div id="import">
Import from text file, HTML, PDF, Word, ODT or RTF:<br/><br/>
<form id="importform" method="post" action="" target="importiframe" enctype="multipart/form-data"> <form id="importform" method="post" action="" target="importiframe" enctype="multipart/form-data">
<div class="importformdiv" id="importformfilediv"> <div class="importformdiv" id="importformfilediv">
<input type="file" name="file" size="15" id="importfileinput" /> <input type="file" name="file" size="15" id="importfileinput">
<div class="importmessage" id="importmessagefail"></div> <div class="importmessage" id="importmessagefail"></div>
</div> </div>
<div id="import"></div>
<div class="importmessage" id="importmessagesuccess">Successful!</div> <div class="importmessage" id="importmessagesuccess">Successful!</div>
<div class="importformdiv" id="importformsubmitdiv"> <div class="importformdiv" id="importformsubmitdiv">
<input type="hidden" name="padId" value="blpmaXT35R" /> <input type="hidden" name="padId" value="blpmaXT35R">
<span class="nowrap"> <span class="nowrap">
<input type="submit" name="submit" value="Import Now" disabled="disabled" id="importsubmitinput" /> <input type="submit" name="submit" value="Import Now" disabled="disabled" id="importsubmitinput">
<img alt="" id="importstatusball" src="../static/img/loading.gif" align="top" /> <img alt="" id="importstatusball" src="../static/img/loading.gif" align="top">
<img alt="" id="importarrow" src="../static/img/leftarrow.png" align="top" /> <img alt="" id="importarrow" src="../static/img/leftarrow.png" align="top">
</span> </span>
</div> </div>
</form> </form>
</div> </div>
<div class="column">
<div id="importexportline"></div> <h2>Export current pad as</h2>
<div id="export">
Export current pad as:
<a id="exporthtmla" target="_blank" class="exportlink"><div class="exporttype" id="exporthtml">HTML</div></a> <a id="exporthtmla" target="_blank" class="exportlink"><div class="exporttype" id="exporthtml">HTML</div></a>
<a id="exportplaina" target="_blank" class="exportlink"><div class="exporttype" id="exportplain">Plain text</div></a> <a id="exportplaina" target="_blank" class="exportlink"><div class="exporttype" id="exportplain">Plain text</div></a>
<a id="exportworda" target="_blank" class="exportlink"><div class="exporttype" id="exportword">Microsoft Word</div></a> <a id="exportworda" target="_blank" class="exportlink"><div class="exporttype" id="exportword">Microsoft Word</div></a>
@ -206,41 +160,36 @@
<a id="exportopena" target="_blank" class="exportlink"><div class="exporttype" id="exportopen">OpenDocument</div></a> <a id="exportopena" target="_blank" class="exportlink"><div class="exporttype" id="exportopen">OpenDocument</div></a>
<a id="exportdokuwikia" target="_blank" class="exportlink"><div class="exporttype" id="exportdokuwiki">DokuWiki text</div></a> <a id="exportdokuwikia" target="_blank" class="exportlink"><div class="exporttype" id="exportdokuwiki">DokuWiki text</div></a>
<a id="exportwordlea" target="_blank" onClick="padimpexp.export2Wordle();return false;" class="exportlink"><div class="exporttype" id="exportwordle">Wordle</div></a> <a id="exportwordlea" target="_blank" onClick="padimpexp.export2Wordle();return false;" class="exportlink"><div class="exporttype" id="exportwordle">Wordle</div></a>
<form id="wordlepost" name="wall" action="http://wordle.net/advanced" method="POST" style="margin-left:0px;">
<div id="hidetext" style=""><textarea id="text" name="text" id="text" style="display:none;">Coming soon!</textarea></div>
</form>
</div> </div>
</div> </div>
<!-- the embed code --> <div id="embed" class="popup">
<div id="embed"> <div id="embedreadonly" class="right">
<div id="embedreadonly"> <input type="checkbox" id="readonlyinput" onClick="padeditbar.setEmbedLinks();">
<input type="checkbox" id="readonlyinput" onClick="padeditbar.setEmbedLinks();"/><label for="readonlyinput">Read only</label> <label for="readonlyinput">Read only</label>
</div> </div>
Share: <h1>Share this pad</h1>
<br/>
<div id="linkcode"> <div id="linkcode">
<label for="linkinput">Link:</label><input id="linkinput" type="text" value=""> <h2>Link</h2>
</div><br/> <input id="linkinput" type="text" value="">
</div>
<br>
<div id="embedcode"> <div id="embedcode">
<label for="embedinput">Embed code:</label><input id="embedinput" type="text" value=""> <h2>Embed URL</h2>
</div><br/> <input id="embedinput" type="text" value="">
</div>
<br>
<div id="qrcode"> <div id="qrcode">
<label for="embedreadonlyqr">QR code:</label><br/> <h2>QR code</h2>
<div id="qr_center"><img id="embedreadonlyqr"></div> <div id="qr_center"><img id="embedreadonlyqr"></div>
</div> </div>
</div> </div>
<div id="chatthrob"> <div id="chatthrob"></div>
</div> <div id="chaticon" title="Open the chat for this pad" onclick="chat.show();return false;">
<div id="chaticon">
<a onClick="chat.show();return false;"
title="Open the chat for this pad">
<span id="chatlabel">Chat</span> <span id="chatlabel">Chat</span>
<div class="buttonicon" style="background-position:0px -102px;display:inline-block;"></div> <span class="buttonicon buttonicon-chat"></span>
</a>
<span id="chatcounter">0</span> <span id="chatcounter">0</span>
</div> </div>
@ -249,87 +198,81 @@
<div id="chattext" class="authorColors"></div> <div id="chattext" class="authorColors"></div>
<div id="chatinputbox"> <div id="chatinputbox">
<form> <form>
<input id="chatinput" type="text" maxlength="140"/> <input id="chatinput" type="text" maxlength="140">
</form> </form>
</div> </div>
</div> </div>
<div id="focusprotector">&nbsp;</div> <div id="focusprotector">&nbsp;</div>
<!-- /padeditor -->
<div id="modaloverlay"> <div id="modaloverlay">
<div id="modaloverlay-inner"> <div id="modaloverlay-inner"></div>
<!-- --> </div>
</div>
</div>
<div id="mainmodals"> <div id="mainmodals">
<div id="connectionbox" class="modaldialog"> <div id="connectionbox" class="modaldialog">
<div id="connectionboxinner" class="modaldialog-inner"> <div id="connectionboxinner" class="modaldialog-inner">
<div class="connecting"> <div class="connecting">Connecting...</div>
Connecting... <div class="reconnecting">Reestablishing connection...</div>
</div>
<div class="reconnecting">
Reestablishing connection...
</div>
<div class="disconnected"> <div class="disconnected">
<h2 class="h2_disconnect">Disconnected.</h2> <h2 class="h2_disconnect">Disconnected.</h2>
<h2 class="h2_userdup">Opened in another window.</h2> <h2 class="h2_userdup">Opened in another window.</h2>
<h2 class="h2_unauth">No Authorization.</h2> <h2 class="h2_unauth">No Authorization.</h2>
<div id="disconnected_looping"> <div id="disconnected_looping">
<p> <p><b>We're having trouble talking to the EtherPad lite synchronization server.</b> You may be connecting through an incompatible firewall or proxy server.</p>
<b>We're having trouble talking to the EtherPad lite synchronization server.</b>
You may be connecting through an incompatible firewall or proxy server.
</p>
</div> </div>
<div id="disconnected_initsocketfail"> <div id="disconnected_initsocketfail">
<p> <p><b>We were unable to connect to the EtherPad lite synchronization server.</b> This may be due to an incompatibility with your web browser or internet connection.</p>
<b>We were unable to connect to the EtherPad lite synchronization server.</b>
This may be due to an incompatibility with your web browser or internet connection.
</p>
</div> </div>
<div id="disconnected_userdup"> <div id="disconnected_userdup">
<p> <p><b>You seem to have opened this pad in another browser window.</b> If you'd like to use this window instead, you can reconnect.</p>
<b>You seem to have opened this pad in another browser window.</b>
If you'd like to use this window instead, you can reconnect.
</p>
</div> </div>
<div id="disconnected_unknown"> <div id="disconnected_unknown">
<p> <p><b>Lost connection with the EtherPad lite synchronization server.</b> This may be due to a loss of network connectivity.</p>
<b>Lost connection with the EtherPad lite synchronization server.</b> This may be due to a loss of network connectivity.
</p>
</div> </div>
<div id="disconnected_slowcommit"> <div id="disconnected_slowcommit">
<p> <p><b>Server not responding.</b> This may be due to network connectivity issues or high load on the server.</p>
<b>Server not responding.</b> This may be due to network connectivity issues or high load on the server.
</p>
</div> </div>
<div id="disconnected_unauth"> <div id="disconnected_unauth">
<p> <p>Your browser's credentials or permissions have changed while viewing this pad. Try reconnecting.</p>
Your browser's credentials or permissions have changed while viewing this pad. Try reconnecting.
</p>
</div> </div>
<div id="disconnected_deleted"> <div id="disconnected_deleted">
<p> <p>This pad was deleted.</p>
This pad was deleted.
</p>
</div> </div>
<div id="reconnect_advise"> <div id="reconnect_advise">
<p> <p>If this continues to happen, please let us know</p>
If this continues to happen, please let us know
</p>
</div> </div>
<div id="reconnect_form"> <div id="reconnect_form">
<button id="forcereconnect">Reconnect Now</button> <button id="forcereconnect">Reconnect Now</button>
</div> </div>
</div> </div>
</div> </div>
<form id="reconnectform" method="post" action="/ep/pad/reconnect" accept-charset="UTF-8" style="display: none;"> <form id="reconnectform" method="post" action="/ep/pad/reconnect" accept-charset="UTF-8" style="display: none;">
<input type="hidden" class="padId" name="padId"/> <input type="hidden" class="padId" name="padId">
<input type="hidden" class="diagnosticInfo" name="diagnosticInfo"/> <input type="hidden" class="diagnosticInfo" name="diagnosticInfo">
<input type="hidden" class="missedChanges" name="missedChanges"/> <input type="hidden" class="missedChanges" name="missedChanges">
</form> </form>
</div>
</div>
<script type="text/javascript" src="../static/js/require-kernel.js"></script>
<script type="text/javascript" src="../socket.io/socket.io.js"></script>
<script type="text/javascript" src="../minified/pad.js?callback=require.define"></script>
<script type="text/javascript" src="../static/custom/pad.js"></script>
<script type="text/javascript">
var clientVars = {};
(function () {
require.setRootURI("../minified/");
require.setGlobalKeyPath("require");
require('/pad').init();
/* TODO: These globals shouldn't exist. */
pad = require('/pad').pad;
chat = require('/chat').chat;
padeditbar = require('/pad_editbar').padeditbar;
padimpexp = require('/pad_impexp').padimpexp;
}());
</script>
</body>
</html> </html>

View file

@ -7,150 +7,8 @@
<title>RPG Write Timeslider</title> <title>RPG Write Timeslider</title>
<link rel="stylesheet" href="../../static/css/pad.css"> <link rel="stylesheet" href="../../static/css/pad.css">
<link rel="stylesheet" href="../../static/css/timeslider.css"> <link rel="stylesheet" href="../../static/css/timeslider.css">
<link rel="stylesheet" href="../../static/custom/timeslider.css">
<style type="text/css" title="dynamicsyntax"></style> <style type="text/css" title="dynamicsyntax"></style>
<script type="text/javascript" src="../../socket.io/socket.io.js"></script>
<script type="text/javascript" src="../../minified/timeslider.js"></script>
<link href="../../static/custom/timeslider.css" rel="stylesheet">
<script src="../../static/custom/timeslider.js"></script>
<script>
// <![CDATA[
var clientVars = {};
function createCookie(name,value,days)
{
if (days) {
var date = new Date();
date.setTime(date.getTime()+(days*24*60*60*1000));
var expires = "; expires="+date.toGMTString();
}
else var expires = "";
document.cookie = name+"="+value+expires+"; path=/";
}
function readCookie(name)
{
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}
return null;
}
function randomString() {
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var string_length = 20;
var randomstring = '';
for (var i=0; i<string_length; i++) {
var rnum = Math.floor(Math.random() * chars.length);
randomstring += chars.substring(rnum,rnum+1);
}
return "t." + randomstring;
}
var socket, token, padId;
$(document).ready(function ()
{
//start the costum js
if(typeof costumStart == "function") costumStart();
//get the padId out of the url
var urlParts= document.location.pathname.split("/");
padId = decodeURIComponent(urlParts[urlParts.length-2]);
//set the title
document.title = document.title + " | " + padId;
//ensure we have a token
token = readCookie("token");
if(token == null)
{
token = randomString();
createCookie("token", token, 60);
}
var loc = document.location;
//get the correct port
var port = loc.port == "" ? (loc.protocol == "https:" ? 443 : 80) : loc.port;
//create the url
var url = loc.protocol + "//" + loc.hostname + ":" + port + "/";
//find out in which subfolder we are
var resource = loc.pathname.substr(1,loc.pathname.indexOf("/p/")) + "socket.io";
//build up the socket io connection
socket = io.connect(url, {resource: resource});
//send the ready message once we're connected
socket.on('connect', function()
{
sendSocketMsg("CLIENT_READY", {});
});
//route the incoming messages
socket.on('message', function(message)
{
if(window.console) console.log(message);
if(message.type == "CLIENT_VARS")
{
handleClientVars(message);
}
else if(message.type == "CHANGESET_REQ")
{
changesetLoader.handleSocketResponse(message);
}
else if(message.accessStatus)
{
$("body").html("<h2>You have no permission to access this pad</h2>")
}
});
});
//sends a message over the socket
function sendSocketMsg(type, data)
{
var sessionID = readCookie("sessionID");
var password = readCookie("password");
var msg = { "component" : "timeslider",
"type": type,
"data": data,
"padId": padId,
"token": token,
"sessionID": sessionID,
"password": password,
"protocolVersion": 2};
socket.json.send(msg);
}
var fireWhenAllScriptsAreLoaded = [];
function handleClientVars(message)
{
//save the client Vars
clientVars = message.data;
//load all script that doesn't work without the clientVars
loadBroadcastSliderJS();
loadBroadcastRevisionsJS();
loadBroadcastJS();
//fire all start functions of these scripts, formerly fired with window.load
for(var i=0;i < fireWhenAllScriptsAreLoaded.length;i++)
{
fireWhenAllScriptsAreLoaded[i]();
}
}
// ]]>
</script>
</head> </head>
<body id="padbody" class="timeslider limwidth nonpropad nonprouser"> <body id="padbody" class="timeslider limwidth nonpropad nonprouser">
@ -269,14 +127,14 @@
<div id="editbarright" class="editbarright"> <div id="editbarright" class="editbarright">
<!-- termporary place holder--> <!-- termporary place holder-->
<ul>
<li onClick="window.padeditbar.toolbarClick('import_export');return false;">
<a id="exportlink" title="Export to different document formats">
<div class="buttonicon buttonicon-import_export"></div>
</a>
</li>
</ul>
<a id = "returnbutton">Return to pad</a> <a id = "returnbutton">Return to pad</a>
<script>
if(document.referrer.length > 0 && document.referrer.substring(document.referrer.lastIndexOf("/")-1,document.referrer.lastIndexOf("/")) === "p") {
$("#returnbutton").attr("href", document.referrer);
} else {
$("#returnbutton").attr("href", document.location.href.substring(0,document.location.href.lastIndexOf("/")));
}
</script>
</div> </div>
<div id="editbarinner" class="editbarinner"> <div id="editbarinner" class="editbarinner">
@ -320,6 +178,41 @@
</div> </div>
<div id="mainmodals"></div> <div id="mainmodals"></div>
<!-- export code -->
<div id="importexport">
<div id="export" class="popup">
Export current version as:
<a id="exporthtmla" target="_blank" class="exportlink"><div class="exporttype" id="exporthtml">HTML</div></a>
<a id="exportplaina" target="_blank" class="exportlink"><div class="exporttype" id="exportplain">Plain text</div></a>
<a id="exportworda" target="_blank" class="exportlink"><div class="exporttype" id="exportword">Microsoft Word</div></a>
<a id="exportpdfa" target="_blank" class="exportlink"><div class="exporttype" id="exportpdf">PDF</div></a>
<a id="exportopena" target="_blank" class="exportlink"><div class="exporttype" id="exportopen">OpenDocument</div></a>
<a id="exportdokuwikia" target="_blank" class="exportlink"><div class="exporttype" id="exportdokuwiki">DokuWiki text</div></a>
<a id="exportwordlea" target="_blank" onClick="padimpexp.export2Wordle();return false;" class="exportlink"><div class="exporttype" id="exportwordle">Wordle</div></a>
<form id="wordlepost" name="wall" action="http://wordle.net/advanced" method="POST" style="margin-left:0px;">
<div id="hidetext" style=""><textarea id="text" name="text" id="text" style="display:none;">Coming soon!</textarea></div>
</form>
</div>
</div>
<script type="text/javascript" src="../../static/js/require-kernel.js"></script>
<script type="text/javascript" src="../../socket.io/socket.io.js"></script>
<script type="text/javascript" src="../../minified/timeslider.js?callback=require.define"></script>
<script type="text/javascript" src="../../static/custom/timeslider.js"></script>
<script type="text/javascript" >
var clientVars = {};
(function () {
require.setRootURI("../minified/");
require.setGlobalKeyPath("require");
require('/timeslider').init();
/* TODO: These globals shouldn't exist. */
padeditbar = require('/pad_editbar').padeditbar;
padimpexp = require('/pad_impexp').padimpexp;
})();
</script>
</body> </body>
</html> </html>