Replace tabs indentation with spaces indentation

Some files are obviously external libraries, I didn't touch them
This commit is contained in:
Luc Didry 2013-12-05 08:41:29 +01:00
parent 03ff5563f4
commit 3d8452b143
35 changed files with 511 additions and 510 deletions

View file

@ -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 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 xvfb
#sudo apt-get install xfonts-100dpi xfonts-75dpi xfonts-scalable xfonts-cyrillic #sudo apt-get install xfonts-100dpi xfonts-75dpi xfonts-scalable xfonts-cyrillic
Launched two instances of Xvfb directly from the terminal: Launched two instances of Xvfb directly from the terminal:
#Xvfb :0 -ac #Xvfb :0 -ac
#Xvfb :1 -ac #Xvfb :1 -ac
I installed PhantomJS following this guide: http://code.google.com/p/phantomjs/wiki/Installation I installed PhantomJS following this guide: http://code.google.com/p/phantomjs/wiki/Installation
#sudo add-apt-repository ppa:jerome-etienne/neoip #sudo add-apt-repository ppa:jerome-etienne/neoip
#sudo apt-get update #sudo apt-get update
#sudo apt-get install phantomjs #sudo apt-get install phantomjs
I created a small JavaScript file for PhatomJS to use to control the browser instances: I created a small JavaScript file for PhatomJS to use to control the browser instances:

View file

@ -3,7 +3,7 @@
# connect 500 instances to display :0 # connect 500 instances to display :0
for i in {1..500} for i in {1..500}
do do
echo $i echo $i
echo "Displaying Some shit" 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 DISPLAY=:0 screen -d -m /home/phantomjs/bin/phantomjs loader.js http://10.0.0.55:9001/p/pad2 && sleep 2
done done

View file

@ -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. 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. "*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 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 If +, text is inserted having this attribute. If =, text is kept but with
the attribute applied as an attribute addition or removal. the attribute applied as an attribute addition or removal.
Consecutive attributes must be sorted lexically by (key,value) with key Consecutive attributes must be sorted lexically by (key,value) with key
and value taken as strings. It's illegal to have duplicate keys 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 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 have an empty value for a key in the case of an insertion (+), the
pair should just be omitted. pair should just be omitted.
Characters from the source text that aren't accounted for are assumed to be kept Characters from the source text that aren't accounted for are assumed to be kept
with the same attributes. with the same attributes.

View file

@ -110,40 +110,41 @@
// https://github.com/nomiddlename/log4js-node // https://github.com/nomiddlename/log4js-node
// You can add as many appenders as you want here: // You can add as many appenders as you want here:
"logconfig" : "logconfig" :
{ "appenders": [ { "appenders": [
{ "type": "console" { "type": "console"
//, "category": "access"// only logs pad access //, "category": "access"// only logs pad access
} }
/* /*
, { "type": "file" , { "type": "file"
, "filename": "your-log-file-here.log" , "filename": "your-log-file-here.log"
, "maxLogSize": 1024 , "maxLogSize": 1024
, "backups": 3 // how many log files there're gonna be at max , "backups": 3 // how many log files there're gonna be at max
//, "category": "test" // only log a specific category //, "category": "test" // only log a specific category
}*/ }*/
/* /*
, { "type": "logLevelFilter" , { "type": "logLevelFilter"
, "level": "warn" // filters out all log messages that have a lower level than "error" , "level": "warn" // filters out all log messages that have a lower level than "error"
, "appender": , "appender":
{ Use whatever appender you want here } { Use whatever appender you want here }
}*/ }*/
/* /*
, { "type": "logLevelFilter" , { "type": "logLevelFilter"
, "level": "error" // filters out all log messages that have a lower level than "error" , "level": "error" // filters out all log messages that have a lower level than "error"
, "appender": , "appender":
{ "type": "smtp" { "type": "smtp"
, "subject": "An error occured in your EPL instance!" , "subject": "An error occured in your EPL instance!"
, "recipients": "bar@blurdybloop.com, baz@blurdybloop.com" , "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 , "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 , "transport": "SMTP", "SMTP": { // see https://github.com/andris9/Nodemailer#possible-transport-methods
"host": "smtp.example.com", "port": 465, "host": "smtp.example.com", "port": 465,
"secureConnection": true, "secureConnection": true,
"auth": { "auth": {
"user": "foo@example.com", "user": "foo@example.com",
"pass": "bar_foo" "pass": "bar_foo"
}
} }
} }
} }*/
}*/ ]
] } }
} }

View file

