mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-04-20 23:46:14 -04:00
beautified all static js files
This commit is contained in:
parent
2fa1d8768b
commit
271ee1776b
36 changed files with 9456 additions and 6035 deletions
206
static/js/ace.js
206
static/js/ace.js
|
@ -18,38 +18,54 @@
|
||||||
// requires: plugins
|
// requires: plugins
|
||||||
// requires: undefined
|
// requires: undefined
|
||||||
|
|
||||||
|
Ace2Editor.registry = {
|
||||||
|
nextId: 1
|
||||||
|
};
|
||||||
|
|
||||||
Ace2Editor.registry = { nextId: 1 };
|
function Ace2Editor()
|
||||||
|
{
|
||||||
function Ace2Editor() {
|
|
||||||
var thisFunctionsName = "Ace2Editor";
|
var thisFunctionsName = "Ace2Editor";
|
||||||
var ace2 = Ace2Editor;
|
var ace2 = Ace2Editor;
|
||||||
|
|
||||||
var editor = {};
|
var editor = {};
|
||||||
var info = { editor: editor, id: (ace2.registry.nextId++) };
|
var info = {
|
||||||
|
editor: editor,
|
||||||
|
id: (ace2.registry.nextId++)
|
||||||
|
};
|
||||||
var loaded = false;
|
var loaded = false;
|
||||||
|
|
||||||
var actionsPendingInit = [];
|
var actionsPendingInit = [];
|
||||||
function pendingInit(func, optDoNow) {
|
|
||||||
return function() {
|
function pendingInit(func, optDoNow)
|
||||||
|
{
|
||||||
|
return function()
|
||||||
|
{
|
||||||
var that = this;
|
var that = this;
|
||||||
var args = arguments;
|
var args = arguments;
|
||||||
function action() {
|
|
||||||
|
function action()
|
||||||
|
{
|
||||||
func.apply(that, args);
|
func.apply(that, args);
|
||||||
}
|
}
|
||||||
if (optDoNow) {
|
if (optDoNow)
|
||||||
|
{
|
||||||
optDoNow.apply(that, args);
|
optDoNow.apply(that, args);
|
||||||
}
|
}
|
||||||
if (loaded) {
|
if (loaded)
|
||||||
|
{
|
||||||
action();
|
action();
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
actionsPendingInit.push(action);
|
actionsPendingInit.push(action);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
function doActionsPendingInit() {
|
|
||||||
for(var i=0;i<actionsPendingInit.length;i++) {
|
function doActionsPendingInit()
|
||||||
|
{
|
||||||
|
for (var i = 0; i < actionsPendingInit.length; i++)
|
||||||
|
{
|
||||||
actionsPendingInit[i]();
|
actionsPendingInit[i]();
|
||||||
}
|
}
|
||||||
actionsPendingInit = [];
|
actionsPendingInit = [];
|
||||||
|
@ -57,30 +73,69 @@ function Ace2Editor() {
|
||||||
|
|
||||||
ace2.registry[info.id] = info;
|
ace2.registry[info.id] = info;
|
||||||
|
|
||||||
editor.importText = pendingInit(function(newCode, undoable) {
|
editor.importText = pendingInit(function(newCode, undoable)
|
||||||
info.ace_importText(newCode, undoable); });
|
{
|
||||||
editor.importAText = pendingInit(function(newCode, apoolJsonObj, undoable) {
|
info.ace_importText(newCode, undoable);
|
||||||
info.ace_importAText(newCode, apoolJsonObj, undoable); });
|
});
|
||||||
editor.exportText = function() {
|
editor.importAText = pendingInit(function(newCode, apoolJsonObj, undoable)
|
||||||
|
{
|
||||||
|
info.ace_importAText(newCode, apoolJsonObj, undoable);
|
||||||
|
});
|
||||||
|
editor.exportText = function()
|
||||||
|
{
|
||||||
if (!loaded) return "(awaiting init)\n";
|
if (!loaded) return "(awaiting init)\n";
|
||||||
return info.ace_exportText();
|
return info.ace_exportText();
|
||||||
};
|
};
|
||||||
editor.getFrame = function() { return info.frame || null; };
|
editor.getFrame = function()
|
||||||
editor.focus = pendingInit(function() { info.ace_focus(); });
|
{
|
||||||
editor.setEditable = pendingInit(function(newVal) { info.ace_setEditable(newVal); });
|
return info.frame || null;
|
||||||
editor.getFormattedCode = function() { return info.ace_getFormattedCode(); };
|
};
|
||||||
editor.setOnKeyPress = pendingInit(function (handler) { info.ace_setOnKeyPress(handler); });
|
editor.focus = pendingInit(function()
|
||||||
editor.setOnKeyDown = pendingInit(function (handler) { info.ace_setOnKeyDown(handler); });
|
{
|
||||||
editor.setNotifyDirty = pendingInit(function (handler) { info.ace_setNotifyDirty(handler); });
|
info.ace_focus();
|
||||||
|
});
|
||||||
|
editor.setEditable = pendingInit(function(newVal)
|
||||||
|
{
|
||||||
|
info.ace_setEditable(newVal);
|
||||||
|
});
|
||||||
|
editor.getFormattedCode = function()
|
||||||
|
{
|
||||||
|
return info.ace_getFormattedCode();
|
||||||
|
};
|
||||||
|
editor.setOnKeyPress = pendingInit(function(handler)
|
||||||
|
{
|
||||||
|
info.ace_setOnKeyPress(handler);
|
||||||
|
});
|
||||||
|
editor.setOnKeyDown = pendingInit(function(handler)
|
||||||
|
{
|
||||||
|
info.ace_setOnKeyDown(handler);
|
||||||
|
});
|
||||||
|
editor.setNotifyDirty = pendingInit(function(handler)
|
||||||
|
{
|
||||||
|
info.ace_setNotifyDirty(handler);
|
||||||
|
});
|
||||||
|
|
||||||
editor.setProperty = pendingInit(function(key, value) { info.ace_setProperty(key, value); });
|
editor.setProperty = pendingInit(function(key, value)
|
||||||
editor.getDebugProperty = function(prop) { return info.ace_getDebugProperty(prop); };
|
{
|
||||||
|
info.ace_setProperty(key, value);
|
||||||
|
});
|
||||||
|
editor.getDebugProperty = function(prop)
|
||||||
|
{
|
||||||
|
return info.ace_getDebugProperty(prop);
|
||||||
|
};
|
||||||
|
|
||||||
editor.setBaseText = pendingInit(function(txt) { info.ace_setBaseText(txt); });
|
editor.setBaseText = pendingInit(function(txt)
|
||||||
editor.setBaseAttributedText = pendingInit(function(atxt, apoolJsonObj) {
|
{
|
||||||
info.ace_setBaseAttributedText(atxt, apoolJsonObj); });
|
info.ace_setBaseText(txt);
|
||||||
editor.applyChangesToBase = pendingInit(function (changes, optAuthor,apoolJsonObj) {
|
});
|
||||||
info.ace_applyChangesToBase(changes, optAuthor, apoolJsonObj); });
|
editor.setBaseAttributedText = pendingInit(function(atxt, apoolJsonObj)
|
||||||
|
{
|
||||||
|
info.ace_setBaseAttributedText(atxt, apoolJsonObj);
|
||||||
|
});
|
||||||
|
editor.applyChangesToBase = pendingInit(function(changes, optAuthor, apoolJsonObj)
|
||||||
|
{
|
||||||
|
info.ace_applyChangesToBase(changes, optAuthor, apoolJsonObj);
|
||||||
|
});
|
||||||
// prepareUserChangeset:
|
// prepareUserChangeset:
|
||||||
// Returns null if no new changes or ACE not ready. Otherwise, bundles up all user changes
|
// Returns null if no new changes or ACE not ready. Otherwise, bundles up all user changes
|
||||||
// to the latest base text into a Changeset, which is returned (as a string if encodeAsString).
|
// to the latest base text into a Changeset, which is returned (as a string if encodeAsString).
|
||||||
|
@ -90,36 +145,48 @@ function Ace2Editor() {
|
||||||
// to prepareUserChangeset will return an updated changeset that takes into account the
|
// to prepareUserChangeset will return an updated changeset that takes into account the
|
||||||
// latest user changes, and modify the changeset to be applied by applyPreparedChangesetToBase
|
// latest user changes, and modify the changeset to be applied by applyPreparedChangesetToBase
|
||||||
// accordingly.
|
// accordingly.
|
||||||
editor.prepareUserChangeset = function() {
|
editor.prepareUserChangeset = function()
|
||||||
|
{
|
||||||
if (!loaded) return null;
|
if (!loaded) return null;
|
||||||
return info.ace_prepareUserChangeset();
|
return info.ace_prepareUserChangeset();
|
||||||
};
|
};
|
||||||
editor.applyPreparedChangesetToBase = pendingInit(
|
editor.applyPreparedChangesetToBase = pendingInit(
|
||||||
function() { info.ace_applyPreparedChangesetToBase(); });
|
|
||||||
editor.setUserChangeNotificationCallback = pendingInit(function(callback) {
|
function()
|
||||||
|
{
|
||||||
|
info.ace_applyPreparedChangesetToBase();
|
||||||
|
});
|
||||||
|
editor.setUserChangeNotificationCallback = pendingInit(function(callback)
|
||||||
|
{
|
||||||
info.ace_setUserChangeNotificationCallback(callback);
|
info.ace_setUserChangeNotificationCallback(callback);
|
||||||
});
|
});
|
||||||
editor.setAuthorInfo = pendingInit(function(author, authorInfo) {
|
editor.setAuthorInfo = pendingInit(function(author, authorInfo)
|
||||||
|
{
|
||||||
info.ace_setAuthorInfo(author, authorInfo);
|
info.ace_setAuthorInfo(author, authorInfo);
|
||||||
});
|
});
|
||||||
editor.setAuthorSelectionRange = pendingInit(function(author, start, end) {
|
editor.setAuthorSelectionRange = pendingInit(function(author, start, end)
|
||||||
|
{
|
||||||
info.ace_setAuthorSelectionRange(author, start, end);
|
info.ace_setAuthorSelectionRange(author, start, end);
|
||||||
});
|
});
|
||||||
|
|
||||||
editor.getUnhandledErrors = function() {
|
editor.getUnhandledErrors = function()
|
||||||
|
{
|
||||||
if (!loaded) return [];
|
if (!loaded) return [];
|
||||||
// returns array of {error: <browser Error object>, time: +new Date()}
|
// returns array of {error: <browser Error object>, time: +new Date()}
|
||||||
return info.ace_getUnhandledErrors();
|
return info.ace_getUnhandledErrors();
|
||||||
};
|
};
|
||||||
|
|
||||||
editor.callWithAce = pendingInit(function(fn, callStack, normalize) {
|
editor.callWithAce = pendingInit(function(fn, callStack, normalize)
|
||||||
|
{
|
||||||
return info.ace_callWithAce(fn, callStack, normalize);
|
return info.ace_callWithAce(fn, callStack, normalize);
|
||||||
});
|
});
|
||||||
|
|
||||||
editor.execCommand = pendingInit(function(cmd, arg1) {
|
editor.execCommand = pendingInit(function(cmd, arg1)
|
||||||
|
{
|
||||||
info.ace_execCommand(cmd, arg1);
|
info.ace_execCommand(cmd, arg1);
|
||||||
});
|
});
|
||||||
editor.replaceRange = pendingInit(function(start, end, text) {
|
editor.replaceRange = pendingInit(function(start, end, text)
|
||||||
|
{
|
||||||
info.ace_replaceRange(start, end, text);
|
info.ace_replaceRange(start, end, text);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -127,49 +194,57 @@ function Ace2Editor() {
|
||||||
// calls to these functions ($$INCLUDE_...) are replaced when this file is processed
|
// calls to these functions ($$INCLUDE_...) are replaced when this file is processed
|
||||||
// and compressed, putting the compressed code from the named file directly into the
|
// and compressed, putting the compressed code from the named file directly into the
|
||||||
// source here.
|
// source here.
|
||||||
|
var $$INCLUDE_CSS = function(fileName)
|
||||||
var $$INCLUDE_CSS = function(fileName) {
|
{
|
||||||
return '<link rel="stylesheet" type="text/css" href="' + fileName + '"/>';
|
return '<link rel="stylesheet" type="text/css" href="' + fileName + '"/>';
|
||||||
};
|
};
|
||||||
var $$INCLUDE_JS = function(fileName) {
|
var $$INCLUDE_JS = function(fileName)
|
||||||
|
{
|
||||||
return '\x3cscript type="text/javascript" src="' + fileName + '">\x3c/script>';
|
return '\x3cscript type="text/javascript" src="' + fileName + '">\x3c/script>';
|
||||||
};
|
};
|
||||||
var $$INCLUDE_JS_DEV = $$INCLUDE_JS;
|
var $$INCLUDE_JS_DEV = $$INCLUDE_JS;
|
||||||
var $$INCLUDE_CSS_DEV = $$INCLUDE_CSS;
|
var $$INCLUDE_CSS_DEV = $$INCLUDE_CSS;
|
||||||
|
|
||||||
var $$INCLUDE_CSS_Q = function(fileName) {
|
var $$INCLUDE_CSS_Q = function(fileName)
|
||||||
|
{
|
||||||
return '\'<link rel="stylesheet" type="text/css" href="' + fileName + '"/>\'';
|
return '\'<link rel="stylesheet" type="text/css" href="' + fileName + '"/>\'';
|
||||||
};
|
};
|
||||||
var $$INCLUDE_JS_Q = function(fileName) {
|
var $$INCLUDE_JS_Q = function(fileName)
|
||||||
|
{
|
||||||
return '\'\\x3cscript type="text/javascript" src="' + fileName + '">\\x3c/script>\'';
|
return '\'\\x3cscript type="text/javascript" src="' + fileName + '">\\x3c/script>\'';
|
||||||
};
|
};
|
||||||
var $$INCLUDE_JS_Q_DEV = $$INCLUDE_JS_Q;
|
var $$INCLUDE_JS_Q_DEV = $$INCLUDE_JS_Q;
|
||||||
var $$INCLUDE_CSS_Q_DEV = $$INCLUDE_CSS_Q;
|
var $$INCLUDE_CSS_Q_DEV = $$INCLUDE_CSS_Q;
|
||||||
|
|
||||||
editor.destroy = pendingInit(function() {
|
editor.destroy = pendingInit(function()
|
||||||
|
{
|
||||||
info.ace_dispose();
|
info.ace_dispose();
|
||||||
info.frame.parentNode.removeChild(info.frame);
|
info.frame.parentNode.removeChild(info.frame);
|
||||||
delete ace2.registry[info.id];
|
delete ace2.registry[info.id];
|
||||||
info = null; // prevent IE 6 closure memory leaks
|
info = null; // prevent IE 6 closure memory leaks
|
||||||
});
|
});
|
||||||
|
|
||||||
editor.init = function(containerId, initialCode, doneFunc) {
|
editor.init = function(containerId, initialCode, doneFunc)
|
||||||
|
{
|
||||||
|
|
||||||
editor.importText(initialCode);
|
editor.importText(initialCode);
|
||||||
|
|
||||||
info.onEditorReady = function() {
|
info.onEditorReady = function()
|
||||||
|
{
|
||||||
loaded = true;
|
loaded = true;
|
||||||
doActionsPendingInit();
|
doActionsPendingInit();
|
||||||
doneFunc();
|
doneFunc();
|
||||||
};
|
};
|
||||||
|
|
||||||
(function() {
|
(function()
|
||||||
|
{
|
||||||
var doctype = "<!doctype html>";
|
var doctype = "<!doctype html>";
|
||||||
|
|
||||||
var iframeHTML = ["'" + doctype + "<html><head>'"];
|
var iframeHTML = ["'" + doctype + "<html><head>'"];
|
||||||
|
|
||||||
plugins.callHook(
|
plugins.callHook("aceInitInnerdocbodyHead", {
|
||||||
"aceInitInnerdocbodyHead", {iframeHTML:iframeHTML});
|
iframeHTML: iframeHTML
|
||||||
|
});
|
||||||
|
|
||||||
// these lines must conform to a specific format because they are passed by the build script:
|
// these lines must conform to a specific format because they are passed by the build script:
|
||||||
iframeHTML.push($$INCLUDE_CSS_Q("static/css/editor.css"));
|
iframeHTML.push($$INCLUDE_CSS_Q("static/css/editor.css"));
|
||||||
|
@ -191,35 +266,22 @@ function Ace2Editor() {
|
||||||
iframeHTML.push('\'\\n<style type="text/css" title="dynamicsyntax"></style>\\n\'');
|
iframeHTML.push('\'\\n<style type="text/css" title="dynamicsyntax"></style>\\n\'');
|
||||||
iframeHTML.push('\'</head><body id="innerdocbody" class="syntax" spellcheck="false"> </body></html>\'');
|
iframeHTML.push('\'</head><body id="innerdocbody" class="syntax" spellcheck="false"> </body></html>\'');
|
||||||
|
|
||||||
var outerScript = 'editorId = "'+info.id+'"; editorInfo = parent.'+
|
var outerScript = 'editorId = "' + info.id + '"; editorInfo = parent.' + thisFunctionsName + '.registry[editorId]; ' + 'window.onload = function() ' + '{ window.onload = null; setTimeout' + '(function() ' + '{ var iframe = document.createElement("IFRAME"); ' + 'iframe.scrolling = "no"; var outerdocbody = document.getElementById("outerdocbody"); ' + 'iframe.frameBorder = 0; iframe.allowTransparency = true; ' + // for IE
|
||||||
thisFunctionsName+'.registry[editorId]; '+
|
'outerdocbody.insertBefore(iframe, outerdocbody.firstChild); ' + 'iframe.ace_outerWin = window; ' + 'readyFunc = function() { editorInfo.onEditorReady(); readyFunc = null; editorInfo = null; }; ' + 'var doc = iframe.contentWindow.document; doc.open(); var text = (' + iframeHTML.join('+') + ').replace(/\\\\x3c/g, \'<\');doc.write(text); doc.close(); ' + '}, 0); }';
|
||||||
'window.onload = function() '+
|
|
||||||
'{ window.onload = null; setTimeout'+
|
|
||||||
'(function() '+
|
|
||||||
'{ var iframe = document.createElement("IFRAME"); '+
|
|
||||||
'iframe.scrolling = "no"; var outerdocbody = document.getElementById("outerdocbody"); '+
|
|
||||||
'iframe.frameBorder = 0; iframe.allowTransparency = true; '+ // for IE
|
|
||||||
'outerdocbody.insertBefore(iframe, outerdocbody.firstChild); '+
|
|
||||||
'iframe.ace_outerWin = window; '+
|
|
||||||
'readyFunc = function() { editorInfo.onEditorReady(); readyFunc = null; editorInfo = null; }; '+
|
|
||||||
'var doc = iframe.contentWindow.document; doc.open(); var text = ('+
|
|
||||||
iframeHTML.join('+')+').replace(/\\\\x3c/g, \'<\');doc.write(text); doc.close(); '+
|
|
||||||
'}, 0); }';
|
|
||||||
|
|
||||||
var outerHTML = [doctype, '<html><head>',
|
var outerHTML = [doctype, '<html><head>', $$INCLUDE_CSS("static/css/editor.css"),
|
||||||
$$INCLUDE_CSS("static/css/editor.css"),
|
|
||||||
// bizarrely, in FF2, a file with no "external" dependencies won't finish loading properly
|
// bizarrely, in FF2, a file with no "external" dependencies won't finish loading properly
|
||||||
// (throbs busy while typing)
|
// (throbs busy while typing)
|
||||||
'<link rel="stylesheet" type="text/css" href="data:text/css,"/>',
|
'<link rel="stylesheet" type="text/css" href="data:text/css,"/>', '\x3cscript>\n', outerScript, '\n\x3c/script>', '</head><body id="outerdocbody"><div id="sidediv"><!-- --></div><div id="linemetricsdiv">x</div><div id="overlaysdiv"><!-- --></div></body></html>'];
|
||||||
'\x3cscript>\n', outerScript, '\n\x3c/script>',
|
|
||||||
'</head><body id="outerdocbody"><div id="sidediv"><!-- --></div><div id="linemetricsdiv">x</div><div id="overlaysdiv"><!-- --></div></body></html>'];
|
|
||||||
|
|
||||||
if (!Array.prototype.map) Array.prototype.map = function(fun) { //needed for IE
|
if (!Array.prototype.map) Array.prototype.map = function(fun)
|
||||||
|
{ //needed for IE
|
||||||
if (typeof fun != "function") throw new TypeError();
|
if (typeof fun != "function") throw new TypeError();
|
||||||
var len = this.length;
|
var len = this.length;
|
||||||
var res = new Array(len);
|
var res = new Array(len);
|
||||||
var thisp = arguments[1];
|
var thisp = arguments[1];
|
||||||
for (var i = 0; i < len; i++) {
|
for (var i = 0; i < len; i++)
|
||||||
|
{
|
||||||
if (i in this) res[i] = fun.call(thisp, this[i], i, this);
|
if (i in this) res[i] = fun.call(thisp, this[i], i, this);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -15,53 +15,63 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
function isNodeText(node) {
|
function isNodeText(node)
|
||||||
|
{
|
||||||
return (node.nodeType == 3);
|
return (node.nodeType == 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
function object(o) {
|
function object(o)
|
||||||
var f = function() {};
|
{
|
||||||
|
var f = function()
|
||||||
|
{};
|
||||||
f.prototype = o;
|
f.prototype = o;
|
||||||
return new f();
|
return new f();
|
||||||
}
|
}
|
||||||
|
|
||||||
function extend(obj, props) {
|
function extend(obj, props)
|
||||||
for(var p in props) {
|
{
|
||||||
|
for (var p in props)
|
||||||
|
{
|
||||||
obj[p] = props[p];
|
obj[p] = props[p];
|
||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
function forEach(array, func) {
|
function forEach(array, func)
|
||||||
for(var i=0;i<array.length;i++) {
|
{
|
||||||
|
for (var i = 0; i < array.length; i++)
|
||||||
|
{
|
||||||
var result = func(array[i], i);
|
var result = func(array[i], i);
|
||||||
if (result) break;
|
if (result) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function map(array, func) {
|
function map(array, func)
|
||||||
|
{
|
||||||
var result = [];
|
var result = [];
|
||||||
// must remain compatible with "arguments" pseudo-array
|
// must remain compatible with "arguments" pseudo-array
|
||||||
for(var i=0;i<array.length;i++) {
|
for (var i = 0; i < array.length; i++)
|
||||||
|
{
|
||||||
if (func) result.push(func(array[i], i));
|
if (func) result.push(func(array[i], i));
|
||||||
else result.push(array[i]);
|
else result.push(array[i]);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function filter(array, func) {
|
function filter(array, func)
|
||||||
|
{
|
||||||
var result = [];
|
var result = [];
|
||||||
// must remain compatible with "arguments" pseudo-array
|
// must remain compatible with "arguments" pseudo-array
|
||||||
for(var i=0;i<array.length;i++) {
|
for (var i = 0; i < array.length; i++)
|
||||||
|
{
|
||||||
if (func(array[i], i)) result.push(array[i]);
|
if (func(array[i], i)) result.push(array[i]);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isArray(testObject) {
|
function isArray(testObject)
|
||||||
return testObject && typeof testObject === 'object' &&
|
{
|
||||||
!(testObject.propertyIsEnumerable('length')) &&
|
return testObject && typeof testObject === 'object' && !(testObject.propertyIsEnumerable('length')) && typeof testObject.length === 'number';
|
||||||
typeof testObject.length === 'number';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Figure out what browser is being used (stolen from jquery 1.2.1)
|
// Figure out what browser is being used (stolen from jquery 1.2.1)
|
||||||
|
@ -75,11 +85,13 @@ var browser = {
|
||||||
windows: /windows/.test(userAgent) // dgreensp
|
windows: /windows/.test(userAgent) // dgreensp
|
||||||
};
|
};
|
||||||
|
|
||||||
function getAssoc(obj, name) {
|
function getAssoc(obj, name)
|
||||||
|
{
|
||||||
return obj["_magicdom_" + name];
|
return obj["_magicdom_" + name];
|
||||||
}
|
}
|
||||||
|
|
||||||
function setAssoc(obj, name, value) {
|
function setAssoc(obj, name, value)
|
||||||
|
{
|
||||||
// note that in IE designMode, properties of a node can get
|
// note that in IE designMode, properties of a node can get
|
||||||
// copied to new nodes that are spawned during editing; also,
|
// copied to new nodes that are spawned during editing; also,
|
||||||
// properties representable in HTML text can survive copy-and-paste
|
// properties representable in HTML text can survive copy-and-paste
|
||||||
|
@ -89,13 +101,17 @@ function setAssoc(obj, name, value) {
|
||||||
// "func" is a function over 0..(numItems-1) that is monotonically
|
// "func" is a function over 0..(numItems-1) that is monotonically
|
||||||
// "increasing" with index (false, then true). Finds the boundary
|
// "increasing" with index (false, then true). Finds the boundary
|
||||||
// between false and true, a number between 0 and numItems inclusive.
|
// between false and true, a number between 0 and numItems inclusive.
|
||||||
function binarySearch(numItems, func) {
|
|
||||||
|
|
||||||
|
function binarySearch(numItems, func)
|
||||||
|
{
|
||||||
if (numItems < 1) return 0;
|
if (numItems < 1) return 0;
|
||||||
if (func(0)) return 0;
|
if (func(0)) return 0;
|
||||||
if (!func(numItems - 1)) return numItems;
|
if (!func(numItems - 1)) return numItems;
|
||||||
var low = 0; // func(low) is always false
|
var low = 0; // func(low) is always false
|
||||||
var high = numItems - 1; // func(high) is always true
|
var high = numItems - 1; // func(high) is always true
|
||||||
while ((high - low) > 1) {
|
while ((high - low) > 1)
|
||||||
|
{
|
||||||
var x = Math.floor((low + high) / 2); // x != low, x != high
|
var x = Math.floor((low + high) / 2); // x != low, x != high
|
||||||
if (func(x)) high = x;
|
if (func(x)) high = x;
|
||||||
else low = x;
|
else low = x;
|
||||||
|
@ -103,13 +119,14 @@ function binarySearch(numItems, func) {
|
||||||
return high;
|
return high;
|
||||||
}
|
}
|
||||||
|
|
||||||
function binarySearchInfinite(expectedLength, func) {
|
function binarySearchInfinite(expectedLength, func)
|
||||||
|
{
|
||||||
var i = 0;
|
var i = 0;
|
||||||
while (!func(i)) i += expectedLength;
|
while (!func(i)) i += expectedLength;
|
||||||
return binarySearch(i, func);
|
return binarySearch(i, func);
|
||||||
}
|
}
|
||||||
|
|
||||||
function htmlPrettyEscape(str) {
|
function htmlPrettyEscape(str)
|
||||||
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
{
|
||||||
.replace(/\r?\n/g, '\\n');
|
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\r?\n/g, '\\n');
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -104,7 +104,6 @@ function loadBroadcastJS()
|
||||||
var userId = "hiddenUser" + randomString();
|
var userId = "hiddenUser" + randomString();
|
||||||
var socketId;
|
var socketId;
|
||||||
//var socket;
|
//var socket;
|
||||||
|
|
||||||
var channelState = "DISCONNECTED";
|
var channelState = "DISCONNECTED";
|
||||||
|
|
||||||
var appLevelDisconnectReason = null;
|
var appLevelDisconnectReason = null;
|
||||||
|
@ -319,6 +318,8 @@ function loadBroadcastJS()
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var date = new Date(padContents.currentTime);
|
var date = new Date(padContents.currentTime);
|
||||||
var dateFormat = function()
|
var dateFormat = function()
|
||||||
{
|
{
|
||||||
|
@ -333,6 +334,8 @@ function loadBroadcastJS()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$('#timer').html(dateFormat());
|
$('#timer').html(dateFormat());
|
||||||
|
|
||||||
var revisionDate = ["Saved", ["Jan", "Feb", "March", "April", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec"][date.getMonth()], date.getDate() + ",", date.getFullYear()].join(" ")
|
var revisionDate = ["Saved", ["Jan", "Feb", "March", "April", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec"][date.getMonth()], date.getDate() + ",", date.getFullYear()].join(" ")
|
||||||
|
@ -464,7 +467,11 @@ function loadBroadcastJS()
|
||||||
|
|
||||||
socket.send(msg);*/
|
socket.send(msg);*/
|
||||||
|
|
||||||
sendSocketMsg("CHANGESET_REQ",{ "start": start, "granularity": granularity, "requestID": requestID});
|
sendSocketMsg("CHANGESET_REQ", {
|
||||||
|
"start": start,
|
||||||
|
"granularity": granularity,
|
||||||
|
"requestID": requestID
|
||||||
|
});
|
||||||
|
|
||||||
self.reqCallbacks[requestID] = callback;
|
self.reqCallbacks[requestID] = callback;
|
||||||
|
|
||||||
|
@ -708,6 +715,8 @@ function loadBroadcastJS()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
BroadcastSlider.onSlider(goToRevisionIfEnabled);
|
BroadcastSlider.onSlider(goToRevisionIfEnabled);
|
||||||
|
|
||||||
(function()
|
(function()
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
// revision info is a skip list whos entries represent a particular revision
|
// revision info is a skip list whos entries represent a particular revision
|
||||||
// of the document. These revisions are connected together by various
|
// of the document. These revisions are connected together by various
|
||||||
// changesets, or deltas, between any two revisions.
|
// changesets, or deltas, between any two revisions.
|
||||||
|
|
||||||
var global = this;
|
var global = this;
|
||||||
|
|
||||||
function loadBroadcastRevisionsJS()
|
function loadBroadcastRevisionsJS()
|
||||||
|
|
|
@ -48,6 +48,8 @@ function loadBroadcastSliderJS()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var updateSliderElements = function()
|
var updateSliderElements = function()
|
||||||
{
|
{
|
||||||
for (var i = 0; i < savedRevisions.length; i++)
|
for (var i = 0; i < savedRevisions.length; i++)
|
||||||
|
@ -60,6 +62,8 @@ function loadBroadcastSliderJS()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var addSavedRevision = function(position, info)
|
var addSavedRevision = function(position, info)
|
||||||
{
|
{
|
||||||
var newSavedRevision = $('<div></div>');
|
var newSavedRevision = $('<div></div>');
|
||||||
|
@ -146,7 +150,6 @@ function loadBroadcastSliderJS()
|
||||||
|
|
||||||
// just take over the whole slider screen with a reconnect message
|
// just take over the whole slider screen with a reconnect message
|
||||||
|
|
||||||
|
|
||||||
function showReconnectUI()
|
function showReconnectUI()
|
||||||
{
|
{
|
||||||
if (!clientVars.sliderEnabled || !clientVars.supportsSlider)
|
if (!clientVars.sliderEnabled || !clientVars.supportsSlider)
|
||||||
|
|
|
@ -15,7 +15,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
function makeChangesetTracker(scheduler, apool, aceCallbacksProvider) {
|
function makeChangesetTracker(scheduler, apool, aceCallbacksProvider)
|
||||||
|
{
|
||||||
|
|
||||||
// latest official text from server
|
// latest official text from server
|
||||||
var baseAText = Changeset.makeAText("\n");
|
var baseAText = Changeset.makeAText("\n");
|
||||||
|
@ -34,16 +35,22 @@ function makeChangesetTracker(scheduler, apool, aceCallbacksProvider) {
|
||||||
var changeCallback = null;
|
var changeCallback = null;
|
||||||
|
|
||||||
var changeCallbackTimeout = null;
|
var changeCallbackTimeout = null;
|
||||||
function setChangeCallbackTimeout() {
|
|
||||||
|
function setChangeCallbackTimeout()
|
||||||
|
{
|
||||||
// can call this multiple times per call-stack, because
|
// can call this multiple times per call-stack, because
|
||||||
// we only schedule a call to changeCallback if it exists
|
// we only schedule a call to changeCallback if it exists
|
||||||
// and if there isn't a timeout already scheduled.
|
// and if there isn't a timeout already scheduled.
|
||||||
if (changeCallback && changeCallbackTimeout === null) {
|
if (changeCallback && changeCallbackTimeout === null)
|
||||||
changeCallbackTimeout = scheduler.setTimeout(function() {
|
{
|
||||||
try {
|
changeCallbackTimeout = scheduler.setTimeout(function()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
changeCallback();
|
changeCallback();
|
||||||
}
|
}
|
||||||
finally {
|
finally
|
||||||
|
{
|
||||||
changeCallbackTimeout = null;
|
changeCallbackTimeout = null;
|
||||||
}
|
}
|
||||||
}, 0);
|
}, 0);
|
||||||
|
@ -52,30 +59,40 @@ function makeChangesetTracker(scheduler, apool, aceCallbacksProvider) {
|
||||||
|
|
||||||
var self;
|
var self;
|
||||||
return self = {
|
return self = {
|
||||||
isTracking: function() { return tracking; },
|
isTracking: function()
|
||||||
setBaseText: function(text) {
|
{
|
||||||
|
return tracking;
|
||||||
|
},
|
||||||
|
setBaseText: function(text)
|
||||||
|
{
|
||||||
self.setBaseAttributedText(Changeset.makeAText(text), null);
|
self.setBaseAttributedText(Changeset.makeAText(text), null);
|
||||||
},
|
},
|
||||||
setBaseAttributedText: function(atext, apoolJsonObj) {
|
setBaseAttributedText: function(atext, apoolJsonObj)
|
||||||
aceCallbacksProvider.withCallbacks("setBaseText", function(callbacks) {
|
{
|
||||||
|
aceCallbacksProvider.withCallbacks("setBaseText", function(callbacks)
|
||||||
|
{
|
||||||
tracking = true;
|
tracking = true;
|
||||||
baseAText = Changeset.cloneAText(atext);
|
baseAText = Changeset.cloneAText(atext);
|
||||||
if (apoolJsonObj) {
|
if (apoolJsonObj)
|
||||||
|
{
|
||||||
var wireApool = (new AttribPool()).fromJsonable(apoolJsonObj);
|
var wireApool = (new AttribPool()).fromJsonable(apoolJsonObj);
|
||||||
baseAText.attribs = Changeset.moveOpsToNewPool(baseAText.attribs, wireApool, apool);
|
baseAText.attribs = Changeset.moveOpsToNewPool(baseAText.attribs, wireApool, apool);
|
||||||
}
|
}
|
||||||
submittedChangeset = null;
|
submittedChangeset = null;
|
||||||
userChangeset = Changeset.identity(atext.text.length);
|
userChangeset = Changeset.identity(atext.text.length);
|
||||||
applyingNonUserChanges = true;
|
applyingNonUserChanges = true;
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
callbacks.setDocumentAttributedText(atext);
|
callbacks.setDocumentAttributedText(atext);
|
||||||
}
|
}
|
||||||
finally {
|
finally
|
||||||
|
{
|
||||||
applyingNonUserChanges = false;
|
applyingNonUserChanges = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
composeUserChangeset: function(c) {
|
composeUserChangeset: function(c)
|
||||||
|
{
|
||||||
if (!tracking) return;
|
if (!tracking) return;
|
||||||
if (applyingNonUserChanges) return;
|
if (applyingNonUserChanges) return;
|
||||||
if (Changeset.isIdentity(c)) return;
|
if (Changeset.isIdentity(c)) return;
|
||||||
|
@ -83,12 +100,15 @@ function makeChangesetTracker(scheduler, apool, aceCallbacksProvider) {
|
||||||
|
|
||||||
setChangeCallbackTimeout();
|
setChangeCallbackTimeout();
|
||||||
},
|
},
|
||||||
applyChangesToBase: function (c, optAuthor, apoolJsonObj) {
|
applyChangesToBase: function(c, optAuthor, apoolJsonObj)
|
||||||
|
{
|
||||||
if (!tracking) return;
|
if (!tracking) return;
|
||||||
|
|
||||||
aceCallbacksProvider.withCallbacks("applyChangesToBase", function(callbacks) {
|
aceCallbacksProvider.withCallbacks("applyChangesToBase", function(callbacks)
|
||||||
|
{
|
||||||
|
|
||||||
if (apoolJsonObj) {
|
if (apoolJsonObj)
|
||||||
|
{
|
||||||
var wireApool = (new AttribPool()).fromJsonable(apoolJsonObj);
|
var wireApool = (new AttribPool()).fromJsonable(apoolJsonObj);
|
||||||
c = Changeset.moveOpsToNewPool(c, wireApool, apool);
|
c = Changeset.moveOpsToNewPool(c, wireApool, apool);
|
||||||
}
|
}
|
||||||
|
@ -96,7 +116,8 @@ function makeChangesetTracker(scheduler, apool, aceCallbacksProvider) {
|
||||||
baseAText = Changeset.applyToAText(c, baseAText, apool);
|
baseAText = Changeset.applyToAText(c, baseAText, apool);
|
||||||
|
|
||||||
var c2 = c;
|
var c2 = c;
|
||||||
if (submittedChangeset) {
|
if (submittedChangeset)
|
||||||
|
{
|
||||||
var oldSubmittedChangeset = submittedChangeset;
|
var oldSubmittedChangeset = submittedChangeset;
|
||||||
submittedChangeset = Changeset.follow(c, oldSubmittedChangeset, false, apool);
|
submittedChangeset = Changeset.follow(c, oldSubmittedChangeset, false, apool);
|
||||||
c2 = Changeset.follow(oldSubmittedChangeset, c, true, apool);
|
c2 = Changeset.follow(oldSubmittedChangeset, c, true, apool);
|
||||||
|
@ -105,53 +126,63 @@ function makeChangesetTracker(scheduler, apool, aceCallbacksProvider) {
|
||||||
var preferInsertingAfterUserChanges = true;
|
var preferInsertingAfterUserChanges = true;
|
||||||
var oldUserChangeset = userChangeset;
|
var oldUserChangeset = userChangeset;
|
||||||
userChangeset = Changeset.follow(c2, oldUserChangeset, preferInsertingAfterUserChanges, apool);
|
userChangeset = Changeset.follow(c2, oldUserChangeset, preferInsertingAfterUserChanges, apool);
|
||||||
var postChange =
|
var postChange = Changeset.follow(oldUserChangeset, c2, !preferInsertingAfterUserChanges, apool);
|
||||||
Changeset.follow(oldUserChangeset, c2, ! preferInsertingAfterUserChanges, apool);
|
|
||||||
|
|
||||||
var preferInsertionAfterCaret = true; //(optAuthor && optAuthor > thisAuthor);
|
var preferInsertionAfterCaret = true; //(optAuthor && optAuthor > thisAuthor);
|
||||||
|
|
||||||
applyingNonUserChanges = true;
|
applyingNonUserChanges = true;
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
callbacks.applyChangesetToDocument(postChange, preferInsertionAfterCaret);
|
callbacks.applyChangesetToDocument(postChange, preferInsertionAfterCaret);
|
||||||
}
|
}
|
||||||
finally {
|
finally
|
||||||
|
{
|
||||||
applyingNonUserChanges = false;
|
applyingNonUserChanges = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
prepareUserChangeset: function() {
|
prepareUserChangeset: function()
|
||||||
|
{
|
||||||
// If there are user changes to submit, 'changeset' will be the
|
// If there are user changes to submit, 'changeset' will be the
|
||||||
// changeset, else it will be null.
|
// changeset, else it will be null.
|
||||||
var toSubmit;
|
var toSubmit;
|
||||||
if (submittedChangeset) {
|
if (submittedChangeset)
|
||||||
|
{
|
||||||
// submission must have been canceled, prepare new changeset
|
// submission must have been canceled, prepare new changeset
|
||||||
// that includes old submittedChangeset
|
// that includes old submittedChangeset
|
||||||
toSubmit = Changeset.compose(submittedChangeset, userChangeset, apool);
|
toSubmit = Changeset.compose(submittedChangeset, userChangeset, apool);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
if (Changeset.isIdentity(userChangeset)) toSubmit = null;
|
if (Changeset.isIdentity(userChangeset)) toSubmit = null;
|
||||||
else toSubmit = userChangeset;
|
else toSubmit = userChangeset;
|
||||||
}
|
}
|
||||||
|
|
||||||
var cs = null;
|
var cs = null;
|
||||||
if (toSubmit) {
|
if (toSubmit)
|
||||||
|
{
|
||||||
submittedChangeset = toSubmit;
|
submittedChangeset = toSubmit;
|
||||||
userChangeset = Changeset.identity(Changeset.newLen(toSubmit));
|
userChangeset = Changeset.identity(Changeset.newLen(toSubmit));
|
||||||
|
|
||||||
cs = toSubmit;
|
cs = toSubmit;
|
||||||
}
|
}
|
||||||
var wireApool = null;
|
var wireApool = null;
|
||||||
if (cs) {
|
if (cs)
|
||||||
|
{
|
||||||
var forWire = Changeset.prepareForWire(cs, apool);
|
var forWire = Changeset.prepareForWire(cs, apool);
|
||||||
wireApool = forWire.pool.toJsonable();
|
wireApool = forWire.pool.toJsonable();
|
||||||
cs = forWire.translated;
|
cs = forWire.translated;
|
||||||
}
|
}
|
||||||
|
|
||||||
var data = { changeset: cs, apool: wireApool };
|
var data = {
|
||||||
|
changeset: cs,
|
||||||
|
apool: wireApool
|
||||||
|
};
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
applyPreparedChangesetToBase: function() {
|
applyPreparedChangesetToBase: function()
|
||||||
if (! submittedChangeset) {
|
{
|
||||||
|
if (!submittedChangeset)
|
||||||
|
{
|
||||||
// violation of protocol; use prepareUserChangeset first
|
// violation of protocol; use prepareUserChangeset first
|
||||||
throw new Error("applySubmittedChangesToBase: no submitted changes to apply");
|
throw new Error("applySubmittedChangesToBase: no submitted changes to apply");
|
||||||
}
|
}
|
||||||
|
@ -159,10 +190,12 @@ function makeChangesetTracker(scheduler, apool, aceCallbacksProvider) {
|
||||||
baseAText = Changeset.applyToAText(submittedChangeset, baseAText, apool);
|
baseAText = Changeset.applyToAText(submittedChangeset, baseAText, apool);
|
||||||
submittedChangeset = null;
|
submittedChangeset = null;
|
||||||
},
|
},
|
||||||
setUserChangeNotificationCallback: function (callback) {
|
setUserChangeNotificationCallback: function(callback)
|
||||||
|
{
|
||||||
changeCallback = callback;
|
changeCallback = callback;
|
||||||
},
|
},
|
||||||
hasUncommittedChanges: function() {
|
hasUncommittedChanges: function()
|
||||||
|
{
|
||||||
return !!(submittedChangeset || (!Changeset.isIdentity(userChangeset)));
|
return !!(submittedChangeset || (!Changeset.isIdentity(userChangeset)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,14 +14,16 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$(window).bind("load", function() {
|
$(window).bind("load", function()
|
||||||
|
{
|
||||||
getCollabClient.windowLoaded = true;
|
getCollabClient.windowLoaded = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
/** Call this when the document is ready, and a new Ace2Editor() has been created and inited.
|
/** Call this when the document is ready, and a new Ace2Editor() has been created and inited.
|
||||||
ACE's ready callback does not need to have fired yet.
|
ACE's ready callback does not need to have fired yet.
|
||||||
"serverVars" are from calling doc.getCollabClientVars() on the server. */
|
"serverVars" are from calling doc.getCollabClientVars() on the server. */
|
||||||
function getCollabClient(ace2editor, serverVars, initialUserInfo, options) {
|
function getCollabClient(ace2editor, serverVars, initialUserInfo, options)
|
||||||
|
{
|
||||||
var editor = ace2editor;
|
var editor = ace2editor;
|
||||||
|
|
||||||
var rev = serverVars.rev;
|
var rev = serverVars.rev;
|
||||||
|
@ -53,36 +55,55 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) {
|
||||||
tellAceActiveAuthorInfo(initialUserInfo);
|
tellAceActiveAuthorInfo(initialUserInfo);
|
||||||
|
|
||||||
var callbacks = {
|
var callbacks = {
|
||||||
onUserJoin: function() {},
|
onUserJoin: function()
|
||||||
onUserLeave: function() {},
|
{},
|
||||||
onUpdateUserInfo: function() {},
|
onUserLeave: function()
|
||||||
onChannelStateChange: function() {},
|
{},
|
||||||
onClientMessage: function() {},
|
onUpdateUserInfo: function()
|
||||||
onInternalAction: function() {},
|
{},
|
||||||
onConnectionTrouble: function() {},
|
onChannelStateChange: function()
|
||||||
onServerMessage: function() {}
|
{},
|
||||||
|
onClientMessage: function()
|
||||||
|
{},
|
||||||
|
onInternalAction: function()
|
||||||
|
{},
|
||||||
|
onConnectionTrouble: function()
|
||||||
|
{},
|
||||||
|
onServerMessage: function()
|
||||||
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
$(window).bind("unload", function() {
|
$(window).bind("unload", function()
|
||||||
if (socket) {
|
{
|
||||||
|
if (socket)
|
||||||
|
{
|
||||||
/*socket.onclosed = function() {};
|
/*socket.onclosed = function() {};
|
||||||
socket.onhiccup = function() {};
|
socket.onhiccup = function() {};
|
||||||
socket.disconnect(true);*/
|
socket.disconnect(true);*/
|
||||||
socket.disconnect();
|
socket.disconnect();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if ($.browser.mozilla) {
|
if ($.browser.mozilla)
|
||||||
|
{
|
||||||
// Prevent "escape" from taking effect and canceling a comet connection;
|
// Prevent "escape" from taking effect and canceling a comet connection;
|
||||||
// doesn't work if focus is on an iframe.
|
// doesn't work if focus is on an iframe.
|
||||||
$(window).bind("keydown", function(evt) { if (evt.which == 27) { evt.preventDefault() } });
|
$(window).bind("keydown", function(evt)
|
||||||
|
{
|
||||||
|
if (evt.which == 27)
|
||||||
|
{
|
||||||
|
evt.preventDefault()
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
editor.setProperty("userAuthor", userId);
|
editor.setProperty("userAuthor", userId);
|
||||||
editor.setBaseAttributedText(serverVars.initialAttributedText, serverVars.apool);
|
editor.setBaseAttributedText(serverVars.initialAttributedText, serverVars.apool);
|
||||||
editor.setUserChangeNotificationCallback(wrapRecordingErrors("handleUserChanges", handleUserChanges));
|
editor.setUserChangeNotificationCallback(wrapRecordingErrors("handleUserChanges", handleUserChanges));
|
||||||
|
|
||||||
function abandonConnection(reason) {
|
function abandonConnection(reason)
|
||||||
if (socket) {
|
{
|
||||||
|
if (socket)
|
||||||
|
{
|
||||||
/*socket.onclosed = function() {};
|
/*socket.onclosed = function() {};
|
||||||
socket.onhiccup = function() {};*/
|
socket.onhiccup = function() {};*/
|
||||||
socket.disconnect();
|
socket.disconnect();
|
||||||
|
@ -91,77 +112,87 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) {
|
||||||
setChannelState("DISCONNECTED", reason);
|
setChannelState("DISCONNECTED", reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
function dmesg(str) {
|
function dmesg(str)
|
||||||
|
{
|
||||||
if (typeof window.ajlog == "string") window.ajlog += str + '\n';
|
if (typeof window.ajlog == "string") window.ajlog += str + '\n';
|
||||||
debugMessages.push(str);
|
debugMessages.push(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleUserChanges() {
|
function handleUserChanges()
|
||||||
if ((! socket) || channelState == "CONNECTING") {
|
{
|
||||||
if (channelState == "CONNECTING" && (((+new Date()) - initialStartConnectTime) > 20000)) {
|
if ((!socket) || channelState == "CONNECTING")
|
||||||
|
{
|
||||||
|
if (channelState == "CONNECTING" && (((+new Date()) - initialStartConnectTime) > 20000))
|
||||||
|
{
|
||||||
abandonConnection("initsocketfail"); // give up
|
abandonConnection("initsocketfail"); // give up
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
// check again in a bit
|
// check again in a bit
|
||||||
setTimeout(wrapRecordingErrors("setTimeout(handleUserChanges)", handleUserChanges),
|
setTimeout(wrapRecordingErrors("setTimeout(handleUserChanges)", handleUserChanges), 1000);
|
||||||
1000);
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var t = (+new Date());
|
var t = (+new Date());
|
||||||
|
|
||||||
if (state != "IDLE") {
|
if (state != "IDLE")
|
||||||
if (state == "COMMITTING" && (t - lastCommitTime) > 20000) {
|
{
|
||||||
|
if (state == "COMMITTING" && (t - lastCommitTime) > 20000)
|
||||||
|
{
|
||||||
// a commit is taking too long
|
// a commit is taking too long
|
||||||
appLevelDisconnectReason = "slowcommit";
|
appLevelDisconnectReason = "slowcommit";
|
||||||
socket.disconnect();
|
socket.disconnect();
|
||||||
}
|
}
|
||||||
else if (state == "COMMITTING" && (t - lastCommitTime) > 5000) {
|
else if (state == "COMMITTING" && (t - lastCommitTime) > 5000)
|
||||||
|
{
|
||||||
callbacks.onConnectionTrouble("SLOW");
|
callbacks.onConnectionTrouble("SLOW");
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
// run again in a few seconds, to detect a disconnect
|
// run again in a few seconds, to detect a disconnect
|
||||||
setTimeout(wrapRecordingErrors("setTimeout(handleUserChanges)", handleUserChanges),
|
setTimeout(wrapRecordingErrors("setTimeout(handleUserChanges)", handleUserChanges), 3000);
|
||||||
3000);
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var earliestCommit = lastCommitTime + 500;
|
var earliestCommit = lastCommitTime + 500;
|
||||||
if (t < earliestCommit) {
|
if (t < earliestCommit)
|
||||||
setTimeout(wrapRecordingErrors("setTimeout(handleUserChanges)", handleUserChanges),
|
{
|
||||||
earliestCommit - t);
|
setTimeout(wrapRecordingErrors("setTimeout(handleUserChanges)", handleUserChanges), earliestCommit - t);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var sentMessage = false;
|
var sentMessage = false;
|
||||||
var userChangesData = editor.prepareUserChangeset();
|
var userChangesData = editor.prepareUserChangeset();
|
||||||
if (userChangesData.changeset) {
|
if (userChangesData.changeset)
|
||||||
|
{
|
||||||
lastCommitTime = t;
|
lastCommitTime = t;
|
||||||
state = "COMMITTING";
|
state = "COMMITTING";
|
||||||
stateMessage = {type:"USER_CHANGES", baseRev:rev,
|
stateMessage = {
|
||||||
|
type: "USER_CHANGES",
|
||||||
|
baseRev: rev,
|
||||||
changeset: userChangesData.changeset,
|
changeset: userChangesData.changeset,
|
||||||
apool: userChangesData.apool };
|
apool: userChangesData.apool
|
||||||
|
};
|
||||||
stateMessageSocketId = socketId;
|
stateMessageSocketId = socketId;
|
||||||
sendMessage(stateMessage);
|
sendMessage(stateMessage);
|
||||||
sentMessage = true;
|
sentMessage = true;
|
||||||
callbacks.onInternalAction("commitPerformed");
|
callbacks.onInternalAction("commitPerformed");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sentMessage) {
|
if (sentMessage)
|
||||||
|
{
|
||||||
// run again in a few seconds, to detect a disconnect
|
// run again in a few seconds, to detect a disconnect
|
||||||
setTimeout(wrapRecordingErrors("setTimeout(handleUserChanges)", handleUserChanges),
|
setTimeout(wrapRecordingErrors("setTimeout(handleUserChanges)", handleUserChanges), 3000);
|
||||||
3000);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStats() {
|
function getStats()
|
||||||
|
{
|
||||||
var stats = {};
|
var stats = {};
|
||||||
|
|
||||||
stats.screen = [$(window).width(), $(window).height(),
|
stats.screen = [$(window).width(), $(window).height(), window.screen.availWidth, window.screen.availHeight, window.screen.width, window.screen.height].join(',');
|
||||||
window.screen.availWidth, window.screen.availHeight,
|
|
||||||
window.screen.width, window.screen.height].join(',');
|
|
||||||
stats.ip = serverVars.clientIp;
|
stats.ip = serverVars.clientIp;
|
||||||
stats.useragent = serverVars.clientAgent;
|
stats.useragent = serverVars.clientAgent;
|
||||||
|
|
||||||
|
@ -172,7 +203,6 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) {
|
||||||
{
|
{
|
||||||
//oldSocketId = String(Math.floor(Math.random()*1e12));
|
//oldSocketId = String(Math.floor(Math.random()*1e12));
|
||||||
//socketId = String(Math.floor(Math.random()*1e12));
|
//socketId = String(Math.floor(Math.random()*1e12));
|
||||||
|
|
||||||
/*socket = new io.Socket();
|
/*socket = new io.Socket();
|
||||||
socket.connect();*/
|
socket.connect();*/
|
||||||
|
|
||||||
|
@ -194,14 +224,14 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) {
|
||||||
|
|
||||||
initialStartConnectTime = +new Date();
|
initialStartConnectTime = +new Date();
|
||||||
// });
|
// });
|
||||||
|
|
||||||
/*socket.on('message', function(obj){
|
/*socket.on('message', function(obj){
|
||||||
if(window.console)
|
if(window.console)
|
||||||
console.log(obj);
|
console.log(obj);
|
||||||
handleMessageFromServer(obj);
|
handleMessageFromServer(obj);
|
||||||
});*/
|
});*/
|
||||||
|
|
||||||
socket.on('disconnect', function(obj){
|
socket.on('disconnect', function(obj)
|
||||||
|
{
|
||||||
handleSocketClosed(true);
|
handleSocketClosed(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -242,43 +272,62 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) {
|
||||||
abandonConnection("initsocketfail");
|
abandonConnection("initsocketfail");
|
||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
function setUpSocketWhenWindowLoaded() {
|
|
||||||
if (getCollabClient.windowLoaded) {
|
function setUpSocketWhenWindowLoaded()
|
||||||
|
{
|
||||||
|
if (getCollabClient.windowLoaded)
|
||||||
|
{
|
||||||
setUpSocket();
|
setUpSocket();
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
setTimeout(setUpSocketWhenWindowLoaded, 200);
|
setTimeout(setUpSocketWhenWindowLoaded, 200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setTimeout(setUpSocketWhenWindowLoaded, 0);
|
setTimeout(setUpSocketWhenWindowLoaded, 0);
|
||||||
|
|
||||||
var hiccupCount = 0;
|
var hiccupCount = 0;
|
||||||
function handleCometHiccup(params) {
|
|
||||||
|
function handleCometHiccup(params)
|
||||||
|
{
|
||||||
dmesg("HICCUP (connected:" + ( !! params.connected) + ")");
|
dmesg("HICCUP (connected:" + ( !! params.connected) + ")");
|
||||||
var connectedNow = params.connected;
|
var connectedNow = params.connected;
|
||||||
if (! connectedNow) {
|
if (!connectedNow)
|
||||||
|
{
|
||||||
hiccupCount++;
|
hiccupCount++;
|
||||||
// skip first "cut off from server" notification
|
// skip first "cut off from server" notification
|
||||||
if (hiccupCount > 1) {
|
if (hiccupCount > 1)
|
||||||
|
{
|
||||||
setChannelState("RECONNECTING");
|
setChannelState("RECONNECTING");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
hiccupCount = 0;
|
hiccupCount = 0;
|
||||||
setChannelState("CONNECTED");
|
setChannelState("CONNECTED");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendMessage(msg) {
|
function sendMessage(msg)
|
||||||
socket.json.send({type: "COLLABROOM", component: "pad", data: msg});
|
{
|
||||||
|
socket.json.send(
|
||||||
|
{
|
||||||
|
type: "COLLABROOM",
|
||||||
|
component: "pad",
|
||||||
|
data: msg
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function wrapRecordingErrors(catcher, func) {
|
function wrapRecordingErrors(catcher, func)
|
||||||
return function() {
|
{
|
||||||
try {
|
return function()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
return func.apply(this, Array.prototype.slice.call(arguments));
|
return func.apply(this, Array.prototype.slice.call(arguments));
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e)
|
||||||
|
{
|
||||||
caughtErrors.push(e);
|
caughtErrors.push(e);
|
||||||
caughtErrorCatchers.push(catcher);
|
caughtErrorCatchers.push(catcher);
|
||||||
caughtErrorTimes.push(+new Date());
|
caughtErrorTimes.push(+new Date());
|
||||||
|
@ -288,28 +337,34 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function callCatchingErrors(catcher, func) {
|
function callCatchingErrors(catcher, func)
|
||||||
try {
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
wrapRecordingErrors(catcher, func)();
|
wrapRecordingErrors(catcher, func)();
|
||||||
}
|
}
|
||||||
catch (e) { /*absorb*/ }
|
catch (e)
|
||||||
|
{ /*absorb*/
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleMessageFromServer(evt) {
|
function handleMessageFromServer(evt)
|
||||||
if(window.console)
|
{
|
||||||
console.log(evt);
|
if (window.console) console.log(evt);
|
||||||
|
|
||||||
if (!socket) return;
|
if (!socket) return;
|
||||||
if (!evt.data) return;
|
if (!evt.data) return;
|
||||||
var wrapper = evt;
|
var wrapper = evt;
|
||||||
if (wrapper.type != "COLLABROOM") return;
|
if (wrapper.type != "COLLABROOM") return;
|
||||||
var msg = wrapper.data;
|
var msg = wrapper.data;
|
||||||
if (msg.type == "NEW_CHANGES") {
|
if (msg.type == "NEW_CHANGES")
|
||||||
|
{
|
||||||
var newRev = msg.newRev;
|
var newRev = msg.newRev;
|
||||||
var changeset = msg.changeset;
|
var changeset = msg.changeset;
|
||||||
var author = (msg.author || '');
|
var author = (msg.author || '');
|
||||||
var apool = msg.apool;
|
var apool = msg.apool;
|
||||||
if (newRev != (rev+1)) {
|
if (newRev != (rev + 1))
|
||||||
|
{
|
||||||
dmesg("bad message revision on NEW_CHANGES: " + newRev + " not " + (rev + 1));
|
dmesg("bad message revision on NEW_CHANGES: " + newRev + " not " + (rev + 1));
|
||||||
socket.disconnect();
|
socket.disconnect();
|
||||||
return;
|
return;
|
||||||
|
@ -317,9 +372,11 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) {
|
||||||
rev = newRev;
|
rev = newRev;
|
||||||
editor.applyChangesToBase(changeset, author, apool);
|
editor.applyChangesToBase(changeset, author, apool);
|
||||||
}
|
}
|
||||||
else if (msg.type == "ACCEPT_COMMIT") {
|
else if (msg.type == "ACCEPT_COMMIT")
|
||||||
|
{
|
||||||
var newRev = msg.newRev;
|
var newRev = msg.newRev;
|
||||||
if (newRev != (rev+1)) {
|
if (newRev != (rev + 1))
|
||||||
|
{
|
||||||
dmesg("bad message revision on ACCEPT_COMMIT: " + newRev + " not " + (rev + 1));
|
dmesg("bad message revision on ACCEPT_COMMIT: " + newRev + " not " + (rev + 1));
|
||||||
socket.disconnect();
|
socket.disconnect();
|
||||||
return;
|
return;
|
||||||
|
@ -327,108 +384,148 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) {
|
||||||
rev = newRev;
|
rev = newRev;
|
||||||
editor.applyPreparedChangesetToBase();
|
editor.applyPreparedChangesetToBase();
|
||||||
setStateIdle();
|
setStateIdle();
|
||||||
callCatchingErrors("onInternalAction", function() {
|
callCatchingErrors("onInternalAction", function()
|
||||||
|
{
|
||||||
callbacks.onInternalAction("commitAcceptedByServer");
|
callbacks.onInternalAction("commitAcceptedByServer");
|
||||||
});
|
});
|
||||||
callCatchingErrors("onConnectionTrouble", function() {
|
callCatchingErrors("onConnectionTrouble", function()
|
||||||
|
{
|
||||||
callbacks.onConnectionTrouble("OK");
|
callbacks.onConnectionTrouble("OK");
|
||||||
});
|
});
|
||||||
handleUserChanges();
|
handleUserChanges();
|
||||||
}
|
}
|
||||||
else if (msg.type == "NO_COMMIT_PENDING") {
|
else if (msg.type == "NO_COMMIT_PENDING")
|
||||||
if (state == "COMMITTING") {
|
{
|
||||||
|
if (state == "COMMITTING")
|
||||||
|
{
|
||||||
// server missed our commit message; abort that commit
|
// server missed our commit message; abort that commit
|
||||||
setStateIdle();
|
setStateIdle();
|
||||||
handleUserChanges();
|
handleUserChanges();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (msg.type == "USER_NEWINFO") {
|
else if (msg.type == "USER_NEWINFO")
|
||||||
|
{
|
||||||
var userInfo = msg.userInfo;
|
var userInfo = msg.userInfo;
|
||||||
var id = userInfo.userId;
|
var id = userInfo.userId;
|
||||||
if (userSet[id]) {
|
if (userSet[id])
|
||||||
|
{
|
||||||
userSet[id] = userInfo;
|
userSet[id] = userInfo;
|
||||||
callbacks.onUpdateUserInfo(userInfo);
|
callbacks.onUpdateUserInfo(userInfo);
|
||||||
dmesgUsers();
|
dmesgUsers();
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
userSet[id] = userInfo;
|
userSet[id] = userInfo;
|
||||||
callbacks.onUserJoin(userInfo);
|
callbacks.onUserJoin(userInfo);
|
||||||
dmesgUsers();
|
dmesgUsers();
|
||||||
}
|
}
|
||||||
tellAceActiveAuthorInfo(userInfo);
|
tellAceActiveAuthorInfo(userInfo);
|
||||||
}
|
}
|
||||||
else if (msg.type == "USER_LEAVE") {
|
else if (msg.type == "USER_LEAVE")
|
||||||
|
{
|
||||||
var userInfo = msg.userInfo;
|
var userInfo = msg.userInfo;
|
||||||
var id = userInfo.userId;
|
var id = userInfo.userId;
|
||||||
if (userSet[id]) {
|
if (userSet[id])
|
||||||
|
{
|
||||||
delete userSet[userInfo.userId];
|
delete userSet[userInfo.userId];
|
||||||
fadeAceAuthorInfo(userInfo);
|
fadeAceAuthorInfo(userInfo);
|
||||||
callbacks.onUserLeave(userInfo);
|
callbacks.onUserLeave(userInfo);
|
||||||
dmesgUsers();
|
dmesgUsers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (msg.type == "DISCONNECT_REASON") {
|
else if (msg.type == "DISCONNECT_REASON")
|
||||||
|
{
|
||||||
appLevelDisconnectReason = msg.reason;
|
appLevelDisconnectReason = msg.reason;
|
||||||
}
|
}
|
||||||
else if (msg.type == "CLIENT_MESSAGE") {
|
else if (msg.type == "CLIENT_MESSAGE")
|
||||||
|
{
|
||||||
callbacks.onClientMessage(msg.payload);
|
callbacks.onClientMessage(msg.payload);
|
||||||
}
|
}
|
||||||
else if (msg.type == "SERVER_MESSAGE") {
|
else if (msg.type == "SERVER_MESSAGE")
|
||||||
|
{
|
||||||
callbacks.onServerMessage(msg.payload);
|
callbacks.onServerMessage(msg.payload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function updateUserInfo(userInfo) {
|
|
||||||
|
function updateUserInfo(userInfo)
|
||||||
|
{
|
||||||
userInfo.userId = userId;
|
userInfo.userId = userId;
|
||||||
userSet[userId] = userInfo;
|
userSet[userId] = userInfo;
|
||||||
tellAceActiveAuthorInfo(userInfo);
|
tellAceActiveAuthorInfo(userInfo);
|
||||||
if (!socket) return;
|
if (!socket) return;
|
||||||
sendMessage({type: "USERINFO_UPDATE", userInfo:userInfo});
|
sendMessage(
|
||||||
|
{
|
||||||
|
type: "USERINFO_UPDATE",
|
||||||
|
userInfo: userInfo
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function tellAceActiveAuthorInfo(userInfo) {
|
function tellAceActiveAuthorInfo(userInfo)
|
||||||
|
{
|
||||||
tellAceAuthorInfo(userInfo.userId, userInfo.colorId);
|
tellAceAuthorInfo(userInfo.userId, userInfo.colorId);
|
||||||
}
|
}
|
||||||
function tellAceAuthorInfo(userId, colorId, inactive) {
|
|
||||||
if (colorId || (typeof colorId) == "number") {
|
function tellAceAuthorInfo(userId, colorId, inactive)
|
||||||
|
{
|
||||||
|
if (colorId || (typeof colorId) == "number")
|
||||||
|
{
|
||||||
colorId = Number(colorId);
|
colorId = Number(colorId);
|
||||||
if (options && options.colorPalette && options.colorPalette[colorId]) {
|
if (options && options.colorPalette && options.colorPalette[colorId])
|
||||||
|
{
|
||||||
var cssColor = options.colorPalette[colorId];
|
var cssColor = options.colorPalette[colorId];
|
||||||
if (inactive) {
|
if (inactive)
|
||||||
editor.setAuthorInfo(userId, {bgcolor: cssColor, fade: 0.5});
|
{
|
||||||
|
editor.setAuthorInfo(userId, {
|
||||||
|
bgcolor: cssColor,
|
||||||
|
fade: 0.5
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
editor.setAuthorInfo(userId, {bgcolor: cssColor});
|
{
|
||||||
|
editor.setAuthorInfo(userId, {
|
||||||
|
bgcolor: cssColor
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function fadeAceAuthorInfo(userInfo) {
|
|
||||||
|
function fadeAceAuthorInfo(userInfo)
|
||||||
|
{
|
||||||
tellAceAuthorInfo(userInfo.userId, userInfo.colorId, true);
|
tellAceAuthorInfo(userInfo.userId, userInfo.colorId, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getConnectedUsers() {
|
function getConnectedUsers()
|
||||||
|
{
|
||||||
return valuesArray(userSet);
|
return valuesArray(userSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
function tellAceAboutHistoricalAuthors(hadata) {
|
function tellAceAboutHistoricalAuthors(hadata)
|
||||||
for(var author in hadata) {
|
{
|
||||||
|
for (var author in hadata)
|
||||||
|
{
|
||||||
var data = hadata[author];
|
var data = hadata[author];
|
||||||
if (! userSet[author]) {
|
if (!userSet[author])
|
||||||
|
{
|
||||||
tellAceAuthorInfo(author, data.colorId, true);
|
tellAceAuthorInfo(author, data.colorId, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function dmesgUsers() {
|
function dmesgUsers()
|
||||||
|
{
|
||||||
//pad.dmesg($.map(getConnectedUsers(), function(u) { return u.userId.slice(-2); }).join(','));
|
//pad.dmesg($.map(getConnectedUsers(), function(u) { return u.userId.slice(-2); }).join(','));
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSocketClosed(params) {
|
function handleSocketClosed(params)
|
||||||
|
{
|
||||||
socket = null;
|
socket = null;
|
||||||
|
|
||||||
$.each(keys(userSet), function() {
|
$.each(keys(userSet), function()
|
||||||
|
{
|
||||||
var uid = String(this);
|
var uid = String(this);
|
||||||
if (uid != userId) {
|
if (uid != userId)
|
||||||
|
{
|
||||||
var userInfo = userSet[uid];
|
var userInfo = userSet[uid];
|
||||||
delete userSet[uid];
|
delete userSet[uid];
|
||||||
callbacks.onUserLeave(userInfo);
|
callbacks.onUserLeave(userInfo);
|
||||||
|
@ -438,120 +535,201 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) {
|
||||||
|
|
||||||
var reason = appLevelDisconnectReason || params.reason;
|
var reason = appLevelDisconnectReason || params.reason;
|
||||||
var shouldReconnect = params.reconnect;
|
var shouldReconnect = params.reconnect;
|
||||||
if (shouldReconnect) {
|
if (shouldReconnect)
|
||||||
|
{
|
||||||
|
|
||||||
// determine if this is a tight reconnect loop due to weird connectivity problems
|
// determine if this is a tight reconnect loop due to weird connectivity problems
|
||||||
reconnectTimes.push(+new Date());
|
reconnectTimes.push(+new Date());
|
||||||
var TOO_MANY_RECONNECTS = 8;
|
var TOO_MANY_RECONNECTS = 8;
|
||||||
var TOO_SHORT_A_TIME_MS = 10000;
|
var TOO_SHORT_A_TIME_MS = 10000;
|
||||||
if (reconnectTimes.length >= TOO_MANY_RECONNECTS &&
|
if (reconnectTimes.length >= TOO_MANY_RECONNECTS && ((+new Date()) - reconnectTimes[reconnectTimes.length - TOO_MANY_RECONNECTS]) < TOO_SHORT_A_TIME_MS)
|
||||||
((+new Date()) - reconnectTimes[reconnectTimes.length-TOO_MANY_RECONNECTS]) <
|
{
|
||||||
TOO_SHORT_A_TIME_MS) {
|
|
||||||
setChannelState("DISCONNECTED", "looping");
|
setChannelState("DISCONNECTED", "looping");
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
setChannelState("RECONNECTING", reason);
|
setChannelState("RECONNECTING", reason);
|
||||||
setUpSocket();
|
setUpSocket();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
setChannelState("DISCONNECTED", reason);
|
setChannelState("DISCONNECTED", reason);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setChannelState(newChannelState, moreInfo) {
|
function setChannelState(newChannelState, moreInfo)
|
||||||
if (newChannelState != channelState) {
|
{
|
||||||
|
if (newChannelState != channelState)
|
||||||
|
{
|
||||||
channelState = newChannelState;
|
channelState = newChannelState;
|
||||||
callbacks.onChannelStateChange(channelState, moreInfo);
|
callbacks.onChannelStateChange(channelState, moreInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function keys(obj) {
|
function keys(obj)
|
||||||
|
{
|
||||||
var array = [];
|
var array = [];
|
||||||
$.each(obj, function (k, v) { array.push(k); });
|
$.each(obj, function(k, v)
|
||||||
|
{
|
||||||
|
array.push(k);
|
||||||
|
});
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
function valuesArray(obj) {
|
|
||||||
|
function valuesArray(obj)
|
||||||
|
{
|
||||||
var array = [];
|
var array = [];
|
||||||
$.each(obj, function (k, v) { array.push(v); });
|
$.each(obj, function(k, v)
|
||||||
|
{
|
||||||
|
array.push(v);
|
||||||
|
});
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to present a working interface even before the socket
|
// We need to present a working interface even before the socket
|
||||||
// is connected for the first time.
|
// is connected for the first time.
|
||||||
var deferredActions = [];
|
var deferredActions = [];
|
||||||
function defer(func, tag) {
|
|
||||||
return function() {
|
function defer(func, tag)
|
||||||
|
{
|
||||||
|
return function()
|
||||||
|
{
|
||||||
var that = this;
|
var that = this;
|
||||||
var args = arguments;
|
var args = arguments;
|
||||||
function action() {
|
|
||||||
|
function action()
|
||||||
|
{
|
||||||
func.apply(that, args);
|
func.apply(that, args);
|
||||||
}
|
}
|
||||||
action.tag = tag;
|
action.tag = tag;
|
||||||
if (channelState == "CONNECTING") {
|
if (channelState == "CONNECTING")
|
||||||
|
{
|
||||||
deferredActions.push(action);
|
deferredActions.push(action);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
action();
|
action();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function doDeferredActions(tag) {
|
|
||||||
|
function doDeferredActions(tag)
|
||||||
|
{
|
||||||
var newArray = [];
|
var newArray = [];
|
||||||
for(var i=0;i<deferredActions.length;i++) {
|
for (var i = 0; i < deferredActions.length; i++)
|
||||||
|
{
|
||||||
var a = deferredActions[i];
|
var a = deferredActions[i];
|
||||||
if ((!tag) || (tag == a.tag)) {
|
if ((!tag) || (tag == a.tag))
|
||||||
|
{
|
||||||
a();
|
a();
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
newArray.push(a);
|
newArray.push(a);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
deferredActions = newArray;
|
deferredActions = newArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendClientMessage(msg) {
|
function sendClientMessage(msg)
|
||||||
sendMessage({ type: "CLIENT_MESSAGE", payload: msg });
|
{
|
||||||
|
sendMessage(
|
||||||
|
{
|
||||||
|
type: "CLIENT_MESSAGE",
|
||||||
|
payload: msg
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCurrentRevisionNumber() {
|
function getCurrentRevisionNumber()
|
||||||
|
{
|
||||||
return rev;
|
return rev;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDiagnosticInfo() {
|
function getDiagnosticInfo()
|
||||||
|
{
|
||||||
var maxCaughtErrors = 3;
|
var maxCaughtErrors = 3;
|
||||||
var maxAceErrors = 3;
|
var maxAceErrors = 3;
|
||||||
var maxDebugMessages = 50;
|
var maxDebugMessages = 50;
|
||||||
var longStringCutoff = 500;
|
var longStringCutoff = 500;
|
||||||
|
|
||||||
function trunc(str) {
|
function trunc(str)
|
||||||
|
{
|
||||||
return String(str).substring(0, longStringCutoff);
|
return String(str).substring(0, longStringCutoff);
|
||||||
}
|
}
|
||||||
|
|
||||||
var info = { errors: {length: 0} };
|
var info = {
|
||||||
function addError(e, catcher, time) {
|
errors: {
|
||||||
var error = {catcher:catcher};
|
length: 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function addError(e, catcher, time)
|
||||||
|
{
|
||||||
|
var error = {
|
||||||
|
catcher: catcher
|
||||||
|
};
|
||||||
if (time) error.time = time;
|
if (time) error.time = time;
|
||||||
|
|
||||||
// a little over-cautious?
|
// a little over-cautious?
|
||||||
try { if (e.description) error.description = e.description; } catch (x) {}
|
try
|
||||||
try { if (e.fileName) error.fileName = e.fileName; } catch (x) {}
|
{
|
||||||
try { if (e.lineNumber) error.lineNumber = e.lineNumber; } catch (x) {}
|
if (e.description) error.description = e.description;
|
||||||
try { if (e.message) error.message = e.message; } catch (x) {}
|
}
|
||||||
try { if (e.name) error.name = e.name; } catch (x) {}
|
catch (x)
|
||||||
try { if (e.number) error.number = e.number; } catch (x) {}
|
{}
|
||||||
try { if (e.stack) error.stack = trunc(e.stack); } catch (x) {}
|
try
|
||||||
|
{
|
||||||
|
if (e.fileName) error.fileName = e.fileName;
|
||||||
|
}
|
||||||
|
catch (x)
|
||||||
|
{}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (e.lineNumber) error.lineNumber = e.lineNumber;
|
||||||
|
}
|
||||||
|
catch (x)
|
||||||
|
{}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (e.message) error.message = e.message;
|
||||||
|
}
|
||||||
|
catch (x)
|
||||||
|
{}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (e.name) error.name = e.name;
|
||||||
|
}
|
||||||
|
catch (x)
|
||||||
|
{}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (e.number) error.number = e.number;
|
||||||
|
}
|
||||||
|
catch (x)
|
||||||
|
{}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (e.stack) error.stack = trunc(e.stack);
|
||||||
|
}
|
||||||
|
catch (x)
|
||||||
|
{}
|
||||||
|
|
||||||
info.errors[info.errors.length] = error;
|
info.errors[info.errors.length] = error;
|
||||||
info.errors.length++;
|
info.errors.length++;
|
||||||
}
|
}
|
||||||
for(var i=0; ((i<caughtErrors.length) && (i<maxCaughtErrors)); i++) {
|
for (var i = 0;
|
||||||
|
((i < caughtErrors.length) && (i < maxCaughtErrors)); i++)
|
||||||
|
{
|
||||||
addError(caughtErrors[i], caughtErrorCatchers[i], caughtErrorTimes[i]);
|
addError(caughtErrors[i], caughtErrorCatchers[i], caughtErrorTimes[i]);
|
||||||
}
|
}
|
||||||
if (editor) {
|
if (editor)
|
||||||
|
{
|
||||||
var aceErrors = editor.getUnhandledErrors();
|
var aceErrors = editor.getUnhandledErrors();
|
||||||
for(var i=0; ((i<aceErrors.length) && (i<maxAceErrors)) ;i++) {
|
for (var i = 0;
|
||||||
|
((i < aceErrors.length) && (i < maxAceErrors)); i++)
|
||||||
|
{
|
||||||
var errorRecord = aceErrors[i];
|
var errorRecord = aceErrors[i];
|
||||||
addError(errorRecord.error, "ACE", errorRecord.time);
|
addError(errorRecord.error, "ACE", errorRecord.time);
|
||||||
}
|
}
|
||||||
|
@ -564,21 +742,26 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) {
|
||||||
info.numSocketReconnects = reconnectTimes.length;
|
info.numSocketReconnects = reconnectTimes.length;
|
||||||
info.userId = userId;
|
info.userId = userId;
|
||||||
info.currentRev = rev;
|
info.currentRev = rev;
|
||||||
info.participants = (function() {
|
info.participants = (function()
|
||||||
|
{
|
||||||
var pp = [];
|
var pp = [];
|
||||||
for(var u in userSet) {
|
for (var u in userSet)
|
||||||
|
{
|
||||||
pp.push(u);
|
pp.push(u);
|
||||||
}
|
}
|
||||||
return pp.join(',');
|
return pp.join(',');
|
||||||
})();
|
})();
|
||||||
|
|
||||||
if (debugMessages.length > maxDebugMessages) {
|
if (debugMessages.length > maxDebugMessages)
|
||||||
debugMessages = debugMessages.slice(debugMessages.length-maxDebugMessages,
|
{
|
||||||
debugMessages.length);
|
debugMessages = debugMessages.slice(debugMessages.length - maxDebugMessages, debugMessages.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
info.debugMessages = {length: 0};
|
info.debugMessages = {
|
||||||
for(var i=0;i<debugMessages.length;i++) {
|
length: 0
|
||||||
|
};
|
||||||
|
for (var i = 0; i < debugMessages.length; i++)
|
||||||
|
{
|
||||||
info.debugMessages[i] = trunc(debugMessages[i]);
|
info.debugMessages[i] = trunc(debugMessages[i]);
|
||||||
info.debugMessages.length++;
|
info.debugMessages.length++;
|
||||||
}
|
}
|
||||||
|
@ -586,40 +769,50 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) {
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMissedChanges() {
|
function getMissedChanges()
|
||||||
|
{
|
||||||
var obj = {};
|
var obj = {};
|
||||||
obj.userInfo = userSet[userId];
|
obj.userInfo = userSet[userId];
|
||||||
obj.baseRev = rev;
|
obj.baseRev = rev;
|
||||||
if (state == "COMMITTING" && stateMessage) {
|
if (state == "COMMITTING" && stateMessage)
|
||||||
|
{
|
||||||
obj.committedChangeset = stateMessage.changeset;
|
obj.committedChangeset = stateMessage.changeset;
|
||||||
obj.committedChangesetAPool = stateMessage.apool;
|
obj.committedChangesetAPool = stateMessage.apool;
|
||||||
obj.committedChangesetSocketId = stateMessageSocketId;
|
obj.committedChangesetSocketId = stateMessageSocketId;
|
||||||
editor.applyPreparedChangesetToBase();
|
editor.applyPreparedChangesetToBase();
|
||||||
}
|
}
|
||||||
var userChangesData = editor.prepareUserChangeset();
|
var userChangesData = editor.prepareUserChangeset();
|
||||||
if (userChangesData.changeset) {
|
if (userChangesData.changeset)
|
||||||
|
{
|
||||||
obj.furtherChangeset = userChangesData.changeset;
|
obj.furtherChangeset = userChangesData.changeset;
|
||||||
obj.furtherChangesetAPool = userChangesData.apool;
|
obj.furtherChangesetAPool = userChangesData.apool;
|
||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setStateIdle() {
|
function setStateIdle()
|
||||||
|
{
|
||||||
state = "IDLE";
|
state = "IDLE";
|
||||||
callbacks.onInternalAction("newlyIdle");
|
callbacks.onInternalAction("newlyIdle");
|
||||||
schedulePerhapsCallIdleFuncs();
|
schedulePerhapsCallIdleFuncs();
|
||||||
}
|
}
|
||||||
|
|
||||||
function callWhenNotCommitting(func) {
|
function callWhenNotCommitting(func)
|
||||||
|
{
|
||||||
idleFuncs.push(func);
|
idleFuncs.push(func);
|
||||||
schedulePerhapsCallIdleFuncs();
|
schedulePerhapsCallIdleFuncs();
|
||||||
}
|
}
|
||||||
|
|
||||||
var idleFuncs = [];
|
var idleFuncs = [];
|
||||||
function schedulePerhapsCallIdleFuncs() {
|
|
||||||
setTimeout(function() {
|
function schedulePerhapsCallIdleFuncs()
|
||||||
if (state == "IDLE") {
|
{
|
||||||
while (idleFuncs.length > 0) {
|
setTimeout(function()
|
||||||
|
{
|
||||||
|
if (state == "IDLE")
|
||||||
|
{
|
||||||
|
while (idleFuncs.length > 0)
|
||||||
|
{
|
||||||
var f = idleFuncs.shift();
|
var f = idleFuncs.shift();
|
||||||
f();
|
f();
|
||||||
}
|
}
|
||||||
|
@ -629,14 +822,38 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) {
|
||||||
|
|
||||||
var self;
|
var self;
|
||||||
return (self = {
|
return (self = {
|
||||||
setOnUserJoin: function(cb) { callbacks.onUserJoin = cb; },
|
setOnUserJoin: function(cb)
|
||||||
setOnUserLeave: function(cb) { callbacks.onUserLeave = cb; },
|
{
|
||||||
setOnUpdateUserInfo: function(cb) { callbacks.onUpdateUserInfo = cb; },
|
callbacks.onUserJoin = cb;
|
||||||
setOnChannelStateChange: function(cb) { callbacks.onChannelStateChange = cb; },
|
},
|
||||||
setOnClientMessage: function(cb) { callbacks.onClientMessage = cb; },
|
setOnUserLeave: function(cb)
|
||||||
setOnInternalAction: function(cb) { callbacks.onInternalAction = cb; },
|
{
|
||||||
setOnConnectionTrouble: function(cb) { callbacks.onConnectionTrouble = cb; },
|
callbacks.onUserLeave = cb;
|
||||||
setOnServerMessage: function(cb) { callbacks.onServerMessage = cb; },
|
},
|
||||||
|
setOnUpdateUserInfo: function(cb)
|
||||||
|
{
|
||||||
|
callbacks.onUpdateUserInfo = cb;
|
||||||
|
},
|
||||||
|
setOnChannelStateChange: function(cb)
|
||||||
|
{
|
||||||
|
callbacks.onChannelStateChange = cb;
|
||||||
|
},
|
||||||
|
setOnClientMessage: function(cb)
|
||||||
|
{
|
||||||
|
callbacks.onClientMessage = cb;
|
||||||
|
},
|
||||||
|
setOnInternalAction: function(cb)
|
||||||
|
{
|
||||||
|
callbacks.onInternalAction = cb;
|
||||||
|
},
|
||||||
|
setOnConnectionTrouble: function(cb)
|
||||||
|
{
|
||||||
|
callbacks.onConnectionTrouble = cb;
|
||||||
|
},
|
||||||
|
setOnServerMessage: function(cb)
|
||||||
|
{
|
||||||
|
callbacks.onServerMessage = cb;
|
||||||
|
},
|
||||||
updateUserInfo: defer(updateUserInfo),
|
updateUserInfo: defer(updateUserInfo),
|
||||||
handleMessageFromServer: handleMessageFromServer,
|
handleMessageFromServer: handleMessageFromServer,
|
||||||
getConnectedUsers: getConnectedUsers,
|
getConnectedUsers: getConnectedUsers,
|
||||||
|
@ -649,16 +866,21 @@ function getCollabClient(ace2editor, serverVars, initialUserInfo, options) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectElementContents(elem) {
|
function selectElementContents(elem)
|
||||||
if ($.browser.msie) {
|
{
|
||||||
|
if ($.browser.msie)
|
||||||
|
{
|
||||||
var range = document.body.createTextRange();
|
var range = document.body.createTextRange();
|
||||||
range.moveToElementText(elem);
|
range.moveToElementText(elem);
|
||||||
range.select();
|
range.select();
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
if (window.getSelection) {
|
{
|
||||||
|
if (window.getSelection)
|
||||||
|
{
|
||||||
var browserSelection = window.getSelection();
|
var browserSelection = window.getSelection();
|
||||||
if (browserSelection) {
|
if (browserSelection)
|
||||||
|
{
|
||||||
var range = document.createRange();
|
var range = document.createRange();
|
||||||
range.selectNodeContents(elem);
|
range.selectNodeContents(elem);
|
||||||
browserSelection.removeAllRanges();
|
browserSelection.removeAllRanges();
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// DO NOT EDIT THIS FILE, edit infrastructure/ace/www/colorutils.js
|
// DO NOT EDIT THIS FILE, edit infrastructure/ace/www/colorutils.js
|
||||||
// THIS FILE IS ALSO SERVED AS CLIENT-SIDE JS
|
// THIS FILE IS ALSO SERVED AS CLIENT-SIDE JS
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copyright 2009 Google Inc.
|
* Copyright 2009 Google Inc.
|
||||||
*
|
*
|
||||||
|
@ -20,20 +19,23 @@
|
||||||
var colorutils = {};
|
var colorutils = {};
|
||||||
|
|
||||||
// "#ffffff" or "#fff" or "ffffff" or "fff" to [1.0, 1.0, 1.0]
|
// "#ffffff" or "#fff" or "ffffff" or "fff" to [1.0, 1.0, 1.0]
|
||||||
colorutils.css2triple = function(cssColor) {
|
colorutils.css2triple = function(cssColor)
|
||||||
|
{
|
||||||
var sixHex = colorutils.css2sixhex(cssColor);
|
var sixHex = colorutils.css2sixhex(cssColor);
|
||||||
function hexToFloat(hh) {
|
|
||||||
|
function hexToFloat(hh)
|
||||||
|
{
|
||||||
return Number("0x" + hh) / 255;
|
return Number("0x" + hh) / 255;
|
||||||
}
|
}
|
||||||
return [hexToFloat(sixHex.substr(0,2)),
|
return [hexToFloat(sixHex.substr(0, 2)), hexToFloat(sixHex.substr(2, 2)), hexToFloat(sixHex.substr(4, 2))];
|
||||||
hexToFloat(sixHex.substr(2,2)),
|
|
||||||
hexToFloat(sixHex.substr(4,2))];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// "#ffffff" or "#fff" or "ffffff" or "fff" to "ffffff"
|
// "#ffffff" or "#fff" or "ffffff" or "fff" to "ffffff"
|
||||||
colorutils.css2sixhex = function(cssColor) {
|
colorutils.css2sixhex = function(cssColor)
|
||||||
|
{
|
||||||
var h = /[0-9a-fA-F]+/.exec(cssColor)[0];
|
var h = /[0-9a-fA-F]+/.exec(cssColor)[0];
|
||||||
if (h.length != 6) {
|
if (h.length != 6)
|
||||||
|
{
|
||||||
var a = h.charAt(0);
|
var a = h.charAt(0);
|
||||||
var b = h.charAt(1);
|
var b = h.charAt(1);
|
||||||
var c = h.charAt(2);
|
var c = h.charAt(2);
|
||||||
|
@ -43,50 +45,71 @@ colorutils.css2sixhex = function(cssColor) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// [1.0, 1.0, 1.0] -> "#ffffff"
|
// [1.0, 1.0, 1.0] -> "#ffffff"
|
||||||
colorutils.triple2css = function(triple) {
|
colorutils.triple2css = function(triple)
|
||||||
function floatToHex(n) {
|
{
|
||||||
|
function floatToHex(n)
|
||||||
|
{
|
||||||
var n2 = colorutils.clamp(Math.round(n * 255), 0, 255);
|
var n2 = colorutils.clamp(Math.round(n * 255), 0, 255);
|
||||||
return ("0" + n2.toString(16)).slice(-2);
|
return ("0" + n2.toString(16)).slice(-2);
|
||||||
}
|
}
|
||||||
return "#" + floatToHex(triple[0]) +
|
return "#" + floatToHex(triple[0]) + floatToHex(triple[1]) + floatToHex(triple[2]);
|
||||||
floatToHex(triple[1]) + floatToHex(triple[2]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
colorutils.clamp = function(v,bot,top) { return v < bot ? bot : (v > top ? top : v); };
|
colorutils.clamp = function(v, bot, top)
|
||||||
colorutils.min3 = function(a,b,c) { return (a < b) ? (a < c ? a : c) : (b < c ? b : c); };
|
{
|
||||||
colorutils.max3 = function(a,b,c) { return (a > b) ? (a > c ? a : c) : (b > c ? b : c); };
|
return v < bot ? bot : (v > top ? top : v);
|
||||||
colorutils.colorMin = function(c) { return colorutils.min3(c[0], c[1], c[2]); };
|
};
|
||||||
colorutils.colorMax = function(c) { return colorutils.max3(c[0], c[1], c[2]); };
|
colorutils.min3 = function(a, b, c)
|
||||||
colorutils.scale = function(v, bot, top) { return colorutils.clamp(bot + v*(top-bot), 0, 1); };
|
{
|
||||||
colorutils.unscale = function(v, bot, top) { return colorutils.clamp((v-bot)/(top-bot), 0, 1); };
|
return (a < b) ? (a < c ? a : c) : (b < c ? b : c);
|
||||||
|
};
|
||||||
|
colorutils.max3 = function(a, b, c)
|
||||||
|
{
|
||||||
|
return (a > b) ? (a > c ? a : c) : (b > c ? b : c);
|
||||||
|
};
|
||||||
|
colorutils.colorMin = function(c)
|
||||||
|
{
|
||||||
|
return colorutils.min3(c[0], c[1], c[2]);
|
||||||
|
};
|
||||||
|
colorutils.colorMax = function(c)
|
||||||
|
{
|
||||||
|
return colorutils.max3(c[0], c[1], c[2]);
|
||||||
|
};
|
||||||
|
colorutils.scale = function(v, bot, top)
|
||||||
|
{
|
||||||
|
return colorutils.clamp(bot + v * (top - bot), 0, 1);
|
||||||
|
};
|
||||||
|
colorutils.unscale = function(v, bot, top)
|
||||||
|
{
|
||||||
|
return colorutils.clamp((v - bot) / (top - bot), 0, 1);
|
||||||
|
};
|
||||||
|
|
||||||
colorutils.scaleColor = function(c, bot, top) {
|
colorutils.scaleColor = function(c, bot, top)
|
||||||
return [colorutils.scale(c[0], bot, top),
|
{
|
||||||
colorutils.scale(c[1], bot, top),
|
return [colorutils.scale(c[0], bot, top), colorutils.scale(c[1], bot, top), colorutils.scale(c[2], bot, top)];
|
||||||
colorutils.scale(c[2], bot, top)];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
colorutils.unscaleColor = function(c, bot, top) {
|
colorutils.unscaleColor = function(c, bot, top)
|
||||||
return [colorutils.unscale(c[0], bot, top),
|
{
|
||||||
colorutils.unscale(c[1], bot, top),
|
return [colorutils.unscale(c[0], bot, top), colorutils.unscale(c[1], bot, top), colorutils.unscale(c[2], bot, top)];
|
||||||
colorutils.unscale(c[2], bot, top)];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
colorutils.luminosity = function(c) {
|
colorutils.luminosity = function(c)
|
||||||
|
{
|
||||||
// rule of thumb for RGB brightness; 1.0 is white
|
// rule of thumb for RGB brightness; 1.0 is white
|
||||||
return c[0] * 0.30 + c[1] * 0.59 + c[2] * 0.11;
|
return c[0] * 0.30 + c[1] * 0.59 + c[2] * 0.11;
|
||||||
}
|
}
|
||||||
|
|
||||||
colorutils.saturate = function(c) {
|
colorutils.saturate = function(c)
|
||||||
|
{
|
||||||
var min = colorutils.colorMin(c);
|
var min = colorutils.colorMin(c);
|
||||||
var max = colorutils.colorMax(c);
|
var max = colorutils.colorMax(c);
|
||||||
if (max - min <= 0) return [1.0, 1.0, 1.0];
|
if (max - min <= 0) return [1.0, 1.0, 1.0];
|
||||||
return colorutils.unscaleColor(c, min, max);
|
return colorutils.unscaleColor(c, min, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
colorutils.blend = function(c1, c2, t) {
|
colorutils.blend = function(c1, c2, t)
|
||||||
return [colorutils.scale(t, c1[0], c2[0]),
|
{
|
||||||
colorutils.scale(t, c1[1], c2[1]),
|
return [colorutils.scale(t, c1[0], c2[0]), colorutils.scale(t, c1[1], c2[1]), colorutils.scale(t, c1[2], c2[2])];
|
||||||
colorutils.scale(t, c1[2], c2[2])];
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.contentcollector
|
// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.contentcollector
|
||||||
// %APPJET%: import("etherpad.collab.ace.easysync2.Changeset");
|
// %APPJET%: import("etherpad.collab.ace.easysync2.Changeset");
|
||||||
// %APPJET%: import("etherpad.admin.plugins");
|
// %APPJET%: import("etherpad.admin.plugins");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copyright 2009 Google Inc.
|
* Copyright 2009 Google Inc.
|
||||||
*
|
*
|
||||||
|
@ -20,88 +19,129 @@
|
||||||
|
|
||||||
var _MAX_LIST_LEVEL = 8;
|
var _MAX_LIST_LEVEL = 8;
|
||||||
|
|
||||||
function sanitizeUnicode(s) {
|
function sanitizeUnicode(s)
|
||||||
|
{
|
||||||
return s.replace(/[\uffff\ufffe\ufeff\ufdd0-\ufdef\ud800-\udfff]/g, '?');
|
return s.replace(/[\uffff\ufffe\ufeff\ufdd0-\ufdef\ud800-\udfff]/g, '?');
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeContentCollector(collectStyles, browser, apool, domInterface,
|
function makeContentCollector(collectStyles, browser, apool, domInterface, className2Author)
|
||||||
className2Author) {
|
{
|
||||||
browser = browser || {};
|
browser = browser || {};
|
||||||
|
|
||||||
var plugins_;
|
var plugins_;
|
||||||
if (typeof(plugins)!='undefined') {
|
if (typeof(plugins) != 'undefined')
|
||||||
|
{
|
||||||
plugins_ = plugins;
|
plugins_ = plugins;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
plugins_ = parent.parent.plugins;
|
plugins_ = parent.parent.plugins;
|
||||||
}
|
}
|
||||||
|
|
||||||
var dom = domInterface || {
|
var dom = domInterface || {
|
||||||
isNodeText: function(n) {
|
isNodeText: function(n)
|
||||||
|
{
|
||||||
return (n.nodeType == 3);
|
return (n.nodeType == 3);
|
||||||
},
|
},
|
||||||
nodeTagName: function(n) {
|
nodeTagName: function(n)
|
||||||
|
{
|
||||||
return n.tagName;
|
return n.tagName;
|
||||||
},
|
},
|
||||||
nodeValue: function(n) {
|
nodeValue: function(n)
|
||||||
|
{
|
||||||
return n.nodeValue;
|
return n.nodeValue;
|
||||||
},
|
},
|
||||||
nodeNumChildren: function(n) {
|
nodeNumChildren: function(n)
|
||||||
|
{
|
||||||
return n.childNodes.length;
|
return n.childNodes.length;
|
||||||
},
|
},
|
||||||
nodeChild: function(n, i) {
|
nodeChild: function(n, i)
|
||||||
|
{
|
||||||
return n.childNodes.item(i);
|
return n.childNodes.item(i);
|
||||||
},
|
},
|
||||||
nodeProp: function(n, p) {
|
nodeProp: function(n, p)
|
||||||
|
{
|
||||||
return n[p];
|
return n[p];
|
||||||
},
|
},
|
||||||
nodeAttr: function(n, a) {
|
nodeAttr: function(n, a)
|
||||||
|
{
|
||||||
return n.getAttribute(a);
|
return n.getAttribute(a);
|
||||||
},
|
},
|
||||||
optNodeInnerHTML: function(n) {
|
optNodeInnerHTML: function(n)
|
||||||
|
{
|
||||||
return n.innerHTML;
|
return n.innerHTML;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var _blockElems = { "div":1, "p":1, "pre":1, "li":1 };
|
var _blockElems = {
|
||||||
function isBlockElement(n) {
|
"div": 1,
|
||||||
|
"p": 1,
|
||||||
|
"pre": 1,
|
||||||
|
"li": 1
|
||||||
|
};
|
||||||
|
|
||||||
|
function isBlockElement(n)
|
||||||
|
{
|
||||||
return !!_blockElems[(dom.nodeTagName(n) || "").toLowerCase()];
|
return !!_blockElems[(dom.nodeTagName(n) || "").toLowerCase()];
|
||||||
}
|
}
|
||||||
function textify(str) {
|
|
||||||
|
function textify(str)
|
||||||
|
{
|
||||||
return sanitizeUnicode(
|
return sanitizeUnicode(
|
||||||
str.replace(/[\n\r ]/g, ' ').replace(/\xa0/g, ' ').replace(/\t/g, ' '));
|
str.replace(/[\n\r ]/g, ' ').replace(/\xa0/g, ' ').replace(/\t/g, ' '));
|
||||||
}
|
}
|
||||||
function getAssoc(node, name) {
|
|
||||||
|
function getAssoc(node, name)
|
||||||
|
{
|
||||||
return dom.nodeProp(node, "_magicdom_" + name);
|
return dom.nodeProp(node, "_magicdom_" + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
var lines = (function() {
|
var lines = (function()
|
||||||
|
{
|
||||||
var textArray = [];
|
var textArray = [];
|
||||||
var attribsArray = [];
|
var attribsArray = [];
|
||||||
var attribsBuilder = null;
|
var attribsBuilder = null;
|
||||||
var op = Changeset.newOp('+');
|
var op = Changeset.newOp('+');
|
||||||
var self = {
|
var self = {
|
||||||
length: function() { return textArray.length; },
|
length: function()
|
||||||
atColumnZero: function() {
|
{
|
||||||
|
return textArray.length;
|
||||||
|
},
|
||||||
|
atColumnZero: function()
|
||||||
|
{
|
||||||
return textArray[textArray.length - 1] === "";
|
return textArray[textArray.length - 1] === "";
|
||||||
},
|
},
|
||||||
startNew: function() {
|
startNew: function()
|
||||||
|
{
|
||||||
textArray.push("");
|
textArray.push("");
|
||||||
self.flush(true);
|
self.flush(true);
|
||||||
attribsBuilder = Changeset.smartOpAssembler();
|
attribsBuilder = Changeset.smartOpAssembler();
|
||||||
},
|
},
|
||||||
textOfLine: function(i) { return textArray[i]; },
|
textOfLine: function(i)
|
||||||
appendText: function(txt, attrString) {
|
{
|
||||||
|
return textArray[i];
|
||||||
|
},
|
||||||
|
appendText: function(txt, attrString)
|
||||||
|
{
|
||||||
textArray[textArray.length - 1] += txt;
|
textArray[textArray.length - 1] += txt;
|
||||||
//dmesg(txt+" / "+attrString);
|
//dmesg(txt+" / "+attrString);
|
||||||
op.attribs = attrString;
|
op.attribs = attrString;
|
||||||
op.chars = txt.length;
|
op.chars = txt.length;
|
||||||
attribsBuilder.append(op);
|
attribsBuilder.append(op);
|
||||||
},
|
},
|
||||||
textLines: function() { return textArray.slice(); },
|
textLines: function()
|
||||||
attribLines: function() { return attribsArray; },
|
{
|
||||||
|
return textArray.slice();
|
||||||
|
},
|
||||||
|
attribLines: function()
|
||||||
|
{
|
||||||
|
return attribsArray;
|
||||||
|
},
|
||||||
// call flush only when you're done
|
// call flush only when you're done
|
||||||
flush: function(withNewline) {
|
flush: function(withNewline)
|
||||||
if (attribsBuilder) {
|
{
|
||||||
|
if (attribsBuilder)
|
||||||
|
{
|
||||||
attribsArray.push(attribsBuilder.toString());
|
attribsArray.push(attribsBuilder.toString());
|
||||||
attribsBuilder = null;
|
attribsBuilder = null;
|
||||||
}
|
}
|
||||||
|
@ -111,21 +151,31 @@ function makeContentCollector(collectStyles, browser, apool, domInterface,
|
||||||
return self;
|
return self;
|
||||||
}());
|
}());
|
||||||
var cc = {};
|
var cc = {};
|
||||||
function _ensureColumnZero(state) {
|
|
||||||
if (! lines.atColumnZero()) {
|
function _ensureColumnZero(state)
|
||||||
|
{
|
||||||
|
if (!lines.atColumnZero())
|
||||||
|
{
|
||||||
cc.startNewLine(state);
|
cc.startNewLine(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var selection, startPoint, endPoint;
|
var selection, startPoint, endPoint;
|
||||||
var selStart = [-1,-1], selEnd = [-1,-1];
|
var selStart = [-1, -1],
|
||||||
var blockElems = { "div":1, "p":1, "pre":1 };
|
selEnd = [-1, -1];
|
||||||
function _isEmpty(node, state) {
|
var blockElems = {
|
||||||
|
"div": 1,
|
||||||
|
"p": 1,
|
||||||
|
"pre": 1
|
||||||
|
};
|
||||||
|
|
||||||
|
function _isEmpty(node, state)
|
||||||
|
{
|
||||||
// consider clean blank lines pasted in IE to be empty
|
// consider clean blank lines pasted in IE to be empty
|
||||||
if (dom.nodeNumChildren(node) == 0) return true;
|
if (dom.nodeNumChildren(node) == 0) return true;
|
||||||
if (dom.nodeNumChildren(node) == 1 &&
|
if (dom.nodeNumChildren(node) == 1 && getAssoc(node, "shouldBeEmpty") && dom.optNodeInnerHTML(node) == " " && !getAssoc(node, "unpasted"))
|
||||||
getAssoc(node, "shouldBeEmpty") && dom.optNodeInnerHTML(node) == " "
|
{
|
||||||
&& ! getAssoc(node, "unpasted")) {
|
if (state)
|
||||||
if (state) {
|
{
|
||||||
var child = dom.nodeChild(node, 0);
|
var child = dom.nodeChild(node, 0);
|
||||||
_reachPoint(child, 0, state);
|
_reachPoint(child, 0, state);
|
||||||
_reachPoint(child, 1, state);
|
_reachPoint(child, 1, state);
|
||||||
|
@ -134,85 +184,116 @@ function makeContentCollector(collectStyles, browser, apool, domInterface,
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
function _pointHere(charsAfter, state) {
|
|
||||||
|
function _pointHere(charsAfter, state)
|
||||||
|
{
|
||||||
var ln = lines.length() - 1;
|
var ln = lines.length() - 1;
|
||||||
var chr = lines.textOfLine(ln).length;
|
var chr = lines.textOfLine(ln).length;
|
||||||
if (chr == 0 && state.listType && state.listType != 'none') {
|
if (chr == 0 && state.listType && state.listType != 'none')
|
||||||
|
{
|
||||||
chr += 1; // listMarker
|
chr += 1; // listMarker
|
||||||
}
|
}
|
||||||
chr += charsAfter;
|
chr += charsAfter;
|
||||||
return [ln, chr];
|
return [ln, chr];
|
||||||
}
|
}
|
||||||
function _reachBlockPoint(nd, idx, state) {
|
|
||||||
|
function _reachBlockPoint(nd, idx, state)
|
||||||
|
{
|
||||||
if (!dom.isNodeText(nd)) _reachPoint(nd, idx, state);
|
if (!dom.isNodeText(nd)) _reachPoint(nd, idx, state);
|
||||||
}
|
}
|
||||||
function _reachPoint(nd, idx, state) {
|
|
||||||
if (startPoint && nd == startPoint.node && startPoint.index == idx) {
|
function _reachPoint(nd, idx, state)
|
||||||
|
{
|
||||||
|
if (startPoint && nd == startPoint.node && startPoint.index == idx)
|
||||||
|
{
|
||||||
selStart = _pointHere(0, state);
|
selStart = _pointHere(0, state);
|
||||||
}
|
}
|
||||||
if (endPoint && nd == endPoint.node && endPoint.index == idx) {
|
if (endPoint && nd == endPoint.node && endPoint.index == idx)
|
||||||
|
{
|
||||||
selEnd = _pointHere(0, state);
|
selEnd = _pointHere(0, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cc.incrementFlag = function(state, flagName) {
|
cc.incrementFlag = function(state, flagName)
|
||||||
|
{
|
||||||
state.flags[flagName] = (state.flags[flagName] || 0) + 1;
|
state.flags[flagName] = (state.flags[flagName] || 0) + 1;
|
||||||
}
|
}
|
||||||
cc.decrementFlag = function(state, flagName) {
|
cc.decrementFlag = function(state, flagName)
|
||||||
|
{
|
||||||
state.flags[flagName]--;
|
state.flags[flagName]--;
|
||||||
}
|
}
|
||||||
cc.incrementAttrib = function(state, attribName) {
|
cc.incrementAttrib = function(state, attribName)
|
||||||
if (! state.attribs[attribName]) {
|
{
|
||||||
|
if (!state.attribs[attribName])
|
||||||
|
{
|
||||||
state.attribs[attribName] = 1;
|
state.attribs[attribName] = 1;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
state.attribs[attribName]++;
|
state.attribs[attribName]++;
|
||||||
}
|
}
|
||||||
_recalcAttribString(state);
|
_recalcAttribString(state);
|
||||||
}
|
}
|
||||||
cc.decrementAttrib = function(state, attribName) {
|
cc.decrementAttrib = function(state, attribName)
|
||||||
|
{
|
||||||
state.attribs[attribName]--;
|
state.attribs[attribName]--;
|
||||||
_recalcAttribString(state);
|
_recalcAttribString(state);
|
||||||
}
|
}
|
||||||
function _enterList(state, listType) {
|
|
||||||
|
function _enterList(state, listType)
|
||||||
|
{
|
||||||
var oldListType = state.listType;
|
var oldListType = state.listType;
|
||||||
state.listLevel = (state.listLevel || 0) + 1;
|
state.listLevel = (state.listLevel || 0) + 1;
|
||||||
if (listType != 'none') {
|
if (listType != 'none')
|
||||||
|
{
|
||||||
state.listNesting = (state.listNesting || 0) + 1;
|
state.listNesting = (state.listNesting || 0) + 1;
|
||||||
}
|
}
|
||||||
state.listType = listType;
|
state.listType = listType;
|
||||||
_recalcAttribString(state);
|
_recalcAttribString(state);
|
||||||
return oldListType;
|
return oldListType;
|
||||||
}
|
}
|
||||||
function _exitList(state, oldListType) {
|
|
||||||
|
function _exitList(state, oldListType)
|
||||||
|
{
|
||||||
state.listLevel--;
|
state.listLevel--;
|
||||||
if (state.listType != 'none') {
|
if (state.listType != 'none')
|
||||||
|
{
|
||||||
state.listNesting--;
|
state.listNesting--;
|
||||||
}
|
}
|
||||||
state.listType = oldListType;
|
state.listType = oldListType;
|
||||||
_recalcAttribString(state);
|
_recalcAttribString(state);
|
||||||
}
|
}
|
||||||
function _enterAuthor(state, author) {
|
|
||||||
|
function _enterAuthor(state, author)
|
||||||
|
{
|
||||||
var oldAuthor = state.author;
|
var oldAuthor = state.author;
|
||||||
state.authorLevel = (state.authorLevel || 0) + 1;
|
state.authorLevel = (state.authorLevel || 0) + 1;
|
||||||
state.author = author;
|
state.author = author;
|
||||||
_recalcAttribString(state);
|
_recalcAttribString(state);
|
||||||
return oldAuthor;
|
return oldAuthor;
|
||||||
}
|
}
|
||||||
function _exitAuthor(state, oldAuthor) {
|
|
||||||
|
function _exitAuthor(state, oldAuthor)
|
||||||
|
{
|
||||||
state.authorLevel--;
|
state.authorLevel--;
|
||||||
state.author = oldAuthor;
|
state.author = oldAuthor;
|
||||||
_recalcAttribString(state);
|
_recalcAttribString(state);
|
||||||
}
|
}
|
||||||
function _recalcAttribString(state) {
|
|
||||||
|
function _recalcAttribString(state)
|
||||||
|
{
|
||||||
var lst = [];
|
var lst = [];
|
||||||
for(var a in state.attribs) {
|
for (var a in state.attribs)
|
||||||
if (state.attribs[a]) {
|
{
|
||||||
|
if (state.attribs[a])
|
||||||
|
{
|
||||||
lst.push([a, 'true']);
|
lst.push([a, 'true']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (state.authorLevel > 0) {
|
if (state.authorLevel > 0)
|
||||||
|
{
|
||||||
var authorAttrib = ['author', state.author];
|
var authorAttrib = ['author', state.author];
|
||||||
if (apool.putAttrib(authorAttrib, true) >= 0) {
|
if (apool.putAttrib(authorAttrib, true) >= 0)
|
||||||
|
{
|
||||||
// require that author already be in pool
|
// require that author already be in pool
|
||||||
// (don't add authors from other documents, etc.)
|
// (don't add authors from other documents, etc.)
|
||||||
lst.push(authorAttrib);
|
lst.push(authorAttrib);
|
||||||
|
@ -220,39 +301,53 @@ function makeContentCollector(collectStyles, browser, apool, domInterface,
|
||||||
}
|
}
|
||||||
state.attribString = Changeset.makeAttribsString('+', lst, apool);
|
state.attribString = Changeset.makeAttribsString('+', lst, apool);
|
||||||
}
|
}
|
||||||
function _produceListMarker(state) {
|
|
||||||
lines.appendText('*', Changeset.makeAttribsString(
|
function _produceListMarker(state)
|
||||||
'+', [['list', state.listType],
|
{
|
||||||
['insertorder', 'first']],
|
lines.appendText('*', Changeset.makeAttribsString('+', [
|
||||||
apool));
|
['list', state.listType],
|
||||||
|
['insertorder', 'first']
|
||||||
|
], apool));
|
||||||
}
|
}
|
||||||
cc.startNewLine = function(state) {
|
cc.startNewLine = function(state)
|
||||||
if (state) {
|
{
|
||||||
|
if (state)
|
||||||
|
{
|
||||||
var atBeginningOfLine = lines.textOfLine(lines.length() - 1).length == 0;
|
var atBeginningOfLine = lines.textOfLine(lines.length() - 1).length == 0;
|
||||||
if (atBeginningOfLine && state.listType && state.listType != 'none') {
|
if (atBeginningOfLine && state.listType && state.listType != 'none')
|
||||||
|
{
|
||||||
_produceListMarker(state);
|
_produceListMarker(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lines.startNew();
|
lines.startNew();
|
||||||
}
|
}
|
||||||
cc.notifySelection = function (sel) {
|
cc.notifySelection = function(sel)
|
||||||
if (sel) {
|
{
|
||||||
|
if (sel)
|
||||||
|
{
|
||||||
selection = sel;
|
selection = sel;
|
||||||
startPoint = selection.startPoint;
|
startPoint = selection.startPoint;
|
||||||
endPoint = selection.endPoint;
|
endPoint = selection.endPoint;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
cc.doAttrib = function(state, na) {
|
cc.doAttrib = function(state, na)
|
||||||
|
{
|
||||||
state.localAttribs = (state.localAttribs || []);
|
state.localAttribs = (state.localAttribs || []);
|
||||||
state.localAttribs.push(na);
|
state.localAttribs.push(na);
|
||||||
cc.incrementAttrib(state, na);
|
cc.incrementAttrib(state, na);
|
||||||
};
|
};
|
||||||
cc.collectContent = function (node, state) {
|
cc.collectContent = function(node, state)
|
||||||
if (! state) {
|
{
|
||||||
state = {flags: {/*name -> nesting counter*/},
|
if (!state)
|
||||||
|
{
|
||||||
|
state = {
|
||||||
|
flags: { /*name -> nesting counter*/
|
||||||
|
},
|
||||||
localAttribs: null,
|
localAttribs: null,
|
||||||
attribs: {/*name -> nesting counter*/},
|
attribs: { /*name -> nesting counter*/
|
||||||
attribString: ''};
|
},
|
||||||
|
attribString: ''
|
||||||
|
};
|
||||||
}
|
}
|
||||||
var localAttribs = state.localAttribs;
|
var localAttribs = state.localAttribs;
|
||||||
state.localAttribs = null;
|
state.localAttribs = null;
|
||||||
|
@ -261,37 +356,46 @@ function makeContentCollector(collectStyles, browser, apool, domInterface,
|
||||||
if (isBlock) _ensureColumnZero(state);
|
if (isBlock) _ensureColumnZero(state);
|
||||||
var startLine = lines.length() - 1;
|
var startLine = lines.length() - 1;
|
||||||
_reachBlockPoint(node, 0, state);
|
_reachBlockPoint(node, 0, state);
|
||||||
if (dom.isNodeText(node)) {
|
if (dom.isNodeText(node))
|
||||||
|
{
|
||||||
var txt = dom.nodeValue(node);
|
var txt = dom.nodeValue(node);
|
||||||
var rest = '';
|
var rest = '';
|
||||||
var x = 0; // offset into original text
|
var x = 0; // offset into original text
|
||||||
if (txt.length == 0) {
|
if (txt.length == 0)
|
||||||
if (startPoint && node == startPoint.node) {
|
{
|
||||||
|
if (startPoint && node == startPoint.node)
|
||||||
|
{
|
||||||
selStart = _pointHere(0, state);
|
selStart = _pointHere(0, state);
|
||||||
}
|
}
|
||||||
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)
|
||||||
|
{
|
||||||
var consumed = 0;
|
var consumed = 0;
|
||||||
if (state.flags.preMode) {
|
if (state.flags.preMode)
|
||||||
|
{
|
||||||
var firstLine = txt.split('\n', 1)[0];
|
var firstLine = txt.split('\n', 1)[0];
|
||||||
consumed = firstLine.length + 1;
|
consumed = firstLine.length + 1;
|
||||||
rest = txt.substring(consumed);
|
rest = txt.substring(consumed);
|
||||||
txt = firstLine;
|
txt = firstLine;
|
||||||
}
|
}
|
||||||
else { /* will only run this loop body once */ }
|
else
|
||||||
if (startPoint && node == startPoint.node &&
|
{ /* will only run this loop body once */
|
||||||
startPoint.index-x <= txt.length) {
|
}
|
||||||
|
if (startPoint && node == startPoint.node && startPoint.index - x <= txt.length)
|
||||||
|
{
|
||||||
selStart = _pointHere(startPoint.index - x, state);
|
selStart = _pointHere(startPoint.index - x, state);
|
||||||
}
|
}
|
||||||
if (endPoint && node == endPoint.node &&
|
if (endPoint && node == endPoint.node && endPoint.index - x <= txt.length)
|
||||||
endPoint.index-x <= txt.length) {
|
{
|
||||||
selEnd = _pointHere(endPoint.index - x, state);
|
selEnd = _pointHere(endPoint.index - x, state);
|
||||||
}
|
}
|
||||||
var txt2 = txt;
|
var txt2 = txt;
|
||||||
if ((! state.flags.preMode) && /^[\r\n]*$/.exec(txt)) {
|
if ((!state.flags.preMode) && /^[\r\n]*$/.exec(txt))
|
||||||
|
{
|
||||||
// prevents textnodes containing just "\n" from being significant
|
// prevents textnodes containing just "\n" from being significant
|
||||||
// in safari when pasting text, now that we convert them to
|
// in safari when pasting text, now that we convert them to
|
||||||
// spaces instead of removing them, because in other cases
|
// spaces instead of removing them, because in other cases
|
||||||
|
@ -299,76 +403,95 @@ function makeContentCollector(collectStyles, browser, apool, domInterface,
|
||||||
txt2 = "";
|
txt2 = "";
|
||||||
}
|
}
|
||||||
var atBeginningOfLine = lines.textOfLine(lines.length() - 1).length == 0;
|
var atBeginningOfLine = lines.textOfLine(lines.length() - 1).length == 0;
|
||||||
if (atBeginningOfLine) {
|
if (atBeginningOfLine)
|
||||||
|
{
|
||||||
// newlines in the source mustn't become spaces at beginning of line box
|
// newlines in the source mustn't become spaces at beginning of line box
|
||||||
txt2 = txt2.replace(/^\n*/, '');
|
txt2 = txt2.replace(/^\n*/, '');
|
||||||
}
|
}
|
||||||
if (atBeginningOfLine && state.listType && state.listType != 'none') {
|
if (atBeginningOfLine && state.listType && state.listType != 'none')
|
||||||
|
{
|
||||||
_produceListMarker(state);
|
_produceListMarker(state);
|
||||||
}
|
}
|
||||||
lines.appendText(textify(txt2), state.attribString);
|
lines.appendText(textify(txt2), state.attribString);
|
||||||
x += consumed;
|
x += consumed;
|
||||||
txt = rest;
|
txt = rest;
|
||||||
if (txt.length > 0) {
|
if (txt.length > 0)
|
||||||
|
{
|
||||||
cc.startNewLine(state);
|
cc.startNewLine(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
var tname = (dom.nodeTagName(node) || "").toLowerCase();
|
var tname = (dom.nodeTagName(node) || "").toLowerCase();
|
||||||
if (tname == "br") {
|
if (tname == "br")
|
||||||
|
{
|
||||||
cc.startNewLine(state);
|
cc.startNewLine(state);
|
||||||
}
|
}
|
||||||
else if (tname == "script" || tname == "style") {
|
else if (tname == "script" || tname == "style")
|
||||||
|
{
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
else if (! isEmpty) {
|
else if (!isEmpty)
|
||||||
|
{
|
||||||
var styl = dom.nodeAttr(node, "style");
|
var styl = dom.nodeAttr(node, "style");
|
||||||
var cls = dom.nodeProp(node, "className");
|
var cls = dom.nodeProp(node, "className");
|
||||||
|
|
||||||
var isPre = (tname == "pre");
|
var isPre = (tname == "pre");
|
||||||
if ((! isPre) && browser.safari) {
|
if ((!isPre) && browser.safari)
|
||||||
|
{
|
||||||
isPre = (styl && /\bwhite-space:\s*pre\b/i.exec(styl));
|
isPre = (styl && /\bwhite-space:\s*pre\b/i.exec(styl));
|
||||||
}
|
}
|
||||||
if (isPre) cc.incrementFlag(state, 'preMode');
|
if (isPre) cc.incrementFlag(state, 'preMode');
|
||||||
var oldListTypeOrNull = null;
|
var oldListTypeOrNull = null;
|
||||||
var oldAuthorOrNull = null;
|
var oldAuthorOrNull = null;
|
||||||
if (collectStyles) {
|
if (collectStyles)
|
||||||
plugins_.callHook('collectContentPre', {cc: cc, state:state, tname:tname, styl:styl, cls:cls});
|
{
|
||||||
if (tname == "b" || (styl && /\bfont-weight:\s*bold\b/i.exec(styl)) ||
|
plugins_.callHook('collectContentPre', {
|
||||||
tname == "strong") {
|
cc: cc,
|
||||||
|
state: state,
|
||||||
|
tname: tname,
|
||||||
|
styl: styl,
|
||||||
|
cls: cls
|
||||||
|
});
|
||||||
|
if (tname == "b" || (styl && /\bfont-weight:\s*bold\b/i.exec(styl)) || tname == "strong")
|
||||||
|
{
|
||||||
cc.doAttrib(state, "bold");
|
cc.doAttrib(state, "bold");
|
||||||
}
|
}
|
||||||
if (tname == "i" || (styl && /\bfont-style:\s*italic\b/i.exec(styl)) ||
|
if (tname == "i" || (styl && /\bfont-style:\s*italic\b/i.exec(styl)) || tname == "em")
|
||||||
tname == "em") {
|
{
|
||||||
cc.doAttrib(state, "italic");
|
cc.doAttrib(state, "italic");
|
||||||
}
|
}
|
||||||
if (tname == "u" || (styl && /\btext-decoration:\s*underline\b/i.exec(styl)) ||
|
if (tname == "u" || (styl && /\btext-decoration:\s*underline\b/i.exec(styl)) || tname == "ins")
|
||||||
tname == "ins") {
|
{
|
||||||
cc.doAttrib(state, "underline");
|
cc.doAttrib(state, "underline");
|
||||||
}
|
}
|
||||||
if (tname == "s" || (styl && /\btext-decoration:\s*line-through\b/i.exec(styl)) ||
|
if (tname == "s" || (styl && /\btext-decoration:\s*line-through\b/i.exec(styl)) || tname == "del")
|
||||||
tname == "del") {
|
{
|
||||||
cc.doAttrib(state, "strikethrough");
|
cc.doAttrib(state, "strikethrough");
|
||||||
}
|
}
|
||||||
if (tname == "ul") {
|
if (tname == "ul")
|
||||||
|
{
|
||||||
var type;
|
var type;
|
||||||
var rr = cls && /(?:^| )list-(bullet[12345678])\b/.exec(cls);
|
var rr = cls && /(?:^| )list-(bullet[12345678])\b/.exec(cls);
|
||||||
type = rr && rr[1] || "bullet"+
|
type = rr && rr[1] || "bullet" + String(Math.min(_MAX_LIST_LEVEL, (state.listNesting || 0) + 1));
|
||||||
String(Math.min(_MAX_LIST_LEVEL, (state.listNesting||0)+1));
|
|
||||||
oldListTypeOrNull = (_enterList(state, type) || 'none');
|
oldListTypeOrNull = (_enterList(state, type) || 'none');
|
||||||
}
|
}
|
||||||
else if ((tname == "div" || tname == "p") && cls &&
|
else if ((tname == "div" || tname == "p") && cls && cls.match(/(?:^| )ace-line\b/))
|
||||||
cls.match(/(?:^| )ace-line\b/)) {
|
{
|
||||||
oldListTypeOrNull = (_enterList(state, type) || 'none');
|
oldListTypeOrNull = (_enterList(state, type) || 'none');
|
||||||
}
|
}
|
||||||
if (className2Author && cls) {
|
if (className2Author && cls)
|
||||||
|
{
|
||||||
var classes = cls.match(/\S+/g);
|
var classes = cls.match(/\S+/g);
|
||||||
if (classes && classes.length > 0) {
|
if (classes && classes.length > 0)
|
||||||
for(var i=0;i<classes.length;i++) {
|
{
|
||||||
|
for (var i = 0; i < classes.length; i++)
|
||||||
|
{
|
||||||
var c = classes[i];
|
var c = classes[i];
|
||||||
var a = className2Author(c);
|
var a = className2Author(c);
|
||||||
if (a) {
|
if (a)
|
||||||
|
{
|
||||||
oldAuthorOrNull = (_enterAuthor(state, a) || 'none');
|
oldAuthorOrNull = (_enterAuthor(state, a) || 'none');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -378,42 +501,59 @@ function makeContentCollector(collectStyles, browser, apool, domInterface,
|
||||||
}
|
}
|
||||||
|
|
||||||
var nc = dom.nodeNumChildren(node);
|
var nc = dom.nodeNumChildren(node);
|
||||||
for(var i=0;i<nc;i++) {
|
for (var i = 0; i < nc; i++)
|
||||||
|
{
|
||||||
var c = dom.nodeChild(node, i);
|
var c = dom.nodeChild(node, i);
|
||||||
cc.collectContent(c, state);
|
cc.collectContent(c, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (collectStyles) {
|
if (collectStyles)
|
||||||
plugins_.callHook('collectContentPost', {cc: cc, state:state, tname:tname, styl:styl, cls:cls});
|
{
|
||||||
|
plugins_.callHook('collectContentPost', {
|
||||||
|
cc: cc,
|
||||||
|
state: state,
|
||||||
|
tname: tname,
|
||||||
|
styl: styl,
|
||||||
|
cls: cls
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPre) cc.decrementFlag(state, 'preMode');
|
if (isPre) cc.decrementFlag(state, 'preMode');
|
||||||
if (state.localAttribs) {
|
if (state.localAttribs)
|
||||||
for(var i=0;i<state.localAttribs.length;i++) {
|
{
|
||||||
|
for (var i = 0; i < state.localAttribs.length; i++)
|
||||||
|
{
|
||||||
cc.decrementAttrib(state, state.localAttribs[i]);
|
cc.decrementAttrib(state, state.localAttribs[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (oldListTypeOrNull) {
|
if (oldListTypeOrNull)
|
||||||
|
{
|
||||||
_exitList(state, oldListTypeOrNull);
|
_exitList(state, oldListTypeOrNull);
|
||||||
}
|
}
|
||||||
if (oldAuthorOrNull) {
|
if (oldAuthorOrNull)
|
||||||
|
{
|
||||||
_exitAuthor(state, oldAuthorOrNull);
|
_exitAuthor(state, oldAuthorOrNull);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (! browser.msie) {
|
if (!browser.msie)
|
||||||
|
{
|
||||||
_reachBlockPoint(node, 1, state);
|
_reachBlockPoint(node, 1, state);
|
||||||
}
|
}
|
||||||
if (isBlock) {
|
if (isBlock)
|
||||||
if (lines.length()-1 == startLine) {
|
{
|
||||||
|
if (lines.length() - 1 == startLine)
|
||||||
|
{
|
||||||
cc.startNewLine(state);
|
cc.startNewLine(state);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
_ensureColumnZero(state);
|
_ensureColumnZero(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (browser.msie) {
|
if (browser.msie)
|
||||||
|
{
|
||||||
// in IE, a point immediately after a DIV appears on the next line
|
// in IE, a point immediately after a DIV appears on the next line
|
||||||
_reachBlockPoint(node, 1, state);
|
_reachBlockPoint(node, 1, state);
|
||||||
}
|
}
|
||||||
|
@ -421,26 +561,38 @@ function makeContentCollector(collectStyles, browser, apool, domInterface,
|
||||||
state.localAttribs = localAttribs;
|
state.localAttribs = localAttribs;
|
||||||
};
|
};
|
||||||
// can pass a falsy value for end of doc
|
// can pass a falsy value for end of doc
|
||||||
cc.notifyNextNode = function (node) {
|
cc.notifyNextNode = function(node)
|
||||||
|
{
|
||||||
// an "empty block" won't end a line; this addresses an issue in IE with
|
// an "empty block" won't end a line; this addresses an issue in IE with
|
||||||
// typing into a blank line at the end of the document. typed text
|
// typing into a blank line at the end of the document. typed text
|
||||||
// goes into the body, and the empty line div still looks clean.
|
// goes into the body, and the empty line div still looks clean.
|
||||||
// it is incorporated as dirty by the rule that a dirty region has
|
// it is incorporated as dirty by the rule that a dirty region has
|
||||||
// to end a line.
|
// to end a line.
|
||||||
if ((!node) || (isBlockElement(node) && !_isEmpty(node))) {
|
if ((!node) || (isBlockElement(node) && !_isEmpty(node)))
|
||||||
|
{
|
||||||
_ensureColumnZero(null);
|
_ensureColumnZero(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// each returns [line, char] or [-1,-1]
|
// each returns [line, char] or [-1,-1]
|
||||||
var getSelectionStart = function() { return selStart; };
|
var getSelectionStart = function()
|
||||||
var getSelectionEnd = function() { return selEnd; };
|
{
|
||||||
|
return selStart;
|
||||||
|
};
|
||||||
|
var getSelectionEnd = function()
|
||||||
|
{
|
||||||
|
return selEnd;
|
||||||
|
};
|
||||||
|
|
||||||
// returns array of strings for lines found, last entry will be "" if
|
// returns array of strings for lines found, last entry will be "" if
|
||||||
// last line is complete (i.e. if a following span should be on a new line).
|
// last line is complete (i.e. if a following span should be on a new line).
|
||||||
// can be called at any point
|
// can be called at any point
|
||||||
cc.getLines = function() { return lines.textLines(); };
|
cc.getLines = function()
|
||||||
|
{
|
||||||
|
return lines.textLines();
|
||||||
|
};
|
||||||
|
|
||||||
cc.finish = function() {
|
cc.finish = function()
|
||||||
|
{
|
||||||
lines.flush();
|
lines.flush();
|
||||||
var lineAttribs = lines.attribLines();
|
var lineAttribs = lines.attribLines();
|
||||||
var lineStrings = cc.getLines();
|
var lineStrings = cc.getLines();
|
||||||
|
@ -451,43 +603,51 @@ function makeContentCollector(collectStyles, browser, apool, domInterface,
|
||||||
var ss = getSelectionStart();
|
var ss = getSelectionStart();
|
||||||
var se = getSelectionEnd();
|
var se = getSelectionEnd();
|
||||||
|
|
||||||
function fixLongLines() {
|
function fixLongLines()
|
||||||
|
{
|
||||||
// design mode does not deal with with really long lines!
|
// design mode does not deal with with really long lines!
|
||||||
var lineLimit = 2000; // chars
|
var lineLimit = 2000; // chars
|
||||||
var buffer = 10; // chars allowed over before wrapping
|
var buffer = 10; // chars allowed over before wrapping
|
||||||
var linesWrapped = 0;
|
var linesWrapped = 0;
|
||||||
var numLinesAfter = 0;
|
var numLinesAfter = 0;
|
||||||
for(var i=lineStrings.length-1; i>=0; i--) {
|
for (var i = lineStrings.length - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
var oldString = lineStrings[i];
|
var oldString = lineStrings[i];
|
||||||
var oldAttribString = lineAttribs[i];
|
var oldAttribString = lineAttribs[i];
|
||||||
if (oldString.length > lineLimit+buffer) {
|
if (oldString.length > lineLimit + buffer)
|
||||||
|
{
|
||||||
var newStrings = [];
|
var newStrings = [];
|
||||||
var newAttribStrings = [];
|
var newAttribStrings = [];
|
||||||
while (oldString.length > lineLimit) {
|
while (oldString.length > lineLimit)
|
||||||
|
{
|
||||||
//var semiloc = oldString.lastIndexOf(';', lineLimit-1);
|
//var semiloc = oldString.lastIndexOf(';', lineLimit-1);
|
||||||
//var lengthToTake = (semiloc >= 0 ? (semiloc+1) : lineLimit);
|
//var lengthToTake = (semiloc >= 0 ? (semiloc+1) : lineLimit);
|
||||||
lengthToTake = lineLimit;
|
lengthToTake = lineLimit;
|
||||||
newStrings.push(oldString.substring(0, lengthToTake));
|
newStrings.push(oldString.substring(0, lengthToTake));
|
||||||
oldString = oldString.substring(lengthToTake);
|
oldString = oldString.substring(lengthToTake);
|
||||||
newAttribStrings.push(Changeset.subattribution(oldAttribString,
|
newAttribStrings.push(Changeset.subattribution(oldAttribString, 0, lengthToTake));
|
||||||
0, lengthToTake));
|
oldAttribString = Changeset.subattribution(oldAttribString, lengthToTake);
|
||||||
oldAttribString = Changeset.subattribution(oldAttribString,
|
|
||||||
lengthToTake);
|
|
||||||
}
|
}
|
||||||
if (oldString.length > 0) {
|
if (oldString.length > 0)
|
||||||
|
{
|
||||||
newStrings.push(oldString);
|
newStrings.push(oldString);
|
||||||
newAttribStrings.push(oldAttribString);
|
newAttribStrings.push(oldAttribString);
|
||||||
}
|
}
|
||||||
function fixLineNumber(lineChar) {
|
|
||||||
|
function fixLineNumber(lineChar)
|
||||||
|
{
|
||||||
if (lineChar[0] < 0) return;
|
if (lineChar[0] < 0) return;
|
||||||
var n = lineChar[0];
|
var n = lineChar[0];
|
||||||
var c = lineChar[1];
|
var c = lineChar[1];
|
||||||
if (n > i) {
|
if (n > i)
|
||||||
|
{
|
||||||
n += (newStrings.length - 1);
|
n += (newStrings.length - 1);
|
||||||
}
|
}
|
||||||
else if (n == i) {
|
else if (n == i)
|
||||||
|
{
|
||||||
var a = 0;
|
var a = 0;
|
||||||
while (c > newStrings[a].length) {
|
while (c > newStrings[a].length)
|
||||||
|
{
|
||||||
c -= newStrings[a].length;
|
c -= newStrings[a].length;
|
||||||
a++;
|
a++;
|
||||||
}
|
}
|
||||||
|
@ -507,13 +667,21 @@ function makeContentCollector(collectStyles, browser, apool, domInterface,
|
||||||
lineAttribs.splice.apply(lineAttribs, newAttribStrings);
|
lineAttribs.splice.apply(lineAttribs, newAttribStrings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {linesWrapped:linesWrapped, numLinesAfter:numLinesAfter};
|
return {
|
||||||
|
linesWrapped: linesWrapped,
|
||||||
|
numLinesAfter: numLinesAfter
|
||||||
|
};
|
||||||
}
|
}
|
||||||
var wrapData = fixLongLines();
|
var wrapData = fixLongLines();
|
||||||
|
|
||||||
return { selStart: ss, selEnd: se, linesWrapped: wrapData.linesWrapped,
|
return {
|
||||||
|
selStart: ss,
|
||||||
|
selEnd: se,
|
||||||
|
linesWrapped: wrapData.linesWrapped,
|
||||||
numLinesAfter: wrapData.numLinesAfter,
|
numLinesAfter: wrapData.numLinesAfter,
|
||||||
lines: lineStrings, lineAttribs: lineAttribs };
|
lines: lineStrings,
|
||||||
|
lineAttribs: lineAttribs
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return cc;
|
return cc;
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copyright 2009 Google Inc.
|
* Copyright 2009 Google Inc.
|
||||||
*
|
*
|
||||||
|
@ -16,13 +14,17 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function makeCSSManager(emptyStylesheetTitle) {
|
function makeCSSManager(emptyStylesheetTitle)
|
||||||
|
{
|
||||||
|
|
||||||
function getSheetByTitle(title) {
|
function getSheetByTitle(title)
|
||||||
|
{
|
||||||
var allSheets = document.styleSheets;
|
var allSheets = document.styleSheets;
|
||||||
for(var i=0;i<allSheets.length;i++) {
|
for (var i = 0; i < allSheets.length; i++)
|
||||||
|
{
|
||||||
var s = allSheets[i];
|
var s = allSheets[i];
|
||||||
if (s.title == title) {
|
if (s.title == title)
|
||||||
|
{
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,29 +44,43 @@ function makeCSSManager(emptyStylesheetTitle) {
|
||||||
|
|
||||||
var browserSheet = getSheetByTitle(emptyStylesheetTitle);
|
var browserSheet = getSheetByTitle(emptyStylesheetTitle);
|
||||||
//var browserTag = getSheetTagByTitle(emptyStylesheetTitle);
|
//var browserTag = getSheetTagByTitle(emptyStylesheetTitle);
|
||||||
function browserRules() { return (browserSheet.cssRules || browserSheet.rules); }
|
|
||||||
function browserDeleteRule(i) {
|
|
||||||
|
function browserRules()
|
||||||
|
{
|
||||||
|
return (browserSheet.cssRules || browserSheet.rules);
|
||||||
|
}
|
||||||
|
|
||||||
|
function browserDeleteRule(i)
|
||||||
|
{
|
||||||
if (browserSheet.deleteRule) browserSheet.deleteRule(i);
|
if (browserSheet.deleteRule) browserSheet.deleteRule(i);
|
||||||
else browserSheet.removeRule(i);
|
else browserSheet.removeRule(i);
|
||||||
}
|
}
|
||||||
function browserInsertRule(i, selector) {
|
|
||||||
|
function browserInsertRule(i, selector)
|
||||||
|
{
|
||||||
if (browserSheet.insertRule) browserSheet.insertRule(selector + ' {}', i);
|
if (browserSheet.insertRule) browserSheet.insertRule(selector + ' {}', i);
|
||||||
else browserSheet.addRule(selector, null, i);
|
else browserSheet.addRule(selector, null, i);
|
||||||
}
|
}
|
||||||
var selectorList = [];
|
var selectorList = [];
|
||||||
|
|
||||||
function indexOfSelector(selector) {
|
function indexOfSelector(selector)
|
||||||
for(var i=0;i<selectorList.length;i++) {
|
{
|
||||||
if (selectorList[i] == selector) {
|
for (var i = 0; i < selectorList.length; i++)
|
||||||
|
{
|
||||||
|
if (selectorList[i] == selector)
|
||||||
|
{
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectorStyle(selector) {
|
function selectorStyle(selector)
|
||||||
|
{
|
||||||
var i = indexOfSelector(selector);
|
var i = indexOfSelector(selector);
|
||||||
if (i < 0) {
|
if (i < 0)
|
||||||
|
{
|
||||||
// add selector
|
// add selector
|
||||||
browserInsertRule(0, selector);
|
browserInsertRule(0, selector);
|
||||||
selectorList.splice(0, 0, selector);
|
selectorList.splice(0, 0, selector);
|
||||||
|
@ -73,16 +89,22 @@ function makeCSSManager(emptyStylesheetTitle) {
|
||||||
return browserRules().item(i).style;
|
return browserRules().item(i).style;
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeSelectorStyle(selector) {
|
function removeSelectorStyle(selector)
|
||||||
|
{
|
||||||
var i = indexOfSelector(selector);
|
var i = indexOfSelector(selector);
|
||||||
if (i >= 0) {
|
if (i >= 0)
|
||||||
|
{
|
||||||
browserDeleteRule(i);
|
browserDeleteRule(i);
|
||||||
selectorList.splice(i, 1);
|
selectorList.splice(i, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {selectorStyle:selectorStyle, removeSelectorStyle:removeSelectorStyle,
|
return {
|
||||||
info: function() {
|
selectorStyle: selectorStyle,
|
||||||
|
removeSelectorStyle: removeSelectorStyle,
|
||||||
|
info: function()
|
||||||
|
{
|
||||||
return selectorList.length + ":" + browserRules().length;
|
return selectorList.length + ":" + browserRules().length;
|
||||||
}};
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,6 @@ function makeCSSManager(emptyStylesheetTitle)
|
||||||
var browserSheet = getSheetByTitle(emptyStylesheetTitle);
|
var browserSheet = getSheetByTitle(emptyStylesheetTitle);
|
||||||
//var browserTag = getSheetTagByTitle(emptyStylesheetTitle);
|
//var browserTag = getSheetTagByTitle(emptyStylesheetTitle);
|
||||||
|
|
||||||
|
|
||||||
function browserRules()
|
function browserRules()
|
||||||
{
|
{
|
||||||
return (browserSheet.cssRules || browserSheet.rules);
|
return (browserSheet.cssRules || browserSheet.rules);
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.domline
|
// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.domline
|
||||||
// %APPJET%: import("etherpad.admin.plugins");
|
// %APPJET%: import("etherpad.admin.plugins");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copyright 2009 Google Inc.
|
* Copyright 2009 Google Inc.
|
||||||
*
|
*
|
||||||
|
@ -20,17 +19,23 @@
|
||||||
// requires: top
|
// requires: top
|
||||||
// requires: plugins
|
// requires: plugins
|
||||||
// requires: undefined
|
// requires: undefined
|
||||||
|
|
||||||
var domline = {};
|
var domline = {};
|
||||||
domline.noop = function() {};
|
domline.noop = function()
|
||||||
domline.identity = function(x) { return x; };
|
{};
|
||||||
|
domline.identity = function(x)
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
};
|
||||||
|
|
||||||
domline.addToLineClass = function(lineClass, cls) {
|
domline.addToLineClass = function(lineClass, cls)
|
||||||
|
{
|
||||||
// an "empty span" at any point can be used to add classes to
|
// an "empty span" at any point can be used to add classes to
|
||||||
// the line, using line:className. otherwise, we ignore
|
// the line, using line:className. otherwise, we ignore
|
||||||
// the span.
|
// the span.
|
||||||
cls.replace(/\S+/g, function (c) {
|
cls.replace(/\S+/g, function(c)
|
||||||
if (c.indexOf("line:") == 0) {
|
{
|
||||||
|
if (c.indexOf("line:") == 0)
|
||||||
|
{
|
||||||
// add class to line
|
// add class to line
|
||||||
lineClass = (lineClass ? lineClass + ' ' : '') + c.substring(5);
|
lineClass = (lineClass ? lineClass + ' ' : '') + c.substring(5);
|
||||||
}
|
}
|
||||||
|
@ -40,41 +45,55 @@ domline.addToLineClass = function(lineClass, cls) {
|
||||||
|
|
||||||
// if "document" is falsy we don't create a DOM node, just
|
// if "document" is falsy we don't create a DOM node, just
|
||||||
// an object with innerHTML and className
|
// an object with innerHTML and className
|
||||||
domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument) {
|
domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument)
|
||||||
var result = { node: null,
|
{
|
||||||
|
var result = {
|
||||||
|
node: null,
|
||||||
appendSpan: domline.noop,
|
appendSpan: domline.noop,
|
||||||
prepareForAdd: domline.noop,
|
prepareForAdd: domline.noop,
|
||||||
notifyAdded: domline.noop,
|
notifyAdded: domline.noop,
|
||||||
clearSpans: domline.noop,
|
clearSpans: domline.noop,
|
||||||
finishUpdate: domline.noop,
|
finishUpdate: domline.noop,
|
||||||
lineMarker: 0 };
|
lineMarker: 0
|
||||||
|
};
|
||||||
|
|
||||||
var browser = (optBrowser || {});
|
var browser = (optBrowser || {});
|
||||||
var document = optDocument;
|
var document = optDocument;
|
||||||
|
|
||||||
if (document) {
|
if (document)
|
||||||
|
{
|
||||||
result.node = document.createElement("div");
|
result.node = document.createElement("div");
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
result.node = {innerHTML: '', className: ''};
|
{
|
||||||
|
result.node = {
|
||||||
|
innerHTML: '',
|
||||||
|
className: ''
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var html = [];
|
var html = [];
|
||||||
var preHtml, postHtml;
|
var preHtml, postHtml;
|
||||||
var curHTML = null;
|
var curHTML = null;
|
||||||
function processSpaces(s) {
|
|
||||||
|
function processSpaces(s)
|
||||||
|
{
|
||||||
return domline.processSpaces(s, doesWrap);
|
return domline.processSpaces(s, doesWrap);
|
||||||
}
|
}
|
||||||
var identity = domline.identity;
|
var identity = domline.identity;
|
||||||
var perTextNodeProcess = (doesWrap ? identity : processSpaces);
|
var perTextNodeProcess = (doesWrap ? identity : processSpaces);
|
||||||
var perHtmlLineProcess = (doesWrap ? processSpaces : identity);
|
var perHtmlLineProcess = (doesWrap ? processSpaces : identity);
|
||||||
var lineClass = 'ace-line';
|
var lineClass = 'ace-line';
|
||||||
result.appendSpan = function(txt, cls) {
|
result.appendSpan = function(txt, cls)
|
||||||
if (cls.indexOf('list') >= 0) {
|
{
|
||||||
|
if (cls.indexOf('list') >= 0)
|
||||||
|
{
|
||||||
var listType = /(?:^| )list:(\S+)/.exec(cls);
|
var listType = /(?:^| )list:(\S+)/.exec(cls);
|
||||||
if (listType) {
|
if (listType)
|
||||||
|
{
|
||||||
listType = listType[1];
|
listType = listType[1];
|
||||||
if (listType) {
|
if (listType)
|
||||||
|
{
|
||||||
preHtml = '<ul class="list-' + listType + '"><li>';
|
preHtml = '<ul class="list-' + listType + '"><li>';
|
||||||
postHtml = '</li></ul>';
|
postHtml = '</li></ul>';
|
||||||
}
|
}
|
||||||
|
@ -84,14 +103,18 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument) {
|
||||||
}
|
}
|
||||||
var href = null;
|
var href = null;
|
||||||
var simpleTags = null;
|
var simpleTags = null;
|
||||||
if (cls.indexOf('url') >= 0) {
|
if (cls.indexOf('url') >= 0)
|
||||||
cls = cls.replace(/(^| )url:(\S+)/g, function(x0, space, url) {
|
{
|
||||||
|
cls = cls.replace(/(^| )url:(\S+)/g, function(x0, space, url)
|
||||||
|
{
|
||||||
href = url;
|
href = url;
|
||||||
return space + "url";
|
return space + "url";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (cls.indexOf('tag') >= 0) {
|
if (cls.indexOf('tag') >= 0)
|
||||||
cls = cls.replace(/(^| )tag:(\S+)/g, function(x0, space, tag) {
|
{
|
||||||
|
cls = cls.replace(/(^| )tag:(\S+)/g, function(x0, space, tag)
|
||||||
|
{
|
||||||
if (!simpleTags) simpleTags = [];
|
if (!simpleTags) simpleTags = [];
|
||||||
simpleTags.push(tag.toLowerCase());
|
simpleTags.push(tag.toLowerCase());
|
||||||
return space + tag;
|
return space + tag;
|
||||||
|
@ -102,60 +125,74 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument) {
|
||||||
var extraCloseTags = "";
|
var extraCloseTags = "";
|
||||||
|
|
||||||
var plugins_;
|
var plugins_;
|
||||||
if (typeof(plugins)!='undefined') {
|
if (typeof(plugins) != 'undefined')
|
||||||
|
{
|
||||||
plugins_ = plugins;
|
plugins_ = plugins;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
plugins_ = parent.parent.plugins;
|
plugins_ = parent.parent.plugins;
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins_.callHook(
|
plugins_.callHook("aceCreateDomLine", {
|
||||||
"aceCreateDomLine", {domline:domline, cls:cls}
|
domline: domline,
|
||||||
).map(function (modifier) {
|
cls: cls
|
||||||
|
}).map(function(modifier)
|
||||||
|
{
|
||||||
cls = modifier.cls;
|
cls = modifier.cls;
|
||||||
extraOpenTags = extraOpenTags + modifier.extraOpenTags;
|
extraOpenTags = extraOpenTags + modifier.extraOpenTags;
|
||||||
extraCloseTags = modifier.extraCloseTags + extraCloseTags;
|
extraCloseTags = modifier.extraCloseTags + extraCloseTags;
|
||||||
});
|
});
|
||||||
|
|
||||||
if ((! txt) && cls) {
|
if ((!txt) && cls)
|
||||||
|
{
|
||||||
lineClass = domline.addToLineClass(lineClass, cls);
|
lineClass = domline.addToLineClass(lineClass, cls);
|
||||||
}
|
}
|
||||||
else if (txt) {
|
else if (txt)
|
||||||
if (href) {
|
{
|
||||||
extraOpenTags = extraOpenTags+'<a href="'+
|
if (href)
|
||||||
href.replace(/\"/g, '"')+'">';
|
{
|
||||||
|
extraOpenTags = extraOpenTags + '<a href="' + href.replace(/\"/g, '"') + '">';
|
||||||
extraCloseTags = '</a>' + extraCloseTags;
|
extraCloseTags = '</a>' + extraCloseTags;
|
||||||
}
|
}
|
||||||
if (simpleTags) {
|
if (simpleTags)
|
||||||
|
{
|
||||||
simpleTags.sort();
|
simpleTags.sort();
|
||||||
extraOpenTags = extraOpenTags + '<' + simpleTags.join('><') + '>';
|
extraOpenTags = extraOpenTags + '<' + simpleTags.join('><') + '>';
|
||||||
simpleTags.reverse();
|
simpleTags.reverse();
|
||||||
extraCloseTags = '</' + simpleTags.join('></') + '>' + extraCloseTags;
|
extraCloseTags = '</' + simpleTags.join('></') + '>' + extraCloseTags;
|
||||||
}
|
}
|
||||||
html.push('<span class="',cls||'','">',extraOpenTags,
|
html.push('<span class="', cls || '', '">', extraOpenTags, perTextNodeProcess(domline.escapeHTML(txt)), extraCloseTags, '</span>');
|
||||||
perTextNodeProcess(domline.escapeHTML(txt)),
|
|
||||||
extraCloseTags,'</span>');
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
result.clearSpans = function() {
|
result.clearSpans = function()
|
||||||
|
{
|
||||||
html = [];
|
html = [];
|
||||||
lineClass = ''; // non-null to cause update
|
lineClass = ''; // non-null to cause update
|
||||||
result.lineMarker = 0;
|
result.lineMarker = 0;
|
||||||
};
|
};
|
||||||
function writeHTML() {
|
|
||||||
|
function writeHTML()
|
||||||
|
{
|
||||||
var newHTML = perHtmlLineProcess(html.join(''));
|
var newHTML = perHtmlLineProcess(html.join(''));
|
||||||
if (! newHTML) {
|
if (!newHTML)
|
||||||
if ((! document) || (! optBrowser)) {
|
{
|
||||||
|
if ((!document) || (!optBrowser))
|
||||||
|
{
|
||||||
newHTML += ' ';
|
newHTML += ' ';
|
||||||
}
|
}
|
||||||
else if (! browser.msie) {
|
else if (!browser.msie)
|
||||||
|
{
|
||||||
newHTML += '<br/>';
|
newHTML += '<br/>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (nonEmpty) {
|
if (nonEmpty)
|
||||||
|
{
|
||||||
newHTML = (preHtml || '') + newHTML + (postHtml || '');
|
newHTML = (preHtml || '') + newHTML + (postHtml || '');
|
||||||
}
|
}
|
||||||
html = preHtml = postHtml = null; // free memory
|
html = preHtml = postHtml = null; // free memory
|
||||||
if (newHTML !== curHTML) {
|
if (newHTML !== curHTML)
|
||||||
|
{
|
||||||
curHTML = newHTML;
|
curHTML = newHTML;
|
||||||
result.node.innerHTML = curHTML;
|
result.node.innerHTML = curHTML;
|
||||||
}
|
}
|
||||||
|
@ -163,14 +200,20 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument) {
|
||||||
}
|
}
|
||||||
result.prepareForAdd = writeHTML;
|
result.prepareForAdd = writeHTML;
|
||||||
result.finishUpdate = writeHTML;
|
result.finishUpdate = writeHTML;
|
||||||
result.getInnerHTML = function() { return curHTML || ''; };
|
result.getInnerHTML = function()
|
||||||
|
{
|
||||||
|
return curHTML || '';
|
||||||
|
};
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
domline.escapeHTML = function(s) {
|
domline.escapeHTML = function(s)
|
||||||
var re = /[&<>'"]/g; /']/; // stupid indentation thing
|
{
|
||||||
if (! re.MAP) {
|
var re = /[&<>'"]/g;
|
||||||
|
/']/; // stupid indentation thing
|
||||||
|
if (!re.MAP)
|
||||||
|
{
|
||||||
// persisted across function calls!
|
// persisted across function calls!
|
||||||
re.MAP = {
|
re.MAP = {
|
||||||
'&': '&',
|
'&': '&',
|
||||||
|
@ -180,50 +223,67 @@ domline.escapeHTML = function(s) {
|
||||||
"'": '''
|
"'": '''
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return s.replace(re, function(c) { return re.MAP[c]; });
|
return s.replace(re, function(c)
|
||||||
|
{
|
||||||
|
return re.MAP[c];
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
domline.processSpaces = function(s, doesWrap) {
|
domline.processSpaces = function(s, doesWrap)
|
||||||
if (s.indexOf("<") < 0 && ! doesWrap) {
|
{
|
||||||
|
if (s.indexOf("<") < 0 && !doesWrap)
|
||||||
|
{
|
||||||
// short-cut
|
// short-cut
|
||||||
return s.replace(/ /g, ' ');
|
return s.replace(/ /g, ' ');
|
||||||
}
|
}
|
||||||
var parts = [];
|
var parts = [];
|
||||||
s.replace(/<[^>]*>?| |[^ <]+/g, function(m) { parts.push(m); });
|
s.replace(/<[^>]*>?| |[^ <]+/g, function(m)
|
||||||
if (doesWrap) {
|
{
|
||||||
|
parts.push(m);
|
||||||
|
});
|
||||||
|
if (doesWrap)
|
||||||
|
{
|
||||||
var endOfLine = true;
|
var endOfLine = true;
|
||||||
var beforeSpace = false;
|
var beforeSpace = false;
|
||||||
// last space in a run is normal, others are nbsp,
|
// last space in a run is normal, others are nbsp,
|
||||||
// end of line is nbsp
|
// end of line is nbsp
|
||||||
for(var i=parts.length-1;i>=0;i--) {
|
for (var i = parts.length - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
var p = parts[i];
|
var p = parts[i];
|
||||||
if (p == " ") {
|
if (p == " ")
|
||||||
if (endOfLine || beforeSpace)
|
{
|
||||||
parts[i] = ' ';
|
if (endOfLine || beforeSpace) parts[i] = ' ';
|
||||||
endOfLine = false;
|
endOfLine = false;
|
||||||
beforeSpace = true;
|
beforeSpace = true;
|
||||||
}
|
}
|
||||||
else if (p.charAt(0) != "<") {
|
else if (p.charAt(0) != "<")
|
||||||
|
{
|
||||||
endOfLine = false;
|
endOfLine = false;
|
||||||
beforeSpace = false;
|
beforeSpace = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// beginning of line is nbsp
|
// beginning of line is nbsp
|
||||||
for(var i=0;i<parts.length;i++) {
|
for (var i = 0; i < parts.length; i++)
|
||||||
|
{
|
||||||
var p = parts[i];
|
var p = parts[i];
|
||||||
if (p == " ") {
|
if (p == " ")
|
||||||
|
{
|
||||||
parts[i] = ' ';
|
parts[i] = ' ';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (p.charAt(0) != "<") {
|
else if (p.charAt(0) != "<")
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
for(var i=0;i<parts.length;i++) {
|
{
|
||||||
|
for (var i = 0; i < parts.length; i++)
|
||||||
|
{
|
||||||
var p = parts[i];
|
var p = parts[i];
|
||||||
if (p == " ") {
|
if (p == " ")
|
||||||
|
{
|
||||||
parts[i] = ' ';
|
parts[i] = ' ';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -580,7 +580,6 @@ if (_opt)
|
||||||
var assem = _opt.opAssembler();
|
var assem = _opt.opAssembler();
|
||||||
// this function allows op to be mutated later (doesn't keep a ref)
|
// this function allows op to be mutated later (doesn't keep a ref)
|
||||||
|
|
||||||
|
|
||||||
function append(op)
|
function append(op)
|
||||||
{
|
{
|
||||||
assem.append(op.opcode, op.chars, op.lines, op.attribs);
|
assem.append(op.opcode, op.chars, op.lines, op.attribs);
|
||||||
|
@ -609,7 +608,6 @@ else
|
||||||
var pieces = [];
|
var pieces = [];
|
||||||
// this function allows op to be mutated later (doesn't keep a ref)
|
// this function allows op to be mutated later (doesn't keep a ref)
|
||||||
|
|
||||||
|
|
||||||
function append(op)
|
function append(op)
|
||||||
{
|
{
|
||||||
pieces.push(op.attribs);
|
pieces.push(op.attribs);
|
||||||
|
@ -743,7 +741,6 @@ Changeset.textLinesMutator = function (lines)
|
||||||
}
|
}
|
||||||
// can be unimplemented if removeLines's return value not needed
|
// can be unimplemented if removeLines's return value not needed
|
||||||
|
|
||||||
|
|
||||||
function lines_slice(start, end)
|
function lines_slice(start, end)
|
||||||
{
|
{
|
||||||
if (lines.slice)
|
if (lines.slice)
|
||||||
|
@ -2041,7 +2038,6 @@ Changeset.inverse = function (cs, lines, alines, pool)
|
||||||
// They may be arrays or objects with .get(i) and .length methods.
|
// They may be arrays or objects with .get(i) and .length methods.
|
||||||
// They include final newlines on lines.
|
// They include final newlines on lines.
|
||||||
|
|
||||||
|
|
||||||
function lines_get(idx)
|
function lines_get(idx)
|
||||||
{
|
{
|
||||||
if (lines.get)
|
if (lines.get)
|
||||||
|
|
|
@ -158,45 +158,40 @@
|
||||||
|
|
||||||
// Create a JSON object only if one does not already exist. We create the
|
// Create a JSON object only if one does not already exist. We create the
|
||||||
// methods in a closure to avoid creating global variables.
|
// methods in a closure to avoid creating global variables.
|
||||||
|
|
||||||
var JSON;
|
var JSON;
|
||||||
if (!JSON) {
|
if (!JSON)
|
||||||
|
{
|
||||||
JSON = {};
|
JSON = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
(function () {
|
(function()
|
||||||
|
{
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
function f(n) {
|
function f(n)
|
||||||
|
{
|
||||||
// Format integers to have at least two digits.
|
// Format integers to have at least two digits.
|
||||||
return n < 10 ? '0' + n : n;
|
return n < 10 ? '0' + n : n;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof Date.prototype.toJSON !== 'function') {
|
if (typeof Date.prototype.toJSON !== 'function')
|
||||||
|
{
|
||||||
|
|
||||||
Date.prototype.toJSON = function (key) {
|
Date.prototype.toJSON = function(key)
|
||||||
|
{
|
||||||
|
|
||||||
return isFinite(this.valueOf()) ?
|
return isFinite(this.valueOf()) ? this.getUTCFullYear() + '-' + f(this.getUTCMonth() + 1) + '-' + f(this.getUTCDate()) + 'T' + f(this.getUTCHours()) + ':' + f(this.getUTCMinutes()) + ':' + f(this.getUTCSeconds()) + 'Z' : null;
|
||||||
this.getUTCFullYear() + '-' +
|
|
||||||
f(this.getUTCMonth() + 1) + '-' +
|
|
||||||
f(this.getUTCDate()) + 'T' +
|
|
||||||
f(this.getUTCHours()) + ':' +
|
|
||||||
f(this.getUTCMinutes()) + ':' +
|
|
||||||
f(this.getUTCSeconds()) + 'Z' : null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
String.prototype.toJSON =
|
String.prototype.toJSON = Number.prototype.toJSON = Boolean.prototype.toJSON = function(key)
|
||||||
Number.prototype.toJSON =
|
{
|
||||||
Boolean.prototype.toJSON = function (key) {
|
|
||||||
return this.valueOf();
|
return this.valueOf();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
||||||
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
||||||
gap,
|
gap, indent, meta = { // table of character substitutions
|
||||||
indent,
|
|
||||||
meta = { // table of character substitutions
|
|
||||||
'\b': '\\b',
|
'\b': '\\b',
|
||||||
'\t': '\\t',
|
'\t': '\\t',
|
||||||
'\n': '\\n',
|
'\n': '\\n',
|
||||||
|
@ -208,58 +203,54 @@ if (!JSON) {
|
||||||
rep;
|
rep;
|
||||||
|
|
||||||
|
|
||||||
function quote(string) {
|
function quote(string)
|
||||||
|
{
|
||||||
|
|
||||||
// If the string contains no control characters, no quote characters, and no
|
// If the string contains no control characters, no quote characters, and no
|
||||||
// backslash characters, then we can safely slap some quotes around it.
|
// backslash characters, then we can safely slap some quotes around it.
|
||||||
// Otherwise we must also replace the offending characters with safe escape
|
// Otherwise we must also replace the offending characters with safe escape
|
||||||
// sequences.
|
// sequences.
|
||||||
|
|
||||||
escapable.lastIndex = 0;
|
escapable.lastIndex = 0;
|
||||||
return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
|
return escapable.test(string) ? '"' + string.replace(escapable, function(a)
|
||||||
|
{
|
||||||
var c = meta[a];
|
var c = meta[a];
|
||||||
return typeof c === 'string' ? c :
|
return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
||||||
'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
|
||||||
}) + '"' : '"' + string + '"';
|
}) + '"' : '"' + string + '"';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function str(key, holder) {
|
function str(key, holder)
|
||||||
|
{
|
||||||
|
|
||||||
// Produce a string from holder[key].
|
// Produce a string from holder[key].
|
||||||
|
|
||||||
var i, // The loop counter.
|
var i, // The loop counter.
|
||||||
k, // The member key.
|
k, // The member key.
|
||||||
v, // The member value.
|
v, // The member value.
|
||||||
length,
|
length, mind = gap,
|
||||||
mind = gap,
|
partial, value = holder[key];
|
||||||
partial,
|
|
||||||
value = holder[key];
|
|
||||||
|
|
||||||
// If the value has a toJSON method, call it to obtain a replacement value.
|
// If the value has a toJSON method, call it to obtain a replacement value.
|
||||||
|
if (value && typeof value === 'object' && typeof value.toJSON === 'function')
|
||||||
if (value && typeof value === 'object' &&
|
{
|
||||||
typeof value.toJSON === 'function') {
|
|
||||||
value = value.toJSON(key);
|
value = value.toJSON(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we were called with a replacer function, then call the replacer to
|
// If we were called with a replacer function, then call the replacer to
|
||||||
// obtain a replacement value.
|
// obtain a replacement value.
|
||||||
|
if (typeof rep === 'function')
|
||||||
if (typeof rep === 'function') {
|
{
|
||||||
value = rep.call(holder, key, value);
|
value = rep.call(holder, key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// What happens next depends on the value's type.
|
// What happens next depends on the value's type.
|
||||||
|
switch (typeof value)
|
||||||
switch (typeof value) {
|
{
|
||||||
case 'string':
|
case 'string':
|
||||||
return quote(value);
|
return quote(value);
|
||||||
|
|
||||||
case 'number':
|
case 'number':
|
||||||
|
|
||||||
// JSON numbers must be finite. Encode non-finite numbers as null.
|
// JSON numbers must be finite. Encode non-finite numbers as null.
|
||||||
|
|
||||||
return isFinite(value) ? String(value) : 'null';
|
return isFinite(value) ? String(value) : 'null';
|
||||||
|
|
||||||
case 'boolean':
|
case 'boolean':
|
||||||
|
@ -268,69 +259,70 @@ if (!JSON) {
|
||||||
// If the value is a boolean or null, convert it to a string. Note:
|
// If the value is a boolean or null, convert it to a string. Note:
|
||||||
// typeof null does not produce 'null'. The case is included here in
|
// typeof null does not produce 'null'. The case is included here in
|
||||||
// the remote chance that this gets fixed someday.
|
// the remote chance that this gets fixed someday.
|
||||||
|
|
||||||
return String(value);
|
return String(value);
|
||||||
|
|
||||||
// If the type is 'object', we might be dealing with an object or an array or
|
// If the type is 'object', we might be dealing with an object or an array or
|
||||||
// null.
|
// null.
|
||||||
|
|
||||||
case 'object':
|
case 'object':
|
||||||
|
|
||||||
// Due to a specification blunder in ECMAScript, typeof null is 'object',
|
// Due to a specification blunder in ECMAScript, typeof null is 'object',
|
||||||
// so watch out for that case.
|
// so watch out for that case.
|
||||||
|
if (!value)
|
||||||
if (!value) {
|
{
|
||||||
return 'null';
|
return 'null';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make an array to hold the partial results of stringifying this object value.
|
// Make an array to hold the partial results of stringifying this object value.
|
||||||
|
|
||||||
gap += indent;
|
gap += indent;
|
||||||
partial = [];
|
partial = [];
|
||||||
|
|
||||||
// Is the value an array?
|
// Is the value an array?
|
||||||
|
if (Object.prototype.toString.apply(value) === '[object Array]')
|
||||||
if (Object.prototype.toString.apply(value) === '[object Array]') {
|
{
|
||||||
|
|
||||||
// The value is an array. Stringify every element. Use null as a placeholder
|
// The value is an array. Stringify every element. Use null as a placeholder
|
||||||
// for non-JSON values.
|
// for non-JSON values.
|
||||||
|
|
||||||
length = value.length;
|
length = value.length;
|
||||||
for (i = 0; i < length; i += 1) {
|
for (i = 0; i < length; i += 1)
|
||||||
|
{
|
||||||
partial[i] = str(i, value) || 'null';
|
partial[i] = str(i, value) || 'null';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Join all of the elements together, separated with commas, and wrap them in
|
// Join all of the elements together, separated with commas, and wrap them in
|
||||||
// brackets.
|
// brackets.
|
||||||
|
v = partial.length === 0 ? '[]' : gap ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : '[' + partial.join(',') + ']';
|
||||||
v = partial.length === 0 ? '[]' : gap ?
|
|
||||||
'[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
|
|
||||||
'[' + partial.join(',') + ']';
|
|
||||||
gap = mind;
|
gap = mind;
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the replacer is an array, use it to select the members to be stringified.
|
// If the replacer is an array, use it to select the members to be stringified.
|
||||||
|
if (rep && typeof rep === 'object')
|
||||||
if (rep && typeof rep === 'object') {
|
{
|
||||||
length = rep.length;
|
length = rep.length;
|
||||||
for (i = 0; i < length; i += 1) {
|
for (i = 0; i < length; i += 1)
|
||||||
if (typeof rep[i] === 'string') {
|
{
|
||||||
|
if (typeof rep[i] === 'string')
|
||||||
|
{
|
||||||
k = rep[i];
|
k = rep[i];
|
||||||
v = str(k, value);
|
v = str(k, value);
|
||||||
if (v) {
|
if (v)
|
||||||
|
{
|
||||||
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
// Otherwise, iterate through all of the keys in the object.
|
// Otherwise, iterate through all of the keys in the object.
|
||||||
|
for (k in value)
|
||||||
for (k in value) {
|
{
|
||||||
if (Object.prototype.hasOwnProperty.call(value, k)) {
|
if (Object.prototype.hasOwnProperty.call(value, k))
|
||||||
|
{
|
||||||
v = str(k, value);
|
v = str(k, value);
|
||||||
if (v) {
|
if (v)
|
||||||
|
{
|
||||||
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -339,85 +331,89 @@ if (!JSON) {
|
||||||
|
|
||||||
// Join all of the member texts together, separated with commas,
|
// Join all of the member texts together, separated with commas,
|
||||||
// and wrap them in braces.
|
// and wrap them in braces.
|
||||||
|
v = partial.length === 0 ? '{}' : gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : '{' + partial.join(',') + '}';
|
||||||
v = partial.length === 0 ? '{}' : gap ?
|
|
||||||
'{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' :
|
|
||||||
'{' + partial.join(',') + '}';
|
|
||||||
gap = mind;
|
gap = mind;
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the JSON object does not yet have a stringify method, give it one.
|
// If the JSON object does not yet have a stringify method, give it one.
|
||||||
|
if (typeof JSON.stringify !== 'function')
|
||||||
if (typeof JSON.stringify !== 'function') {
|
{
|
||||||
JSON.stringify = function (value, replacer, space) {
|
JSON.stringify = function(value, replacer, space)
|
||||||
|
{
|
||||||
|
|
||||||
// The stringify method takes a value and an optional replacer, and an optional
|
// The stringify method takes a value and an optional replacer, and an optional
|
||||||
// space parameter, and returns a JSON text. The replacer can be a function
|
// space parameter, and returns a JSON text. The replacer can be a function
|
||||||
// that can replace values, or an array of strings that will select the keys.
|
// that can replace values, or an array of strings that will select the keys.
|
||||||
// A default replacer method can be provided. Use of the space parameter can
|
// A default replacer method can be provided. Use of the space parameter can
|
||||||
// produce text that is more easily readable.
|
// produce text that is more easily readable.
|
||||||
|
|
||||||
var i;
|
var i;
|
||||||
gap = '';
|
gap = '';
|
||||||
indent = '';
|
indent = '';
|
||||||
|
|
||||||
// If the space parameter is a number, make an indent string containing that
|
// If the space parameter is a number, make an indent string containing that
|
||||||
// many spaces.
|
// many spaces.
|
||||||
|
if (typeof space === 'number')
|
||||||
if (typeof space === 'number') {
|
{
|
||||||
for (i = 0; i < space; i += 1) {
|
for (i = 0; i < space; i += 1)
|
||||||
|
{
|
||||||
indent += ' ';
|
indent += ' ';
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the space parameter is a string, it will be used as the indent string.
|
// If the space parameter is a string, it will be used as the indent string.
|
||||||
|
}
|
||||||
} else if (typeof space === 'string') {
|
else if (typeof space === 'string')
|
||||||
|
{
|
||||||
indent = space;
|
indent = space;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is a replacer, it must be a function or an array.
|
// If there is a replacer, it must be a function or an array.
|
||||||
// Otherwise, throw an error.
|
// Otherwise, throw an error.
|
||||||
|
|
||||||
rep = replacer;
|
rep = replacer;
|
||||||
if (replacer && typeof replacer !== 'function' &&
|
if (replacer && typeof replacer !== 'function' && (typeof replacer !== 'object' || typeof replacer.length !== 'number'))
|
||||||
(typeof replacer !== 'object' ||
|
{
|
||||||
typeof replacer.length !== 'number')) {
|
|
||||||
throw new Error('JSON.stringify');
|
throw new Error('JSON.stringify');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a fake root object containing our value under the key of ''.
|
// Make a fake root object containing our value under the key of ''.
|
||||||
// Return the result of stringifying the value.
|
// Return the result of stringifying the value.
|
||||||
|
return str('', {
|
||||||
return str('', {'': value});
|
'': value
|
||||||
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// If the JSON object does not yet have a parse method, give it one.
|
// If the JSON object does not yet have a parse method, give it one.
|
||||||
|
if (typeof JSON.parse !== 'function')
|
||||||
if (typeof JSON.parse !== 'function') {
|
{
|
||||||
JSON.parse = function (text, reviver) {
|
JSON.parse = function(text, reviver)
|
||||||
|
{
|
||||||
|
|
||||||
// The parse method takes a text and an optional reviver function, and returns
|
// The parse method takes a text and an optional reviver function, and returns
|
||||||
// a JavaScript value if the text is a valid JSON text.
|
// a JavaScript value if the text is a valid JSON text.
|
||||||
|
|
||||||
var j;
|
var j;
|
||||||
|
|
||||||
function walk(holder, key) {
|
function walk(holder, key)
|
||||||
|
{
|
||||||
|
|
||||||
// The walk method is used to recursively walk the resulting structure so
|
// The walk method is used to recursively walk the resulting structure so
|
||||||
// that modifications can be made.
|
// that modifications can be made.
|
||||||
|
|
||||||
var k, v, value = holder[key];
|
var k, v, value = holder[key];
|
||||||
if (value && typeof value === 'object') {
|
if (value && typeof value === 'object')
|
||||||
for (k in value) {
|
{
|
||||||
if (Object.prototype.hasOwnProperty.call(value, k)) {
|
for (k in value)
|
||||||
|
{
|
||||||
|
if (Object.prototype.hasOwnProperty.call(value, k))
|
||||||
|
{
|
||||||
v = walk(value, k);
|
v = walk(value, k);
|
||||||
if (v !== undefined) {
|
if (v !== undefined)
|
||||||
|
{
|
||||||
value[k] = v;
|
value[k] = v;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
delete value[k];
|
delete value[k];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -430,13 +426,13 @@ if (!JSON) {
|
||||||
// Parsing happens in four stages. In the first stage, we replace certain
|
// Parsing happens in four stages. In the first stage, we replace certain
|
||||||
// Unicode characters with escape sequences. JavaScript handles many characters
|
// Unicode characters with escape sequences. JavaScript handles many characters
|
||||||
// incorrectly, either silently deleting them, or treating them as line endings.
|
// incorrectly, either silently deleting them, or treating them as line endings.
|
||||||
|
|
||||||
text = String(text);
|
text = String(text);
|
||||||
cx.lastIndex = 0;
|
cx.lastIndex = 0;
|
||||||
if (cx.test(text)) {
|
if (cx.test(text))
|
||||||
text = text.replace(cx, function (a) {
|
{
|
||||||
return '\\u' +
|
text = text.replace(cx, function(a)
|
||||||
('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
{
|
||||||
|
return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,7 +440,6 @@ if (!JSON) {
|
||||||
// for non-JSON patterns. We are especially concerned with '()' and 'new'
|
// for non-JSON patterns. We are especially concerned with '()' and 'new'
|
||||||
// because they can cause invocation, and '=' because it can cause mutation.
|
// because they can cause invocation, and '=' because it can cause mutation.
|
||||||
// But just to be safe, we want to reject all unexpected forms.
|
// But just to be safe, we want to reject all unexpected forms.
|
||||||
|
|
||||||
// We split the second stage into 4 regexp operations in order to work around
|
// We split the second stage into 4 regexp operations in order to work around
|
||||||
// crippling inefficiencies in IE's and Safari's regexp engines. First we
|
// crippling inefficiencies in IE's and Safari's regexp engines. First we
|
||||||
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
|
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
|
||||||
|
@ -452,28 +447,24 @@ if (!JSON) {
|
||||||
// open brackets that follow a colon or comma or that begin the text. Finally,
|
// open brackets that follow a colon or comma or that begin the text. Finally,
|
||||||
// we look to see that the remaining characters are only whitespace or ']' or
|
// we look to see that the remaining characters are only whitespace or ']' or
|
||||||
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
|
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
|
||||||
|
if (/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').replace(/(?:^|:|,)(?:\s*\[)+/g, '')))
|
||||||
if (/^[\],:{}\s]*$/
|
{
|
||||||
.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
|
|
||||||
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
|
|
||||||
.replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
|
|
||||||
|
|
||||||
// In the third stage we use the eval function to compile the text into a
|
// In the third stage we use the eval function to compile the text into a
|
||||||
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
|
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
|
||||||
// in JavaScript: it can begin a block or an object literal. We wrap the text
|
// in JavaScript: it can begin a block or an object literal. We wrap the text
|
||||||
// in parens to eliminate the ambiguity.
|
// in parens to eliminate the ambiguity.
|
||||||
|
|
||||||
j = eval('(' + text + ')');
|
j = eval('(' + text + ')');
|
||||||
|
|
||||||
// In the optional fourth stage, we recursively walk the new structure, passing
|
// In the optional fourth stage, we recursively walk the new structure, passing
|
||||||
// each name/value pair to a reviver function for possible transformation.
|
// each name/value pair to a reviver function for possible transformation.
|
||||||
|
return typeof reviver === 'function' ? walk(
|
||||||
return typeof reviver === 'function' ?
|
{
|
||||||
walk({'': j}, '') : j;
|
'': j
|
||||||
|
}, '') : j;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the text is not JSON parseable, then a SyntaxError is thrown.
|
// If the text is not JSON parseable, then a SyntaxError is thrown.
|
||||||
|
|
||||||
throw new SyntaxError('JSON.parse');
|
throw new SyntaxError('JSON.parse');
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.linestylefilter
|
// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.linestylefilter
|
||||||
// %APPJET%: import("etherpad.collab.ace.easysync2.Changeset");
|
// %APPJET%: import("etherpad.collab.ace.easysync2.Changeset");
|
||||||
// %APPJET%: import("etherpad.admin.plugins");
|
// %APPJET%: import("etherpad.admin.plugins");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copyright 2009 Google Inc.
|
* Copyright 2009 Google Inc.
|
||||||
*
|
*
|
||||||
|
@ -22,7 +21,6 @@
|
||||||
// requires: top
|
// requires: top
|
||||||
// requires: plugins
|
// requires: plugins
|
||||||
// requires: undefined
|
// requires: undefined
|
||||||
|
|
||||||
var linestylefilter = {};
|
var linestylefilter = {};
|
||||||
|
|
||||||
linestylefilter.ATTRIB_CLASSES = {
|
linestylefilter.ATTRIB_CLASSES = {
|
||||||
|
@ -32,8 +30,10 @@ linestylefilter.ATTRIB_CLASSES = {
|
||||||
'strikethrough': 'tag:s'
|
'strikethrough': 'tag:s'
|
||||||
};
|
};
|
||||||
|
|
||||||
linestylefilter.getAuthorClassName = function(author) {
|
linestylefilter.getAuthorClassName = function(author)
|
||||||
return "author-"+author.replace(/[^a-y0-9]/g, function(c) {
|
{
|
||||||
|
return "author-" + author.replace(/[^a-y0-9]/g, function(c)
|
||||||
|
{
|
||||||
if (c == ".") return "-";
|
if (c == ".") return "-";
|
||||||
return 'z' + c.charCodeAt(0) + 'z';
|
return 'z' + c.charCodeAt(0) + 'z';
|
||||||
});
|
});
|
||||||
|
@ -41,13 +41,16 @@ linestylefilter.getAuthorClassName = function(author) {
|
||||||
|
|
||||||
// lineLength is without newline; aline includes newline,
|
// lineLength is without newline; aline includes newline,
|
||||||
// but may be falsy if lineLength == 0
|
// but may be falsy if lineLength == 0
|
||||||
linestylefilter.getLineStyleFilter = function(lineLength, aline,
|
linestylefilter.getLineStyleFilter = function(lineLength, aline, textAndClassFunc, apool)
|
||||||
textAndClassFunc, apool) {
|
{
|
||||||
|
|
||||||
var plugins_;
|
var plugins_;
|
||||||
if (typeof(plugins)!='undefined') {
|
if (typeof(plugins) != 'undefined')
|
||||||
|
{
|
||||||
plugins_ = plugins;
|
plugins_ = plugins;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
plugins_ = parent.parent.plugins;
|
plugins_ = parent.parent.plugins;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,29 +58,43 @@ linestylefilter.getLineStyleFilter = function(lineLength, aline,
|
||||||
|
|
||||||
var nextAfterAuthorColors = textAndClassFunc;
|
var nextAfterAuthorColors = textAndClassFunc;
|
||||||
|
|
||||||
var authorColorFunc = (function() {
|
var authorColorFunc = (function()
|
||||||
|
{
|
||||||
var lineEnd = lineLength;
|
var lineEnd = lineLength;
|
||||||
var curIndex = 0;
|
var curIndex = 0;
|
||||||
var extraClasses;
|
var extraClasses;
|
||||||
var leftInAuthor;
|
var leftInAuthor;
|
||||||
|
|
||||||
function attribsToClasses(attribs) {
|
function attribsToClasses(attribs)
|
||||||
|
{
|
||||||
var classes = '';
|
var classes = '';
|
||||||
Changeset.eachAttribNumber(attribs, function(n) {
|
Changeset.eachAttribNumber(attribs, function(n)
|
||||||
|
{
|
||||||
var key = apool.getAttribKey(n);
|
var key = apool.getAttribKey(n);
|
||||||
if (key) {
|
if (key)
|
||||||
|
{
|
||||||
var value = apool.getAttribValue(n);
|
var value = apool.getAttribValue(n);
|
||||||
if (value) {
|
if (value)
|
||||||
if (key == 'author') {
|
{
|
||||||
|
if (key == 'author')
|
||||||
|
{
|
||||||
classes += ' ' + linestylefilter.getAuthorClassName(value);
|
classes += ' ' + linestylefilter.getAuthorClassName(value);
|
||||||
}
|
}
|
||||||
else if (key == 'list') {
|
else if (key == 'list')
|
||||||
|
{
|
||||||
classes += ' list:' + value;
|
classes += ' list:' + value;
|
||||||
}
|
}
|
||||||
else if (linestylefilter.ATTRIB_CLASSES[key]) {
|
else if (linestylefilter.ATTRIB_CLASSES[key])
|
||||||
|
{
|
||||||
classes += ' ' + linestylefilter.ATTRIB_CLASSES[key];
|
classes += ' ' + linestylefilter.ATTRIB_CLASSES[key];
|
||||||
} else {
|
}
|
||||||
classes += plugins_.callHookStr("aceAttribsToClasses", {linestylefilter:linestylefilter, key:key, value:value}, " ", " ", "");
|
else
|
||||||
|
{
|
||||||
|
classes += plugins_.callHookStr("aceAttribsToClasses", {
|
||||||
|
linestylefilter: linestylefilter,
|
||||||
|
key: key,
|
||||||
|
value: value
|
||||||
|
}, " ", " ", "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,17 +104,23 @@ linestylefilter.getLineStyleFilter = function(lineLength, aline,
|
||||||
|
|
||||||
var attributionIter = Changeset.opIterator(aline);
|
var attributionIter = Changeset.opIterator(aline);
|
||||||
var nextOp, nextOpClasses;
|
var nextOp, nextOpClasses;
|
||||||
function goNextOp() {
|
|
||||||
|
function goNextOp()
|
||||||
|
{
|
||||||
nextOp = attributionIter.next();
|
nextOp = attributionIter.next();
|
||||||
nextOpClasses = (nextOp.opcode && attribsToClasses(nextOp.attribs));
|
nextOpClasses = (nextOp.opcode && attribsToClasses(nextOp.attribs));
|
||||||
}
|
}
|
||||||
goNextOp();
|
goNextOp();
|
||||||
function nextClasses() {
|
|
||||||
if (curIndex < lineEnd) {
|
function nextClasses()
|
||||||
|
{
|
||||||
|
if (curIndex < lineEnd)
|
||||||
|
{
|
||||||
extraClasses = nextOpClasses;
|
extraClasses = nextOpClasses;
|
||||||
leftInAuthor = nextOp.chars;
|
leftInAuthor = nextOp.chars;
|
||||||
goNextOp();
|
goNextOp();
|
||||||
while (nextOp.opcode && nextOpClasses == extraClasses) {
|
while (nextOp.opcode && nextOpClasses == extraClasses)
|
||||||
|
{
|
||||||
leftInAuthor += nextOp.chars;
|
leftInAuthor += nextOp.chars;
|
||||||
goNextOp();
|
goNextOp();
|
||||||
}
|
}
|
||||||
|
@ -105,14 +128,18 @@ linestylefilter.getLineStyleFilter = function(lineLength, aline,
|
||||||
}
|
}
|
||||||
nextClasses();
|
nextClasses();
|
||||||
|
|
||||||
return function(txt, cls) {
|
return function(txt, cls)
|
||||||
while (txt.length > 0) {
|
{
|
||||||
if (leftInAuthor <= 0) {
|
while (txt.length > 0)
|
||||||
|
{
|
||||||
|
if (leftInAuthor <= 0)
|
||||||
|
{
|
||||||
// prevent infinite loop if something funny's going on
|
// prevent infinite loop if something funny's going on
|
||||||
return nextAfterAuthorColors(txt, cls);
|
return nextAfterAuthorColors(txt, cls);
|
||||||
}
|
}
|
||||||
var spanSize = txt.length;
|
var spanSize = txt.length;
|
||||||
if (spanSize > leftInAuthor) {
|
if (spanSize > leftInAuthor)
|
||||||
|
{
|
||||||
spanSize = leftInAuthor;
|
spanSize = leftInAuthor;
|
||||||
}
|
}
|
||||||
var curTxt = txt.substring(0, spanSize);
|
var curTxt = txt.substring(0, spanSize);
|
||||||
|
@ -120,7 +147,8 @@ linestylefilter.getLineStyleFilter = function(lineLength, aline,
|
||||||
nextAfterAuthorColors(curTxt, (cls && cls + " ") + extraClasses);
|
nextAfterAuthorColors(curTxt, (cls && cls + " ") + extraClasses);
|
||||||
curIndex += spanSize;
|
curIndex += spanSize;
|
||||||
leftInAuthor -= spanSize;
|
leftInAuthor -= spanSize;
|
||||||
if (leftInAuthor == 0) {
|
if (leftInAuthor == 0)
|
||||||
|
{
|
||||||
nextClasses();
|
nextClasses();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,14 +157,16 @@ linestylefilter.getLineStyleFilter = function(lineLength, aline,
|
||||||
return authorColorFunc;
|
return authorColorFunc;
|
||||||
};
|
};
|
||||||
|
|
||||||
linestylefilter.getAtSignSplitterFilter = function(lineText,
|
linestylefilter.getAtSignSplitterFilter = function(lineText, textAndClassFunc)
|
||||||
textAndClassFunc) {
|
{
|
||||||
var at = /@/g;
|
var at = /@/g;
|
||||||
at.lastIndex = 0;
|
at.lastIndex = 0;
|
||||||
var splitPoints = null;
|
var splitPoints = null;
|
||||||
var execResult;
|
var execResult;
|
||||||
while ((execResult = at.exec(lineText))) {
|
while ((execResult = at.exec(lineText)))
|
||||||
if (! splitPoints) {
|
{
|
||||||
|
if (!splitPoints)
|
||||||
|
{
|
||||||
splitPoints = [];
|
splitPoints = [];
|
||||||
}
|
}
|
||||||
splitPoints.push(execResult.index);
|
splitPoints.push(execResult.index);
|
||||||
|
@ -144,18 +174,21 @@ linestylefilter.getAtSignSplitterFilter = function(lineText,
|
||||||
|
|
||||||
if (!splitPoints) return textAndClassFunc;
|
if (!splitPoints) return textAndClassFunc;
|
||||||
|
|
||||||
return linestylefilter.textAndClassFuncSplitter(textAndClassFunc,
|
return linestylefilter.textAndClassFuncSplitter(textAndClassFunc, splitPoints);
|
||||||
splitPoints);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
linestylefilter.getRegexpFilter = function (regExp, tag) {
|
linestylefilter.getRegexpFilter = function(regExp, tag)
|
||||||
return function (lineText, textAndClassFunc) {
|
{
|
||||||
|
return function(lineText, textAndClassFunc)
|
||||||
|
{
|
||||||
regExp.lastIndex = 0;
|
regExp.lastIndex = 0;
|
||||||
var regExpMatchs = null;
|
var regExpMatchs = null;
|
||||||
var splitPoints = null;
|
var splitPoints = null;
|
||||||
var execResult;
|
var execResult;
|
||||||
while ((execResult = regExp.exec(lineText))) {
|
while ((execResult = regExp.exec(lineText)))
|
||||||
if (! regExpMatchs) {
|
{
|
||||||
|
if (!regExpMatchs)
|
||||||
|
{
|
||||||
regExpMatchs = [];
|
regExpMatchs = [];
|
||||||
splitPoints = [];
|
splitPoints = [];
|
||||||
}
|
}
|
||||||
|
@ -167,23 +200,29 @@ linestylefilter.getRegexpFilter = function (regExp, tag) {
|
||||||
|
|
||||||
if (!regExpMatchs) return textAndClassFunc;
|
if (!regExpMatchs) return textAndClassFunc;
|
||||||
|
|
||||||
function regExpMatchForIndex(idx) {
|
function regExpMatchForIndex(idx)
|
||||||
for(var k=0; k<regExpMatchs.length; k++) {
|
{
|
||||||
|
for (var k = 0; k < regExpMatchs.length; k++)
|
||||||
|
{
|
||||||
var u = regExpMatchs[k];
|
var u = regExpMatchs[k];
|
||||||
if (idx >= u[0] && idx < u[0]+u[1].length) {
|
if (idx >= u[0] && idx < u[0] + u[1].length)
|
||||||
|
{
|
||||||
return u[1];
|
return u[1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var handleRegExpMatchsAfterSplit = (function() {
|
var handleRegExpMatchsAfterSplit = (function()
|
||||||
|
{
|
||||||
var curIndex = 0;
|
var curIndex = 0;
|
||||||
return function(txt, cls) {
|
return function(txt, cls)
|
||||||
|
{
|
||||||
var txtlen = txt.length;
|
var txtlen = txt.length;
|
||||||
var newCls = cls;
|
var newCls = cls;
|
||||||
var regExpMatch = regExpMatchForIndex(curIndex);
|
var regExpMatch = regExpMatchForIndex(curIndex);
|
||||||
if (regExpMatch) {
|
if (regExpMatch)
|
||||||
|
{
|
||||||
newCls += " " + tag + ":" + regExpMatch;
|
newCls += " " + tag + ":" + regExpMatch;
|
||||||
}
|
}
|
||||||
textAndClassFunc(txt, newCls);
|
textAndClassFunc(txt, newCls);
|
||||||
|
@ -191,8 +230,7 @@ linestylefilter.getRegexpFilter = function (regExp, tag) {
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
return linestylefilter.textAndClassFuncSplitter(handleRegExpMatchsAfterSplit,
|
return linestylefilter.textAndClassFuncSplitter(handleRegExpMatchsAfterSplit, splitPoints);
|
||||||
splitPoints);
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -203,35 +241,42 @@ linestylefilter.REGEX_URL = new RegExp(/(?:(?:https?|s?ftp|ftps|file|smb|afp|nfs
|
||||||
linestylefilter.getURLFilter = linestylefilter.getRegexpFilter(
|
linestylefilter.getURLFilter = linestylefilter.getRegexpFilter(
|
||||||
linestylefilter.REGEX_URL, 'url');
|
linestylefilter.REGEX_URL, 'url');
|
||||||
|
|
||||||
linestylefilter.textAndClassFuncSplitter = function(func, splitPointsOpt) {
|
linestylefilter.textAndClassFuncSplitter = function(func, splitPointsOpt)
|
||||||
|
{
|
||||||
var nextPointIndex = 0;
|
var nextPointIndex = 0;
|
||||||
var idx = 0;
|
var idx = 0;
|
||||||
|
|
||||||
// don't split at 0
|
// don't split at 0
|
||||||
while (splitPointsOpt &&
|
while (splitPointsOpt && nextPointIndex < splitPointsOpt.length && splitPointsOpt[nextPointIndex] == 0)
|
||||||
nextPointIndex < splitPointsOpt.length &&
|
{
|
||||||
splitPointsOpt[nextPointIndex] == 0) {
|
|
||||||
nextPointIndex++;
|
nextPointIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
function spanHandler(txt, cls) {
|
function spanHandler(txt, cls)
|
||||||
if ((! splitPointsOpt) || nextPointIndex >= splitPointsOpt.length) {
|
{
|
||||||
|
if ((!splitPointsOpt) || nextPointIndex >= splitPointsOpt.length)
|
||||||
|
{
|
||||||
func(txt, cls);
|
func(txt, cls);
|
||||||
idx += txt.length;
|
idx += txt.length;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
var splitPoints = splitPointsOpt;
|
var splitPoints = splitPointsOpt;
|
||||||
var pointLocInSpan = splitPoints[nextPointIndex] - idx;
|
var pointLocInSpan = splitPoints[nextPointIndex] - idx;
|
||||||
var txtlen = txt.length;
|
var txtlen = txt.length;
|
||||||
if (pointLocInSpan >= txtlen) {
|
if (pointLocInSpan >= txtlen)
|
||||||
|
{
|
||||||
func(txt, cls);
|
func(txt, cls);
|
||||||
idx += txt.length;
|
idx += txt.length;
|
||||||
if (pointLocInSpan == txtlen) {
|
if (pointLocInSpan == txtlen)
|
||||||
|
{
|
||||||
nextPointIndex++;
|
nextPointIndex++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
if (pointLocInSpan > 0) {
|
{
|
||||||
|
if (pointLocInSpan > 0)
|
||||||
|
{
|
||||||
func(txt.substring(0, pointLocInSpan), cls);
|
func(txt.substring(0, pointLocInSpan), cls);
|
||||||
idx += pointLocInSpan;
|
idx += pointLocInSpan;
|
||||||
}
|
}
|
||||||
|
@ -244,23 +289,31 @@ linestylefilter.textAndClassFuncSplitter = function(func, splitPointsOpt) {
|
||||||
return spanHandler;
|
return spanHandler;
|
||||||
};
|
};
|
||||||
|
|
||||||
linestylefilter.getFilterStack = function(lineText, textAndClassFunc, browser) {
|
linestylefilter.getFilterStack = function(lineText, textAndClassFunc, browser)
|
||||||
|
{
|
||||||
var func = linestylefilter.getURLFilter(lineText, textAndClassFunc);
|
var func = linestylefilter.getURLFilter(lineText, textAndClassFunc);
|
||||||
|
|
||||||
var plugins_;
|
var plugins_;
|
||||||
if (typeof(plugins)!='undefined') {
|
if (typeof(plugins) != 'undefined')
|
||||||
|
{
|
||||||
plugins_ = plugins;
|
plugins_ = plugins;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
plugins_ = parent.parent.plugins;
|
plugins_ = parent.parent.plugins;
|
||||||
}
|
}
|
||||||
|
|
||||||
var hookFilters = plugins_.callHook(
|
var hookFilters = plugins_.callHook("aceGetFilterStack", {
|
||||||
"aceGetFilterStack", {linestylefilter:linestylefilter, browser:browser});
|
linestylefilter: linestylefilter,
|
||||||
hookFilters.map(function (hookFilter) {
|
browser: browser
|
||||||
|
});
|
||||||
|
hookFilters.map(function(hookFilter)
|
||||||
|
{
|
||||||
func = hookFilter(lineText, func);
|
func = hookFilter(lineText, func);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (browser !== undefined && browser.msie) {
|
if (browser !== undefined && browser.msie)
|
||||||
|
{
|
||||||
// IE7+ will take an e-mail address like <foo@bar.com> and linkify it to foo@bar.com.
|
// IE7+ will take an e-mail address like <foo@bar.com> and linkify it to foo@bar.com.
|
||||||
// We then normalize it back to text with no angle brackets. It's weird. So always
|
// We then normalize it back to text with no angle brackets. It's weird. So always
|
||||||
// break spans at an "at" sign.
|
// break spans at an "at" sign.
|
||||||
|
@ -271,20 +324,21 @@ linestylefilter.getFilterStack = function(lineText, textAndClassFunc, browser) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// domLineObj is like that returned by domline.createDomLine
|
// domLineObj is like that returned by domline.createDomLine
|
||||||
linestylefilter.populateDomLine = function(textLine, aline, apool,
|
linestylefilter.populateDomLine = function(textLine, aline, apool, domLineObj)
|
||||||
domLineObj) {
|
{
|
||||||
// remove final newline from text if any
|
// remove final newline from text if any
|
||||||
var text = textLine;
|
var text = textLine;
|
||||||
if (text.slice(-1) == '\n') {
|
if (text.slice(-1) == '\n')
|
||||||
|
{
|
||||||
text = text.substring(0, text.length - 1);
|
text = text.substring(0, text.length - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function textAndClassFunc(tokenText, tokenClass) {
|
function textAndClassFunc(tokenText, tokenClass)
|
||||||
|
{
|
||||||
domLineObj.appendSpan(tokenText, tokenClass);
|
domLineObj.appendSpan(tokenText, tokenClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
var func = linestylefilter.getFilterStack(text, textAndClassFunc);
|
var func = linestylefilter.getFilterStack(text, textAndClassFunc);
|
||||||
func = linestylefilter.getLineStyleFilter(text.length, aline,
|
func = linestylefilter.getLineStyleFilter(text.length, aline, func, apool);
|
||||||
func, apool);
|
|
||||||
func(text, '');
|
func(text, '');
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,16 +18,20 @@
|
||||||
|
|
||||||
var socket;
|
var socket;
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function()
|
||||||
|
{
|
||||||
handshake();
|
handshake();
|
||||||
});
|
});
|
||||||
|
|
||||||
$(window).unload(function() {
|
$(window).unload(function()
|
||||||
|
{
|
||||||
pad.dispose();
|
pad.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
function createCookie(name,value,days) {
|
function createCookie(name, value, days)
|
||||||
if (days) {
|
{
|
||||||
|
if (days)
|
||||||
|
{
|
||||||
var date = new Date();
|
var date = new Date();
|
||||||
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||||
var expires = "; expires=" + date.toGMTString();
|
var expires = "; expires=" + date.toGMTString();
|
||||||
|
@ -36,10 +40,12 @@ function createCookie(name,value,days) {
|
||||||
document.cookie = name + "=" + value + expires + "; path=/";
|
document.cookie = name + "=" + value + expires + "; path=/";
|
||||||
}
|
}
|
||||||
|
|
||||||
function readCookie(name) {
|
function readCookie(name)
|
||||||
|
{
|
||||||
var nameEQ = name + "=";
|
var nameEQ = name + "=";
|
||||||
var ca = document.cookie.split(';');
|
var ca = document.cookie.split(';');
|
||||||
for(var i=0;i < ca.length;i++) {
|
for (var i = 0; i < ca.length; i++)
|
||||||
|
{
|
||||||
var c = ca[i];
|
var c = ca[i];
|
||||||
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
|
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
|
||||||
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
|
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
|
||||||
|
@ -47,11 +53,13 @@ function readCookie(name) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function randomString() {
|
function randomString()
|
||||||
|
{
|
||||||
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
|
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
|
||||||
var string_length = 20;
|
var string_length = 20;
|
||||||
var randomstring = '';
|
var randomstring = '';
|
||||||
for (var i=0; i<string_length; i++) {
|
for (var i = 0; i < string_length; i++)
|
||||||
|
{
|
||||||
var rnum = Math.floor(Math.random() * chars.length);
|
var rnum = Math.floor(Math.random() * chars.length);
|
||||||
randomstring += chars.substring(rnum, rnum + 1);
|
randomstring += chars.substring(rnum, rnum + 1);
|
||||||
}
|
}
|
||||||
|
@ -70,9 +78,12 @@ function handshake()
|
||||||
var resource = loc.pathname.substr(1, loc.pathname.indexOf("/p/")) + "socket.io";
|
var resource = loc.pathname.substr(1, loc.pathname.indexOf("/p/")) + "socket.io";
|
||||||
console.log(resource);
|
console.log(resource);
|
||||||
//connect
|
//connect
|
||||||
socket = io.connect(url, {resource: resource});
|
socket = io.connect(url, {
|
||||||
|
resource: resource
|
||||||
|
});
|
||||||
|
|
||||||
socket.on('connect', function(){
|
socket.on('connect', function()
|
||||||
|
{
|
||||||
var padId = document.URL.substring(document.URL.lastIndexOf("/") + 1);
|
var padId = document.URL.substring(document.URL.lastIndexOf("/") + 1);
|
||||||
|
|
||||||
document.title = document.title + " | " + padId;
|
document.title = document.title + " | " + padId;
|
||||||
|
@ -84,11 +95,13 @@ function handshake()
|
||||||
createCookie("token", token, 60);
|
createCookie("token", token, 60);
|
||||||
}
|
}
|
||||||
|
|
||||||
var msg = { "component" : "pad",
|
var msg = {
|
||||||
|
"component": "pad",
|
||||||
"type": "CLIENT_READY",
|
"type": "CLIENT_READY",
|
||||||
"padId": padId,
|
"padId": padId,
|
||||||
"token": token,
|
"token": token,
|
||||||
"protocolVersion": 2};
|
"protocolVersion": 2
|
||||||
|
};
|
||||||
|
|
||||||
socket.json.send(msg);
|
socket.json.send(msg);
|
||||||
});
|
});
|
||||||
|
@ -96,12 +109,12 @@ function handshake()
|
||||||
var receivedClientVars = false;
|
var receivedClientVars = false;
|
||||||
var initalized = false;
|
var initalized = false;
|
||||||
|
|
||||||
socket.on('message', function(obj){
|
socket.on('message', function(obj)
|
||||||
|
{
|
||||||
//if we haven't recieved the clientVars yet, then this message should it be
|
//if we haven't recieved the clientVars yet, then this message should it be
|
||||||
if (!receivedClientVars)
|
if (!receivedClientVars)
|
||||||
{
|
{
|
||||||
if(window.console)
|
if (window.console) console.log(obj);
|
||||||
console.log(obj);
|
|
||||||
|
|
||||||
receivedClientVars = true;
|
receivedClientVars = true;
|
||||||
|
|
||||||
|
@ -142,43 +155,75 @@ var pad = {
|
||||||
padOptions: {},
|
padOptions: {},
|
||||||
|
|
||||||
// these don't require init; clientVars should all go through here
|
// these don't require init; clientVars should all go through here
|
||||||
getPadId: function() { return clientVars.padId; },
|
getPadId: function()
|
||||||
getClientIp: function() { return clientVars.clientIp; },
|
{
|
||||||
getIsProPad: function() { return clientVars.isProPad; },
|
return clientVars.padId;
|
||||||
getColorPalette: function() { return clientVars.colorPalette; },
|
},
|
||||||
getDisplayUserAgent: function() {
|
getClientIp: function()
|
||||||
|
{
|
||||||
|
return clientVars.clientIp;
|
||||||
|
},
|
||||||
|
getIsProPad: function()
|
||||||
|
{
|
||||||
|
return clientVars.isProPad;
|
||||||
|
},
|
||||||
|
getColorPalette: function()
|
||||||
|
{
|
||||||
|
return clientVars.colorPalette;
|
||||||
|
},
|
||||||
|
getDisplayUserAgent: function()
|
||||||
|
{
|
||||||
return padutils.uaDisplay(clientVars.userAgent);
|
return padutils.uaDisplay(clientVars.userAgent);
|
||||||
},
|
},
|
||||||
getIsDebugEnabled: function() { return clientVars.debugEnabled; },
|
getIsDebugEnabled: function()
|
||||||
getPrivilege: function(name) { return clientVars.accountPrivs[name]; },
|
{
|
||||||
getUserIsGuest: function() { return clientVars.userIsGuest; },
|
return clientVars.debugEnabled;
|
||||||
|
},
|
||||||
|
getPrivilege: function(name)
|
||||||
|
{
|
||||||
|
return clientVars.accountPrivs[name];
|
||||||
|
},
|
||||||
|
getUserIsGuest: function()
|
||||||
|
{
|
||||||
|
return clientVars.userIsGuest;
|
||||||
|
},
|
||||||
//
|
//
|
||||||
|
getUserId: function()
|
||||||
getUserId: function() { return pad.myUserInfo.userId; },
|
{
|
||||||
getUserName: function() { return pad.myUserInfo.name; },
|
return pad.myUserInfo.userId;
|
||||||
sendClientMessage: function(msg) {
|
},
|
||||||
|
getUserName: function()
|
||||||
|
{
|
||||||
|
return pad.myUserInfo.name;
|
||||||
|
},
|
||||||
|
sendClientMessage: function(msg)
|
||||||
|
{
|
||||||
pad.collabClient.sendClientMessage(msg);
|
pad.collabClient.sendClientMessage(msg);
|
||||||
},
|
},
|
||||||
|
|
||||||
init: function() {
|
init: function()
|
||||||
|
{
|
||||||
pad.diagnosticInfo.uniqueId = padutils.uniqueId();
|
pad.diagnosticInfo.uniqueId = padutils.uniqueId();
|
||||||
pad.initTime = +(new Date());
|
pad.initTime = +(new Date());
|
||||||
pad.padOptions = clientVars.initialOptions;
|
pad.padOptions = clientVars.initialOptions;
|
||||||
|
|
||||||
if ((! $.browser.msie) &&
|
if ((!$.browser.msie) && (!($.browser.mozilla && $.browser.version.indexOf("1.8.") == 0)))
|
||||||
(! ($.browser.mozilla && $.browser.version.indexOf("1.8.") == 0))) {
|
{
|
||||||
document.domain = document.domain; // for comet
|
document.domain = document.domain; // for comet
|
||||||
}
|
}
|
||||||
|
|
||||||
// for IE
|
// for IE
|
||||||
if ($.browser.msie) {
|
if ($.browser.msie)
|
||||||
try {
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
doc.execCommand("BackgroundImageCache", false, true);
|
doc.execCommand("BackgroundImageCache", false, true);
|
||||||
} catch (e) {}
|
}
|
||||||
|
catch (e)
|
||||||
|
{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// order of inits is important here:
|
// order of inits is important here:
|
||||||
|
|
||||||
padcookie.init(clientVars.cookiePrefsToSet);
|
padcookie.init(clientVars.cookiePrefsToSet);
|
||||||
|
|
||||||
$("#widthprefcheck").click(pad.toggleWidthPref);
|
$("#widthprefcheck").click(pad.toggleWidthPref);
|
||||||
|
@ -191,14 +236,17 @@ var pad = {
|
||||||
colorId: clientVars.userColor,
|
colorId: clientVars.userColor,
|
||||||
userAgent: pad.getDisplayUserAgent()
|
userAgent: pad.getDisplayUserAgent()
|
||||||
};
|
};
|
||||||
if (clientVars.specialKey) {
|
if (clientVars.specialKey)
|
||||||
|
{
|
||||||
pad.myUserInfo.specialKey = clientVars.specialKey;
|
pad.myUserInfo.specialKey = clientVars.specialKey;
|
||||||
if (clientVars.specialKeyTranslation) {
|
if (clientVars.specialKeyTranslation)
|
||||||
$("#specialkeyarea").html("mode: "+
|
{
|
||||||
String(clientVars.specialKeyTranslation).toUpperCase());
|
$("#specialkeyarea").html("mode: " + String(clientVars.specialKeyTranslation).toUpperCase());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
paddocbar.init({isTitleEditable: pad.getIsProPad(),
|
paddocbar.init(
|
||||||
|
{
|
||||||
|
isTitleEditable: pad.getIsProPad(),
|
||||||
initialTitle: clientVars.initialTitle,
|
initialTitle: clientVars.initialTitle,
|
||||||
initialPassword: clientVars.initialPassword,
|
initialPassword: clientVars.initialPassword,
|
||||||
guestPolicy: pad.padOptions.guestPolicy
|
guestPolicy: pad.padOptions.guestPolicy
|
||||||
|
@ -213,11 +261,9 @@ var pad = {
|
||||||
padconnectionstatus.init();
|
padconnectionstatus.init();
|
||||||
padmodals.init();
|
padmodals.init();
|
||||||
|
|
||||||
pad.collabClient =
|
pad.collabClient = getCollabClient(padeditor.ace, clientVars.collab_client_vars, pad.myUserInfo, {
|
||||||
getCollabClient(padeditor.ace,
|
colorPalette: pad.getColorPalette()
|
||||||
clientVars.collab_client_vars,
|
});
|
||||||
pad.myUserInfo,
|
|
||||||
{ colorPalette: pad.getColorPalette() });
|
|
||||||
pad.collabClient.setOnUserJoin(pad.handleUserJoin);
|
pad.collabClient.setOnUserJoin(pad.handleUserJoin);
|
||||||
pad.collabClient.setOnUpdateUserInfo(pad.handleUserUpdate);
|
pad.collabClient.setOnUpdateUserInfo(pad.handleUserUpdate);
|
||||||
pad.collabClient.setOnUserLeave(pad.handleUserLeave);
|
pad.collabClient.setOnUserLeave(pad.handleUserLeave);
|
||||||
|
@ -226,180 +272,235 @@ var pad = {
|
||||||
pad.collabClient.setOnChannelStateChange(pad.handleChannelStateChange);
|
pad.collabClient.setOnChannelStateChange(pad.handleChannelStateChange);
|
||||||
pad.collabClient.setOnInternalAction(pad.handleCollabAction);
|
pad.collabClient.setOnInternalAction(pad.handleCollabAction);
|
||||||
|
|
||||||
function postAceInit() {
|
function postAceInit()
|
||||||
|
{
|
||||||
padeditbar.init();
|
padeditbar.init();
|
||||||
setTimeout(function() { padeditor.ace.focus(); }, 0);
|
setTimeout(function()
|
||||||
|
{
|
||||||
|
padeditor.ace.focus();
|
||||||
|
}, 0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dispose: function() {
|
dispose: function()
|
||||||
|
{
|
||||||
padeditor.dispose();
|
padeditor.dispose();
|
||||||
},
|
},
|
||||||
notifyChangeName: function(newName) {
|
notifyChangeName: function(newName)
|
||||||
|
{
|
||||||
pad.myUserInfo.name = newName;
|
pad.myUserInfo.name = newName;
|
||||||
pad.collabClient.updateUserInfo(pad.myUserInfo);
|
pad.collabClient.updateUserInfo(pad.myUserInfo);
|
||||||
//padchat.handleUserJoinOrUpdate(pad.myUserInfo);
|
//padchat.handleUserJoinOrUpdate(pad.myUserInfo);
|
||||||
},
|
},
|
||||||
notifyChangeColor: function(newColorId) {
|
notifyChangeColor: function(newColorId)
|
||||||
|
{
|
||||||
pad.myUserInfo.colorId = newColorId;
|
pad.myUserInfo.colorId = newColorId;
|
||||||
pad.collabClient.updateUserInfo(pad.myUserInfo);
|
pad.collabClient.updateUserInfo(pad.myUserInfo);
|
||||||
//padchat.handleUserJoinOrUpdate(pad.myUserInfo);
|
//padchat.handleUserJoinOrUpdate(pad.myUserInfo);
|
||||||
},
|
},
|
||||||
notifyChangeTitle: function(newTitle) {
|
notifyChangeTitle: function(newTitle)
|
||||||
pad.collabClient.sendClientMessage({
|
{
|
||||||
|
pad.collabClient.sendClientMessage(
|
||||||
|
{
|
||||||
type: 'padtitle',
|
type: 'padtitle',
|
||||||
title: newTitle,
|
title: newTitle,
|
||||||
changedBy: pad.myUserInfo.name || "unnamed"
|
changedBy: pad.myUserInfo.name || "unnamed"
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
notifyChangePassword: function(newPass) {
|
notifyChangePassword: function(newPass)
|
||||||
pad.collabClient.sendClientMessage({
|
{
|
||||||
|
pad.collabClient.sendClientMessage(
|
||||||
|
{
|
||||||
type: 'padpassword',
|
type: 'padpassword',
|
||||||
password: newPass,
|
password: newPass,
|
||||||
changedBy: pad.myUserInfo.name || "unnamed"
|
changedBy: pad.myUserInfo.name || "unnamed"
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
changePadOption: function(key, value) {
|
changePadOption: function(key, value)
|
||||||
|
{
|
||||||
var options = {};
|
var options = {};
|
||||||
options[key] = value;
|
options[key] = value;
|
||||||
pad.handleOptionsChange(options);
|
pad.handleOptionsChange(options);
|
||||||
pad.collabClient.sendClientMessage({
|
pad.collabClient.sendClientMessage(
|
||||||
|
{
|
||||||
type: 'padoptions',
|
type: 'padoptions',
|
||||||
options: options,
|
options: options,
|
||||||
changedBy: pad.myUserInfo.name || "unnamed"
|
changedBy: pad.myUserInfo.name || "unnamed"
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
changeViewOption: function(key, value) {
|
changeViewOption: function(key, value)
|
||||||
var options = {view: {}};
|
{
|
||||||
|
var options = {
|
||||||
|
view: {}
|
||||||
|
};
|
||||||
options.view[key] = value;
|
options.view[key] = value;
|
||||||
pad.handleOptionsChange(options);
|
pad.handleOptionsChange(options);
|
||||||
pad.collabClient.sendClientMessage({
|
pad.collabClient.sendClientMessage(
|
||||||
|
{
|
||||||
type: 'padoptions',
|
type: 'padoptions',
|
||||||
options: options,
|
options: options,
|
||||||
changedBy: pad.myUserInfo.name || "unnamed"
|
changedBy: pad.myUserInfo.name || "unnamed"
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleOptionsChange: function(opts) {
|
handleOptionsChange: function(opts)
|
||||||
|
{
|
||||||
// opts object is a full set of options or just
|
// opts object is a full set of options or just
|
||||||
// some options to change
|
// some options to change
|
||||||
if (opts.view) {
|
if (opts.view)
|
||||||
if (! pad.padOptions.view) {
|
{
|
||||||
|
if (!pad.padOptions.view)
|
||||||
|
{
|
||||||
pad.padOptions.view = {};
|
pad.padOptions.view = {};
|
||||||
}
|
}
|
||||||
for(var k in opts.view) {
|
for (var k in opts.view)
|
||||||
|
{
|
||||||
pad.padOptions.view[k] = opts.view[k];
|
pad.padOptions.view[k] = opts.view[k];
|
||||||
}
|
}
|
||||||
padeditor.setViewOptions(pad.padOptions.view);
|
padeditor.setViewOptions(pad.padOptions.view);
|
||||||
}
|
}
|
||||||
if (opts.guestPolicy) {
|
if (opts.guestPolicy)
|
||||||
|
{
|
||||||
// order important here
|
// order important here
|
||||||
pad.padOptions.guestPolicy = opts.guestPolicy;
|
pad.padOptions.guestPolicy = opts.guestPolicy;
|
||||||
paddocbar.setGuestPolicy(opts.guestPolicy);
|
paddocbar.setGuestPolicy(opts.guestPolicy);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getPadOptions: function() {
|
getPadOptions: function()
|
||||||
|
{
|
||||||
// caller shouldn't mutate the object
|
// caller shouldn't mutate the object
|
||||||
return pad.padOptions;
|
return pad.padOptions;
|
||||||
},
|
},
|
||||||
isPadPublic: function() {
|
isPadPublic: function()
|
||||||
|
{
|
||||||
return (!pad.getIsProPad()) || (pad.getPadOptions().guestPolicy == 'allow');
|
return (!pad.getIsProPad()) || (pad.getPadOptions().guestPolicy == 'allow');
|
||||||
},
|
},
|
||||||
suggestUserName: function(userId, name) {
|
suggestUserName: function(userId, name)
|
||||||
pad.collabClient.sendClientMessage({
|
{
|
||||||
|
pad.collabClient.sendClientMessage(
|
||||||
|
{
|
||||||
type: 'suggestUserName',
|
type: 'suggestUserName',
|
||||||
unnamedId: userId,
|
unnamedId: userId,
|
||||||
newName: name
|
newName: name
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleUserJoin: function(userInfo) {
|
handleUserJoin: function(userInfo)
|
||||||
|
{
|
||||||
paduserlist.userJoinOrUpdate(userInfo);
|
paduserlist.userJoinOrUpdate(userInfo);
|
||||||
//padchat.handleUserJoinOrUpdate(userInfo);
|
//padchat.handleUserJoinOrUpdate(userInfo);
|
||||||
},
|
},
|
||||||
handleUserUpdate: function(userInfo) {
|
handleUserUpdate: function(userInfo)
|
||||||
|
{
|
||||||
paduserlist.userJoinOrUpdate(userInfo);
|
paduserlist.userJoinOrUpdate(userInfo);
|
||||||
//padchat.handleUserJoinOrUpdate(userInfo);
|
//padchat.handleUserJoinOrUpdate(userInfo);
|
||||||
},
|
},
|
||||||
handleUserLeave: function(userInfo) {
|
handleUserLeave: function(userInfo)
|
||||||
|
{
|
||||||
paduserlist.userLeave(userInfo);
|
paduserlist.userLeave(userInfo);
|
||||||
//padchat.handleUserLeave(userInfo);
|
//padchat.handleUserLeave(userInfo);
|
||||||
},
|
},
|
||||||
handleClientMessage: function(msg) {
|
handleClientMessage: function(msg)
|
||||||
if (msg.type == 'suggestUserName') {
|
{
|
||||||
if (msg.unnamedId == pad.myUserInfo.userId && msg.newName &&
|
if (msg.type == 'suggestUserName')
|
||||||
! pad.myUserInfo.name) {
|
{
|
||||||
|
if (msg.unnamedId == pad.myUserInfo.userId && msg.newName && !pad.myUserInfo.name)
|
||||||
|
{
|
||||||
pad.notifyChangeName(msg.newName);
|
pad.notifyChangeName(msg.newName);
|
||||||
paduserlist.setMyUserInfo(pad.myUserInfo);
|
paduserlist.setMyUserInfo(pad.myUserInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (msg.type == 'chat') {
|
else if (msg.type == 'chat')
|
||||||
|
{
|
||||||
//padchat.receiveChat(msg);
|
//padchat.receiveChat(msg);
|
||||||
}
|
}
|
||||||
else if (msg.type == 'padtitle') {
|
else if (msg.type == 'padtitle')
|
||||||
|
{
|
||||||
paddocbar.changeTitle(msg.title);
|
paddocbar.changeTitle(msg.title);
|
||||||
}
|
}
|
||||||
else if (msg.type == 'padpassword') {
|
else if (msg.type == 'padpassword')
|
||||||
|
{
|
||||||
paddocbar.changePassword(msg.password);
|
paddocbar.changePassword(msg.password);
|
||||||
}
|
}
|
||||||
else if (msg.type == 'newRevisionList') {
|
else if (msg.type == 'newRevisionList')
|
||||||
|
{
|
||||||
padsavedrevs.newRevisionList(msg.revisionList);
|
padsavedrevs.newRevisionList(msg.revisionList);
|
||||||
}
|
}
|
||||||
else if (msg.type == 'revisionLabel') {
|
else if (msg.type == 'revisionLabel')
|
||||||
|
{
|
||||||
padsavedrevs.newRevisionList(msg.revisionList);
|
padsavedrevs.newRevisionList(msg.revisionList);
|
||||||
}
|
}
|
||||||
else if (msg.type == 'padoptions') {
|
else if (msg.type == 'padoptions')
|
||||||
|
{
|
||||||
var opts = msg.options;
|
var opts = msg.options;
|
||||||
pad.handleOptionsChange(opts);
|
pad.handleOptionsChange(opts);
|
||||||
}
|
}
|
||||||
else if (msg.type == 'guestanswer') {
|
else if (msg.type == 'guestanswer')
|
||||||
|
{
|
||||||
// someone answered a prompt, remove it
|
// someone answered a prompt, remove it
|
||||||
paduserlist.removeGuestPrompt(msg.guestId);
|
paduserlist.removeGuestPrompt(msg.guestId);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
editbarClick: function(cmd) {
|
editbarClick: function(cmd)
|
||||||
if (padeditbar) {
|
{
|
||||||
|
if (padeditbar)
|
||||||
|
{
|
||||||
padeditbar.toolbarClick(cmd);
|
padeditbar.toolbarClick(cmd);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dmesg: function(m) {
|
dmesg: function(m)
|
||||||
if (pad.getIsDebugEnabled()) {
|
{
|
||||||
|
if (pad.getIsDebugEnabled())
|
||||||
|
{
|
||||||
var djs = $('#djs').get(0);
|
var djs = $('#djs').get(0);
|
||||||
var wasAtBottom = (djs.scrollTop - (djs.scrollHeight - $(djs).height())
|
var wasAtBottom = (djs.scrollTop - (djs.scrollHeight - $(djs).height()) >= -20);
|
||||||
>= -20);
|
|
||||||
$('#djs').append('<p>' + m + '</p>');
|
$('#djs').append('<p>' + m + '</p>');
|
||||||
if (wasAtBottom) {
|
if (wasAtBottom)
|
||||||
|
{
|
||||||
djs.scrollTop = djs.scrollHeight;
|
djs.scrollTop = djs.scrollHeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleServerMessage: function(m) {
|
handleServerMessage: function(m)
|
||||||
if (m.type == 'NOTICE') {
|
{
|
||||||
if (m.text) {
|
if (m.type == 'NOTICE')
|
||||||
alertBar.displayMessage(function (abar) {
|
{
|
||||||
|
if (m.text)
|
||||||
|
{
|
||||||
|
alertBar.displayMessage(function(abar)
|
||||||
|
{
|
||||||
abar.find("#servermsgdate").html(" (" + padutils.simpleDateTime(new Date) + ")");
|
abar.find("#servermsgdate").html(" (" + padutils.simpleDateTime(new Date) + ")");
|
||||||
abar.find("#servermsgtext").html(m.text);
|
abar.find("#servermsgtext").html(m.text);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (m.js) {
|
if (m.js)
|
||||||
|
{
|
||||||
window['ev' + 'al'](m.js);
|
window['ev' + 'al'](m.js);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (m.type == 'GUEST_PROMPT') {
|
else if (m.type == 'GUEST_PROMPT')
|
||||||
|
{
|
||||||
paduserlist.showGuestPrompt(m.userId, m.displayName);
|
paduserlist.showGuestPrompt(m.userId, m.displayName);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleChannelStateChange: function(newState, message) {
|
handleChannelStateChange: function(newState, message)
|
||||||
|
{
|
||||||
var oldFullyConnected = !! padconnectionstatus.isFullyConnected();
|
var oldFullyConnected = !! padconnectionstatus.isFullyConnected();
|
||||||
var wasConnecting = (padconnectionstatus.getStatus().what == 'connecting');
|
var wasConnecting = (padconnectionstatus.getStatus().what == 'connecting');
|
||||||
if (newState == "CONNECTED") {
|
if (newState == "CONNECTED")
|
||||||
|
{
|
||||||
padconnectionstatus.connected();
|
padconnectionstatus.connected();
|
||||||
}
|
}
|
||||||
else if (newState == "RECONNECTING") {
|
else if (newState == "RECONNECTING")
|
||||||
|
{
|
||||||
padconnectionstatus.reconnecting();
|
padconnectionstatus.reconnecting();
|
||||||
}
|
}
|
||||||
else if (newState == "DISCONNECTED") {
|
else if (newState == "DISCONNECTED")
|
||||||
|
{
|
||||||
pad.diagnosticInfo.disconnectedMessage = message;
|
pad.diagnosticInfo.disconnectedMessage = message;
|
||||||
pad.diagnosticInfo.padInitTime = pad.initTime;
|
pad.diagnosticInfo.padInitTime = pad.initTime;
|
||||||
pad.asyncSendDiagnosticInfo();
|
pad.asyncSendDiagnosticInfo();
|
||||||
if (typeof window.ajlog == "string") { window.ajlog += ("Disconnected: "+message+'\n'); }
|
if (typeof window.ajlog == "string")
|
||||||
|
{
|
||||||
|
window.ajlog += ("Disconnected: " + message + '\n');
|
||||||
|
}
|
||||||
padeditor.disable();
|
padeditor.disable();
|
||||||
padeditbar.disable();
|
padeditbar.disable();
|
||||||
paddocbar.disable();
|
paddocbar.disable();
|
||||||
|
@ -408,16 +509,21 @@ var pad = {
|
||||||
padconnectionstatus.disconnected(message);
|
padconnectionstatus.disconnected(message);
|
||||||
}
|
}
|
||||||
var newFullyConnected = !! padconnectionstatus.isFullyConnected();
|
var newFullyConnected = !! padconnectionstatus.isFullyConnected();
|
||||||
if (newFullyConnected != oldFullyConnected) {
|
if (newFullyConnected != oldFullyConnected)
|
||||||
|
{
|
||||||
pad.handleIsFullyConnected(newFullyConnected, wasConnecting);
|
pad.handleIsFullyConnected(newFullyConnected, wasConnecting);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleIsFullyConnected: function(isConnected, isInitialConnect) {
|
handleIsFullyConnected: function(isConnected, isInitialConnect)
|
||||||
|
{
|
||||||
// load all images referenced from CSS, one at a time,
|
// load all images referenced from CSS, one at a time,
|
||||||
// starting one second after connection is first established.
|
// starting one second after connection is first established.
|
||||||
if (isConnected && ! pad.preloadedImages) {
|
if (isConnected && !pad.preloadedImages)
|
||||||
window.setTimeout(function() {
|
{
|
||||||
if (! pad.preloadedImages) {
|
window.setTimeout(function()
|
||||||
|
{
|
||||||
|
if (!pad.preloadedImages)
|
||||||
|
{
|
||||||
pad.preloadImages();
|
pad.preloadImages();
|
||||||
pad.preloadedImages = true;
|
pad.preloadedImages = true;
|
||||||
}
|
}
|
||||||
|
@ -428,156 +534,192 @@ var pad = {
|
||||||
|
|
||||||
pad.determineSidebarVisibility(isConnected && !isInitialConnect);
|
pad.determineSidebarVisibility(isConnected && !isInitialConnect);
|
||||||
},
|
},
|
||||||
determineSidebarVisibility: function(asNowConnectedFeedback) {
|
determineSidebarVisibility: function(asNowConnectedFeedback)
|
||||||
if (pad.isFullyConnected()) {
|
{
|
||||||
var setSidebarVisibility =
|
if (pad.isFullyConnected())
|
||||||
padutils.getCancellableAction(
|
{
|
||||||
"set-sidebar-visibility",
|
var setSidebarVisibility = padutils.getCancellableAction("set-sidebar-visibility", function()
|
||||||
function() {
|
{
|
||||||
$("body").toggleClass('hidesidebar',
|
$("body").toggleClass('hidesidebar', !! padcookie.getPref('hideSidebar'));
|
||||||
!! padcookie.getPref('hideSidebar'));
|
|
||||||
});
|
});
|
||||||
window.setTimeout(setSidebarVisibility,
|
window.setTimeout(setSidebarVisibility, asNowConnectedFeedback ? 3000 : 0);
|
||||||
asNowConnectedFeedback ? 3000 : 0);
|
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
padutils.cancelActions("set-sidebar-visibility");
|
padutils.cancelActions("set-sidebar-visibility");
|
||||||
$("body").removeClass('hidesidebar');
|
$("body").removeClass('hidesidebar');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleCollabAction: function(action) {
|
handleCollabAction: function(action)
|
||||||
if (action == "commitPerformed") {
|
{
|
||||||
|
if (action == "commitPerformed")
|
||||||
|
{
|
||||||
padeditbar.setSyncStatus("syncing");
|
padeditbar.setSyncStatus("syncing");
|
||||||
}
|
}
|
||||||
else if (action == "newlyIdle") {
|
else if (action == "newlyIdle")
|
||||||
|
{
|
||||||
padeditbar.setSyncStatus("done");
|
padeditbar.setSyncStatus("done");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
hideServerMessage: function() {
|
hideServerMessage: function()
|
||||||
|
{
|
||||||
alertBar.hideMessage();
|
alertBar.hideMessage();
|
||||||
},
|
},
|
||||||
asyncSendDiagnosticInfo: function() {
|
asyncSendDiagnosticInfo: function()
|
||||||
|
{
|
||||||
pad.diagnosticInfo.collabDiagnosticInfo = pad.collabClient.getDiagnosticInfo();
|
pad.diagnosticInfo.collabDiagnosticInfo = pad.collabClient.getDiagnosticInfo();
|
||||||
window.setTimeout(function() {
|
window.setTimeout(function()
|
||||||
$.ajax({
|
{
|
||||||
|
$.ajax(
|
||||||
|
{
|
||||||
type: 'post',
|
type: 'post',
|
||||||
url: '/ep/pad/connection-diagnostic-info',
|
url: '/ep/pad/connection-diagnostic-info',
|
||||||
data: {padId: pad.getPadId(), diagnosticInfo: JSON.stringify(pad.diagnosticInfo)},
|
data: {
|
||||||
success: function() {},
|
padId: pad.getPadId(),
|
||||||
error: function() {}
|
diagnosticInfo: JSON.stringify(pad.diagnosticInfo)
|
||||||
|
},
|
||||||
|
success: function()
|
||||||
|
{},
|
||||||
|
error: function()
|
||||||
|
{}
|
||||||
});
|
});
|
||||||
}, 0);
|
}, 0);
|
||||||
},
|
},
|
||||||
forceReconnect: function() {
|
forceReconnect: function()
|
||||||
|
{
|
||||||
$('form#reconnectform input.padId').val(pad.getPadId());
|
$('form#reconnectform input.padId').val(pad.getPadId());
|
||||||
pad.diagnosticInfo.collabDiagnosticInfo = pad.collabClient.getDiagnosticInfo();
|
pad.diagnosticInfo.collabDiagnosticInfo = pad.collabClient.getDiagnosticInfo();
|
||||||
$('form#reconnectform input.diagnosticInfo').val(JSON.stringify(pad.diagnosticInfo));
|
$('form#reconnectform input.diagnosticInfo').val(JSON.stringify(pad.diagnosticInfo));
|
||||||
$('form#reconnectform input.missedChanges').val(JSON.stringify(pad.collabClient.getMissedChanges()));
|
$('form#reconnectform input.missedChanges').val(JSON.stringify(pad.collabClient.getMissedChanges()));
|
||||||
$('form#reconnectform').submit();
|
$('form#reconnectform').submit();
|
||||||
},
|
},
|
||||||
toggleWidthPref: function() {
|
toggleWidthPref: function()
|
||||||
|
{
|
||||||
var newValue = !padcookie.getPref('fullWidth');
|
var newValue = !padcookie.getPref('fullWidth');
|
||||||
padcookie.setPref('fullWidth', newValue);
|
padcookie.setPref('fullWidth', newValue);
|
||||||
$("#widthprefcheck").toggleClass('widthprefchecked', !!newValue).toggleClass(
|
$("#widthprefcheck").toggleClass('widthprefchecked', !! newValue).toggleClass('widthprefunchecked', !newValue);
|
||||||
'widthprefunchecked', !newValue);
|
|
||||||
pad.handleWidthChange();
|
pad.handleWidthChange();
|
||||||
},
|
},
|
||||||
toggleSidebar: function() {
|
toggleSidebar: function()
|
||||||
|
{
|
||||||
var newValue = !padcookie.getPref('hideSidebar');
|
var newValue = !padcookie.getPref('hideSidebar');
|
||||||
padcookie.setPref('hideSidebar', newValue);
|
padcookie.setPref('hideSidebar', newValue);
|
||||||
$("#sidebarcheck").toggleClass('sidebarchecked', !newValue).toggleClass(
|
$("#sidebarcheck").toggleClass('sidebarchecked', !newValue).toggleClass('sidebarunchecked', !! newValue);
|
||||||
'sidebarunchecked', !!newValue);
|
|
||||||
pad.determineSidebarVisibility();
|
pad.determineSidebarVisibility();
|
||||||
},
|
},
|
||||||
handleWidthChange: function() {
|
handleWidthChange: function()
|
||||||
|
{
|
||||||
var isFullWidth = padcookie.getPref('fullWidth');
|
var isFullWidth = padcookie.getPref('fullWidth');
|
||||||
if (isFullWidth) {
|
if (isFullWidth)
|
||||||
$("body").addClass('fullwidth').removeClass('limwidth').removeClass(
|
{
|
||||||
'squish1width').removeClass('squish2width');
|
$("body").addClass('fullwidth').removeClass('limwidth').removeClass('squish1width').removeClass('squish2width');
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
$("body").addClass('limwidth').removeClass('fullwidth');
|
$("body").addClass('limwidth').removeClass('fullwidth');
|
||||||
|
|
||||||
var pageWidth = $(window).width();
|
var pageWidth = $(window).width();
|
||||||
$("body").toggleClass('squish1width', (pageWidth < 912 && pageWidth > 812)).toggleClass(
|
$("body").toggleClass('squish1width', (pageWidth < 912 && pageWidth > 812)).toggleClass('squish2width', (pageWidth <= 812));
|
||||||
'squish2width', (pageWidth <= 812));
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// this is called from code put into a frame from the server:
|
// this is called from code put into a frame from the server:
|
||||||
handleImportExportFrameCall: function(callName, varargs) {
|
handleImportExportFrameCall: function(callName, varargs)
|
||||||
padimpexp.handleFrameCall.call(padimpexp, callName,
|
{
|
||||||
Array.prototype.slice.call(arguments, 1));
|
padimpexp.handleFrameCall.call(padimpexp, callName, Array.prototype.slice.call(arguments, 1));
|
||||||
},
|
},
|
||||||
callWhenNotCommitting: function(f) {
|
callWhenNotCommitting: function(f)
|
||||||
|
{
|
||||||
pad.collabClient.callWhenNotCommitting(f);
|
pad.collabClient.callWhenNotCommitting(f);
|
||||||
},
|
},
|
||||||
getCollabRevisionNumber: function() {
|
getCollabRevisionNumber: function()
|
||||||
|
{
|
||||||
return pad.collabClient.getCurrentRevisionNumber();
|
return pad.collabClient.getCurrentRevisionNumber();
|
||||||
},
|
},
|
||||||
isFullyConnected: function() {
|
isFullyConnected: function()
|
||||||
|
{
|
||||||
return padconnectionstatus.isFullyConnected();
|
return padconnectionstatus.isFullyConnected();
|
||||||
},
|
},
|
||||||
addHistoricalAuthors: function(data) {
|
addHistoricalAuthors: function(data)
|
||||||
if (! pad.collabClient) {
|
{
|
||||||
window.setTimeout(function() { pad.addHistoricalAuthors(data); },
|
if (!pad.collabClient)
|
||||||
1000);
|
{
|
||||||
|
window.setTimeout(function()
|
||||||
|
{
|
||||||
|
pad.addHistoricalAuthors(data);
|
||||||
|
}, 1000);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
pad.collabClient.addHistoricalAuthors(data);
|
pad.collabClient.addHistoricalAuthors(data);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
preloadImages: function() {
|
preloadImages: function()
|
||||||
var images = [
|
{
|
||||||
'../static/img/colorpicker.gif'
|
var images = ['../static/img/colorpicker.gif'];
|
||||||
];
|
|
||||||
function loadNextImage() {
|
function loadNextImage()
|
||||||
if (images.length == 0) {
|
{
|
||||||
|
if (images.length == 0)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var img = new Image();
|
var img = new Image();
|
||||||
img.src = images.shift();
|
img.src = images.shift();
|
||||||
if (img.complete) {
|
if (img.complete)
|
||||||
|
{
|
||||||
scheduleLoadNextImage();
|
scheduleLoadNextImage();
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
$(img).bind('error load onreadystatechange', scheduleLoadNextImage);
|
$(img).bind('error load onreadystatechange', scheduleLoadNextImage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function scheduleLoadNextImage() {
|
|
||||||
|
function scheduleLoadNextImage()
|
||||||
|
{
|
||||||
window.setTimeout(loadNextImage, 0);
|
window.setTimeout(loadNextImage, 0);
|
||||||
}
|
}
|
||||||
scheduleLoadNextImage();
|
scheduleLoadNextImage();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var alertBar = (function() {
|
var alertBar = (function()
|
||||||
|
{
|
||||||
|
|
||||||
var animator = padutils.makeShowHideAnimator(arriveAtAnimationState, false, 25, 400);
|
var animator = padutils.makeShowHideAnimator(arriveAtAnimationState, false, 25, 400);
|
||||||
|
|
||||||
function arriveAtAnimationState(state) {
|
function arriveAtAnimationState(state)
|
||||||
if (state == -1) {
|
{
|
||||||
|
if (state == -1)
|
||||||
|
{
|
||||||
$("#alertbar").css('opacity', 0).css('display', 'block');
|
$("#alertbar").css('opacity', 0).css('display', 'block');
|
||||||
}
|
}
|
||||||
else if (state == 0) {
|
else if (state == 0)
|
||||||
|
{
|
||||||
$("#alertbar").css('opacity', 1);
|
$("#alertbar").css('opacity', 1);
|
||||||
}
|
}
|
||||||
else if (state == 1) {
|
else if (state == 1)
|
||||||
|
{
|
||||||
$("#alertbar").css('opacity', 0).css('display', 'none');
|
$("#alertbar").css('opacity', 0).css('display', 'none');
|
||||||
}
|
}
|
||||||
else if (state < 0) {
|
else if (state < 0)
|
||||||
|
{
|
||||||
$("#alertbar").css('opacity', state + 1);
|
$("#alertbar").css('opacity', state + 1);
|
||||||
}
|
}
|
||||||
else if (state > 0) {
|
else if (state > 0)
|
||||||
|
{
|
||||||
$("#alertbar").css('opacity', 1 - state);
|
$("#alertbar").css('opacity', 1 - state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var self = {
|
var self = {
|
||||||
displayMessage: function(setupFunc) {
|
displayMessage: function(setupFunc)
|
||||||
|
{
|
||||||
animator.show();
|
animator.show();
|
||||||
setupFunc($("#alertbar"));
|
setupFunc($("#alertbar"));
|
||||||
},
|
},
|
||||||
hideMessage: function() {
|
hideMessage: function()
|
||||||
|
{
|
||||||
animator.hide();
|
animator.hide();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,44 +14,64 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var padconnectionstatus = (function() {
|
var padconnectionstatus = (function()
|
||||||
|
{
|
||||||
|
|
||||||
var status = {what: 'connecting'};
|
var status = {
|
||||||
|
what: 'connecting'
|
||||||
|
};
|
||||||
|
|
||||||
var self = {
|
var self = {
|
||||||
init: function() {
|
init: function()
|
||||||
$('button#forcereconnect').click(function() {
|
{
|
||||||
|
$('button#forcereconnect').click(function()
|
||||||
|
{
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
connected: function() {
|
connected: function()
|
||||||
status = {what: 'connected'};
|
{
|
||||||
|
status = {
|
||||||
|
what: 'connected'
|
||||||
|
};
|
||||||
padmodals.hideModal(500);
|
padmodals.hideModal(500);
|
||||||
},
|
},
|
||||||
reconnecting: function() {
|
reconnecting: function()
|
||||||
status = {what: 'reconnecting'};
|
{
|
||||||
|
status = {
|
||||||
|
what: 'reconnecting'
|
||||||
|
};
|
||||||
$("#connectionbox").get(0).className = 'modaldialog cboxreconnecting';
|
$("#connectionbox").get(0).className = 'modaldialog cboxreconnecting';
|
||||||
padmodals.showModal("#connectionbox", 500);
|
padmodals.showModal("#connectionbox", 500);
|
||||||
},
|
},
|
||||||
disconnected: function(msg) {
|
disconnected: function(msg)
|
||||||
status = {what: 'disconnected', why: msg};
|
{
|
||||||
|
status = {
|
||||||
|
what: 'disconnected',
|
||||||
|
why: msg
|
||||||
|
};
|
||||||
var k = String(msg).toLowerCase(); // known reason why
|
var k = String(msg).toLowerCase(); // known reason why
|
||||||
if (!(k == 'userdup' || k == 'looping' || k == 'slowcommit' ||
|
if (!(k == 'userdup' || k == 'looping' || k == 'slowcommit' || k == 'initsocketfail' || k == 'unauth'))
|
||||||
k == 'initsocketfail' || k == 'unauth')) {
|
{
|
||||||
k = 'unknown';
|
k = 'unknown';
|
||||||
}
|
}
|
||||||
var cls = 'modaldialog cboxdisconnected cboxdisconnected_' + k;
|
var cls = 'modaldialog cboxdisconnected cboxdisconnected_' + k;
|
||||||
$("#connectionbox").get(0).className = cls;
|
$("#connectionbox").get(0).className = cls;
|
||||||
padmodals.showModal("#connectionbox", 500);
|
padmodals.showModal("#connectionbox", 500);
|
||||||
|
|
||||||
$('button#forcereconnect').click(function() {
|
$('button#forcereconnect').click(function()
|
||||||
|
{
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
isFullyConnected: function() {
|
isFullyConnected: function()
|
||||||
|
{
|
||||||
return status.what == 'connected';
|
return status.what == 'connected';
|
||||||
},
|
},
|
||||||
getStatus: function() { return status; }
|
getStatus: function()
|
||||||
|
{
|
||||||
|
return status;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
return self;
|
return self;
|
||||||
}());
|
}());
|
||||||
|
|
|
@ -15,49 +15,61 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
var padcookie = (function(){
|
var padcookie = (function()
|
||||||
function getRawCookie() {
|
{
|
||||||
|
function getRawCookie()
|
||||||
|
{
|
||||||
// returns null if can't get cookie text
|
// returns null if can't get cookie text
|
||||||
if (! document.cookie) {
|
if (!document.cookie)
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// look for (start of string OR semicolon) followed by whitespace followed by prefs=(something);
|
// look for (start of string OR semicolon) followed by whitespace followed by prefs=(something);
|
||||||
var regexResult = document.cookie.match(/(?:^|;)\s*prefs=([^;]*)(?:;|$)/);
|
var regexResult = document.cookie.match(/(?:^|;)\s*prefs=([^;]*)(?:;|$)/);
|
||||||
if ((! regexResult) || (! regexResult[1])) {
|
if ((!regexResult) || (!regexResult[1]))
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return regexResult[1];
|
return regexResult[1];
|
||||||
}
|
}
|
||||||
function setRawCookie(safeText) {
|
|
||||||
|
function setRawCookie(safeText)
|
||||||
|
{
|
||||||
var expiresDate = new Date();
|
var expiresDate = new Date();
|
||||||
expiresDate.setFullYear(3000);
|
expiresDate.setFullYear(3000);
|
||||||
document.cookie = ('prefs=' + safeText + ';expires=' + expiresDate.toGMTString());
|
document.cookie = ('prefs=' + safeText + ';expires=' + expiresDate.toGMTString());
|
||||||
}
|
}
|
||||||
function parseCookie(text) {
|
|
||||||
// returns null if can't parse cookie.
|
|
||||||
|
|
||||||
try {
|
function parseCookie(text)
|
||||||
|
{
|
||||||
|
// returns null if can't parse cookie.
|
||||||
|
try
|
||||||
|
{
|
||||||
var cookieData = JSON.parse(unescape(text));
|
var cookieData = JSON.parse(unescape(text));
|
||||||
return cookieData;
|
return cookieData;
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e)
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function stringifyCookie(data) {
|
|
||||||
|
function stringifyCookie(data)
|
||||||
|
{
|
||||||
return escape(JSON.stringify(data));
|
return escape(JSON.stringify(data));
|
||||||
}
|
}
|
||||||
function saveCookie() {
|
|
||||||
if (! inited) {
|
function saveCookie()
|
||||||
|
{
|
||||||
|
if (!inited)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setRawCookie(stringifyCookie(cookieData));
|
setRawCookie(stringifyCookie(cookieData));
|
||||||
|
|
||||||
if (pad.getIsProPad() && (! getRawCookie()) && (! alreadyWarnedAboutNoCookies)) {
|
if (pad.getIsProPad() && (!getRawCookie()) && (!alreadyWarnedAboutNoCookies))
|
||||||
alert("Warning: it appears that your browser does not have cookies enabled."+
|
{
|
||||||
" EtherPad uses cookies to keep track of unique users for the purpose"+
|
alert("Warning: it appears that your browser does not have cookies enabled." + " EtherPad uses cookies to keep track of unique users for the purpose" + " of putting a quota on the number of active users. Using EtherPad without " + " cookies may fill up your server's user quota faster than expected.");
|
||||||
" of putting a quota on the number of active users. Using EtherPad without "+
|
|
||||||
" cookies may fill up your server's user quota faster than expected.");
|
|
||||||
alreadyWarnedAboutNoCookies = true;
|
alreadyWarnedAboutNoCookies = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,11 +80,14 @@ var padcookie = (function(){
|
||||||
var inited = false;
|
var inited = false;
|
||||||
|
|
||||||
var self = {
|
var self = {
|
||||||
init: function(prefsToSet) {
|
init: function(prefsToSet)
|
||||||
|
{
|
||||||
var rawCookie = getRawCookie();
|
var rawCookie = getRawCookie();
|
||||||
if (rawCookie) {
|
if (rawCookie)
|
||||||
|
{
|
||||||
var cookieObj = parseCookie(rawCookie);
|
var cookieObj = parseCookie(rawCookie);
|
||||||
if (cookieObj) {
|
if (cookieObj)
|
||||||
|
{
|
||||||
wasNoCookie = false; // there was a cookie
|
wasNoCookie = false; // there was a cookie
|
||||||
delete cookieObj.userId;
|
delete cookieObj.userId;
|
||||||
delete cookieObj.name;
|
delete cookieObj.name;
|
||||||
|
@ -81,18 +96,24 @@ var padcookie = (function(){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(var k in prefsToSet) {
|
for (var k in prefsToSet)
|
||||||
|
{
|
||||||
cookieData[k] = prefsToSet[k];
|
cookieData[k] = prefsToSet[k];
|
||||||
}
|
}
|
||||||
|
|
||||||
inited = true;
|
inited = true;
|
||||||
saveCookie();
|
saveCookie();
|
||||||
},
|
},
|
||||||
wasNoCookie: function() { return wasNoCookie; },
|
wasNoCookie: function()
|
||||||
getPref: function(prefName) {
|
{
|
||||||
|
return wasNoCookie;
|
||||||
|
},
|
||||||
|
getPref: function(prefName)
|
||||||
|
{
|
||||||
return cookieData[prefName];
|
return cookieData[prefName];
|
||||||
},
|
},
|
||||||
setPref: function(prefName, value) {
|
setPref: function(prefName, value)
|
||||||
|
{
|
||||||
cookieData[prefName] = value;
|
cookieData[prefName] = value;
|
||||||
saveCookie();
|
saveCookie();
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,50 +15,66 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
var paddocbar = (function() {
|
var paddocbar = (function()
|
||||||
|
{
|
||||||
var isTitleEditable = false;
|
var isTitleEditable = false;
|
||||||
var isEditingTitle = false;
|
var isEditingTitle = false;
|
||||||
var isEditingPassword = false;
|
var isEditingPassword = false;
|
||||||
var enabled = false;
|
var enabled = false;
|
||||||
|
|
||||||
function getPanelOpenCloseAnimator(panelName, panelHeight) {
|
function getPanelOpenCloseAnimator(panelName, panelHeight)
|
||||||
|
{
|
||||||
var wrapper = $("#" + panelName + "-wrapper");
|
var wrapper = $("#" + panelName + "-wrapper");
|
||||||
var openingClass = "docbar" + panelName + "-opening";
|
var openingClass = "docbar" + panelName + "-opening";
|
||||||
var openClass = "docbar" + panelName + "-open";
|
var openClass = "docbar" + panelName + "-open";
|
||||||
var closingClass = "docbar" + panelName + "-closing";
|
var closingClass = "docbar" + panelName + "-closing";
|
||||||
function setPanelState(action) {
|
|
||||||
|
function setPanelState(action)
|
||||||
|
{
|
||||||
$("#docbar").removeClass(openingClass).removeClass(openClass).
|
$("#docbar").removeClass(openingClass).removeClass(openClass).
|
||||||
removeClass(closingClass);
|
removeClass(closingClass);
|
||||||
if (action != "closed") {
|
if (action != "closed")
|
||||||
|
{
|
||||||
$("#docbar").addClass("docbar" + panelName + "-" + action);
|
$("#docbar").addClass("docbar" + panelName + "-" + action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function openCloseAnimate(state) {
|
function openCloseAnimate(state)
|
||||||
function pow(x) { x = 1-x; x *= x*x; return 1-x; }
|
{
|
||||||
|
function pow(x)
|
||||||
|
{
|
||||||
|
x = 1 - x;
|
||||||
|
x *= x * x;
|
||||||
|
return 1 - x;
|
||||||
|
}
|
||||||
|
|
||||||
if (state == -1) {
|
if (state == -1)
|
||||||
|
{
|
||||||
// startng to open
|
// startng to open
|
||||||
setPanelState("opening");
|
setPanelState("opening");
|
||||||
wrapper.css('height', '0');
|
wrapper.css('height', '0');
|
||||||
}
|
}
|
||||||
else if (state < 0) {
|
else if (state < 0)
|
||||||
|
{
|
||||||
// opening
|
// opening
|
||||||
var height = Math.round(pow(state + 1) * (panelHeight - 1)) + 'px';
|
var height = Math.round(pow(state + 1) * (panelHeight - 1)) + 'px';
|
||||||
wrapper.css('height', height);
|
wrapper.css('height', height);
|
||||||
}
|
}
|
||||||
else if (state == 0) {
|
else if (state == 0)
|
||||||
|
{
|
||||||
// open
|
// open
|
||||||
setPanelState("open");
|
setPanelState("open");
|
||||||
wrapper.css('height', panelHeight - 1);
|
wrapper.css('height', panelHeight - 1);
|
||||||
}
|
}
|
||||||
else if (state < 1) {
|
else if (state < 1)
|
||||||
|
{
|
||||||
// closing
|
// closing
|
||||||
setPanelState("closing");
|
setPanelState("closing");
|
||||||
var height = Math.round((1 - pow(state)) * (panelHeight - 1)) + 'px';
|
var height = Math.round((1 - pow(state)) * (panelHeight - 1)) + 'px';
|
||||||
wrapper.css('height', height);
|
wrapper.css('height', height);
|
||||||
}
|
}
|
||||||
else if (state == 1) {
|
else if (state == 1)
|
||||||
|
{
|
||||||
// closed
|
// closed
|
||||||
setPanelState("closed");
|
setPanelState("closed");
|
||||||
wrapper.css('height', '0');
|
wrapper.css('height', '0');
|
||||||
|
@ -70,16 +86,21 @@ var paddocbar = (function() {
|
||||||
|
|
||||||
|
|
||||||
var currentPanel = null;
|
var currentPanel = null;
|
||||||
function setCurrentPanel(newCurrentPanel) {
|
|
||||||
if (currentPanel != newCurrentPanel) {
|
function setCurrentPanel(newCurrentPanel)
|
||||||
|
{
|
||||||
|
if (currentPanel != newCurrentPanel)
|
||||||
|
{
|
||||||
currentPanel = newCurrentPanel;
|
currentPanel = newCurrentPanel;
|
||||||
padutils.cancelActions("hide-docbar-panel");
|
padutils.cancelActions("hide-docbar-panel");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var panels;
|
var panels;
|
||||||
|
|
||||||
function changePassword(newPass) {
|
function changePassword(newPass)
|
||||||
if ((newPass || null) != (self.password || null)) {
|
{
|
||||||
|
if ((newPass || null) != (self.password || null))
|
||||||
|
{
|
||||||
self.password = (newPass || null);
|
self.password = (newPass || null);
|
||||||
pad.notifyChangePassword(newPass);
|
pad.notifyChangePassword(newPass);
|
||||||
}
|
}
|
||||||
|
@ -89,37 +110,72 @@ var paddocbar = (function() {
|
||||||
var self = {
|
var self = {
|
||||||
title: null,
|
title: null,
|
||||||
password: null,
|
password: null,
|
||||||
init: function(opts) {
|
init: function(opts)
|
||||||
|
{
|
||||||
panels = {
|
panels = {
|
||||||
impexp: { animator: getPanelOpenCloseAnimator("impexp", 160) },
|
impexp: {
|
||||||
savedrevs: { animator: getPanelOpenCloseAnimator("savedrevs", 79) },
|
animator: getPanelOpenCloseAnimator("impexp", 160)
|
||||||
options: { animator: getPanelOpenCloseAnimator(
|
},
|
||||||
"options", 114) },
|
savedrevs: {
|
||||||
security: { animator: getPanelOpenCloseAnimator("security", 130) }
|
animator: getPanelOpenCloseAnimator("savedrevs", 79)
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
animator: getPanelOpenCloseAnimator("options", 114)
|
||||||
|
},
|
||||||
|
security: {
|
||||||
|
animator: getPanelOpenCloseAnimator("security", 130)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
isTitleEditable = opts.isTitleEditable;
|
isTitleEditable = opts.isTitleEditable;
|
||||||
self.title = opts.initialTitle;
|
self.title = opts.initialTitle;
|
||||||
self.password = opts.initialPassword;
|
self.password = opts.initialPassword;
|
||||||
|
|
||||||
$("#docbarimpexp").click(function() {self.togglePanel("impexp");});
|
$("#docbarimpexp").click(function()
|
||||||
$("#docbarsavedrevs").click(function() {self.togglePanel("savedrevs");});
|
{
|
||||||
$("#docbaroptions").click(function() {self.togglePanel("options");});
|
self.togglePanel("impexp");
|
||||||
$("#docbarsecurity").click(function() {self.togglePanel("security");});
|
});
|
||||||
|
$("#docbarsavedrevs").click(function()
|
||||||
|
{
|
||||||
|
self.togglePanel("savedrevs");
|
||||||
|
});
|
||||||
|
$("#docbaroptions").click(function()
|
||||||
|
{
|
||||||
|
self.togglePanel("options");
|
||||||
|
});
|
||||||
|
$("#docbarsecurity").click(function()
|
||||||
|
{
|
||||||
|
self.togglePanel("security");
|
||||||
|
});
|
||||||
|
|
||||||
$("#docbarrenamelink").click(self.editTitle);
|
$("#docbarrenamelink").click(self.editTitle);
|
||||||
$("#padtitlesave").click(function() { self.closeTitleEdit(true); });
|
$("#padtitlesave").click(function()
|
||||||
$("#padtitlecancel").click(function() { self.closeTitleEdit(false); });
|
{
|
||||||
padutils.bindEnterAndEscape($("#padtitleedit"),
|
self.closeTitleEdit(true);
|
||||||
function() {
|
});
|
||||||
$("#padtitlesave").trigger('click'); },
|
$("#padtitlecancel").click(function()
|
||||||
function() {
|
{
|
||||||
$("#padtitlecancel").trigger('click'); });
|
self.closeTitleEdit(false);
|
||||||
|
});
|
||||||
|
padutils.bindEnterAndEscape($("#padtitleedit"), function()
|
||||||
|
{
|
||||||
|
$("#padtitlesave").trigger('click');
|
||||||
|
}, function()
|
||||||
|
{
|
||||||
|
$("#padtitlecancel").trigger('click');
|
||||||
|
});
|
||||||
|
|
||||||
$("#options-close").click(function() {self.setShownPanel(null);});
|
$("#options-close").click(function()
|
||||||
$("#security-close").click(function() {self.setShownPanel(null);});
|
{
|
||||||
|
self.setShownPanel(null);
|
||||||
|
});
|
||||||
|
$("#security-close").click(function()
|
||||||
|
{
|
||||||
|
self.setShownPanel(null);
|
||||||
|
});
|
||||||
|
|
||||||
if (pad.getIsProPad()) {
|
if (pad.getIsProPad())
|
||||||
|
{
|
||||||
self.initPassword();
|
self.initPassword();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,135 +183,171 @@ var paddocbar = (function() {
|
||||||
self.render();
|
self.render();
|
||||||
|
|
||||||
// public/private
|
// public/private
|
||||||
$("#security-access input").bind("change click", function(evt) {
|
$("#security-access input").bind("change click", function(evt)
|
||||||
pad.changePadOption('guestPolicy',
|
{
|
||||||
$("#security-access input[name='padaccess']:checked").val());
|
pad.changePadOption('guestPolicy', $("#security-access input[name='padaccess']:checked").val());
|
||||||
});
|
});
|
||||||
self.setGuestPolicy(opts.guestPolicy);
|
self.setGuestPolicy(opts.guestPolicy);
|
||||||
},
|
},
|
||||||
setGuestPolicy: function(newPolicy) {
|
setGuestPolicy: function(newPolicy)
|
||||||
$("#security-access input[value='"+newPolicy+"']").attr("checked",
|
{
|
||||||
"checked");
|
$("#security-access input[value='" + newPolicy + "']").attr("checked", "checked");
|
||||||
self.render();
|
self.render();
|
||||||
},
|
},
|
||||||
initPassword: function() {
|
initPassword: function()
|
||||||
|
{
|
||||||
self.renderPassword();
|
self.renderPassword();
|
||||||
$("#password-clearlink").click(function() {
|
$("#password-clearlink").click(function()
|
||||||
|
{
|
||||||
changePassword(null);
|
changePassword(null);
|
||||||
});
|
});
|
||||||
$("#password-setlink, #password-display").click(function() {
|
$("#password-setlink, #password-display").click(function()
|
||||||
|
{
|
||||||
self.enterPassword();
|
self.enterPassword();
|
||||||
});
|
});
|
||||||
$("#password-cancellink").click(function() {
|
$("#password-cancellink").click(function()
|
||||||
|
{
|
||||||
self.exitPassword(false);
|
self.exitPassword(false);
|
||||||
});
|
});
|
||||||
$("#password-savelink").click(function() {
|
$("#password-savelink").click(function()
|
||||||
|
{
|
||||||
self.exitPassword(true);
|
self.exitPassword(true);
|
||||||
});
|
});
|
||||||
padutils.bindEnterAndEscape($("#security-passwordedit"),
|
padutils.bindEnterAndEscape($("#security-passwordedit"), function()
|
||||||
function() {
|
{
|
||||||
self.exitPassword(true);
|
self.exitPassword(true);
|
||||||
},
|
}, function()
|
||||||
function() {
|
{
|
||||||
self.exitPassword(false);
|
self.exitPassword(false);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
enterPassword: function() {
|
enterPassword: function()
|
||||||
|
{
|
||||||
isEditingPassword = true;
|
isEditingPassword = true;
|
||||||
$("#security-passwordedit").val(self.password || '');
|
$("#security-passwordedit").val(self.password || '');
|
||||||
self.renderPassword();
|
self.renderPassword();
|
||||||
$("#security-passwordedit").focus().select();
|
$("#security-passwordedit").focus().select();
|
||||||
},
|
},
|
||||||
exitPassword: function(accept) {
|
exitPassword: function(accept)
|
||||||
|
{
|
||||||
isEditingPassword = false;
|
isEditingPassword = false;
|
||||||
if (accept) {
|
if (accept)
|
||||||
|
{
|
||||||
changePassword($("#security-passwordedit").val());
|
changePassword($("#security-passwordedit").val());
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
self.renderPassword();
|
self.renderPassword();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
renderPassword: function() {
|
renderPassword: function()
|
||||||
if (isEditingPassword) {
|
{
|
||||||
|
if (isEditingPassword)
|
||||||
|
{
|
||||||
$("#password-nonedit").hide();
|
$("#password-nonedit").hide();
|
||||||
$("#password-inedit").show();
|
$("#password-inedit").show();
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
$("#password-nonedit").toggleClass('nopassword', !self.password);
|
$("#password-nonedit").toggleClass('nopassword', !self.password);
|
||||||
$("#password-setlink").html(self.password ? "Change..." : "Set...");
|
$("#password-setlink").html(self.password ? "Change..." : "Set...");
|
||||||
if (self.password) {
|
if (self.password)
|
||||||
|
{
|
||||||
$("#password-display").html(self.password.replace(/./g, '•'));
|
$("#password-display").html(self.password.replace(/./g, '•'));
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
$("#password-display").html("None");
|
$("#password-display").html("None");
|
||||||
}
|
}
|
||||||
$("#password-inedit").hide();
|
$("#password-inedit").hide();
|
||||||
$("#password-nonedit").show();
|
$("#password-nonedit").show();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
togglePanel: function(panelName) {
|
togglePanel: function(panelName)
|
||||||
if (panelName in panels) {
|
{
|
||||||
if (currentPanel == panelName) {
|
if (panelName in panels)
|
||||||
|
{
|
||||||
|
if (currentPanel == panelName)
|
||||||
|
{
|
||||||
self.setShownPanel(null);
|
self.setShownPanel(null);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
self.setShownPanel(panelName);
|
self.setShownPanel(panelName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setShownPanel: function(panelName) {
|
setShownPanel: function(panelName)
|
||||||
function animateHidePanel(panelName, next) {
|
{
|
||||||
|
function animateHidePanel(panelName, next)
|
||||||
|
{
|
||||||
var delay = 0;
|
var delay = 0;
|
||||||
if (panelName == 'options' && isEditingPassword) {
|
if (panelName == 'options' && isEditingPassword)
|
||||||
|
{
|
||||||
// give user feedback that the password they've
|
// give user feedback that the password they've
|
||||||
// typed in won't actually take effect
|
// typed in won't actually take effect
|
||||||
self.exitPassword(false);
|
self.exitPassword(false);
|
||||||
delay = 500;
|
delay = 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.setTimeout(function() {
|
window.setTimeout(function()
|
||||||
|
{
|
||||||
panels[panelName].animator.hide();
|
panels[panelName].animator.hide();
|
||||||
if (next) {
|
if (next)
|
||||||
|
{
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
}, delay);
|
}, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! panelName) {
|
if (!panelName)
|
||||||
if (currentPanel) {
|
{
|
||||||
|
if (currentPanel)
|
||||||
|
{
|
||||||
animateHidePanel(currentPanel);
|
animateHidePanel(currentPanel);
|
||||||
setCurrentPanel(null);
|
setCurrentPanel(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (panelName in panels) {
|
else if (panelName in panels)
|
||||||
if (currentPanel != panelName) {
|
{
|
||||||
if (currentPanel) {
|
if (currentPanel != panelName)
|
||||||
animateHidePanel(currentPanel, function() {
|
{
|
||||||
|
if (currentPanel)
|
||||||
|
{
|
||||||
|
animateHidePanel(currentPanel, function()
|
||||||
|
{
|
||||||
panels[panelName].animator.show();
|
panels[panelName].animator.show();
|
||||||
setCurrentPanel(panelName);
|
setCurrentPanel(panelName);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
panels[panelName].animator.show();
|
panels[panelName].animator.show();
|
||||||
setCurrentPanel(panelName);
|
setCurrentPanel(panelName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isPanelShown: function(panelName) {
|
isPanelShown: function(panelName)
|
||||||
if (! panelName) {
|
{
|
||||||
|
if (!panelName)
|
||||||
|
{
|
||||||
return !currentPanel;
|
return !currentPanel;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
return (panelName == currentPanel);
|
return (panelName == currentPanel);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
changeTitle: function(newTitle) {
|
changeTitle: function(newTitle)
|
||||||
|
{
|
||||||
self.title = newTitle;
|
self.title = newTitle;
|
||||||
self.render();
|
self.render();
|
||||||
},
|
},
|
||||||
editTitle: function() {
|
editTitle: function()
|
||||||
if (! enabled) {
|
{
|
||||||
|
if (!enabled)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$("#padtitleedit").val(self.title);
|
$("#padtitleedit").val(self.title);
|
||||||
|
@ -263,13 +355,17 @@ var paddocbar = (function() {
|
||||||
self.render();
|
self.render();
|
||||||
$("#padtitleedit").focus().select();
|
$("#padtitleedit").focus().select();
|
||||||
},
|
},
|
||||||
closeTitleEdit: function(accept) {
|
closeTitleEdit: function(accept)
|
||||||
if (! enabled) {
|
{
|
||||||
|
if (!enabled)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (accept) {
|
if (accept)
|
||||||
|
{
|
||||||
var newTitle = $("#padtitleedit").val();
|
var newTitle = $("#padtitleedit").val();
|
||||||
if (newTitle) {
|
if (newTitle)
|
||||||
|
{
|
||||||
newTitle = newTitle.substring(0, 80);
|
newTitle = newTitle.substring(0, 80);
|
||||||
self.title = newTitle;
|
self.title = newTitle;
|
||||||
|
|
||||||
|
@ -280,65 +376,74 @@ var paddocbar = (function() {
|
||||||
isEditingTitle = false;
|
isEditingTitle = false;
|
||||||
self.render();
|
self.render();
|
||||||
},
|
},
|
||||||
changePassword: function(newPass) {
|
changePassword: function(newPass)
|
||||||
if (newPass) {
|
{
|
||||||
|
if (newPass)
|
||||||
|
{
|
||||||
self.password = newPass;
|
self.password = newPass;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
self.password = null;
|
self.password = null;
|
||||||
}
|
}
|
||||||
self.renderPassword();
|
self.renderPassword();
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function()
|
||||||
if (isEditingTitle) {
|
{
|
||||||
|
if (isEditingTitle)
|
||||||
|
{
|
||||||
$("#docbarpadtitle").hide();
|
$("#docbarpadtitle").hide();
|
||||||
$("#docbarrenamelink").hide();
|
$("#docbarrenamelink").hide();
|
||||||
$("#padtitleedit").show();
|
$("#padtitleedit").show();
|
||||||
$("#padtitlebuttons").show();
|
$("#padtitlebuttons").show();
|
||||||
if (! enabled) {
|
if (!enabled)
|
||||||
|
{
|
||||||
$("#padtitleedit").attr('disabled', 'disabled');
|
$("#padtitleedit").attr('disabled', 'disabled');
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
$("#padtitleedit").removeAttr('disabled');
|
$("#padtitleedit").removeAttr('disabled');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
$("#padtitleedit").hide();
|
$("#padtitleedit").hide();
|
||||||
$("#padtitlebuttons").hide();
|
$("#padtitlebuttons").hide();
|
||||||
|
|
||||||
var titleSpan = $("#docbarpadtitle span");
|
var titleSpan = $("#docbarpadtitle span");
|
||||||
titleSpan.html(padutils.escapeHtml(self.title));
|
titleSpan.html(padutils.escapeHtml(self.title));
|
||||||
$("#docbarpadtitle").attr('title',
|
$("#docbarpadtitle").attr('title', (pad.isPadPublic() ? "Public Pad: " : "") + self.title);
|
||||||
(pad.isPadPublic() ? "Public Pad: " : "")+
|
|
||||||
self.title);
|
|
||||||
$("#docbarpadtitle").show();
|
$("#docbarpadtitle").show();
|
||||||
|
|
||||||
if (isTitleEditable) {
|
if (isTitleEditable)
|
||||||
var titleRight = $("#docbarpadtitle").position().left +
|
{
|
||||||
$("#docbarpadtitle span").position().left +
|
var titleRight = $("#docbarpadtitle").position().left + $("#docbarpadtitle span").position().left + Math.min($("#docbarpadtitle").width(), $("#docbarpadtitle span").width());
|
||||||
Math.min($("#docbarpadtitle").width(),
|
|
||||||
$("#docbarpadtitle span").width());
|
|
||||||
$("#docbarrenamelink").css('left', titleRight + 10).show();
|
$("#docbarrenamelink").css('left', titleRight + 10).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pad.isPadPublic()) {
|
if (pad.isPadPublic())
|
||||||
|
{
|
||||||
$("#docbar").addClass("docbar-public");
|
$("#docbar").addClass("docbar-public");
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
$("#docbar").removeClass("docbar-public");
|
$("#docbar").removeClass("docbar-public");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
disable: function() {
|
disable: function()
|
||||||
|
{
|
||||||
enabled = false;
|
enabled = false;
|
||||||
self.render();
|
self.render();
|
||||||
},
|
},
|
||||||
handleResizePage: function() {
|
handleResizePage: function()
|
||||||
|
{
|
||||||
padsavedrevs.handleResizePage();
|
padsavedrevs.handleResizePage();
|
||||||
},
|
},
|
||||||
hideLaterIfNoOtherInteraction: function() {
|
hideLaterIfNoOtherInteraction: function()
|
||||||
return padutils.getCancellableAction('hide-docbar-panel',
|
{
|
||||||
function() {
|
return padutils.getCancellableAction('hide-docbar-panel', function()
|
||||||
|
{
|
||||||
self.setShownPanel(null);
|
self.setShownPanel(null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,9 +15,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
var padeditbar = (function(){
|
var padeditbar = (function()
|
||||||
|
{
|
||||||
|
|
||||||
var syncAnimation = (function() {
|
var syncAnimation = (function()
|
||||||
|
{
|
||||||
var SYNCING = -100;
|
var SYNCING = -100;
|
||||||
var DONE = 100;
|
var DONE = 100;
|
||||||
var state = DONE;
|
var state = DONE;
|
||||||
|
@ -26,39 +28,48 @@ var padeditbar = (function(){
|
||||||
var T_START = -0.5;
|
var T_START = -0.5;
|
||||||
var T_FADE = 1.0;
|
var T_FADE = 1.0;
|
||||||
var T_GONE = 1.5;
|
var T_GONE = 1.5;
|
||||||
var animator = padutils.makeAnimationScheduler(function() {
|
var animator = padutils.makeAnimationScheduler(function()
|
||||||
if (state == SYNCING || state == DONE) {
|
{
|
||||||
|
if (state == SYNCING || state == DONE)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (state >= T_GONE) {
|
else if (state >= T_GONE)
|
||||||
|
{
|
||||||
state = DONE;
|
state = DONE;
|
||||||
$("#syncstatussyncing").css('display', 'none');
|
$("#syncstatussyncing").css('display', 'none');
|
||||||
$("#syncstatusdone").css('display', 'none');
|
$("#syncstatusdone").css('display', 'none');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (state < 0) {
|
else if (state < 0)
|
||||||
|
{
|
||||||
state += step;
|
state += step;
|
||||||
if (state >= 0) {
|
if (state >= 0)
|
||||||
|
{
|
||||||
$("#syncstatussyncing").css('display', 'none');
|
$("#syncstatussyncing").css('display', 'none');
|
||||||
$("#syncstatusdone").css('display', 'block').css('opacity', 1);
|
$("#syncstatusdone").css('display', 'block').css('opacity', 1);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
state += step;
|
state += step;
|
||||||
if (state >= T_FADE) {
|
if (state >= T_FADE)
|
||||||
|
{
|
||||||
$("#syncstatusdone").css('opacity', (T_GONE - state) / (T_GONE - T_FADE));
|
$("#syncstatusdone").css('opacity', (T_GONE - state) / (T_GONE - T_FADE));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}, step * 1000);
|
}, step * 1000);
|
||||||
return {
|
return {
|
||||||
syncing: function() {
|
syncing: function()
|
||||||
|
{
|
||||||
state = SYNCING;
|
state = SYNCING;
|
||||||
$("#syncstatussyncing").css('display', 'block');
|
$("#syncstatussyncing").css('display', 'block');
|
||||||
$("#syncstatusdone").css('display', 'none');
|
$("#syncstatusdone").css('display', 'none');
|
||||||
},
|
},
|
||||||
done: function() {
|
done: function()
|
||||||
|
{
|
||||||
state = T_START;
|
state = T_START;
|
||||||
animator.scheduleAnimation();
|
animator.scheduleAnimation();
|
||||||
}
|
}
|
||||||
|
@ -66,23 +77,30 @@ var padeditbar = (function(){
|
||||||
}());
|
}());
|
||||||
|
|
||||||
var self = {
|
var self = {
|
||||||
init: function() {
|
init: function()
|
||||||
|
{
|
||||||
$("#editbar .editbarbutton").attr("unselectable", "on"); // for IE
|
$("#editbar .editbarbutton").attr("unselectable", "on"); // for IE
|
||||||
$("#editbar").removeClass("disabledtoolbar").addClass("enabledtoolbar");
|
$("#editbar").removeClass("disabledtoolbar").addClass("enabledtoolbar");
|
||||||
},
|
},
|
||||||
isEnabled: function() {
|
isEnabled: function()
|
||||||
|
{
|
||||||
return !$("#editbar").hasClass('disabledtoolbar');
|
return !$("#editbar").hasClass('disabledtoolbar');
|
||||||
},
|
},
|
||||||
disable: function() {
|
disable: function()
|
||||||
|
{
|
||||||
$("#editbar").addClass('disabledtoolbar').removeClass("enabledtoolbar");
|
$("#editbar").addClass('disabledtoolbar').removeClass("enabledtoolbar");
|
||||||
},
|
},
|
||||||
toolbarClick: function(cmd) {
|
toolbarClick: function(cmd)
|
||||||
if (self.isEnabled()) {
|
{
|
||||||
if (cmd == 'showusers') {
|
if (self.isEnabled())
|
||||||
|
{
|
||||||
|
if (cmd == 'showusers')
|
||||||
|
{
|
||||||
// show users shows the current users on teh pad
|
// show users shows the current users on teh pad
|
||||||
// get current height
|
// get current height
|
||||||
var editbarheight = $('#users').css('display');
|
var editbarheight = $('#users').css('display');
|
||||||
if (editbarheight == "none"){
|
if (editbarheight == "none")
|
||||||
|
{
|
||||||
// increase the size of the editbar
|
// increase the size of the editbar
|
||||||
//$('#editbar').animate({height:'72px'});
|
//$('#editbar').animate({height:'72px'});
|
||||||
//$('#editorcontainerbox').animate({top:'72px'});
|
//$('#editorcontainerbox').animate({top:'72px'});
|
||||||
|
@ -97,14 +115,19 @@ var padeditbar = (function(){
|
||||||
$('#users').slideUp("fast");
|
$('#users').slideUp("fast");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cmd == 'embed') {
|
if (cmd == 'embed')
|
||||||
|
{
|
||||||
// embed shows the embed link
|
// embed shows the embed link
|
||||||
// get current height
|
// get current height
|
||||||
var editbarheight = $('#embed').css('display');
|
var editbarheight = $('#embed').css('display');
|
||||||
if (editbarheight == "none"){
|
if (editbarheight == "none")
|
||||||
|
{
|
||||||
// increase the size of the editbar
|
// increase the size of the editbar
|
||||||
//$('#editbar').animate({height:'72px'});
|
//$('#editbar').animate({height:'72px'});
|
||||||
$('#editorcontainerbox').animate({top:'72px'});
|
$('#editorcontainerbox').animate(
|
||||||
|
{
|
||||||
|
top: '72px'
|
||||||
|
});
|
||||||
// get the pad url
|
// get the pad url
|
||||||
padurl = document.location;
|
padurl = document.location;
|
||||||
// change the div contents to include the pad url in an input box
|
// change the div contents to include the pad url in an input box
|
||||||
|
@ -116,33 +139,48 @@ var padeditbar = (function(){
|
||||||
{
|
{
|
||||||
// increase the size of the editbar
|
// increase the size of the editbar
|
||||||
//$('#editbar').animate({height:'36px'});
|
//$('#editbar').animate({height:'36px'});
|
||||||
$('#editorcontainerbox').animate({top:'36px'});
|
$('#editorcontainerbox').animate(
|
||||||
|
{
|
||||||
|
top: '36px'
|
||||||
|
});
|
||||||
$('#embed').hide();
|
$('#embed').hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cmd == 'save') {
|
if (cmd == 'save')
|
||||||
|
{
|
||||||
padsavedrevs.saveNow();
|
padsavedrevs.saveNow();
|
||||||
} else {
|
}
|
||||||
padeditor.ace.callWithAce(function (ace) {
|
else
|
||||||
if (cmd == 'bold' || cmd == 'italic' || cmd == 'underline' || cmd == 'strikethrough')
|
{
|
||||||
ace.ace_toggleAttributeOnSelection(cmd);
|
padeditor.ace.callWithAce(function(ace)
|
||||||
else if (cmd == 'undo' || cmd == 'redo')
|
{
|
||||||
ace.ace_doUndoRedo(cmd);
|
if (cmd == 'bold' || cmd == 'italic' || cmd == 'underline' || cmd == 'strikethrough') ace.ace_toggleAttributeOnSelection(cmd);
|
||||||
else if (cmd == 'insertunorderedlist')
|
else if (cmd == 'undo' || cmd == 'redo') ace.ace_doUndoRedo(cmd);
|
||||||
ace.ace_doInsertUnorderedList();
|
else if (cmd == 'insertunorderedlist') ace.ace_doInsertUnorderedList();
|
||||||
else if (cmd == 'indent') {
|
else if (cmd == 'indent')
|
||||||
if (! ace.ace_doIndentOutdent(false)) {
|
{
|
||||||
|
if (!ace.ace_doIndentOutdent(false))
|
||||||
|
{
|
||||||
ace.ace_doInsertUnorderedList();
|
ace.ace_doInsertUnorderedList();
|
||||||
}
|
}
|
||||||
} else if (cmd == 'outdent') {
|
}
|
||||||
|
else if (cmd == 'outdent')
|
||||||
|
{
|
||||||
ace.ace_doIndentOutdent(true);
|
ace.ace_doIndentOutdent(true);
|
||||||
} else if (cmd == 'clearauthorship') {
|
|
||||||
if ((!(ace.ace_getRep().selStart && ace.ace_getRep().selEnd)) || ace.ace_isCaret()) {
|
|
||||||
if (window.confirm("Clear authorship colors on entire document?")) {
|
|
||||||
ace.ace_performDocumentApplyAttributesToCharRange(0, ace.ace_getRep().alltext.length,
|
|
||||||
[['author', '']]);
|
|
||||||
}
|
}
|
||||||
} else {
|
else if (cmd == 'clearauthorship')
|
||||||
|
{
|
||||||
|
if ((!(ace.ace_getRep().selStart && ace.ace_getRep().selEnd)) || ace.ace_isCaret())
|
||||||
|
{
|
||||||
|
if (window.confirm("Clear authorship colors on entire document?"))
|
||||||
|
{
|
||||||
|
ace.ace_performDocumentApplyAttributesToCharRange(0, ace.ace_getRep().alltext.length, [
|
||||||
|
['author', '']
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
ace.ace_setAttributeOnSelection('author', '');
|
ace.ace_setAttributeOnSelection('author', '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -151,11 +189,14 @@ var padeditbar = (function(){
|
||||||
}
|
}
|
||||||
padeditor.ace.focus();
|
padeditor.ace.focus();
|
||||||
},
|
},
|
||||||
setSyncStatus: function(status) {
|
setSyncStatus: function(status)
|
||||||
if (status == "syncing") {
|
{
|
||||||
|
if (status == "syncing")
|
||||||
|
{
|
||||||
syncAnimation.syncing();
|
syncAnimation.syncing();
|
||||||
}
|
}
|
||||||
else if (status == "done") {
|
else if (status == "done")
|
||||||
|
{
|
||||||
syncAnimation.done();
|
syncAnimation.done();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,15 +15,20 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
var padeditor = (function(){
|
var padeditor = (function()
|
||||||
|
{
|
||||||
var self = {
|
var self = {
|
||||||
ace: null, // this is accessed directly from other files
|
ace: null,
|
||||||
|
// this is accessed directly from other files
|
||||||
viewZoom: 100,
|
viewZoom: 100,
|
||||||
init: function(readyFunc, initialViewOptions) {
|
init: function(readyFunc, initialViewOptions)
|
||||||
|
{
|
||||||
|
|
||||||
function aceReady() {
|
function aceReady()
|
||||||
|
{
|
||||||
$("#editorloadingbox").hide();
|
$("#editorloadingbox").hide();
|
||||||
if (readyFunc) {
|
if (readyFunc)
|
||||||
|
{
|
||||||
readyFunc();
|
readyFunc();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +36,8 @@ var padeditor = (function(){
|
||||||
self.ace = new Ace2Editor();
|
self.ace = new Ace2Editor();
|
||||||
self.ace.init("editorcontainer", "", aceReady);
|
self.ace.init("editorcontainer", "", aceReady);
|
||||||
self.ace.setProperty("wraps", true);
|
self.ace.setProperty("wraps", true);
|
||||||
if (pad.getIsDebugEnabled()) {
|
if (pad.getIsDebugEnabled())
|
||||||
|
{
|
||||||
self.ace.setProperty("dmesg", pad.dmesg);
|
self.ace.setProperty("dmesg", pad.dmesg);
|
||||||
}
|
}
|
||||||
self.initViewOptions();
|
self.initViewOptions();
|
||||||
|
@ -41,22 +47,25 @@ var padeditor = (function(){
|
||||||
self.initViewZoom();
|
self.initViewZoom();
|
||||||
$("#viewbarcontents").show();
|
$("#viewbarcontents").show();
|
||||||
},
|
},
|
||||||
initViewOptions: function() {
|
initViewOptions: function()
|
||||||
padutils.bindCheckboxChange($("#options-linenoscheck"), function() {
|
{
|
||||||
pad.changeViewOption('showLineNumbers',
|
padutils.bindCheckboxChange($("#options-linenoscheck"), function()
|
||||||
padutils.getCheckbox($("#options-linenoscheck")));
|
{
|
||||||
|
pad.changeViewOption('showLineNumbers', padutils.getCheckbox($("#options-linenoscheck")));
|
||||||
});
|
});
|
||||||
padutils.bindCheckboxChange($("#options-colorscheck"), function() {
|
padutils.bindCheckboxChange($("#options-colorscheck"), function()
|
||||||
pad.changeViewOption('showAuthorColors',
|
{
|
||||||
padutils.getCheckbox("#options-colorscheck"));
|
pad.changeViewOption('showAuthorColors', padutils.getCheckbox("#options-colorscheck"));
|
||||||
});
|
});
|
||||||
$("#viewfontmenu").change(function() {
|
$("#viewfontmenu").change(function()
|
||||||
pad.changeViewOption('useMonospaceFont',
|
{
|
||||||
$("#viewfontmenu").val() == 'monospace');
|
pad.changeViewOption('useMonospaceFont', $("#viewfontmenu").val() == 'monospace');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
setViewOptions: function(newOptions) {
|
setViewOptions: function(newOptions)
|
||||||
function getOption(key, defaultValue) {
|
{
|
||||||
|
function getOption(key, defaultValue)
|
||||||
|
{
|
||||||
var value = String(newOptions[key]);
|
var value = String(newOptions[key]);
|
||||||
if (value == "true") return true;
|
if (value == "true") return true;
|
||||||
if (value == "false") return false;
|
if (value == "false") return false;
|
||||||
|
@ -73,23 +82,27 @@ var padeditor = (function(){
|
||||||
padutils.setCheckbox($("#options-colorscheck"), v);
|
padutils.setCheckbox($("#options-colorscheck"), v);
|
||||||
|
|
||||||
v = getOption('useMonospaceFont', false);
|
v = getOption('useMonospaceFont', false);
|
||||||
self.ace.setProperty("textface",
|
self.ace.setProperty("textface", (v ? "monospace" : "Arial, sans-serif"));
|
||||||
(v ? "monospace" : "Arial, sans-serif"));
|
|
||||||
$("#viewfontmenu").val(v ? "monospace" : "normal");
|
$("#viewfontmenu").val(v ? "monospace" : "normal");
|
||||||
},
|
},
|
||||||
initViewZoom: function() {
|
initViewZoom: function()
|
||||||
|
{
|
||||||
var viewZoom = Number(padcookie.getPref('viewZoom'));
|
var viewZoom = Number(padcookie.getPref('viewZoom'));
|
||||||
if ((! viewZoom) || isNaN(viewZoom)) {
|
if ((!viewZoom) || isNaN(viewZoom))
|
||||||
|
{
|
||||||
viewZoom = 100;
|
viewZoom = 100;
|
||||||
}
|
}
|
||||||
self.setViewZoom(viewZoom);
|
self.setViewZoom(viewZoom);
|
||||||
$("#viewzoommenu").change(function(evt) {
|
$("#viewzoommenu").change(function(evt)
|
||||||
|
{
|
||||||
// strip initial 'z' from val
|
// strip initial 'z' from val
|
||||||
self.setViewZoom(Number($("#viewzoommenu").val().substring(1)));
|
self.setViewZoom(Number($("#viewzoommenu").val().substring(1)));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
setViewZoom: function(percent) {
|
setViewZoom: function(percent)
|
||||||
if (! (percent >= 50 && percent <= 1000)) {
|
{
|
||||||
|
if (!(percent >= 50 && percent <= 1000))
|
||||||
|
{
|
||||||
// percent is out of sane range or NaN (which fails comparisons)
|
// percent is out of sane range or NaN (which fails comparisons)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -98,27 +111,30 @@ var padeditor = (function(){
|
||||||
$("#viewzoommenu").val('z' + percent);
|
$("#viewzoommenu").val('z' + percent);
|
||||||
|
|
||||||
var baseSize = 13;
|
var baseSize = 13;
|
||||||
self.ace.setProperty('textsize',
|
self.ace.setProperty('textsize', Math.round(baseSize * self.viewZoom / 100));
|
||||||
Math.round(baseSize * self.viewZoom / 100));
|
|
||||||
|
|
||||||
padcookie.setPref('viewZoom', percent);
|
padcookie.setPref('viewZoom', percent);
|
||||||
},
|
},
|
||||||
dispose: function() {
|
dispose: function()
|
||||||
if (self.ace) {
|
{
|
||||||
|
if (self.ace)
|
||||||
|
{
|
||||||
self.ace.destroy();
|
self.ace.destroy();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
disable: function() {
|
disable: function()
|
||||||
if (self.ace) {
|
{
|
||||||
|
if (self.ace)
|
||||||
|
{
|
||||||
self.ace.setProperty("grayedOut", true);
|
self.ace.setProperty("grayedOut", true);
|
||||||
self.ace.setEditable(false);
|
self.ace.setEditable(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
restoreRevisionText: function(dataFromServer) {
|
restoreRevisionText: function(dataFromServer)
|
||||||
|
{
|
||||||
pad.addHistoricalAuthors(dataFromServer.historicalAuthorData);
|
pad.addHistoricalAuthors(dataFromServer.historicalAuthorData);
|
||||||
self.ace.importAText(dataFromServer.atext, dataFromServer.apool, true);
|
self.ace.importAText(dataFromServer.atext, dataFromServer.apool, true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return self;
|
return self;
|
||||||
}());
|
}());
|
||||||
|
|
||||||
|
|
|
@ -15,121 +15,182 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
var padimpexp = (function() {
|
var padimpexp = (function()
|
||||||
|
{
|
||||||
|
|
||||||
///// import
|
///// import
|
||||||
|
|
||||||
var currentImportTimer = null;
|
var currentImportTimer = null;
|
||||||
var hidePanelCall = null;
|
var hidePanelCall = null;
|
||||||
|
|
||||||
function addImportFrames() {
|
function addImportFrames()
|
||||||
|
{
|
||||||
$("#impexp-import .importframe").remove();
|
$("#impexp-import .importframe").remove();
|
||||||
$('#impexp-import').append(
|
$('#impexp-import').append(
|
||||||
$('<iframe style="display: none;" name="importiframe" class="importframe"></iframe>'));
|
$('<iframe style="display: none;" name="importiframe" class="importframe"></iframe>'));
|
||||||
}
|
}
|
||||||
function fileInputUpdated() {
|
|
||||||
|
function fileInputUpdated()
|
||||||
|
{
|
||||||
$('#importformfilediv').addClass('importformenabled');
|
$('#importformfilediv').addClass('importformenabled');
|
||||||
$('#importsubmitinput').removeAttr('disabled');
|
$('#importsubmitinput').removeAttr('disabled');
|
||||||
$('#importmessagefail').fadeOut("fast");
|
$('#importmessagefail').fadeOut("fast");
|
||||||
$('#importarrow').show();
|
$('#importarrow').show();
|
||||||
$('#importarrow').animate({paddingLeft:"0px"}, 500)
|
$('#importarrow').animate(
|
||||||
.animate({paddingLeft:"10px"}, 150, 'swing')
|
{
|
||||||
.animate({paddingLeft:"0px"}, 150, 'swing')
|
paddingLeft: "0px"
|
||||||
.animate({paddingLeft:"10px"}, 150, 'swing')
|
}, 500).animate(
|
||||||
.animate({paddingLeft:"0px"}, 150, 'swing')
|
{
|
||||||
.animate({paddingLeft:"10px"}, 150, 'swing')
|
paddingLeft: "10px"
|
||||||
.animate({paddingLeft:"0px"}, 150, 'swing');
|
}, 150, 'swing').animate(
|
||||||
|
{
|
||||||
|
paddingLeft: "0px"
|
||||||
|
}, 150, 'swing').animate(
|
||||||
|
{
|
||||||
|
paddingLeft: "10px"
|
||||||
|
}, 150, 'swing').animate(
|
||||||
|
{
|
||||||
|
paddingLeft: "0px"
|
||||||
|
}, 150, 'swing').animate(
|
||||||
|
{
|
||||||
|
paddingLeft: "10px"
|
||||||
|
}, 150, 'swing').animate(
|
||||||
|
{
|
||||||
|
paddingLeft: "0px"
|
||||||
|
}, 150, 'swing');
|
||||||
}
|
}
|
||||||
function fileInputSubmit() {
|
|
||||||
|
function fileInputSubmit()
|
||||||
|
{
|
||||||
$('#importmessagefail').fadeOut("fast");
|
$('#importmessagefail').fadeOut("fast");
|
||||||
var ret = window.confirm(
|
var ret = window.confirm("Importing a file will overwrite the current text of the pad." + " Are you sure you want to proceed?");
|
||||||
"Importing a file will overwrite the current text of the pad."+
|
if (ret)
|
||||||
" Are you sure you want to proceed?");
|
{
|
||||||
if (ret) {
|
|
||||||
hidePanelCall = paddocbar.hideLaterIfNoOtherInteraction();
|
hidePanelCall = paddocbar.hideLaterIfNoOtherInteraction();
|
||||||
currentImportTimer = window.setTimeout(function() {
|
currentImportTimer = window.setTimeout(function()
|
||||||
if (! currentImportTimer) {
|
{
|
||||||
|
if (!currentImportTimer)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
currentImportTimer = null;
|
currentImportTimer = null;
|
||||||
importFailed("Request timed out.");
|
importFailed("Request timed out.");
|
||||||
}, 25000); // time out after some number of seconds
|
}, 25000); // time out after some number of seconds
|
||||||
$('#importsubmitinput').attr({disabled: true}).val("Importing...");
|
$('#importsubmitinput').attr(
|
||||||
window.setTimeout(function() {
|
{
|
||||||
$('#importfileinput').attr({disabled: true}); }, 0);
|
disabled: true
|
||||||
|
}).val("Importing...");
|
||||||
|
window.setTimeout(function()
|
||||||
|
{
|
||||||
|
$('#importfileinput').attr(
|
||||||
|
{
|
||||||
|
disabled: true
|
||||||
|
});
|
||||||
|
}, 0);
|
||||||
$('#importarrow').stop(true, true).hide();
|
$('#importarrow').stop(true, true).hide();
|
||||||
$('#importstatusball').show();
|
$('#importstatusball').show();
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
function importFailed(msg) {
|
|
||||||
|
function importFailed(msg)
|
||||||
|
{
|
||||||
importErrorMessage(msg);
|
importErrorMessage(msg);
|
||||||
importDone();
|
importDone();
|
||||||
addImportFrames();
|
addImportFrames();
|
||||||
}
|
}
|
||||||
function importDone() {
|
|
||||||
|
function importDone()
|
||||||
|
{
|
||||||
$('#importsubmitinput').removeAttr('disabled').val("Import Now");
|
$('#importsubmitinput').removeAttr('disabled').val("Import Now");
|
||||||
window.setTimeout(function() {
|
window.setTimeout(function()
|
||||||
$('#importfileinput').removeAttr('disabled'); }, 0);
|
{
|
||||||
|
$('#importfileinput').removeAttr('disabled');
|
||||||
|
}, 0);
|
||||||
$('#importstatusball').hide();
|
$('#importstatusball').hide();
|
||||||
importClearTimeout();
|
importClearTimeout();
|
||||||
}
|
}
|
||||||
function importClearTimeout() {
|
|
||||||
if (currentImportTimer) {
|
function importClearTimeout()
|
||||||
|
{
|
||||||
|
if (currentImportTimer)
|
||||||
|
{
|
||||||
window.clearTimeout(currentImportTimer);
|
window.clearTimeout(currentImportTimer);
|
||||||
currentImportTimer = null;
|
currentImportTimer = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function importErrorMessage(msg) {
|
|
||||||
function showError(fade) {
|
function importErrorMessage(msg)
|
||||||
$('#importmessagefail').html(
|
{
|
||||||
'<strong style="color: red">Import failed:</strong> '+
|
function showError(fade)
|
||||||
(msg || 'Please try a different file.'))[(fade?"fadeIn":"show")]();
|
{
|
||||||
|
$('#importmessagefail').html('<strong style="color: red">Import failed:</strong> ' + (msg || 'Please try a different file.'))[(fade ? "fadeIn" : "show")]();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($('#importexport .importmessage').is(':visible')) {
|
if ($('#importexport .importmessage').is(':visible'))
|
||||||
|
{
|
||||||
$('#importmessagesuccess').fadeOut("fast");
|
$('#importmessagesuccess').fadeOut("fast");
|
||||||
$('#importmessagefail').fadeOut("fast", function() {
|
$('#importmessagefail').fadeOut("fast", function()
|
||||||
showError(true); });
|
{
|
||||||
} else {
|
showError(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
showError();
|
showError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function importSuccessful(token) {
|
|
||||||
$.ajax({
|
function importSuccessful(token)
|
||||||
|
{
|
||||||
|
$.ajax(
|
||||||
|
{
|
||||||
type: 'post',
|
type: 'post',
|
||||||
url: '/ep/pad/impexp/import2',
|
url: '/ep/pad/impexp/import2',
|
||||||
data: {token: token, padId: pad.getPadId()},
|
data: {
|
||||||
|
token: token,
|
||||||
|
padId: pad.getPadId()
|
||||||
|
},
|
||||||
success: importApplicationSuccessful,
|
success: importApplicationSuccessful,
|
||||||
error: importApplicationFailed,
|
error: importApplicationFailed,
|
||||||
timeout: 25000
|
timeout: 25000
|
||||||
});
|
});
|
||||||
addImportFrames();
|
addImportFrames();
|
||||||
}
|
}
|
||||||
function importApplicationFailed(xhr, textStatus, errorThrown) {
|
|
||||||
|
function importApplicationFailed(xhr, textStatus, errorThrown)
|
||||||
|
{
|
||||||
importErrorMessage("Error during conversion.");
|
importErrorMessage("Error during conversion.");
|
||||||
importDone();
|
importDone();
|
||||||
}
|
}
|
||||||
function importApplicationSuccessful(data, textStatus) {
|
|
||||||
if (data.substr(0, 2) == "ok") {
|
function importApplicationSuccessful(data, textStatus)
|
||||||
if ($('#importexport .importmessage').is(':visible')) {
|
{
|
||||||
|
if (data.substr(0, 2) == "ok")
|
||||||
|
{
|
||||||
|
if ($('#importexport .importmessage').is(':visible'))
|
||||||
|
{
|
||||||
$('#importexport .importmessage').hide();
|
$('#importexport .importmessage').hide();
|
||||||
}
|
}
|
||||||
$('#importmessagesuccess').html(
|
$('#importmessagesuccess').html('<strong style="color: green">Import successful!</strong>').show();
|
||||||
'<strong style="color: green">Import successful!</strong>').show();
|
|
||||||
$('#importformfilediv').hide();
|
$('#importformfilediv').hide();
|
||||||
window.setTimeout(function() {
|
window.setTimeout(function()
|
||||||
$('#importmessagesuccess').fadeOut("slow", function() {
|
{
|
||||||
|
$('#importmessagesuccess').fadeOut("slow", function()
|
||||||
|
{
|
||||||
$('#importformfilediv').show();
|
$('#importformfilediv').show();
|
||||||
});
|
});
|
||||||
if (hidePanelCall) {
|
if (hidePanelCall)
|
||||||
|
{
|
||||||
hidePanelCall();
|
hidePanelCall();
|
||||||
}
|
}
|
||||||
}, 3000);
|
}, 3000);
|
||||||
} else if (data.substr(0, 4) == "fail") {
|
}
|
||||||
importErrorMessage(
|
else if (data.substr(0, 4) == "fail")
|
||||||
"Couldn't update pad contents. This can happen if your web browser has \"cookies\" disabled.");
|
{
|
||||||
} else if (data.substr(0, 4) == "msg:") {
|
importErrorMessage("Couldn't update pad contents. This can happen if your web browser has \"cookies\" disabled.");
|
||||||
|
}
|
||||||
|
else if (data.substr(0, 4) == "msg:")
|
||||||
|
{
|
||||||
importErrorMessage(data.substr(4));
|
importErrorMessage(data.substr(4));
|
||||||
}
|
}
|
||||||
importDone();
|
importDone();
|
||||||
|
@ -137,47 +198,62 @@ var padimpexp = (function() {
|
||||||
|
|
||||||
///// export
|
///// export
|
||||||
|
|
||||||
function cantExport() {
|
function cantExport()
|
||||||
|
{
|
||||||
var type = $(this);
|
var type = $(this);
|
||||||
if (type.hasClass("exporthrefpdf")) {
|
if (type.hasClass("exporthrefpdf"))
|
||||||
|
{
|
||||||
type = "PDF";
|
type = "PDF";
|
||||||
} else if (type.hasClass("exporthrefdoc")) {
|
}
|
||||||
|
else if (type.hasClass("exporthrefdoc"))
|
||||||
|
{
|
||||||
type = "Microsoft Word";
|
type = "Microsoft Word";
|
||||||
} else if (type.hasClass("exporthrefodt")) {
|
}
|
||||||
|
else if (type.hasClass("exporthrefodt"))
|
||||||
|
{
|
||||||
type = "OpenDocument";
|
type = "OpenDocument";
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
type = "this file";
|
type = "this file";
|
||||||
}
|
}
|
||||||
alert("Exporting as "+type+" format is disabled. Please contact your"+
|
alert("Exporting as " + type + " format is disabled. Please contact your" + " system administrator for details.");
|
||||||
" system administrator for details.");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/////
|
/////
|
||||||
|
|
||||||
var self = {
|
var self = {
|
||||||
init: function() {
|
init: function()
|
||||||
$("#impexp-close").click(function() {paddocbar.setShownPanel(null);});
|
{
|
||||||
|
$("#impexp-close").click(function()
|
||||||
|
{
|
||||||
|
paddocbar.setShownPanel(null);
|
||||||
|
});
|
||||||
|
|
||||||
addImportFrames();
|
addImportFrames();
|
||||||
$("#importfileinput").change(fileInputUpdated);
|
$("#importfileinput").change(fileInputUpdated);
|
||||||
$('#importform').submit(fileInputSubmit);
|
$('#importform').submit(fileInputSubmit);
|
||||||
$('.disabledexport').click(cantExport);
|
$('.disabledexport').click(cantExport);
|
||||||
},
|
},
|
||||||
handleFrameCall: function(callName, argsArray) {
|
handleFrameCall: function(callName, argsArray)
|
||||||
if (callName == 'importFailed') {
|
{
|
||||||
|
if (callName == 'importFailed')
|
||||||
|
{
|
||||||
importFailed(argsArray[0]);
|
importFailed(argsArray[0]);
|
||||||
}
|
}
|
||||||
else if (callName == 'importSuccessful') {
|
else if (callName == 'importSuccessful')
|
||||||
|
{
|
||||||
importSuccessful(argsArray[0]);
|
importSuccessful(argsArray[0]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
disable: function() {
|
disable: function()
|
||||||
|
{
|
||||||
$("#impexp-disabled-clickcatcher").show();
|
$("#impexp-disabled-clickcatcher").show();
|
||||||
$("#impexp-import").css('opacity', 0.5);
|
$("#impexp-import").css('opacity', 0.5);
|
||||||
$("#impexp-export").css('opacity', 0.5);
|
$("#impexp-export").css('opacity', 0.5);
|
||||||
},
|
},
|
||||||
enable: function() {
|
enable: function()
|
||||||
|
{
|
||||||
$("#impexp-disabled-clickcatcher").hide();
|
$("#impexp-disabled-clickcatcher").hide();
|
||||||
$("#impexp-import").css('opacity', 1);
|
$("#impexp-import").css('opacity', 1);
|
||||||
$("#impexp-export").css('opacity', 1);
|
$("#impexp-export").css('opacity', 1);
|
||||||
|
|
|
@ -14,7 +14,8 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var padmodals = (function() {
|
var padmodals = (function()
|
||||||
|
{
|
||||||
|
|
||||||
/*var clearFeedbackEmail = function() {};
|
/*var clearFeedbackEmail = function() {};
|
||||||
function clearFeedback() {
|
function clearFeedback() {
|
||||||
|
@ -37,138 +38,185 @@ var padmodals = (function() {
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
var sendingInvite = false;
|
var sendingInvite = false;
|
||||||
function setSendingInvite(v) {
|
|
||||||
|
function setSendingInvite(v)
|
||||||
|
{
|
||||||
v = !! v;
|
v = !! v;
|
||||||
if (sendingInvite != v) {
|
if (sendingInvite != v)
|
||||||
|
{
|
||||||
sendingInvite = v;
|
sendingInvite = v;
|
||||||
if (v) {
|
if (v)
|
||||||
|
{
|
||||||
$(".sharebox-send").css('opacity', 0.75);
|
$(".sharebox-send").css('opacity', 0.75);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
$("#sharebox-send").css('opacity', 1);
|
$("#sharebox-send").css('opacity', 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var clearShareBoxTo = function() {};
|
var clearShareBoxTo = function()
|
||||||
function clearShareBox() {
|
{};
|
||||||
|
|
||||||
|
function clearShareBox()
|
||||||
|
{
|
||||||
clearShareBoxTo();
|
clearShareBoxTo();
|
||||||
}
|
}
|
||||||
|
|
||||||
var self = {
|
var self = {
|
||||||
init: function() {
|
init: function()
|
||||||
|
{
|
||||||
self.initFeedback();
|
self.initFeedback();
|
||||||
self.initShareBox();
|
self.initShareBox();
|
||||||
},
|
},
|
||||||
initFeedback: function() {
|
initFeedback: function()
|
||||||
|
{
|
||||||
/*var emailField = $("#feedbackbox-email");
|
/*var emailField = $("#feedbackbox-email");
|
||||||
clearFeedbackEmail =
|
clearFeedbackEmail =
|
||||||
padutils.makeFieldLabeledWhenEmpty(emailField, '(your email address)').clear;
|
padutils.makeFieldLabeledWhenEmpty(emailField, '(your email address)').clear;
|
||||||
clearFeedback();*/
|
clearFeedback();*/
|
||||||
|
|
||||||
$("#feedbackbox-hide").click(function() {
|
$("#feedbackbox-hide").click(function()
|
||||||
|
{
|
||||||
self.hideModal();
|
self.hideModal();
|
||||||
});
|
});
|
||||||
/*$("#feedbackbox-send").click(function() {
|
/*$("#feedbackbox-send").click(function() {
|
||||||
self.sendFeedbackEmail();
|
self.sendFeedbackEmail();
|
||||||
});*/
|
});*/
|
||||||
|
|
||||||
$("#feedbackbutton").click(function() {
|
$("#feedbackbutton").click(function()
|
||||||
|
{
|
||||||
self.showFeedback();
|
self.showFeedback();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
initShareBox: function() {
|
initShareBox: function()
|
||||||
$("#sharebutton").click(function() {
|
{
|
||||||
|
$("#sharebutton").click(function()
|
||||||
|
{
|
||||||
self.showShareBox();
|
self.showShareBox();
|
||||||
});
|
});
|
||||||
$("#sharebox-hide").click(function() {
|
$("#sharebox-hide").click(function()
|
||||||
|
{
|
||||||
self.hideModal();
|
self.hideModal();
|
||||||
});
|
});
|
||||||
$("#sharebox-send").click(function() {
|
$("#sharebox-send").click(function()
|
||||||
|
{
|
||||||
self.sendInvite();
|
self.sendInvite();
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#sharebox-url").click(function() {
|
$("#sharebox-url").click(function()
|
||||||
|
{
|
||||||
$("#sharebox-url").focus().select();
|
$("#sharebox-url").focus().select();
|
||||||
});
|
});
|
||||||
|
|
||||||
clearShareBoxTo =
|
clearShareBoxTo = padutils.makeFieldLabeledWhenEmpty($("#sharebox-to"), "(email addresses)").clear;
|
||||||
padutils.makeFieldLabeledWhenEmpty($("#sharebox-to"),
|
|
||||||
"(email addresses)").clear;
|
|
||||||
clearShareBox();
|
clearShareBox();
|
||||||
|
|
||||||
$("#sharebox-subject").val(self.getDefaultShareBoxSubjectForName(pad.getUserName()));
|
$("#sharebox-subject").val(self.getDefaultShareBoxSubjectForName(pad.getUserName()));
|
||||||
$("#sharebox-message").val(self.getDefaultShareBoxMessageForName(pad.getUserName()));
|
$("#sharebox-message").val(self.getDefaultShareBoxMessageForName(pad.getUserName()));
|
||||||
|
|
||||||
$("#sharebox-stripe .setsecurity").click(function() {
|
$("#sharebox-stripe .setsecurity").click(function()
|
||||||
|
{
|
||||||
self.hideModal();
|
self.hideModal();
|
||||||
paddocbar.setShownPanel('security');
|
paddocbar.setShownPanel('security');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getDefaultShareBoxMessageForName: function(name) {
|
getDefaultShareBoxMessageForName: function(name)
|
||||||
return (name || "Somebody")+" has shared an EtherPad document with you."+
|
{
|
||||||
"\n\n"+"View it here:\n\n"+
|
return (name || "Somebody") + " has shared an EtherPad document with you." + "\n\n" + "View it here:\n\n" + padutils.escapeHtml($(".sharebox-url").val() + "\n");
|
||||||
padutils.escapeHtml($(".sharebox-url").val()+"\n");
|
|
||||||
},
|
},
|
||||||
getDefaultShareBoxSubjectForName: function(name) {
|
getDefaultShareBoxSubjectForName: function(name)
|
||||||
|
{
|
||||||
return (name || "Somebody") + " invited you to an EtherPad document";
|
return (name || "Somebody") + " invited you to an EtherPad document";
|
||||||
},
|
},
|
||||||
relayoutWithBottom: function(px) {
|
relayoutWithBottom: function(px)
|
||||||
|
{
|
||||||
$("#modaloverlay").height(px);
|
$("#modaloverlay").height(px);
|
||||||
$("#sharebox").css('left',
|
$("#sharebox").css('left', Math.floor(($(window).width() - $("#sharebox").outerWidth()) / 2));
|
||||||
Math.floor(($(window).width() -
|
$("#feedbackbox").css('left', Math.floor(($(window).width() - $("#feedbackbox").outerWidth()) / 2));
|
||||||
$("#sharebox").outerWidth())/2));
|
|
||||||
$("#feedbackbox").css('left',
|
|
||||||
Math.floor(($(window).width() -
|
|
||||||
$("#feedbackbox").outerWidth())/2));
|
|
||||||
},
|
},
|
||||||
showFeedback: function() {
|
showFeedback: function()
|
||||||
|
{
|
||||||
self.showModal("#feedbackbox");
|
self.showModal("#feedbackbox");
|
||||||
},
|
},
|
||||||
showShareBox: function() {
|
showShareBox: function()
|
||||||
|
{
|
||||||
// when showing the dialog, if it still says "Somebody" invited you
|
// when showing the dialog, if it still says "Somebody" invited you
|
||||||
// then we fill in the updated username if there is one;
|
// then we fill in the updated username if there is one;
|
||||||
// otherwise, we don't touch it, perhaps the user is happy with it
|
// otherwise, we don't touch it, perhaps the user is happy with it
|
||||||
var msgbox = $("#sharebox-message");
|
var msgbox = $("#sharebox-message");
|
||||||
if (msgbox.val() == self.getDefaultShareBoxMessageForName(null)) {
|
if (msgbox.val() == self.getDefaultShareBoxMessageForName(null))
|
||||||
|
{
|
||||||
msgbox.val(self.getDefaultShareBoxMessageForName(pad.getUserName()));
|
msgbox.val(self.getDefaultShareBoxMessageForName(pad.getUserName()));
|
||||||
}
|
}
|
||||||
var subjBox = $("#sharebox-subject");
|
var subjBox = $("#sharebox-subject");
|
||||||
if (subjBox.val() == self.getDefaultShareBoxSubjectForName(null)) {
|
if (subjBox.val() == self.getDefaultShareBoxSubjectForName(null))
|
||||||
|
{
|
||||||
subjBox.val(self.getDefaultShareBoxSubjectForName(pad.getUserName()));
|
subjBox.val(self.getDefaultShareBoxSubjectForName(pad.getUserName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pad.isPadPublic()) {
|
if (pad.isPadPublic())
|
||||||
|
{
|
||||||
$("#sharebox-stripe").get(0).className = 'sharebox-stripe-public';
|
$("#sharebox-stripe").get(0).className = 'sharebox-stripe-public';
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
$("#sharebox-stripe").get(0).className = 'sharebox-stripe-private';
|
$("#sharebox-stripe").get(0).className = 'sharebox-stripe-private';
|
||||||
}
|
}
|
||||||
|
|
||||||
self.showModal("#sharebox", 500);
|
self.showModal("#sharebox", 500);
|
||||||
$("#sharebox-url").focus().select();
|
$("#sharebox-url").focus().select();
|
||||||
},
|
},
|
||||||
showModal: function(modalId, duration) {
|
showModal: function(modalId, duration)
|
||||||
|
{
|
||||||
$(".modaldialog").hide();
|
$(".modaldialog").hide();
|
||||||
$(modalId).show().css({'opacity': 0}).animate({'opacity': 1}, duration);
|
$(modalId).show().css(
|
||||||
$("#modaloverlay").show().css({'opacity': 0}).animate({'opacity': 1}, duration);
|
{
|
||||||
|
'opacity': 0
|
||||||
|
}).animate(
|
||||||
|
{
|
||||||
|
'opacity': 1
|
||||||
|
}, duration);
|
||||||
|
$("#modaloverlay").show().css(
|
||||||
|
{
|
||||||
|
'opacity': 0
|
||||||
|
}).animate(
|
||||||
|
{
|
||||||
|
'opacity': 1
|
||||||
|
}, duration);
|
||||||
},
|
},
|
||||||
hideModal: function(duration) {
|
hideModal: function(duration)
|
||||||
|
{
|
||||||
padutils.cancelActions('hide-feedbackbox');
|
padutils.cancelActions('hide-feedbackbox');
|
||||||
padutils.cancelActions('hide-sharebox');
|
padutils.cancelActions('hide-sharebox');
|
||||||
$("#sharebox-response").hide();
|
$("#sharebox-response").hide();
|
||||||
$(".modaldialog").animate({'opacity': 0}, duration, function () { $("#modaloverlay").hide(); });
|
$(".modaldialog").animate(
|
||||||
$("#modaloverlay").animate({'opacity': 0}, duration, function () { $("#modaloverlay").hide(); });
|
{
|
||||||
|
'opacity': 0
|
||||||
|
}, duration, function()
|
||||||
|
{
|
||||||
|
$("#modaloverlay").hide();
|
||||||
|
});
|
||||||
|
$("#modaloverlay").animate(
|
||||||
|
{
|
||||||
|
'opacity': 0
|
||||||
|
}, duration, function()
|
||||||
|
{
|
||||||
|
$("#modaloverlay").hide();
|
||||||
|
});
|
||||||
},
|
},
|
||||||
hideFeedbackLaterIfNoOtherInteraction: function() {
|
hideFeedbackLaterIfNoOtherInteraction: function()
|
||||||
return padutils.getCancellableAction('hide-feedbackbox',
|
{
|
||||||
function() {
|
return padutils.getCancellableAction('hide-feedbackbox', function()
|
||||||
|
{
|
||||||
self.hideModal();
|
self.hideModal();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
hideShareboxLaterIfNoOtherInteraction: function() {
|
hideShareboxLaterIfNoOtherInteraction: function()
|
||||||
return padutils.getCancellableAction('hide-sharebox',
|
{
|
||||||
function() {
|
return padutils.getCancellableAction('hide-sharebox', function()
|
||||||
|
{
|
||||||
self.hideModal();
|
self.hideModal();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -217,38 +265,44 @@ var padmodals = (function() {
|
||||||
$("#feedbackbox-response").show();
|
$("#feedbackbox-response").show();
|
||||||
}
|
}
|
||||||
},*/
|
},*/
|
||||||
sendInvite: function() {
|
sendInvite: function()
|
||||||
if (sendingInvite) {
|
{
|
||||||
|
if (sendingInvite)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (! pad.isFullyConnected()) {
|
if (!pad.isFullyConnected())
|
||||||
|
{
|
||||||
displayErrorMessage("Error: Connection to the server is down or flaky.");
|
displayErrorMessage("Error: Connection to the server is down or flaky.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var message = $("#sharebox-message").val();
|
var message = $("#sharebox-message").val();
|
||||||
if (! message) {
|
if (!message)
|
||||||
|
{
|
||||||
displayErrorMessage("Please enter a message body before sending.");
|
displayErrorMessage("Please enter a message body before sending.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var emails = ($("#sharebox-to").hasClass('editempty') ? '' :
|
var emails = ($("#sharebox-to").hasClass('editempty') ? '' : $("#sharebox-to").val()) || '';
|
||||||
$("#sharebox-to").val()) || '';
|
|
||||||
// find runs of characters that aren't obviously non-email punctuation
|
// find runs of characters that aren't obviously non-email punctuation
|
||||||
var emailArray = emails.match(/[^\s,:;<>\"\'\/\(\)\[\]{}]+/g) || [];
|
var emailArray = emails.match(/[^\s,:;<>\"\'\/\(\)\[\]{}]+/g) || [];
|
||||||
if (emailArray.length == 0) {
|
if (emailArray.length == 0)
|
||||||
|
{
|
||||||
displayErrorMessage('Please enter at least one "To:" address.');
|
displayErrorMessage('Please enter at least one "To:" address.');
|
||||||
$("#sharebox-to").focus().select();
|
$("#sharebox-to").focus().select();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for(var i=0;i<emailArray.length;i++) {
|
for (var i = 0; i < emailArray.length; i++)
|
||||||
|
{
|
||||||
var addr = emailArray[i];
|
var addr = emailArray[i];
|
||||||
if (! addr.match(/^[\w\.\_\+\-]+\@[\w\_\-]+\.[\w\_\-\.]+$/)) {
|
if (!addr.match(/^[\w\.\_\+\-]+\@[\w\_\-]+\.[\w\_\-\.]+$/))
|
||||||
displayErrorMessage('"'+padutils.escapeHtml(addr) +
|
{
|
||||||
'" does not appear to be a valid email address.');
|
displayErrorMessage('"' + padutils.escapeHtml(addr) + '" does not appear to be a valid email address.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var subject = $("#sharebox-subject").val();
|
var subject = $("#sharebox-subject").val();
|
||||||
if (! subject) {
|
if (!subject)
|
||||||
|
{
|
||||||
subject = self.getDefaultShareBoxSubjectForName(pad.getUserName());
|
subject = self.getDefaultShareBoxSubjectForName(pad.getUserName());
|
||||||
$("#sharebox-subject").val(subject); // force the default subject
|
$("#sharebox-subject").val(subject); // force the default subject
|
||||||
}
|
}
|
||||||
|
@ -258,7 +312,8 @@ var padmodals = (function() {
|
||||||
setSendingInvite(true);
|
setSendingInvite(true);
|
||||||
$("#sharebox-response").html("Sending...").get(0).className = '';
|
$("#sharebox-response").html("Sending...").get(0).className = '';
|
||||||
$("#sharebox-response").show();
|
$("#sharebox-response").show();
|
||||||
$.ajax({
|
$.ajax(
|
||||||
|
{
|
||||||
type: 'post',
|
type: 'post',
|
||||||
url: '/ep/pad/emailinvite',
|
url: '/ep/pad/emailinvite',
|
||||||
data: {
|
data: {
|
||||||
|
@ -272,22 +327,30 @@ var padmodals = (function() {
|
||||||
error: error
|
error: error
|
||||||
});
|
});
|
||||||
var hideCall = self.hideShareboxLaterIfNoOtherInteraction();
|
var hideCall = self.hideShareboxLaterIfNoOtherInteraction();
|
||||||
function success(msg) {
|
|
||||||
|
function success(msg)
|
||||||
|
{
|
||||||
setSendingInvite(false);
|
setSendingInvite(false);
|
||||||
$("#sharebox-response").html("Email invitation sent!").get(0).className = 'goodresponse';
|
$("#sharebox-response").html("Email invitation sent!").get(0).className = 'goodresponse';
|
||||||
$("#sharebox-response").show();
|
$("#sharebox-response").show();
|
||||||
window.setTimeout(function() {
|
window.setTimeout(function()
|
||||||
$("#sharebox-response").fadeOut('slow', function() {
|
{
|
||||||
|
$("#sharebox-response").fadeOut('slow', function()
|
||||||
|
{
|
||||||
hideCall();
|
hideCall();
|
||||||
});
|
});
|
||||||
}, 1500);
|
}, 1500);
|
||||||
}
|
}
|
||||||
function error(e) {
|
|
||||||
|
function error(e)
|
||||||
|
{
|
||||||
setSendingFeedback(false);
|
setSendingFeedback(false);
|
||||||
$("#sharebox-response").html("An error occurred; no email was sent.").get(0).className = 'badresponse';
|
$("#sharebox-response").html("An error occurred; no email was sent.").get(0).className = 'badresponse';
|
||||||
$("#sharebox-response").show();
|
$("#sharebox-response").show();
|
||||||
}
|
}
|
||||||
function displayErrorMessage(msgHtml) {
|
|
||||||
|
function displayErrorMessage(msgHtml)
|
||||||
|
{
|
||||||
$("#sharebox-response").html(msgHtml).get(0).className = 'badresponse';
|
$("#sharebox-response").html(msgHtml).get(0).className = 'badresponse';
|
||||||
$("#sharebox-response").show();
|
$("#sharebox-response").show();
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,23 +15,19 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
var padsavedrevs = (function() {
|
var padsavedrevs = (function()
|
||||||
|
{
|
||||||
|
|
||||||
function reversedCopy(L) {
|
function reversedCopy(L)
|
||||||
|
{
|
||||||
var L2 = L.slice();
|
var L2 = L.slice();
|
||||||
L2.reverse();
|
L2.reverse();
|
||||||
return L2;
|
return L2;
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeRevisionBox(revisionInfo, rnum) {
|
function makeRevisionBox(revisionInfo, rnum)
|
||||||
var box = $('<div class="srouterbox">'+
|
{
|
||||||
'<div class="srinnerbox">'+
|
var box = $('<div class="srouterbox">' + '<div class="srinnerbox">' + '<a href="javascript:void(0)" class="srname"><!-- --></a>' + '<div class="sractions"><a class="srview" href="javascript:void(0)" target="_blank">view</a> | <a class="srrestore" href="javascript:void(0)">restore</a></div>' + '<div class="srtime"><!-- --></div>' + '<div class="srauthor"><!-- --></div>' + '<img class="srtwirly" src="static/img/misc/status-ball.gif">' + '</div></div>');
|
||||||
'<a href="javascript:void(0)" class="srname"><!-- --></a>'+
|
|
||||||
'<div class="sractions"><a class="srview" href="javascript:void(0)" target="_blank">view</a> | <a class="srrestore" href="javascript:void(0)">restore</a></div>'+
|
|
||||||
'<div class="srtime"><!-- --></div>'+
|
|
||||||
'<div class="srauthor"><!-- --></div>'+
|
|
||||||
'<img class="srtwirly" src="static/img/misc/status-ball.gif">'+
|
|
||||||
'</div></div>');
|
|
||||||
setBoxLabel(box, revisionInfo.label);
|
setBoxLabel(box, revisionInfo.label);
|
||||||
setBoxTimestamp(box, revisionInfo.timestamp);
|
setBoxTimestamp(box, revisionInfo.timestamp);
|
||||||
box.find(".srauthor").html("by " + padutils.escapeHtml(revisionInfo.savedBy));
|
box.find(".srauthor").html("by " + padutils.escapeHtml(revisionInfo.savedBy));
|
||||||
|
@ -39,22 +35,31 @@ var padsavedrevs = (function() {
|
||||||
box.find(".srview").attr('href', viewLink);
|
box.find(".srview").attr('href', viewLink);
|
||||||
var restoreLink = 'javascript:void padsavedrevs.restoreRevision(' + rnum + ');';
|
var restoreLink = 'javascript:void padsavedrevs.restoreRevision(' + rnum + ');';
|
||||||
box.find(".srrestore").attr('href', restoreLink);
|
box.find(".srrestore").attr('href', restoreLink);
|
||||||
box.find(".srname").click(function(evt) {
|
box.find(".srname").click(function(evt)
|
||||||
|
{
|
||||||
editRevisionLabel(rnum, box);
|
editRevisionLabel(rnum, box);
|
||||||
});
|
});
|
||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
function setBoxLabel(box, label) {
|
|
||||||
|
function setBoxLabel(box, label)
|
||||||
|
{
|
||||||
box.find(".srname").html(padutils.escapeHtml(label)).attr('title', label);
|
box.find(".srname").html(padutils.escapeHtml(label)).attr('title', label);
|
||||||
}
|
}
|
||||||
function setBoxTimestamp(box, timestamp) {
|
|
||||||
|
function setBoxTimestamp(box, timestamp)
|
||||||
|
{
|
||||||
box.find(".srtime").html(padutils.escapeHtml(
|
box.find(".srtime").html(padutils.escapeHtml(
|
||||||
padutils.timediff(new Date(timestamp))));
|
padutils.timediff(new Date(timestamp))));
|
||||||
}
|
}
|
||||||
function getNthBox(n) {
|
|
||||||
|
function getNthBox(n)
|
||||||
|
{
|
||||||
return $("#savedrevisions .srouterbox").eq(n);
|
return $("#savedrevisions .srouterbox").eq(n);
|
||||||
}
|
}
|
||||||
function editRevisionLabel(rnum, box) {
|
|
||||||
|
function editRevisionLabel(rnum, box)
|
||||||
|
{
|
||||||
var input = $('<input type="text" class="srnameedit"/>');
|
var input = $('<input type="text" class="srnameedit"/>');
|
||||||
box.find(".srnameedit").remove(); // just in case
|
box.find(".srnameedit").remove(); // just in case
|
||||||
var label = box.find(".srname");
|
var label = box.find(".srname");
|
||||||
|
@ -64,114 +69,148 @@ var padsavedrevs = (function() {
|
||||||
input.css('left', label.position().left);
|
input.css('left', label.position().left);
|
||||||
label.after(input);
|
label.after(input);
|
||||||
label.css('opacity', 0);
|
label.css('opacity', 0);
|
||||||
function endEdit() {
|
|
||||||
|
function endEdit()
|
||||||
|
{
|
||||||
input.remove();
|
input.remove();
|
||||||
label.css('opacity', 1);
|
label.css('opacity', 1);
|
||||||
}
|
}
|
||||||
var rev = currentRevisionList[rnum];
|
var rev = currentRevisionList[rnum];
|
||||||
var oldLabel = rev.label;
|
var oldLabel = rev.label;
|
||||||
input.blur(function() {
|
input.blur(function()
|
||||||
|
{
|
||||||
var newLabel = input.val();
|
var newLabel = input.val();
|
||||||
if (newLabel && newLabel != oldLabel) {
|
if (newLabel && newLabel != oldLabel)
|
||||||
|
{
|
||||||
relabelRevision(rnum, newLabel);
|
relabelRevision(rnum, newLabel);
|
||||||
}
|
}
|
||||||
endEdit();
|
endEdit();
|
||||||
});
|
});
|
||||||
input.val(rev.label).focus().select();
|
input.val(rev.label).focus().select();
|
||||||
padutils.bindEnterAndEscape(input, function onEnter() {
|
padutils.bindEnterAndEscape(input, function onEnter()
|
||||||
|
{
|
||||||
input.blur();
|
input.blur();
|
||||||
}, function onEscape() {
|
}, function onEscape()
|
||||||
|
{
|
||||||
input.val('').blur();
|
input.val('').blur();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function relabelRevision(rnum, newLabel) {
|
|
||||||
|
function relabelRevision(rnum, newLabel)
|
||||||
|
{
|
||||||
var rev = currentRevisionList[rnum];
|
var rev = currentRevisionList[rnum];
|
||||||
$.ajax({
|
$.ajax(
|
||||||
|
{
|
||||||
type: 'post',
|
type: 'post',
|
||||||
url: '/ep/pad/saverevisionlabel',
|
url: '/ep/pad/saverevisionlabel',
|
||||||
data: {userId: pad.getUserId(),
|
data: {
|
||||||
|
userId: pad.getUserId(),
|
||||||
padId: pad.getPadId(),
|
padId: pad.getPadId(),
|
||||||
revId: rev.id,
|
revId: rev.id,
|
||||||
newLabel: newLabel},
|
newLabel: newLabel
|
||||||
|
},
|
||||||
success: success,
|
success: success,
|
||||||
error: error
|
error: error
|
||||||
});
|
});
|
||||||
function success(text) {
|
|
||||||
|
function success(text)
|
||||||
|
{
|
||||||
var newRevisionList = JSON.parse(text);
|
var newRevisionList = JSON.parse(text);
|
||||||
self.newRevisionList(newRevisionList);
|
self.newRevisionList(newRevisionList);
|
||||||
pad.sendClientMessage({
|
pad.sendClientMessage(
|
||||||
|
{
|
||||||
type: 'revisionLabel',
|
type: 'revisionLabel',
|
||||||
revisionList: reversedCopy(currentRevisionList),
|
revisionList: reversedCopy(currentRevisionList),
|
||||||
savedBy: pad.getUserName(),
|
savedBy: pad.getUserName(),
|
||||||
newLabel: newLabel
|
newLabel: newLabel
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function error(e) {
|
|
||||||
|
function error(e)
|
||||||
|
{
|
||||||
alert("Oops! There was an error saving that revision label. Please try again later.");
|
alert("Oops! There was an error saving that revision label. Please try again later.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentRevisionList = [];
|
var currentRevisionList = [];
|
||||||
function setRevisionList(newRevisionList, noAnimation) {
|
|
||||||
|
function setRevisionList(newRevisionList, noAnimation)
|
||||||
|
{
|
||||||
// deals with changed labels and new added revisions
|
// deals with changed labels and new added revisions
|
||||||
for(var i=0; i<currentRevisionList.length; i++) {
|
for (var i = 0; i < currentRevisionList.length; i++)
|
||||||
|
{
|
||||||
var a = currentRevisionList[i];
|
var a = currentRevisionList[i];
|
||||||
var b = newRevisionList[i];
|
var b = newRevisionList[i];
|
||||||
if (b.label != a.label) {
|
if (b.label != a.label)
|
||||||
|
{
|
||||||
setBoxLabel(getNthBox(i), b.label);
|
setBoxLabel(getNthBox(i), b.label);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(var j=currentRevisionList.length; j<newRevisionList.length; j++) {
|
for (var j = currentRevisionList.length; j < newRevisionList.length; j++)
|
||||||
|
{
|
||||||
var newBox = makeRevisionBox(newRevisionList[j], j);
|
var newBox = makeRevisionBox(newRevisionList[j], j);
|
||||||
$("#savedrevs-scrollinner").append(newBox);
|
$("#savedrevs-scrollinner").append(newBox);
|
||||||
newBox.css('left', j * REVISION_BOX_WIDTH);
|
newBox.css('left', j * REVISION_BOX_WIDTH);
|
||||||
}
|
}
|
||||||
var newOnes = (newRevisionList.length > currentRevisionList.length);
|
var newOnes = (newRevisionList.length > currentRevisionList.length);
|
||||||
currentRevisionList = newRevisionList;
|
currentRevisionList = newRevisionList;
|
||||||
if (newOnes) {
|
if (newOnes)
|
||||||
|
{
|
||||||
setDesiredScroll(getMaxScroll());
|
setDesiredScroll(getMaxScroll());
|
||||||
if (noAnimation) {
|
if (noAnimation)
|
||||||
|
{
|
||||||
setScroll(desiredScroll);
|
setScroll(desiredScroll);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! noAnimation) {
|
if (!noAnimation)
|
||||||
|
{
|
||||||
var nameOfLast = currentRevisionList[currentRevisionList.length - 1].label;
|
var nameOfLast = currentRevisionList[currentRevisionList.length - 1].label;
|
||||||
displaySavedTip(nameOfLast);
|
displaySavedTip(nameOfLast);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function refreshRevisionList() {
|
|
||||||
for(var i=0;i<currentRevisionList.length; i++) {
|
function refreshRevisionList()
|
||||||
|
{
|
||||||
|
for (var i = 0; i < currentRevisionList.length; i++)
|
||||||
|
{
|
||||||
var r = currentRevisionList[i];
|
var r = currentRevisionList[i];
|
||||||
var box = getNthBox(i);
|
var box = getNthBox(i);
|
||||||
setBoxTimestamp(box, r.timestamp);
|
setBoxTimestamp(box, r.timestamp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var savedTipAnimator = padutils.makeShowHideAnimator(function(state) {
|
var savedTipAnimator = padutils.makeShowHideAnimator(function(state)
|
||||||
if (state == -1) {
|
{
|
||||||
|
if (state == -1)
|
||||||
|
{
|
||||||
$("#revision-notifier").css('opacity', 0).css('display', 'block');
|
$("#revision-notifier").css('opacity', 0).css('display', 'block');
|
||||||
}
|
}
|
||||||
else if (state == 0) {
|
else if (state == 0)
|
||||||
|
{
|
||||||
$("#revision-notifier").css('opacity', 1);
|
$("#revision-notifier").css('opacity', 1);
|
||||||
}
|
}
|
||||||
else if (state == 1) {
|
else if (state == 1)
|
||||||
|
{
|
||||||
$("#revision-notifier").css('opacity', 0).css('display', 'none');
|
$("#revision-notifier").css('opacity', 0).css('display', 'none');
|
||||||
}
|
}
|
||||||
else if (state < 0) {
|
else if (state < 0)
|
||||||
|
{
|
||||||
$("#revision-notifier").css('opacity', 1);
|
$("#revision-notifier").css('opacity', 1);
|
||||||
}
|
}
|
||||||
else if (state > 0) {
|
else if (state > 0)
|
||||||
|
{
|
||||||
$("#revision-notifier").css('opacity', 1 - state);
|
$("#revision-notifier").css('opacity', 1 - state);
|
||||||
}
|
}
|
||||||
}, false, 25, 300);
|
}, false, 25, 300);
|
||||||
|
|
||||||
function displaySavedTip(text) {
|
function displaySavedTip(text)
|
||||||
|
{
|
||||||
$("#revision-notifier .name").html(padutils.escapeHtml(text));
|
$("#revision-notifier .name").html(padutils.escapeHtml(text));
|
||||||
savedTipAnimator.show();
|
savedTipAnimator.show();
|
||||||
padutils.cancelActions("hide-revision-notifier");
|
padutils.cancelActions("hide-revision-notifier");
|
||||||
var hideLater = padutils.getCancellableAction("hide-revision-notifier",
|
var hideLater = padutils.getCancellableAction("hide-revision-notifier", function()
|
||||||
function() {
|
{
|
||||||
savedTipAnimator.hide();
|
savedTipAnimator.hide();
|
||||||
});
|
});
|
||||||
window.setTimeout(hideLater, 3000);
|
window.setTimeout(hideLater, 3000);
|
||||||
|
@ -180,79 +219,106 @@ var padsavedrevs = (function() {
|
||||||
var REVISION_BOX_WIDTH = 120;
|
var REVISION_BOX_WIDTH = 120;
|
||||||
var curScroll = 0; // distance between left of revisions and right of view
|
var curScroll = 0; // distance between left of revisions and right of view
|
||||||
var desiredScroll = 0;
|
var desiredScroll = 0;
|
||||||
function getScrollWidth() {
|
|
||||||
|
function getScrollWidth()
|
||||||
|
{
|
||||||
return REVISION_BOX_WIDTH * currentRevisionList.length;
|
return REVISION_BOX_WIDTH * currentRevisionList.length;
|
||||||
}
|
}
|
||||||
function getViewportWidth() {
|
|
||||||
|
function getViewportWidth()
|
||||||
|
{
|
||||||
return $("#savedrevs-scrollouter").width();
|
return $("#savedrevs-scrollouter").width();
|
||||||
}
|
}
|
||||||
function getMinScroll() {
|
|
||||||
|
function getMinScroll()
|
||||||
|
{
|
||||||
return Math.min(getViewportWidth(), getScrollWidth());
|
return Math.min(getViewportWidth(), getScrollWidth());
|
||||||
}
|
}
|
||||||
function getMaxScroll() {
|
|
||||||
|
function getMaxScroll()
|
||||||
|
{
|
||||||
return getScrollWidth();
|
return getScrollWidth();
|
||||||
}
|
}
|
||||||
function setScroll(newScroll) {
|
|
||||||
|
function setScroll(newScroll)
|
||||||
|
{
|
||||||
curScroll = newScroll;
|
curScroll = newScroll;
|
||||||
$("#savedrevs-scrollinner").css('right', newScroll);
|
$("#savedrevs-scrollinner").css('right', newScroll);
|
||||||
updateScrollArrows();
|
updateScrollArrows();
|
||||||
}
|
}
|
||||||
function setDesiredScroll(newDesiredScroll, dontUpdate) {
|
|
||||||
desiredScroll = Math.min(getMaxScroll(), Math.max(getMinScroll(),
|
function setDesiredScroll(newDesiredScroll, dontUpdate)
|
||||||
newDesiredScroll));
|
{
|
||||||
if (! dontUpdate) {
|
desiredScroll = Math.min(getMaxScroll(), Math.max(getMinScroll(), newDesiredScroll));
|
||||||
|
if (!dontUpdate)
|
||||||
|
{
|
||||||
updateScroll();
|
updateScroll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function updateScroll() {
|
|
||||||
|
function updateScroll()
|
||||||
|
{
|
||||||
updateScrollArrows();
|
updateScrollArrows();
|
||||||
scrollAnimator.scheduleAnimation();
|
scrollAnimator.scheduleAnimation();
|
||||||
}
|
}
|
||||||
function updateScrollArrows() {
|
|
||||||
$("#savedrevs-scrollleft").toggleClass("disabledscrollleft",
|
function updateScrollArrows()
|
||||||
desiredScroll <= getMinScroll());
|
{
|
||||||
$("#savedrevs-scrollright").toggleClass("disabledscrollright",
|
$("#savedrevs-scrollleft").toggleClass("disabledscrollleft", desiredScroll <= getMinScroll());
|
||||||
desiredScroll >= getMaxScroll());
|
$("#savedrevs-scrollright").toggleClass("disabledscrollright", desiredScroll >= getMaxScroll());
|
||||||
}
|
}
|
||||||
var scrollAnimator = padutils.makeAnimationScheduler(function() {
|
var scrollAnimator = padutils.makeAnimationScheduler(function()
|
||||||
|
{
|
||||||
setDesiredScroll(desiredScroll, true); // re-clamp
|
setDesiredScroll(desiredScroll, true); // re-clamp
|
||||||
if (Math.abs(desiredScroll - curScroll) < 1) {
|
if (Math.abs(desiredScroll - curScroll) < 1)
|
||||||
|
{
|
||||||
setScroll(desiredScroll);
|
setScroll(desiredScroll);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
setScroll(curScroll + (desiredScroll - curScroll) * 0.5);
|
setScroll(curScroll + (desiredScroll - curScroll) * 0.5);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}, 50, 2);
|
}, 50, 2);
|
||||||
|
|
||||||
var isSaving = false;
|
var isSaving = false;
|
||||||
function setIsSaving(v) {
|
|
||||||
|
function setIsSaving(v)
|
||||||
|
{
|
||||||
isSaving = v;
|
isSaving = v;
|
||||||
rerenderButton();
|
rerenderButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
function haveReachedRevLimit() {
|
function haveReachedRevLimit()
|
||||||
|
{
|
||||||
var mv = pad.getPrivilege('maxRevisions');
|
var mv = pad.getPrivilege('maxRevisions');
|
||||||
return (!(mv < 0 || mv > currentRevisionList.length));
|
return (!(mv < 0 || mv > currentRevisionList.length));
|
||||||
}
|
}
|
||||||
function rerenderButton() {
|
|
||||||
if (isSaving || (! pad.isFullyConnected()) ||
|
function rerenderButton()
|
||||||
haveReachedRevLimit()) {
|
{
|
||||||
|
if (isSaving || (!pad.isFullyConnected()) || haveReachedRevLimit())
|
||||||
|
{
|
||||||
$("#savedrevs-savenow").css('opacity', 0.75);
|
$("#savedrevs-savenow").css('opacity', 0.75);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
$("#savedrevs-savenow").css('opacity', 1);
|
$("#savedrevs-savenow").css('opacity', 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var scrollRepeatTimer = null;
|
var scrollRepeatTimer = null;
|
||||||
var scrollStartTime = 0;
|
var scrollStartTime = 0;
|
||||||
function setScrollRepeatTimer(dir) {
|
|
||||||
|
function setScrollRepeatTimer(dir)
|
||||||
|
{
|
||||||
clearScrollRepeatTimer();
|
clearScrollRepeatTimer();
|
||||||
scrollStartTime = +new Date;
|
scrollStartTime = +new Date;
|
||||||
scrollRepeatTimer = window.setTimeout(function f() {
|
scrollRepeatTimer = window.setTimeout(function f()
|
||||||
if (! scrollRepeatTimer) {
|
{
|
||||||
|
if (!scrollRepeatTimer)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.scroll(dir);
|
self.scroll(dir);
|
||||||
|
@ -262,8 +328,11 @@ var padsavedrevs = (function() {
|
||||||
}, 300);
|
}, 300);
|
||||||
$(document).bind('mouseup', clearScrollRepeatTimer);
|
$(document).bind('mouseup', clearScrollRepeatTimer);
|
||||||
}
|
}
|
||||||
function clearScrollRepeatTimer() {
|
|
||||||
if (scrollRepeatTimer) {
|
function clearScrollRepeatTimer()
|
||||||
|
{
|
||||||
|
if (scrollRepeatTimer)
|
||||||
|
{
|
||||||
window.clearTimeout(scrollRepeatTimer);
|
window.clearTimeout(scrollRepeatTimer);
|
||||||
scrollRepeatTimer = null;
|
scrollRepeatTimer = null;
|
||||||
}
|
}
|
||||||
|
@ -271,78 +340,103 @@ var padsavedrevs = (function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
var self = {
|
var self = {
|
||||||
init: function(initialRevisions) {
|
init: function(initialRevisions)
|
||||||
|
{
|
||||||
self.newRevisionList(initialRevisions, true);
|
self.newRevisionList(initialRevisions, true);
|
||||||
|
|
||||||
$("#savedrevs-savenow").click(function() { self.saveNow(); });
|
$("#savedrevs-savenow").click(function()
|
||||||
$("#savedrevs-scrollleft").mousedown(function() {
|
{
|
||||||
|
self.saveNow();
|
||||||
|
});
|
||||||
|
$("#savedrevs-scrollleft").mousedown(function()
|
||||||
|
{
|
||||||
self.scroll('left');
|
self.scroll('left');
|
||||||
setScrollRepeatTimer('left');
|
setScrollRepeatTimer('left');
|
||||||
});
|
});
|
||||||
$("#savedrevs-scrollright").mousedown(function() {
|
$("#savedrevs-scrollright").mousedown(function()
|
||||||
|
{
|
||||||
self.scroll('right');
|
self.scroll('right');
|
||||||
setScrollRepeatTimer('right');
|
setScrollRepeatTimer('right');
|
||||||
});
|
});
|
||||||
$("#savedrevs-close").click(function() {paddocbar.setShownPanel(null);});
|
$("#savedrevs-close").click(function()
|
||||||
|
{
|
||||||
|
paddocbar.setShownPanel(null);
|
||||||
|
});
|
||||||
|
|
||||||
// update "saved n minutes ago" times
|
// update "saved n minutes ago" times
|
||||||
window.setInterval(function() {
|
window.setInterval(function()
|
||||||
|
{
|
||||||
refreshRevisionList();
|
refreshRevisionList();
|
||||||
}, 60 * 1000);
|
}, 60 * 1000);
|
||||||
},
|
},
|
||||||
restoreRevision: function(rnum) {
|
restoreRevision: function(rnum)
|
||||||
|
{
|
||||||
var rev = currentRevisionList[rnum];
|
var rev = currentRevisionList[rnum];
|
||||||
var warning = ("Restoring this revision will overwrite the current"
|
var warning = ("Restoring this revision will overwrite the current" + " text of the pad. " + "Are you sure you want to continue?");
|
||||||
+ " text of the pad. "+
|
|
||||||
"Are you sure you want to continue?");
|
|
||||||
var hidePanel = paddocbar.hideLaterIfNoOtherInteraction();
|
var hidePanel = paddocbar.hideLaterIfNoOtherInteraction();
|
||||||
var box = getNthBox(rnum);
|
var box = getNthBox(rnum);
|
||||||
if (confirm(warning)) {
|
if (confirm(warning))
|
||||||
|
{
|
||||||
box.find(".srtwirly").show();
|
box.find(".srtwirly").show();
|
||||||
$.ajax({
|
$.ajax(
|
||||||
|
{
|
||||||
type: 'get',
|
type: 'get',
|
||||||
url: '/ep/pad/getrevisionatext',
|
url: '/ep/pad/getrevisionatext',
|
||||||
data: {padId: pad.getPadId(), revId: rev.id},
|
data: {
|
||||||
|
padId: pad.getPadId(),
|
||||||
|
revId: rev.id
|
||||||
|
},
|
||||||
success: success,
|
success: success,
|
||||||
error: error
|
error: error
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function success(resultJson) {
|
|
||||||
|
function success(resultJson)
|
||||||
|
{
|
||||||
untwirl();
|
untwirl();
|
||||||
var result = JSON.parse(resultJson);
|
var result = JSON.parse(resultJson);
|
||||||
padeditor.restoreRevisionText(result);
|
padeditor.restoreRevisionText(result);
|
||||||
window.setTimeout(function() {
|
window.setTimeout(function()
|
||||||
|
{
|
||||||
hidePanel();
|
hidePanel();
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
function error(e) {
|
|
||||||
|
function error(e)
|
||||||
|
{
|
||||||
untwirl();
|
untwirl();
|
||||||
alert("Oops! There was an error retreiving the text (revNum= "+
|
alert("Oops! There was an error retreiving the text (revNum= " + rev.revNum + "; padId=" + pad.getPadId());
|
||||||
rev.revNum+"; padId="+pad.getPadId());
|
|
||||||
}
|
}
|
||||||
function untwirl() {
|
|
||||||
|
function untwirl()
|
||||||
|
{
|
||||||
box.find(".srtwirly").hide();
|
box.find(".srtwirly").hide();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
showReachedLimit: function() {
|
showReachedLimit: function()
|
||||||
alert("Sorry, you do not have privileges to save more than "+
|
{
|
||||||
pad.getPrivilege('maxRevisions')+" revisions.");
|
alert("Sorry, you do not have privileges to save more than " + pad.getPrivilege('maxRevisions') + " revisions.");
|
||||||
},
|
},
|
||||||
newRevisionList: function(lst, noAnimation) {
|
newRevisionList: function(lst, noAnimation)
|
||||||
|
{
|
||||||
// server gives us list with newest first;
|
// server gives us list with newest first;
|
||||||
// we want chronological order
|
// we want chronological order
|
||||||
var L = reversedCopy(lst);
|
var L = reversedCopy(lst);
|
||||||
setRevisionList(L, noAnimation);
|
setRevisionList(L, noAnimation);
|
||||||
rerenderButton();
|
rerenderButton();
|
||||||
},
|
},
|
||||||
saveNow: function() {
|
saveNow: function()
|
||||||
if (isSaving) {
|
{
|
||||||
|
if (isSaving)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (! pad.isFullyConnected()) {
|
if (!pad.isFullyConnected())
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (haveReachedRevLimit()) {
|
if (haveReachedRevLimit())
|
||||||
|
{
|
||||||
self.showReachedLimit();
|
self.showReachedLimit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -350,8 +444,10 @@ var padsavedrevs = (function() {
|
||||||
var savedBy = pad.getUserName() || "unnamed";
|
var savedBy = pad.getUserName() || "unnamed";
|
||||||
pad.callWhenNotCommitting(submitSave);
|
pad.callWhenNotCommitting(submitSave);
|
||||||
|
|
||||||
function submitSave() {
|
function submitSave()
|
||||||
$.ajax({
|
{
|
||||||
|
$.ajax(
|
||||||
|
{
|
||||||
type: 'post',
|
type: 'post',
|
||||||
url: '/ep/pad/saverevision',
|
url: '/ep/pad/saverevision',
|
||||||
data: {
|
data: {
|
||||||
|
@ -364,41 +460,51 @@ var padsavedrevs = (function() {
|
||||||
error: error
|
error: error
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function success(text) {
|
|
||||||
|
function success(text)
|
||||||
|
{
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
var newRevisionList = JSON.parse(text);
|
var newRevisionList = JSON.parse(text);
|
||||||
self.newRevisionList(newRevisionList);
|
self.newRevisionList(newRevisionList);
|
||||||
pad.sendClientMessage({
|
pad.sendClientMessage(
|
||||||
|
{
|
||||||
type: 'newRevisionList',
|
type: 'newRevisionList',
|
||||||
revisionList: newRevisionList,
|
revisionList: newRevisionList,
|
||||||
savedBy: savedBy
|
savedBy: savedBy
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function error(e) {
|
|
||||||
|
function error(e)
|
||||||
|
{
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
alert("Oops! The server failed to save the revision. Please try again later.");
|
alert("Oops! The server failed to save the revision. Please try again later.");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleResizePage: function() {
|
handleResizePage: function()
|
||||||
|
{
|
||||||
updateScrollArrows();
|
updateScrollArrows();
|
||||||
},
|
},
|
||||||
handleIsFullyConnected: function(isConnected) {
|
handleIsFullyConnected: function(isConnected)
|
||||||
|
{
|
||||||
rerenderButton();
|
rerenderButton();
|
||||||
},
|
},
|
||||||
scroll: function(dir) {
|
scroll: function(dir)
|
||||||
|
{
|
||||||
var minScroll = getMinScroll();
|
var minScroll = getMinScroll();
|
||||||
var maxScroll = getMaxScroll();
|
var maxScroll = getMaxScroll();
|
||||||
if (dir == 'left') {
|
if (dir == 'left')
|
||||||
if (desiredScroll > minScroll) {
|
{
|
||||||
var n = Math.floor((desiredScroll - 1 - minScroll) /
|
if (desiredScroll > minScroll)
|
||||||
REVISION_BOX_WIDTH);
|
{
|
||||||
|
var n = Math.floor((desiredScroll - 1 - minScroll) / REVISION_BOX_WIDTH);
|
||||||
setDesiredScroll(Math.max(0, n) * REVISION_BOX_WIDTH + minScroll);
|
setDesiredScroll(Math.max(0, n) * REVISION_BOX_WIDTH + minScroll);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (dir == 'right') {
|
else if (dir == 'right')
|
||||||
if (desiredScroll < maxScroll) {
|
{
|
||||||
var n = Math.floor((maxScroll - desiredScroll - 1) /
|
if (desiredScroll < maxScroll)
|
||||||
REVISION_BOX_WIDTH);
|
{
|
||||||
|
var n = Math.floor((maxScroll - desiredScroll - 1) / REVISION_BOX_WIDTH);
|
||||||
setDesiredScroll(maxScroll - Math.max(0, n) * REVISION_BOX_WIDTH);
|
setDesiredScroll(maxScroll - Math.max(0, n) * REVISION_BOX_WIDTH);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,14 +21,17 @@ var colorPickerSetup = false;
|
||||||
var previousColorId = 0;
|
var previousColorId = 0;
|
||||||
|
|
||||||
|
|
||||||
var paduserlist = (function() {
|
var paduserlist = (function()
|
||||||
|
{
|
||||||
|
|
||||||
var rowManager = (function() {
|
var rowManager = (function()
|
||||||
|
{
|
||||||
// The row manager handles rendering rows of the user list and animating
|
// The row manager handles rendering rows of the user list and animating
|
||||||
// their insertion, removal, and reordering. It manipulates TD height
|
// their insertion, removal, and reordering. It manipulates TD height
|
||||||
// and TD opacity.
|
// and TD opacity.
|
||||||
|
|
||||||
function nextRowId() {
|
function nextRowId()
|
||||||
|
{
|
||||||
return "usertr" + (nextRowId.counter++);
|
return "usertr" + (nextRowId.counter++);
|
||||||
}
|
}
|
||||||
nextRowId.counter = 1;
|
nextRowId.counter = 1;
|
||||||
|
@ -36,10 +39,12 @@ var paduserlist = (function() {
|
||||||
var rowsFadingOut = []; // unordered set
|
var rowsFadingOut = []; // unordered set
|
||||||
var rowsFadingIn = []; // unordered set
|
var rowsFadingIn = []; // unordered set
|
||||||
var rowsPresent = []; // in order
|
var rowsPresent = []; // in order
|
||||||
|
|
||||||
var ANIMATION_START = -12; // just starting to fade in
|
var ANIMATION_START = -12; // just starting to fade in
|
||||||
var ANIMATION_END = 12; // just finishing fading out
|
var ANIMATION_END = 12; // just finishing fading out
|
||||||
function getAnimationHeight(step, power) {
|
|
||||||
|
|
||||||
|
function getAnimationHeight(step, power)
|
||||||
|
{
|
||||||
var a = Math.abs(step / 12);
|
var a = Math.abs(step / 12);
|
||||||
if (power == 2) a = a * a;
|
if (power == 2) a = a * a;
|
||||||
else if (power == 3) a = a * a * a;
|
else if (power == 3) a = a * a * a;
|
||||||
|
@ -51,87 +56,115 @@ var paduserlist = (function() {
|
||||||
|
|
||||||
var ANIMATION_STEP_TIME = 20;
|
var ANIMATION_STEP_TIME = 20;
|
||||||
var LOWER_FRAMERATE_FACTOR = 2;
|
var LOWER_FRAMERATE_FACTOR = 2;
|
||||||
var scheduleAnimation = padutils.makeAnimationScheduler(animateStep, ANIMATION_STEP_TIME,
|
var scheduleAnimation = padutils.makeAnimationScheduler(animateStep, ANIMATION_STEP_TIME, LOWER_FRAMERATE_FACTOR).scheduleAnimation;
|
||||||
LOWER_FRAMERATE_FACTOR).scheduleAnimation;
|
|
||||||
|
|
||||||
var NUMCOLS = 4;
|
var NUMCOLS = 4;
|
||||||
|
|
||||||
// we do lots of manipulation of table rows and stuff that JQuery makes ok, despite
|
// we do lots of manipulation of table rows and stuff that JQuery makes ok, despite
|
||||||
// IE's poor handling when manipulating the DOM directly.
|
// IE's poor handling when manipulating the DOM directly.
|
||||||
|
|
||||||
function getEmptyRowHtml(height) {
|
function getEmptyRowHtml(height)
|
||||||
|
{
|
||||||
return '<td colspan="' + NUMCOLS + '" style="border:0;height:' + height + 'px"><!-- --></td>';
|
return '<td colspan="' + NUMCOLS + '" style="border:0;height:' + height + 'px"><!-- --></td>';
|
||||||
}
|
}
|
||||||
function isNameEditable(data) {
|
|
||||||
|
function isNameEditable(data)
|
||||||
|
{
|
||||||
return (!data.name) && (data.status != 'Disconnected');
|
return (!data.name) && (data.status != 'Disconnected');
|
||||||
}
|
}
|
||||||
function replaceUserRowContents(tr, height, data) {
|
|
||||||
|
function replaceUserRowContents(tr, height, data)
|
||||||
|
{
|
||||||
var tds = getUserRowHtml(height, data).match(/<td.*?<\/td>/gi);
|
var tds = getUserRowHtml(height, data).match(/<td.*?<\/td>/gi);
|
||||||
if (isNameEditable(data) && tr.find("td.usertdname input:enabled").length > 0) {
|
if (isNameEditable(data) && tr.find("td.usertdname input:enabled").length > 0)
|
||||||
|
{
|
||||||
// preserve input field node
|
// preserve input field node
|
||||||
for(var i=0; i<tds.length; i++) {
|
for (var i = 0; i < tds.length; i++)
|
||||||
|
{
|
||||||
var oldTd = $(tr.find("td").get(i));
|
var oldTd = $(tr.find("td").get(i));
|
||||||
if (! oldTd.hasClass('usertdname')) {
|
if (!oldTd.hasClass('usertdname'))
|
||||||
|
{
|
||||||
oldTd.replaceWith(tds[i]);
|
oldTd.replaceWith(tds[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
tr.html(tds.join(''));
|
tr.html(tds.join(''));
|
||||||
}
|
}
|
||||||
return tr;
|
return tr;
|
||||||
}
|
}
|
||||||
function getUserRowHtml(height, data) {
|
|
||||||
|
function getUserRowHtml(height, data)
|
||||||
|
{
|
||||||
var nameHtml;
|
var nameHtml;
|
||||||
var isGuest = (data.id.charAt(0) != 'p');
|
var isGuest = (data.id.charAt(0) != 'p');
|
||||||
if (data.name) {
|
if (data.name)
|
||||||
|
{
|
||||||
nameHtml = padutils.escapeHtml(data.name);
|
nameHtml = padutils.escapeHtml(data.name);
|
||||||
if (isGuest && pad.getIsProPad()) {
|
if (isGuest && pad.getIsProPad())
|
||||||
|
{
|
||||||
nameHtml += ' (Guest)';
|
nameHtml += ' (Guest)';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
nameHtml = '<input type="text" class="editempty newinput" value="unnamed" '+
|
{
|
||||||
(isNameEditable(data) ? '' : 'disabled="disabled" ')+
|
nameHtml = '<input type="text" class="editempty newinput" value="unnamed" ' + (isNameEditable(data) ? '' : 'disabled="disabled" ') + '/>';
|
||||||
'/>';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ['<td style="height:',height,'px" class="usertdswatch"><div class="swatch" style="background:'+data.color+'"> </div></td>',
|
return ['<td style="height:', height, 'px" class="usertdswatch"><div class="swatch" style="background:' + data.color + '"> </div></td>', '<td style="height:', height, 'px" class="usertdname">', nameHtml, '</td>', '<td style="height:', height, 'px" class="usertdstatus">', padutils.escapeHtml(data.status), '</td>', '<td style="height:', height, 'px" class="activity">', padutils.escapeHtml(data.activity), '</td>'].join('');
|
||||||
'<td style="height:',height,'px" class="usertdname">',nameHtml,'</td>',
|
|
||||||
'<td style="height:',height,'px" class="usertdstatus">',padutils.escapeHtml(data.status),'</td>',
|
|
||||||
'<td style="height:',height,'px" class="activity">',padutils.escapeHtml(data.activity),'</td>'].join('');
|
|
||||||
}
|
}
|
||||||
function getRowHtml(id, innerHtml) {
|
|
||||||
|
function getRowHtml(id, innerHtml)
|
||||||
|
{
|
||||||
return '<tr id="' + id + '">' + innerHtml + '</tr>';
|
return '<tr id="' + id + '">' + innerHtml + '</tr>';
|
||||||
}
|
}
|
||||||
function rowNode(row) {
|
|
||||||
|
function rowNode(row)
|
||||||
|
{
|
||||||
return $("#" + row.domId);
|
return $("#" + row.domId);
|
||||||
}
|
}
|
||||||
function handleRowData(row) {
|
|
||||||
if (row.data && row.data.status == 'Disconnected') {
|
function handleRowData(row)
|
||||||
|
{
|
||||||
|
if (row.data && row.data.status == 'Disconnected')
|
||||||
|
{
|
||||||
row.opacity = 0.5;
|
row.opacity = 0.5;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
delete row.opacity;
|
delete row.opacity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function handleRowNode(tr, data) {
|
|
||||||
if (data.titleText) {
|
function handleRowNode(tr, data)
|
||||||
|
{
|
||||||
|
if (data.titleText)
|
||||||
|
{
|
||||||
var titleText = data.titleText;
|
var titleText = data.titleText;
|
||||||
window.setTimeout(function() { tr.attr('title', titleText )}, 0);
|
window.setTimeout(function()
|
||||||
|
{
|
||||||
|
tr.attr('title', titleText)
|
||||||
|
}, 0);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
tr.removeAttr('title');
|
tr.removeAttr('title');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function handleOtherUserInputs() {
|
|
||||||
|
function handleOtherUserInputs()
|
||||||
|
{
|
||||||
// handle 'INPUT' elements for naming other unnamed users
|
// handle 'INPUT' elements for naming other unnamed users
|
||||||
$("#otheruserstable input.newinput").each(function() {
|
$("#otheruserstable input.newinput").each(function()
|
||||||
|
{
|
||||||
var input = $(this);
|
var input = $(this);
|
||||||
var tr = input.closest("tr");
|
var tr = input.closest("tr");
|
||||||
if (tr.length > 0) {
|
if (tr.length > 0)
|
||||||
|
{
|
||||||
var index = tr.parent().children().index(tr);
|
var index = tr.parent().children().index(tr);
|
||||||
if (index >= 0) {
|
if (index >= 0)
|
||||||
|
{
|
||||||
var userId = rowsPresent[index].data.id;
|
var userId = rowsPresent[index].data.id;
|
||||||
rowManagerMakeNameEditor($(this), userId);
|
rowManagerMakeNameEditor($(this), userId);
|
||||||
}
|
}
|
||||||
|
@ -140,33 +173,45 @@ var paduserlist = (function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// animationPower is 0 to skip animation, 1 for linear, 2 for quadratic, etc.
|
// animationPower is 0 to skip animation, 1 for linear, 2 for quadratic, etc.
|
||||||
function insertRow(position, data, animationPower) {
|
|
||||||
|
|
||||||
|
function insertRow(position, data, animationPower)
|
||||||
|
{
|
||||||
position = Math.max(0, Math.min(rowsPresent.length, position));
|
position = Math.max(0, Math.min(rowsPresent.length, position));
|
||||||
animationPower = (animationPower === undefined ? 4 : animationPower);
|
animationPower = (animationPower === undefined ? 4 : animationPower);
|
||||||
|
|
||||||
var domId = nextRowId();
|
var domId = nextRowId();
|
||||||
var row = {data: data, animationStep: ANIMATION_START, domId: domId,
|
var row = {
|
||||||
animationPower: animationPower};
|
data: data,
|
||||||
|
animationStep: ANIMATION_START,
|
||||||
|
domId: domId,
|
||||||
|
animationPower: animationPower
|
||||||
|
};
|
||||||
handleRowData(row);
|
handleRowData(row);
|
||||||
rowsPresent.splice(position, 0, row);
|
rowsPresent.splice(position, 0, row);
|
||||||
var tr;
|
var tr;
|
||||||
if (animationPower == 0) {
|
if (animationPower == 0)
|
||||||
|
{
|
||||||
tr = $(getRowHtml(domId, getUserRowHtml(getAnimationHeight(0), data)));
|
tr = $(getRowHtml(domId, getUserRowHtml(getAnimationHeight(0), data)));
|
||||||
row.animationStep = 0;
|
row.animationStep = 0;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
rowsFadingIn.push(row);
|
rowsFadingIn.push(row);
|
||||||
tr = $(getRowHtml(domId, getEmptyRowHtml(getAnimationHeight(ANIMATION_START))));
|
tr = $(getRowHtml(domId, getEmptyRowHtml(getAnimationHeight(ANIMATION_START))));
|
||||||
}
|
}
|
||||||
handleRowNode(tr, data);
|
handleRowNode(tr, data);
|
||||||
if (position == 0) {
|
if (position == 0)
|
||||||
|
{
|
||||||
$("table#otheruserstable").prepend(tr);
|
$("table#otheruserstable").prepend(tr);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
rowNode(rowsPresent[position - 1]).after(tr);
|
rowNode(rowsPresent[position - 1]).after(tr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (animationPower != 0) {
|
if (animationPower != 0)
|
||||||
|
{
|
||||||
scheduleAnimation();
|
scheduleAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,31 +220,37 @@ var paduserlist = (function() {
|
||||||
return row;
|
return row;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateRow(position, data) {
|
function updateRow(position, data)
|
||||||
|
{
|
||||||
var row = rowsPresent[position];
|
var row = rowsPresent[position];
|
||||||
if (row) {
|
if (row)
|
||||||
|
{
|
||||||
row.data = data;
|
row.data = data;
|
||||||
handleRowData(row);
|
handleRowData(row);
|
||||||
if (row.animationStep == 0) {
|
if (row.animationStep == 0)
|
||||||
|
{
|
||||||
// not currently animating
|
// not currently animating
|
||||||
var tr = rowNode(row);
|
var tr = rowNode(row);
|
||||||
replaceUserRowContents(tr, getAnimationHeight(0), row.data).find(
|
replaceUserRowContents(tr, getAnimationHeight(0), row.data).find("td").css('opacity', (row.opacity === undefined ? 1 : row.opacity));
|
||||||
"td").css('opacity', (row.opacity === undefined ? 1 : row.opacity));
|
|
||||||
handleRowNode(tr, data);
|
handleRowNode(tr, data);
|
||||||
handleOtherUserInputs();
|
handleOtherUserInputs();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeRow(position, animationPower) {
|
function removeRow(position, animationPower)
|
||||||
|
{
|
||||||
animationPower = (animationPower === undefined ? 4 : animationPower);
|
animationPower = (animationPower === undefined ? 4 : animationPower);
|
||||||
var row = rowsPresent[position];
|
var row = rowsPresent[position];
|
||||||
if (row) {
|
if (row)
|
||||||
|
{
|
||||||
rowsPresent.splice(position, 1); // remove
|
rowsPresent.splice(position, 1); // remove
|
||||||
if (animationPower == 0) {
|
if (animationPower == 0)
|
||||||
|
{
|
||||||
rowNode(row).remove();
|
rowNode(row).remove();
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
row.animationStep = -row.animationStep; // use symmetry
|
row.animationStep = -row.animationStep; // use symmetry
|
||||||
row.animationPower = animationPower;
|
row.animationPower = animationPower;
|
||||||
rowsFadingOut.push(row);
|
rowsFadingOut.push(row);
|
||||||
|
@ -209,59 +260,72 @@ var paduserlist = (function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// newPosition is position after the row has been removed
|
// newPosition is position after the row has been removed
|
||||||
function moveRow(oldPosition, newPosition, animationPower) {
|
|
||||||
|
|
||||||
|
function moveRow(oldPosition, newPosition, animationPower)
|
||||||
|
{
|
||||||
animationPower = (animationPower === undefined ? 1 : animationPower); // linear is best
|
animationPower = (animationPower === undefined ? 1 : animationPower); // linear is best
|
||||||
var row = rowsPresent[oldPosition];
|
var row = rowsPresent[oldPosition];
|
||||||
if (row && oldPosition != newPosition) {
|
if (row && oldPosition != newPosition)
|
||||||
|
{
|
||||||
var rowData = row.data;
|
var rowData = row.data;
|
||||||
removeRow(oldPosition, animationPower);
|
removeRow(oldPosition, animationPower);
|
||||||
insertRow(newPosition, rowData, animationPower);
|
insertRow(newPosition, rowData, animationPower);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function animateStep() {
|
function animateStep()
|
||||||
|
{
|
||||||
// animation must be symmetrical
|
// animation must be symmetrical
|
||||||
for(var i=rowsFadingIn.length-1;i>=0;i--) { // backwards to allow removal
|
for (var i = rowsFadingIn.length - 1; i >= 0; i--)
|
||||||
|
{ // backwards to allow removal
|
||||||
var row = rowsFadingIn[i];
|
var row = rowsFadingIn[i];
|
||||||
var step = ++row.animationStep;
|
var step = ++row.animationStep;
|
||||||
var animHeight = getAnimationHeight(step, row.animationPower);
|
var animHeight = getAnimationHeight(step, row.animationPower);
|
||||||
var node = rowNode(row);
|
var node = rowNode(row);
|
||||||
var baseOpacity = (row.opacity === undefined ? 1 : row.opacity);
|
var baseOpacity = (row.opacity === undefined ? 1 : row.opacity);
|
||||||
if (step <= -OPACITY_STEPS) {
|
if (step <= -OPACITY_STEPS)
|
||||||
|
{
|
||||||
node.find("td").height(animHeight);
|
node.find("td").height(animHeight);
|
||||||
}
|
}
|
||||||
else if (step == -OPACITY_STEPS+1) {
|
else if (step == -OPACITY_STEPS + 1)
|
||||||
node.html(getUserRowHtml(animHeight, row.data)).find("td").css(
|
{
|
||||||
'opacity', baseOpacity*1/OPACITY_STEPS);
|
node.html(getUserRowHtml(animHeight, row.data)).find("td").css('opacity', baseOpacity * 1 / OPACITY_STEPS);
|
||||||
handleRowNode(node, row.data);
|
handleRowNode(node, row.data);
|
||||||
}
|
}
|
||||||
else if (step < 0) {
|
else if (step < 0)
|
||||||
|
{
|
||||||
node.find("td").css('opacity', baseOpacity * (OPACITY_STEPS - (-step)) / OPACITY_STEPS).height(animHeight);
|
node.find("td").css('opacity', baseOpacity * (OPACITY_STEPS - (-step)) / OPACITY_STEPS).height(animHeight);
|
||||||
}
|
}
|
||||||
else if (step == 0) {
|
else if (step == 0)
|
||||||
|
{
|
||||||
// set HTML in case modified during animation
|
// set HTML in case modified during animation
|
||||||
node.html(getUserRowHtml(animHeight, row.data)).find("td").css(
|
node.html(getUserRowHtml(animHeight, row.data)).find("td").css('opacity', baseOpacity * 1).height(animHeight);
|
||||||
'opacity', baseOpacity*1).height(animHeight);
|
|
||||||
handleRowNode(node, row.data);
|
handleRowNode(node, row.data);
|
||||||
rowsFadingIn.splice(i, 1); // remove from set
|
rowsFadingIn.splice(i, 1); // remove from set
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(var i=rowsFadingOut.length-1;i>=0;i--) { // backwards to allow removal
|
for (var i = rowsFadingOut.length - 1; i >= 0; i--)
|
||||||
|
{ // backwards to allow removal
|
||||||
var row = rowsFadingOut[i];
|
var row = rowsFadingOut[i];
|
||||||
var step = ++row.animationStep;
|
var step = ++row.animationStep;
|
||||||
var node = rowNode(row);
|
var node = rowNode(row);
|
||||||
var animHeight = getAnimationHeight(step, row.animationPower);
|
var animHeight = getAnimationHeight(step, row.animationPower);
|
||||||
var baseOpacity = (row.opacity === undefined ? 1 : row.opacity);
|
var baseOpacity = (row.opacity === undefined ? 1 : row.opacity);
|
||||||
if (step < OPACITY_STEPS) {
|
if (step < OPACITY_STEPS)
|
||||||
|
{
|
||||||
node.find("td").css('opacity', baseOpacity * (OPACITY_STEPS - step) / OPACITY_STEPS).height(animHeight);
|
node.find("td").css('opacity', baseOpacity * (OPACITY_STEPS - step) / OPACITY_STEPS).height(animHeight);
|
||||||
}
|
}
|
||||||
else if (step == OPACITY_STEPS) {
|
else if (step == OPACITY_STEPS)
|
||||||
|
{
|
||||||
node.html(getEmptyRowHtml(animHeight));
|
node.html(getEmptyRowHtml(animHeight));
|
||||||
}
|
}
|
||||||
else if (step <= ANIMATION_END) {
|
else if (step <= ANIMATION_END)
|
||||||
|
{
|
||||||
node.find("td").height(animHeight);
|
node.find("td").height(animHeight);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
rowsFadingOut.splice(i, 1); // remove from set
|
rowsFadingOut.splice(i, 1); // remove from set
|
||||||
node.remove();
|
node.remove();
|
||||||
}
|
}
|
||||||
|
@ -280,35 +344,44 @@ var paduserlist = (function() {
|
||||||
};
|
};
|
||||||
return self;
|
return self;
|
||||||
}()); ////////// rowManager
|
}()); ////////// rowManager
|
||||||
|
|
||||||
var otherUsersInfo = [];
|
var otherUsersInfo = [];
|
||||||
var otherUsersData = [];
|
var otherUsersData = [];
|
||||||
|
|
||||||
function rowManagerMakeNameEditor(jnode, userId) {
|
function rowManagerMakeNameEditor(jnode, userId)
|
||||||
setUpEditable(jnode, function() {
|
{
|
||||||
|
setUpEditable(jnode, function()
|
||||||
|
{
|
||||||
var existingIndex = findExistingIndex(userId);
|
var existingIndex = findExistingIndex(userId);
|
||||||
if (existingIndex >= 0) {
|
if (existingIndex >= 0)
|
||||||
|
{
|
||||||
return otherUsersInfo[existingIndex].name || '';
|
return otherUsersInfo[existingIndex].name || '';
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
}, function(newName) {
|
}, function(newName)
|
||||||
if (! newName) {
|
{
|
||||||
|
if (!newName)
|
||||||
|
{
|
||||||
jnode.addClass("editempty");
|
jnode.addClass("editempty");
|
||||||
jnode.val("unnamed");
|
jnode.val("unnamed");
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
jnode.attr('disabled', 'disabled');
|
jnode.attr('disabled', 'disabled');
|
||||||
pad.suggestUserName(userId, newName);
|
pad.suggestUserName(userId, newName);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function findExistingIndex(userId) {
|
function findExistingIndex(userId)
|
||||||
|
{
|
||||||
var existingIndex = -1;
|
var existingIndex = -1;
|
||||||
for(var i=0;i<otherUsersInfo.length;i++) {
|
for (var i = 0; i < otherUsersInfo.length; i++)
|
||||||
if (otherUsersInfo[i].userId == userId) {
|
{
|
||||||
|
if (otherUsersInfo[i].userId == userId)
|
||||||
|
{
|
||||||
existingIndex = i;
|
existingIndex = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -316,32 +389,41 @@ var paduserlist = (function() {
|
||||||
return existingIndex;
|
return existingIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setUpEditable(jqueryNode, valueGetter, valueSetter) {
|
function setUpEditable(jqueryNode, valueGetter, valueSetter)
|
||||||
jqueryNode.bind('focus', function(evt) {
|
{
|
||||||
|
jqueryNode.bind('focus', function(evt)
|
||||||
|
{
|
||||||
var oldValue = valueGetter();
|
var oldValue = valueGetter();
|
||||||
if (jqueryNode.val() !== oldValue) {
|
if (jqueryNode.val() !== oldValue)
|
||||||
|
{
|
||||||
jqueryNode.val(oldValue);
|
jqueryNode.val(oldValue);
|
||||||
}
|
}
|
||||||
jqueryNode.addClass("editactive").removeClass("editempty");
|
jqueryNode.addClass("editactive").removeClass("editempty");
|
||||||
});
|
});
|
||||||
jqueryNode.bind('blur', function(evt) {
|
jqueryNode.bind('blur', function(evt)
|
||||||
|
{
|
||||||
var newValue = jqueryNode.removeClass("editactive").val();
|
var newValue = jqueryNode.removeClass("editactive").val();
|
||||||
valueSetter(newValue);
|
valueSetter(newValue);
|
||||||
});
|
});
|
||||||
padutils.bindEnterAndEscape(jqueryNode, function onEnter() {
|
padutils.bindEnterAndEscape(jqueryNode, function onEnter()
|
||||||
|
{
|
||||||
jqueryNode.blur();
|
jqueryNode.blur();
|
||||||
}, function onEscape() {
|
}, function onEscape()
|
||||||
|
{
|
||||||
jqueryNode.val(valueGetter()).blur();
|
jqueryNode.val(valueGetter()).blur();
|
||||||
});
|
});
|
||||||
jqueryNode.removeAttr('disabled').addClass('editable');
|
jqueryNode.removeAttr('disabled').addClass('editable');
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateInviteNotice() {
|
function updateInviteNotice()
|
||||||
if (otherUsersInfo.length == 0) {
|
{
|
||||||
|
if (otherUsersInfo.length == 0)
|
||||||
|
{
|
||||||
$("#otheruserstable").hide();
|
$("#otheruserstable").hide();
|
||||||
$("#nootherusers").show();
|
$("#nootherusers").show();
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
$("#nootherusers").hide();
|
$("#nootherusers").hide();
|
||||||
$("#otheruserstable").show();
|
$("#otheruserstable").show();
|
||||||
}
|
}
|
||||||
|
@ -350,17 +432,22 @@ var paduserlist = (function() {
|
||||||
var knocksToIgnore = {};
|
var knocksToIgnore = {};
|
||||||
var guestPromptFlashState = 0;
|
var guestPromptFlashState = 0;
|
||||||
var guestPromptFlash = padutils.makeAnimationScheduler(
|
var guestPromptFlash = padutils.makeAnimationScheduler(
|
||||||
function () {
|
|
||||||
|
function()
|
||||||
|
{
|
||||||
var prompts = $("#guestprompts .guestprompt");
|
var prompts = $("#guestprompts .guestprompt");
|
||||||
if (prompts.length == 0) {
|
if (prompts.length == 0)
|
||||||
|
{
|
||||||
return false; // no more to do
|
return false; // no more to do
|
||||||
}
|
}
|
||||||
|
|
||||||
guestPromptFlashState = 1 - guestPromptFlashState;
|
guestPromptFlashState = 1 - guestPromptFlashState;
|
||||||
if (guestPromptFlashState) {
|
if (guestPromptFlashState)
|
||||||
|
{
|
||||||
prompts.css('background', '#ffa');
|
prompts.css('background', '#ffa');
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
prompts.css('background', '#ffe');
|
prompts.css('background', '#ffe');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,23 +455,26 @@ var paduserlist = (function() {
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
var self = {
|
var self = {
|
||||||
init: function(myInitialUserInfo) {
|
init: function(myInitialUserInfo)
|
||||||
|
{
|
||||||
self.setMyUserInfo(myInitialUserInfo);
|
self.setMyUserInfo(myInitialUserInfo);
|
||||||
|
|
||||||
$("#otheruserstable tr").remove();
|
$("#otheruserstable tr").remove();
|
||||||
|
|
||||||
if (pad.getUserIsGuest()) {
|
if (pad.getUserIsGuest())
|
||||||
|
{
|
||||||
$("#myusernameedit").addClass('myusernameedithoverable');
|
$("#myusernameedit").addClass('myusernameedithoverable');
|
||||||
setUpEditable($("#myusernameedit"),
|
setUpEditable($("#myusernameedit"), function()
|
||||||
function() {
|
{
|
||||||
return myUserInfo.name || '';
|
return myUserInfo.name || '';
|
||||||
},
|
}, function(newValue)
|
||||||
function(newValue) {
|
{
|
||||||
myUserInfo.name = newValue;
|
myUserInfo.name = newValue;
|
||||||
pad.notifyChangeName(newValue);
|
pad.notifyChangeName(newValue);
|
||||||
// wrap with setTimeout to do later because we get
|
// wrap with setTimeout to do later because we get
|
||||||
// a double "blur" fire in IE...
|
// a double "blur" fire in IE...
|
||||||
window.setTimeout(function() {
|
window.setTimeout(function()
|
||||||
|
{
|
||||||
self.renderMyUserInfo();
|
self.renderMyUserInfo();
|
||||||
}, 0);
|
}, 0);
|
||||||
});
|
});
|
||||||
|
@ -392,26 +482,32 @@ var paduserlist = (function() {
|
||||||
|
|
||||||
// color picker
|
// color picker
|
||||||
$("#myswatchbox").click(showColorPicker);
|
$("#myswatchbox").click(showColorPicker);
|
||||||
$("#mycolorpicker .pickerswatchouter").click(function() {
|
$("#mycolorpicker .pickerswatchouter").click(function()
|
||||||
|
{
|
||||||
$("#mycolorpicker .pickerswatchouter").removeClass('picked');
|
$("#mycolorpicker .pickerswatchouter").removeClass('picked');
|
||||||
$(this).addClass('picked');
|
$(this).addClass('picked');
|
||||||
});
|
});
|
||||||
$("#mycolorpickersave").click(function() {
|
$("#mycolorpickersave").click(function()
|
||||||
|
{
|
||||||
closeColorPicker(true);
|
closeColorPicker(true);
|
||||||
});
|
});
|
||||||
$("#mycolorpickercancel").click(function() {
|
$("#mycolorpickercancel").click(function()
|
||||||
|
{
|
||||||
closeColorPicker(false);
|
closeColorPicker(false);
|
||||||
});
|
});
|
||||||
//
|
//
|
||||||
|
|
||||||
},
|
},
|
||||||
setMyUserInfo: function(info) {
|
setMyUserInfo: function(info)
|
||||||
myUserInfo = $.extend({}, info);
|
{
|
||||||
|
myUserInfo = $.extend(
|
||||||
|
{}, info);
|
||||||
|
|
||||||
self.renderMyUserInfo();
|
self.renderMyUserInfo();
|
||||||
},
|
},
|
||||||
userJoinOrUpdate: function(info) {
|
userJoinOrUpdate: function(info)
|
||||||
if ((! info.userId) || (info.userId == myUserInfo.userId)) {
|
{
|
||||||
|
if ((!info.userId) || (info.userId == myUserInfo.userId))
|
||||||
|
{
|
||||||
// not sure how this would happen
|
// not sure how this would happen
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -428,11 +524,14 @@ var paduserlist = (function() {
|
||||||
var existingIndex = findExistingIndex(info.userId);
|
var existingIndex = findExistingIndex(info.userId);
|
||||||
|
|
||||||
var numUsersBesides = otherUsersInfo.length;
|
var numUsersBesides = otherUsersInfo.length;
|
||||||
if (existingIndex >= 0) {
|
if (existingIndex >= 0)
|
||||||
|
{
|
||||||
numUsersBesides--;
|
numUsersBesides--;
|
||||||
}
|
}
|
||||||
var newIndex = padutils.binarySearch(numUsersBesides, function(n) {
|
var newIndex = padutils.binarySearch(numUsersBesides, function(n)
|
||||||
if (existingIndex >= 0 && n >= existingIndex) {
|
{
|
||||||
|
if (existingIndex >= 0 && n >= existingIndex)
|
||||||
|
{
|
||||||
// pretend existingIndex isn't there
|
// pretend existingIndex isn't there
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
|
@ -441,18 +540,20 @@ var paduserlist = (function() {
|
||||||
var nameThis = (info.name || '').toLowerCase();
|
var nameThis = (info.name || '').toLowerCase();
|
||||||
var idN = infoN.userId;
|
var idN = infoN.userId;
|
||||||
var idThis = info.userId;
|
var idThis = info.userId;
|
||||||
return (nameN > nameThis) || (nameN == nameThis &&
|
return (nameN > nameThis) || (nameN == nameThis && idN > idThis);
|
||||||
idN > idThis);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (existingIndex >= 0) {
|
if (existingIndex >= 0)
|
||||||
|
{
|
||||||
// update
|
// update
|
||||||
if (existingIndex == newIndex) {
|
if (existingIndex == newIndex)
|
||||||
|
{
|
||||||
otherUsersInfo[existingIndex] = info;
|
otherUsersInfo[existingIndex] = info;
|
||||||
otherUsersData[existingIndex] = userData;
|
otherUsersData[existingIndex] = userData;
|
||||||
rowManager.updateRow(existingIndex, userData);
|
rowManager.updateRow(existingIndex, userData);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
otherUsersInfo.splice(existingIndex, 1);
|
otherUsersInfo.splice(existingIndex, 1);
|
||||||
otherUsersData.splice(existingIndex, 1);
|
otherUsersData.splice(existingIndex, 1);
|
||||||
otherUsersInfo.splice(newIndex, 0, info);
|
otherUsersInfo.splice(newIndex, 0, info);
|
||||||
|
@ -461,7 +562,8 @@ var paduserlist = (function() {
|
||||||
rowManager.moveRow(existingIndex, newIndex);
|
rowManager.moveRow(existingIndex, newIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
otherUsersInfo.splice(newIndex, 0, info);
|
otherUsersInfo.splice(newIndex, 0, info);
|
||||||
otherUsersData.splice(newIndex, 0, userData);
|
otherUsersData.splice(newIndex, 0, userData);
|
||||||
rowManager.insertRow(newIndex, userData);
|
rowManager.insertRow(newIndex, userData);
|
||||||
|
@ -471,9 +573,11 @@ var paduserlist = (function() {
|
||||||
|
|
||||||
self.updateNumberOfOnlineUsers();
|
self.updateNumberOfOnlineUsers();
|
||||||
},
|
},
|
||||||
updateNumberOfOnlineUsers: function(){
|
updateNumberOfOnlineUsers: function()
|
||||||
|
{
|
||||||
var online = 1; // you are always online!
|
var online = 1; // you are always online!
|
||||||
for(var i=0;i<otherUsersData.length;i++) {
|
for (var i = 0; i < otherUsersData.length; i++)
|
||||||
|
{
|
||||||
if (otherUsersData[i].status == "")
|
if (otherUsersData[i].status == "")
|
||||||
{
|
{
|
||||||
online++;
|
online++;
|
||||||
|
@ -483,25 +587,30 @@ var paduserlist = (function() {
|
||||||
|
|
||||||
return online;
|
return online;
|
||||||
},
|
},
|
||||||
userLeave: function(info) {
|
userLeave: function(info)
|
||||||
|
{
|
||||||
var existingIndex = findExistingIndex(info.userId);
|
var existingIndex = findExistingIndex(info.userId);
|
||||||
if (existingIndex >= 0) {
|
if (existingIndex >= 0)
|
||||||
|
{
|
||||||
var userData = otherUsersData[existingIndex];
|
var userData = otherUsersData[existingIndex];
|
||||||
userData.status = 'Disconnected';
|
userData.status = 'Disconnected';
|
||||||
rowManager.updateRow(existingIndex, userData);
|
rowManager.updateRow(existingIndex, userData);
|
||||||
if (userData.leaveTimer) {
|
if (userData.leaveTimer)
|
||||||
|
{
|
||||||
window.clearTimeout(userData.leaveTimer);
|
window.clearTimeout(userData.leaveTimer);
|
||||||
}
|
}
|
||||||
// set up a timer that will only fire if no leaves,
|
// set up a timer that will only fire if no leaves,
|
||||||
// joins, or updates happen for this user in the
|
// joins, or updates happen for this user in the
|
||||||
// next N seconds, to remove the user from the list.
|
// next N seconds, to remove the user from the list.
|
||||||
var thisUserId = info.userId;
|
var thisUserId = info.userId;
|
||||||
var thisLeaveTimer = window.setTimeout(function() {
|
var thisLeaveTimer = window.setTimeout(function()
|
||||||
|
{
|
||||||
var newExistingIndex = findExistingIndex(thisUserId);
|
var newExistingIndex = findExistingIndex(thisUserId);
|
||||||
if (newExistingIndex >= 0) {
|
if (newExistingIndex >= 0)
|
||||||
|
{
|
||||||
var newUserData = otherUsersData[newExistingIndex];
|
var newUserData = otherUsersData[newExistingIndex];
|
||||||
if (newUserData.status == 'Disconnected' &&
|
if (newUserData.status == 'Disconnected' && newUserData.leaveTimer == thisLeaveTimer)
|
||||||
newUserData.leaveTimer == thisLeaveTimer) {
|
{
|
||||||
otherUsersInfo.splice(newExistingIndex, 1);
|
otherUsersInfo.splice(newExistingIndex, 1);
|
||||||
otherUsersData.splice(newExistingIndex, 1);
|
otherUsersData.splice(newExistingIndex, 1);
|
||||||
rowManager.removeRow(newExistingIndex);
|
rowManager.removeRow(newExistingIndex);
|
||||||
|
@ -515,8 +624,10 @@ var paduserlist = (function() {
|
||||||
|
|
||||||
self.updateNumberOfOnlineUsers();
|
self.updateNumberOfOnlineUsers();
|
||||||
},
|
},
|
||||||
showGuestPrompt: function(userId, displayName) {
|
showGuestPrompt: function(userId, displayName)
|
||||||
if (knocksToIgnore[userId]) {
|
{
|
||||||
|
if (knocksToIgnore[userId])
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -526,35 +637,41 @@ var paduserlist = (function() {
|
||||||
padutils.cancelActions(actionName);
|
padutils.cancelActions(actionName);
|
||||||
|
|
||||||
var box = $("#guestprompt-" + encodedUserId);
|
var box = $("#guestprompt-" + encodedUserId);
|
||||||
if (box.length == 0) {
|
if (box.length == 0)
|
||||||
|
{
|
||||||
// make guest prompt box
|
// make guest prompt box
|
||||||
box = $('<div id="guestprompt-' + encodedUserId + '" class="guestprompt"><div class="choices"><a href="javascript:void(paduserlist.answerGuestPrompt(\'' + encodedUserId + '\',false))">Deny</a> <a href="javascript:void(paduserlist.answerGuestPrompt(\'' + encodedUserId + '\',true))">Approve</a></div><div class="guestname"><strong>Guest:</strong> ' + padutils.escapeHtml(displayName) + '</div></div>');
|
box = $('<div id="guestprompt-' + encodedUserId + '" class="guestprompt"><div class="choices"><a href="javascript:void(paduserlist.answerGuestPrompt(\'' + encodedUserId + '\',false))">Deny</a> <a href="javascript:void(paduserlist.answerGuestPrompt(\'' + encodedUserId + '\',true))">Approve</a></div><div class="guestname"><strong>Guest:</strong> ' + padutils.escapeHtml(displayName) + '</div></div>');
|
||||||
$("#guestprompts").append(box);
|
$("#guestprompts").append(box);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
// update display name
|
// update display name
|
||||||
box.find(".guestname").html('<strong>Guest:</strong> ' + padutils.escapeHtml(displayName));
|
box.find(".guestname").html('<strong>Guest:</strong> ' + padutils.escapeHtml(displayName));
|
||||||
}
|
}
|
||||||
var hideLater = padutils.getCancellableAction(actionName, function() {
|
var hideLater = padutils.getCancellableAction(actionName, function()
|
||||||
|
{
|
||||||
self.removeGuestPrompt(userId);
|
self.removeGuestPrompt(userId);
|
||||||
});
|
});
|
||||||
window.setTimeout(hideLater, 15000); // time-out with no knock
|
window.setTimeout(hideLater, 15000); // time-out with no knock
|
||||||
|
|
||||||
guestPromptFlash.scheduleAnimation();
|
guestPromptFlash.scheduleAnimation();
|
||||||
},
|
},
|
||||||
removeGuestPrompt: function(userId) {
|
removeGuestPrompt: function(userId)
|
||||||
|
{
|
||||||
var box = $("#guestprompt-" + padutils.encodeUserId(userId));
|
var box = $("#guestprompt-" + padutils.encodeUserId(userId));
|
||||||
// remove ID now so a new knock by same user gets new, unfaded box
|
// remove ID now so a new knock by same user gets new, unfaded box
|
||||||
box.removeAttr('id').fadeOut("fast", function() {
|
box.removeAttr('id').fadeOut("fast", function()
|
||||||
|
{
|
||||||
box.remove();
|
box.remove();
|
||||||
});
|
});
|
||||||
|
|
||||||
knocksToIgnore[userId] = true;
|
knocksToIgnore[userId] = true;
|
||||||
window.setTimeout(function() {
|
window.setTimeout(function()
|
||||||
|
{
|
||||||
delete knocksToIgnore[userId];
|
delete knocksToIgnore[userId];
|
||||||
}, 5000);
|
}, 5000);
|
||||||
},
|
},
|
||||||
answerGuestPrompt: function(encodedUserId, approve) {
|
answerGuestPrompt: function(encodedUserId, approve)
|
||||||
|
{
|
||||||
var guestId = padutils.decodeUserId(encodedUserId);
|
var guestId = padutils.decodeUserId(encodedUserId);
|
||||||
|
|
||||||
var msg = {
|
var msg = {
|
||||||
|
@ -567,22 +684,24 @@ var paduserlist = (function() {
|
||||||
|
|
||||||
self.removeGuestPrompt(guestId);
|
self.removeGuestPrompt(guestId);
|
||||||
},
|
},
|
||||||
renderMyUserInfo: function() {
|
renderMyUserInfo: function()
|
||||||
if (myUserInfo.name) {
|
{
|
||||||
|
if (myUserInfo.name)
|
||||||
|
{
|
||||||
$("#myusernameedit").removeClass("editempty").val(
|
$("#myusernameedit").removeClass("editempty").val(
|
||||||
myUserInfo.name);
|
myUserInfo.name);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
$("#myusernameedit").addClass("editempty").val(
|
{
|
||||||
"Enter your name");
|
$("#myusernameedit").addClass("editempty").val("Enter your name");
|
||||||
}
|
}
|
||||||
if (colorPickerOpen) {
|
if (colorPickerOpen)
|
||||||
$("#myswatchbox").addClass('myswatchboxunhoverable').removeClass(
|
{
|
||||||
'myswatchboxhoverable');
|
$("#myswatchbox").addClass('myswatchboxunhoverable').removeClass('myswatchboxhoverable');
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
$("#myswatchbox").addClass('myswatchboxhoverable').removeClass(
|
{
|
||||||
'myswatchboxunhoverable');
|
$("#myswatchbox").addClass('myswatchboxhoverable').removeClass('myswatchboxunhoverable');
|
||||||
}
|
}
|
||||||
$("#myswatch").css('background', pad.getColorPalette()[myUserInfo.colorId]);
|
$("#myswatch").css('background', pad.getColorPalette()[myUserInfo.colorId]);
|
||||||
}
|
}
|
||||||
|
@ -590,20 +709,27 @@ var paduserlist = (function() {
|
||||||
return self;
|
return self;
|
||||||
}());
|
}());
|
||||||
|
|
||||||
function getColorPickerSwatchIndex(jnode) {
|
function getColorPickerSwatchIndex(jnode)
|
||||||
|
{
|
||||||
// return Number(jnode.get(0).className.match(/\bn([0-9]+)\b/)[1])-1;
|
// return Number(jnode.get(0).className.match(/\bn([0-9]+)\b/)[1])-1;
|
||||||
return $("#colorpickerswatches li").index(jnode);
|
return $("#colorpickerswatches li").index(jnode);
|
||||||
}
|
}
|
||||||
function closeColorPicker(accept) {
|
|
||||||
if (accept) {
|
function closeColorPicker(accept)
|
||||||
|
{
|
||||||
|
if (accept)
|
||||||
|
{
|
||||||
var newColorId = getColorPickerSwatchIndex($("#colorpickerswatches .picked"));
|
var newColorId = getColorPickerSwatchIndex($("#colorpickerswatches .picked"));
|
||||||
if (newColorId >= 0) { // fails on NaN
|
if (newColorId >= 0)
|
||||||
|
{ // fails on NaN
|
||||||
myUserInfo.colorId = newColorId;
|
myUserInfo.colorId = newColorId;
|
||||||
pad.notifyChangeColor(newColorId);
|
pad.notifyChangeColor(newColorId);
|
||||||
}
|
}
|
||||||
|
|
||||||
paduserlist.renderMyUserInfo();
|
paduserlist.renderMyUserInfo();
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
pad.notifyChangeColor(previousColorId);
|
pad.notifyChangeColor(previousColorId);
|
||||||
paduserlist.renderMyUserInfo();
|
paduserlist.renderMyUserInfo();
|
||||||
}
|
}
|
||||||
|
@ -612,15 +738,19 @@ function closeColorPicker(accept) {
|
||||||
$("#mycolorpicker").fadeOut("fast");
|
$("#mycolorpicker").fadeOut("fast");
|
||||||
}
|
}
|
||||||
|
|
||||||
function showColorPicker() {
|
function showColorPicker()
|
||||||
|
{
|
||||||
previousColorId = myUserInfo.colorId;
|
previousColorId = myUserInfo.colorId;
|
||||||
|
|
||||||
if (! colorPickerOpen) {
|
if (!colorPickerOpen)
|
||||||
|
{
|
||||||
var palette = pad.getColorPalette();
|
var palette = pad.getColorPalette();
|
||||||
|
|
||||||
if(!colorPickerSetup) {
|
if (!colorPickerSetup)
|
||||||
|
{
|
||||||
var colorsList = $("#colorpickerswatches")
|
var colorsList = $("#colorpickerswatches")
|
||||||
for(var i=0;i<palette.length;i++) {
|
for (var i = 0; i < palette.length; i++)
|
||||||
|
{
|
||||||
|
|
||||||
var li = $('<li>', {
|
var li = $('<li>', {
|
||||||
style: 'background: ' + palette[i] + ';'
|
style: 'background: ' + palette[i] + ';'
|
||||||
|
@ -628,7 +758,8 @@ function showColorPicker() {
|
||||||
|
|
||||||
li.appendTo(colorsList);
|
li.appendTo(colorsList);
|
||||||
|
|
||||||
li.bind('click', function(event){
|
li.bind('click', function(event)
|
||||||
|
{
|
||||||
$("#colorpickerswatches li").removeClass('picked');
|
$("#colorpickerswatches li").removeClass('picked');
|
||||||
$(event.target).addClass("picked");
|
$(event.target).addClass("picked");
|
||||||
|
|
||||||
|
|
|
@ -15,62 +15,78 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var padutils = {
|
var padutils = {
|
||||||
escapeHtml: function(x) {
|
escapeHtml: function(x)
|
||||||
|
{
|
||||||
return String(x).replace(/\</g, '<').replace(/\>/g, '>');
|
return String(x).replace(/\</g, '<').replace(/\>/g, '>');
|
||||||
},
|
},
|
||||||
uniqueId: function() {
|
uniqueId: function()
|
||||||
function encodeNum(n, width) {
|
{
|
||||||
|
function encodeNum(n, width)
|
||||||
|
{
|
||||||
// returns string that is exactly 'width' chars, padding with zeros
|
// returns string that is exactly 'width' chars, padding with zeros
|
||||||
// and taking rightmost digits
|
// and taking rightmost digits
|
||||||
return (Array(width + 1).join('0') + Number(n).toString(35)).slice(-width);
|
return (Array(width + 1).join('0') + Number(n).toString(35)).slice(-width);
|
||||||
}
|
}
|
||||||
return [pad.getClientIp(),
|
return [pad.getClientIp(), encodeNum(+new Date, 7), encodeNum(Math.floor(Math.random() * 1e9), 4)].join('.');
|
||||||
encodeNum(+new Date, 7),
|
|
||||||
encodeNum(Math.floor(Math.random()*1e9), 4)].join('.');
|
|
||||||
},
|
},
|
||||||
uaDisplay: function(ua) {
|
uaDisplay: function(ua)
|
||||||
|
{
|
||||||
var m;
|
var m;
|
||||||
|
|
||||||
function clean(a) {
|
function clean(a)
|
||||||
|
{
|
||||||
var maxlen = 16;
|
var maxlen = 16;
|
||||||
a = a.replace(/[^a-zA-Z0-9\.]/g, '');
|
a = a.replace(/[^a-zA-Z0-9\.]/g, '');
|
||||||
if (a.length > maxlen) {
|
if (a.length > maxlen)
|
||||||
|
{
|
||||||
a = a.substr(0, maxlen);
|
a = a.substr(0, maxlen);
|
||||||
}
|
}
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkver(name) {
|
function checkver(name)
|
||||||
|
{
|
||||||
var m = ua.match(RegExp(name + '\\/([\\d\\.]+)'));
|
var m = ua.match(RegExp(name + '\\/([\\d\\.]+)'));
|
||||||
if (m && m.length > 1) {
|
if (m && m.length > 1)
|
||||||
|
{
|
||||||
return clean(name + m[1]);
|
return clean(name + m[1]);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// firefox
|
// firefox
|
||||||
if (checkver('Firefox')) { return checkver('Firefox'); }
|
if (checkver('Firefox'))
|
||||||
|
{
|
||||||
|
return checkver('Firefox');
|
||||||
|
}
|
||||||
|
|
||||||
// misc browsers, including IE
|
// misc browsers, including IE
|
||||||
m = ua.match(/compatible; ([^;]+);/);
|
m = ua.match(/compatible; ([^;]+);/);
|
||||||
if (m && m.length > 1) {
|
if (m && m.length > 1)
|
||||||
|
{
|
||||||
return clean(m[1]);
|
return clean(m[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// iphone
|
// iphone
|
||||||
if (ua.match(/\(iPhone;/)) {
|
if (ua.match(/\(iPhone;/))
|
||||||
|
{
|
||||||
return 'iPhone';
|
return 'iPhone';
|
||||||
}
|
}
|
||||||
|
|
||||||
// chrome
|
// chrome
|
||||||
if (checkver('Chrome')) { return checkver('Chrome'); }
|
if (checkver('Chrome'))
|
||||||
|
{
|
||||||
|
return checkver('Chrome');
|
||||||
|
}
|
||||||
|
|
||||||
// safari
|
// safari
|
||||||
m = ua.match(/Safari\/[\d\.]+/);
|
m = ua.match(/Safari\/[\d\.]+/);
|
||||||
if (m) {
|
if (m)
|
||||||
|
{
|
||||||
var v = '?';
|
var v = '?';
|
||||||
m = ua.match(/Version\/([\d\.]+)/);
|
m = ua.match(/Version\/([\d\.]+)/);
|
||||||
if (m && m.length > 1) {
|
if (m && m.length > 1)
|
||||||
|
{
|
||||||
v = m[1];
|
v = m[1];
|
||||||
}
|
}
|
||||||
return clean('Safari' + v);
|
return clean('Safari' + v);
|
||||||
|
@ -83,13 +99,15 @@ var padutils = {
|
||||||
// "func" is a function over 0..(numItems-1) that is monotonically
|
// "func" is a function over 0..(numItems-1) that is monotonically
|
||||||
// "increasing" with index (false, then true). Finds the boundary
|
// "increasing" with index (false, then true). Finds the boundary
|
||||||
// between false and true, a number between 0 and numItems inclusive.
|
// between false and true, a number between 0 and numItems inclusive.
|
||||||
binarySearch: function (numItems, func) {
|
binarySearch: function(numItems, func)
|
||||||
|
{
|
||||||
if (numItems < 1) return 0;
|
if (numItems < 1) return 0;
|
||||||
if (func(0)) return 0;
|
if (func(0)) return 0;
|
||||||
if (!func(numItems - 1)) return numItems;
|
if (!func(numItems - 1)) return numItems;
|
||||||
var low = 0; // func(low) is always false
|
var low = 0; // func(low) is always false
|
||||||
var high = numItems - 1; // func(high) is always true
|
var high = numItems - 1; // func(high) is always true
|
||||||
while ((high - low) > 1) {
|
while ((high - low) > 1)
|
||||||
|
{
|
||||||
var x = Math.floor((low + high) / 2); // x != low, x != high
|
var x = Math.floor((low + high) / 2); // x != low, x != high
|
||||||
if (func(x)) high = x;
|
if (func(x)) high = x;
|
||||||
else low = x;
|
else low = x;
|
||||||
|
@ -97,7 +115,8 @@ var padutils = {
|
||||||
return high;
|
return high;
|
||||||
},
|
},
|
||||||
// e.g. "Thu Jun 18 2009 13:09"
|
// e.g. "Thu Jun 18 2009 13:09"
|
||||||
simpleDateTime: function(date) {
|
simpleDateTime: function(date)
|
||||||
|
{
|
||||||
var d = new Date(+date); // accept either number or date
|
var d = new Date(+date); // accept either number or date
|
||||||
var dayOfWeek = (['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'])[d.getDay()];
|
var dayOfWeek = (['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'])[d.getDay()];
|
||||||
var month = (['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'])[d.getMonth()];
|
var month = (['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'])[d.getMonth()];
|
||||||
|
@ -106,18 +125,23 @@ var padutils = {
|
||||||
var hourmin = d.getHours() + ":" + ("0" + d.getMinutes()).slice(-2);
|
var hourmin = d.getHours() + ":" + ("0" + d.getMinutes()).slice(-2);
|
||||||
return dayOfWeek + ' ' + month + ' ' + dayOfMonth + ' ' + year + ' ' + hourmin;
|
return dayOfWeek + ' ' + month + ' ' + dayOfMonth + ' ' + year + ' ' + hourmin;
|
||||||
},
|
},
|
||||||
findURLs: function(text) {
|
findURLs: function(text)
|
||||||
|
{
|
||||||
// copied from ACE
|
// copied from ACE
|
||||||
var _REGEX_WORDCHAR = /[\u0030-\u0039\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u1FFF\u3040-\u9FFF\uF900-\uFDFF\uFE70-\uFEFE\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFDC]/;
|
var _REGEX_WORDCHAR = /[\u0030-\u0039\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u1FFF\u3040-\u9FFF\uF900-\uFDFF\uFE70-\uFEFE\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFDC]/;
|
||||||
var _REGEX_URLCHAR = new RegExp('(' + /[-:@a-zA-Z0-9_.,~%+\/?=&#;()$]/.source + '|' + _REGEX_WORDCHAR.source + ')');
|
var _REGEX_URLCHAR = new RegExp('(' + /[-:@a-zA-Z0-9_.,~%+\/?=&#;()$]/.source + '|' + _REGEX_WORDCHAR.source + ')');
|
||||||
var _REGEX_URL = new RegExp(/(?:(?:https?|s?ftp|ftps|file|smb|afp|nfs|(x-)?man|gopher|txmt):\/\/|mailto:)/.source + _REGEX_URLCHAR.source + '*(?![:.,;])' + _REGEX_URLCHAR.source, 'g');
|
var _REGEX_URL = new RegExp(/(?:(?:https?|s?ftp|ftps|file|smb|afp|nfs|(x-)?man|gopher|txmt):\/\/|mailto:)/.source + _REGEX_URLCHAR.source + '*(?![:.,;])' + _REGEX_URLCHAR.source, 'g');
|
||||||
|
|
||||||
// returns null if no URLs, or [[startIndex1, url1], [startIndex2, url2], ...]
|
// returns null if no URLs, or [[startIndex1, url1], [startIndex2, url2], ...]
|
||||||
function _findURLs(text) {
|
|
||||||
|
|
||||||
|
function _findURLs(text)
|
||||||
|
{
|
||||||
_REGEX_URL.lastIndex = 0;
|
_REGEX_URL.lastIndex = 0;
|
||||||
var urls = null;
|
var urls = null;
|
||||||
var execResult;
|
var execResult;
|
||||||
while ((execResult = _REGEX_URL.exec(text))) {
|
while ((execResult = _REGEX_URL.exec(text)))
|
||||||
|
{
|
||||||
urls = (urls || []);
|
urls = (urls || []);
|
||||||
var startIndex = execResult.index;
|
var startIndex = execResult.index;
|
||||||
var url = execResult[0];
|
var url = execResult[0];
|
||||||
|
@ -129,23 +153,28 @@ var padutils = {
|
||||||
|
|
||||||
return _findURLs(text);
|
return _findURLs(text);
|
||||||
},
|
},
|
||||||
escapeHtmlWithClickableLinks: function(text, target) {
|
escapeHtmlWithClickableLinks: function(text, target)
|
||||||
|
{
|
||||||
var idx = 0;
|
var idx = 0;
|
||||||
var pieces = [];
|
var pieces = [];
|
||||||
var urls = padutils.findURLs(text);
|
var urls = padutils.findURLs(text);
|
||||||
function advanceTo(i) {
|
|
||||||
if (i > idx) {
|
function advanceTo(i)
|
||||||
|
{
|
||||||
|
if (i > idx)
|
||||||
|
{
|
||||||
pieces.push(padutils.escapeHtml(text.substring(idx, i)));
|
pieces.push(padutils.escapeHtml(text.substring(idx, i)));
|
||||||
idx = i;
|
idx = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (urls) {
|
if (urls)
|
||||||
for(var j=0;j<urls.length;j++) {
|
{
|
||||||
|
for (var j = 0; j < urls.length; j++)
|
||||||
|
{
|
||||||
var startIndex = urls[j][0];
|
var startIndex = urls[j][0];
|
||||||
var href = urls[j][1];
|
var href = urls[j][1];
|
||||||
advanceTo(startIndex);
|
advanceTo(startIndex);
|
||||||
pieces.push('<a ', (target?'target="'+target+'" ':''),
|
pieces.push('<a ', (target ? 'target="' + target + '" ' : ''), 'href="', href.replace(/\"/g, '"'), '">');
|
||||||
'href="', href.replace(/\"/g, '"'), '">');
|
|
||||||
advanceTo(startIndex + href.length);
|
advanceTo(startIndex + href.length);
|
||||||
pieces.push('</a>');
|
pieces.push('</a>');
|
||||||
}
|
}
|
||||||
|
@ -153,209 +182,280 @@ var padutils = {
|
||||||
advanceTo(text.length);
|
advanceTo(text.length);
|
||||||
return pieces.join('');
|
return pieces.join('');
|
||||||
},
|
},
|
||||||
bindEnterAndEscape: function(node, onEnter, onEscape) {
|
bindEnterAndEscape: function(node, onEnter, onEscape)
|
||||||
|
{
|
||||||
|
|
||||||
// Use keypress instead of keyup in bindEnterAndEscape
|
// Use keypress instead of keyup in bindEnterAndEscape
|
||||||
// Keyup event is fired on enter in IME (Input Method Editor), But
|
// Keyup event is fired on enter in IME (Input Method Editor), But
|
||||||
// keypress is not. So, I changed to use keypress instead of keyup.
|
// keypress is not. So, I changed to use keypress instead of keyup.
|
||||||
// It is work on Windows (IE8, Chrome 6.0.472), CentOs (Firefox 3.0) and Mac OSX (Firefox 3.6.10, Chrome 6.0.472, Safari 5.0).
|
// It is work on Windows (IE8, Chrome 6.0.472), CentOs (Firefox 3.0) and Mac OSX (Firefox 3.6.10, Chrome 6.0.472, Safari 5.0).
|
||||||
|
if (onEnter)
|
||||||
if (onEnter) {
|
{
|
||||||
node.keypress( function(evt) {
|
node.keypress(function(evt)
|
||||||
if (evt.which == 13) {
|
{
|
||||||
|
if (evt.which == 13)
|
||||||
|
{
|
||||||
onEnter(evt);
|
onEnter(evt);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (onEscape) {
|
if (onEscape)
|
||||||
node.keydown( function(evt) {
|
{
|
||||||
if (evt.which == 27) {
|
node.keydown(function(evt)
|
||||||
|
{
|
||||||
|
if (evt.which == 27)
|
||||||
|
{
|
||||||
onEscape(evt);
|
onEscape(evt);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
timediff: function(d) {
|
timediff: function(d)
|
||||||
function format(n, word) {
|
{
|
||||||
|
function format(n, word)
|
||||||
|
{
|
||||||
n = Math.round(n);
|
n = Math.round(n);
|
||||||
return ('' + n + ' ' + word + (n != 1 ? 's' : '') + ' ago');
|
return ('' + n + ' ' + word + (n != 1 ? 's' : '') + ' ago');
|
||||||
}
|
}
|
||||||
d = Math.max(0, (+(new Date) - (+d) - pad.clientTimeOffset) / 1000);
|
d = Math.max(0, (+(new Date) - (+d) - pad.clientTimeOffset) / 1000);
|
||||||
if (d < 60) { return format(d, 'second'); }
|
if (d < 60)
|
||||||
|
{
|
||||||
|
return format(d, 'second');
|
||||||
|
}
|
||||||
d /= 60;
|
d /= 60;
|
||||||
if (d < 60) { return format(d, 'minute'); }
|
if (d < 60)
|
||||||
|
{
|
||||||
|
return format(d, 'minute');
|
||||||
|
}
|
||||||
d /= 60;
|
d /= 60;
|
||||||
if (d < 24) { return format(d, 'hour'); }
|
if (d < 24)
|
||||||
|
{
|
||||||
|
return format(d, 'hour');
|
||||||
|
}
|
||||||
d /= 24;
|
d /= 24;
|
||||||
return format(d, 'day');
|
return format(d, 'day');
|
||||||
},
|
},
|
||||||
makeAnimationScheduler: function(funcToAnimateOneStep, stepTime, stepsAtOnce) {
|
makeAnimationScheduler: function(funcToAnimateOneStep, stepTime, stepsAtOnce)
|
||||||
if (stepsAtOnce === undefined) {
|
{
|
||||||
|
if (stepsAtOnce === undefined)
|
||||||
|
{
|
||||||
stepsAtOnce = 1;
|
stepsAtOnce = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
var animationTimer = null;
|
var animationTimer = null;
|
||||||
|
|
||||||
function scheduleAnimation() {
|
function scheduleAnimation()
|
||||||
if (! animationTimer) {
|
{
|
||||||
animationTimer = window.setTimeout(function() {
|
if (!animationTimer)
|
||||||
|
{
|
||||||
|
animationTimer = window.setTimeout(function()
|
||||||
|
{
|
||||||
animationTimer = null;
|
animationTimer = null;
|
||||||
var n = stepsAtOnce;
|
var n = stepsAtOnce;
|
||||||
var moreToDo = true;
|
var moreToDo = true;
|
||||||
while (moreToDo && n > 0) {
|
while (moreToDo && n > 0)
|
||||||
|
{
|
||||||
moreToDo = funcToAnimateOneStep();
|
moreToDo = funcToAnimateOneStep();
|
||||||
n--;
|
n--;
|
||||||
}
|
}
|
||||||
if (moreToDo) {
|
if (moreToDo)
|
||||||
|
{
|
||||||
// more to do
|
// more to do
|
||||||
scheduleAnimation();
|
scheduleAnimation();
|
||||||
}
|
}
|
||||||
}, stepTime * stepsAtOnce);
|
}, stepTime * stepsAtOnce);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { scheduleAnimation: scheduleAnimation };
|
return {
|
||||||
|
scheduleAnimation: scheduleAnimation
|
||||||
|
};
|
||||||
},
|
},
|
||||||
makeShowHideAnimator: function(funcToArriveAtState, initiallyShown, fps, totalMs) {
|
makeShowHideAnimator: function(funcToArriveAtState, initiallyShown, fps, totalMs)
|
||||||
|
{
|
||||||
var animationState = (initiallyShown ? 0 : -2); // -2 hidden, -1 to 0 fade in, 0 to 1 fade out
|
var animationState = (initiallyShown ? 0 : -2); // -2 hidden, -1 to 0 fade in, 0 to 1 fade out
|
||||||
var animationFrameDelay = 1000 / fps;
|
var animationFrameDelay = 1000 / fps;
|
||||||
var animationStep = animationFrameDelay / totalMs;
|
var animationStep = animationFrameDelay / totalMs;
|
||||||
|
|
||||||
var scheduleAnimation =
|
var scheduleAnimation = padutils.makeAnimationScheduler(animateOneStep, animationFrameDelay).scheduleAnimation;
|
||||||
padutils.makeAnimationScheduler(animateOneStep, animationFrameDelay).scheduleAnimation;
|
|
||||||
|
|
||||||
function doShow() {
|
function doShow()
|
||||||
|
{
|
||||||
animationState = -1;
|
animationState = -1;
|
||||||
funcToArriveAtState(animationState);
|
funcToArriveAtState(animationState);
|
||||||
scheduleAnimation();
|
scheduleAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
function doQuickShow() { // start showing without losing any fade-in progress
|
function doQuickShow()
|
||||||
if (animationState < -1) {
|
{ // start showing without losing any fade-in progress
|
||||||
|
if (animationState < -1)
|
||||||
|
{
|
||||||
animationState = -1;
|
animationState = -1;
|
||||||
}
|
}
|
||||||
else if (animationState <= 0) {
|
else if (animationState <= 0)
|
||||||
|
{
|
||||||
animationState = animationState;
|
animationState = animationState;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
animationState = Math.max(-1, Math.min(0, -animationState));
|
animationState = Math.max(-1, Math.min(0, -animationState));
|
||||||
}
|
}
|
||||||
funcToArriveAtState(animationState);
|
funcToArriveAtState(animationState);
|
||||||
scheduleAnimation();
|
scheduleAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
function doHide() {
|
function doHide()
|
||||||
if (animationState >= -1 && animationState <= 0) {
|
{
|
||||||
|
if (animationState >= -1 && animationState <= 0)
|
||||||
|
{
|
||||||
animationState = 1e-6;
|
animationState = 1e-6;
|
||||||
scheduleAnimation();
|
scheduleAnimation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function animateOneStep() {
|
function animateOneStep()
|
||||||
if (animationState < -1 || animationState == 0) {
|
{
|
||||||
|
if (animationState < -1 || animationState == 0)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (animationState < 0) {
|
else if (animationState < 0)
|
||||||
|
{
|
||||||
// animate show
|
// animate show
|
||||||
animationState += animationStep;
|
animationState += animationStep;
|
||||||
if (animationState >= 0) {
|
if (animationState >= 0)
|
||||||
|
{
|
||||||
animationState = 0;
|
animationState = 0;
|
||||||
funcToArriveAtState(animationState);
|
funcToArriveAtState(animationState);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
funcToArriveAtState(animationState);
|
funcToArriveAtState(animationState);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (animationState > 0) {
|
else if (animationState > 0)
|
||||||
|
{
|
||||||
// animate hide
|
// animate hide
|
||||||
animationState += animationStep;
|
animationState += animationStep;
|
||||||
if (animationState >= 1) {
|
if (animationState >= 1)
|
||||||
|
{
|
||||||
animationState = 1;
|
animationState = 1;
|
||||||
funcToArriveAtState(animationState);
|
funcToArriveAtState(animationState);
|
||||||
animationState = -2;
|
animationState = -2;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
funcToArriveAtState(animationState);
|
funcToArriveAtState(animationState);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {show: doShow, hide: doHide, quickShow: doQuickShow};
|
return {
|
||||||
|
show: doShow,
|
||||||
|
hide: doHide,
|
||||||
|
quickShow: doQuickShow
|
||||||
|
};
|
||||||
},
|
},
|
||||||
_nextActionId: 1,
|
_nextActionId: 1,
|
||||||
uncanceledActions: {},
|
uncanceledActions: {},
|
||||||
getCancellableAction: function(actionType, actionFunc) {
|
getCancellableAction: function(actionType, actionFunc)
|
||||||
|
{
|
||||||
var o = padutils.uncanceledActions[actionType];
|
var o = padutils.uncanceledActions[actionType];
|
||||||
if (! o) {
|
if (!o)
|
||||||
|
{
|
||||||
o = {};
|
o = {};
|
||||||
padutils.uncanceledActions[actionType] = o;
|
padutils.uncanceledActions[actionType] = o;
|
||||||
}
|
}
|
||||||
var actionId = (padutils._nextActionId++);
|
var actionId = (padutils._nextActionId++);
|
||||||
o[actionId] = true;
|
o[actionId] = true;
|
||||||
return function() {
|
return function()
|
||||||
|
{
|
||||||
var p = padutils.uncanceledActions[actionType];
|
var p = padutils.uncanceledActions[actionType];
|
||||||
if (p && p[actionId]) {
|
if (p && p[actionId])
|
||||||
|
{
|
||||||
actionFunc();
|
actionFunc();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
cancelActions: function(actionType) {
|
cancelActions: function(actionType)
|
||||||
|
{
|
||||||
var o = padutils.uncanceledActions[actionType];
|
var o = padutils.uncanceledActions[actionType];
|
||||||
if (o) {
|
if (o)
|
||||||
|
{
|
||||||
// clear it
|
// clear it
|
||||||
delete padutils.uncanceledActions[actionType];
|
delete padutils.uncanceledActions[actionType];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
makeFieldLabeledWhenEmpty: function(field, labelText) {
|
makeFieldLabeledWhenEmpty: function(field, labelText)
|
||||||
|
{
|
||||||
field = $(field);
|
field = $(field);
|
||||||
function clear() {
|
|
||||||
|
function clear()
|
||||||
|
{
|
||||||
field.addClass('editempty');
|
field.addClass('editempty');
|
||||||
field.val(labelText);
|
field.val(labelText);
|
||||||
}
|
}
|
||||||
field.focus(function() {
|
field.focus(function()
|
||||||
if (field.hasClass('editempty')) {
|
{
|
||||||
|
if (field.hasClass('editempty'))
|
||||||
|
{
|
||||||
field.val('');
|
field.val('');
|
||||||
}
|
}
|
||||||
field.removeClass('editempty');
|
field.removeClass('editempty');
|
||||||
});
|
});
|
||||||
field.blur(function() {
|
field.blur(function()
|
||||||
if (! field.val()) {
|
{
|
||||||
|
if (!field.val())
|
||||||
|
{
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return {clear:clear};
|
return {
|
||||||
|
clear: clear
|
||||||
|
};
|
||||||
},
|
},
|
||||||
getCheckbox: function(node) {
|
getCheckbox: function(node)
|
||||||
|
{
|
||||||
return $(node).is(':checked');
|
return $(node).is(':checked');
|
||||||
},
|
},
|
||||||
setCheckbox: function(node, value) {
|
setCheckbox: function(node, value)
|
||||||
if (value) {
|
{
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
$(node).attr('checked', 'checked');
|
$(node).attr('checked', 'checked');
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
$(node).removeAttr('checked');
|
$(node).removeAttr('checked');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
bindCheckboxChange: function(node, func) {
|
bindCheckboxChange: function(node, func)
|
||||||
|
{
|
||||||
$(node).bind("click change", func);
|
$(node).bind("click change", func);
|
||||||
},
|
},
|
||||||
encodeUserId: function(userId) {
|
encodeUserId: function(userId)
|
||||||
return userId.replace(/[^a-y0-9]/g, function(c) {
|
{
|
||||||
|
return userId.replace(/[^a-y0-9]/g, function(c)
|
||||||
|
{
|
||||||
if (c == ".") return "-";
|
if (c == ".") return "-";
|
||||||
return 'z' + c.charCodeAt(0) + 'z';
|
return 'z' + c.charCodeAt(0) + 'z';
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
decodeUserId: function(encodedUserId) {
|
decodeUserId: function(encodedUserId)
|
||||||
return encodedUserId.replace(/[a-y0-9]+|-|z.+?z/g, function(cc) {
|
{
|
||||||
|
return encodedUserId.replace(/[a-y0-9]+|-|z.+?z/g, function(cc)
|
||||||
|
{
|
||||||
if (cc == '-') return '.';
|
if (cc == '-') return '.';
|
||||||
else if (cc.charAt(0) == 'z') {
|
else if (cc.charAt(0) == 'z')
|
||||||
|
{
|
||||||
return String.fromCharCode(Number(cc.slice(1, -1)));
|
return String.fromCharCode(Number(cc.slice(1, -1)));
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
return cc;
|
return cc;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,22 +1,26 @@
|
||||||
plugins = {
|
plugins = {
|
||||||
callHook: function (hookName, args) {
|
callHook: function(hookName, args)
|
||||||
|
{
|
||||||
var hook = clientVars.hooks[hookName];
|
var hook = clientVars.hooks[hookName];
|
||||||
if (hook === undefined)
|
if (hook === undefined) return [];
|
||||||
return [];
|
|
||||||
var res = [];
|
var res = [];
|
||||||
for (var i = 0, N=hook.length; i < N; i++) {
|
for (var i = 0, N = hook.length; i < N; i++)
|
||||||
|
{
|
||||||
var plugin = hook[i];
|
var plugin = hook[i];
|
||||||
var pluginRes = eval(plugin.plugin)[plugin.original || hookName](args);
|
var pluginRes = eval(plugin.plugin)[plugin.original || hookName](args);
|
||||||
if (pluginRes != undefined && pluginRes != null)
|
if (pluginRes != undefined && pluginRes != null) res = res.concat(pluginRes);
|
||||||
res = res.concat(pluginRes);
|
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
|
|
||||||
callHookStr: function (hookName, args, sep, pre, post) {
|
callHookStr: function(hookName, args, sep, pre, post)
|
||||||
|
{
|
||||||
if (sep == undefined) sep = '';
|
if (sep == undefined) sep = '';
|
||||||
if (pre == undefined) pre = '';
|
if (pre == undefined) pre = '';
|
||||||
if (post == undefined) post = '';
|
if (post == undefined) post = '';
|
||||||
return plugins.callHook(hookName, args).map(function (x) { return pre + x + post}).join(sep || "");
|
return plugins.callHook(hookName, args).map(function(x)
|
||||||
|
{
|
||||||
|
return pre + x + post
|
||||||
|
}).join(sep || "");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,16 +18,43 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function newSkipList() {
|
function newSkipList()
|
||||||
|
{
|
||||||
var PROFILER = window.PROFILER;
|
var PROFILER = window.PROFILER;
|
||||||
if (!PROFILER) {
|
if (!PROFILER)
|
||||||
PROFILER = function() { return {start:noop, mark:noop, literal:noop, end:noop, cancel:noop}; };
|
{
|
||||||
|
PROFILER = function()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
start: noop,
|
||||||
|
mark: noop,
|
||||||
|
literal: noop,
|
||||||
|
end: noop,
|
||||||
|
cancel: noop
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
function noop() {}
|
|
||||||
|
function noop()
|
||||||
|
{}
|
||||||
|
|
||||||
// if there are N elements in the skiplist, "start" is element -1 and "end" is element N
|
// if there are N elements in the skiplist, "start" is element -1 and "end" is element N
|
||||||
var start = {key:null, levels: 1, upPtrs:[null], downPtrs:[null], downSkips:[1], downSkipWidths:[0]};
|
var start = {
|
||||||
var end = {key:null, levels: 1, upPtrs:[null], downPtrs:[null], downSkips:[null], downSkipWidths:[null]};
|
key: null,
|
||||||
|
levels: 1,
|
||||||
|
upPtrs: [null],
|
||||||
|
downPtrs: [null],
|
||||||
|
downSkips: [1],
|
||||||
|
downSkipWidths: [0]
|
||||||
|
};
|
||||||
|
var end = {
|
||||||
|
key: null,
|
||||||
|
levels: 1,
|
||||||
|
upPtrs: [null],
|
||||||
|
downPtrs: [null],
|
||||||
|
downSkips: [null],
|
||||||
|
downSkipWidths: [null]
|
||||||
|
};
|
||||||
var numNodes = 0;
|
var numNodes = 0;
|
||||||
var totalWidth = 0;
|
var totalWidth = 0;
|
||||||
var keyToNodeMap = {};
|
var keyToNodeMap = {};
|
||||||
|
@ -38,20 +65,25 @@ function newSkipList() {
|
||||||
// After an insert or delete using point P, the point is still valid and points
|
// After an insert or delete using point P, the point is still valid and points
|
||||||
// to the same index in the skiplist. Other operations with other points invalidate
|
// to the same index in the skiplist. Other operations with other points invalidate
|
||||||
// this point.
|
// this point.
|
||||||
function _getPoint(targetLoc) {
|
|
||||||
|
|
||||||
|
function _getPoint(targetLoc)
|
||||||
|
{
|
||||||
var numLevels = start.levels;
|
var numLevels = start.levels;
|
||||||
var lvl = numLevels - 1;
|
var lvl = numLevels - 1;
|
||||||
var i = -1, ws = 0;
|
var i = -1,
|
||||||
|
ws = 0;
|
||||||
var nodes = new Array(numLevels);
|
var nodes = new Array(numLevels);
|
||||||
var idxs = new Array(numLevels);
|
var idxs = new Array(numLevels);
|
||||||
var widthSkips = new Array(numLevels);
|
var widthSkips = new Array(numLevels);
|
||||||
nodes[lvl] = start;
|
nodes[lvl] = start;
|
||||||
idxs[lvl] = -1;
|
idxs[lvl] = -1;
|
||||||
widthSkips[lvl] = 0;
|
widthSkips[lvl] = 0;
|
||||||
while (lvl >= 0) {
|
while (lvl >= 0)
|
||||||
|
{
|
||||||
var n = nodes[lvl];
|
var n = nodes[lvl];
|
||||||
while (n.downPtrs[lvl] &&
|
while (n.downPtrs[lvl] && (i + n.downSkips[lvl] < targetLoc))
|
||||||
(i + n.downSkips[lvl] < targetLoc)) {
|
{
|
||||||
i += n.downSkips[lvl];
|
i += n.downSkips[lvl];
|
||||||
ws += n.downSkipWidths[lvl];
|
ws += n.downSkipWidths[lvl];
|
||||||
n = n.downPtrs[lvl];
|
n = n.downPtrs[lvl];
|
||||||
|
@ -60,19 +92,32 @@ function newSkipList() {
|
||||||
idxs[lvl] = i;
|
idxs[lvl] = i;
|
||||||
widthSkips[lvl] = ws;
|
widthSkips[lvl] = ws;
|
||||||
lvl--;
|
lvl--;
|
||||||
if (lvl >= 0) {
|
if (lvl >= 0)
|
||||||
|
{
|
||||||
nodes[lvl] = n;
|
nodes[lvl] = n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {nodes:nodes, idxs:idxs, loc:targetLoc, widthSkips:widthSkips, toString: function() {
|
return {
|
||||||
return "getPoint("+targetLoc+")"; } };
|
nodes: nodes,
|
||||||
|
idxs: idxs,
|
||||||
|
loc: targetLoc,
|
||||||
|
widthSkips: widthSkips,
|
||||||
|
toString: function()
|
||||||
|
{
|
||||||
|
return "getPoint(" + targetLoc + ")";
|
||||||
}
|
}
|
||||||
function _getNodeAtOffset(targetOffset) {
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getNodeAtOffset(targetOffset)
|
||||||
|
{
|
||||||
var i = 0;
|
var i = 0;
|
||||||
var n = start;
|
var n = start;
|
||||||
var lvl = start.levels - 1;
|
var lvl = start.levels - 1;
|
||||||
while (lvl >= 0 && n.downPtrs[lvl]) {
|
while (lvl >= 0 && n.downPtrs[lvl])
|
||||||
while (n.downPtrs[lvl] && (i + n.downSkipWidths[lvl] <= targetOffset)) {
|
{
|
||||||
|
while (n.downPtrs[lvl] && (i + n.downSkipWidths[lvl] <= targetOffset))
|
||||||
|
{
|
||||||
i += n.downSkipWidths[lvl];
|
i += n.downSkipWidths[lvl];
|
||||||
n = n.downPtrs[lvl];
|
n = n.downPtrs[lvl];
|
||||||
}
|
}
|
||||||
|
@ -82,10 +127,23 @@ function newSkipList() {
|
||||||
else if (n === end) return (targetOffset == totalWidth ? (end.upPtrs[0] || null) : null);
|
else if (n === end) return (targetOffset == totalWidth ? (end.upPtrs[0] || null) : null);
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
function _entryWidth(e) { return (e && e.width) || 0; }
|
|
||||||
function _insertKeyAtPoint(point, newKey, entry) {
|
function _entryWidth(e)
|
||||||
|
{
|
||||||
|
return (e && e.width) || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _insertKeyAtPoint(point, newKey, entry)
|
||||||
|
{
|
||||||
var p = PROFILER("insertKey", false);
|
var p = PROFILER("insertKey", false);
|
||||||
var newNode = {key:newKey, levels: 0, upPtrs:[], downPtrs:[], downSkips:[], downSkipWidths:[]};
|
var newNode = {
|
||||||
|
key: newKey,
|
||||||
|
levels: 0,
|
||||||
|
upPtrs: [],
|
||||||
|
downPtrs: [],
|
||||||
|
downSkips: [],
|
||||||
|
downSkipWidths: []
|
||||||
|
};
|
||||||
p.mark("donealloc");
|
p.mark("donealloc");
|
||||||
var pNodes = point.nodes;
|
var pNodes = point.nodes;
|
||||||
var pIdxs = point.idxs;
|
var pIdxs = point.idxs;
|
||||||
|
@ -93,10 +151,12 @@ function newSkipList() {
|
||||||
var widthLoc = point.widthSkips[0] + point.nodes[0].downSkipWidths[0];
|
var widthLoc = point.widthSkips[0] + point.nodes[0].downSkipWidths[0];
|
||||||
var newWidth = _entryWidth(entry);
|
var newWidth = _entryWidth(entry);
|
||||||
p.mark("loop1");
|
p.mark("loop1");
|
||||||
while (newNode.levels == 0 || Math.random() < 0.01) {
|
while (newNode.levels == 0 || Math.random() < 0.01)
|
||||||
|
{
|
||||||
var lvl = newNode.levels;
|
var lvl = newNode.levels;
|
||||||
newNode.levels++;
|
newNode.levels++;
|
||||||
if (lvl == pNodes.length) {
|
if (lvl == pNodes.length)
|
||||||
|
{
|
||||||
// assume we have just passed the end of point.nodes, and reached one level greater
|
// assume we have just passed the end of point.nodes, and reached one level greater
|
||||||
// than the skiplist currently supports
|
// than the skiplist currently supports
|
||||||
pNodes[lvl] = start;
|
pNodes[lvl] = start;
|
||||||
|
@ -127,7 +187,8 @@ function newSkipList() {
|
||||||
}
|
}
|
||||||
p.mark("loop2");
|
p.mark("loop2");
|
||||||
p.literal(pNodes.length, "PNL");
|
p.literal(pNodes.length, "PNL");
|
||||||
for(var lvl=newNode.levels; lvl<pNodes.length; lvl++) {
|
for (var lvl = newNode.levels; lvl < pNodes.length; lvl++)
|
||||||
|
{
|
||||||
var up = pNodes[lvl];
|
var up = pNodes[lvl];
|
||||||
up.downSkips[lvl]++;
|
up.downSkips[lvl]++;
|
||||||
up.downSkipWidths[lvl] += newWidth;
|
up.downSkipWidths[lvl] += newWidth;
|
||||||
|
@ -138,24 +199,34 @@ function newSkipList() {
|
||||||
totalWidth += newWidth;
|
totalWidth += newWidth;
|
||||||
p.end();
|
p.end();
|
||||||
}
|
}
|
||||||
function _getNodeAtPoint(point) {
|
|
||||||
|
function _getNodeAtPoint(point)
|
||||||
|
{
|
||||||
return point.nodes[0].downPtrs[0];
|
return point.nodes[0].downPtrs[0];
|
||||||
}
|
}
|
||||||
function _incrementPoint(point) {
|
|
||||||
|
function _incrementPoint(point)
|
||||||
|
{
|
||||||
point.loc++;
|
point.loc++;
|
||||||
for(var i=0;i<point.nodes.length;i++) {
|
for (var i = 0; i < point.nodes.length; i++)
|
||||||
if (point.idxs[i] + point.nodes[i].downSkips[i] < point.loc) {
|
{
|
||||||
|
if (point.idxs[i] + point.nodes[i].downSkips[i] < point.loc)
|
||||||
|
{
|
||||||
point.idxs[i] += point.nodes[i].downSkips[i];
|
point.idxs[i] += point.nodes[i].downSkips[i];
|
||||||
point.widthSkips[i] += point.nodes[i].downSkipWidths[i];
|
point.widthSkips[i] += point.nodes[i].downSkipWidths[i];
|
||||||
point.nodes[i] = point.nodes[i].downPtrs[i];
|
point.nodes[i] = point.nodes[i].downPtrs[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function _deleteKeyAtPoint(point) {
|
|
||||||
|
function _deleteKeyAtPoint(point)
|
||||||
|
{
|
||||||
var elem = point.nodes[0].downPtrs[0];
|
var elem = point.nodes[0].downPtrs[0];
|
||||||
var elemWidth = _entryWidth(elem.entry);
|
var elemWidth = _entryWidth(elem.entry);
|
||||||
for(var i=0;i<point.nodes.length;i++) {
|
for (var i = 0; i < point.nodes.length; i++)
|
||||||
if (i < elem.levels) {
|
{
|
||||||
|
if (i < elem.levels)
|
||||||
|
{
|
||||||
var up = elem.upPtrs[i];
|
var up = elem.upPtrs[i];
|
||||||
var down = elem.downPtrs[i];
|
var down = elem.downPtrs[i];
|
||||||
var totalSkip = up.downSkips[i] + elem.downSkips[i] - 1;
|
var totalSkip = up.downSkips[i] + elem.downSkips[i] - 1;
|
||||||
|
@ -165,7 +236,8 @@ function newSkipList() {
|
||||||
var totalWidthSkip = up.downSkipWidths[i] + elem.downSkipWidths[i] - elemWidth;
|
var totalWidthSkip = up.downSkipWidths[i] + elem.downSkipWidths[i] - elemWidth;
|
||||||
up.downSkipWidths[i] = totalWidthSkip;
|
up.downSkipWidths[i] = totalWidthSkip;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
var up = point.nodes[i];
|
var up = point.nodes[i];
|
||||||
var down = up.downPtrs[i];
|
var down = up.downPtrs[i];
|
||||||
up.downSkips[i]--;
|
up.downSkips[i]--;
|
||||||
|
@ -176,25 +248,32 @@ function newSkipList() {
|
||||||
numNodes--;
|
numNodes--;
|
||||||
totalWidth -= elemWidth;
|
totalWidth -= elemWidth;
|
||||||
}
|
}
|
||||||
function _propagateWidthChange(node) {
|
|
||||||
|
function _propagateWidthChange(node)
|
||||||
|
{
|
||||||
var oldWidth = node.downSkipWidths[0];
|
var oldWidth = node.downSkipWidths[0];
|
||||||
var newWidth = _entryWidth(node.entry);
|
var newWidth = _entryWidth(node.entry);
|
||||||
var widthChange = newWidth - oldWidth;
|
var widthChange = newWidth - oldWidth;
|
||||||
var n = node;
|
var n = node;
|
||||||
var lvl = 0;
|
var lvl = 0;
|
||||||
while (lvl < n.levels) {
|
while (lvl < n.levels)
|
||||||
|
{
|
||||||
n.downSkipWidths[lvl] += widthChange;
|
n.downSkipWidths[lvl] += widthChange;
|
||||||
lvl++;
|
lvl++;
|
||||||
while (lvl >= n.levels && n.upPtrs[lvl-1]) {
|
while (lvl >= n.levels && n.upPtrs[lvl - 1])
|
||||||
|
{
|
||||||
n = n.upPtrs[lvl - 1];
|
n = n.upPtrs[lvl - 1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
totalWidth += widthChange;
|
totalWidth += widthChange;
|
||||||
}
|
}
|
||||||
function _getNodeIndex(node, byWidth) {
|
|
||||||
|
function _getNodeIndex(node, byWidth)
|
||||||
|
{
|
||||||
var dist = (byWidth ? 0 : -1);
|
var dist = (byWidth ? 0 : -1);
|
||||||
var n = node;
|
var n = node;
|
||||||
while (n !== start) {
|
while (n !== start)
|
||||||
|
{
|
||||||
var lvl = n.levels - 1;
|
var lvl = n.levels - 1;
|
||||||
n = n.upPtrs[lvl];
|
n = n.upPtrs[lvl];
|
||||||
if (byWidth) dist += n.downSkipWidths[lvl];
|
if (byWidth) dist += n.downSkipWidths[lvl];
|
||||||
|
@ -224,25 +303,33 @@ function newSkipList() {
|
||||||
return map(processedArray, function (x) { return x.toSource(); }).join("\n");
|
return map(processedArray, function (x) { return x.toSource(); }).join("\n");
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
function _getNodeByKey(key) {
|
function _getNodeByKey(key)
|
||||||
|
{
|
||||||
return keyToNodeMap['$KEY$' + key];
|
return keyToNodeMap['$KEY$' + key];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns index of first entry such that entryFunc(entry) is truthy,
|
// Returns index of first entry such that entryFunc(entry) is truthy,
|
||||||
// or length() if no such entry. Assumes all falsy entries come before
|
// or length() if no such entry. Assumes all falsy entries come before
|
||||||
// all truthy entries.
|
// all truthy entries.
|
||||||
function _search(entryFunc) {
|
|
||||||
|
|
||||||
|
function _search(entryFunc)
|
||||||
|
{
|
||||||
var low = start;
|
var low = start;
|
||||||
var lvl = start.levels - 1;
|
var lvl = start.levels - 1;
|
||||||
var lowIndex = -1;
|
var lowIndex = -1;
|
||||||
function f(node) {
|
|
||||||
|
function f(node)
|
||||||
|
{
|
||||||
if (node === start) return false;
|
if (node === start) return false;
|
||||||
else if (node === end) return true;
|
else if (node === end) return true;
|
||||||
else return entryFunc(node.entry);
|
else return entryFunc(node.entry);
|
||||||
}
|
}
|
||||||
while (lvl >= 0) {
|
while (lvl >= 0)
|
||||||
|
{
|
||||||
var nextLow = low.downPtrs[lvl];
|
var nextLow = low.downPtrs[lvl];
|
||||||
while (!f(nextLow)) {
|
while (!f(nextLow))
|
||||||
|
{
|
||||||
lowIndex += low.downSkips[lvl];
|
lowIndex += low.downSkips[lvl];
|
||||||
low = nextLow;
|
low = nextLow;
|
||||||
nextLow = low.downPtrs[lvl];
|
nextLow = low.downPtrs[lvl];
|
||||||
|
@ -257,16 +344,22 @@ The skip-list contains "entries", JavaScript objects that each must have a uniqu
|
||||||
that is a string.
|
that is a string.
|
||||||
*/
|
*/
|
||||||
var self = {
|
var self = {
|
||||||
length: function() { return numNodes; },
|
length: function()
|
||||||
atIndex: function(i) {
|
{
|
||||||
|
return numNodes;
|
||||||
|
},
|
||||||
|
atIndex: function(i)
|
||||||
|
{
|
||||||
if (i < 0) console.warn("atIndex(" + i + ")");
|
if (i < 0) console.warn("atIndex(" + i + ")");
|
||||||
if (i >= numNodes) console.warn("atIndex(" + i + ">=" + numNodes + ")");
|
if (i >= numNodes) console.warn("atIndex(" + i + ">=" + numNodes + ")");
|
||||||
return _getNodeAtPoint(_getPoint(i)).entry;
|
return _getNodeAtPoint(_getPoint(i)).entry;
|
||||||
},
|
},
|
||||||
// differs from Array.splice() in that new elements are in an array, not varargs
|
// differs from Array.splice() in that new elements are in an array, not varargs
|
||||||
splice: function(start, deleteCount, newEntryArray) {
|
splice: function(start, deleteCount, newEntryArray)
|
||||||
|
{
|
||||||
if (start < 0) console.warn("splice(" + start + ", ...)");
|
if (start < 0) console.warn("splice(" + start + ", ...)");
|
||||||
if (start + deleteCount > numNodes) {
|
if (start + deleteCount > numNodes)
|
||||||
|
{
|
||||||
console.warn("splice(" + start + ", " + deleteCount + ", ...), N=" + numNodes);
|
console.warn("splice(" + start + ", " + deleteCount + ", ...), N=" + numNodes);
|
||||||
console.warn("%s %s %s", typeof start, typeof deleteCount, typeof numNodes);
|
console.warn("%s %s %s", typeof start, typeof deleteCount, typeof numNodes);
|
||||||
console.trace();
|
console.trace();
|
||||||
|
@ -274,26 +367,32 @@ that is a string.
|
||||||
|
|
||||||
if (!newEntryArray) newEntryArray = [];
|
if (!newEntryArray) newEntryArray = [];
|
||||||
var pt = _getPoint(start);
|
var pt = _getPoint(start);
|
||||||
for(var i=0;i<deleteCount;i++) {
|
for (var i = 0; i < deleteCount; i++)
|
||||||
|
{
|
||||||
_deleteKeyAtPoint(pt);
|
_deleteKeyAtPoint(pt);
|
||||||
}
|
}
|
||||||
for(var i=(newEntryArray.length-1);i>=0;i--) {
|
for (var i = (newEntryArray.length - 1); i >= 0; i--)
|
||||||
|
{
|
||||||
var entry = newEntryArray[i];
|
var entry = newEntryArray[i];
|
||||||
_insertKeyAtPoint(pt, entry.key, entry);
|
_insertKeyAtPoint(pt, entry.key, entry);
|
||||||
var node = _getNodeByKey(entry.key);
|
var node = _getNodeByKey(entry.key);
|
||||||
node.entry = entry;
|
node.entry = entry;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
next: function (entry) {
|
next: function(entry)
|
||||||
|
{
|
||||||
return _getNodeByKey(entry.key).downPtrs[0].entry || null;
|
return _getNodeByKey(entry.key).downPtrs[0].entry || null;
|
||||||
},
|
},
|
||||||
prev: function (entry) {
|
prev: function(entry)
|
||||||
|
{
|
||||||
return _getNodeByKey(entry.key).upPtrs[0].entry || null;
|
return _getNodeByKey(entry.key).upPtrs[0].entry || null;
|
||||||
},
|
},
|
||||||
push: function(entry) {
|
push: function(entry)
|
||||||
|
{
|
||||||
self.splice(numNodes, 0, [entry]);
|
self.splice(numNodes, 0, [entry]);
|
||||||
},
|
},
|
||||||
slice: function(start, end) {
|
slice: function(start, end)
|
||||||
|
{
|
||||||
// act like Array.slice()
|
// act like Array.slice()
|
||||||
if (start === undefined) start = 0;
|
if (start === undefined) start = 0;
|
||||||
else if (start < 0) start += numNodes;
|
else if (start < 0) start += numNodes;
|
||||||
|
@ -309,39 +408,77 @@ that is a string.
|
||||||
if (end <= start) return [];
|
if (end <= start) return [];
|
||||||
var n = self.atIndex(start);
|
var n = self.atIndex(start);
|
||||||
var array = [n];
|
var array = [n];
|
||||||
for(var i=1;i<(end-start);i++) {
|
for (var i = 1; i < (end - start); i++)
|
||||||
|
{
|
||||||
n = self.next(n);
|
n = self.next(n);
|
||||||
array.push(n);
|
array.push(n);
|
||||||
}
|
}
|
||||||
return array;
|
return array;
|
||||||
},
|
},
|
||||||
atKey: function(key) { return _getNodeByKey(key).entry; },
|
atKey: function(key)
|
||||||
indexOfKey: function(key) { return _getNodeIndex(_getNodeByKey(key)); },
|
{
|
||||||
indexOfEntry: function (entry) { return self.indexOfKey(entry.key); },
|
return _getNodeByKey(key).entry;
|
||||||
containsKey: function(key) { return !!(_getNodeByKey(key)); },
|
},
|
||||||
|
indexOfKey: function(key)
|
||||||
|
{
|
||||||
|
return _getNodeIndex(_getNodeByKey(key));
|
||||||
|
},
|
||||||
|
indexOfEntry: function(entry)
|
||||||
|
{
|
||||||
|
return self.indexOfKey(entry.key);
|
||||||
|
},
|
||||||
|
containsKey: function(key)
|
||||||
|
{
|
||||||
|
return !!(_getNodeByKey(key));
|
||||||
|
},
|
||||||
// gets the last entry starting at or before the offset
|
// gets the last entry starting at or before the offset
|
||||||
atOffset: function(offset) { return _getNodeAtOffset(offset).entry; },
|
atOffset: function(offset)
|
||||||
keyAtOffset: function(offset) { return self.atOffset(offset).key; },
|
{
|
||||||
offsetOfKey: function(key) { return _getNodeIndex(_getNodeByKey(key), true); },
|
return _getNodeAtOffset(offset).entry;
|
||||||
offsetOfEntry: function(entry) { return self.offsetOfKey(entry.key); },
|
},
|
||||||
setEntryWidth: function(entry, width) { entry.width = width; _propagateWidthChange(_getNodeByKey(entry.key)); },
|
keyAtOffset: function(offset)
|
||||||
totalWidth: function() { return totalWidth; },
|
{
|
||||||
offsetOfIndex: function(i) {
|
return self.atOffset(offset).key;
|
||||||
|
},
|
||||||
|
offsetOfKey: function(key)
|
||||||
|
{
|
||||||
|
return _getNodeIndex(_getNodeByKey(key), true);
|
||||||
|
},
|
||||||
|
offsetOfEntry: function(entry)
|
||||||
|
{
|
||||||
|
return self.offsetOfKey(entry.key);
|
||||||
|
},
|
||||||
|
setEntryWidth: function(entry, width)
|
||||||
|
{
|
||||||
|
entry.width = width;
|
||||||
|
_propagateWidthChange(_getNodeByKey(entry.key));
|
||||||
|
},
|
||||||
|
totalWidth: function()
|
||||||
|
{
|
||||||
|
return totalWidth;
|
||||||
|
},
|
||||||
|
offsetOfIndex: function(i)
|
||||||
|
{
|
||||||
if (i < 0) return 0;
|
if (i < 0) return 0;
|
||||||
if (i >= numNodes) return totalWidth;
|
if (i >= numNodes) return totalWidth;
|
||||||
return self.offsetOfEntry(self.atIndex(i));
|
return self.offsetOfEntry(self.atIndex(i));
|
||||||
},
|
},
|
||||||
indexOfOffset: function(offset) {
|
indexOfOffset: function(offset)
|
||||||
|
{
|
||||||
if (offset <= 0) return 0;
|
if (offset <= 0) return 0;
|
||||||
if (offset >= totalWidth) return numNodes;
|
if (offset >= totalWidth) return numNodes;
|
||||||
return self.indexOfEntry(self.atOffset(offset));
|
return self.indexOfEntry(self.atOffset(offset));
|
||||||
},
|
},
|
||||||
search: function(entryFunc) {
|
search: function(entryFunc)
|
||||||
|
{
|
||||||
return _search(entryFunc);
|
return _search(entryFunc);
|
||||||
},
|
},
|
||||||
//debugToString: _debugToString,
|
//debugToString: _debugToString,
|
||||||
debugGetPoint: _getPoint,
|
debugGetPoint: _getPoint,
|
||||||
debugDepth: function() { return start.levels; }
|
debugDepth: function()
|
||||||
|
{
|
||||||
|
return start.levels;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,12 +14,15 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (window._orig_windowOpen) {
|
if (window._orig_windowOpen)
|
||||||
|
{
|
||||||
window.open = _orig_windowOpen;
|
window.open = _orig_windowOpen;
|
||||||
}
|
}
|
||||||
if (window._orig_windowSetTimeout) {
|
if (window._orig_windowSetTimeout)
|
||||||
|
{
|
||||||
window.setTimeout = _orig_windowSetTimeout;
|
window.setTimeout = _orig_windowSetTimeout;
|
||||||
}
|
}
|
||||||
if (window._orig_windowSetInterval) {
|
if (window._orig_windowSetInterval)
|
||||||
|
{
|
||||||
window.setInterval = _orig_windowSetInterval;
|
window.setInterval = _orig_windowSetInterval;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
undoModule = (function() {
|
undoModule = (function()
|
||||||
var stack = (function() {
|
{
|
||||||
|
var stack = (function()
|
||||||
|
{
|
||||||
var stackElements = [];
|
var stackElements = [];
|
||||||
// two types of stackElements:
|
// two types of stackElements:
|
||||||
// 1) { elementType: UNDOABLE_EVENT, eventType: "anything", [backset: <changeset>,]
|
// 1) { elementType: UNDOABLE_EVENT, eventType: "anything", [backset: <changeset>,]
|
||||||
|
@ -28,105 +30,137 @@ undoModule = (function() {
|
||||||
var UNDOABLE_EVENT = "undoableEvent";
|
var UNDOABLE_EVENT = "undoableEvent";
|
||||||
var EXTERNAL_CHANGE = "externalChange";
|
var EXTERNAL_CHANGE = "externalChange";
|
||||||
|
|
||||||
function clearStack() {
|
function clearStack()
|
||||||
|
{
|
||||||
stackElements.length = 0;
|
stackElements.length = 0;
|
||||||
stackElements.push({ elementType: UNDOABLE_EVENT, eventType: "bottom" });
|
stackElements.push(
|
||||||
|
{
|
||||||
|
elementType: UNDOABLE_EVENT,
|
||||||
|
eventType: "bottom"
|
||||||
|
});
|
||||||
numUndoableEvents = 1;
|
numUndoableEvents = 1;
|
||||||
}
|
}
|
||||||
clearStack();
|
clearStack();
|
||||||
|
|
||||||
function pushEvent(event) {
|
function pushEvent(event)
|
||||||
var e = extend({}, event);
|
{
|
||||||
|
var e = extend(
|
||||||
|
{}, event);
|
||||||
e.elementType = UNDOABLE_EVENT;
|
e.elementType = UNDOABLE_EVENT;
|
||||||
stackElements.push(e);
|
stackElements.push(e);
|
||||||
numUndoableEvents++;
|
numUndoableEvents++;
|
||||||
//dmesg("pushEvent backset: "+event.backset);
|
//dmesg("pushEvent backset: "+event.backset);
|
||||||
}
|
}
|
||||||
|
|
||||||
function pushExternalChange(cs) {
|
function pushExternalChange(cs)
|
||||||
|
{
|
||||||
var idx = stackElements.length - 1;
|
var idx = stackElements.length - 1;
|
||||||
if (stackElements[idx].elementType == EXTERNAL_CHANGE) {
|
if (stackElements[idx].elementType == EXTERNAL_CHANGE)
|
||||||
|
{
|
||||||
stackElements[idx].changeset = Changeset.compose(stackElements[idx].changeset, cs, getAPool());
|
stackElements[idx].changeset = Changeset.compose(stackElements[idx].changeset, cs, getAPool());
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
stackElements.push({elementType: EXTERNAL_CHANGE, changeset: cs});
|
{
|
||||||
|
stackElements.push(
|
||||||
|
{
|
||||||
|
elementType: EXTERNAL_CHANGE,
|
||||||
|
changeset: cs
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _exposeEvent(nthFromTop) {
|
function _exposeEvent(nthFromTop)
|
||||||
|
{
|
||||||
// precond: 0 <= nthFromTop < numUndoableEvents
|
// precond: 0 <= nthFromTop < numUndoableEvents
|
||||||
var targetIndex = stackElements.length - 1 - nthFromTop;
|
var targetIndex = stackElements.length - 1 - nthFromTop;
|
||||||
var idx = stackElements.length - 1;
|
var idx = stackElements.length - 1;
|
||||||
while (idx > targetIndex || stackElements[idx].elementType == EXTERNAL_CHANGE) {
|
while (idx > targetIndex || stackElements[idx].elementType == EXTERNAL_CHANGE)
|
||||||
if (stackElements[idx].elementType == EXTERNAL_CHANGE) {
|
{
|
||||||
|
if (stackElements[idx].elementType == EXTERNAL_CHANGE)
|
||||||
|
{
|
||||||
var ex = stackElements[idx];
|
var ex = stackElements[idx];
|
||||||
var un = stackElements[idx - 1];
|
var un = stackElements[idx - 1];
|
||||||
if (un.backset) {
|
if (un.backset)
|
||||||
|
{
|
||||||
var excs = ex.changeset;
|
var excs = ex.changeset;
|
||||||
var unbs = un.backset;
|
var unbs = un.backset;
|
||||||
un.backset = Changeset.follow(excs, un.backset, false, getAPool());
|
un.backset = Changeset.follow(excs, un.backset, false, getAPool());
|
||||||
ex.changeset = Changeset.follow(unbs, ex.changeset, true, getAPool());
|
ex.changeset = Changeset.follow(unbs, ex.changeset, true, getAPool());
|
||||||
if ((typeof un.selStart) == "number") {
|
if ((typeof un.selStart) == "number")
|
||||||
|
{
|
||||||
var newSel = Changeset.characterRangeFollow(excs, un.selStart, un.selEnd);
|
var newSel = Changeset.characterRangeFollow(excs, un.selStart, un.selEnd);
|
||||||
un.selStart = newSel[0];
|
un.selStart = newSel[0];
|
||||||
un.selEnd = newSel[1];
|
un.selEnd = newSel[1];
|
||||||
if (un.selStart == un.selEnd) {
|
if (un.selStart == un.selEnd)
|
||||||
|
{
|
||||||
un.selFocusAtStart = false;
|
un.selFocusAtStart = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stackElements[idx - 1] = ex;
|
stackElements[idx - 1] = ex;
|
||||||
stackElements[idx] = un;
|
stackElements[idx] = un;
|
||||||
if (idx >= 2 && stackElements[idx-2].elementType == EXTERNAL_CHANGE) {
|
if (idx >= 2 && stackElements[idx - 2].elementType == EXTERNAL_CHANGE)
|
||||||
ex.changeset = Changeset.compose(stackElements[idx-2].changeset,
|
{
|
||||||
ex.changeset, getAPool());
|
ex.changeset = Changeset.compose(stackElements[idx - 2].changeset, ex.changeset, getAPool());
|
||||||
stackElements.splice(idx - 2, 1);
|
stackElements.splice(idx - 2, 1);
|
||||||
idx--;
|
idx--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
idx--;
|
idx--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNthFromTop(n) {
|
function getNthFromTop(n)
|
||||||
|
{
|
||||||
// precond: 0 <= n < numEvents()
|
// precond: 0 <= n < numEvents()
|
||||||
_exposeEvent(n);
|
_exposeEvent(n);
|
||||||
return stackElements[stackElements.length - 1 - n];
|
return stackElements[stackElements.length - 1 - n];
|
||||||
}
|
}
|
||||||
|
|
||||||
function numEvents() {
|
function numEvents()
|
||||||
|
{
|
||||||
return numUndoableEvents;
|
return numUndoableEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
function popEvent() {
|
function popEvent()
|
||||||
|
{
|
||||||
// precond: numEvents() > 0
|
// precond: numEvents() > 0
|
||||||
_exposeEvent(0);
|
_exposeEvent(0);
|
||||||
numUndoableEvents--;
|
numUndoableEvents--;
|
||||||
return stackElements.pop();
|
return stackElements.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
return {numEvents:numEvents, popEvent:popEvent, pushEvent: pushEvent,
|
return {
|
||||||
pushExternalChange: pushExternalChange, clearStack: clearStack,
|
numEvents: numEvents,
|
||||||
getNthFromTop:getNthFromTop};
|
popEvent: popEvent,
|
||||||
|
pushEvent: pushEvent,
|
||||||
|
pushExternalChange: pushExternalChange,
|
||||||
|
clearStack: clearStack,
|
||||||
|
getNthFromTop: getNthFromTop
|
||||||
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// invariant: stack always has at least one undoable event
|
// invariant: stack always has at least one undoable event
|
||||||
|
|
||||||
var undoPtr = 0; // zero-index from top of stack, 0 == top
|
var undoPtr = 0; // zero-index from top of stack, 0 == top
|
||||||
|
|
||||||
function clearHistory() {
|
function clearHistory()
|
||||||
|
{
|
||||||
stack.clearStack();
|
stack.clearStack();
|
||||||
undoPtr = 0;
|
undoPtr = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _charOccurrences(str, c) {
|
function _charOccurrences(str, c)
|
||||||
|
{
|
||||||
var i = 0;
|
var i = 0;
|
||||||
var count = 0;
|
var count = 0;
|
||||||
while (i >= 0 && i < str.length) {
|
while (i >= 0 && i < str.length)
|
||||||
|
{
|
||||||
i = str.indexOf(c, i);
|
i = str.indexOf(c, i);
|
||||||
if (i >= 0) {
|
if (i >= 0)
|
||||||
|
{
|
||||||
count++;
|
count++;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
@ -134,11 +168,13 @@ undoModule = (function() {
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _opcodeOccurrences(cs, opcode) {
|
function _opcodeOccurrences(cs, opcode)
|
||||||
|
{
|
||||||
return _charOccurrences(Changeset.unpack(cs).ops, opcode);
|
return _charOccurrences(Changeset.unpack(cs).ops, opcode);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _mergeChangesets(cs1, cs2) {
|
function _mergeChangesets(cs1, cs2)
|
||||||
|
{
|
||||||
if (!cs1) return cs2;
|
if (!cs1) return cs2;
|
||||||
if (!cs2) return cs1;
|
if (!cs2) return cs1;
|
||||||
|
|
||||||
|
@ -152,51 +188,63 @@ undoModule = (function() {
|
||||||
var plusCount2 = _opcodeOccurrences(cs2, '+');
|
var plusCount2 = _opcodeOccurrences(cs2, '+');
|
||||||
var minusCount1 = _opcodeOccurrences(cs1, '-');
|
var minusCount1 = _opcodeOccurrences(cs1, '-');
|
||||||
var minusCount2 = _opcodeOccurrences(cs2, '-');
|
var minusCount2 = _opcodeOccurrences(cs2, '-');
|
||||||
if (plusCount1 == 1 && plusCount2 == 1 && minusCount1 == 0 && minusCount2 == 0) {
|
if (plusCount1 == 1 && plusCount2 == 1 && minusCount1 == 0 && minusCount2 == 0)
|
||||||
|
{
|
||||||
var merge = Changeset.compose(cs1, cs2, getAPool());
|
var merge = Changeset.compose(cs1, cs2, getAPool());
|
||||||
var plusCount3 = _opcodeOccurrences(merge, '+');
|
var plusCount3 = _opcodeOccurrences(merge, '+');
|
||||||
var minusCount3 = _opcodeOccurrences(merge, '-');
|
var minusCount3 = _opcodeOccurrences(merge, '-');
|
||||||
if (plusCount3 == 1 && minusCount3 == 0) {
|
if (plusCount3 == 1 && minusCount3 == 0)
|
||||||
|
{
|
||||||
return merge;
|
return merge;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (plusCount1 == 0 && plusCount2 == 0 && minusCount1 == 1 && minusCount2 == 1) {
|
else if (plusCount1 == 0 && plusCount2 == 0 && minusCount1 == 1 && minusCount2 == 1)
|
||||||
|
{
|
||||||
var merge = Changeset.compose(cs1, cs2, getAPool());
|
var merge = Changeset.compose(cs1, cs2, getAPool());
|
||||||
var plusCount3 = _opcodeOccurrences(merge, '+');
|
var plusCount3 = _opcodeOccurrences(merge, '+');
|
||||||
var minusCount3 = _opcodeOccurrences(merge, '-');
|
var minusCount3 = _opcodeOccurrences(merge, '-');
|
||||||
if (plusCount3 == 0 && minusCount3 == 1) {
|
if (plusCount3 == 0 && minusCount3 == 1)
|
||||||
|
{
|
||||||
return merge;
|
return merge;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function reportEvent(event) {
|
function reportEvent(event)
|
||||||
|
{
|
||||||
var topEvent = stack.getNthFromTop(0);
|
var topEvent = stack.getNthFromTop(0);
|
||||||
|
|
||||||
function applySelectionToTop() {
|
function applySelectionToTop()
|
||||||
if ((typeof event.selStart) == "number") {
|
{
|
||||||
|
if ((typeof event.selStart) == "number")
|
||||||
|
{
|
||||||
topEvent.selStart = event.selStart;
|
topEvent.selStart = event.selStart;
|
||||||
topEvent.selEnd = event.selEnd;
|
topEvent.selEnd = event.selEnd;
|
||||||
topEvent.selFocusAtStart = event.selFocusAtStart;
|
topEvent.selFocusAtStart = event.selFocusAtStart;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((! event.backset) || Changeset.isIdentity(event.backset)) {
|
if ((!event.backset) || Changeset.isIdentity(event.backset))
|
||||||
|
{
|
||||||
applySelectionToTop();
|
applySelectionToTop();
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
var merged = false;
|
var merged = false;
|
||||||
if (topEvent.eventType == event.eventType) {
|
if (topEvent.eventType == event.eventType)
|
||||||
|
{
|
||||||
var merge = _mergeChangesets(event.backset, topEvent.backset);
|
var merge = _mergeChangesets(event.backset, topEvent.backset);
|
||||||
if (merge) {
|
if (merge)
|
||||||
|
{
|
||||||
topEvent.backset = merge;
|
topEvent.backset = merge;
|
||||||
//dmesg("reportEvent merge: "+merge);
|
//dmesg("reportEvent merge: "+merge);
|
||||||
applySelectionToTop();
|
applySelectionToTop();
|
||||||
merged = true;
|
merged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (! merged) {
|
if (!merged)
|
||||||
|
{
|
||||||
stack.pushEvent(event);
|
stack.pushEvent(event);
|
||||||
}
|
}
|
||||||
undoPtr = 0;
|
undoPtr = 0;
|
||||||
|
@ -204,19 +252,27 @@ undoModule = (function() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function reportExternalChange(changeset) {
|
function reportExternalChange(changeset)
|
||||||
if (changeset && ! Changeset.isIdentity(changeset)) {
|
{
|
||||||
|
if (changeset && !Changeset.isIdentity(changeset))
|
||||||
|
{
|
||||||
stack.pushExternalChange(changeset);
|
stack.pushExternalChange(changeset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _getSelectionInfo(event) {
|
function _getSelectionInfo(event)
|
||||||
if ((typeof event.selStart) != "number") {
|
{
|
||||||
|
if ((typeof event.selStart) != "number")
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
return {selStart: event.selStart, selEnd: event.selEnd,
|
{
|
||||||
selFocusAtStart: event.selFocusAtStart};
|
return {
|
||||||
|
selStart: event.selStart,
|
||||||
|
selEnd: event.selEnd,
|
||||||
|
selFocusAtStart: event.selFocusAtStart
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,8 +282,10 @@ undoModule = (function() {
|
||||||
// or can be called with no arguments to mean that no undo is possible.
|
// or can be called with no arguments to mean that no undo is possible.
|
||||||
// "eventFunc" will be called exactly once.
|
// "eventFunc" will be called exactly once.
|
||||||
|
|
||||||
function performUndo(eventFunc) {
|
function performUndo(eventFunc)
|
||||||
if (undoPtr < stack.numEvents()-1) {
|
{
|
||||||
|
if (undoPtr < stack.numEvents() - 1)
|
||||||
|
{
|
||||||
var backsetEvent = stack.getNthFromTop(undoPtr);
|
var backsetEvent = stack.getNthFromTop(undoPtr);
|
||||||
var selectionEvent = stack.getNthFromTop(undoPtr + 1);
|
var selectionEvent = stack.getNthFromTop(undoPtr + 1);
|
||||||
var undoEvent = eventFunc(backsetEvent.backset, _getSelectionInfo(selectionEvent));
|
var undoEvent = eventFunc(backsetEvent.backset, _getSelectionInfo(selectionEvent));
|
||||||
|
@ -237,8 +295,10 @@ undoModule = (function() {
|
||||||
else eventFunc();
|
else eventFunc();
|
||||||
}
|
}
|
||||||
|
|
||||||
function performRedo(eventFunc) {
|
function performRedo(eventFunc)
|
||||||
if (undoPtr >= 2) {
|
{
|
||||||
|
if (undoPtr >= 2)
|
||||||
|
{
|
||||||
var backsetEvent = stack.getNthFromTop(0);
|
var backsetEvent = stack.getNthFromTop(0);
|
||||||
var selectionEvent = stack.getNthFromTop(1);
|
var selectionEvent = stack.getNthFromTop(1);
|
||||||
eventFunc(backsetEvent.backset, _getSelectionInfo(selectionEvent));
|
eventFunc(backsetEvent.backset, _getSelectionInfo(selectionEvent));
|
||||||
|
@ -248,11 +308,18 @@ undoModule = (function() {
|
||||||
else eventFunc();
|
else eventFunc();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAPool() {
|
function getAPool()
|
||||||
|
{
|
||||||
return undoModule.apool;
|
return undoModule.apool;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {clearHistory:clearHistory, reportEvent:reportEvent, reportExternalChange:reportExternalChange,
|
return {
|
||||||
performUndo:performUndo, performRedo:performRedo, enabled: true,
|
clearHistory: clearHistory,
|
||||||
apool: null}; // apool is filled in by caller
|
reportEvent: reportEvent,
|
||||||
|
reportExternalChange: reportExternalChange,
|
||||||
|
performUndo: performUndo,
|
||||||
|
performRedo: performRedo,
|
||||||
|
enabled: true,
|
||||||
|
apool: null
|
||||||
|
}; // apool is filled in by caller
|
||||||
})();
|
})();
|
|
@ -14,7 +14,8 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function makeVirtualLineView(lineNode) {
|
function makeVirtualLineView(lineNode)
|
||||||
|
{
|
||||||
|
|
||||||
// how much to jump forward or backward at once in a charSeeker before
|
// how much to jump forward or backward at once in a charSeeker before
|
||||||
// constructing a DOM node and checking the coordinates (which takes a
|
// constructing a DOM node and checking the coordinates (which takes a
|
||||||
|
@ -26,12 +27,15 @@ function makeVirtualLineView(lineNode) {
|
||||||
var maxCharIncrement = 20;
|
var maxCharIncrement = 20;
|
||||||
var seekerAtEnd = null;
|
var seekerAtEnd = null;
|
||||||
|
|
||||||
function getNumChars() {
|
function getNumChars()
|
||||||
|
{
|
||||||
return lineNode.textContent.length;
|
return lineNode.textContent.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNumVirtualLines() {
|
function getNumVirtualLines()
|
||||||
if (! seekerAtEnd) {
|
{
|
||||||
|
if (!seekerAtEnd)
|
||||||
|
{
|
||||||
var seeker = makeCharSeeker();
|
var seeker = makeCharSeeker();
|
||||||
seeker.forwardByWhile(maxCharIncrement);
|
seeker.forwardByWhile(maxCharIncrement);
|
||||||
seekerAtEnd = seeker;
|
seekerAtEnd = seeker;
|
||||||
|
@ -39,22 +43,34 @@ function makeVirtualLineView(lineNode) {
|
||||||
return seekerAtEnd.getVirtualLine() + 1;
|
return seekerAtEnd.getVirtualLine() + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVLineAndOffsetForChar(lineChar) {
|
function getVLineAndOffsetForChar(lineChar)
|
||||||
|
{
|
||||||
var seeker = makeCharSeeker();
|
var seeker = makeCharSeeker();
|
||||||
seeker.forwardByWhile(maxCharIncrement, null, lineChar);
|
seeker.forwardByWhile(maxCharIncrement, null, lineChar);
|
||||||
var theLine = seeker.getVirtualLine();
|
var theLine = seeker.getVirtualLine();
|
||||||
seeker.backwardByWhile(8, function() { return seeker.getVirtualLine() == theLine; });
|
seeker.backwardByWhile(8, function()
|
||||||
seeker.forwardByWhile(1, function() { return seeker.getVirtualLine() != theLine; });
|
{
|
||||||
|
return seeker.getVirtualLine() == theLine;
|
||||||
|
});
|
||||||
|
seeker.forwardByWhile(1, function()
|
||||||
|
{
|
||||||
|
return seeker.getVirtualLine() != theLine;
|
||||||
|
});
|
||||||
var lineStartChar = seeker.getOffset();
|
var lineStartChar = seeker.getOffset();
|
||||||
return {vline:theLine, offset:(lineChar - lineStartChar)};
|
return {
|
||||||
|
vline: theLine,
|
||||||
|
offset: (lineChar - lineStartChar)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCharForVLineAndOffset(vline, offset) {
|
function getCharForVLineAndOffset(vline, offset)
|
||||||
|
{
|
||||||
// returns revised vline and offset as well as absolute char index within line.
|
// returns revised vline and offset as well as absolute char index within line.
|
||||||
// if offset is beyond end of line, for example, will give new offset at end of line.
|
// if offset is beyond end of line, for example, will give new offset at end of line.
|
||||||
var seeker = makeCharSeeker();
|
var seeker = makeCharSeeker();
|
||||||
// go to start of line
|
// go to start of line
|
||||||
seeker.binarySearch(function() {
|
seeker.binarySearch(function()
|
||||||
|
{
|
||||||
return seeker.getVirtualLine() >= vline;
|
return seeker.getVirtualLine() >= vline;
|
||||||
});
|
});
|
||||||
var lineStart = seeker.getOffset();
|
var lineStart = seeker.getOffset();
|
||||||
|
@ -62,50 +78,75 @@ function makeVirtualLineView(lineNode) {
|
||||||
// go to offset, overshooting the virtual line only if offset is too large for it
|
// go to offset, overshooting the virtual line only if offset is too large for it
|
||||||
seeker.forwardByWhile(maxCharIncrement, null, lineStart + offset);
|
seeker.forwardByWhile(maxCharIncrement, null, lineStart + offset);
|
||||||
// get back into line
|
// get back into line
|
||||||
seeker.backwardByWhile(1, function() { return seeker.getVirtualLine() != theLine; }, lineStart);
|
seeker.backwardByWhile(1, function()
|
||||||
|
{
|
||||||
|
return seeker.getVirtualLine() != theLine;
|
||||||
|
}, lineStart);
|
||||||
var lineChar = seeker.getOffset();
|
var lineChar = seeker.getOffset();
|
||||||
var theOffset = lineChar - lineStart;
|
var theOffset = lineChar - lineStart;
|
||||||
// handle case of last virtual line; should be able to be at end of it
|
// handle case of last virtual line; should be able to be at end of it
|
||||||
if (theOffset < offset && theLine == (getNumVirtualLines()-1)) {
|
if (theOffset < offset && theLine == (getNumVirtualLines() - 1))
|
||||||
|
{
|
||||||
var lineLen = getNumChars();
|
var lineLen = getNumChars();
|
||||||
theOffset += lineLen - lineChar;
|
theOffset += lineLen - lineChar;
|
||||||
lineChar = lineLen;
|
lineChar = lineLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { vline:theLine, offset:theOffset, lineChar:lineChar };
|
return {
|
||||||
|
vline: theLine,
|
||||||
|
offset: theOffset,
|
||||||
|
lineChar: lineChar
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {getNumVirtualLines:getNumVirtualLines, getVLineAndOffsetForChar:getVLineAndOffsetForChar,
|
return {
|
||||||
|
getNumVirtualLines: getNumVirtualLines,
|
||||||
|
getVLineAndOffsetForChar: getVLineAndOffsetForChar,
|
||||||
getCharForVLineAndOffset: getCharForVLineAndOffset,
|
getCharForVLineAndOffset: getCharForVLineAndOffset,
|
||||||
makeCharSeeker: function() { return makeCharSeeker(); } };
|
makeCharSeeker: function()
|
||||||
|
{
|
||||||
|
return makeCharSeeker();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function deepFirstChildTextNode(nd) {
|
function deepFirstChildTextNode(nd)
|
||||||
|
{
|
||||||
nd = nd.firstChild;
|
nd = nd.firstChild;
|
||||||
while (nd && nd.firstChild) nd = nd.firstChild;
|
while (nd && nd.firstChild) nd = nd.firstChild;
|
||||||
if (nd.data) return nd;
|
if (nd.data) return nd;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeCharSeeker(/*lineNode*/) {
|
function makeCharSeeker( /*lineNode*/ )
|
||||||
|
{
|
||||||
|
|
||||||
function charCoords(tnode, i) {
|
function charCoords(tnode, i)
|
||||||
|
{
|
||||||
var container = tnode.parentNode;
|
var container = tnode.parentNode;
|
||||||
|
|
||||||
// treat space specially; a space at the end of a virtual line
|
// treat space specially; a space at the end of a virtual line
|
||||||
// will have weird coordinates
|
// will have weird coordinates
|
||||||
var isSpace = (tnode.nodeValue.charAt(i) === " ");
|
var isSpace = (tnode.nodeValue.charAt(i) === " ");
|
||||||
if (isSpace) {
|
if (isSpace)
|
||||||
if (i == 0) {
|
{
|
||||||
if (container.previousSibling && deepFirstChildTextNode(container.previousSibling)) {
|
if (i == 0)
|
||||||
|
{
|
||||||
|
if (container.previousSibling && deepFirstChildTextNode(container.previousSibling))
|
||||||
|
{
|
||||||
tnode = deepFirstChildTextNode(container.previousSibling);
|
tnode = deepFirstChildTextNode(container.previousSibling);
|
||||||
i = tnode.length - 1;
|
i = tnode.length - 1;
|
||||||
container = tnode.parentNode;
|
container = tnode.parentNode;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
return {top:container.offsetTop, left:container.offsetLeft};
|
{
|
||||||
|
return {
|
||||||
|
top: container.offsetTop,
|
||||||
|
left: container.offsetLeft
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
i--; // use previous char
|
i--; // use previous char
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,9 +163,11 @@ function makeVirtualLineView(lineNode) {
|
||||||
frag.appendChild(document.createTextNode(tnodeText.substring(i + 1)));
|
frag.appendChild(document.createTextNode(tnodeText.substring(i + 1)));
|
||||||
container.replaceChild(frag, tnode);
|
container.replaceChild(frag, tnode);
|
||||||
|
|
||||||
var result = {top:charWrapper.offsetTop,
|
var result = {
|
||||||
|
top: charWrapper.offsetTop,
|
||||||
left: charWrapper.offsetLeft + (isSpace ? charWrapper.offsetWidth : 0),
|
left: charWrapper.offsetLeft + (isSpace ? charWrapper.offsetWidth : 0),
|
||||||
height:charWrapper.offsetHeight};
|
height: charWrapper.offsetHeight
|
||||||
|
};
|
||||||
|
|
||||||
while (container.firstChild) container.removeChild(container.firstChild);
|
while (container.firstChild) container.removeChild(container.firstChild);
|
||||||
container.appendChild(tnode);
|
container.appendChild(tnode);
|
||||||
|
@ -143,34 +186,41 @@ function makeVirtualLineView(lineNode) {
|
||||||
var approxLineHeight;
|
var approxLineHeight;
|
||||||
var whichLine = 0;
|
var whichLine = 0;
|
||||||
|
|
||||||
function nextNode() {
|
function nextNode()
|
||||||
|
{
|
||||||
var n = curNode;
|
var n = curNode;
|
||||||
if (!n) n = lineNode.firstChild;
|
if (!n) n = lineNode.firstChild;
|
||||||
else n = n.nextSibling;
|
else n = n.nextSibling;
|
||||||
while (n && ! deepFirstChildTextNode(n)) {
|
while (n && !deepFirstChildTextNode(n))
|
||||||
|
{
|
||||||
n = n.nextSibling;
|
n = n.nextSibling;
|
||||||
}
|
}
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
function prevNode() {
|
|
||||||
|
function prevNode()
|
||||||
|
{
|
||||||
var n = curNode;
|
var n = curNode;
|
||||||
if (!n) n = lineNode.lastChild;
|
if (!n) n = lineNode.lastChild;
|
||||||
else n = n.previousSibling;
|
else n = n.previousSibling;
|
||||||
while (n && ! deepFirstChildTextNode(n)) {
|
while (n && !deepFirstChildTextNode(n))
|
||||||
|
{
|
||||||
n = n.previousSibling;
|
n = n.previousSibling;
|
||||||
}
|
}
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
var seeker;
|
var seeker;
|
||||||
if (lineLength > 0) {
|
if (lineLength > 0)
|
||||||
|
{
|
||||||
curNode = nextNode();
|
curNode = nextNode();
|
||||||
var firstCharData = charCoords(deepFirstChildTextNode(curNode), 0);
|
var firstCharData = charCoords(deepFirstChildTextNode(curNode), 0);
|
||||||
approxLineHeight = firstCharData.height;
|
approxLineHeight = firstCharData.height;
|
||||||
curTop = firstCharData.top;
|
curTop = firstCharData.top;
|
||||||
curLeft = firstCharData.left;
|
curLeft = firstCharData.left;
|
||||||
|
|
||||||
function updateCharData(tnode, i) {
|
function updateCharData(tnode, i)
|
||||||
|
{
|
||||||
var coords = charCoords(tnode, i);
|
var coords = charCoords(tnode, i);
|
||||||
whichLine += Math.round((coords.top - curTop) / approxLineHeight);
|
whichLine += Math.round((coords.top - curTop) / approxLineHeight);
|
||||||
curTop = coords.top;
|
curTop = coords.top;
|
||||||
|
@ -178,15 +228,17 @@ function makeVirtualLineView(lineNode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
seeker = {
|
seeker = {
|
||||||
forward: function(numChars) {
|
forward: function(numChars)
|
||||||
|
{
|
||||||
var oldChar = curChar;
|
var oldChar = curChar;
|
||||||
var newChar = curChar + numChars;
|
var newChar = curChar + numChars;
|
||||||
if (newChar > (lineLength-1))
|
if (newChar > (lineLength - 1)) newChar = lineLength - 1;
|
||||||
newChar = lineLength-1;
|
while (curChar < newChar)
|
||||||
while (curChar < newChar) {
|
{
|
||||||
var curNodeLength = deepFirstChildTextNode(curNode).length;
|
var curNodeLength = deepFirstChildTextNode(curNode).length;
|
||||||
var toGo = curNodeLength - curCharWithinNode;
|
var toGo = curNodeLength - curCharWithinNode;
|
||||||
if (curChar + toGo > newChar || ! nextNode()) {
|
if (curChar + toGo > newChar || !nextNode())
|
||||||
|
{
|
||||||
// going to next node would be too far
|
// going to next node would be too far
|
||||||
var n = newChar - curChar;
|
var n = newChar - curChar;
|
||||||
if (n >= toGo) n = toGo - 1;
|
if (n >= toGo) n = toGo - 1;
|
||||||
|
@ -194,7 +246,8 @@ function makeVirtualLineView(lineNode) {
|
||||||
curCharWithinNode += n;
|
curCharWithinNode += n;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
// go to next node
|
// go to next node
|
||||||
curChar += toGo;
|
curChar += toGo;
|
||||||
curCharWithinNode = 0;
|
curCharWithinNode = 0;
|
||||||
|
@ -204,12 +257,15 @@ function makeVirtualLineView(lineNode) {
|
||||||
updateCharData(deepFirstChildTextNode(curNode), curCharWithinNode);
|
updateCharData(deepFirstChildTextNode(curNode), curCharWithinNode);
|
||||||
return curChar - oldChar;
|
return curChar - oldChar;
|
||||||
},
|
},
|
||||||
backward: function(numChars) {
|
backward: function(numChars)
|
||||||
|
{
|
||||||
var oldChar = curChar;
|
var oldChar = curChar;
|
||||||
var newChar = curChar - numChars;
|
var newChar = curChar - numChars;
|
||||||
if (newChar < 0) newChar = 0;
|
if (newChar < 0) newChar = 0;
|
||||||
while (curChar > newChar) {
|
while (curChar > newChar)
|
||||||
if (curChar - curCharWithinNode <= newChar || !prevNode()) {
|
{
|
||||||
|
if (curChar - curCharWithinNode <= newChar || !prevNode())
|
||||||
|
{
|
||||||
// going to prev node would be too far
|
// going to prev node would be too far
|
||||||
var n = curChar - newChar;
|
var n = curChar - newChar;
|
||||||
if (n > curCharWithinNode) n = curCharWithinNode;
|
if (n > curCharWithinNode) n = curCharWithinNode;
|
||||||
|
@ -217,7 +273,8 @@ function makeVirtualLineView(lineNode) {
|
||||||
curCharWithinNode -= n;
|
curCharWithinNode -= n;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
// go to prev node
|
// go to prev node
|
||||||
curChar -= curCharWithinNode + 1;
|
curChar -= curCharWithinNode + 1;
|
||||||
curNode = prevNode();
|
curNode = prevNode();
|
||||||
|
@ -227,33 +284,63 @@ function makeVirtualLineView(lineNode) {
|
||||||
updateCharData(deepFirstChildTextNode(curNode), curCharWithinNode);
|
updateCharData(deepFirstChildTextNode(curNode), curCharWithinNode);
|
||||||
return oldChar - curChar;
|
return oldChar - curChar;
|
||||||
},
|
},
|
||||||
getVirtualLine: function() { return whichLine; },
|
getVirtualLine: function()
|
||||||
getLeftCoord: function() { return curLeft; }
|
{
|
||||||
|
return whichLine;
|
||||||
|
},
|
||||||
|
getLeftCoord: function()
|
||||||
|
{
|
||||||
|
return curLeft;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
curLeft = lineNode.offsetLeft;
|
curLeft = lineNode.offsetLeft;
|
||||||
seeker = { forward: function(numChars) { return 0; },
|
seeker = {
|
||||||
backward: function(numChars) { return 0; },
|
forward: function(numChars)
|
||||||
getVirtualLine: function() { return 0; },
|
{
|
||||||
getLeftCoord: function() { return curLeft; }
|
return 0;
|
||||||
|
},
|
||||||
|
backward: function(numChars)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
getVirtualLine: function()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
getLeftCoord: function()
|
||||||
|
{
|
||||||
|
return curLeft;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
seeker.getOffset = function() { return curChar; };
|
seeker.getOffset = function()
|
||||||
seeker.getLineLength = function() { return lineLength; };
|
{
|
||||||
seeker.toString = function() {
|
return curChar;
|
||||||
|
};
|
||||||
|
seeker.getLineLength = function()
|
||||||
|
{
|
||||||
|
return lineLength;
|
||||||
|
};
|
||||||
|
seeker.toString = function()
|
||||||
|
{
|
||||||
return "seeker[curChar: " + curChar + "(" + lineText.charAt(curChar) + "), left: " + seeker.getLeftCoord() + ", vline: " + seeker.getVirtualLine() + "]";
|
return "seeker[curChar: " + curChar + "(" + lineText.charAt(curChar) + "), left: " + seeker.getLeftCoord() + ", vline: " + seeker.getVirtualLine() + "]";
|
||||||
};
|
};
|
||||||
|
|
||||||
function moveByWhile(isBackward, amount, optCondFunc, optCharLimit) {
|
function moveByWhile(isBackward, amount, optCondFunc, optCharLimit)
|
||||||
|
{
|
||||||
var charsMovedLast = null;
|
var charsMovedLast = null;
|
||||||
var hasCondFunc = ((typeof optCondFunc) == "function");
|
var hasCondFunc = ((typeof optCondFunc) == "function");
|
||||||
var condFunc = optCondFunc;
|
var condFunc = optCondFunc;
|
||||||
var hasCharLimit = ((typeof optCharLimit) == "number");
|
var hasCharLimit = ((typeof optCharLimit) == "number");
|
||||||
var charLimit = optCharLimit;
|
var charLimit = optCharLimit;
|
||||||
while (charsMovedLast !== 0 && ((! hasCondFunc) || condFunc())) {
|
while (charsMovedLast !== 0 && ((!hasCondFunc) || condFunc()))
|
||||||
|
{
|
||||||
var toMove = amount;
|
var toMove = amount;
|
||||||
if (hasCharLimit) {
|
if (hasCharLimit)
|
||||||
|
{
|
||||||
var untilLimit = (isBackward ? curChar - charLimit : charLimit - curChar);
|
var untilLimit = (isBackward ? curChar - charLimit : charLimit - curChar);
|
||||||
if (untilLimit < toMove) toMove = untilLimit;
|
if (untilLimit < toMove) toMove = untilLimit;
|
||||||
}
|
}
|
||||||
|
@ -262,17 +349,23 @@ function makeVirtualLineView(lineNode) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
seeker.forwardByWhile = function(amount, optCondFunc, optCharLimit) {
|
seeker.forwardByWhile = function(amount, optCondFunc, optCharLimit)
|
||||||
|
{
|
||||||
moveByWhile(false, amount, optCondFunc, optCharLimit);
|
moveByWhile(false, amount, optCondFunc, optCharLimit);
|
||||||
}
|
}
|
||||||
seeker.backwardByWhile = function(amount, optCondFunc, optCharLimit) {
|
seeker.backwardByWhile = function(amount, optCondFunc, optCharLimit)
|
||||||
|
{
|
||||||
moveByWhile(true, amount, optCondFunc, optCharLimit);
|
moveByWhile(true, amount, optCondFunc, optCharLimit);
|
||||||
}
|
}
|
||||||
seeker.binarySearch = function(condFunc) {
|
seeker.binarySearch = function(condFunc)
|
||||||
|
{
|
||||||
// returns index of boundary between false chars and true chars;
|
// returns index of boundary between false chars and true chars;
|
||||||
// positions seeker at first true char, or else last char
|
// positions seeker at first true char, or else last char
|
||||||
var trueFunc = condFunc;
|
var trueFunc = condFunc;
|
||||||
var falseFunc = function() { return ! condFunc(); };
|
var falseFunc = function()
|
||||||
|
{
|
||||||
|
return !condFunc();
|
||||||
|
};
|
||||||
seeker.forwardByWhile(20, falseFunc);
|
seeker.forwardByWhile(20, falseFunc);
|
||||||
seeker.backwardByWhile(20, trueFunc);
|
seeker.backwardByWhile(20, trueFunc);
|
||||||
seeker.forwardByWhile(10, falseFunc);
|
seeker.forwardByWhile(10, falseFunc);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue