pull/511/head
Stefan 'Gared 2012-02-26 19:52:09 +00:00
commit 6706332e42
25 changed files with 1433 additions and 778 deletions

1
.gitignore vendored
View File

@ -10,3 +10,4 @@ var/dirty.db
bin/convertSettings.json
*~
*.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

@ -122,5 +122,9 @@ contribute to Etherpad Lite.
* [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"
# Donations
* [Etherpad Foundation Flattr] (http://flattr.com/thing/71378/Etherpad-Foundation)
* [Paypal] (https://www.paypal.com/uk/cgi-bin/webscr?cmd=_flow&SESSION=TXRTE1vjRbRm3BLkUVjy905bTyvanL6f_zwKicQII2Vp8aijc2gHHd4tTgm&dispatch=5885d80a13c0db1f8e263663d3faee8d43b1bb6ca6ed6d454adc375ba2d28b99)
# 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)

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

@ -43,7 +43,8 @@ var globalPads = {
* time, and allow us to "play back" these changes so legacy padIds can be found.
*/
var padIdTransforms = [
[/\s+/g, '_']
[/\s+/g, '_'],
[/:+/g, '_']
];
/**

View File

@ -98,7 +98,7 @@ async.waterfall([
//the pad id was sanitized, so we redirect to the sanitized version
if(sanitizedPadId != padId)
{
var real_path = req.path.replace(/^\/p\/[^\/]+/, '/p/' + sanitizedPadId);
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);
}

View File

@ -170,34 +170,3 @@ p {
}
#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

@ -816,8 +816,8 @@ ul#colorpickerswatches li:hover
#chaticon
{
z-index: 400;
position:absolute;
bottom:0px;
position: fixed;
bottom: 0px;
right: 20px;
padding: 5px;
border-left: 1px solid #999;
@ -1104,83 +1104,6 @@ width:33px !important;
transform: scale(1.5);
}
@media screen and (max-width: 960px) {
.modaldialog {
position: relative;
margin: 0 auto;
width: 80%;
top: 40px;
left: 0;
}
}
@media screen and (max-width: 600px) {
#editbar ul li {
padding: 4px 1px;
}
}
@media only screen and (min-device-width: 320px) and (max-device-width: 720px) {
#editbar ul li {
padding: 4px 3px;
}
#editbar ul#menu_right > li {
padding: 4px 8px;
margin-top: 2px;
}
#chaticon {
opacity: .8;
}
#users {
right: 4px;
}
#mycolorpicker {
left: -72px; /* #mycolorpicker:width - #users:width */
}
#editorcontainer {
margin-bottom: 33px;
}
#editbar ul#menu_right {
background: #f7f7f7;
background: linear-gradient(#f7f7f7, #f1f1f1 80%);
width: 100%;
overflow: hidden;
height: 32px;
position: fixed;
bottom: 0;
border-top: 1px solid #ccc;
}
#editbar ul#menu_right li:not(:last-child) {
display: none;
}
#editbar ul#menu_right li:last-child {
height: 24px;
border-radius: 0;
margin-top: 0;
border: 0;
float: right;
}
#chaticon {
bottom: 0;
right: 55px;
border-right: none;
border-radius: 0;
background: #f7f7f7;
background: linear-gradient(#f7f7f7, #f1f1f1 80%);
border: 0;
}
#chatbox {
bottom: 32px;
right: 0;
border-top-right-radius: 0;
}
#editbar ul li a span {
top: -3px;
}
#usericonback {
margin-top: 4px;
}
}
.rtl{
direction:RTL;
}
@ -1242,7 +1165,6 @@ label {
.right_popup {
float: left;
width: 50%;
box-sizing: border-box;
}
#settingsmenu, #importexport, #embed {
@ -1262,3 +1184,115 @@ label {
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) {
.modaldialog {
position: relative;
margin: 0 auto;
width: 80%;
top: 40px;
left: 0;
}
}
@media screen and (max-width: 600px) {
#editbar ul li {
padding: 4px 1px;
}
}
@media only screen and (min-device-width: 320px) and (max-device-width: 720px) {
#editbar ul li {
padding: 4px 3px;
}
#users {
right: 4px;
}
#mycolorpicker {
left: -72px; /* #mycolorpicker:width - #users:width */
}
#editorcontainer {
margin-bottom: 33px;
}
#editbar ul#menu_right {
background: #f7f7f7;
background: linear-gradient(#f7f7f7, #f1f1f1 80%);
width: 100%;
overflow: hidden;
height: 32px;
position: fixed;
bottom: 0;
border-top: 1px solid #ccc;
}
#editbar ul#menu_right li:last-child {
height: 24px;
border-radius: 0;
margin-top: 0;
border: 0;
float: right;
}
#chaticon {
bottom: 0;
right: 55px;
border-right: none;
border-radius: 0;
background: #f7f7f7;
background: linear-gradient(#f7f7f7, #f1f1f1 80%);
border: 0;
}
#chatbox {
bottom: 32px;
right: 0;
border-top-right-radius: 0;
border-right: none;
}
#editbar ul li a span {
top: -3px;
}
#usericonback {
margin-top: 4px;
}
#qrcode {
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;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -4,27 +4,24 @@
<title>Etherpad Lite</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<meta name="viewport" content="width=device-width, user-scalable=0">
<style>
*{ margin:0;padding:0; }
<style>
body {
background: rgba(0,0,0,.05);
margin: 0;
height: 100%;
color: #333;
font: 14px helvetica,sans-serif;
background: #ccc;
font: 14px helvetica, sans-serif;
background: #ddd;
background: -webkit-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: -o-radial-gradient(circle,#aaa,#eee 60%) center fixed;
overflow-x: hidden;
border-top: 8px solid rgba(51,51,51,.8);
}
#container {
text-shadow: 0 1px 1px #fff;
#wrapper {
border-top: 1px solid #999;
margin-top: 160px;
text-align: center;
padding: 15px;
background: #eee;
background: -webkit-linear-gradient(#fff,#ccc);
@ -34,6 +31,10 @@
opacity: .9;
box-shadow: 0px 1px 8px rgba(0,0,0,0.3);
}
#inner {
width: 300px;
margin: 0 auto;
}
#button {
margin: 0 auto;
border-radius: 3px;
@ -43,7 +44,6 @@
text-shadow: 0 -1px 0 rgba(0,0,0,.8);
height: 70px;
line-height: 70px;
width: 300px;
background: #555;
background: -webkit-linear-gradient(#5F5F5F,#565656 50%,#4C4C4C 51%,#373737);
background: -moz-linear-gradient(#5F5F5F,#565656 50%,#4C4C4C 51%,#373737);
@ -65,64 +65,69 @@
}
#label {
text-align: left;
margin: 0 auto;
width: 300px;
text-shadow: 0 1px 1px #fff;
margin: 16px auto 0;
}
input {
vertical-align: middle;
form {
height: 38px;
background: #fff;
border: 1px solid #bbb;
border-radius: 3px;
position: relative;
}
button, input {
font-weight: bold;
font-size: 15px;
}
input[type="text"] {
width: 243px;
padding: 10px 47px 10px 10px;
background: #fff;
border: 1px solid #bbb;
outline: none;
border-radius: 3px;
text-shadow: 0 0 1px #fff;
box-sizing: border-box;
-moz-box-sizing: border-box;
padding: 0 45px 0 10px;
width: 100%;
height: 100%;
outline: none;
border: none;
position: absolute;
}
input[type="submit"] {
button[type="submit"] {
position: absolute;
right: 0;
width: 45px;
margin-left: -50px;
padding: 8px;
height: 38px;
}
input[type="submit"]::-moz-focus-inner { border: 0 }
@-moz-document url-prefix() { input[type="submit"] { padding: 7px } }
@media only screen and (min-device-width: 320px) and (max-device-width: 720px) {
body {
background: #bbb;
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;
text-align: left;
}
#button, #label {
text-align: center;
#inner {
width: 95%;
}
form {
text-align: center;
}
input[type=text] {
width: 75%;
#label {
text-align: center;
}
}
</style>
</style>
<link href="static/custom/index.css" rel="stylesheet">
<script src="static/custom/index.js"></script>
<div id="container">
<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>
<form action="#" onsubmit="go2Name();return false;">
<div id="wrapper">
<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;">
<input type="text" id="padname" autofocus x-webkit-speech>
<input type="submit" value="OK">
</form>
<button type="submit">OK</button>
</form>
</div>
</div>
<script src="static/custom/index.js"></script>
<script>
function go2Name()
{

View File

@ -29,12 +29,26 @@ var AttributePoolFactory = require("/AttributePoolFactory");
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) {
var e = new Error(msg);
e.easysync = true;
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) {
if (!b) {
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) {
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) {
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) {
var dollarIndex = cs.indexOf('$');
var beforeDollar = cs.substring(0, dollarIndex);
@ -57,13 +89,34 @@ exports.toBaseTen = function (cs) {
}) + 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) {
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) {
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) {
//print(opsStr);
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) {
op.opcode = '';
op.chars = 0;
op.lines = 0;
op.attribs = '';
};
/**
* Creates a new Op object
* @param optOpcode the type operation of the Op object
*/
exports.newOp = function (optOpcode) {
return {
opcode: (optOpcode || ''),
@ -143,6 +205,11 @@ exports.newOp = function (optOpcode) {
attribs: ''
};
};
/**
* Clones an Op
* @param op Op to be cloned
*/
exports.cloneOp = function (op) {
return {
opcode: op.opcode,
@ -151,12 +218,22 @@ exports.cloneOp = function (op) {
attribs: op.attribs
};
};
/**
* Copies op1 to op2
* @param op1 src Op
* @param op2 dest Op
*/
exports.copyOp = function (op1, op2) {
op2.opcode = op1.opcode;
op2.chars = op1.chars;
op2.lines = op1.lines;
op2.attribs = op1.attribs;
};
/**
* Writes the Op in a string the way that changesets need it
*/
exports.opString = function (op) {
// just for debugging
if (!op.opcode) return 'null';
@ -164,11 +241,19 @@ exports.opString = function (op) {
assem.append(op);
return assem.toString();
};
/**
* Used just for debugging
*/
exports.stringOp = function (str) {
// just for debugging
return exports.opIterator(str).next();
};
/**
* Used to check if a Changeset if valid
* @param cs {Changeset} Changeset to be checked
*/
exports.checkRep = function (cs) {
// doesn't check things that require access to attrib pool (e.g. attribute order)
// or original string (e.g. newline positions)
@ -218,6 +303,15 @@ exports.checkRep = function (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 () {
// Like opAssembler but able to produce conforming exportss
// 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) {
var curIndex = 0;
@ -510,6 +608,9 @@ exports.stringIterator = function (str) {
};
};
/**
* A custom made StringBuffer
*/
exports.stringAssembler = function () {
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) {
// Mutates lines, an array of strings, in place.
// Mutation operations have the same constraints as exports operations
@ -781,6 +886,21 @@ exports.textLinesMutator = function (lines) {
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) {
var iter1 = exports.opIterator(in1, idx1);
var iter2 = exports.opIterator(in2, idx2);
@ -802,6 +922,11 @@ exports.applyZip = function (in1, idx1, in2, idx2, func) {
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) {
var headerRegex = /Z:([0-9a-z]+)([><])([0-9a-z]+)|/;
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) {
var lenDiff = newLen - oldLen;
var lenDiffStr = (lenDiff >= 0 ? '>' + exports.numToString(lenDiff) : '<' + exports.numToString(-lenDiff));
@ -831,6 +964,11 @@ exports.pack = function (oldLen, newLen, opsStr, bank) {
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) {
var unpacked = exports.unpack(cs);
exports.assert(str.length == unpacked.oldLen, "mismatched apply: ", str.length, " / ", unpacked.oldLen);
@ -856,6 +994,11 @@ exports.applyToText = function (cs, str) {
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) {
var unpacked = exports.unpack(cs);
var csIter = exports.opIterator(unpacked.ops);
@ -878,6 +1021,13 @@ exports.mutateTextLines = function (cs, lines) {
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) {
// att1 and att2 are strings like "*3*f*1c", asMutation is a boolean.
// Sometimes attribute (key,value) pairs are treated as attribute presence
@ -935,6 +1085,10 @@ exports.composeAttributes = function (att1, att2, resultIsMutation, pool) {
return buf.toString();
};
/**
* Function used as parameter for applyZip to apply a Changeset to an
* attribute
*/
exports._slicerZipperFunc = function (attOp, csOp, opOut, pool) {
// attOp is the op from the sequence that is being operated on, either an
// 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) {
var unpacked = exports.unpack(cs);
@ -1129,6 +1289,11 @@ exports.mutateAttributionLines = function (cs, lines, pool) {
//dmesg("-> "+lines.toSource());
};
/**
* joins several Attribution lines
* @param theAlines collection of Attribution lines
* @returns {string} joined Attribution lines
*/
exports.joinAttributionLines = function (theAlines) {
var assem = exports.mergingOpAssembler();
for (var i = 0; i < theAlines.length; i++) {
@ -1179,10 +1344,20 @@ exports.splitAttributionLines = function (attrOps, text) {
return lines;
};
/**
* splits text into lines
* @param {string} text to be splitted
*/
exports.splitTextLines = function (text) {
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) {
var unpacked1 = exports.unpack(cs1);
var unpacked2 = exports.unpack(cs2);
@ -1225,10 +1400,14 @@ exports.compose = function (cs1, cs2, pool) {
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) {
// 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) {
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) {
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) {
var oldLen = oldFullText.length;
@ -1271,8 +1467,14 @@ exports.makeSplice = function (oldFullText, spliceStart, numRemoved, newText, op
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) {
// get a list of splices, [startChar, endChar, newText]
//
var unpacked = exports.unpack(cs);
var splices = [];
@ -1302,6 +1504,9 @@ exports.toSplices = function (cs) {
return splices;
};
/**
*
*/
exports.characterRangeFollow = function (cs, startChar, endChar, insertionsAfter) {
var newStartChar = startChar;
var newEndChar = endChar;
@ -1346,6 +1551,14 @@ exports.characterRangeFollow = function (cs, startChar, endChar, insertionsAfter
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) {
// works on exports or attribution string
var dollarPos = cs.indexOf('$');
@ -1363,13 +1576,22 @@ exports.moveOpsToNewPool = function (cs, oldPool, newPool) {
}) + fromDollar;
};
/**
* create an attribution inserting a text
* @param text {string} text to be inserted
*/
exports.makeAttribution = function (text) {
var assem = exports.smartOpAssembler();
assem.appendOpWithText('+', text);
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) {
var dollarPos = cs.indexOf('$');
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) {
return exports.mapAttribNumbers(cs, filter);
};
/**
* does exactly the same as exports.filterAttribNumbers
*/
exports.mapAttribNumbers = function (cs, func) {
var dollarPos = cs.indexOf('$');
if (dollarPos < 0) {
@ -1410,6 +1641,12 @@ exports.mapAttribNumbers = function (cs, func) {
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) {
return {
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) {
return {
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) {
return {
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) {
atext2.text = atext1.text;
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) {
// intentionally skips last newline char of atext
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) {
var newPool = AttributePoolFactory.createAttributePool();;
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) {
var unpacked = exports.unpack(cs);
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) {
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) {
var value = '';
if (attribs) {
@ -1499,6 +1777,11 @@ exports.attribsAttributeValue = function (attribs, key, pool) {
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) {
var assem = exports.smartOpAssembler();
var o = exports.newOp();

View File

@ -49,8 +49,7 @@ function Ace2Editor()
{
var that = this;
var args = arguments;
function action()
var action = function()
{
func.apply(that, args);
}
@ -71,78 +70,47 @@ function Ace2Editor()
function doActionsPendingInit()
{
for (var i = 0; i < actionsPendingInit.length; i++)
{
actionsPendingInit[i]();
}
$.each(actionsPendingInit, function(i,fn){
fn()
});
actionsPendingInit = [];
}
ace2.registry[info.id] = info;
editor.importText = pendingInit(function(newCode, undoable)
{
info.ace_importText(newCode, undoable);
});
editor.importAText = pendingInit(function(newCode, apoolJsonObj, undoable)
{
info.ace_importAText(newCode, apoolJsonObj, undoable);
// The following functions (prefixed by 'ace_') are exposed by editor, but
// execution is delayed until init is complete
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.exportText = function()
{
if (!loaded) return "(awaiting init)\n";
return info.ace_exportText();
};
editor.getFrame = function()
{
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)
{
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:
// 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).
@ -157,24 +125,6 @@ function Ace2Editor()
if (!loaded) return null;
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()
{
@ -183,19 +133,7 @@ function Ace2Editor()
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);
});
function sortFilesByEmbeded(files) {
var embededFiles = [];

View File

@ -141,6 +141,9 @@ function htmlPrettyEscape(str)
return Security.escapeHTML(str).replace(/\r?\n/g, '\\n');
}
var noop = function(){};
var identity = function(x){return x};
exports.isNodeText = isNodeText;
exports.object = object;
exports.extend = extend;
@ -155,3 +158,5 @@ exports.binarySearch = binarySearch;
exports.binarySearchInfinite = binarySearchInfinite;
exports.htmlPrettyEscape = htmlPrettyEscape;
exports.map = map;
exports.noop = noop;
exports.identity = identity;

View File

@ -21,6 +21,7 @@
*/
var Ace2Common = require('/ace2_common');
// Extract useful method defined in the other module.
var isNodeText = Ace2Common.isNodeText;
var object = Ace2Common.object;
@ -32,10 +33,10 @@ var isArray = Ace2Common.isArray;
var browser = Ace2Common.browser;
var getAssoc = Ace2Common.getAssoc;
var setAssoc = Ace2Common.setAssoc;
var binarySearch = Ace2Common.binarySearch;
var binarySearchInfinite = Ace2Common.binarySearchInfinite;
var htmlPrettyEscape = Ace2Common.htmlPrettyEscape;
var map = Ace2Common.map;
var noop = Ace2Common.noop;
var makeChangesetTracker = require('/changesettracker').makeChangesetTracker;
var colorutils = require('/colorutils').colorutils;
@ -49,9 +50,8 @@ var newSkipList = require('/skiplist').newSkipList;
var undoModule = require('/undomodule').undoModule;
var makeVirtualLineView = require('/virtual_lines').makeVirtualLineView;
function OUTER(gscope)
{
function Ace2Inner(){
var DEBUG = false; //$$ build script replaces the string "var DEBUG=true;//$$" with "var DEBUG=false;"
// changed to false
var isSetUp = false;
@ -122,13 +122,13 @@ function OUTER(gscope)
iframePadRight = 0;
var console = (DEBUG && window.console);
if (!window.console)
{
var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];
console = {};
for (var i = 0; i < names.length; ++i)
console[names[i]] = function()
{};
console[names[i]] = noop;
//console.error = function(str) { alert(str); };
}
@ -147,14 +147,6 @@ function OUTER(gscope)
};
}
function noop()
{}
function identity(x)
{
return x;
}
// "dmesg" is for displaying messages in the in-page output pane
// visible when "?djs=1" is appended to the pad URL. It generally
// remains a no-op unless djs is enabled, but we make a habit of
@ -338,7 +330,7 @@ function OUTER(gscope)
editorInfo.ace_getRep = function()
{
return rep;
}
};
var currentCallStack = null;
@ -460,12 +452,11 @@ function OUTER(gscope)
submitOldEvent(cs.editEvent);
if (cs.domClean && cs.type != "setup")
{
if (cs.isUserChange)
{
if (cs.repChanged) parenModule.notifyChange();
else parenModule.notifyTick();
}
recolorModule.recolorLines();
// if (cs.isUserChange)
// {
// if (cs.repChanged) parenModule.notifyChange();
// else parenModule.notifyTick();
// }
if (cs.selectionAffected)
{
updateBrowserSelectionFromRep();
@ -522,230 +513,7 @@ function OUTER(gscope)
{
return rep.lines.atOffset(charOffset).key;
}
var recolorModule = (function()
{
var dirtyLineKeys = {};
var module = {};
module.setCharNeedsRecoloring = function(offset)
{
if (offset >= rep.alltext.length)
{
offset = rep.alltext.length - 1;
}
dirtyLineKeys[getLineKeyForOffset(offset)] = true;
}
module.setCharRangeNeedsRecoloring = function(offset1, offset2)
{
if (offset1 >= rep.alltext.length)
{
offset1 = rep.alltext.length - 1;
}
if (offset2 >= rep.alltext.length)
{
offset2 = rep.alltext.length - 1;
}
var firstEntry = rep.lines.atOffset(offset1);
var lastKey = rep.lines.atOffset(offset2).key;
dirtyLineKeys[lastKey] = true;
var entry = firstEntry;
while (entry && entry.key != lastKey)
{
dirtyLineKeys[entry.key] = true;
entry = rep.lines.next(entry);
}
}
module.recolorLines = function()
{
for (var k in dirtyLineKeys)
{
recolorLineByKey(k);
}
dirtyLineKeys = {};
}
return module;
})();
var parenModule = (function()
{
var module = {};
module.notifyTick = function()
{
handleFlashing(false);
};
module.notifyChange = function()
{
handleFlashing(true);
};
module.shouldNormalizeOnChar = function(c)
{
if (parenFlashRep.active)
{
// avoid highlight style from carrying on to typed text
return true;
}
c = String.fromCharCode(c);
return !!(bracketMap[c]);
}
var parenFlashRep = {
active: false,
whichChars: null,
whichLineKeys: null,
expireTime: null
};
var bracketMap = {
'(': 1,
')': -1,
'[': 2,
']': -2,
'{': 3,
'}': -3
};
var bracketRegex = /[{}\[\]()]/g;
function handleFlashing(docChanged)
{
function getSearchRange(aroundLoc)
{
var rng = getVisibleCharRange();
var d = 100; // minimum radius
var e = 3000; // maximum radius;
if (rng[0] > aroundLoc - d) rng[0] = aroundLoc - d;
if (rng[0] < aroundLoc - e) rng[0] = aroundLoc - e;
if (rng[0] < 0) rng[0] = 0;
if (rng[1] < aroundLoc + d) rng[1] = aroundLoc + d;
if (rng[1] > aroundLoc + e) rng[1] = aroundLoc + e;
if (rng[1] > rep.lines.totalWidth()) rng[1] = rep.lines.totalWidth();
return rng;
}
function findMatchingVisibleBracket(startLoc, forwards)
{
var rng = getSearchRange(startLoc);
var str = rep.alltext.substring(rng[0], rng[1]);
var bstr = str.replace(bracketRegex, '('); // handy for searching
var loc = startLoc - rng[0];
var bracketState = [];
var foundParen = false;
var goodParen = false;
function nextLoc()
{
if (loc < 0) return;
if (forwards) loc++;
else loc--;
if (loc < 0 || loc >= str.length) loc = -1;
if (loc >= 0)
{
if (forwards) loc = bstr.indexOf('(', loc);
else loc = bstr.lastIndexOf('(', loc);
}
}
while ((!foundParen) && (loc >= 0))
{
if (getCharType(loc + rng[0]) == "p")
{
var b = bracketMap[str.charAt(loc)]; // -1, 1, -2, 2, -3, 3
var into = forwards;
var typ = b;
if (typ < 0)
{
into = !into;
typ = -typ;
}
if (into) bracketState.push(typ);
else
{
var recent = bracketState.pop();
if (recent != typ)
{
foundParen = true;
goodParen = false;
}
else if (bracketState.length == 0)
{
foundParen = true;
goodParen = true;
}
}
}
//console.log(bracketState.toSource());
if ((!foundParen) && (loc >= 0)) nextLoc();
}
if (!foundParen) return null;
return {
chr: (loc + rng[0]),
good: goodParen
};
}
var r = parenFlashRep;
var charsToHighlight = null;
var linesToUnhighlight = null;
if (r.active && (docChanged || (now() > r.expireTime)))
{
linesToUnhighlight = r.whichLineKeys;
r.active = false;
}
if ((!r.active) && docChanged && isCaret() && caretColumn() > 0)
{
var caret = caretDocChar();
if (caret > 0 && getCharType(caret - 1) == "p")
{
var charBefore = rep.alltext.charAt(caret - 1);
if (bracketMap[charBefore])
{
var lookForwards = (bracketMap[charBefore] > 0);
var findResult = findMatchingVisibleBracket(caret - 1, lookForwards);
if (findResult)
{
var mateLoc = findResult.chr;
var mateGood = findResult.good;
r.active = true;
charsToHighlight = {};
charsToHighlight[caret - 1] = 'flash';
charsToHighlight[mateLoc] = (mateGood ? 'flash' : 'flashbad');
r.whichLineKeys = [];
r.whichLineKeys.push(getLineKeyForOffset(caret - 1));
r.whichLineKeys.push(getLineKeyForOffset(mateLoc));
r.expireTime = now() + 4000;
newlyActive = true;
}
}
}
}
if (linesToUnhighlight)
{
recolorLineByKey(linesToUnhighlight[0]);
recolorLineByKey(linesToUnhighlight[1]);
}
if (r.active && charsToHighlight)
{
function f(txt, cls, next, ofst)
{
var flashClass = charsToHighlight[ofst];
if (cls)
{
next(txt, cls + " " + flashClass);
}
else next(txt, cls);
}
for (var c in charsToHighlight)
{
recolorLinesInRange((+c), (+c) + 1, null, f);
}
}
}
return module;
})();
function dispose()
{
disposed = true;
@ -779,7 +547,7 @@ function OUTER(gscope)
alineLength += o.chars;
if (opIter.hasNext())
{
if (o.lines != 0) error();
if (o.lines !== 0) error();
}
else
{
@ -1097,9 +865,9 @@ function OUTER(gscope)
editorInfo.ace_callWithAce = function(fn, callStack, normalize)
{
var wrapper = function()
{
return fn(editorInfo);
}
{
return fn(editorInfo);
};
@ -1110,7 +878,7 @@ function OUTER(gscope)
{
editorInfo.ace_fastIncorp(9);
wrapper1();
}
};
}
if (callStack !== undefined)
@ -1121,61 +889,49 @@ function OUTER(gscope)
{
return wrapper();
}
}
};
// This methed exposes a setter for some ace properties
// @param key the name of the parameter
// @param value the value to set to
editorInfo.ace_setProperty = function(key, value)
{
var k = key.toLowerCase();
if (k == "wraps")
{
setWraps(value);
// Convinience function returning a setter for a class on an element
var setClassPresenceNamed = function(element, cls){
return function(value){
setClassPresence(element, cls, !! value)
}
};
// These properties are exposed
var setters = {
wraps: setWraps,
showsauthorcolors: setClassPresenceNamed(root, "authorColors"),
showsuserselections: setClassPresenceNamed(root, "userSelections"),
showslinenumbers : function(value){
hasLineNumbers = !! value;
// disable line numbers on mobile devices
if (browser.mobile) hasLineNumbers = false;
setClassPresence(sideDiv, "sidedivhidden", !hasLineNumbers);
fixView();
},
grayedout: setClassPresenceNamed(outerWin.document.body, "grayedout"),
dmesg: function(){ dmesg = window.dmesg = value; },
userauthor: function(value){ thisAuthor = String(value); },
styled: setStyled,
textface: setTextFace,
textsize: setTextSize,
rtlistrue: setClassPresenceNamed(root, "rtl")
};
var setter = setters[key.toLowerCase()];
// check if setter is present
if(setter !== undefined){
setter(value)
}
else if (k == "showsauthorcolors")
{
setClassPresence(root, "authorColors", !! value);
}
else if (k == "showsuserselections")
{
setClassPresence(root, "userSelections", !! value);
}
else if (k == "showslinenumbers")
{
hasLineNumbers = !! value;
// disable line numbers on mobile devices
if (browser.mobile) hasLineNumbers = false;
setClassPresence(sideDiv, "sidedivhidden", !hasLineNumbers);
fixView();
}
else if (k == "grayedout")
{
setClassPresence(outerWin.document.body, "grayedout", !! value);
}
else if (k == "dmesg")
{
dmesg = value;
window.dmesg = value;
}
else if (k == 'userauthor')
{
thisAuthor = String(value);
}
else if (k == 'styled')
{
setStyled(value);
}
else if (k == 'textface')
{
setTextFace(value);
}
else if (k == 'textsize')
{
setTextSize(value);
}
else if (k == 'rtlistrue')
{
setClassPresence(root, "rtl", !! value);
}
}
};
editorInfo.ace_setBaseText = function(txt)
{
@ -1274,12 +1030,12 @@ function OUTER(gscope)
lastElapsed = elapsed;
return false;
}
}
};
isTimeUp.elapsed = function()
{
return now() - startTime;
}
};
return isTimeUp;
}
@ -1337,7 +1093,7 @@ function OUTER(gscope)
{
unschedule();
}
}
};
}
function fastIncorp(n)
@ -1529,7 +1285,7 @@ function OUTER(gscope)
}
var text = lineEntry.text;
var width = lineEntry.width; // text.length+1
if (text.length == 0)
if (text.length === 0)
{
// allow getLineStyleFilter to set line-div styles
var func = linestylefilter.getLineStyleFilter(
@ -1548,12 +1304,6 @@ function OUTER(gscope)
}
}
function getCharType(charIndex)
{
return '';
}
var observedChanges;
function clearObservedChanges()
@ -1662,17 +1412,19 @@ function OUTER(gscope)
var p = PROFILER("getSelection", false);
var selection = getSelection();
p.end();
function topLevel(n)
{
if ((!n) || n == root) return null;
while (n.parentNode != root)
{
n = n.parentNode;
}
return n;
}
if (selection)
{
function topLevel(n)
{
if ((!n) || n == root) return null;
while (n.parentNode != root)
{
n = n.parentNode;
}
return n;
}
var node1 = topLevel(selection.startPoint.node);
var node2 = topLevel(selection.endPoint.node);
if (node1) observeChangesAroundNode(node1);
@ -1745,7 +1497,7 @@ function OUTER(gscope)
{
a = dirtyRanges[j][0];
b = dirtyRanges[j][1];
if (!((a == 0 || getCleanNodeByKey(rep.lines.atIndex(a - 1).key)) && (b == rep.lines.length() || getCleanNodeByKey(rep.lines.atIndex(b).key))))
if (!((a === 0 || getCleanNodeByKey(rep.lines.atIndex(a - 1).key)) && (b == rep.lines.length() || getCleanNodeByKey(rep.lines.atIndex(b).key))))
{
dirtyRangesCheckOut = false;
break;
@ -1786,7 +1538,7 @@ function OUTER(gscope)
var range = dirtyRanges[i];
a = range[0];
b = range[1];
var firstDirtyNode = (((a == 0) && root.firstChild) || getCleanNodeByKey(rep.lines.atIndex(a - 1).key).nextSibling);
var firstDirtyNode = (((a === 0) && root.firstChild) || getCleanNodeByKey(rep.lines.atIndex(a - 1).key).nextSibling);
firstDirtyNode = (firstDirtyNode && isNodeDirty(firstDirtyNode) && firstDirtyNode);
var lastDirtyNode = (((b == rep.lines.length()) && root.lastChild) || getCleanNodeByKey(rep.lines.atIndex(b).key).previousSibling);
lastDirtyNode = (lastDirtyNode && isNodeDirty(lastDirtyNode) && lastDirtyNode);
@ -2084,7 +1836,7 @@ function OUTER(gscope)
function handleReturnIndentation()
{
// on return, indent to level of previous line
if (isCaret() && caretColumn() == 0 && caretLine() > 0)
if (isCaret() && caretColumn() === 0 && caretLine() > 0)
{
var lineNum = caretLine();
var thisLine = rep.lines.atIndex(lineNum);
@ -2166,10 +1918,10 @@ function OUTER(gscope)
var lineNode = lineEntry.lineNode;
var n = lineNode;
var after = false;
if (charsLeft == 0)
if (charsLeft === 0)
{
var index = 0;
if (browser.msie && line == (rep.lines.length() - 1) && lineNode.childNodes.length == 0)
if (browser.msie && line == (rep.lines.length() - 1) && lineNode.childNodes.length === 0)
{
// best to stay at end of last empty div in IE
index = 1;
@ -2233,7 +1985,7 @@ function OUTER(gscope)
// assuming the point is not in a dirty node.
if (point.node == root)
{
if (point.index == 0)
if (point.index === 0)
{
return [0, 0];
}
@ -2271,7 +2023,7 @@ function OUTER(gscope)
n = parNode;
}
}
if (n.id == "") console.debug("BAD");
if (n.id === "") console.debug("BAD");
if (n.firstChild && isBlockElement(n.firstChild))
{
col += 1; // lineMarker
@ -2496,7 +2248,7 @@ function OUTER(gscope)
function performDocumentReplaceCharRange(startChar, endChar, newText)
{
if (startChar == endChar && newText.length == 0)
if (startChar == endChar && newText.length === 0)
{
return;
}
@ -2513,7 +2265,7 @@ function OUTER(gscope)
endChar--;
newText = '\n' + newText.substring(0, newText.length - 1);
}
else if (newText.length == 0)
else if (newText.length === 0)
{
// a delete at end
startChar--;
@ -2531,8 +2283,8 @@ function OUTER(gscope)
function performDocumentReplaceRange(start, end, newText)
{
if (start == undefined) start = rep.selStart;
if (end == undefined) end = rep.selEnd;
if (start === undefined) start = rep.selStart;
if (end === undefined) end = rep.selEnd;
//dmesg(String([start.toSource(),end.toSource(),newText.toSource()]));
// start[0]: <--- start[1] --->CCCCCCCCCCC\n
@ -2772,7 +2524,7 @@ function OUTER(gscope)
spliceEnd--;
commonEnd++;
}
if (shortOldText.length == 0 && spliceStart == rep.alltext.length && shortNewText.length > 0)
if (shortOldText.length === 0 && spliceStart == rep.alltext.length && shortNewText.length > 0)
{
// inserting after final newline, bad
spliceStart--;
@ -2780,7 +2532,7 @@ function OUTER(gscope)
shortNewText = '\n' + shortNewText.slice(0, -1);
shiftFinalNewlineToBeforeNewText = true;
}
if (spliceEnd == rep.alltext.length && shortOldText.length > 0 && shortNewText.length == 0)
if (spliceEnd == rep.alltext.length && shortOldText.length > 0 && shortNewText.length === 0)
{
// deletion at end of rep.alltext
if (rep.alltext.charAt(spliceStart - 1) == '\n')
@ -2792,7 +2544,7 @@ function OUTER(gscope)
}
}
if (!(shortOldText.length == 0 && shortNewText.length == 0))
if (!(shortOldText.length === 0 && shortNewText.length === 0))
{
var oldDocText = rep.alltext;
var oldLen = oldDocText.length;
@ -2800,15 +2552,15 @@ function OUTER(gscope)
var spliceStartLine = rep.lines.indexOfOffset(spliceStart);
var spliceStartLineStart = rep.lines.offsetOfIndex(spliceStartLine);
function startBuilder()
var startBuilder = function()
{
var builder = Changeset.builder(oldLen);
builder.keep(spliceStartLineStart, spliceStartLine);
builder.keep(spliceStart - spliceStartLineStart);
return builder;
}
};
function eachAttribRun(attribs, func /*(startInNewText, endInNewText, attribs)*/ )
var eachAttribRun = function(attribs, func /*(startInNewText, endInNewText, attribs)*/ )
{
var attribsIter = Changeset.opIterator(attribs);
var textIndex = 0;
@ -2824,7 +2576,7 @@ function OUTER(gscope)
}
textIndex = nextIndex;
}
}
};
var justApplyStyles = (shortNewText == shortOldText);
var theChangeset;
@ -3024,7 +2776,7 @@ function OUTER(gscope)
var newEndIter = attribIterator(newARuns, true);
while (commonEnd < minLen)
{
if (commonEnd == 0)
if (commonEnd === 0)
{
// assume newline in common
oldEndIter();
@ -3167,10 +2919,11 @@ function OUTER(gscope)
lineClass = ''; // non-null to cause update
};
function writeClass()
var writeClass = function()
{
if (lineClass !== null) lineElem.className = lineClass;
}
};
result.prepareForAdd = writeClass;
result.finishUpdate = writeClass;
result.getInnerHTML = function()
@ -3397,7 +3150,7 @@ function OUTER(gscope)
}
}
if (N == 0)
if (N === 0)
{
p.cancel();
if (!isConsecutive(0))
@ -3519,26 +3272,20 @@ function OUTER(gscope)
function handleClick(evt)
{
//hide the dropdowns
if(window.top.padeditbar){ // required in case its in an iframe should probably use parent.. See Issue 327 https://github.com/Pita/etherpad-lite/issues/327
window.top.padeditbar.toogleDropDown("none");
}
inCallStack("handleClick", function()
{
idleWorkTimer.atMost(200);
});
function isLink(n)
{
return (n.tagName || '').toLowerCase() == "a" && n.href;
}
// only want to catch left-click
if ((!evt.ctrlKey) && (evt.button != 2) && (evt.button != 3))
{
// find A tag with HREF
function isLink(n)
{
return (n.tagName || '').toLowerCase() == "a" && n.href;
}
var n = evt.target;
while (n && n.parentNode && !isLink(n))
{
@ -3558,6 +3305,10 @@ function OUTER(gscope)
evt.preventDefault();
}
}
//hide the dropdownso
if(window.parent.parent.padeditbar){ // required in case its in an iframe should probably use parent.. See Issue 327 https://github.com/Pita/etherpad-lite/issues/327
window.parent.parent.padeditbar.toogleDropDown("none");
}
}
function doReturnKey()
@ -3613,7 +3364,7 @@ function OUTER(gscope)
var firstLine, lastLine;
firstLine = rep.selStart[0];
lastLine = Math.max(firstLine, rep.selEnd[0] - ((rep.selEnd[1] == 0) ? 1 : 0));
lastLine = Math.max(firstLine, rep.selEnd[0] - ((rep.selEnd[1] === 0) ? 1 : 0));
var mods = [];
for (var n = firstLine; n <= lastLine; n++)
@ -3747,7 +3498,7 @@ function OUTER(gscope)
//separated. If it returns null, it means that the list was not cut, try
//from the current one.
var line = caretLine();
if(line != -1 && renumberList(line+1)==null)
if(line != -1 && renumberList(line+1) === null)
{
renumberList(line);
}
@ -3968,7 +3719,7 @@ function OUTER(gscope)
}
else if (type == "keypress")
{
if ((!specialHandled) && parenModule.shouldNormalizeOnChar(charCode))
if ((!specialHandled) && false /*parenModule.shouldNormalizeOnChar(charCode)*/)
{
idleWorkTimer.atMost(0);
}
@ -3985,7 +3736,7 @@ function OUTER(gscope)
}
// Is part of multi-keystroke international character on Firefox Mac
var isFirefoxHalfCharacter = (browser.mozilla && evt.altKey && charCode == 0 && keyCode == 0);
var isFirefoxHalfCharacter = (browser.mozilla && evt.altKey && charCode === 0 && keyCode === 0);
// Is part of multi-keystroke international character on Safari Mac
var isSafariHalfCharacter = (browser.safari && evt.altKey && keyCode == 229);
@ -4084,7 +3835,7 @@ function OUTER(gscope)
{
var text = entry.text;
var content;
if (text.length == 0)
if (text.length === 0)
{
content = '<span style="color: #aaa">--</span>';
}
@ -4150,27 +3901,27 @@ function OUTER(gscope)
var selectionParent = origSelectionRange.parentElement();
if (selectionParent.ownerDocument != doc) return null;
function newRange()
var newRange = function()
{
return doc.body.createTextRange();
}
};
function rangeForElementNode(nd)
var rangeForElementNode = function(nd)
{
var rng = newRange();
// doesn't work on text nodes
rng.moveToElementText(nd);
return rng;
}
};
function pointFromCollapsedRange(rng)
var pointFromCollapsedRange = function(rng)
{
var parNode = rng.parentElement();
var elemBelow = -1;
var elemAbove = parNode.childNodes.length;
var rangeWithin = rangeForElementNode(parNode);
if (rng.compareEndPoints("StartToStart", rangeWithin) == 0)
if (rng.compareEndPoints("StartToStart", rangeWithin) === 0)
{
return {
node: parNode,
@ -4178,7 +3929,7 @@ function OUTER(gscope)
maxIndex: 1
};
}
else if (rng.compareEndPoints("EndToEnd", rangeWithin) == 0)
else if (rng.compareEndPoints("EndToEnd", rangeWithin) === 0)
{
if (isBlockElement(parNode) && parNode.nextSibling)
{
@ -4196,7 +3947,7 @@ function OUTER(gscope)
maxIndex: 1
};
}
else if (parNode.childNodes.length == 0)
else if (parNode.childNodes.length === 0)
{
return {
node: parNode,
@ -4305,9 +4056,10 @@ function OUTER(gscope)
index: tn.nodeValue.length,
maxIndex: tn.nodeValue.length
};
}
};
var selection = {};
if (origSelectionRange.compareEndPoints("StartToEnd", origSelectionRange) == 0)
if (origSelectionRange.compareEndPoints("StartToEnd", origSelectionRange) === 0)
{
// collapsed
var pnt = pointFromCollapsedRange(origSelectionRange);
@ -4327,10 +4079,10 @@ function OUTER(gscope)
selection.startPoint = pointFromCollapsedRange(start);
selection.endPoint = pointFromCollapsedRange(end);
/*if ((!selection.startPoint.node.isText) && (!selection.endPoint.node.isText)) {
console.log(selection.startPoint.node.uniqueId()+","+
selection.startPoint.index+" / "+
selection.endPoint.node.uniqueId()+","+
selection.endPoint.index);
console.log(selection.startPoint.node.uniqueId()+","+
selection.startPoint.index+" / "+
selection.endPoint.node.uniqueId()+","+
selection.endPoint.index);
}*/
}
return selection;
@ -4373,7 +4125,7 @@ function OUTER(gscope)
maxIndex: n.nodeValue.length
};
}
else if (childCount == 0)
else if (childCount === 0)
{
return {
node: n,
@ -4499,7 +4251,7 @@ function OUTER(gscope)
setCollapsedBefore(s, n);
s.move("character", point.index);
}
else if (point.index == 0)
else if (point.index === 0)
{
setCollapsedBefore(s, n);
}
@ -4587,7 +4339,7 @@ function OUTER(gscope)
while (p.node.childNodes.length > 0)
{
//&& (p.node == root || p.node.parentNode == root)) {
if (p.index == 0)
if (p.index === 0)
{
p.node = p.node.firstChild;
p.maxIndex = nodeMaxIndex(p.node);
@ -4690,7 +4442,7 @@ function OUTER(gscope)
function fixView()
{
// calling this method repeatedly should be fast
if (getInnerWidth() == 0 || getInnerHeight() == 0)
if (getInnerWidth() === 0 || getInnerHeight() === 0)
{
return;
}
@ -5109,7 +4861,7 @@ function OUTER(gscope)
}
if (!isNodeText(node))
{
if (index == 0) return leftOf(node);
if (index === 0) return leftOf(node);
else return rightOf(node);
}
else
@ -5390,7 +5142,7 @@ function OUTER(gscope)
var firstLine, lastLine;
firstLine = rep.selStart[0];
lastLine = Math.max(firstLine, rep.selEnd[0] - ((rep.selEnd[1] == 0) ? 1 : 0));
lastLine = Math.max(firstLine, rep.selEnd[0] - ((rep.selEnd[1] === 0) ? 1 : 0));
var allLinesAreList = true;
for (var n = firstLine; n <= lastLine; n++)
@ -5600,7 +5352,7 @@ function OUTER(gscope)
// move by "paragraph", a feature that Firefox lacks but IE and Safari both have
if (up)
{
if (focusCaret[1] == 0 && canChangeLines)
if (focusCaret[1] === 0 && canChangeLines)
{
focusCaret[0]--;
focusCaret[1] = 0;
@ -5828,16 +5580,19 @@ function OUTER(gscope)
{
var newNumLines = rep.lines.length();
if (newNumLines < 1) newNumLines = 1;
//update height of all current line numbers
//update height of all current line numbers
var a = sideDivInner.firstChild;
var b = doc.body.firstChild;
var n = 0;
if (currentCallStack && currentCallStack.domClean)
{
var a = sideDivInner.firstChild;
var b = doc.body.firstChild;
var n = 0;
while (a && b)
{
if(n > lineNumbersShown) //all updated, break
break;
if(n > lineNumbersShown) //all updated, break
break;
var h = (b.clientHeight || b.offsetHeight);
if (b.nextSibling)
@ -5853,12 +5608,12 @@ function OUTER(gscope)
{
var hpx = h + "px";
if (a.style.height != hpx) {
a.style.height = hpx;
}
a.style.height = hpx;
}
}
a = a.nextSibling;
b = b.nextSibling;
n++;
n++;
}
}
@ -5872,17 +5627,20 @@ function OUTER(gscope)
lineNumbersShown++;
var n = lineNumbersShown;
var div = odoc.createElement("DIV");
//calculate height for new line number
var h = (b.clientHeight || b.offsetHeight);
if (b.nextSibling)
h = b.nextSibling.offsetTop - b.offsetTop;
if(h) // apply style to div
div.style.height = h +"px";
//calculate height for new line number
var h = (b.clientHeight || b.offsetHeight);
if (b.nextSibling)
h = b.nextSibling.offsetTop - b.offsetTop;
if(h) // apply style to div
div.style.height = h +"px";
div.appendChild(odoc.createTextNode(String(n)));
fragment.appendChild(div);
b = b.nextSibling;
fragment.appendChild(div);
b = b.nextSibling;
}
container.appendChild(fragment);
while (lineNumbersShown > newNumLines)
{
@ -5892,8 +5650,6 @@ function OUTER(gscope)
}
}
};
}
OUTER(this);
exports.OUTER = OUTER; // This is probably unimportant.
exports.editor = new Ace2Inner();

View File

@ -26,47 +26,16 @@ var AttribPool = require('/AttributePoolFactory').createAttributePool;
var Changeset = require('/Changeset');
var linestylefilter = require('/linestylefilter').linestylefilter;
var colorutils = require('/colorutils').colorutils;
var Ace2Common = require('./ace2_common');
var map = Ace2Common.map;
var forEach = Ace2Common.forEach;
// These parameters were global, now they are injected. A reference to the
// Timeslider controller would probably be more appropriate.
function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, BroadcastSlider)
{
var changesetLoader = undefined;
// 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);
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;
};
}
// 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
if (!Array.prototype.indexOf)
@ -99,11 +68,6 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro
}
}
function randomString()
{
return "_" + Math.floor(Math.random() * 1000000);
}
// for IE
if ($.browser.msie)
{
@ -115,7 +79,7 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro
{}
}
var userId = "hiddenUser" + randomString();
var socketId;
//var socket;
var channelState = "DISCONNECTED";
@ -191,10 +155,7 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro
// splice the lines
splice: function(start, numRemoved, newLinesVA)
{
var newLines = Array.prototype.slice.call(arguments, 2).map(
function(s)
{
var newLines = map(Array.prototype.slice.call(arguments, 2), function(s) {
return s;
});
@ -316,10 +277,13 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro
padContents.currentTime += timeDelta * 1000;
debugLog('Time Delta: ', timeDelta)
updateTimer();
BroadcastSlider.setAuthors(padContents.getActiveAuthors().map(function(name)
var authors = map(padContents.getActiveAuthors(), function(name)
{
return authorData[name];
}));
});
BroadcastSlider.setAuthors(authors);
}
function updateTimer()
@ -419,10 +383,11 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro
changesetLoader.queueUp(start, 1, update);
}
BroadcastSlider.setAuthors(padContents.getActiveAuthors().map(function(name)
{
var authors = map(padContents.getActiveAuthors(), function(name){
return authorData[name];
}));
});
BroadcastSlider.setAuthors(authors);
}
changesetLoader = {
@ -561,10 +526,12 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro
var authorMap = {};
authorMap[obj.author] = obj.data;
receiveAuthorData(authorMap);
BroadcastSlider.setAuthors(padContents.getActiveAuthors().map(function(name)
{
var authors = map(padContents.getActiveAuthors(),function(name) {
return authorData[name];
}));
});
BroadcastSlider.setAuthors(authors);
}
else if (obj['type'] == "NEW_SAVEDREV")
{
@ -616,53 +583,6 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro
}));
}
/*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)
{
@ -691,7 +611,7 @@ function loadBroadcastJS(socket, sendSocketMsg, fireWhenAllScriptsAreLoaded, Bro
window.onload = function ()
{
window['isloaded'] = true;
window['onloadFuncts'].forEach(function (funct)
forEach(window['onloadFuncts'],function (funct)
{
funct();
});

View File

@ -42,13 +42,13 @@ var chat = (function()
chat.show();
if(!isStuck || fromInitialCall) { // Stick it to
padcookie.setPref("chatAlwaysVisible", true);
$('#chatbox').css({"right":"0px", "top":"36px", "border-radius":"0px", "height":"auto", "border-right":"none", "border-left":"1px solid #ccc", "border-top":"none", "background-color":"#f1f1f1", "width":"185px"});
$('#chatbox').addClass("stickyChat");
$('#chattext').css({"top":"0px"});
$('#editorcontainer').css({"right":"192px", "width":"auto"});
isStuck = true;
} else { // Unstick it
padcookie.setPref("chatAlwaysVisible", false);
$('#chatbox').css({"right":"20px", "top":"auto", "border-top-left-radius":"5px", "border-top-right-radius":"5px", "border-right":"1px solid #999", "height":"200px", "border-top":"1px solid #999", "background-color":"#f7f7f7"});
$('#chatbox').removeClass("stickyChat");
$('#chattext').css({"top":"25px"});
$('#editorcontainer').css({"right":"0px", "width":"100%"});
isStuck = false;

View File

@ -27,16 +27,13 @@
// requires: undefined
var Security = require('/security');
var Ace2Common = require('/ace2_common');
var plugins = require('/plugins').plugins;
var map = require('/ace2_common').map;
var map = Ace2Common.map;
var noop = Ace2Common.noop;
var identity = Ace2Common.identity;
var domline = {};
domline.noop = function()
{};
domline.identity = function(x)
{
return x;
};
domline.addToLineClass = function(lineClass, cls)
{
@ -60,11 +57,11 @@ 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,
appendSpan: noop,
prepareForAdd: noop,
notifyAdded: noop,
clearSpans: noop,
finishUpdate: noop,
lineMarker: 0
};
@ -91,7 +88,7 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument)
{
return domline.processSpaces(s, doesWrap);
}
var identity = domline.identity;
var perTextNodeProcess = (doesWrap ? identity : processSpaces);
var perHtmlLineProcess = (doesWrap ? processSpaces : identity);
var lineClass = 'ace-line';

View File

@ -146,6 +146,12 @@ function savePassword()
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()
{
var loc = document.location;
@ -364,7 +370,6 @@ var pad = {
{
return clientVars.userIsGuest;
},
//
getUserId: function()
{
return pad.myUserInfo.userId;
@ -384,6 +389,8 @@ var pad = {
$(document).ready(function()
{
// test for XML HTTP capabiites
ieTestXMLHTTP();
// start the custom js
if (typeof customStart == "function") customStart();
getParams();
@ -422,7 +429,7 @@ var pad = {
// order of inits is important here:
padcookie.init(clientVars.cookiePrefsToSet, this);
$("#widthprefcheck").click(pad.toggleWidthPref);
// $("#sidebarcheck").click(pad.togglewSidebar);
@ -477,6 +484,10 @@ var pad = {
{
padeditor.ace.focus();
}, 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
}
}
},
dispose: function()
@ -972,3 +983,4 @@ exports.handshake = handshake;
exports.pad = pad;
exports.init = init;
exports.alertBar = alertBar;

View File

@ -239,14 +239,14 @@ var padeditbar = (function()
var readonlyLink = basePath + "/ro/" + clientVars.readOnlyId;
$('#embedinput').val("<iframe src='" + readonlyLink + "?showControls=true&showChat=true&showLineNumbers=true&useMonospaceFont=false' width=600 height=400>");
$('#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
{
var padurl = window.location.href.split("?")[0];
$('#embedinput').val("<iframe src='" + padurl + "?showControls=true&showChat=true&showLineNumbers=true&useMonospaceFont=false' width=600 height=400>");
$('#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);
}
}
};

View File

@ -257,8 +257,6 @@ var padimpexp = (function()
$("#exportworda").remove();
$("#exportpdfa").remove();
$("#exportopena").remove();
$("#importexport").css({"height":"115px"});
$("#importexportline").css({"height":"115px"});
$("#import").html("Import is not available. To enable import please install abiword");
}
else if(clientVars.abiwordAvailable == "withoutPDF")

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);

View File

@ -21,7 +21,7 @@
*/
var noop = require('./ace2_common').noop;
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
var start = {
key: null,

View File

@ -5,7 +5,7 @@
<meta charset="utf-8">
<meta name="robots" content="noindex, nofollow">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<meta name="viewport" content="width=device-width, user-scalable=0">
<link href="../static/css/pad.css" rel="stylesheet">
<link href="../static/custom/pad.css" rel="stylesheet">
@ -170,6 +170,7 @@
<input type="file" name="file" size="15" id="importfileinput">
<div class="importmessage" id="importmessagefail"></div>
</div>
<div id="import"></div>
<div class="importmessage" id="importmessagesuccess">Successful!</div>
<div class="importformdiv" id="importformsubmitdiv">
<input type="hidden" name="padId" value="blpmaXT35R">