@ -554,7 +554,7 @@ exports.deletePad = function(padID, callback)
/** /**
copyPad(sourceID, destinationID[, force=false]) copies a pad. If force is true, 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: 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, 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: Example returns:

View file

@ -635,7 +635,7 @@ Pad.prototype.remove = function remove(callback) {
authorIDs.forEach(function (authorID) authorIDs.forEach(function (authorID)
{ {
authorManager.removePad(authorID, padID); authorManager.removePad(authorID, padID);
}); });
callback(); callback();

View file

@ -151,16 +151,16 @@ exports.checkAccess = function (padID, sessionCookie, token, password, callback)
if(sessionInfo.groupID != groupID) if(sessionInfo.groupID != groupID)
{ {
authLogger.debug("Auth failed: wrong group"); authLogger.debug("Auth failed: wrong group");
callback(); callback();
return; return;
} }
//is validUntil still ok? //is validUntil still ok?
if(sessionInfo.validUntil <= now) if(sessionInfo.validUntil <= now)
{ {
authLogger.debug("Auth failed: validUntil"); authLogger.debug("Auth failed: validUntil");
callback(); callback();
return; return;
} }
// There is a valid session // There is a valid session

View file

@ -1564,7 +1564,7 @@ exports.padUsers = function (padID, callback) {
author.id = s.author; author.id = s.author;
result.push(author); result.push(author);
callback(); callback();
}); });
} }
}, function(err) { }, function(err) {

View file

@ -57,11 +57,11 @@ exports.setSocketIO = function(_socket) {
socket.sockets.on('connection', function(client) socket.sockets.on('connection', function(client)
{ {
if(settings.trustProxy && client.handshake.headers['x-forwarded-for'] !== undefined){ if(settings.trustProxy && client.handshake.headers['x-forwarded-for'] !== undefined){
client.set('remoteAddress', client.handshake.headers['x-forwarded-for']); client.set('remoteAddress', client.handshake.headers['x-forwarded-for']);
} }
else{ else{
client.set('remoteAddress', client.handshake.address.address); client.set('remoteAddress', client.handshake.address.address);
} }
var clientAuthorized = false; var clientAuthorized = false;
//wrap the original send function to log the messages //wrap the original send function to log the messages

View file

@ -16,49 +16,49 @@ exports.expressCreateServer = function (hook_name, args, cb) {
//translate the read only pad to a padId //translate the read only pad to a padId
function(callback) function(callback)
{ {
readOnlyManager.getPadId(req.params.id, function(err, _padId) readOnlyManager.getPadId(req.params.id, function(err, _padId)
{ {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
padId = _padId; padId = _padId;
//we need that to tell hasPadAcess about the pad //we need that to tell hasPadAcess about the pad
req.params.pad = padId; req.params.pad = padId;
callback(); callback();
}); });
}, },
//render the html document //render the html document
function(callback) function(callback)
{ {
//return if the there is no padId //return if the there is no padId
if(padId == null) if(padId == null)
{ {
callback("notfound"); callback("notfound");
return; return;
} }
hasPadAccess(req, res, function() hasPadAccess(req, res, function()
{ {
//render the html document //render the html document
exporthtml.getPadHTMLDocument(padId, null, false, function(err, _html) exporthtml.getPadHTMLDocument(padId, null, false, function(err, _html)
{ {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
html = _html; html = _html;
callback(); callback();
}); });
}); });
} }
], function(err) ], function(err)
{ {
//throw any unexpected error //throw any unexpected error
if(err && err != "notfound") if(err && err != "notfound")
ERR(err); ERR(err);
if(err == "notfound") if(err == "notfound")
res.send(404, '404 - Not Found'); res.send(404, '404 - Not Found');
else else
res.send(html); res.send(html);
}); });
}); });

View file

@ -12,20 +12,20 @@ exports.expressCreateServer = function (hook_name, args, cb) {
else else
{ {
padManager.sanitizePadId(padId, function(sanitizedPadId) { padManager.sanitizePadId(padId, function(sanitizedPadId) {
//the pad id was sanitized, so we redirect to the sanitized version //the pad id was sanitized, so we redirect to the sanitized version
if(sanitizedPadId != padId) if(sanitizedPadId != padId)
{ {
var real_url = sanitizedPadId; var real_url = sanitizedPadId;
var query = url.parse(req.url).query; var query = url.parse(req.url).query;
if ( query ) real_url += '?' + query; if ( query ) real_url += '?' + query;
res.header('Location', real_url); res.header('Location', real_url);
res.send(302, 'You should be redirected to <a href="' + real_url + '">' + real_url + '</a>'); res.send(302, 'You should be redirected to <a href="' + real_url + '">' + real_url + '</a>');
} }
//the pad id was fine, so just render it //the pad id was fine, so just render it
else else
{ {
next(); next();
} }
}); });
} }
}); });

View file

@ -49,8 +49,8 @@ exports.expressCreateServer = function (hook_name, args, cb) {
//there is no custom favicon, send the default favicon //there is no custom favicon, send the default favicon
if(err) if(err)
{ {
filePath = path.normalize(__dirname + "/../../../static/favicon.ico"); filePath = path.normalize(__dirname + "/../../../static/favicon.ico");
res.sendfile(filePath); res.sendfile(filePath);
} }
}); });
}); });

View file

@ -455,14 +455,14 @@ function getHTMLFromAtext(pad, atext, authorColors)
attribLine: attribLines[i], attribLine: attribLines[i],
text: textLines[i] text: textLines[i]
}, " ", " ", ""); }, " ", " ", "");
if (lineContentFromHook) if (lineContentFromHook)
{ {
pieces.push(lineContentFromHook, ''); pieces.push(lineContentFromHook, '');
} }
else else
{ {
pieces.push(lineContent, '<br>'); pieces.push(lineContent, '<br>');
} }
} }
} }
@ -490,7 +490,7 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback)
var head = var head =
(noDocType ? '' : '<!doctype html>\n') + (noDocType ? '' : '<!doctype html>\n') +
'<html lang="en">\n' + (noDocType ? '' : '<head>\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' + '<meta charset="utf-8">\n' +
'<style> * { font-family: arial, sans-serif;\n' + '<style> * { font-family: arial, sans-serif;\n' +
'font-size: 13px;\n' + 'font-size: 13px;\n' +

View file

@ -673,9 +673,9 @@ exports.textLinesMutator = function (lines) {
} }
//print(inSplice+" / "+isCurLineInSplice()+" / "+curSplice[0]+" / "+curSplice[1]+" / "+lines.length); //print(inSplice+" / "+isCurLineInSplice()+" / "+curSplice[0]+" / "+curSplice[1]+" / "+lines.length);
/*if (inSplice && (! isCurLineInSplice()) && (curSplice[0] + curSplice[1] < lines.length)) { /*if (inSplice && (! isCurLineInSplice()) && (curSplice[0] + curSplice[1] < lines.length)) {
print("BLAH"); print("BLAH");
putCurLineInSplice(); putCurLineInSplice();
}*/ }*/
// tests case foo in remove(), which isn't otherwise covered in current impl // tests case foo in remove(), which isn't otherwise covered in current impl
} }
//debugPrint("skip"); //debugPrint("skip");

View file

@ -4187,7 +4187,7 @@ function Ace2Inner(){
selection.startPoint.index+" / "+ selection.startPoint.index+" / "+
selection.endPoint.node.uniqueId()+","+ selection.endPoint.node.uniqueId()+","+
selection.endPoint.index); selection.endPoint.index);
}*/ }*/
} }
return selection; return selection;
} }
@ -5255,7 +5255,7 @@ function Ace2Inner(){
if(h){ // apply style to div if(h){ // apply style to div
div.style.height = h +"px"; div.style.height = h +"px";
} }
div.appendChild(odoc.createTextNode(String(n))); div.appendChild(odoc.createTextNode(String(n)));
fragment.appendChild(div); fragment.appendChild(div);

View file

@ -461,10 +461,10 @@ function loadBroadcastSliderJS(fireWhenAllScriptsAreLoaded)
var startPos = clientVars.collab_client_vars.rev; var startPos = clientVars.collab_client_vars.rev;
if(window.location.hash.length > 1) if(window.location.hash.length > 1)
{ {
var hashRev = Number(window.location.hash.substr(1)); var hashRev = Number(window.location.hash.substr(1));
if(!isNaN(hashRev)) if(!isNaN(hashRev))
{ {
// this is necessary because of the socket.io-event which loads the changesets // this is necessary because of the socket.io-event which loads the changesets
setTimeout(function() { setSliderPosition(hashRev); }, 1); setTimeout(function() { setSliderPosition(hashRev); }, 1);
} }
} }

View file

@ -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); $("#chatcounter").text(0);
$("#chatloadmessagesbutton").click(function() $("#chatloadmessagesbutton").click(function()
{ {
var start = Math.max(self.historyPointer - 20, 0); var start = Math.max(self.historyPointer - 20, 0);
var end = self.historyPointer; var end = self.historyPointer;
@ -200,7 +200,7 @@ var chat = (function()
pad.collabClient.sendMessage({"type": "GET_CHAT_MESSAGES", "start": start, "end": end}); pad.collabClient.sendMessage({"type": "GET_CHAT_MESSAGES", "start": start, "end": end});
self.historyPointer = start; self.historyPointer = start;
}); });
} }
} }

View file

@ -398,7 +398,7 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class
if (endPoint && node == endPoint.node) if (endPoint && node == endPoint.node)
{ {
selEnd = _pointHere(0, state); selEnd = _pointHere(0, state);
} }
} }
while (txt.length > 0) while (txt.length > 0)
{ {

View file

@ -235,9 +235,9 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument)
} }
if (lineClass !== null) result.node.className = lineClass; if (lineClass !== null) result.node.className = lineClass;
hooks.callAll("acePostWriteDomLineHTML", { hooks.callAll("acePostWriteDomLineHTML", {
node: result.node node: result.node
}); });
} }
result.prepareForAdd = writeHTML; result.prepareForAdd = writeHTML;
result.finishUpdate = writeHTML; result.finishUpdate = writeHTML;

View file

@ -97,21 +97,21 @@ window.html10n = (function(window, document, undefined) {
* MicroEvent - to make any js object an event emitter (server or browser) * MicroEvent - to make any js object an event emitter (server or browser)
*/ */
var MicroEvent = function(){} var MicroEvent = function(){}
MicroEvent.prototype = { MicroEvent.prototype = {
bind : function(event, fct){ bind: function(event, fct){
this._events = this._events || {}; this._events = this._events || {};
this._events[event] = this._events[event] || []; this._events[event] = this._events[event] || [];
this._events[event].push(fct); this._events[event].push(fct);
}, },
unbind : function(event, fct){ unbind: function(event, fct){
this._events = this._events || {}; 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); this._events[event].splice(this._events[event].indexOf(fct), 1);
}, },
trigger : function(event /* , args... */){ trigger: function(event /* , args... */){
this._events = this._events || {}; 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++){ for(var i = 0; i < this._events[event].length; i++){
this._events[event][i].apply(this, Array.prototype.slice.call(arguments, 1)) 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 * mixin will delegate all MicroEvent.js function in the destination object
* @param {Object} the object which will support MicroEvent * @param {Object} the object which will support MicroEvent
*/ */
MicroEvent.mixin = function(destObject){ MicroEvent.mixin = function(destObject){
var props = ['bind', 'unbind', 'trigger']; var props = ['bind', 'unbind', 'trigger'];
if(!destObject) return; if(!destObject) return;
for(var i = 0; i < props.length; i ++){ for(var i = 0; i < props.length; i ++){
destObject[props[i]] = MicroEvent.prototype[props[i]]; destObject[props[i]] = MicroEvent.prototype[props[i]];

View file

@ -140,7 +140,7 @@ var padeditbar = (function()
} }
else if (cmd == 'import_export') else if (cmd == 'import_export')
{ {
self.toggleDropDown("importexport"); self.toggleDropDown("importexport");
} }
else if (cmd == 'savedRevision') else if (cmd == 'savedRevision')
{ {

View file

@ -61,7 +61,7 @@ exports.flatten = function (lst) {
if (lst[i] != undefined && lst[i] != null) { if (lst[i] != undefined && lst[i] != null) {
for (var j = 0; j < lst[i].length; j++) { for (var j = 0; j < lst[i].length; j++) {
res.push(lst[i][j]); res.push(lst[i][j]);
} }
} }
} }
} }

View file

@ -5,29 +5,29 @@
<html> <html>
<title><%=settings.title%></title> <title><%=settings.title%></title>
<script> <script>
/* /*
|@licstart The following is the entire license notice for the |@licstart The following is the entire license notice for the
JavaScript code in this page.| 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"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at 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 Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
|@licend The above is the entire license notice |@licend The above is the entire license notice
for the JavaScript code in this page.| for the JavaScript code in this page.|
*/ */
</script> </script>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">

View file

@ -27,278 +27,278 @@
(function($){ (function($){
bililiteRange = function(el, debug){ bililiteRange = function(el, debug){
var ret; var ret;
if (debug){ if (debug){
ret = new NothingRange(); // Easier to force it to use the no-selection type than to try to find an old browser 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){ }else if (document.selection && !document.addEventListener){
// Internet Explorer 8 and lower // Internet Explorer 8 and lower
ret = new IERange(); ret = new IERange();
}else if (window.getSelection && el.setSelectionRange){ }else if (window.getSelection && el.setSelectionRange){
// Standards. Element is an input or textarea // Standards. Element is an input or textarea
ret = new InputRange(); ret = new InputRange();
}else if (window.getSelection){ }else if (window.getSelection){
// Standards, with any other kind of element // Standards, with any other kind of element
ret = new W3CRange() ret = new W3CRange()
}else{ }else{
// doesn't support selection // doesn't support selection
ret = new NothingRange(); ret = new NothingRange();
} }
ret._el = el; ret._el = el;
ret._doc = el.ownerDocument; ret._doc = el.ownerDocument;
ret._win = 'defaultView' in ret._doc ? ret._doc.defaultView : ret._doc.parentWindow; ret._win = 'defaultView' in ret._doc ? ret._doc.defaultView : ret._doc.parentWindow;
ret._textProp = textProp(el); ret._textProp = textProp(el);
ret._bounds = [0, ret.length()]; ret._bounds = [0, ret.length()];
return ret; return ret;
} }
function textProp(el){ function textProp(el){
// returns the property that contains the text of the element // returns the property that contains the text of the element
if (typeof el.value != 'undefined') return 'value'; if (typeof el.value != 'undefined') return 'value';
if (typeof el.text != 'undefined') return 'text'; if (typeof el.text != 'undefined') return 'text';
if (typeof el.textContent != 'undefined') return 'textContent'; if (typeof el.textContent != 'undefined') return 'textContent';
return 'innerText'; return 'innerText';
} }
// base class // base class
function Range(){} function Range(){}
Range.prototype = { Range.prototype = {
length: function() { length: function() {
return this._el[this._textProp].replace(/\r/g, '').length; // need to correct for IE's CrLf weirdness return this._el[this._textProp].replace(/\r/g, '').length; // need to correct for IE's CrLf weirdness
}, },
bounds: function(s){ bounds: function(s){
if (s === 'all'){ if (s === 'all'){
this._bounds = [0, this.length()]; this._bounds = [0, this.length()];
}else if (s === 'start'){ }else if (s === 'start'){
this._bounds = [0, 0]; this._bounds = [0, 0];
}else if (s === 'end'){ }else if (s === 'end'){
this._bounds = [this.length(), this.length()]; this._bounds = [this.length(), this.length()];
}else if (s === 'selection'){ }else if (s === 'selection'){
this.bounds ('all'); // first select the whole thing for constraining this.bounds ('all'); // first select the whole thing for constraining
this._bounds = this._nativeSelection(); this._bounds = this._nativeSelection();
}else if (s){ }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. this._bounds = s; // don't error check now; the element may change at any moment, so constrain it when we need it.
}else{ }else{
var b = [ var b = [
Math.max(0, Math.min (this.length(), this._bounds[0])), Math.max(0, Math.min (this.length(), this._bounds[0])),
Math.max(0, Math.min (this.length(), this._bounds[1])) Math.max(0, Math.min (this.length(), this._bounds[1]))
]; ];
return b; // need to constrain it to fit return b; // need to constrain it to fit
} }
return this; // allow for chaining return this; // allow for chaining
}, },
select: function(){ select: function(){
this._nativeSelect(this._nativeRange(this.bounds())); this._nativeSelect(this._nativeRange(this.bounds()));
return this; // allow for chaining return this; // allow for chaining
}, },
text: function(text, select){ text: function(text, select){
if (arguments.length){ if (arguments.length){
this._nativeSetText(text, this._nativeRange(this.bounds())); this._nativeSetText(text, this._nativeRange(this.bounds()));
if (select == 'start'){ if (select == 'start'){
this.bounds ([this._bounds[0], this._bounds[0]]); this.bounds ([this._bounds[0], this._bounds[0]]);
this.select(); this.select();
}else if (select == 'end'){ }else if (select == 'end'){
this.bounds ([this._bounds[0]+text.length, this._bounds[0]+text.length]); this.bounds ([this._bounds[0]+text.length, this._bounds[0]+text.length]);
this.select(); this.select();
}else if (select == 'all'){ }else if (select == 'all'){
this.bounds ([this._bounds[0], this._bounds[0]+text.length]); this.bounds ([this._bounds[0], this._bounds[0]+text.length]);
this.select(); this.select();
} }
return this; // allow for chaining return this; // allow for chaining
}else{ }else{
return this._nativeGetText(this._nativeRange(this.bounds())); return this._nativeGetText(this._nativeRange(this.bounds()));
} }
}, },
insertEOL: function (){ insertEOL: function (){
this._nativeEOL(); this._nativeEOL();
this._bounds = [this._bounds[0]+1, this._bounds[0]+1]; // move past the EOL marker this._bounds = [this._bounds[0]+1, this._bounds[0]+1]; // move past the EOL marker
return this; return this;
} }
}; };
function IERange(){} function IERange(){}
IERange.prototype = new Range(); IERange.prototype = new Range();
IERange.prototype._nativeRange = function (bounds){ IERange.prototype._nativeRange = function (bounds){
var rng; var rng;
if (this._el.tagName == 'INPUT'){ if (this._el.tagName == 'INPUT'){
// IE 8 is very inconsistent; textareas have createTextRange but it doesn't work // IE 8 is very inconsistent; textareas have createTextRange but it doesn't work
rng = this._el.createTextRange(); rng = this._el.createTextRange();
}else{ }else{
rng = this._doc.body.createTextRange (); rng = this._doc.body.createTextRange ();
rng.moveToElementText(this._el); rng.moveToElementText(this._el);
} }
if (bounds){ if (bounds){
if (bounds[1] < 0) bounds[1] = 0; // IE tends to run elements out of 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[0] > this.length()) bounds[0] = this.length();
if (bounds[1] < rng.text.replace(/\r/g, '').length){ // correct for IE's CrLf wierdness 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 // 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', -1);
rng.moveEnd ('character', bounds[1]-rng.text.replace(/\r/g, '').length); rng.moveEnd ('character', bounds[1]-rng.text.replace(/\r/g, '').length);
} }
if (bounds[0] > 0) rng.moveStart('character', bounds[0]); if (bounds[0] > 0) rng.moveStart('character', bounds[0]);
} }
return rng; return rng;
}; };
IERange.prototype._nativeSelect = function (rng){ IERange.prototype._nativeSelect = function (rng){
rng.select(); rng.select();
}; };
IERange.prototype._nativeSelection = function (){ IERange.prototype._nativeSelection = function (){
// returns [start, end] for the selection constrained to be in element // returns [start, end] for the selection constrained to be in element
var rng = this._nativeRange(); // range of the element to constrain to var rng = this._nativeRange(); // range of the element to constrain to
var len = this.length(); var len = this.length();
if (this._doc.selection.type != 'Text') return [0,0]; // append to the end if (this._doc.selection.type != 'Text') return [0,0]; // append to the end
var sel = this._doc.selection.createRange(); var sel = this._doc.selection.createRange();
try{ try{
return [ return [
iestart(sel, rng), iestart(sel, rng),
ieend (sel, rng) ieend (sel, rng)
]; ];
}catch (e){ }catch (e){
// IE gets upset sometimes about comparing text to input elements, but the selections cannot overlap, so make a best guess // 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]; return (sel.parentElement().sourceIndex < this._el.sourceIndex) ? [0,0] : [len, len];
} }
}; };
IERange.prototype._nativeGetText = function (rng){ 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){ IERange.prototype._nativeSetText = function (text, rng){
rng.text = text; rng.text = text;
}; };
IERange.prototype._nativeEOL = function(){ IERange.prototype._nativeEOL = function(){
if (typeof this._el.value != 'undefined'){ if (typeof this._el.value != 'undefined'){
this.text('\n'); // for input and textarea, insert it straight this.text('\n'); // for input and textarea, insert it straight
}else{ }else{
this._nativeRange(this.bounds()).pasteHTML('<br/>'); this._nativeRange(this.bounds()).pasteHTML('<br/>');
} }
}; };
// IE internals // IE internals
function iestart(rng, constraint){ 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 // 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 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 ('StartToStart', constraint) <= 0) return 0; // at or before the beginning
if (rng.compareEndPoints ('StartToEnd', constraint) >= 0) return len; if (rng.compareEndPoints ('StartToEnd', constraint) >= 0) return len;
for (var i = 0; rng.compareEndPoints ('StartToStart', constraint) > 0; ++i, rng.moveStart('character', -1)); for (var i = 0; rng.compareEndPoints ('StartToStart', constraint) > 0; ++i, rng.moveStart('character', -1));
return i; return i;
} }
function ieend (rng, constraint){ 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 // 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 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 ('EndToEnd', constraint) >= 0) return len; // at or after the end
if (rng.compareEndPoints ('EndToStart', constraint) <= 0) return 0; if (rng.compareEndPoints ('EndToStart', constraint) <= 0) return 0;
for (var i = 0; rng.compareEndPoints ('EndToStart', constraint) > 0; ++i, rng.moveEnd('character', -1)); for (var i = 0; rng.compareEndPoints ('EndToStart', constraint) > 0; ++i, rng.moveEnd('character', -1));
return i; return i;
} }
// an input element in a standards document. "Native Range" is just the bounds array // an input element in a standards document. "Native Range" is just the bounds array
function InputRange(){} function InputRange(){}
InputRange.prototype = new Range(); InputRange.prototype = new Range();
InputRange.prototype._nativeRange = function(bounds) { InputRange.prototype._nativeRange = function(bounds) {
return bounds || [0, this.length()]; return bounds || [0, this.length()];
}; };
InputRange.prototype._nativeSelect = function (rng){ InputRange.prototype._nativeSelect = function (rng){
this._el.setSelectionRange(rng[0], rng[1]); this._el.setSelectionRange(rng[0], rng[1]);
}; };
InputRange.prototype._nativeSelection = function(){ InputRange.prototype._nativeSelection = function(){
return [this._el.selectionStart, this._el.selectionEnd]; return [this._el.selectionStart, this._el.selectionEnd];
}; };
InputRange.prototype._nativeGetText = function(rng){ 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){ InputRange.prototype._nativeSetText = function(text, rng){
var val = this._el.value; var val = this._el.value;
this._el.value = val.substring(0, rng[0]) + text + val.substring(rng[1]); this._el.value = val.substring(0, rng[0]) + text + val.substring(rng[1]);
}; };
InputRange.prototype._nativeEOL = function(){ InputRange.prototype._nativeEOL = function(){
this.text('\n'); this.text('\n');
}; };
function W3CRange(){} function W3CRange(){}
W3CRange.prototype = new Range(); W3CRange.prototype = new Range();
W3CRange.prototype._nativeRange = function (bounds){ W3CRange.prototype._nativeRange = function (bounds){
var rng = this._doc.createRange(); var rng = this._doc.createRange();
rng.selectNodeContents(this._el); rng.selectNodeContents(this._el);
if (bounds){ if (bounds){
w3cmoveBoundary (rng, bounds[0], true, this._el); w3cmoveBoundary (rng, bounds[0], true, this._el);
rng.collapse (true); rng.collapse (true);
w3cmoveBoundary (rng, bounds[1]-bounds[0], false, this._el); w3cmoveBoundary (rng, bounds[1]-bounds[0], false, this._el);
} }
return rng; return rng;
}; };
W3CRange.prototype._nativeSelect = function (rng){ W3CRange.prototype._nativeSelect = function (rng){
this._win.getSelection().removeAllRanges(); this._win.getSelection().removeAllRanges();
this._win.getSelection().addRange (rng); this._win.getSelection().addRange (rng);
}; };
W3CRange.prototype._nativeSelection = function (){ W3CRange.prototype._nativeSelection = function (){
// returns [start, end] for the selection constrained to be in element // returns [start, end] for the selection constrained to be in element
var rng = this._nativeRange(); // range of the element to constrain to 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 if (this._win.getSelection().rangeCount == 0) return [this.length(), this.length()]; // append to the end
var sel = this._win.getSelection().getRangeAt(0); var sel = this._win.getSelection().getRangeAt(0);
return [ return [
w3cstart(sel, rng), w3cstart(sel, rng),
w3cend (sel, rng) w3cend (sel, rng)
]; ];
} }
W3CRange.prototype._nativeGetText = function (rng){ W3CRange.prototype._nativeGetText = function (rng){
return rng.toString(); return rng.toString();
}; };
W3CRange.prototype._nativeSetText = function (text, rng){ W3CRange.prototype._nativeSetText = function (text, rng){
rng.deleteContents(); rng.deleteContents();
rng.insertNode (this._doc.createTextNode(text)); rng.insertNode (this._doc.createTextNode(text));
this._el.normalize(); // merge the text with the surrounding text this._el.normalize(); // merge the text with the surrounding text
}; };
W3CRange.prototype._nativeEOL = function(){ W3CRange.prototype._nativeEOL = function(){
var rng = this._nativeRange(this.bounds()); var rng = this._nativeRange(this.bounds());
rng.deleteContents(); rng.deleteContents();
var br = this._doc.createElement('br'); var br = this._doc.createElement('br');
br.setAttribute ('_moz_dirty', ''); // for Firefox br.setAttribute ('_moz_dirty', ''); // for Firefox
rng.insertNode (br); rng.insertNode (br);
rng.insertNode (this._doc.createTextNode('\n')); rng.insertNode (this._doc.createTextNode('\n'));
rng.collapse (false); rng.collapse (false);
}; };
// W3C internals // W3C internals
function nextnode (node, root){ function nextnode (node, root){
// in-order traversal // in-order traversal
// we've already visited node, so get kids then siblings // we've already visited node, so get kids then siblings
if (node.firstChild) return node.firstChild; if (node.firstChild) return node.firstChild;
if (node.nextSibling) return node.nextSibling; if (node.nextSibling) return node.nextSibling;
if (node===root) return null; if (node===root) return null;
while (node.parentNode){ while (node.parentNode){
// get uncles // get uncles
node = node.parentNode; node = node.parentNode;
if (node == root) return null; if (node == root) return null;
if (node.nextSibling) return node.nextSibling; if (node.nextSibling) return node.nextSibling;
} }
return null; return null;
} }
function w3cmoveBoundary (rng, n, bStart, el){ 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! // 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 the start is moved after the end, then an exception is raised
if (n <= 0) return; if (n <= 0) return;
var node = rng[bStart ? 'startContainer' : 'endContainer']; var node = rng[bStart ? 'startContainer' : 'endContainer'];
if (node.nodeType == 3){ if (node.nodeType == 3){
// we may be starting somewhere into the text // we may be starting somewhere into the text
n += rng[bStart ? 'startOffset' : 'endOffset']; n += rng[bStart ? 'startOffset' : 'endOffset'];
} }
while (node){ while (node){
if (node.nodeType == 3){ if (node.nodeType == 3){
if (n <= node.nodeValue.length){ if (n <= node.nodeValue.length){
rng[bStart ? 'setStart' : 'setEnd'](node, n); rng[bStart ? 'setStart' : 'setEnd'](node, n);
// special case: if we end next to a <br>, include that node. // special case: if we end next to a <br>, include that node.
if (n == node.nodeValue.length){ if (n == node.nodeValue.length){
// skip past zero-length text nodes // skip past zero-length text nodes
for (var next = nextnode (node, el); next && next.nodeType==3 && next.nodeValue.length == 0; next = nextnode(next, el)){ for (var next = nextnode (node, el); next && next.nodeType==3 && next.nodeValue.length == 0; next = nextnode(next, el)){
rng[bStart ? 'setStartAfter' : 'setEndAfter'](next); rng[bStart ? 'setStartAfter' : 'setEndAfter'](next);
} }
if (next && next.nodeType == 1 && next.nodeName == "BR") rng[bStart ? 'setStartAfter' : 'setEndAfter'](next); if (next && next.nodeType == 1 && next.nodeName == "BR") rng[bStart ? 'setStartAfter' : 'setEndAfter'](next);
} }
return; return;
}else{ }else{
rng[bStart ? 'setStartAfter' : 'setEndAfter'](node); // skip past this one rng[bStart ? 'setStartAfter' : 'setEndAfter'](node); // skip past this one
n -= node.nodeValue.length; // and eat these characters n -= node.nodeValue.length; // and eat these characters
} }
} }
node = nextnode (node, el); node = nextnode (node, el);
} }
} }
var START_TO_START = 0; // from the w3c definitions var START_TO_START = 0; // from the w3c definitions
var START_TO_END = 1; 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_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. // * Range.START_TO_START compares the start boundary-point of sourceRange to the start boundary-point of range.
function w3cstart(rng, constraint){ function w3cstart(rng, constraint){
if (rng.compareBoundaryPoints (START_TO_START, constraint) <= 0) return 0; // at or before the beginning 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; if (rng.compareBoundaryPoints (END_TO_START, constraint) >= 0) return constraint.toString().length;
rng = rng.cloneRange(); // don't change the original rng = rng.cloneRange(); // don't change the original
rng.setEnd (constraint.endContainer, constraint.endOffset); // they now end at the same place rng.setEnd (constraint.endContainer, constraint.endOffset); // they now end at the same place
return constraint.toString().length - rng.toString().length; return constraint.toString().length - rng.toString().length;
} }
function w3cend (rng, constraint){ function w3cend (rng, constraint){
if (rng.compareBoundaryPoints (END_TO_END, constraint) >= 0) return constraint.toString().length; // at or after the end 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; if (rng.compareBoundaryPoints (START_TO_END, constraint) <= 0) return 0;
rng = rng.cloneRange(); // don't change the original rng = rng.cloneRange(); // don't change the original
rng.setStart (constraint.startContainer, constraint.startOffset); // they now start at the same place rng.setStart (constraint.startContainer, constraint.startOffset); // they now start at the same place
return rng.toString().length; return rng.toString().length;
} }
function NothingRange(){} function NothingRange(){}
NothingRange.prototype = new Range(); NothingRange.prototype = new Range();
NothingRange.prototype._nativeRange = function(bounds) { NothingRange.prototype._nativeRange = function(bounds) {
return bounds || [0,this.length()]; return bounds || [0,this.length()];
}; };
NothingRange.prototype._nativeSelect = function (rng){ // do nothing NothingRange.prototype._nativeSelect = function (rng){ // do nothing
}; };
NothingRange.prototype._nativeSelection = function(){ NothingRange.prototype._nativeSelection = function(){
return [0,0]; return [0,0];
}; };
NothingRange.prototype._nativeGetText = function (rng){ 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){ NothingRange.prototype._nativeSetText = function (text, rng){
var val = this._el[this._textProp]; var val = this._el[this._textProp];
this._el[this._textProp] = val.substring(0, rng[0]) + text + val.substring(rng[1]); this._el[this._textProp] = val.substring(0, rng[0]) + text + val.substring(rng[1]);
}; };
NothingRange.prototype._nativeEOL = function(){ NothingRange.prototype._nativeEOL = function(){
this.text('\n'); this.text('\n');
}; };
})(jQuery); })(jQuery);
@ -378,90 +378,90 @@ NothingRange.prototype._nativeEOL = function(){
(function($){ (function($){
$.fn.sendkeys = function (x, opts){ $.fn.sendkeys = function (x, opts){
return this.each( function(){ return this.each( function(){
var localkeys = $.extend({}, opts, $(this).data('sendkeys')); // allow for element-specific key functions 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 // 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'); var rng = $.data (this, 'sendkeys.selection');
if (!rng){ if (!rng){
rng = bililiteRange(this).bounds('selection'); rng = bililiteRange(this).bounds('selection');
$.data(this, 'sendkeys.selection', rng); $.data(this, 'sendkeys.selection', rng);
$(this).bind('mouseup.sendkeys', function(){ $(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 // 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'); $.data(this, 'sendkeys.selection').bounds('selection');
}).bind('keyup.sendkeys', function(evt){ }).bind('keyup.sendkeys', function(evt){
// restore the selection if we got here with a tab (a click should select what was clicked on) // restore the selection if we got here with a tab (a click should select what was clicked on)
if (evt.which == 9){ if (evt.which == 9){
// there's a flash of selection when we restore the focus, but I don't know how to avoid that. // 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(); $.data(this, 'sendkeys.selection').select();
}else{ }else{
$.data(this, 'sendkeys.selection').bounds('selection'); $.data(this, 'sendkeys.selection').bounds('selection');
} }
}); });
} }
this.focus(); this.focus();
if (typeof x === 'undefined') return; // no string, so we just set up the event handlers if (typeof x === 'undefined') return; // no string, so we just set up the event handlers
$.data(this, 'sendkeys.originalText', rng.text()); $.data(this, 'sendkeys.originalText', rng.text());
x.replace(/\n/g, '{enter}'). // turn line feeds into explicit break insertions x.replace(/\n/g, '{enter}'). // turn line feeds into explicit break insertions
replace(/{[^}]*}|[^{]+/g, function(s){ replace(/{[^}]*}|[^{]+/g, function(s){
(localkeys[s] || $.fn.sendkeys.defaults[s] || $.fn.sendkeys.defaults.simplechar)(rng, s); (localkeys[s] || $.fn.sendkeys.defaults[s] || $.fn.sendkeys.defaults.simplechar)(rng, s);
}); });
$(this).trigger({type: 'sendkeys', which: x}); $(this).trigger({type: 'sendkeys', which: x});
}); });
}; // sendkeys }; // sendkeys
// add the functions publicly so they can be overridden // add the functions publicly so they can be overridden
$.fn.sendkeys.defaults = { $.fn.sendkeys.defaults = {
simplechar: function (rng, s){ simplechar: function (rng, s){
rng.text(s, 'end'); rng.text(s, 'end');
for (var i =0; i < s.length; ++i){ for (var i =0; i < s.length; ++i){
var x = s.charCodeAt(i); var x = s.charCodeAt(i);
// a bit of cheating: rng._el is the element associated with rng. // a bit of cheating: rng._el is the element associated with rng.
$(rng._el).trigger({type: 'keypress', keyCode: x, which: x, charCode: x}); $(rng._el).trigger({type: 'keypress', keyCode: x, which: x, charCode: x});
} }
}, },
'{{}': function (rng){ '{{}': function (rng){
$.fn.sendkeys.defaults.simplechar (rng, '{') $.fn.sendkeys.defaults.simplechar (rng, '{')
}, },
'{enter}': function (rng){ '{enter}': function (rng){
rng.insertEOL(); rng.insertEOL();
rng.select(); rng.select();
var x = '\n'.charCodeAt(0); var x = '\n'.charCodeAt(0);
$(rng._el).trigger({type: 'keypress', keyCode: x, which: x, charCode: x}); $(rng._el).trigger({type: 'keypress', keyCode: x, which: x, charCode: x});
}, },
'{backspace}': function (rng){ '{backspace}': function (rng){
var b = rng.bounds(); 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 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 rng.text('', 'end'); // delete the characters and update the selection
}, },
'{del}': function (rng){ '{del}': function (rng){
var b = rng.bounds(); 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 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 rng.text('', 'end'); // delete the characters and update the selection
}, },
'{rightarrow}': function (rng){ '{rightarrow}': function (rng){
var b = rng.bounds(); var b = rng.bounds();
if (b[0] == b[1]) ++b[1]; // no characters selected; it's just an insertion point. Move to the right 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(); rng.bounds([b[1], b[1]]).select();
}, },
'{leftarrow}': function (rng){ '{leftarrow}': function (rng){
var b = rng.bounds(); var b = rng.bounds();
if (b[0] == b[1]) --b[0]; // no characters selected; it's just an insertion point. Move to the left 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(); rng.bounds([b[0], b[0]]).select();
}, },
'{selectall}' : function (rng){ '{selectall}' : function (rng){
rng.bounds('all').select(); rng.bounds('all').select();
}, },
'{selection}': function (rng){ '{selection}': function (rng){
$.fn.sendkeys.defaults.simplechar(rng, $.data(rng._el, 'sendkeys.originalText')); $.fn.sendkeys.defaults.simplechar(rng, $.data(rng._el, 'sendkeys.originalText'));
}, },
'{mark}' : function (rng){ '{mark}' : function (rng){
var bounds = rng.bounds(); var bounds = rng.bounds();
$(rng._el).one('sendkeys', function(){ $(rng._el).one('sendkeys', function(){
// set up the event listener to change the selection after the sendkeys is done // set up the event listener to change the selection after the sendkeys is done
rng.bounds(bounds).select(); rng.bounds(bounds).select();
}); });
} }
}; };
})(jQuery) })(jQuery)

View file

@ -188,13 +188,13 @@ $(function(){
//initalize the test helper //initalize the test helper
helper.init(function(){ helper.init(function(){
//configure and start the test framework //configure and start the test framework
var grep = getURLParameter("grep"); var grep = getURLParameter("grep");
if(grep != "null"){ if(grep != "null"){
mocha.grep(grep); mocha.grep(grep);
} }
mocha.ignoreLeaks(); mocha.ignoreLeaks();
mocha.reporter(WebdriverAndHtmlReporter(mocha._reporter)); mocha.reporter(WebdriverAndHtmlReporter(mocha._reporter));

View file

@ -6,14 +6,14 @@ describe("the test helper", function(){
var times = 10; var times = 10;
var loadPad = function(){ var loadPad = function(){
helper.newPad(function(){ helper.newPad(function(){
times--; times--;
if(times > 0){ if(times > 0){
loadPad(); loadPad();
} else { } else {
done(); done();
} }
}) })
} }
loadPad(); loadPad();
@ -22,8 +22,8 @@ describe("the test helper", function(){
it("gives me 3 jquery instances of chrome, outer and inner", function(done){ it("gives me 3 jquery instances of chrome, outer and inner", function(done){
this.timeout(5000); this.timeout(5000);
helper.newPad(function(){ helper.newPad(function(){
//check if the jquery selectors have the desired elements //check if the jquery selectors have the desired elements
expect(helper.padChrome$("#editbar").length).to.be(1); expect(helper.padChrome$("#editbar").length).to.be(1);
expect(helper.padOuter$("#outerdocbody").length).to.be(1); expect(helper.padOuter$("#outerdocbody").length).to.be(1);
expect(helper.padInner$("#innerdocbody").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(){ describe("the waitFor method", function(){
it("takes a timeout and waits long enough", function(done){ it("takes a timeout and waits long enough", function(done){
this.timeout(2000); this.timeout(2000);
var startTime = new Date().getTime(); var startTime = new Date().getTime();
helper.waitFor(function(){ helper.waitFor(function(){
return false; return false;
}, 1500).fail(function(){ }, 1500).fail(function(){
var duration = new Date().getTime() - startTime; var duration = new Date().getTime() - startTime;
expect(duration).to.be.greaterThan(1400); expect(duration).to.be.greaterThan(1400);
done(); 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); this.timeout(4000);
var checks = 0; var checks = 0;
@ -64,36 +64,36 @@ describe("the test helper", function(){
expect(checks).to.be.lessThan(30); expect(checks).to.be.lessThan(30);
done(); done();
}); });
}); });
describe("returns a deferred object", function(){ describe("returns a deferred object", function(){
it("it calls done after success", function(done){ it("it calls done after success", function(done){
helper.waitFor(function(){ helper.waitFor(function(){
return true; return true;
}).done(function(){ }).done(function(){
done(); done();
}); });
}); });
it("calls fail after failure", function(done){ it("calls fail after failure", function(done){
helper.waitFor(function(){ helper.waitFor(function(){
return false; return false;
},0).fail(function(){ },0).fail(function(){
done(); done();
}); });
}); });
xit("throws if you don't listen for fails", function(done){ xit("throws if you don't listen for fails", function(done){
var onerror = window.onerror; var onerror = window.onerror;
window.onerror = function(){ window.onerror = function(){
window.onerror = onerror; window.onerror = onerror;
done(); done();
} }
helper.waitFor(function(){ helper.waitFor(function(){
return false; return false;
},100); },100);
}); });
}); });
}); });
}); });

View file

@ -158,8 +158,8 @@ describe("indentation button", function(){
testHelper.selectText(firstTextElement[0], $inner); testHelper.selectText(firstTextElement[0], $inner);
/* this test creates the below content, both should have double indentation /* this test creates the below content, both should have double indentation
line1 line1
line2 line2
firstTextElement.sendkeys('{rightarrow}'); // simulate a keypress of enter firstTextElement.sendkeys('{rightarrow}'); // simulate a keypress of enter

View file

@ -3,7 +3,7 @@ var wd = require(srcFolder + "wd");
var async = require(srcFolder + "async"); var async = require(srcFolder + "async");
var config = { var config = {
host: "ondemand.saucelabs.com" host: "ondemand.saucelabs.com"
, port: 80 , port: 80
, username: process.env.SAUCE_USER , username: process.env.SAUCE_USER
, accessKey: process.env.SAUCE_ACCESS_KEY , accessKey: process.env.SAUCE_ACCESS_KEY