summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan 'Gared <mu.stefan@googlemail.com>2012-02-26 19:52:09 +0000
committerStefan 'Gared <mu.stefan@googlemail.com>2012-02-26 19:52:09 +0000
commit6706332e4282e003c369f32c7d3191842301e4a2 (patch)
tree6f53cec0be3df75f7500d0421fef7001c832f0c8
parent38d93eac39484bd598094838f5862bba4f5295b5 (diff)
parent7d923277a673f936e30ebf1ba94f7f55090cef96 (diff)
downloadetherpad-lite-6706332e4282e003c369f32c7d3191842301e4a2.zip
Merge branch 'master' of http://github.com/Pita/etherpad-lite
-rw-r--r--.gitignore1
-rw-r--r--LICENSE202
-rw-r--r--README.md6
-rw-r--r--bin/loadTesting/README75
-rwxr-xr-xbin/loadTesting/launcher.sh16
-rw-r--r--bin/loadTesting/loader.js20
-rw-r--r--node/db/PadManager.js3
-rw-r--r--node/server.js2
-rw-r--r--static/css/iframe_editor.css31
-rw-r--r--static/css/pad.css194
-rw-r--r--static/favicon.icobin1150 -> 1150 bytes
-rw-r--r--static/index.html93
-rw-r--r--static/js/Changeset.js301
-rw-r--r--static/js/ace.js108
-rw-r--r--static/js/ace2_common.js5
-rw-r--r--static/js/ace2_inner.js552
-rw-r--r--static/js/broadcast.js122
-rw-r--r--static/js/chat.js4
-rw-r--r--static/js/domline.js23
-rw-r--r--static/js/pad.js16
-rw-r--r--static/js/pad_editbar.js4
-rw-r--r--static/js/pad_impexp.js2
-rw-r--r--static/js/prefixfree.js421
-rw-r--r--static/js/skiplist.js5
-rw-r--r--static/pad.html3
25 files changed, 1432 insertions, 777 deletions
diff --git a/.gitignore b/.gitignore
index 6b1e54c6..31b7d2ea 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,4 @@ var/dirty.db
bin/convertSettings.json
*~
*.patch
+*.DS_Store \ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..a35c2553
--- /dev/null
+++ b/LICENSE
@@ -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.
diff --git a/README.md b/README.md
index d593115c..a996c905 100644
--- a/README.md
+++ b/README.md
@@ -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) \ No newline at end of file
diff --git a/bin/loadTesting/README b/bin/loadTesting/README
new file mode 100644
index 00000000..297756f9
--- /dev/null
+++ b/bin/loadTesting/README
@@ -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 ###
diff --git a/bin/loadTesting/launcher.sh b/bin/loadTesting/launcher.sh
new file mode 100755
index 00000000..375b1544
--- /dev/null
+++ b/bin/loadTesting/launcher.sh
@@ -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
diff --git a/bin/loadTesting/loader.js b/bin/loadTesting/loader.js
new file mode 100644
index 00000000..ddcd0572
--- /dev/null
+++ b/bin/loadTesting/loader.js
@@ -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);
+
+}
diff --git a/node/db/PadManager.js b/node/db/PadManager.js
index 231aa901..12682612 100644
--- a/node/db/PadManager.js
+++ b/node/db/PadManager.js
@@ -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, '_']
];
/**
diff --git a/node/server.js b/node/server.js
index a6a57497..f254c2ca 100644
--- a/node/server.js
+++ b/node/server.js
@@ -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);
}
diff --git a/static/css/iframe_editor.css b/static/css/iframe_editor.css
index 6a483c07..d2d2f977 100644
--- a/static/css/iframe_editor.css
+++ b/static/css/iframe_editor.css
@@ -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; }
-
-
diff --git a/static/css/pad.css b/static/css/pad.css
index e407b3a4..e12738b5 100644
--- a/static/css/pad.css
+++ b/static/css/pad.css
@@ -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;
+ }
+}
diff --git a/static/favicon.ico b/static/favicon.ico
index 2529c923..df7b6289 100644
--- a/static/favicon.ico
+++ b/static/favicon.ico
Binary files differ
diff --git a/static/index.html b/static/index.html
index 97736d1e..da24a7a7 100644
--- a/static/index.html
+++ b/static/index.html
@@ -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()
{
diff --git a/static/js/Changeset.js b/static/js/Changeset.js
index 715836d5..81c0c81b 100644
--- a/static/js/Changeset.js
+++ b/static/js/Changeset.js
@@ -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();
diff --git a/static/js/ace.js b/static/js/ace.js
index 04930910..6c7bb84e 100644
--- a/static/js/ace.js
+++ b/static/js/ace.js
@@ -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 = [];
diff --git a/static/js/ace2_common.js b/static/js/ace2_common.js
index b4c72a92..0f8195fa 100644
--- a/static/js/ace2_common.js
+++ b/static/js/ace2_common.js
@@ -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;
diff --git a/static/js/ace2_inner.js b/static/js/ace2_inner.js
index d2113574..2418b384 100644
--- a/static/js/ace2_inner.js
+++ b/static/js/ace2_inner.js
@@ -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);
- }
- 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);
+
+ // 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)
}
- }
+ };
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();
- if (selection)
+
+ function topLevel(n)
{
- function topLevel(n)
+ if ((!n) || n == root) return null;
+ while (n.parentNode != root)
{
- if ((!n) || n == root) return null;
- while (n.parentNode != root)
- {
- n = n.parentNode;
- }
- return n;
+ n = n.parentNode;
}
+ return n;
+ }
+
+ if (selection)
+ {
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();
diff --git a/static/js/broadcast.js b/static/js/broadcast.js
index 4a7b0168..97256bbf 100644
--- a/static/js/broadcast.js
+++ b/static/js/broadcast.js
@@ -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();
});
diff --git a/static/js/chat.js b/static/js/chat.js
index fb2acac1..8f076af6 100644
--- a/static/js/chat.js
+++ b/static/js/chat.js
@@ -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;
diff --git a/static/js/domline.js b/static/js/domline.js
index 15528bf7..3074c9e9 100644
--- a/static/js/domline.js
+++ b/static/js/domline.js
@@ -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';
diff --git a/static/js/pad.js b/static/js/pad.js
index 537ebed2..d6bb4c71 100644
--- a/static/js/pad.js
+++ b/static/js/pad.js
@@ -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;
+
diff --git a/static/js/pad_editbar.js b/static/js/pad_editbar.js
index 8e4ffa3c..23692631 100644
--- a/static/js/pad_editbar.js
+++ b/static/js/pad_editbar.js
@@ -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);
}
}
};
diff --git a/static/js/pad_impexp.js b/static/js/pad_impexp.js
index 0037195f..6fe42c70 100644
--- a/static/js/pad_impexp.js
+++ b/static/js/pad_impexp.js
@@ -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")
diff --git a/static/js/prefixfree.js b/static/js/prefixfree.js
new file mode 100644
index 00000000..1f62dc34
--- /dev/null
+++ b/static/js/prefixfree.js
@@ -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 shoudn’t 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);
diff --git a/static/js/skiplist.js b/static/js/skiplist.js
index 385f08f0..190bc55b 100644
--- a/static/js/skiplist.js
+++ b/static/js/skiplist.js
@@ -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,
diff --git a/static/pad.html b/static/pad.html
index 0345d65b..ea0ad6f3 100644
--- a/static/pad.html
+++ b/static/pad.html
@@ -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">