mirror of
				https://github.com/ether/etherpad-lite.git
				synced 2025-10-31 16:21:11 +01:00 
			
		
		
		
	Merge branch 'pita'
Resolved conflicts: .gitignore src/static/js/ace.js src/static/js/ace2_inner.js src/static/js/broadcast.js src/static/js/domline.js src/static/pad.html src/static/timeslider.html Ignored conflicts (please merge manually later): node/server.js src/node/utils/Minify.js
This commit is contained in:
		
						commit
						ce5d2d8685
					
				
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -13,3 +13,4 @@ bin/convertSettings.json | ||||
| src/static/js/jquery.js | ||||
| src/static/js/prefixfree.js | ||||
| npm-debug.log | ||||
| *.DS_Store | ||||
|  | ||||
							
								
								
									
										202
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,202 @@ | ||||
| 
 | ||||
|                                  Apache License | ||||
|                            Version 2.0, January 2004 | ||||
|                         http://www.apache.org/licenses/ | ||||
| 
 | ||||
|    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||||
| 
 | ||||
|    1. Definitions. | ||||
| 
 | ||||
|       "License" shall mean the terms and conditions for use, reproduction, | ||||
|       and distribution as defined by Sections 1 through 9 of this document. | ||||
| 
 | ||||
|       "Licensor" shall mean the copyright owner or entity authorized by | ||||
|       the copyright owner that is granting the License. | ||||
| 
 | ||||
|       "Legal Entity" shall mean the union of the acting entity and all | ||||
|       other entities that control, are controlled by, or are under common | ||||
|       control with that entity. For the purposes of this definition, | ||||
|       "control" means (i) the power, direct or indirect, to cause the | ||||
|       direction or management of such entity, whether by contract or | ||||
|       otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||||
|       outstanding shares, or (iii) beneficial ownership of such entity. | ||||
| 
 | ||||
|       "You" (or "Your") shall mean an individual or Legal Entity | ||||
|       exercising permissions granted by this License. | ||||
| 
 | ||||
|       "Source" form shall mean the preferred form for making modifications, | ||||
|       including but not limited to software source code, documentation | ||||
|       source, and configuration files. | ||||
| 
 | ||||
|       "Object" form shall mean any form resulting from mechanical | ||||
|       transformation or translation of a Source form, including but | ||||
|       not limited to compiled object code, generated documentation, | ||||
|       and conversions to other media types. | ||||
| 
 | ||||
|       "Work" shall mean the work of authorship, whether in Source or | ||||
|       Object form, made available under the License, as indicated by a | ||||
|       copyright notice that is included in or attached to the work | ||||
|       (an example is provided in the Appendix below). | ||||
| 
 | ||||
|       "Derivative Works" shall mean any work, whether in Source or Object | ||||
|       form, that is based on (or derived from) the Work and for which the | ||||
|       editorial revisions, annotations, elaborations, or other modifications | ||||
|       represent, as a whole, an original work of authorship. For the purposes | ||||
|       of this License, Derivative Works shall not include works that remain | ||||
|       separable from, or merely link (or bind by name) to the interfaces of, | ||||
|       the Work and Derivative Works thereof. | ||||
| 
 | ||||
|       "Contribution" shall mean any work of authorship, including | ||||
|       the original version of the Work and any modifications or additions | ||||
|       to that Work or Derivative Works thereof, that is intentionally | ||||
|       submitted to Licensor for inclusion in the Work by the copyright owner | ||||
|       or by an individual or Legal Entity authorized to submit on behalf of | ||||
|       the copyright owner. For the purposes of this definition, "submitted" | ||||
|       means any form of electronic, verbal, or written communication sent | ||||
|       to the Licensor or its representatives, including but not limited to | ||||
|       communication on electronic mailing lists, source code control systems, | ||||
|       and issue tracking systems that are managed by, or on behalf of, the | ||||
|       Licensor for the purpose of discussing and improving the Work, but | ||||
|       excluding communication that is conspicuously marked or otherwise | ||||
|       designated in writing by the copyright owner as "Not a Contribution." | ||||
| 
 | ||||
|       "Contributor" shall mean Licensor and any individual or Legal Entity | ||||
|       on behalf of whom a Contribution has been received by Licensor and | ||||
|       subsequently incorporated within the Work. | ||||
| 
 | ||||
|    2. Grant of Copyright License. Subject to the terms and conditions of | ||||
|       this License, each Contributor hereby grants to You a perpetual, | ||||
|       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|       copyright license to reproduce, prepare Derivative Works of, | ||||
|       publicly display, publicly perform, sublicense, and distribute the | ||||
|       Work and such Derivative Works in Source or Object form. | ||||
| 
 | ||||
|    3. Grant of Patent License. Subject to the terms and conditions of | ||||
|       this License, each Contributor hereby grants to You a perpetual, | ||||
|       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|       (except as stated in this section) patent license to make, have made, | ||||
|       use, offer to sell, sell, import, and otherwise transfer the Work, | ||||
|       where such license applies only to those patent claims licensable | ||||
|       by such Contributor that are necessarily infringed by their | ||||
|       Contribution(s) alone or by combination of their Contribution(s) | ||||
|       with the Work to which such Contribution(s) was submitted. If You | ||||
|       institute patent litigation against any entity (including a | ||||
|       cross-claim or counterclaim in a lawsuit) alleging that the Work | ||||
|       or a Contribution incorporated within the Work constitutes direct | ||||
|       or contributory patent infringement, then any patent licenses | ||||
|       granted to You under this License for that Work shall terminate | ||||
|       as of the date such litigation is filed. | ||||
| 
 | ||||
|    4. Redistribution. You may reproduce and distribute copies of the | ||||
|       Work or Derivative Works thereof in any medium, with or without | ||||
|       modifications, and in Source or Object form, provided that You | ||||
|       meet the following conditions: | ||||
| 
 | ||||
|       (a) You must give any other recipients of the Work or | ||||
|           Derivative Works a copy of this License; and | ||||
| 
 | ||||
|       (b) You must cause any modified files to carry prominent notices | ||||
|           stating that You changed the files; and | ||||
| 
 | ||||
|       (c) You must retain, in the Source form of any Derivative Works | ||||
|           that You distribute, all copyright, patent, trademark, and | ||||
|           attribution notices from the Source form of the Work, | ||||
|           excluding those notices that do not pertain to any part of | ||||
|           the Derivative Works; and | ||||
| 
 | ||||
|       (d) If the Work includes a "NOTICE" text file as part of its | ||||
|           distribution, then any Derivative Works that You distribute must | ||||
|           include a readable copy of the attribution notices contained | ||||
|           within such NOTICE file, excluding those notices that do not | ||||
|           pertain to any part of the Derivative Works, in at least one | ||||
|           of the following places: within a NOTICE text file distributed | ||||
|           as part of the Derivative Works; within the Source form or | ||||
|           documentation, if provided along with the Derivative Works; or, | ||||
|           within a display generated by the Derivative Works, if and | ||||
|           wherever such third-party notices normally appear. The contents | ||||
|           of the NOTICE file are for informational purposes only and | ||||
|           do not modify the License. You may add Your own attribution | ||||
|           notices within Derivative Works that You distribute, alongside | ||||
|           or as an addendum to the NOTICE text from the Work, provided | ||||
|           that such additional attribution notices cannot be construed | ||||
|           as modifying the License. | ||||
| 
 | ||||
|       You may add Your own copyright statement to Your modifications and | ||||
|       may provide additional or different license terms and conditions | ||||
|       for use, reproduction, or distribution of Your modifications, or | ||||
|       for any such Derivative Works as a whole, provided Your use, | ||||
|       reproduction, and distribution of the Work otherwise complies with | ||||
|       the conditions stated in this License. | ||||
| 
 | ||||
|    5. Submission of Contributions. Unless You explicitly state otherwise, | ||||
|       any Contribution intentionally submitted for inclusion in the Work | ||||
|       by You to the Licensor shall be under the terms and conditions of | ||||
|       this License, without any additional terms or conditions. | ||||
|       Notwithstanding the above, nothing herein shall supersede or modify | ||||
|       the terms of any separate license agreement you may have executed | ||||
|       with Licensor regarding such Contributions. | ||||
| 
 | ||||
|    6. Trademarks. This License does not grant permission to use the trade | ||||
|       names, trademarks, service marks, or product names of the Licensor, | ||||
|       except as required for reasonable and customary use in describing the | ||||
|       origin of the Work and reproducing the content of the NOTICE file. | ||||
| 
 | ||||
|    7. Disclaimer of Warranty. Unless required by applicable law or | ||||
|       agreed to in writing, Licensor provides the Work (and each | ||||
|       Contributor provides its Contributions) on an "AS IS" BASIS, | ||||
|       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||||
|       implied, including, without limitation, any warranties or conditions | ||||
|       of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | ||||
|       PARTICULAR PURPOSE. You are solely responsible for determining the | ||||
|       appropriateness of using or redistributing the Work and assume any | ||||
|       risks associated with Your exercise of permissions under this License. | ||||
| 
 | ||||
|    8. Limitation of Liability. In no event and under no legal theory, | ||||
|       whether in tort (including negligence), contract, or otherwise, | ||||
|       unless required by applicable law (such as deliberate and grossly | ||||
|       negligent acts) or agreed to in writing, shall any Contributor be | ||||
|       liable to You for damages, including any direct, indirect, special, | ||||
|       incidental, or consequential damages of any character arising as a | ||||
|       result of this License or out of the use or inability to use the | ||||
|       Work (including but not limited to damages for loss of goodwill, | ||||
|       work stoppage, computer failure or malfunction, or any and all | ||||
|       other commercial damages or losses), even if such Contributor | ||||
|       has been advised of the possibility of such damages. | ||||
| 
 | ||||
|    9. Accepting Warranty or Additional Liability. While redistributing | ||||
|       the Work or Derivative Works thereof, You may choose to offer, | ||||
|       and charge a fee for, acceptance of support, warranty, indemnity, | ||||
|       or other liability obligations and/or rights consistent with this | ||||
|       License. However, in accepting such obligations, You may act only | ||||
|       on Your own behalf and on Your sole responsibility, not on behalf | ||||
|       of any other Contributor, and only if You agree to indemnify, | ||||
|       defend, and hold each Contributor harmless for any liability | ||||
|       incurred by, or claims asserted against, such Contributor by reason | ||||
|       of your accepting any such warranty or additional liability. | ||||
| 
 | ||||
|    END OF TERMS AND CONDITIONS | ||||
| 
 | ||||
|    APPENDIX: How to apply the Apache License to your work. | ||||
| 
 | ||||
|       To apply the Apache License to your work, attach the following | ||||
|       boilerplate notice, with the fields enclosed by brackets "[]" | ||||
|       replaced with your own identifying information. (Don't include | ||||
|       the brackets!)  The text should be enclosed in the appropriate | ||||
|       comment syntax for the file format. We also recommend that a | ||||
|       file or class name and description of purpose be included on the | ||||
|       same "printed page" as the copyright notice for easier | ||||
|       identification within third-party archives. | ||||
| 
 | ||||
|    Copyright 2012 THE ETHERPAD FOUNDATION | ||||
| 
 | ||||
|    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|    you may not use this file except in compliance with the License. | ||||
|    You may obtain a copy of the License at | ||||
| 
 | ||||
|        http://www.apache.org/licenses/LICENSE-2.0 | ||||
| 
 | ||||
|    Unless required by applicable law or agreed to in writing, software | ||||
|    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|    See the License for the specific language governing permissions and | ||||
|    limitations under the License. | ||||
| @ -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] (http://etherpad.org) <-- Click the donate button | ||||
| 
 | ||||
| # License | ||||
| [Apache License v2](http://www.apache.org/licenses/LICENSE-2.0.html) | ||||
| [Apache License v2](http://www.apache.org/licenses/LICENSE-2.0.html) | ||||
							
								
								
									
										75
									
								
								bin/loadTesting/README
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								bin/loadTesting/README
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,75 @@ | ||||
| This load tester is extremely useful for testing how many dormant clients can connect to etherpad lite.   | ||||
| 
 | ||||
| TODO: | ||||
| Emulate characters being typed into a pad | ||||
| 
 | ||||
| HOW TO USE (from @mjd75) proper formatting at: https://github.com/Pita/etherpad-lite/issues/360 | ||||
| 
 | ||||
| Server 1: | ||||
| Installed Node.js (etc), EtherPad Lite and MySQL | ||||
| 
 | ||||
| Server 2: | ||||
| Installed Xvfb and PhantomJS | ||||
| 
 | ||||
| I installed Xvfb following (roughly) this guide: http://blog.martin-lyness.com/archives/installing-xvfb-on-ubuntu-9-10-karmic-koala | ||||
| 
 | ||||
| 	#sudo apt-get install xvfb | ||||
| 	#sudo apt-get install xfonts-100dpi xfonts-75dpi xfonts-scalable xfonts-cyrillic    | ||||
| 
 | ||||
| Launched two instances of Xvfb directly from the terminal: | ||||
| 
 | ||||
| 	#Xvfb :0 -ac | ||||
| 	#Xvfb :1 -ac | ||||
| 
 | ||||
| I installed PhantomJS following this guide: http://code.google.com/p/phantomjs/wiki/Installation | ||||
| 
 | ||||
| 	#sudo add-apt-repository ppa:jerome-etienne/neoip | ||||
| 	#sudo apt-get update | ||||
| 	#sudo apt-get install phantomjs | ||||
| 
 | ||||
| I created a small JavaScript file for PhatomJS to use to control the browser instances: | ||||
| 
 | ||||
| ### BEGIN JAVASCRIPT ### | ||||
| 
 | ||||
| var page = new WebPage(), | ||||
|     t, address; | ||||
| 
 | ||||
| if (phantom.args.length === 0) { | ||||
|     console.log('Usage: loader.js <some URL>'); | ||||
|     phantom.exit(); | ||||
| } else { | ||||
|     t = Date.now(); | ||||
|     address = phantom.args[0]; | ||||
| 
 | ||||
|     var page = new WebPage(); | ||||
|     page.onResourceRequested = function (request) { | ||||
|         console.log('Request ' + JSON.stringify(request, undefined, 4)); | ||||
|     }; | ||||
|     page.onResourceReceived = function (response) { | ||||
|         console.log('Receive ' + JSON.stringify(response, undefined, 4)); | ||||
|     }; | ||||
|     page.open(address); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| ### END JAVASCRIPT ### | ||||
| 
 | ||||
| And finally a launcher script that uses screen to run 400 instances of PhantomJS with the above script: | ||||
| 
 | ||||
| ### BEGIN SHELL SCRIPT ### | ||||
| 
 | ||||
| #!/bin/bash      | ||||
| 
 | ||||
| # connect 200 instances to display :0                                                              | ||||
| for i in {1..200} | ||||
| do | ||||
|   DISPLAY=:0 screen -d -m phantomjs loader.js http://ec2-50-17-168-xx.compute-1.amazonaws.com:9001/p/pad2 && sleep 2 | ||||
| done | ||||
| 
 | ||||
| # connect 200 instances to display :1                                                              | ||||
| for i in {1..200} | ||||
| do | ||||
|   DISPLAY=:1 screen -d -m phantomjs loader.js http://ec2-50-17-168-xx.compute-1.amazonaws.com:9001/p/pad2 && sleep 2 | ||||
| done | ||||
| 
 | ||||
| ### END SHELL SCRIPT ### | ||||
							
								
								
									
										16
									
								
								bin/loadTesting/launcher.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										16
									
								
								bin/loadTesting/launcher.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,16 @@ | ||||
| #!/bin/bash | ||||
| 
 | ||||
| # connect 500 instances to display :0 | ||||
| for i in {1..500} | ||||
| do | ||||
|   echo 	$i | ||||
|   echo "Displaying Some shit" | ||||
|   DISPLAY=:0 screen -d -m /home/phantomjs/bin/phantomjs loader.js http://10.0.0.55:9001/p/pad2 && sleep 2 | ||||
| done | ||||
| 
 | ||||
| # connect 500 instances to display :1 | ||||
| for i in {1..500} | ||||
| do | ||||
|   echo $i | ||||
|   DISPLAY=:1 screen -d -m /home/phantomjs/bin/phantomjs loader.js http://10.0.0.55:9001/p/pad2 && sleep 2 | ||||
| done | ||||
							
								
								
									
										20
									
								
								bin/loadTesting/loader.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								bin/loadTesting/loader.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | ||||
| var page = new WebPage(), | ||||
|     t, address; | ||||
| 
 | ||||
| if (phantom.args.length === 0) { | ||||
|     console.log('Usage: loader.js <some URL>'); | ||||
|     phantom.exit(); | ||||
| } else { | ||||
|     t = Date.now(); | ||||
|     address = phantom.args[0]; | ||||
| 
 | ||||
|     var page = new WebPage(); | ||||
|     page.onResourceRequested = function (request) { | ||||
|         console.log('Request ' + JSON.stringify(request, undefined, 4)); | ||||
|     }; | ||||
|     page.onResourceReceived = function (response) { | ||||
|         console.log('Receive ' + JSON.stringify(response, undefined, 4)); | ||||
|     }; | ||||
|     page.open(address); | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										177
									
								
								node/utils/caching_middleware.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								node/utils/caching_middleware.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,177 @@ | ||||
| /* | ||||
|  * 2011 Peter 'Pita' Martischka (Primary Technology Ltd) | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *      http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS-IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| var async = require('async'); | ||||
| var Buffer = require('buffer').Buffer; | ||||
| var fs = require('fs'); | ||||
| var path = require('path'); | ||||
| var server = require('../server'); | ||||
| var zlib = require('zlib'); | ||||
| var util = require('util'); | ||||
| 
 | ||||
| var ROOT_DIR = path.normalize(__dirname + "/../"); | ||||
| var CACHE_DIR = ROOT_DIR + '../var/'; | ||||
| 
 | ||||
| var responseCache = {}; | ||||
| 
 | ||||
| /* | ||||
|   This caches and compresses 200 and 404 responses to GET and HEAD requests. | ||||
|   TODO: Caching and compressing are solved problems, a middleware configuration | ||||
|   should replace this. | ||||
| */ | ||||
| 
 | ||||
| function CachingMiddleware() { | ||||
| } | ||||
| CachingMiddleware.prototype = new function () { | ||||
|   function handle(req, res, next) { | ||||
|     if (!(req.method == "GET" || req.method == "HEAD")) { | ||||
|       return next(undefined, req, res); | ||||
|     } | ||||
| 
 | ||||
|     var old_req = {}; | ||||
|     var old_res = {}; | ||||
| 
 | ||||
|     var supportsGzip = | ||||
|         req.header('Accept-Encoding', '').indexOf('gzip') != -1; | ||||
| 
 | ||||
|     var path = require('url').parse(req.url).path; | ||||
|     var cacheKey = (new Buffer(path)).toString('base64').replace(/[\/\+=]/g, ''); | ||||
| 
 | ||||
|     fs.stat(CACHE_DIR + 'minified_' + cacheKey, function (error, stats) { | ||||
|       var modifiedSince = (req.headers['if-modified-since'] | ||||
|           && new Date(req.headers['if-modified-since'])); | ||||
|       var lastModifiedCache = !error && stats.mtime; | ||||
|       if (lastModifiedCache) { | ||||
|         req.headers['if-modified-since'] = lastModifiedCache.toUTCString(); | ||||
|       } else { | ||||
|         delete req.headers['if-modified-since']; | ||||
|       } | ||||
| 
 | ||||
|       // Always issue get to downstream.
 | ||||
|       old_req.method = req.method; | ||||
|       req.method = 'GET'; | ||||
| 
 | ||||
|       var expirationDate = new Date(((responseCache[cacheKey] || {}).headers || {})['expires']); | ||||
|       if (expirationDate > new Date()) { | ||||
|         // Our cached version is still valid.
 | ||||
|         return respond(); | ||||
|       } | ||||
| 
 | ||||
|       var _headers = {}; | ||||
|       old_res.setHeader = res.setHeader; | ||||
|       res.setHeader = function (key, value) { | ||||
|         _headers[key.toLowerCase()] = value; | ||||
|         old_res.setHeader.call(res, key, value); | ||||
|       }; | ||||
| 
 | ||||
|       old_res.writeHead = res.writeHead; | ||||
|       res.writeHead = function (status, headers) { | ||||
|         var lastModified = (res.getHeader('last-modified') | ||||
|             && new Date(res.getHeader('last-modified'))); | ||||
| 
 | ||||
|         res.writeHead = old_res.writeHead; | ||||
|         if (status == 200 || status == 404) { | ||||
|           // Update cache
 | ||||
|           var buffer = ''; | ||||
| 
 | ||||
|           Object.keys(headers || {}).forEach(function (key) { | ||||
|             res.setHeader(key, headers[key]); | ||||
|           }); | ||||
|           headers = _headers; | ||||
| 
 | ||||
|           old_res.write = res.write; | ||||
|           old_res.end = res.end; | ||||
|           res.write = function(data, encoding) { | ||||
|             buffer += data.toString(encoding); | ||||
|           }; | ||||
|           res.end = function(data, encoding) { | ||||
|             async.parallel([ | ||||
|               function (callback) { | ||||
|                 var path = CACHE_DIR + 'minified_' + cacheKey; | ||||
|                 fs.writeFile(path, buffer, function (error, stats) { | ||||
|                   callback(); | ||||
|                 }); | ||||
|               } | ||||
|             , function (callback) { | ||||
|                 var path = CACHE_DIR + 'minified_' + cacheKey + '.gz'; | ||||
|                 zlib.gzip(buffer, function(error, content) { | ||||
|                   if (error) { | ||||
|                     callback(); | ||||
|                   } else { | ||||
|                     fs.writeFile(path, content, function (error, stats) { | ||||
|                       callback(); | ||||
|                     }); | ||||
|                   } | ||||
|                 }); | ||||
|               } | ||||
|             ], function () { | ||||
|               responseCache[cacheKey] = {statusCode: status, headers: headers}; | ||||
|               respond(); | ||||
|             }); | ||||
|           }; | ||||
|         } else if (status == 304) { | ||||
|           // Nothing new changed from the cached version.
 | ||||
|           old_res.write = res.write; | ||||
|           old_res.end = res.end; | ||||
|           res.write = function(data, encoding) {}; | ||||
|           res.end = function(data, encoding) { respond() }; | ||||
|         } else { | ||||
|           res.writeHead(status, headers); | ||||
|         } | ||||
|       }; | ||||
| 
 | ||||
|       next(undefined, req, res); | ||||
| 
 | ||||
|       // This handles read/write synchronization as well as its predecessor,
 | ||||
|       // which is to say, not at all.
 | ||||
|       // TODO: Implement locking on write or ditch caching of gzip and use
 | ||||
|       // existing middlewares.
 | ||||
|       function respond() { | ||||
|         req.method = old_req.method || req.method; | ||||
|         res.write = old_res.write || res.write; | ||||
|         res.end = old_res.end || res.end; | ||||
| 
 | ||||
|         var headers = responseCache[cacheKey].headers; | ||||
|         var statusCode = responseCache[cacheKey].statusCode; | ||||
| 
 | ||||
|         var pathStr = CACHE_DIR + 'minified_' + cacheKey; | ||||
|         if (supportsGzip && (headers['content-type'] || '').match(/^text\//)) { | ||||
|           pathStr = pathStr + '.gz'; | ||||
|           headers['content-encoding'] = 'gzip'; | ||||
|         } | ||||
| 
 | ||||
|         var lastModified = (headers['last-modified'] | ||||
|             && new Date(headers['last-modified'])); | ||||
| 
 | ||||
|         if (statusCode == 200 && lastModified <= modifiedSince) { | ||||
|           res.writeHead(304, headers); | ||||
|           res.end(); | ||||
|         } else if (req.method == 'GET') { | ||||
|           var readStream = fs.createReadStream(pathStr); | ||||
|           res.writeHead(statusCode, headers); | ||||
|           util.pump(readStream, res); | ||||
|         } else { | ||||
|           res.writeHead(statusCode, headers); | ||||
|           res.end(); | ||||
|         } | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   this.handle = handle; | ||||
| }(); | ||||
| 
 | ||||
| module.exports = CachingMiddleware; | ||||
| @ -38,6 +38,10 @@ | ||||
|   /* if true, all css & js will be minified before sending to the client. This will improve the loading performance massivly,  | ||||
|      but makes it impossible to debug the javascript/css */ | ||||
|   "minify" : true, | ||||
| 
 | ||||
|   /* How long may clients use served javascript code? Without versioning this | ||||
|     is may cause problems during deployment. */ | ||||
|   "maxAge" : 21600000, // 6 hours | ||||
|    | ||||
|   /* This is the path to the Abiword executable. Setting it to null, disables abiword. | ||||
|      Abiword is needed to enable the import/export of pads*/   | ||||
|  | ||||
| @ -6,7 +6,7 @@ | ||||
| 
 | ||||
| # Module name conventions | ||||
| 
 | ||||
| Module file names starts with a capital letter and uses camelCase | ||||
| Module file names start with a capital letter and uses camelCase | ||||
| 
 | ||||
| # Where does it start? | ||||
| 
 | ||||
|  | ||||
| @ -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, '_'] | ||||
| ]; | ||||
| 
 | ||||
| /** | ||||
| @ -114,7 +115,7 @@ exports.doesPadExists = function(padId, callback) | ||||
|   db.get("pad:"+padId, function(err, value) | ||||
|   { | ||||
|     if(ERR(err, callback)) return; | ||||
|     callback(null, value != null);   | ||||
|     callback(null, value != null && value.atext);   | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -82,7 +82,7 @@ exports.doImport = function(req, res, padId) | ||||
|     //this allows us to accept source code files like .c or .java
 | ||||
|     function(callback) | ||||
|     { | ||||
|       var fileEnding = srcFile.split(".")[1].toLowerCase(); | ||||
|       var fileEnding = (srcFile.split(".")[1] || "").toLowerCase(); | ||||
|       var knownFileEndings = ["txt", "doc", "docx", "pdf", "odt", "html", "htm"]; | ||||
|        | ||||
|       //find out if this is a known file ending
 | ||||
| @ -115,7 +115,15 @@ exports.doImport = function(req, res, padId) | ||||
|     { | ||||
|       var randNum = Math.floor(Math.random()*0xFFFFFFFF); | ||||
|       destFile = tempDirectory + "eplite_import_" + randNum + ".txt"; | ||||
|       abiword.convertFile(srcFile, destFile, "txt", callback); | ||||
|       abiword.convertFile(srcFile, destFile, "txt", function(err){ | ||||
|         //catch convert errors
 | ||||
|         if(err){ | ||||
|           console.warn("Converting Error:", err); | ||||
|           return callback("convertFailed"); | ||||
|         } else { | ||||
|           callback(); | ||||
|         } | ||||
|       }); | ||||
|     }, | ||||
|      | ||||
|     //get the pad object
 | ||||
| @ -176,16 +184,18 @@ exports.doImport = function(req, res, padId) | ||||
|     } | ||||
|   ], function(err) | ||||
|   { | ||||
|     //the upload failed, there is nothing we can do, send a 500
 | ||||
|     if(err == "uploadFailed") | ||||
|     var status = "ok"; | ||||
|      | ||||
|     //check for known errors and replace the status
 | ||||
|     if(err == "uploadFailed" || err == "convertFailed") | ||||
|     { | ||||
|       res.send(500); | ||||
|       return; | ||||
|       status = err; | ||||
|       err = null; | ||||
|     } | ||||
| 
 | ||||
|     ERR(err); | ||||
|    | ||||
|     //close the connection
 | ||||
|     res.send("ok"); | ||||
|     res.send("<script>document.domain = document.domain; var impexp = window.top.require('/pad_impexp').padimpexp.handleFrameCall('" + status + "'); </script>", 200); | ||||
|   }); | ||||
| } | ||||
|  | ||||
| @ -53,7 +53,7 @@ if(os.type().indexOf("Windows") > -1) | ||||
|     abiword.on('exit', function (code) | ||||
|     { | ||||
|       if(code != 0) { | ||||
|         throw "Abiword died with exit code " + code; | ||||
|         return callback("Abiword died with exit code " + code); | ||||
|       } | ||||
| 
 | ||||
|       if(stdoutBuffer != "") | ||||
| @ -75,52 +75,54 @@ if(os.type().indexOf("Windows") > -1) | ||||
| else | ||||
| { | ||||
|   //spawn the abiword process
 | ||||
|   var abiword = spawn(settings.abiword, ["--plugin", "AbiCommand"]); | ||||
| 
 | ||||
|   //append error messages to the buffer
 | ||||
|   abiword.stderr.on('data', function (data)  | ||||
|   { | ||||
|     stdoutBuffer += data.toString(); | ||||
|   }); | ||||
| 
 | ||||
|   //throw exceptions if abiword is dieing
 | ||||
|   abiword.on('exit', function (code)  | ||||
|   { | ||||
|     throw "Abiword died with exit code " + code; | ||||
|   }); | ||||
| 
 | ||||
|   //delegate the processing of stdout to a other function
 | ||||
|   abiword.stdout.on('data',onAbiwordStdout); | ||||
| 
 | ||||
|   var abiword; | ||||
|   var stdoutCallback = null; | ||||
|   var stdoutBuffer = ""; | ||||
|   var firstPrompt = true; | ||||
|   var spawnAbiword = function (){ | ||||
|     abiword = spawn(settings.abiword, ["--plugin", "AbiCommand"]); | ||||
|     var stdoutBuffer = ""; | ||||
|     var firstPrompt = true;   | ||||
| 
 | ||||
|   function onAbiwordStdout(data) | ||||
|   { | ||||
|     //add data to buffer
 | ||||
|     stdoutBuffer+=data.toString(); | ||||
|      | ||||
|     //we're searching for the prompt, cause this means everything we need is in the buffer
 | ||||
|     if(stdoutBuffer.search("AbiWord:>") != -1) | ||||
|     //append error messages to the buffer
 | ||||
|     abiword.stderr.on('data', function (data)  | ||||
|     { | ||||
|       //filter the feedback message
 | ||||
|       var err = stdoutBuffer.search("OK") != -1 ? null : stdoutBuffer; | ||||
|       stdoutBuffer += data.toString(); | ||||
|     }); | ||||
| 
 | ||||
|     //abiword died, let's restart abiword and return an error with the callback
 | ||||
|     abiword.on('exit', function (code)  | ||||
|     { | ||||
|       spawnAbiword(); | ||||
|       stdoutCallback("Abiword died with exit code " + code); | ||||
|     }); | ||||
| 
 | ||||
|     //delegate the processing of stdout to a other function
 | ||||
|     abiword.stdout.on('data',function (data) | ||||
|     { | ||||
|       //add data to buffer
 | ||||
|       stdoutBuffer+=data.toString(); | ||||
|        | ||||
|       //reset the buffer
 | ||||
|       stdoutBuffer = ""; | ||||
|        | ||||
|       //call the callback with the error message
 | ||||
|       //skip the first prompt
 | ||||
|       if(stdoutCallback != null && !firstPrompt) | ||||
|       //we're searching for the prompt, cause this means everything we need is in the buffer
 | ||||
|       if(stdoutBuffer.search("AbiWord:>") != -1) | ||||
|       { | ||||
|         stdoutCallback(err); | ||||
|         stdoutCallback = null; | ||||
|         //filter the feedback message
 | ||||
|         var err = stdoutBuffer.search("OK") != -1 ? null : stdoutBuffer; | ||||
|          | ||||
|         //reset the buffer
 | ||||
|         stdoutBuffer = ""; | ||||
|          | ||||
|         //call the callback with the error message
 | ||||
|         //skip the first prompt
 | ||||
|         if(stdoutCallback != null && !firstPrompt) | ||||
|         { | ||||
|           stdoutCallback(err); | ||||
|           stdoutCallback = null; | ||||
|         } | ||||
|          | ||||
|         firstPrompt = false; | ||||
|       } | ||||
|        | ||||
|       firstPrompt = false; | ||||
|     } | ||||
|     }); | ||||
|   } | ||||
|   spawnAbiword(); | ||||
| 
 | ||||
|   doConvertTask = function(task, callback) | ||||
|   { | ||||
| @ -130,6 +132,7 @@ else | ||||
|     stdoutCallback = function (err) | ||||
|     { | ||||
|       callback(); | ||||
|       console.log("queue continue"); | ||||
|       task.callback(err); | ||||
|     }; | ||||
|   } | ||||
|  | ||||
| @ -60,6 +60,11 @@ exports.requireSession = false; | ||||
|  */ | ||||
| exports.editOnly = false; | ||||
| 
 | ||||
| /** | ||||
|  * Max age that responses will have (affects caching layer). | ||||
|  */ | ||||
| exports.maxAge = 1000*60*60*6; // 6 hours
 | ||||
| 
 | ||||
| /** | ||||
|  * A flag that shows if minification is enabled or not | ||||
|  */ | ||||
|  | ||||
| @ -10,14 +10,16 @@ | ||||
|                         "name": "Robin Buse" } | ||||
|                      ], | ||||
|   "dependencies"   : { | ||||
|                       "yajsml"              : "1.1.2", | ||||
|                       "request"             : "2.9.100", | ||||
|                       "require-kernel"      : "1.0.3", | ||||
|                       "socket.io"           : "0.8.7", | ||||
|                       "ueberDB"             : "0.1.7", | ||||
|                       "async"               : "0.1.15", | ||||
|                       "express"             : "2.5.0", | ||||
|                       "async"               : "0.1.18", | ||||
|                       "express"             : "2.5.8", | ||||
|                       "clean-css"           : "0.3.2", | ||||
|                       "uglify-js"           : "1.2.5", | ||||
|                       "formidable"          : "1.0.7", | ||||
|                       "formidable"          : "1.0.9", | ||||
|                       "log4js"              : "0.4.1", | ||||
|                       "jsdom-nocontextifiy" : "0.2.10", | ||||
|                       "async-stacktrace"    : "0.0.2", | ||||
|  | ||||
| @ -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; } | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -65,6 +65,7 @@ a img | ||||
| { | ||||
|   text-decoration: none; | ||||
|   color: #ccc; | ||||
|   position: absolute; | ||||
| } | ||||
| 
 | ||||
| #editbar ul li a span | ||||
| @ -75,11 +76,13 @@ a img | ||||
| 
 | ||||
| #editbar ul li:hover { | ||||
|   background: #fff; | ||||
|   background: linear-gradient(#f4f4f4, #e4e4e4); | ||||
| } | ||||
| 
 | ||||
| #editbar ul li:active { | ||||
|   background: #eee; | ||||
|   background: linear-gradient(#ddd, #fff); | ||||
|   box-shadow: 0 0 8px rgba(0,0,0,.1) inset; | ||||
| } | ||||
| 
 | ||||
| #editbar ul li.separator | ||||
| @ -798,12 +801,11 @@ ul#colorpickerswatches li:hover | ||||
| #chatlabel | ||||
| { | ||||
|   font-size:13px; | ||||
|   line-height:16px; | ||||
|   font-weight:bold; | ||||
|   color:#555; | ||||
|   text-decoration: none; | ||||
|   position: relative; | ||||
|   bottom: 3px; | ||||
|   margin-right: 3px; | ||||
|   vertical-align: middle; | ||||
| } | ||||
| 
 | ||||
| #chatinput | ||||
| @ -816,8 +818,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; | ||||
| @ -838,8 +840,7 @@ ul#colorpickerswatches li:hover | ||||
| { | ||||
|   color:#555; | ||||
|   font-size:9px; | ||||
|   position:relative; | ||||
|   bottom: 2px; | ||||
|   vertical-align: middle; | ||||
| } | ||||
| 
 | ||||
| #titlebar | ||||
| @ -877,40 +878,6 @@ ul#colorpickerswatches li:hover | ||||
|   margin-left: 3px; | ||||
|   margin-right: 3px; | ||||
|   margin-top:2px; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* resizable stuff for chat */ | ||||
| .ui-resizable { | ||||
| position: relative; | ||||
| }  | ||||
| .ui-resizable-handle {  | ||||
|   position: absolute; | ||||
|   font-size: 0.1px; | ||||
|   z-index: 99999; | ||||
|   display: block; | ||||
|      | ||||
| }  | ||||
| 
 | ||||
| .ui-resizable-nw { | ||||
|   background-image: url("../../static/img/etherpad_lite_icons.png"); | ||||
|   background-position: 0 -416px; | ||||
|   background-repeat: no-repeat; | ||||
|   background-size: 100% auto; | ||||
|   cursor: nw-resize; | ||||
|   height: 17px; | ||||
|   left: 3px; | ||||
|   top: 3px; | ||||
|   width: 17px; | ||||
| }  | ||||
| 
 | ||||
| .ui-resizable-ne  | ||||
| {  | ||||
|   cursor: ne-resize;  | ||||
|   width: 9px;  | ||||
|   height: 9px;  | ||||
|   right: -5px;  | ||||
|   top: -5px; | ||||
| }  | ||||
| 
 | ||||
| .exporttype{ | ||||
| @ -1060,6 +1027,8 @@ margin-top: 1px; | ||||
| .buttonicon-chat { | ||||
|   background-position: 0px -102px; | ||||
|   display: inline-block; | ||||
|   vertical-align: middle; | ||||
|   margin: 0 !important; | ||||
| } | ||||
| .buttonicon-showusers { | ||||
|   background-position: 0px -183px; | ||||
| @ -1085,7 +1054,10 @@ width:33px !important; | ||||
| } | ||||
| 
 | ||||
| #online_count{ | ||||
|   color: #999; | ||||
|   color: #888; | ||||
|   font-size: 11px; | ||||
|   line-height: 18px; | ||||
|   position: fixed; | ||||
| } | ||||
| 
 | ||||
| #qr_center { | ||||
| @ -1104,83 +1076,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; | ||||
| }  | ||||
| @ -1189,10 +1084,9 @@ width:33px !important; | ||||
|  word-wrap: break-word; | ||||
| } | ||||
| 
 | ||||
| /* fix for misaligned labels */ | ||||
| label { | ||||
|  position: relative; | ||||
|  bottom: 1px; | ||||
| /* fix for misaligned checkboxes */ | ||||
| input[type=checkbox] { | ||||
|  vertical-align: -1px; | ||||
| } | ||||
| 
 | ||||
| .right { | ||||
| @ -1234,17 +1128,11 @@ label { | ||||
|  margin: 5px 0; | ||||
| } | ||||
| 
 | ||||
| .left_popup { | ||||
| .column { | ||||
|  float: left; | ||||
|  width: 50%; | ||||
| } | ||||
| 
 | ||||
| .right_popup { | ||||
|  float: left; | ||||
|  width: 50%; | ||||
|  box-sizing: border-box; | ||||
| } | ||||
| 
 | ||||
| #settingsmenu, #importexport, #embed { | ||||
|  position: absolute; | ||||
|  top: 55px; | ||||
| @ -1262,3 +1150,121 @@ 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: 0; | ||||
|     top: 36px; | ||||
|     bottom: 33px; | ||||
|     border-radius: none; | ||||
|   } | ||||
|   #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: 3px; | ||||
|     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; | ||||
|   } | ||||
|   #online_count { | ||||
|     line-height: 24px; | ||||
|   } | ||||
| } | ||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB | 
| @ -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, initial-scale=1, maximum-scale=1, 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,70 @@ | ||||
|             } | ||||
|             #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; | ||||
|               *padding: 0; /* IE7 hack */ | ||||
|               width: 100%; | ||||
|               height: 100%; | ||||
|               outline: none; | ||||
|               border: none; | ||||
|               position: absolute; | ||||
|             } | ||||
|             input[type="submit"] { | ||||
|             button[type="submit"] { | ||||
|               position: absolute; | ||||
|               right: 0; | ||||
|               width: 45px; | ||||
|               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()  | ||||
|             { | ||||
|  | ||||
| @ -29,12 +29,26 @@ var AttributePoolFactory = require("ep_etherpad-lite/static/js/AttributePoolFact | ||||
| 
 | ||||
| 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(); | ||||
|  | ||||
| @ -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 = []; | ||||
| @ -228,8 +166,8 @@ function Ace2Editor() | ||||
|   } | ||||
|   function pushScriptsTo(buffer) { | ||||
|     /* Folling is for packaging regular expression. */ | ||||
|     /* $$INCLUDE_JS("../static/js/ace2_inner.js"); */ | ||||
|     var ACE_SOURCE = '../static/js/ace2_inner.js'; | ||||
|     /* $$INCLUDE_JS("../minified/ace2_inner.js?callback=require.define"); */ | ||||
|     var ACE_SOURCE = '../minified/ace2_inner.js?callback=require.define'; | ||||
|     if (Ace2Editor.EMBEDED && Ace2Editor.EMBEDED[ACE_SOURCE]) { | ||||
|       buffer.push('<script type="text/javascript">'); | ||||
|       buffer.push(Ace2Editor.EMBEDED[ACE_SOURCE]); | ||||
| @ -242,7 +180,7 @@ function Ace2Editor() | ||||
|       buffer.push('<script type="text/javascript">'); | ||||
|       buffer.push('require.setRootURI("../minified/"); require.setLibraryURI("../minified/plugins/"); require.setGlobalKeyPath("require");'); | ||||
|       buffer.push('<\/script>'); | ||||
|       buffer.push('<script type="application/javascript" src="' + file + '"><\/script>'); | ||||
|       buffer.push('<script type="application/javascript" src="' + ACE_SOURCE + '"><\/script>'); | ||||
|       buffer.push('<script type="text/javascript">'); | ||||
|       buffer.push('require("ep_etherpad-lite/static/js/ace2_inner");'); | ||||
|       buffer.push('<\/script>'); | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -26,47 +26,16 @@ var AttribPool = require('ep_etherpad-lite/static/js/AttributePoolFactory').crea | ||||
| var Changeset = require('ep_etherpad-lite/static/js/Changeset'); | ||||
| var linestylefilter = require('ep_etherpad-lite/static/js/linestylefilter').linestylefilter; | ||||
| var colorutils = require('ep_etherpad-lite/static/js/colorutils').colorutils; | ||||
| var Ace2Common = require('ep_etherpad-lite/static/js/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(); | ||||
|     }); | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -84,13 +84,6 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options, _pad) | ||||
|     {} | ||||
|   }; | ||||
| 
 | ||||
|   $(window).bind("unload", function() | ||||
|   { | ||||
|     if (getSocket()) | ||||
|     { | ||||
|       setChannelState("DISCONNECTED", "unload"); | ||||
|     } | ||||
|   }); | ||||
|   if ($.browser.mozilla) | ||||
|   { | ||||
|     // Prevent "escape" from taking effect and canceling a comet connection;
 | ||||
|  | ||||
| @ -120,4 +120,19 @@ colorutils.blend = function(c1, c2, t) | ||||
|   return [colorutils.scale(t, c1[0], c2[0]), colorutils.scale(t, c1[1], c2[1]), colorutils.scale(t, c1[2], c2[2])]; | ||||
| } | ||||
| 
 | ||||
| colorutils.invert = function(c) | ||||
| { | ||||
|   return [1 - c[0], 1 - c[1], 1- c[2]]; | ||||
| } | ||||
| 
 | ||||
| colorutils.complementary = function(c) | ||||
| { | ||||
|   var inv = colorutils.invert(c); | ||||
|   return [ | ||||
|     (inv[0] >= c[0]) ? Math.min(inv[0] * 1.30, 1) : (c[0] * 0.30), | ||||
|     (inv[1] >= c[1]) ? Math.min(inv[1] * 1.59, 1) : (c[1] * 0.59), | ||||
|     (inv[2] >= c[2]) ? Math.min(inv[2] * 1.11, 1) : (c[2] * 0.11) | ||||
|   ]; | ||||
| } | ||||
| 
 | ||||
| exports.colorutils = colorutils; | ||||
|  | ||||
| @ -28,15 +28,12 @@ | ||||
| 
 | ||||
| var Security = require('ep_etherpad-lite/static/js/security'); | ||||
| var hooks = require('ep_etherpad-lite/static/js/pluginfw/hooks'); | ||||
| var map = require('ep_etherpad-lite/static/js/ace2_common').map; | ||||
| var Ace2Common = require('ep_etherpad-lite/static/js/ace2_common'); | ||||
| 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'; | ||||
|  | ||||
| @ -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; | ||||
| @ -158,7 +164,8 @@ function handshake() | ||||
|   //connect
 | ||||
|   socket = pad.socket = io.connect(url, { | ||||
|     resource: resource, | ||||
|     'max reconnection attempts': 3 | ||||
|     'max reconnection attempts': 3, | ||||
|     'sync disconnect on unload' : false | ||||
|   }); | ||||
| 
 | ||||
|   function sendClientReady(isReconnect) | ||||
| @ -216,15 +223,19 @@ function handshake() | ||||
|     sendClientReady(true); | ||||
|   }); | ||||
|    | ||||
|   socket.on('disconnect', function () { | ||||
|     function disconnectEvent() | ||||
|     { | ||||
|       pad.collabClient.setChannelState("DISCONNECTED", "reconnect_timeout"); | ||||
|   socket.on('disconnect', function (reason) { | ||||
|     if(reason == "booted"){ | ||||
|       pad.collabClient.setChannelState("DISCONNECTED"); | ||||
|     } else { | ||||
|       function disconnectEvent() | ||||
|       { | ||||
|         pad.collabClient.setChannelState("DISCONNECTED", "reconnect_timeout"); | ||||
|       } | ||||
|        | ||||
|       pad.collabClient.setChannelState("RECONNECTING"); | ||||
|        | ||||
|       disconnectTimeout = setTimeout(disconnectEvent, 10000); | ||||
|     } | ||||
|      | ||||
|     pad.collabClient.setChannelState("RECONNECTING"); | ||||
|      | ||||
|     disconnectTimeout = setTimeout(disconnectEvent, 10000); | ||||
|   }); | ||||
| 
 | ||||
|   var receivedClientVars = false; | ||||
| @ -276,7 +287,7 @@ function handshake() | ||||
|         pad.changeViewOption('showLineNumbers', false); | ||||
|       } | ||||
| 
 | ||||
|       // If the noColors value is set to true then we need to hide the backround colors on the ace spans
 | ||||
|       // If the noColors value is set to true then we need to hide the background colors on the ace spans
 | ||||
|       if (settings.noColors == true) | ||||
|       { | ||||
|         pad.changeViewOption('noColors', true); | ||||
| @ -364,7 +375,6 @@ var pad = { | ||||
|   { | ||||
|     return clientVars.userIsGuest; | ||||
|   }, | ||||
|   //
 | ||||
|   getUserId: function() | ||||
|   { | ||||
|     return pad.myUserInfo.userId; | ||||
| @ -384,16 +394,13 @@ var pad = { | ||||
| 
 | ||||
|     $(document).ready(function() | ||||
|     { | ||||
|       // test for XML HTTP capabiites
 | ||||
|       ieTestXMLHTTP(); | ||||
|       // start the custom js
 | ||||
|       if (typeof customStart == "function") customStart(); | ||||
|       getParams(); | ||||
|       handshake(); | ||||
|     }); | ||||
| 
 | ||||
|     $(window).unload(function() | ||||
|     { | ||||
|       pad.dispose(); | ||||
|     }); | ||||
|   }, | ||||
|   _afterHandshake: function() | ||||
|   { | ||||
| @ -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,13 @@ 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
 | ||||
|       } | ||||
|       if(padcookie.getPref("showAuthorshipColors") == false){ | ||||
| 	pad.changeViewOption('showAuthorColors', false); | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   dispose: function() | ||||
| @ -741,6 +755,7 @@ var pad = { | ||||
| 
 | ||||
|     // pad.determineSidebarVisibility(isConnected && !isInitialConnect);
 | ||||
|     pad.determineChatVisibility(isConnected && !isInitialConnect); | ||||
|     pad.determineAuthorshipColorsVisibility(); | ||||
| 
 | ||||
|   }, | ||||
| /*  determineSidebarVisibility: function(asNowConnectedFeedback) | ||||
| @ -770,6 +785,16 @@ var pad = { | ||||
|       $('#options-stickychat').prop("checked", false); // set the checkbox for off
 | ||||
|     } | ||||
|   }, | ||||
|   determineAuthorshipColorsVisibility: function(){ | ||||
|     var authColCookie = padcookie.getPref('showAuthorshipColors'); | ||||
|     if (authColCookie){ | ||||
|       pad.changeViewOption('showAuthorColors', true); | ||||
|       $('#options-colorscheck').prop("checked", true); | ||||
|     } | ||||
|     else { | ||||
|       $('#options-colorscheck').prop("checked", false); | ||||
|     } | ||||
|   }, | ||||
|   handleCollabAction: function(action) | ||||
|   { | ||||
|     if (action == "commitPerformed") | ||||
| @ -972,3 +997,4 @@ exports.handshake = handshake; | ||||
| exports.pad = pad; | ||||
| exports.init = init; | ||||
| exports.alertBar = alertBar; | ||||
| 
 | ||||
|  | ||||
| @ -239,14 +239,14 @@ var padeditbar = (function() | ||||
|         var readonlyLink = basePath + "/ro/" + clientVars.readOnlyId; | ||||
|         $('#embedinput').val("<iframe name='embed_readonly' 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 name='embed_readwrite' 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); | ||||
|       } | ||||
|     } | ||||
|   }; | ||||
|  | ||||
| @ -69,6 +69,7 @@ var padeditor = (function() | ||||
|       }); | ||||
|       padutils.bindCheckboxChange($("#options-colorscheck"), function() | ||||
|       { | ||||
|         padcookie.setPref('showAuthorshipColors', padutils.getCheckbox("#options-colorscheck")); | ||||
|         pad.changeViewOption('showAuthorColors', padutils.getCheckbox("#options-colorscheck")); | ||||
|       }); | ||||
|       $("#viewfontmenu").change(function() | ||||
|  | ||||
| @ -95,11 +95,6 @@ var padimpexp = (function() | ||||
|       }, 0); | ||||
|       $('#importarrow').stop(true, true).hide(); | ||||
|       $('#importstatusball').show(); | ||||
|        | ||||
|       $("#import .importframe").load(function() | ||||
|       { | ||||
|         importDone(); | ||||
|       }); | ||||
|     } | ||||
|     return ret; | ||||
|   } | ||||
| @ -107,8 +102,6 @@ var padimpexp = (function() | ||||
|   function importFailed(msg) | ||||
|   { | ||||
|     importErrorMessage(msg); | ||||
|     importDone(); | ||||
|     addImportFrames(); | ||||
|   } | ||||
| 
 | ||||
|   function importDone() | ||||
| @ -120,6 +113,7 @@ var padimpexp = (function() | ||||
|     }, 0); | ||||
|     $('#importstatusball').hide(); | ||||
|     importClearTimeout(); | ||||
|     addImportFrames(); | ||||
|   } | ||||
| 
 | ||||
|   function importClearTimeout() | ||||
| @ -131,11 +125,19 @@ var padimpexp = (function() | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function importErrorMessage(msg) | ||||
|   function importErrorMessage(status) | ||||
|   { | ||||
|     var msg=""; | ||||
|    | ||||
|     if(status === "convertFailed"){ | ||||
|       msg = "We were not able to import this file. Please use a different document format or copy paste manually"; | ||||
|     } else if(status === "uploadFailed"){ | ||||
|       msg = "The upload failed, please try again"; | ||||
|     } | ||||
|    | ||||
|     function showError(fade) | ||||
|     { | ||||
|       $('#importmessagefail').html('<strong style="color: red">Import failed:</strong> ' + (msg || 'Please try a different file.'))[(fade ? "fadeIn" : "show")](); | ||||
|       $('#importmessagefail').html('<strong style="color: red">Import failed:</strong> ' + (msg || 'Please copy paste'))[(fade ? "fadeIn" : "show")](); | ||||
|     } | ||||
| 
 | ||||
|     if ($('#importexport .importmessage').is(':visible')) | ||||
| @ -175,39 +177,6 @@ var padimpexp = (function() | ||||
|     importDone(); | ||||
|   } | ||||
| 
 | ||||
|   function importApplicationSuccessful(data, textStatus) | ||||
|   { | ||||
|     if (data.substr(0, 2) == "ok") | ||||
|     { | ||||
|       if ($('#importexport .importmessage').is(':visible')) | ||||
|       { | ||||
|         $('#importexport .importmessage').hide(); | ||||
|       } | ||||
|       $('#importmessagesuccess').html('<strong style="color: green">Import successful!</strong>').show(); | ||||
|       $('#importformfilediv').hide(); | ||||
|       window.setTimeout(function() | ||||
|       { | ||||
|         $('#importmessagesuccess').fadeOut("slow", function() | ||||
|         { | ||||
|           $('#importformfilediv').show(); | ||||
|         }); | ||||
|         if (hidePanelCall) | ||||
|         { | ||||
|           hidePanelCall(); | ||||
|         } | ||||
|       }, 3000); | ||||
|     } | ||||
|     else if (data.substr(0, 4) == "fail") | ||||
|     { | ||||
|       importErrorMessage("Couldn't update pad contents. This can happen if your web browser has \"cookies\" disabled."); | ||||
|     } | ||||
|     else if (data.substr(0, 4) == "msg:") | ||||
|     { | ||||
|       importErrorMessage(data.substr(4)); | ||||
|     } | ||||
|     importDone(); | ||||
|   } | ||||
| 
 | ||||
|   ///// export
 | ||||
| 
 | ||||
|   function cantExport() | ||||
| @ -257,8 +226,7 @@ var padimpexp = (function() | ||||
|         $("#exportworda").remove(); | ||||
|         $("#exportpdfa").remove(); | ||||
|         $("#exportopena").remove(); | ||||
|         $("#importexport").css({"height":"115px"}); | ||||
|         $("#importexportline").css({"height":"115px"}); | ||||
|         $(".importformdiv").remove(); | ||||
|         $("#import").html("Import is not available.  To enable import please install abiword"); | ||||
|       } | ||||
|       else if(clientVars.abiwordAvailable == "withoutPDF") | ||||
| @ -292,16 +260,14 @@ var padimpexp = (function() | ||||
|       $('#importform').submit(fileInputSubmit); | ||||
|       $('.disabledexport').click(cantExport); | ||||
|     }, | ||||
|     handleFrameCall: function(callName, argsArray) | ||||
|     handleFrameCall: function(status) | ||||
|     { | ||||
|       if (callName == 'importFailed') | ||||
|       if (status !== "ok") | ||||
|       { | ||||
|         importFailed(argsArray[0]); | ||||
|       } | ||||
|       else if (callName == 'importSuccessful') | ||||
|       { | ||||
|         importSuccessful(argsArray[0]); | ||||
|         importFailed(status); | ||||
|       } | ||||
|        | ||||
|       importDone(); | ||||
|     }, | ||||
|     disable: function() | ||||
|     { | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -5,100 +5,70 @@ | ||||
| 
 | ||||
|         <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, initial-scale=1, maximum-scale=1, user-scalable=0"> | ||||
| 
 | ||||
|         <link href="../static/css/pad.css" rel="stylesheet"> | ||||
|         <link href="../static/custom/pad.css" rel="stylesheet"> | ||||
|         <style title="dynamicsyntax"></style> | ||||
| 
 | ||||
|         <!-- head and body had been removed intentionally --> | ||||
| 
 | ||||
|         <div id="editbar"> | ||||
|             <ul id="menu_left"> | ||||
|                 <li onClick="window.pad&&pad.editbarClick('bold');return false" > | ||||
|                     <a title="Bold (ctrl-B)"> | ||||
|                         <div class="buttonicon buttonicon-bold"></div> | ||||
|                     </a> | ||||
|                 <li id="bold" onClick="window.pad&&pad.editbarClick('bold');return false"> | ||||
|                     <a class="buttonicon buttonicon-bold" title="Bold (ctrl-B)"></a> | ||||
|                 </li> | ||||
|                 <li onClick="window.pad&&pad.editbarClick('italic'); return false;" > | ||||
|                     <a title="Italics (ctrl-I)"> | ||||
|                         <div class="buttonicon buttonicon-italic"></div> | ||||
|                     </a> | ||||
|                 <li id="italic" onClick="window.pad&&pad.editbarClick('italic'); return false;"> | ||||
|                     <a class="buttonicon buttonicon-italic" title="Italics (ctrl-I)"></a> | ||||
|                 </li> | ||||
|                 <li onClick="window.pad&&pad.editbarClick('underline');return false;" > | ||||
|                     <a title="Underline (ctrl-U)"> | ||||
|                         <div class="buttonicon buttonicon-underline"></div> | ||||
|                     </a> | ||||
|                 <li id="underline" onClick="window.pad&&pad.editbarClick('underline');return false;" > | ||||
|                     <a class="buttonicon buttonicon-underline" title="Underline (ctrl-U)"></a> | ||||
|                 </li> | ||||
|                 <li onClick="window.pad&&pad.editbarClick('strikethrough');return false;" > | ||||
|                     <a title="Strikethrough"> | ||||
|                         <div class="buttonicon buttonicon-strikethrough"></div> | ||||
|                     </a> | ||||
|                 <li id="strikethrough" onClick="window.pad&&pad.editbarClick('strikethrough');return false;"> | ||||
|                     <a class="buttonicon buttonicon-strikethrough" title="Strikethrough"></a> | ||||
|                 </li> | ||||
|                 <li class="separator"></li> | ||||
|                 <li onClick="window.pad&&pad.editbarClick('insertorderedlist');return false;" > | ||||
|                     <a title="Toggle Ordered List"> | ||||
|                         <div class="buttonicon buttonicon-insertorderedlist"></div> | ||||
|                     </a> | ||||
|                 <li id="oderedlist" onClick="window.pad&&pad.editbarClick('insertorderedlist');return false;"> | ||||
|                     <a class="buttonicon buttonicon-insertorderedlist" title="Toggle Ordered List"></a> | ||||
|                 </li> | ||||
|                 <li onClick="window.pad&&pad.editbarClick('insertunorderedlist');return false;" > | ||||
|                     <a title="Toggle Bullet List"> | ||||
|                         <div class="buttonicon buttonicon-insertunorderedlist"></div> | ||||
|                     </a> | ||||
|                 <li id="unoderedlist" onClick="window.pad&&pad.editbarClick('insertunorderedlist');return false;"> | ||||
|                     <a class="buttonicon buttonicon-insertunorderedlist" title="Toggle Bullet List"></a> | ||||
|                 </li> | ||||
|                 <li onClick="window.pad&&pad.editbarClick('indent');return false;" > | ||||
|                     <a title="Indent"> | ||||
|                         <div class="buttonicon buttonicon-indent"></div> | ||||
|                     </a> | ||||
|                 <li id="indent" onClick="window.pad&&pad.editbarClick('indent');return false;"> | ||||
|                     <a class="buttonicon buttonicon-indent" title="Indent"></a> | ||||
|                 </li> | ||||
|                 <li onClick="window.pad&&pad.editbarClick('outdent');return false;" > | ||||
|                     <a title="Unindent"> | ||||
|                         <div class="buttonicon buttonicon-outdent"></div> | ||||
|                     </a> | ||||
|                 <li id="outdent" onClick="window.pad&&pad.editbarClick('outdent');return false;"> | ||||
|                     <a class="buttonicon buttonicon-outdent" title="Unindent"></a> | ||||
|                 </li> | ||||
|                 <li class="separator"></li> | ||||
|                 <li onClick="window.pad&&pad.editbarClick('undo');return false;" > | ||||
|                     <a title="Undo (ctrl-Z)"> | ||||
|                         <div class="buttonicon buttonicon-undo"></div> | ||||
|                     </a> | ||||
|                 <li id="undo" onClick="window.pad&&pad.editbarClick('undo');return false;"> | ||||
|                     <a class="buttonicon buttonicon-undo" title="Undo (ctrl-Z)"></a> | ||||
|                 </li> | ||||
|                 <li onClick="window.pad&&pad.editbarClick('redo');return false;" > | ||||
|                     <a title="Redo (ctrl-Y)"> | ||||
|                         <div class="buttonicon buttonicon-redo"></div> | ||||
|                     </a> | ||||
|                 <li id="redo" onClick="window.pad&&pad.editbarClick('redo');return false;"> | ||||
|                     <a class="buttonicon buttonicon-redo" title="Redo (ctrl-Y)"></a> | ||||
|                 </li> | ||||
|                 <li class="separator"></li> | ||||
|                 <li id="clearAuthorship" onClick="window.pad&&pad.editbarClick('clearauthorship');return false;" > | ||||
|                     <a title="Clear Authorship Colors"> | ||||
|                         <div class="buttonicon buttonicon-clearauthorship"></div> | ||||
|                     </a> | ||||
|                 <li id="clearAuthorship" onClick="window.pad&&pad.editbarClick('clearauthorship');return false;"> | ||||
|                     <a class="buttonicon buttonicon-clearauthorship" title="Clear Authorship Colors"></a> | ||||
|                 </li> | ||||
|             </ul>     | ||||
|             <ul id="menu_right"> | ||||
|                 <li onClick="window.pad&&pad.editbarClick('settings');return false;"> | ||||
|                     <a id="settingslink" title="Settings of this pad"> | ||||
|                         <div class="buttonicon buttonicon-settings"></div> | ||||
|                     </a> | ||||
|                 <li id="settingslink" onClick="window.pad&&pad.editbarClick('settings');return false;"> | ||||
|                     <a class="buttonicon buttonicon-settings" id="settingslink" title="Settings of this pad"></a> | ||||
|                 </li> | ||||
|                 <li onClick="window.pad&&pad.editbarClick('import_export');return false;"> | ||||
|                     <a id="exportlink" title="Import/Export from/to different document formats"> | ||||
|                         <div class="buttonicon buttonicon-import_export"></div> | ||||
|                     </a> | ||||
|                 <li id="importexportlink" onClick="window.pad&&pad.editbarClick('import_export');return false;"> | ||||
|                     <a class="buttonicon buttonicon-import_export" id="exportlink" title="Import/Export from/to different document formats"></a> | ||||
|                 </li> | ||||
|                 <li onClick="window.pad&&pad.editbarClick('embed');return false;" > | ||||
|                     <a id="embedlink" title="Share and Embed this pad"> | ||||
|                         <div class="buttonicon buttonicon-embed"></div> | ||||
|                     </a> | ||||
|                 <li id="embedlink" onClick="window.pad&&pad.editbarClick('embed');return false;" > | ||||
|                     <a class="buttonicon buttonicon-embed" id="embedlink" title="Share and Embed this pad"></a> | ||||
|                 </li> | ||||
|                 <li class="separator"></li> | ||||
|                 <li id="timesliderlink" onClick="document.location = document.location.pathname+ '/timeslider'"> | ||||
|                     <a title="Show the history of this pad"> | ||||
|                         <div class="buttonicon buttonicon-history"></div> | ||||
|                     </a> | ||||
|                     <a class="buttonicon buttonicon-history" title="Show the history of this pad"></a> | ||||
|                 </li> | ||||
|                 <li id="usericon" onClick="window.pad&&pad.editbarClick('showusers');return false;" > | ||||
|                     <a title="Show connected users"> | ||||
|                         <div class="buttonicon buttonicon-showusers" id="usericonback"></div> | ||||
|                         <span id="online_count">1</span> | ||||
|                     </a> | ||||
|                 <li id="usericon" onClick="window.pad&&pad.editbarClick('showusers');return false;" title="Show connected users"> | ||||
|                     <span class="buttonicon buttonicon-showusers" id="usericonback"></span> | ||||
|                     <span id="online_count">1</span> | ||||
|                 </li> | ||||
|             </ul> | ||||
|         </div> | ||||
| @ -133,14 +103,14 @@ | ||||
| 
 | ||||
|         <div id="settingsmenu" class="popup"> | ||||
|             <h1>Pad settings</h1> | ||||
|             <div class="left_popup"> | ||||
|             <div class="column"> | ||||
|                 <h2>My view</h2> | ||||
|                 <p> | ||||
|                     <input type="checkbox" id="options-stickychat" onClick="chat.stickToScreen();"> | ||||
|                     <label for="options-stickychat">Chat always on screen</label> | ||||
|                 </p> | ||||
|                 <p> | ||||
|                     <input type="checkbox" id="options-colorscheck" checked> | ||||
|                     <input type="checkbox" id="options-colorscheck"> | ||||
|                     <label for="options-colorscheck">Authorship colors</label> | ||||
|                 </p> | ||||
|                 <p> | ||||
| @ -155,7 +125,7 @@ | ||||
|                     </select> | ||||
|                 </p> | ||||
|             </div> | ||||
|             <div class="right_popup"> | ||||
|             <div class="column"> | ||||
|                 <h2>Global view</h2> | ||||
|                 <p>Currently nothing.</p> | ||||
|                 <p class="note">These options affect everyone viewing this pad.</p> | ||||
| @ -163,7 +133,7 @@ | ||||
|         </div> | ||||
| 
 | ||||
|         <div id="importexport" class="popup"> | ||||
|             <div class="left_popup"> | ||||
|             <div class="column"> | ||||
|                 <h2>Import from text file, HTML, PDF, Word, ODT or RTF</h2><br> | ||||
|                 <form id="importform" method="post" action="" target="importiframe" enctype="multipart/form-data"> | ||||
|                     <div class="importformdiv" id="importformfilediv">  | ||||
| @ -182,7 +152,7 @@ | ||||
|                     </div>  | ||||
|                 </form> | ||||
|             </div> | ||||
|             <div class="right_popup"> | ||||
|             <div class="column"> | ||||
|                 <h2>Export current pad as</h2> | ||||
|                 <a id="exporthtmla" target="_blank" class="exportlink"><div class="exporttype" id="exporthtml">HTML</div></a> | ||||
|                 <a id="exportplaina" target="_blank" class="exportlink"><div class="exporttype" id="exportplain">Plain text</div></a> | ||||
| @ -218,11 +188,9 @@ | ||||
| 
 | ||||
|         <div id="chatthrob"></div> | ||||
| 
 | ||||
|         <div id="chaticon"> | ||||
|             <a onClick="chat.show();return false;" title="Open the chat for this pad"> | ||||
|                 <span id="chatlabel">Chat</span> | ||||
|                 <div class="buttonicon buttonicon-chat"></div> | ||||
|             </a> | ||||
|         <div id="chaticon" title="Open the chat for this pad" onclick="chat.show();return false;"> | ||||
|             <span id="chatlabel">Chat</span> | ||||
|             <span class="buttonicon buttonicon-chat"></span> | ||||
|             <span id="chatcounter">0</span> | ||||
|         </div> | ||||
| 
 | ||||
| @ -292,7 +260,7 @@ | ||||
|         <script type="text/javascript" src="../static/js/require-kernel.js"></script> | ||||
|         <script type="text/javascript" src="../static/js/jquery.js"></script> | ||||
|         <script type="text/javascript" src="../socket.io/socket.io.js"></script> | ||||
|         <script type="text/javascript" src="../minified/pad.js"></script> | ||||
|         <script type="text/javascript" src="../minified/pad.js?callback=require.define"></script> | ||||
|         <script type="text/javascript"> | ||||
|             var clientVars = {}; | ||||
|             (function () { | ||||
|  | ||||
| @ -200,7 +200,7 @@ | ||||
| <script type="text/javascript" src="../../../static/js/require-kernel.js"></script> | ||||
| <script type="text/javascript" src="../../../static/js/jquery.js"></script> | ||||
| <script type="text/javascript" src="../../../socket.io/socket.io.js"></script> | ||||
| <script type="text/javascript" src="../../../minified/timeslider.js"></script> | ||||
| <script type="text/javascript" src="../../../minified/timeslider.js?callback=require.define"></script> | ||||
| <script type="text/javascript" src="../../../static/custom/timeslider.js"></script> | ||||
| <script type="text/javascript" > | ||||
|   var clientVars = {}; | ||||
|  | ||||
							
								
								
									
										421
									
								
								static/js/prefixfree.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										421
									
								
								static/js/prefixfree.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,421 @@ | ||||
| /** | ||||
|  * StyleFix 1.0.1 | ||||
|  * @author Lea Verou | ||||
|  * MIT license | ||||
|  */ | ||||
| 
 | ||||
| (function(){ | ||||
| 
 | ||||
| if(!window.addEventListener) { | ||||
| 	return; | ||||
| } | ||||
| 
 | ||||
| var self = window.StyleFix = { | ||||
| 	link: function(link) { | ||||
| 		try { | ||||
| 			// Ignore stylesheets with data-noprefix attribute as well as alternate stylesheets
 | ||||
| 			if(link.rel !== 'stylesheet' || !link.sheet.cssRules || link.hasAttribute('data-noprefix')) { | ||||
| 				return; | ||||
| 			} | ||||
| 		} | ||||
| 		catch(e) { | ||||
| 			return; | ||||
| 		} | ||||
|                 if(link.href == "data:text/css,"){ | ||||
|                     return false; | ||||
|                 } | ||||
| 		var url = link.href || link.getAttribute('data-href'), | ||||
| 		    base = url.replace(/[^\/]+$/, ''), | ||||
| 		    parent = link.parentNode, | ||||
| 		    xhr = new XMLHttpRequest(); | ||||
| 		 | ||||
| 		xhr.open('GET', url); | ||||
| 
 | ||||
| 		xhr.onreadystatechange = function() { | ||||
| 			if(xhr.readyState === 4) { | ||||
| 				var css = xhr.responseText; | ||||
| 				 | ||||
| 				if(css && link.parentNode) { | ||||
| 					css = self.fix(css, true, link); | ||||
| 					 | ||||
| 					// Convert relative URLs to absolute, if needed
 | ||||
| 					if(base) { | ||||
| 						css = css.replace(/url\((?:'|")?(.+?)(?:'|")?\)/gi, function($0, url) { | ||||
| 							if(!/^([a-z]{3,10}:|\/|#)/i.test(url)) { // If url not absolute & not a hash
 | ||||
| 								// May contain sequences like /../ and /./ but those DO work
 | ||||
| 								return 'url("' + base + url + '")'; | ||||
| 							} | ||||
| 							 | ||||
| 							return $0;						 | ||||
| 						}); | ||||
| 						 | ||||
| 						// behavior URLs 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); | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user