diff options
author | Stefan 'Gared <mu.stefan@googlemail.com> | 2012-02-26 19:52:09 +0000 |
---|---|---|
committer | Stefan 'Gared <mu.stefan@googlemail.com> | 2012-02-26 19:52:09 +0000 |
commit | 6706332e4282e003c369f32c7d3191842301e4a2 (patch) | |
tree | 6f53cec0be3df75f7500d0421fef7001c832f0c8 | |
parent | 38d93eac39484bd598094838f5862bba4f5295b5 (diff) | |
parent | 7d923277a673f936e30ebf1ba94f7f55090cef96 (diff) | |
download | etherpad-lite-6706332e4282e003c369f32c7d3191842301e4a2.zip |
Merge branch 'master' of http://github.com/Pita/etherpad-lite
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | LICENSE | 202 | ||||
-rw-r--r-- | README.md | 6 | ||||
-rw-r--r-- | bin/loadTesting/README | 75 | ||||
-rwxr-xr-x | bin/loadTesting/launcher.sh | 16 | ||||
-rw-r--r-- | bin/loadTesting/loader.js | 20 | ||||
-rw-r--r-- | node/db/PadManager.js | 3 | ||||
-rw-r--r-- | node/server.js | 2 | ||||
-rw-r--r-- | static/css/iframe_editor.css | 31 | ||||
-rw-r--r-- | static/css/pad.css | 194 | ||||
-rw-r--r-- | static/favicon.ico | bin | 1150 -> 1150 bytes | |||
-rw-r--r-- | static/index.html | 93 | ||||
-rw-r--r-- | static/js/Changeset.js | 301 | ||||
-rw-r--r-- | static/js/ace.js | 108 | ||||
-rw-r--r-- | static/js/ace2_common.js | 5 | ||||
-rw-r--r-- | static/js/ace2_inner.js | 552 | ||||
-rw-r--r-- | static/js/broadcast.js | 122 | ||||
-rw-r--r-- | static/js/chat.js | 4 | ||||
-rw-r--r-- | static/js/domline.js | 23 | ||||
-rw-r--r-- | static/js/pad.js | 16 | ||||
-rw-r--r-- | static/js/pad_editbar.js | 4 | ||||
-rw-r--r-- | static/js/pad_impexp.js | 2 | ||||
-rw-r--r-- | static/js/prefixfree.js | 421 | ||||
-rw-r--r-- | static/js/skiplist.js | 5 | ||||
-rw-r--r-- | static/pad.html | 3 |
25 files changed, 1432 insertions, 777 deletions
@@ -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. @@ -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 Binary files differindex 2529c923..df7b6289 100644 --- a/static/favicon.ico +++ b/static/favicon.ico 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"> |