mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-01-19 14:13:34 +01:00
Replace tabs indentation with spaces indentation
Some files are obviously external libraries, I didn't touch them
This commit is contained in:
parent
03ff5563f4
commit
3d8452b143
35 changed files with 511 additions and 510 deletions
|
@ -17,19 +17,19 @@ 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
|
||||
#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
|
||||
#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
|
||||
#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:
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# connect 500 instances to display :0
|
||||
for i in {1..500}
|
||||
do
|
||||
echo $i
|
||||
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
|
||||
|
|
|
@ -79,14 +79,14 @@ Here are descriptions of the operations, where capital letters are variables:
|
|||
kept MUST be a newline, and the final newline of the document is allowed.
|
||||
"*I" : Apply attribute I from the pool to the following +, =, |+, or |= command.
|
||||
In other words, any number of * ops can come before a +, =, or | but not
|
||||
between a | and the corresponding + or =.
|
||||
between a | and the corresponding + or =.
|
||||
If +, text is inserted having this attribute. If =, text is kept but with
|
||||
the attribute applied as an attribute addition or removal.
|
||||
Consecutive attributes must be sorted lexically by (key,value) with key
|
||||
and value taken as strings. It's illegal to have duplicate keys
|
||||
for (key,value) pairs that apply to the same text. It's illegal to
|
||||
have an empty value for a key in the case of an insertion (+), the
|
||||
pair should just be omitted.
|
||||
the attribute applied as an attribute addition or removal.
|
||||
Consecutive attributes must be sorted lexically by (key,value) with key
|
||||
and value taken as strings. It's illegal to have duplicate keys
|
||||
for (key,value) pairs that apply to the same text. It's illegal to
|
||||
have an empty value for a key in the case of an insertion (+), the
|
||||
pair should just be omitted.
|
||||
|
||||
Characters from the source text that aren't accounted for are assumed to be kept
|
||||
with the same attributes.
|
||||
|
|
|
@ -110,40 +110,41 @@
|
|||
// https://github.com/nomiddlename/log4js-node
|
||||
// You can add as many appenders as you want here:
|
||||
"logconfig" :
|
||||
{ "appenders": [
|
||||
{ "type": "console"
|
||||
//, "category": "access"// only logs pad access
|
||||
}
|
||||
{ "appenders": [
|
||||
{ "type": "console"
|
||||
//, "category": "access"// only logs pad access
|
||||
}
|
||||
/*
|
||||
, { "type": "file"
|
||||
, { "type": "file"
|
||||
, "filename": "your-log-file-here.log"
|
||||
, "maxLogSize": 1024
|
||||
, "backups": 3 // how many log files there're gonna be at max
|
||||
//, "category": "test" // only log a specific category
|
||||
}*/
|
||||
}*/
|
||||
/*
|
||||
, { "type": "logLevelFilter"
|
||||
, "level": "warn" // filters out all log messages that have a lower level than "error"
|
||||
, "appender":
|
||||
{ Use whatever appender you want here }
|
||||
}*/
|
||||
, { "type": "logLevelFilter"
|
||||
, "level": "warn" // filters out all log messages that have a lower level than "error"
|
||||
, "appender":
|
||||
{ Use whatever appender you want here }
|
||||
}*/
|
||||
/*
|
||||
, { "type": "logLevelFilter"
|
||||
, "level": "error" // filters out all log messages that have a lower level than "error"
|
||||
, "appender":
|
||||
{ "type": "smtp"
|
||||
, "subject": "An error occured in your EPL instance!"
|
||||
, "recipients": "bar@blurdybloop.com, baz@blurdybloop.com"
|
||||
, "sendInterval": 60*5 // in secs -- will buffer log messages; set to 0 to send a mail for every message
|
||||
, "transport": "SMTP", "SMTP": { // see https://github.com/andris9/Nodemailer#possible-transport-methods
|
||||
"host": "smtp.example.com", "port": 465,
|
||||
"secureConnection": true,
|
||||
"auth": {
|
||||
"user": "foo@example.com",
|
||||
"pass": "bar_foo"
|
||||
, { "type": "logLevelFilter"
|
||||
, "level": "error" // filters out all log messages that have a lower level than "error"
|
||||
, "appender":
|
||||
{ "type": "smtp"
|
||||
, "subject": "An error occured in your EPL instance!"
|
||||
, "recipients": "bar@blurdybloop.com, baz@blurdybloop.com"
|
||||
, "sendInterval": 60*5 // in secs -- will buffer log messages; set to 0 to send a mail for every message
|
||||
, "transport": "SMTP", "SMTP": { // see https://github.com/andris9/Nodemailer#possible-transport-methods
|
||||
"host": "smtp.example.com", "port": 465,
|
||||
"secureConnection": true,
|
||||
"auth": {
|
||||
"user": "foo@example.com",
|
||||
"pass": "bar_foo"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
] }
|
||||
}
|
||||
}*/
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -554,7 +554,7 @@ exports.deletePad = function(padID, callback)
|
|||
|
||||
/**
|
||||
copyPad(sourceID, destinationID[, force=false]) copies a pad. If force is true,
|
||||
the destination will be overwritten if it exists.
|
||||
the destination will be overwritten if it exists.
|
||||
|
||||
Example returns:
|
||||
|
||||
|
@ -573,7 +573,7 @@ exports.copyPad = function(sourceID, destinationID, force, callback)
|
|||
|
||||
/**
|
||||
movePad(sourceID, destinationID[, force=false]) moves a pad. If force is true,
|
||||
the destination will be overwritten if it exists.
|
||||
the destination will be overwritten if it exists.
|
||||
|
||||
Example returns:
|
||||
|
||||
|
|
|
@ -635,7 +635,7 @@ Pad.prototype.remove = function remove(callback) {
|
|||
|
||||
authorIDs.forEach(function (authorID)
|
||||
{
|
||||
authorManager.removePad(authorID, padID);
|
||||
authorManager.removePad(authorID, padID);
|
||||
});
|
||||
|
||||
callback();
|
||||
|
|
|
@ -151,16 +151,16 @@ exports.checkAccess = function (padID, sessionCookie, token, password, callback)
|
|||
if(sessionInfo.groupID != groupID)
|
||||
{
|
||||
authLogger.debug("Auth failed: wrong group");
|
||||
callback();
|
||||
return;
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
//is validUntil still ok?
|
||||
if(sessionInfo.validUntil <= now)
|
||||
{
|
||||
authLogger.debug("Auth failed: validUntil");
|
||||
callback();
|
||||
return;
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
// There is a valid session
|
||||
|
|
|
@ -1564,7 +1564,7 @@ exports.padUsers = function (padID, callback) {
|
|||
|
||||
author.id = s.author;
|
||||
result.push(author);
|
||||
callback();
|
||||
callback();
|
||||
});
|
||||
}
|
||||
}, function(err) {
|
||||
|
|
|
@ -57,11 +57,11 @@ exports.setSocketIO = function(_socket) {
|
|||
socket.sockets.on('connection', function(client)
|
||||
{
|
||||
if(settings.trustProxy && client.handshake.headers['x-forwarded-for'] !== undefined){
|
||||
client.set('remoteAddress', client.handshake.headers['x-forwarded-for']);
|
||||
}
|
||||
else{
|
||||
client.set('remoteAddress', client.handshake.address.address);
|
||||
}
|
||||
client.set('remoteAddress', client.handshake.headers['x-forwarded-for']);
|
||||
}
|
||||
else{
|
||||
client.set('remoteAddress', client.handshake.address.address);
|
||||
}
|
||||
var clientAuthorized = false;
|
||||
|
||||
//wrap the original send function to log the messages
|
||||
|
|
|
@ -16,49 +16,49 @@ exports.expressCreateServer = function (hook_name, args, cb) {
|
|||
//translate the read only pad to a padId
|
||||
function(callback)
|
||||
{
|
||||
readOnlyManager.getPadId(req.params.id, function(err, _padId)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
readOnlyManager.getPadId(req.params.id, function(err, _padId)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
|
||||
padId = _padId;
|
||||
padId = _padId;
|
||||
|
||||
//we need that to tell hasPadAcess about the pad
|
||||
req.params.pad = padId;
|
||||
//we need that to tell hasPadAcess about the pad
|
||||
req.params.pad = padId;
|
||||
|
||||
callback();
|
||||
});
|
||||
callback();
|
||||
});
|
||||
},
|
||||
//render the html document
|
||||
function(callback)
|
||||
{
|
||||
//return if the there is no padId
|
||||
if(padId == null)
|
||||
{
|
||||
callback("notfound");
|
||||
return;
|
||||
}
|
||||
//return if the there is no padId
|
||||
if(padId == null)
|
||||
{
|
||||
callback("notfound");
|
||||
return;
|
||||
}
|
||||
|
||||
hasPadAccess(req, res, function()
|
||||
{
|
||||
//render the html document
|
||||
exporthtml.getPadHTMLDocument(padId, null, false, function(err, _html)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
html = _html;
|
||||
callback();
|
||||
});
|
||||
});
|
||||
hasPadAccess(req, res, function()
|
||||
{
|
||||
//render the html document
|
||||
exporthtml.getPadHTMLDocument(padId, null, false, function(err, _html)
|
||||
{
|
||||
if(ERR(err, callback)) return;
|
||||
html = _html;
|
||||
callback();
|
||||
});
|
||||
});
|
||||
}
|
||||
], function(err)
|
||||
{
|
||||
//throw any unexpected error
|
||||
if(err && err != "notfound")
|
||||
ERR(err);
|
||||
ERR(err);
|
||||
|
||||
if(err == "notfound")
|
||||
res.send(404, '404 - Not Found');
|
||||
res.send(404, '404 - Not Found');
|
||||
else
|
||||
res.send(html);
|
||||
res.send(html);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -12,20 +12,20 @@ exports.expressCreateServer = function (hook_name, args, cb) {
|
|||
else
|
||||
{
|
||||
padManager.sanitizePadId(padId, function(sanitizedPadId) {
|
||||
//the pad id was sanitized, so we redirect to the sanitized version
|
||||
if(sanitizedPadId != padId)
|
||||
{
|
||||
//the pad id was sanitized, so we redirect to the sanitized version
|
||||
if(sanitizedPadId != padId)
|
||||
{
|
||||
var real_url = sanitizedPadId;
|
||||
var query = url.parse(req.url).query;
|
||||
if ( query ) real_url += '?' + query;
|
||||
res.header('Location', real_url);
|
||||
res.send(302, 'You should be redirected to <a href="' + real_url + '">' + real_url + '</a>');
|
||||
}
|
||||
//the pad id was fine, so just render it
|
||||
else
|
||||
{
|
||||
next();
|
||||
}
|
||||
res.header('Location', real_url);
|
||||
res.send(302, 'You should be redirected to <a href="' + real_url + '">' + real_url + '</a>');
|
||||
}
|
||||
//the pad id was fine, so just render it
|
||||
else
|
||||
{
|
||||
next();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -49,8 +49,8 @@ exports.expressCreateServer = function (hook_name, args, cb) {
|
|||
//there is no custom favicon, send the default favicon
|
||||
if(err)
|
||||
{
|
||||
filePath = path.normalize(__dirname + "/../../../static/favicon.ico");
|
||||
res.sendfile(filePath);
|
||||
filePath = path.normalize(__dirname + "/../../../static/favicon.ico");
|
||||
res.sendfile(filePath);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -455,14 +455,14 @@ function getHTMLFromAtext(pad, atext, authorColors)
|
|||
attribLine: attribLines[i],
|
||||
text: textLines[i]
|
||||
}, " ", " ", "");
|
||||
if (lineContentFromHook)
|
||||
{
|
||||
pieces.push(lineContentFromHook, '');
|
||||
}
|
||||
else
|
||||
{
|
||||
pieces.push(lineContent, '<br>');
|
||||
}
|
||||
if (lineContentFromHook)
|
||||
{
|
||||
pieces.push(lineContentFromHook, '');
|
||||
}
|
||||
else
|
||||
{
|
||||
pieces.push(lineContent, '<br>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -490,7 +490,7 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback)
|
|||
var head =
|
||||
(noDocType ? '' : '<!doctype html>\n') +
|
||||
'<html lang="en">\n' + (noDocType ? '' : '<head>\n' +
|
||||
'<title>' + Security.escapeHTML(padId) + '</title>\n' +
|
||||
'<title>' + Security.escapeHTML(padId) + '</title>\n' +
|
||||
'<meta charset="utf-8">\n' +
|
||||
'<style> * { font-family: arial, sans-serif;\n' +
|
||||
'font-size: 13px;\n' +
|
||||
|
|
|
@ -673,9 +673,9 @@ exports.textLinesMutator = function (lines) {
|
|||
}
|
||||
//print(inSplice+" / "+isCurLineInSplice()+" / "+curSplice[0]+" / "+curSplice[1]+" / "+lines.length);
|
||||
/*if (inSplice && (! isCurLineInSplice()) && (curSplice[0] + curSplice[1] < lines.length)) {
|
||||
print("BLAH");
|
||||
putCurLineInSplice();
|
||||
}*/
|
||||
print("BLAH");
|
||||
putCurLineInSplice();
|
||||
}*/
|
||||
// tests case foo in remove(), which isn't otherwise covered in current impl
|
||||
}
|
||||
//debugPrint("skip");
|
||||
|
|
|
@ -4187,7 +4187,7 @@ function Ace2Inner(){
|
|||
selection.startPoint.index+" / "+
|
||||
selection.endPoint.node.uniqueId()+","+
|
||||
selection.endPoint.index);
|
||||
}*/
|
||||
}*/
|
||||
}
|
||||
return selection;
|
||||
}
|
||||
|
@ -5255,7 +5255,7 @@ function Ace2Inner(){
|
|||
|
||||
if(h){ // apply style to div
|
||||
div.style.height = h +"px";
|
||||
}
|
||||
}
|
||||
|
||||
div.appendChild(odoc.createTextNode(String(n)));
|
||||
fragment.appendChild(div);
|
||||
|
|
|
@ -461,10 +461,10 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded)
|
|||
var startPos = clientVars.collab_client_vars.rev;
|
||||
if(window.location.hash.length > 1)
|
||||
{
|
||||
var hashRev = Number(window.location.hash.substr(1));
|
||||
if(!isNaN(hashRev))
|
||||
{
|
||||
// this is necessary because of the socket.io-event which loads the changesets
|
||||
var hashRev = Number(window.location.hash.substr(1));
|
||||
if(!isNaN(hashRev))
|
||||
{
|
||||
// this is necessary because of the socket.io-event which loads the changesets
|
||||
setTimeout(function() { setSliderPosition(hashRev); }, 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -184,11 +184,11 @@ var chat = (function()
|
|||
}
|
||||
});
|
||||
|
||||
// initial messages are loaded in pad.js' _afterHandshake
|
||||
// initial messages are loaded in pad.js' _afterHandshake
|
||||
|
||||
$("#chatcounter").text(0);
|
||||
$("#chatloadmessagesbutton").click(function()
|
||||
{
|
||||
$("#chatcounter").text(0);
|
||||
$("#chatloadmessagesbutton").click(function()
|
||||
{
|
||||
var start = Math.max(self.historyPointer - 20, 0);
|
||||
var end = self.historyPointer;
|
||||
|
||||
|
@ -200,7 +200,7 @@ var chat = (function()
|
|||
|
||||
pad.collabClient.sendMessage({"type": "GET_CHAT_MESSAGES", "start": start, "end": end});
|
||||
self.historyPointer = start;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -398,7 +398,7 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class
|
|||
if (endPoint && node == endPoint.node)
|
||||
{
|
||||
selEnd = _pointHere(0, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
while (txt.length > 0)
|
||||
{
|
||||
|
|
|
@ -235,9 +235,9 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument)
|
|||
}
|
||||
if (lineClass !== null) result.node.className = lineClass;
|
||||
|
||||
hooks.callAll("acePostWriteDomLineHTML", {
|
||||
node: result.node
|
||||
});
|
||||
hooks.callAll("acePostWriteDomLineHTML", {
|
||||
node: result.node
|
||||
});
|
||||
}
|
||||
result.prepareForAdd = writeHTML;
|
||||
result.finishUpdate = writeHTML;
|
||||
|
|
|
@ -97,21 +97,21 @@ window.html10n = (function(window, document, undefined) {
|
|||
* MicroEvent - to make any js object an event emitter (server or browser)
|
||||
*/
|
||||
|
||||
var MicroEvent = function(){}
|
||||
MicroEvent.prototype = {
|
||||
bind : function(event, fct){
|
||||
var MicroEvent = function(){}
|
||||
MicroEvent.prototype = {
|
||||
bind: function(event, fct){
|
||||
this._events = this._events || {};
|
||||
this._events[event] = this._events[event] || [];
|
||||
this._events[event] = this._events[event] || [];
|
||||
this._events[event].push(fct);
|
||||
},
|
||||
unbind : function(event, fct){
|
||||
unbind: function(event, fct){
|
||||
this._events = this._events || {};
|
||||
if( event in this._events === false ) return;
|
||||
if( event in this._events === false ) return;
|
||||
this._events[event].splice(this._events[event].indexOf(fct), 1);
|
||||
},
|
||||
trigger : function(event /* , args... */){
|
||||
trigger: function(event /* , args... */){
|
||||
this._events = this._events || {};
|
||||
if( event in this._events === false ) return;
|
||||
if( event in this._events === false ) return;
|
||||
for(var i = 0; i < this._events[event].length; i++){
|
||||
this._events[event][i].apply(this, Array.prototype.slice.call(arguments, 1))
|
||||
}
|
||||
|
@ -121,8 +121,8 @@ window.html10n = (function(window, document, undefined) {
|
|||
* mixin will delegate all MicroEvent.js function in the destination object
|
||||
* @param {Object} the object which will support MicroEvent
|
||||
*/
|
||||
MicroEvent.mixin = function(destObject){
|
||||
var props = ['bind', 'unbind', 'trigger'];
|
||||
MicroEvent.mixin = function(destObject){
|
||||
var props = ['bind', 'unbind', 'trigger'];
|
||||
if(!destObject) return;
|
||||
for(var i = 0; i < props.length; i ++){
|
||||
destObject[props[i]] = MicroEvent.prototype[props[i]];
|
||||
|
|
|
@ -140,7 +140,7 @@ var padeditbar = (function()
|
|||
}
|
||||
else if (cmd == 'import_export')
|
||||
{
|
||||
self.toggleDropDown("importexport");
|
||||
self.toggleDropDown("importexport");
|
||||
}
|
||||
else if (cmd == 'savedRevision')
|
||||
{
|
||||
|
|
|
@ -61,7 +61,7 @@ exports.flatten = function (lst) {
|
|||
if (lst[i] != undefined && lst[i] != null) {
|
||||
for (var j = 0; j < lst[i].length; j++) {
|
||||
res.push(lst[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,29 +5,29 @@
|
|||
<html>
|
||||
|
||||
<title><%=settings.title%></title>
|
||||
<script>
|
||||
/*
|
||||
|@licstart The following is the entire license notice for the
|
||||
JavaScript code in this page.|
|
||||
<script>
|
||||
/*
|
||||
|@licstart The following is the entire license notice for the
|
||||
JavaScript code in this page.|
|
||||
|
||||
Copyright 2011 Peter Martischka, Primary Technology.
|
||||
Copyright 2011 Peter Martischka, Primary Technology.
|
||||
|
||||
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
|
||||
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
|
||||
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.
|
||||
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.
|
||||
|
||||
|@licend The above is the entire license notice
|
||||
for the JavaScript code in this page.|
|
||||
*/
|
||||
</script>
|
||||
|@licend The above is the entire license notice
|
||||
for the JavaScript code in this page.|
|
||||
*/
|
||||
</script>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
|
||||
|
|
|
@ -27,278 +27,278 @@
|
|||
(function($){
|
||||
|
||||
bililiteRange = function(el, debug){
|
||||
var ret;
|
||||
if (debug){
|
||||
ret = new NothingRange(); // Easier to force it to use the no-selection type than to try to find an old browser
|
||||
}else if (document.selection && !document.addEventListener){
|
||||
// Internet Explorer 8 and lower
|
||||
ret = new IERange();
|
||||
}else if (window.getSelection && el.setSelectionRange){
|
||||
// Standards. Element is an input or textarea
|
||||
ret = new InputRange();
|
||||
}else if (window.getSelection){
|
||||
// Standards, with any other kind of element
|
||||
ret = new W3CRange()
|
||||
}else{
|
||||
// doesn't support selection
|
||||
ret = new NothingRange();
|
||||
}
|
||||
ret._el = el;
|
||||
ret._doc = el.ownerDocument;
|
||||
ret._win = 'defaultView' in ret._doc ? ret._doc.defaultView : ret._doc.parentWindow;
|
||||
ret._textProp = textProp(el);
|
||||
ret._bounds = [0, ret.length()];
|
||||
return ret;
|
||||
var ret;
|
||||
if (debug){
|
||||
ret = new NothingRange(); // Easier to force it to use the no-selection type than to try to find an old browser
|
||||
}else if (document.selection && !document.addEventListener){
|
||||
// Internet Explorer 8 and lower
|
||||
ret = new IERange();
|
||||
}else if (window.getSelection && el.setSelectionRange){
|
||||
// Standards. Element is an input or textarea
|
||||
ret = new InputRange();
|
||||
}else if (window.getSelection){
|
||||
// Standards, with any other kind of element
|
||||
ret = new W3CRange()
|
||||
}else{
|
||||
// doesn't support selection
|
||||
ret = new NothingRange();
|
||||
}
|
||||
ret._el = el;
|
||||
ret._doc = el.ownerDocument;
|
||||
ret._win = 'defaultView' in ret._doc ? ret._doc.defaultView : ret._doc.parentWindow;
|
||||
ret._textProp = textProp(el);
|
||||
ret._bounds = [0, ret.length()];
|
||||
return ret;
|
||||
}
|
||||
|
||||
function textProp(el){
|
||||
// returns the property that contains the text of the element
|
||||
if (typeof el.value != 'undefined') return 'value';
|
||||
if (typeof el.text != 'undefined') return 'text';
|
||||
if (typeof el.textContent != 'undefined') return 'textContent';
|
||||
return 'innerText';
|
||||
// returns the property that contains the text of the element
|
||||
if (typeof el.value != 'undefined') return 'value';
|
||||
if (typeof el.text != 'undefined') return 'text';
|
||||
if (typeof el.textContent != 'undefined') return 'textContent';
|
||||
return 'innerText';
|
||||
}
|
||||
|
||||
// base class
|
||||
function Range(){}
|
||||
Range.prototype = {
|
||||
length: function() {
|
||||
return this._el[this._textProp].replace(/\r/g, '').length; // need to correct for IE's CrLf weirdness
|
||||
},
|
||||
bounds: function(s){
|
||||
if (s === 'all'){
|
||||
this._bounds = [0, this.length()];
|
||||
}else if (s === 'start'){
|
||||
this._bounds = [0, 0];
|
||||
}else if (s === 'end'){
|
||||
this._bounds = [this.length(), this.length()];
|
||||
}else if (s === 'selection'){
|
||||
this.bounds ('all'); // first select the whole thing for constraining
|
||||
this._bounds = this._nativeSelection();
|
||||
}else if (s){
|
||||
this._bounds = s; // don't error check now; the element may change at any moment, so constrain it when we need it.
|
||||
}else{
|
||||
var b = [
|
||||
Math.max(0, Math.min (this.length(), this._bounds[0])),
|
||||
Math.max(0, Math.min (this.length(), this._bounds[1]))
|
||||
];
|
||||
return b; // need to constrain it to fit
|
||||
}
|
||||
return this; // allow for chaining
|
||||
},
|
||||
select: function(){
|
||||
this._nativeSelect(this._nativeRange(this.bounds()));
|
||||
return this; // allow for chaining
|
||||
},
|
||||
text: function(text, select){
|
||||
if (arguments.length){
|
||||
this._nativeSetText(text, this._nativeRange(this.bounds()));
|
||||
if (select == 'start'){
|
||||
this.bounds ([this._bounds[0], this._bounds[0]]);
|
||||
this.select();
|
||||
}else if (select == 'end'){
|
||||
this.bounds ([this._bounds[0]+text.length, this._bounds[0]+text.length]);
|
||||
this.select();
|
||||
}else if (select == 'all'){
|
||||
this.bounds ([this._bounds[0], this._bounds[0]+text.length]);
|
||||
this.select();
|
||||
}
|
||||
return this; // allow for chaining
|
||||
}else{
|
||||
return this._nativeGetText(this._nativeRange(this.bounds()));
|
||||
}
|
||||
},
|
||||
insertEOL: function (){
|
||||
this._nativeEOL();
|
||||
this._bounds = [this._bounds[0]+1, this._bounds[0]+1]; // move past the EOL marker
|
||||
return this;
|
||||
}
|
||||
length: function() {
|
||||
return this._el[this._textProp].replace(/\r/g, '').length; // need to correct for IE's CrLf weirdness
|
||||
},
|
||||
bounds: function(s){
|
||||
if (s === 'all'){
|
||||
this._bounds = [0, this.length()];
|
||||
}else if (s === 'start'){
|
||||
this._bounds = [0, 0];
|
||||
}else if (s === 'end'){
|
||||
this._bounds = [this.length(), this.length()];
|
||||
}else if (s === 'selection'){
|
||||
this.bounds ('all'); // first select the whole thing for constraining
|
||||
this._bounds = this._nativeSelection();
|
||||
}else if (s){
|
||||
this._bounds = s; // don't error check now; the element may change at any moment, so constrain it when we need it.
|
||||
}else{
|
||||
var b = [
|
||||
Math.max(0, Math.min (this.length(), this._bounds[0])),
|
||||
Math.max(0, Math.min (this.length(), this._bounds[1]))
|
||||
];
|
||||
return b; // need to constrain it to fit
|
||||
}
|
||||
return this; // allow for chaining
|
||||
},
|
||||
select: function(){
|
||||
this._nativeSelect(this._nativeRange(this.bounds()));
|
||||
return this; // allow for chaining
|
||||
},
|
||||
text: function(text, select){
|
||||
if (arguments.length){
|
||||
this._nativeSetText(text, this._nativeRange(this.bounds()));
|
||||
if (select == 'start'){
|
||||
this.bounds ([this._bounds[0], this._bounds[0]]);
|
||||
this.select();
|
||||
}else if (select == 'end'){
|
||||
this.bounds ([this._bounds[0]+text.length, this._bounds[0]+text.length]);
|
||||
this.select();
|
||||
}else if (select == 'all'){
|
||||
this.bounds ([this._bounds[0], this._bounds[0]+text.length]);
|
||||
this.select();
|
||||
}
|
||||
return this; // allow for chaining
|
||||
}else{
|
||||
return this._nativeGetText(this._nativeRange(this.bounds()));
|
||||
}
|
||||
},
|
||||
insertEOL: function (){
|
||||
this._nativeEOL();
|
||||
this._bounds = [this._bounds[0]+1, this._bounds[0]+1]; // move past the EOL marker
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function IERange(){}
|
||||
IERange.prototype = new Range();
|
||||
IERange.prototype._nativeRange = function (bounds){
|
||||
var rng;
|
||||
if (this._el.tagName == 'INPUT'){
|
||||
// IE 8 is very inconsistent; textareas have createTextRange but it doesn't work
|
||||
rng = this._el.createTextRange();
|
||||
}else{
|
||||
rng = this._doc.body.createTextRange ();
|
||||
rng.moveToElementText(this._el);
|
||||
}
|
||||
if (bounds){
|
||||
if (bounds[1] < 0) bounds[1] = 0; // IE tends to run elements out of bounds
|
||||
if (bounds[0] > this.length()) bounds[0] = this.length();
|
||||
if (bounds[1] < rng.text.replace(/\r/g, '').length){ // correct for IE's CrLf wierdness
|
||||
// block-display elements have an invisible, uncounted end of element marker, so we move an extra one and use the current length of the range
|
||||
rng.moveEnd ('character', -1);
|
||||
rng.moveEnd ('character', bounds[1]-rng.text.replace(/\r/g, '').length);
|
||||
}
|
||||
if (bounds[0] > 0) rng.moveStart('character', bounds[0]);
|
||||
}
|
||||
return rng;
|
||||
var rng;
|
||||
if (this._el.tagName == 'INPUT'){
|
||||
// IE 8 is very inconsistent; textareas have createTextRange but it doesn't work
|
||||
rng = this._el.createTextRange();
|
||||
}else{
|
||||
rng = this._doc.body.createTextRange ();
|
||||
rng.moveToElementText(this._el);
|
||||
}
|
||||
if (bounds){
|
||||
if (bounds[1] < 0) bounds[1] = 0; // IE tends to run elements out of bounds
|
||||
if (bounds[0] > this.length()) bounds[0] = this.length();
|
||||
if (bounds[1] < rng.text.replace(/\r/g, '').length){ // correct for IE's CrLf wierdness
|
||||
// block-display elements have an invisible, uncounted end of element marker, so we move an extra one and use the current length of the range
|
||||
rng.moveEnd ('character', -1);
|
||||
rng.moveEnd ('character', bounds[1]-rng.text.replace(/\r/g, '').length);
|
||||
}
|
||||
if (bounds[0] > 0) rng.moveStart('character', bounds[0]);
|
||||
}
|
||||
return rng;
|
||||
};
|
||||
IERange.prototype._nativeSelect = function (rng){
|
||||
rng.select();
|
||||
rng.select();
|
||||
};
|
||||
IERange.prototype._nativeSelection = function (){
|
||||
// returns [start, end] for the selection constrained to be in element
|
||||
var rng = this._nativeRange(); // range of the element to constrain to
|
||||
var len = this.length();
|
||||
if (this._doc.selection.type != 'Text') return [0,0]; // append to the end
|
||||
var sel = this._doc.selection.createRange();
|
||||
try{
|
||||
return [
|
||||
iestart(sel, rng),
|
||||
ieend (sel, rng)
|
||||
];
|
||||
}catch (e){
|
||||
// IE gets upset sometimes about comparing text to input elements, but the selections cannot overlap, so make a best guess
|
||||
return (sel.parentElement().sourceIndex < this._el.sourceIndex) ? [0,0] : [len, len];
|
||||
}
|
||||
// returns [start, end] for the selection constrained to be in element
|
||||
var rng = this._nativeRange(); // range of the element to constrain to
|
||||
var len = this.length();
|
||||
if (this._doc.selection.type != 'Text') return [0,0]; // append to the end
|
||||
var sel = this._doc.selection.createRange();
|
||||
try{
|
||||
return [
|
||||
iestart(sel, rng),
|
||||
ieend (sel, rng)
|
||||
];
|
||||
}catch (e){
|
||||
// IE gets upset sometimes about comparing text to input elements, but the selections cannot overlap, so make a best guess
|
||||
return (sel.parentElement().sourceIndex < this._el.sourceIndex) ? [0,0] : [len, len];
|
||||
}
|
||||
};
|
||||
IERange.prototype._nativeGetText = function (rng){
|
||||
return rng.text.replace(/\r/g, ''); // correct for IE's CrLf weirdness
|
||||
return rng.text.replace(/\r/g, ''); // correct for IE's CrLf weirdness
|
||||
};
|
||||
IERange.prototype._nativeSetText = function (text, rng){
|
||||
rng.text = text;
|
||||
rng.text = text;
|
||||
};
|
||||
IERange.prototype._nativeEOL = function(){
|
||||
if (typeof this._el.value != 'undefined'){
|
||||
this.text('\n'); // for input and textarea, insert it straight
|
||||
}else{
|
||||
this._nativeRange(this.bounds()).pasteHTML('<br/>');
|
||||
}
|
||||
if (typeof this._el.value != 'undefined'){
|
||||
this.text('\n'); // for input and textarea, insert it straight
|
||||
}else{
|
||||
this._nativeRange(this.bounds()).pasteHTML('<br/>');
|
||||
}
|
||||
};
|
||||
// IE internals
|
||||
function iestart(rng, constraint){
|
||||
// returns the position (in character) of the start of rng within constraint. If it's not in constraint, returns 0 if it's before, length if it's after
|
||||
var len = constraint.text.replace(/\r/g, '').length; // correct for IE's CrLf wierdness
|
||||
if (rng.compareEndPoints ('StartToStart', constraint) <= 0) return 0; // at or before the beginning
|
||||
if (rng.compareEndPoints ('StartToEnd', constraint) >= 0) return len;
|
||||
for (var i = 0; rng.compareEndPoints ('StartToStart', constraint) > 0; ++i, rng.moveStart('character', -1));
|
||||
return i;
|
||||
// returns the position (in character) of the start of rng within constraint. If it's not in constraint, returns 0 if it's before, length if it's after
|
||||
var len = constraint.text.replace(/\r/g, '').length; // correct for IE's CrLf wierdness
|
||||
if (rng.compareEndPoints ('StartToStart', constraint) <= 0) return 0; // at or before the beginning
|
||||
if (rng.compareEndPoints ('StartToEnd', constraint) >= 0) return len;
|
||||
for (var i = 0; rng.compareEndPoints ('StartToStart', constraint) > 0; ++i, rng.moveStart('character', -1));
|
||||
return i;
|
||||
}
|
||||
function ieend (rng, constraint){
|
||||
// returns the position (in character) of the end of rng within constraint. If it's not in constraint, returns 0 if it's before, length if it's after
|
||||
var len = constraint.text.replace(/\r/g, '').length; // correct for IE's CrLf wierdness
|
||||
if (rng.compareEndPoints ('EndToEnd', constraint) >= 0) return len; // at or after the end
|
||||
if (rng.compareEndPoints ('EndToStart', constraint) <= 0) return 0;
|
||||
for (var i = 0; rng.compareEndPoints ('EndToStart', constraint) > 0; ++i, rng.moveEnd('character', -1));
|
||||
return i;
|
||||
// returns the position (in character) of the end of rng within constraint. If it's not in constraint, returns 0 if it's before, length if it's after
|
||||
var len = constraint.text.replace(/\r/g, '').length; // correct for IE's CrLf wierdness
|
||||
if (rng.compareEndPoints ('EndToEnd', constraint) >= 0) return len; // at or after the end
|
||||
if (rng.compareEndPoints ('EndToStart', constraint) <= 0) return 0;
|
||||
for (var i = 0; rng.compareEndPoints ('EndToStart', constraint) > 0; ++i, rng.moveEnd('character', -1));
|
||||
return i;
|
||||
}
|
||||
|
||||
// an input element in a standards document. "Native Range" is just the bounds array
|
||||
function InputRange(){}
|
||||
InputRange.prototype = new Range();
|
||||
InputRange.prototype._nativeRange = function(bounds) {
|
||||
return bounds || [0, this.length()];
|
||||
return bounds || [0, this.length()];
|
||||
};
|
||||
InputRange.prototype._nativeSelect = function (rng){
|
||||
this._el.setSelectionRange(rng[0], rng[1]);
|
||||
this._el.setSelectionRange(rng[0], rng[1]);
|
||||
};
|
||||
InputRange.prototype._nativeSelection = function(){
|
||||
return [this._el.selectionStart, this._el.selectionEnd];
|
||||
return [this._el.selectionStart, this._el.selectionEnd];
|
||||
};
|
||||
InputRange.prototype._nativeGetText = function(rng){
|
||||
return this._el.value.substring(rng[0], rng[1]);
|
||||
return this._el.value.substring(rng[0], rng[1]);
|
||||
};
|
||||
InputRange.prototype._nativeSetText = function(text, rng){
|
||||
var val = this._el.value;
|
||||
this._el.value = val.substring(0, rng[0]) + text + val.substring(rng[1]);
|
||||
var val = this._el.value;
|
||||
this._el.value = val.substring(0, rng[0]) + text + val.substring(rng[1]);
|
||||
};
|
||||
InputRange.prototype._nativeEOL = function(){
|
||||
this.text('\n');
|
||||
this.text('\n');
|
||||
};
|
||||
|
||||
function W3CRange(){}
|
||||
W3CRange.prototype = new Range();
|
||||
W3CRange.prototype._nativeRange = function (bounds){
|
||||
var rng = this._doc.createRange();
|
||||
rng.selectNodeContents(this._el);
|
||||
if (bounds){
|
||||
w3cmoveBoundary (rng, bounds[0], true, this._el);
|
||||
rng.collapse (true);
|
||||
w3cmoveBoundary (rng, bounds[1]-bounds[0], false, this._el);
|
||||
}
|
||||
return rng;
|
||||
var rng = this._doc.createRange();
|
||||
rng.selectNodeContents(this._el);
|
||||
if (bounds){
|
||||
w3cmoveBoundary (rng, bounds[0], true, this._el);
|
||||
rng.collapse (true);
|
||||
w3cmoveBoundary (rng, bounds[1]-bounds[0], false, this._el);
|
||||
}
|
||||
return rng;
|
||||
};
|
||||
W3CRange.prototype._nativeSelect = function (rng){
|
||||
this._win.getSelection().removeAllRanges();
|
||||
this._win.getSelection().addRange (rng);
|
||||
this._win.getSelection().removeAllRanges();
|
||||
this._win.getSelection().addRange (rng);
|
||||
};
|
||||
W3CRange.prototype._nativeSelection = function (){
|
||||
// returns [start, end] for the selection constrained to be in element
|
||||
var rng = this._nativeRange(); // range of the element to constrain to
|
||||
if (this._win.getSelection().rangeCount == 0) return [this.length(), this.length()]; // append to the end
|
||||
var sel = this._win.getSelection().getRangeAt(0);
|
||||
return [
|
||||
w3cstart(sel, rng),
|
||||
w3cend (sel, rng)
|
||||
];
|
||||
}
|
||||
// returns [start, end] for the selection constrained to be in element
|
||||
var rng = this._nativeRange(); // range of the element to constrain to
|
||||
if (this._win.getSelection().rangeCount == 0) return [this.length(), this.length()]; // append to the end
|
||||
var sel = this._win.getSelection().getRangeAt(0);
|
||||
return [
|
||||
w3cstart(sel, rng),
|
||||
w3cend (sel, rng)
|
||||
];
|
||||
}
|
||||
W3CRange.prototype._nativeGetText = function (rng){
|
||||
return rng.toString();
|
||||
return rng.toString();
|
||||
};
|
||||
W3CRange.prototype._nativeSetText = function (text, rng){
|
||||
rng.deleteContents();
|
||||
rng.insertNode (this._doc.createTextNode(text));
|
||||
this._el.normalize(); // merge the text with the surrounding text
|
||||
rng.deleteContents();
|
||||
rng.insertNode (this._doc.createTextNode(text));
|
||||
this._el.normalize(); // merge the text with the surrounding text
|
||||
};
|
||||
W3CRange.prototype._nativeEOL = function(){
|
||||
var rng = this._nativeRange(this.bounds());
|
||||
rng.deleteContents();
|
||||
var br = this._doc.createElement('br');
|
||||
br.setAttribute ('_moz_dirty', ''); // for Firefox
|
||||
rng.insertNode (br);
|
||||
rng.insertNode (this._doc.createTextNode('\n'));
|
||||
rng.collapse (false);
|
||||
var rng = this._nativeRange(this.bounds());
|
||||
rng.deleteContents();
|
||||
var br = this._doc.createElement('br');
|
||||
br.setAttribute ('_moz_dirty', ''); // for Firefox
|
||||
rng.insertNode (br);
|
||||
rng.insertNode (this._doc.createTextNode('\n'));
|
||||
rng.collapse (false);
|
||||
};
|
||||
// W3C internals
|
||||
function nextnode (node, root){
|
||||
// in-order traversal
|
||||
// we've already visited node, so get kids then siblings
|
||||
if (node.firstChild) return node.firstChild;
|
||||
if (node.nextSibling) return node.nextSibling;
|
||||
if (node===root) return null;
|
||||
while (node.parentNode){
|
||||
// get uncles
|
||||
node = node.parentNode;
|
||||
if (node == root) return null;
|
||||
if (node.nextSibling) return node.nextSibling;
|
||||
}
|
||||
return null;
|
||||
// in-order traversal
|
||||
// we've already visited node, so get kids then siblings
|
||||
if (node.firstChild) return node.firstChild;
|
||||
if (node.nextSibling) return node.nextSibling;
|
||||
if (node===root) return null;
|
||||
while (node.parentNode){
|
||||
// get uncles
|
||||
node = node.parentNode;
|
||||
if (node == root) return null;
|
||||
if (node.nextSibling) return node.nextSibling;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function w3cmoveBoundary (rng, n, bStart, el){
|
||||
// move the boundary (bStart == true ? start : end) n characters forward, up to the end of element el. Forward only!
|
||||
// if the start is moved after the end, then an exception is raised
|
||||
if (n <= 0) return;
|
||||
var node = rng[bStart ? 'startContainer' : 'endContainer'];
|
||||
if (node.nodeType == 3){
|
||||
// we may be starting somewhere into the text
|
||||
n += rng[bStart ? 'startOffset' : 'endOffset'];
|
||||
}
|
||||
while (node){
|
||||
if (node.nodeType == 3){
|
||||
if (n <= node.nodeValue.length){
|
||||
rng[bStart ? 'setStart' : 'setEnd'](node, n);
|
||||
// special case: if we end next to a <br>, include that node.
|
||||
if (n == node.nodeValue.length){
|
||||
// skip past zero-length text nodes
|
||||
for (var next = nextnode (node, el); next && next.nodeType==3 && next.nodeValue.length == 0; next = nextnode(next, el)){
|
||||
rng[bStart ? 'setStartAfter' : 'setEndAfter'](next);
|
||||
}
|
||||
if (next && next.nodeType == 1 && next.nodeName == "BR") rng[bStart ? 'setStartAfter' : 'setEndAfter'](next);
|
||||
}
|
||||
return;
|
||||
}else{
|
||||
rng[bStart ? 'setStartAfter' : 'setEndAfter'](node); // skip past this one
|
||||
n -= node.nodeValue.length; // and eat these characters
|
||||
}
|
||||
}
|
||||
node = nextnode (node, el);
|
||||
}
|
||||
// move the boundary (bStart == true ? start : end) n characters forward, up to the end of element el. Forward only!
|
||||
// if the start is moved after the end, then an exception is raised
|
||||
if (n <= 0) return;
|
||||
var node = rng[bStart ? 'startContainer' : 'endContainer'];
|
||||
if (node.nodeType == 3){
|
||||
// we may be starting somewhere into the text
|
||||
n += rng[bStart ? 'startOffset' : 'endOffset'];
|
||||
}
|
||||
while (node){
|
||||
if (node.nodeType == 3){
|
||||
if (n <= node.nodeValue.length){
|
||||
rng[bStart ? 'setStart' : 'setEnd'](node, n);
|
||||
// special case: if we end next to a <br>, include that node.
|
||||
if (n == node.nodeValue.length){
|
||||
// skip past zero-length text nodes
|
||||
for (var next = nextnode (node, el); next && next.nodeType==3 && next.nodeValue.length == 0; next = nextnode(next, el)){
|
||||
rng[bStart ? 'setStartAfter' : 'setEndAfter'](next);
|
||||
}
|
||||
if (next && next.nodeType == 1 && next.nodeName == "BR") rng[bStart ? 'setStartAfter' : 'setEndAfter'](next);
|
||||
}
|
||||
return;
|
||||
}else{
|
||||
rng[bStart ? 'setStartAfter' : 'setEndAfter'](node); // skip past this one
|
||||
n -= node.nodeValue.length; // and eat these characters
|
||||
}
|
||||
}
|
||||
node = nextnode (node, el);
|
||||
}
|
||||
}
|
||||
var START_TO_START = 0; // from the w3c definitions
|
||||
var START_TO_END = 1;
|
||||
|
@ -311,39 +311,39 @@ var END_TO_START = 3;
|
|||
// * Range.START_TO_END compares the start boundary-point of sourceRange to the end boundary-point of range.
|
||||
// * Range.START_TO_START compares the start boundary-point of sourceRange to the start boundary-point of range.
|
||||
function w3cstart(rng, constraint){
|
||||
if (rng.compareBoundaryPoints (START_TO_START, constraint) <= 0) return 0; // at or before the beginning
|
||||
if (rng.compareBoundaryPoints (END_TO_START, constraint) >= 0) return constraint.toString().length;
|
||||
rng = rng.cloneRange(); // don't change the original
|
||||
rng.setEnd (constraint.endContainer, constraint.endOffset); // they now end at the same place
|
||||
return constraint.toString().length - rng.toString().length;
|
||||
if (rng.compareBoundaryPoints (START_TO_START, constraint) <= 0) return 0; // at or before the beginning
|
||||
if (rng.compareBoundaryPoints (END_TO_START, constraint) >= 0) return constraint.toString().length;
|
||||
rng = rng.cloneRange(); // don't change the original
|
||||
rng.setEnd (constraint.endContainer, constraint.endOffset); // they now end at the same place
|
||||
return constraint.toString().length - rng.toString().length;
|
||||
}
|
||||
function w3cend (rng, constraint){
|
||||
if (rng.compareBoundaryPoints (END_TO_END, constraint) >= 0) return constraint.toString().length; // at or after the end
|
||||
if (rng.compareBoundaryPoints (START_TO_END, constraint) <= 0) return 0;
|
||||
rng = rng.cloneRange(); // don't change the original
|
||||
rng.setStart (constraint.startContainer, constraint.startOffset); // they now start at the same place
|
||||
return rng.toString().length;
|
||||
if (rng.compareBoundaryPoints (END_TO_END, constraint) >= 0) return constraint.toString().length; // at or after the end
|
||||
if (rng.compareBoundaryPoints (START_TO_END, constraint) <= 0) return 0;
|
||||
rng = rng.cloneRange(); // don't change the original
|
||||
rng.setStart (constraint.startContainer, constraint.startOffset); // they now start at the same place
|
||||
return rng.toString().length;
|
||||
}
|
||||
|
||||
function NothingRange(){}
|
||||
NothingRange.prototype = new Range();
|
||||
NothingRange.prototype._nativeRange = function(bounds) {
|
||||
return bounds || [0,this.length()];
|
||||
return bounds || [0,this.length()];
|
||||
};
|
||||
NothingRange.prototype._nativeSelect = function (rng){ // do nothing
|
||||
};
|
||||
NothingRange.prototype._nativeSelection = function(){
|
||||
return [0,0];
|
||||
return [0,0];
|
||||
};
|
||||
NothingRange.prototype._nativeGetText = function (rng){
|
||||
return this._el[this._textProp].substring(rng[0], rng[1]);
|
||||
return this._el[this._textProp].substring(rng[0], rng[1]);
|
||||
};
|
||||
NothingRange.prototype._nativeSetText = function (text, rng){
|
||||
var val = this._el[this._textProp];
|
||||
this._el[this._textProp] = val.substring(0, rng[0]) + text + val.substring(rng[1]);
|
||||
var val = this._el[this._textProp];
|
||||
this._el[this._textProp] = val.substring(0, rng[0]) + text + val.substring(rng[1]);
|
||||
};
|
||||
NothingRange.prototype._nativeEOL = function(){
|
||||
this.text('\n');
|
||||
this.text('\n');
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
|
@ -378,90 +378,90 @@ NothingRange.prototype._nativeEOL = function(){
|
|||
(function($){
|
||||
|
||||
$.fn.sendkeys = function (x, opts){
|
||||
return this.each( function(){
|
||||
var localkeys = $.extend({}, opts, $(this).data('sendkeys')); // allow for element-specific key functions
|
||||
// most elements to not keep track of their selection when they lose focus, so we have to do it for them
|
||||
var rng = $.data (this, 'sendkeys.selection');
|
||||
if (!rng){
|
||||
rng = bililiteRange(this).bounds('selection');
|
||||
$.data(this, 'sendkeys.selection', rng);
|
||||
$(this).bind('mouseup.sendkeys', function(){
|
||||
// we have to update the saved range. The routines here update the bounds with each press, but actual keypresses and mouseclicks do not
|
||||
$.data(this, 'sendkeys.selection').bounds('selection');
|
||||
}).bind('keyup.sendkeys', function(evt){
|
||||
// restore the selection if we got here with a tab (a click should select what was clicked on)
|
||||
if (evt.which == 9){
|
||||
// there's a flash of selection when we restore the focus, but I don't know how to avoid that.
|
||||
$.data(this, 'sendkeys.selection').select();
|
||||
}else{
|
||||
$.data(this, 'sendkeys.selection').bounds('selection');
|
||||
}
|
||||
});
|
||||
}
|
||||
this.focus();
|
||||
if (typeof x === 'undefined') return; // no string, so we just set up the event handlers
|
||||
$.data(this, 'sendkeys.originalText', rng.text());
|
||||
x.replace(/\n/g, '{enter}'). // turn line feeds into explicit break insertions
|
||||
replace(/{[^}]*}|[^{]+/g, function(s){
|
||||
(localkeys[s] || $.fn.sendkeys.defaults[s] || $.fn.sendkeys.defaults.simplechar)(rng, s);
|
||||
});
|
||||
$(this).trigger({type: 'sendkeys', which: x});
|
||||
});
|
||||
return this.each( function(){
|
||||
var localkeys = $.extend({}, opts, $(this).data('sendkeys')); // allow for element-specific key functions
|
||||
// most elements to not keep track of their selection when they lose focus, so we have to do it for them
|
||||
var rng = $.data (this, 'sendkeys.selection');
|
||||
if (!rng){
|
||||
rng = bililiteRange(this).bounds('selection');
|
||||
$.data(this, 'sendkeys.selection', rng);
|
||||
$(this).bind('mouseup.sendkeys', function(){
|
||||
// we have to update the saved range. The routines here update the bounds with each press, but actual keypresses and mouseclicks do not
|
||||
$.data(this, 'sendkeys.selection').bounds('selection');
|
||||
}).bind('keyup.sendkeys', function(evt){
|
||||
// restore the selection if we got here with a tab (a click should select what was clicked on)
|
||||
if (evt.which == 9){
|
||||
// there's a flash of selection when we restore the focus, but I don't know how to avoid that
|
||||
$.data(this, 'sendkeys.selection').select();
|
||||
}else{
|
||||
$.data(this, 'sendkeys.selection').bounds('selection');
|
||||
}
|
||||
});
|
||||
}
|
||||
this.focus();
|
||||
if (typeof x === 'undefined') return; // no string, so we just set up the event handlers
|
||||
$.data(this, 'sendkeys.originalText', rng.text());
|
||||
x.replace(/\n/g, '{enter}'). // turn line feeds into explicit break insertions
|
||||
replace(/{[^}]*}|[^{]+/g, function(s){
|
||||
(localkeys[s] || $.fn.sendkeys.defaults[s] || $.fn.sendkeys.defaults.simplechar)(rng, s);
|
||||
});
|
||||
$(this).trigger({type: 'sendkeys', which: x});
|
||||
});
|
||||
}; // sendkeys
|
||||
|
||||
|
||||
// add the functions publicly so they can be overridden
|
||||
$.fn.sendkeys.defaults = {
|
||||
simplechar: function (rng, s){
|
||||
rng.text(s, 'end');
|
||||
for (var i =0; i < s.length; ++i){
|
||||
var x = s.charCodeAt(i);
|
||||
// a bit of cheating: rng._el is the element associated with rng.
|
||||
$(rng._el).trigger({type: 'keypress', keyCode: x, which: x, charCode: x});
|
||||
}
|
||||
},
|
||||
'{{}': function (rng){
|
||||
$.fn.sendkeys.defaults.simplechar (rng, '{')
|
||||
},
|
||||
'{enter}': function (rng){
|
||||
rng.insertEOL();
|
||||
rng.select();
|
||||
var x = '\n'.charCodeAt(0);
|
||||
$(rng._el).trigger({type: 'keypress', keyCode: x, which: x, charCode: x});
|
||||
},
|
||||
'{backspace}': function (rng){
|
||||
var b = rng.bounds();
|
||||
if (b[0] == b[1]) rng.bounds([b[0]-1, b[0]]); // no characters selected; it's just an insertion point. Remove the previous character
|
||||
rng.text('', 'end'); // delete the characters and update the selection
|
||||
},
|
||||
'{del}': function (rng){
|
||||
var b = rng.bounds();
|
||||
if (b[0] == b[1]) rng.bounds([b[0], b[0]+1]); // no characters selected; it's just an insertion point. Remove the next character
|
||||
rng.text('', 'end'); // delete the characters and update the selection
|
||||
},
|
||||
'{rightarrow}': function (rng){
|
||||
var b = rng.bounds();
|
||||
if (b[0] == b[1]) ++b[1]; // no characters selected; it's just an insertion point. Move to the right
|
||||
rng.bounds([b[1], b[1]]).select();
|
||||
},
|
||||
'{leftarrow}': function (rng){
|
||||
var b = rng.bounds();
|
||||
if (b[0] == b[1]) --b[0]; // no characters selected; it's just an insertion point. Move to the left
|
||||
rng.bounds([b[0], b[0]]).select();
|
||||
},
|
||||
'{selectall}' : function (rng){
|
||||
rng.bounds('all').select();
|
||||
},
|
||||
'{selection}': function (rng){
|
||||
$.fn.sendkeys.defaults.simplechar(rng, $.data(rng._el, 'sendkeys.originalText'));
|
||||
},
|
||||
'{mark}' : function (rng){
|
||||
var bounds = rng.bounds();
|
||||
$(rng._el).one('sendkeys', function(){
|
||||
// set up the event listener to change the selection after the sendkeys is done
|
||||
rng.bounds(bounds).select();
|
||||
});
|
||||
}
|
||||
simplechar: function (rng, s){
|
||||
rng.text(s, 'end');
|
||||
for (var i =0; i < s.length; ++i){
|
||||
var x = s.charCodeAt(i);
|
||||
// a bit of cheating: rng._el is the element associated with rng.
|
||||
$(rng._el).trigger({type: 'keypress', keyCode: x, which: x, charCode: x});
|
||||
}
|
||||
},
|
||||
'{{}': function (rng){
|
||||
$.fn.sendkeys.defaults.simplechar (rng, '{')
|
||||
},
|
||||
'{enter}': function (rng){
|
||||
rng.insertEOL();
|
||||
rng.select();
|
||||
var x = '\n'.charCodeAt(0);
|
||||
$(rng._el).trigger({type: 'keypress', keyCode: x, which: x, charCode: x});
|
||||
},
|
||||
'{backspace}': function (rng){
|
||||
var b = rng.bounds();
|
||||
if (b[0] == b[1]) rng.bounds([b[0]-1, b[0]]); // no characters selected; it's just an insertion point. Remove the previous character
|
||||
rng.text('', 'end'); // delete the characters and update the selection
|
||||
},
|
||||
'{del}': function (rng){
|
||||
var b = rng.bounds();
|
||||
if (b[0] == b[1]) rng.bounds([b[0], b[0]+1]); // no characters selected; it's just an insertion point. Remove the next character
|
||||
rng.text('', 'end'); // delete the characters and update the selection
|
||||
},
|
||||
'{rightarrow}': function (rng){
|
||||
var b = rng.bounds();
|
||||
if (b[0] == b[1]) ++b[1]; // no characters selected; it's just an insertion point. Move to the right
|
||||
rng.bounds([b[1], b[1]]).select();
|
||||
},
|
||||
'{leftarrow}': function (rng){
|
||||
var b = rng.bounds();
|
||||
if (b[0] == b[1]) --b[0]; // no characters selected; it's just an insertion point. Move to the left
|
||||
rng.bounds([b[0], b[0]]).select();
|
||||
},
|
||||
'{selectall}' : function (rng){
|
||||
rng.bounds('all').select();
|
||||
},
|
||||
'{selection}': function (rng){
|
||||
$.fn.sendkeys.defaults.simplechar(rng, $.data(rng._el, 'sendkeys.originalText'));
|
||||
},
|
||||
'{mark}' : function (rng){
|
||||
var bounds = rng.bounds();
|
||||
$(rng._el).one('sendkeys', function(){
|
||||
// set up the event listener to change the selection after the sendkeys is done
|
||||
rng.bounds(bounds).select();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery)
|
||||
|
|
|
@ -188,13 +188,13 @@ $(function(){
|
|||
|
||||
//initalize the test helper
|
||||
helper.init(function(){
|
||||
//configure and start the test framework
|
||||
//configure and start the test framework
|
||||
var grep = getURLParameter("grep");
|
||||
if(grep != "null"){
|
||||
mocha.grep(grep);
|
||||
}
|
||||
|
||||
mocha.ignoreLeaks();
|
||||
mocha.ignoreLeaks();
|
||||
|
||||
mocha.reporter(WebdriverAndHtmlReporter(mocha._reporter));
|
||||
|
||||
|
|
|
@ -6,14 +6,14 @@ describe("the test helper", function(){
|
|||
var times = 10;
|
||||
|
||||
var loadPad = function(){
|
||||
helper.newPad(function(){
|
||||
helper.newPad(function(){
|
||||
times--;
|
||||
if(times > 0){
|
||||
loadPad();
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
loadPad();
|
||||
|
@ -22,8 +22,8 @@ describe("the test helper", function(){
|
|||
it("gives me 3 jquery instances of chrome, outer and inner", function(done){
|
||||
this.timeout(5000);
|
||||
|
||||
helper.newPad(function(){
|
||||
//check if the jquery selectors have the desired elements
|
||||
helper.newPad(function(){
|
||||
//check if the jquery selectors have the desired elements
|
||||
expect(helper.padChrome$("#editbar").length).to.be(1);
|
||||
expect(helper.padOuter$("#outerdocbody").length).to.be(1);
|
||||
expect(helper.padInner$("#innerdocbody").length).to.be(1);
|
||||
|
@ -39,20 +39,20 @@ describe("the test helper", function(){
|
|||
});
|
||||
|
||||
describe("the waitFor method", function(){
|
||||
it("takes a timeout and waits long enough", function(done){
|
||||
this.timeout(2000);
|
||||
it("takes a timeout and waits long enough", function(done){
|
||||
this.timeout(2000);
|
||||
var startTime = new Date().getTime();
|
||||
|
||||
helper.waitFor(function(){
|
||||
return false;
|
||||
}, 1500).fail(function(){
|
||||
var duration = new Date().getTime() - startTime;
|
||||
var duration = new Date().getTime() - startTime;
|
||||
expect(duration).to.be.greaterThan(1400);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("takes an interval and checks on every interval", function(done){
|
||||
it("takes an interval and checks on every interval", function(done){
|
||||
this.timeout(4000);
|
||||
var checks = 0;
|
||||
|
||||
|
@ -64,36 +64,36 @@ describe("the test helper", function(){
|
|||
expect(checks).to.be.lessThan(30);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("returns a deferred object", function(){
|
||||
describe("returns a deferred object", function(){
|
||||
it("it calls done after success", function(done){
|
||||
helper.waitFor(function(){
|
||||
return true;
|
||||
}).done(function(){
|
||||
return true;
|
||||
}).done(function(){
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("calls fail after failure", function(done){
|
||||
helper.waitFor(function(){
|
||||
return false;
|
||||
},0).fail(function(){
|
||||
helper.waitFor(function(){
|
||||
return false;
|
||||
},0).fail(function(){
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
xit("throws if you don't listen for fails", function(done){
|
||||
var onerror = window.onerror;
|
||||
window.onerror = function(){
|
||||
var onerror = window.onerror;
|
||||
window.onerror = function(){
|
||||
window.onerror = onerror;
|
||||
done();
|
||||
}
|
||||
done();
|
||||
}
|
||||
|
||||
helper.waitFor(function(){
|
||||
return false;
|
||||
},100);
|
||||
helper.waitFor(function(){
|
||||
return false;
|
||||
},100);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -158,8 +158,8 @@ describe("indentation button", function(){
|
|||
testHelper.selectText(firstTextElement[0], $inner);
|
||||
|
||||
/* this test creates the below content, both should have double indentation
|
||||
line1
|
||||
line2
|
||||
line1
|
||||
line2
|
||||
|
||||
|
||||
firstTextElement.sendkeys('{rightarrow}'); // simulate a keypress of enter
|
||||
|
|
|
@ -3,7 +3,7 @@ var wd = require(srcFolder + "wd");
|
|||
var async = require(srcFolder + "async");
|
||||
|
||||
var config = {
|
||||
host: "ondemand.saucelabs.com"
|
||||
host: "ondemand.saucelabs.com"
|
||||
, port: 80
|
||||
, username: process.env.SAUCE_USER
|
||||
, accessKey: process.env.SAUCE_ACCESS_KEY
|
||||
|
|
Loading…
Reference in a new issue